Build Lightweight Web ModuleMorgan Cheng@morganchengMay 26th, 2011
Expand Thriving Web Site
Inject Your Content Into Other Sites
Partner Website ServerYour Website ServerBrowser
Partner Website ServerYour Website ServerWeb ModuleBrowser
Cross Domain
JSONP
Inject Content with JSONPCreate one hidden elementSend JSONP request When JSONP data is received, compose HTMLFill the hidden element with composed HTMLShow hidden element
Inject an iframe
iframe covering whole viewport
It is easy to create abigiframe. The hard part is how to close it.
Same Domain CallbackHidden Proxy Iframe
Time to Wear Hacker’s Hat
Cannot be “javascript: … ”Hidden Proxy Iframe
API Design First
<script src=“XXXXX_loader.js”></script><script>XXXXX.load(parameter);</script>
YAHOO.ABC.load(parameter);
YAHOO.ABC.load(parameter);Y.ABC.load(parameter);
YAHOO.ABC.load(parameter);Y.ABC.load(parameter);YABC.load(parameter);
YABC.load(	‘wretch’, // appid. Mandatory	1234, 		// spaceid. Mandatory	100 	// delay in ms. Optional);
YABC.load(	‘wretch’, // appid. Mandatory	1234, 		// spaceid. Mandatory	100, 	// delay in ms. Optional   	‘tw’		// intl. Mandatory);What if new Mandatory parameter is added?
YABC.load({appid: ‘wretch’,spaceid: ‘1234’,    delay: 100,intl: ‘tw’});Better!Config Object Pattern
Be Disciplined
Rules #1: Don’t Assume Too Much on Hosting PageWhat?You Don’t Have YUI?
Rules #2: Don’t Be Obstructive to Hosting Page
Rules #3: Don’t Impact Hosting Page Performance
In a Word, We Have To DIY
And, It MUST Be Lightweight
Simplest Feature vs. Rich Feature
YAHOO.util.Connect.asyncRequest(	‘GET’,	‘http://www.example.com/jsonp’,	{success: sucessHandler,failure: failureHandler,argument: argumentObj	},postData};
We Don’t Need Full FeatureYAHOO.util.Connect.asyncRequest(	‘GET’,	‘http://www.example.com/jsonp’,	{success: sucessHandler,failure: failureHandler,argument: argumentObj	},postData};
This is What We Need
Minify-Friendly JavaScript
/*  * Gets query string presentation of given object.  */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key]));    }    return encParams.join('&');}
/*  * Gets query string presentation of given object.  */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key]));    }    return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Before 287 bytesAfter 119 bytesCompression Rate: 59%Minify
What Can Be Minified?
/*  * Gets query string presentation of given object.  */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key]));    }    return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Comments are stripped
/*  * Gets query string presentation of given object.  */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key]));    }    return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Unnecessary White Spaces are Stripped
/*  * Gets query string presentation of given object.  */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key]));    }    return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Argument Names are Obfuscated
/*  * Gets query string presentation of given object.  */function toQueryString(params) {varencParams= [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key]));    }    return encParams.join('&');}function toQueryString(c){vara=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Local Variable Names are Minified
What Can NOT Be Minified?
/*  * Gets query string presentation of given object.  */functiontoQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key]));    }return encParams.join('&');}functiontoQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Keywords are NOT Minified
/*  * Gets query string presentation of given object.  */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key]));    }    return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Global Variables are NOT Minified
/*  * Gets query string presentation of given object.  */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key]));    }    return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Object Properties are NOT Minified
/*  * Gets query string presentation of given object.  */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key]));    }    return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};How About This?
Wait, Do We Need Minify If All Browsers Support Gzip?
JavaScript?GzipGzip is no-loss compressionIt Doesn’t Understand JavaScript
~15% “Accept-Encoding: gzip, deflate”HTTP Headers Are Stripped
Evolution of Code
YABC = {load: function() {			// do something    }};
YABC = {privateVariable: ‘hello’,privateFunction: function() {           // do some private thing    },load: function() {			// do something    }};
YABC = {privateVariable: ‘hello’,privateFunction: function() {           // do some helping    },load: function() {			// do something    }};Not Minifiable
(function() {var _privateVariable = ‘hello’;function _privateFunction () {	// do some private thing}YABC = {    load: function() {			// do something    }};}())Immediate Function Pattern
(function() {var_privateVariable= ‘hello’;function _privateFunction() {	// do some private thing}YABC = {    load: function() {			// do something    }};}())Minifiable
(function() {var win = window; _privateVariable = ‘hello’;function _privateFunction () {	// do some private thing}YABC = {    load: function() {			// do something    }};}())“window” is used more than once
(function() {var win = window; _privateVariable = ‘hello’;function _privateFunction () {	// do some private thing}YABC = {    unload: function() {    },    load: function() {			// do something    }};}())
(function() {var win = window; _privateVariable = ‘hello’;function _privateFunction () {	// do some private thing}YABC = {unload: function() {    },    load: function() {			// do something    }};}())Every invocation of this method has to be “YABC.unload”
(function() {var win = window; _privateVariable = ‘hello’,yabc;function _privateFunction () {	// do some private thing}varyabc= {unload: function() {    },    load: function() {			// do something    }};YABC = yabc;}())Local Invocation of “yabc.unload” can be minfied
(function() {var win = window; _privateVariable = ‘hello’,yabc;function _privateFunction () {	// do some private thing}varyabc= {    unload: function() {    },    load: function() {			// do something    }};YABC = yabc;}())YUI Developers,Looks Familiar?
Be a JavaScript Ninjia
(function() {	// immediate functioning}())
(function() {	// immediate functioning}())!function() {	// immediate functioning}()Saving 1 Byte
if (-1===indexOf(foo,’bar’)) {	// do something}
if (-1!==foo.indexOf(’bar’)) {	// do something}if (~foo.indexOf(’bar’)) {	// do something}Saving 4 Bytes
function escapeHTML(s) {	return s.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g,'&lt;').replace(/"/g, '&quot').replace(/'/g,'&#x27;').replace(/\//g,'&#x2F;');}
function escapeHTML(s) {	return s.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g,'&lt;').replace(/"/g, '&quot').replace(/'/g,'&#x27;').replace(/\//g,'&#x2F;');}function escapeHTML(s, r) {r='replace';returns[r](/&/g,'&amp;')[r](/>/g,'&gt;')[r](/</g,'&lt;')[r](/"/g, '&quot')[r](/'/g,'&#x27;')[r](/\//g,'&#x2F;’);}Saving 19 Bytes
History of code size
JavaScript is NOT SlowBut, DOM is Slow
Module Versioning
Traditionally …http://www.example.com/v1/loader.jshttp://www.example.com/loader_20110510.js
Short Time Caching
TakeawaysMake your partners happyHack your own code. Hack it Hard!Minify JavaScript Code
Thank You!

Build Lightweight Web Module

  • 1.
    Build Lightweight WebModuleMorgan Cheng@morganchengMay 26th, 2011
  • 2.
  • 3.
    Inject Your ContentInto Other Sites
  • 4.
    Partner Website ServerYourWebsite ServerBrowser
  • 5.
    Partner Website ServerYourWebsite ServerWeb ModuleBrowser
  • 6.
  • 7.
  • 8.
    Inject Content withJSONPCreate one hidden elementSend JSONP request When JSONP data is received, compose HTMLFill the hidden element with composed HTMLShow hidden element
  • 9.
  • 11.
  • 12.
    It is easyto create abigiframe. The hard part is how to close it.
  • 13.
  • 14.
    Time to WearHacker’s Hat
  • 15.
    Cannot be “javascript:… ”Hidden Proxy Iframe
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
    YABC.load( ‘wretch’, // appid.Mandatory 1234, // spaceid. Mandatory 100 // delay in ms. Optional);
  • 22.
    YABC.load( ‘wretch’, // appid.Mandatory 1234, // spaceid. Mandatory 100, // delay in ms. Optional ‘tw’ // intl. Mandatory);What if new Mandatory parameter is added?
  • 23.
    YABC.load({appid: ‘wretch’,spaceid: ‘1234’, delay: 100,intl: ‘tw’});Better!Config Object Pattern
  • 24.
  • 25.
    Rules #1: Don’tAssume Too Much on Hosting PageWhat?You Don’t Have YUI?
  • 26.
    Rules #2: Don’tBe Obstructive to Hosting Page
  • 27.
    Rules #3: Don’tImpact Hosting Page Performance
  • 28.
    In a Word,We Have To DIY
  • 29.
    And, It MUSTBe Lightweight
  • 30.
  • 31.
  • 32.
    We Don’t NeedFull FeatureYAHOO.util.Connect.asyncRequest( ‘GET’, ‘http://www.example.com/jsonp’, {success: sucessHandler,failure: failureHandler,argument: argumentObj },postData};
  • 33.
  • 34.
  • 35.
    /* *Gets query string presentation of given object. */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key])); } return encParams.join('&');}
  • 36.
    /* *Gets query string presentation of given object. */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key])); } return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Before 287 bytesAfter 119 bytesCompression Rate: 59%Minify
  • 37.
    What Can BeMinified?
  • 38.
    /* *Gets query string presentation of given object. */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key])); } return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Comments are stripped
  • 39.
    /* *Gets query string presentation of given object. */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key])); } return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Unnecessary White Spaces are Stripped
  • 40.
    /* *Gets query string presentation of given object. */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key])); } return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Argument Names are Obfuscated
  • 41.
    /* *Gets query string presentation of given object. */function toQueryString(params) {varencParams= [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key])); } return encParams.join('&');}function toQueryString(c){vara=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Local Variable Names are Minified
  • 42.
    What Can NOTBe Minified?
  • 43.
    /* *Gets query string presentation of given object. */functiontoQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key])); }return encParams.join('&');}functiontoQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Keywords are NOT Minified
  • 44.
    /* *Gets query string presentation of given object. */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key])); } return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Global Variables are NOT Minified
  • 45.
    /* *Gets query string presentation of given object. */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key])); } return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};Object Properties are NOT Minified
  • 46.
    /* *Gets query string presentation of given object. */function toQueryString(params) {varencParams = [], encode = encodeURIComponent;for(key in params) {encParams.push(encode(key) + '=' + encode(params[key])); } return encParams.join('&');}function toQueryString(c){var a=[],b=encodeURIComponent;for(key in c){a.push(b(key)+"="+b(c[key]))}returna.join("&")};How About This?
  • 47.
    Wait, Do WeNeed Minify If All Browsers Support Gzip?
  • 48.
    JavaScript?GzipGzip is no-losscompressionIt Doesn’t Understand JavaScript
  • 49.
    ~15% “Accept-Encoding: gzip,deflate”HTTP Headers Are Stripped
  • 50.
  • 51.
    YABC = {load:function() { // do something }};
  • 52.
    YABC = {privateVariable:‘hello’,privateFunction: function() { // do some private thing },load: function() { // do something }};
  • 53.
    YABC = {privateVariable:‘hello’,privateFunction: function() { // do some helping },load: function() { // do something }};Not Minifiable
  • 54.
    (function() {var _privateVariable= ‘hello’;function _privateFunction () { // do some private thing}YABC = { load: function() { // do something }};}())Immediate Function Pattern
  • 55.
    (function() {var_privateVariable= ‘hello’;function_privateFunction() { // do some private thing}YABC = { load: function() { // do something }};}())Minifiable
  • 56.
    (function() {var win= window; _privateVariable = ‘hello’;function _privateFunction () { // do some private thing}YABC = { load: function() { // do something }};}())“window” is used more than once
  • 57.
    (function() {var win= window; _privateVariable = ‘hello’;function _privateFunction () { // do some private thing}YABC = { unload: function() { }, load: function() { // do something }};}())
  • 58.
    (function() {var win= window; _privateVariable = ‘hello’;function _privateFunction () { // do some private thing}YABC = {unload: function() { }, load: function() { // do something }};}())Every invocation of this method has to be “YABC.unload”
  • 59.
    (function() {var win= window; _privateVariable = ‘hello’,yabc;function _privateFunction () { // do some private thing}varyabc= {unload: function() { }, load: function() { // do something }};YABC = yabc;}())Local Invocation of “yabc.unload” can be minfied
  • 60.
    (function() {var win= window; _privateVariable = ‘hello’,yabc;function _privateFunction () { // do some private thing}varyabc= { unload: function() { }, load: function() { // do something }};YABC = yabc;}())YUI Developers,Looks Familiar?
  • 61.
  • 62.
  • 63.
    (function() { // immediatefunctioning}())!function() { // immediate functioning}()Saving 1 Byte
  • 64.
  • 65.
    if (-1!==foo.indexOf(’bar’)) { //do something}if (~foo.indexOf(’bar’)) { // do something}Saving 4 Bytes
  • 66.
    function escapeHTML(s) { returns.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g,'&lt;').replace(/"/g, '&quot').replace(/'/g,'&#x27;').replace(/\//g,'&#x2F;');}
  • 67.
    function escapeHTML(s) { returns.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g,'&lt;').replace(/"/g, '&quot').replace(/'/g,'&#x27;').replace(/\//g,'&#x2F;');}function escapeHTML(s, r) {r='replace';returns[r](/&/g,'&amp;')[r](/>/g,'&gt;')[r](/</g,'&lt;')[r](/"/g, '&quot')[r](/'/g,'&#x27;')[r](/\//g,'&#x2F;’);}Saving 19 Bytes
  • 68.
  • 69.
    JavaScript is NOTSlowBut, DOM is Slow
  • 70.
  • 71.
  • 72.
  • 73.
    TakeawaysMake your partnershappyHack your own code. Hack it Hard!Minify JavaScript Code
  • 74.