From 6c7343f86b48ca5a933d56fc9ae0ff3ece363108 Mon Sep 17 00:00:00 2001 From: Taha Jahangir Date: Tue, 28 Feb 2012 08:45:52 +0330 Subject: [PATCH 01/10] Unobfuscated, compiled with google closure --- nbl.plus.js | 207 +++++++++++++++++++++++++++--------------------- nbl.plus.min.js | 2 +- 2 files changed, 116 insertions(+), 93 deletions(-) diff --git a/nbl.plus.js b/nbl.plus.js index c749d4c..4770173 100644 --- a/nbl.plus.js +++ b/nbl.plus.js @@ -5,96 +5,119 @@ * * Date: 2010-09-24 */ -this.nbl = { - c: document, - q: {}, // The dictionary that will hold the script-queue - n: null, - - // The loader function - // - // Called with an array, it will interpret the options array - // Called without an array it will try to load the options from the script-tag's data-nbl attribute - l: function(a) { - var b, c, x, y, z, s, l, i = j = 0, m = this; m.h = m.c.head || m.c.body || m.c.documentElement || m.h; - - // The timeout counter, counts backwards every 50ms from 50 ticks (50*50=2500ms by default) - if (!m.i) { - m.s = m.f = 0; // Reset counters: completed, created, timeout function - m.i = setInterval( - function() { - // If the timeout counter dips below zero, or the amount of completed scripts equals the amount - // of created script-tags, we can clear the interval - if (m.o < 0 || m.s == 0) { - m.i = clearInterval(m.i); - // If the amount of completed scripts is smaller than the amount of created script-tags, - // and there is a timeout function available, call it with the current script-queue. - (m.s > 0 && m.f) && m.f(m.q) - } - m.o-- - }, - m.o = 50 // Set the initial ticks at 50, as well as the interval at 50ms - ); - } - // If no arguments were given (a == l, which is null), try to load the options from the script tag - if (a == m.n) { - s = m.c.getElementsByTagName("script"); // Get all script tags in the current document - while (j < s.length) { - if ((a = eval("("+s[j].getAttribute("data-nbl")+")")) && a) { // Check for the data-nbl attribute - m.h = s[j].parentNode; - break - } - j++ - } - } - - // If an options array was provided, proceed to interpret it - if (a&&a.shift) { - while (i < a.length) { // Loop through the options - b = a[i]; // Get the current element - c = a[i+1]; // Get the next element - x = 'function'; - y = typeof b; - z = typeof c; - l = (z == x) ? c : (y == x) ? b : m.n; // Check whether the current or next element is a function and store it - if (y == 'number') m.o = b/50; // If the current element is a number, set the timeout interval to this number/50 - // If the current element is a string, call this.a() with the string as a one-element array and the callback function l - if (y == 'string') m.a([b], l); - // If the current element is an array, call this.a() with a two-element array of the string and the next element - // as well as the callback function l - b.shift && m.a([b.shift(), b], l); - if (!m.f && l) m.f = l; // Store the function l as the timeout function if it hasn't been set yet - i++ - } - } - }, - a: function(u,l) { - var s, t, m = this, n = u[0].replace(/.+\/|\.min\.js|\.js|\?.+|\W/gi, ''), k = {js: {t: "script", a: "src"}, css: {t: "link", a: "href", r: "stylesheet"}, "i": {t: "img", a: "src"}}; // Clean up the name of the script for storage in the queue - t = u[0].match(/\.([cjs]{2,4})$|\?.+/i); t = (t) ? t[1] : "i"; - s = m.q[n] = m.c.createElement(k[t].t); - s.setAttribute(k[t].a, u[0]); - // Fix: CSS links do not fire onload events - Richard Lopes - // Images do. Limitation: no callback function possible after CSS loads - if (k[t].r) s.setAttribute("rel", k[t].r); - else { - // When this script completes loading, it will trigger a callback function consisting of two things: - // 1. It will call nbl.l() with the remaining items in u[1] (if there are any) - // 2. It will execute the function l (if it is a function) - s.onload = s.onreadystatechange = function(){ - var s = this, d = function(){ - var s = m, r = u[1]; - s.q[n] = true; // Set the entry for this script in the script-queue to true - r && s.l(r); // Call nbl.l() with the remaining elements of the original array - l && l(); // Call the callback function l - s.s-- - }; - if ( !s.readyState || /de|te/.test( s.readyState ) ) { - s.onload = s.onreadystatechange = m.n; d() // On completion execute the callback function as defined above - } - }; - m.s++ - } - m.h.appendChild(s) // Add the script to the document - } -}; -nbl.l() +// ==ClosureCompiler== +// @output_file_name default.js +// @compilation_level ADVANCED_OPTIMIZATIONS +// ==/ClosureCompiler== + +/** + * @constructor + */ +(function (document, nul) { + var queue = {}, // The dictionary that will hold the script-queue + timeout, ticks, + timeoutHandler, + pendingCount, + head = document.head || document.body || document.documentElement, + // The loader function + // + // Called with an array, it will interpret the options array + // Called without an array it will try to load the options from the script-tag's data-nbl attribute + /** + * @param {*=} params + */ + load = function(params) { + var cur_element, next_element, function_type, cur_type, next_type, scripts, handler, i=0,j = 0; + + // The timeout counter, counts backwards every 50ms from 50 ticks (50*50=2500ms by default) + if (!timeout) { + pendingCount = timeoutHandler = 0; // Reset counters: completed, created, timeout function + timeout = setInterval( + function() { + // If the timeout counter dips below zero, or the amount of completed scripts equals the amount + // of created script-tags, we can clear the interval + if (ticks < 0 || pendingCount == 0) { + timeout = clearInterval(timeout); + // If the amount of completed scripts is smaller than the amount of created script-tags, + // and there is a timeout function available, call it with the current script-queue. + (pendingCount > 0 && timeoutHandler) && timeoutHandler(queue); + } + ticks-- + }, + ticks = 50 // Set the initial ticks at 50, as well as the interval at 50ms + ); + } + + // If no arguments were given (a == l, which is null), try to load the options from the script tag + if (params == nul) { + scripts = document.getElementsByTagName("script"); // Get all script tags in the current document + while (j < scripts.length) { + if ((params = eval("("+scripts[j].getAttribute("data-nbl")+")")) && params) { // Check for the data-nbl attribute + head = scripts[j].parentNode; + break + } + j++ + } + } + + // If an options array was provided, proceed to interpret it + if (params && params.shift) { + while (i < params.length) { // Loop through the options + cur_element = params[i]; // Get the current element + next_element = params[i+1]; // Get the next element + function_type = 'function'; + cur_type = typeof cur_element; + next_type = typeof next_element; + handler = (next_type == function_type) ? + next_element : + (cur_type == function_type) ? cur_element : nul; // Check whether the current or next element is a function and store it + if (cur_type == 'number') ticks = cur_element/50; // If the current element is a number, set the timeout interval to this number/50 + // If the current element is a string, call this.addScripts() with the string as a one-element array and the callback function l + if (cur_type == 'string') addScripts([cur_element], handler); + // If the current element is an array, call this.addScripts() with a two-element array of the string and the next element + // as well as the callback function l + cur_element.shift && addScripts([cur_element.shift(), cur_element], handler); + + if (!timeoutHandler && handler) timeoutHandler = handler; // Store the function l as the timeout function if it hasn't been set yet + i++ + } + } + }, + addScripts = function(addList, handler) { + var tag, type, m = this, + name = addList[0].replace(/.+\/|\.min\.js|\.js|\?.+|\W/gi, ''), + k = {'js': {tagname: "script", attr: "src"}, + 'css': {tagname: "link", attr: "href", relat: "stylesheet"}, + 'i': {tagname: "img", attr: "src"}}; // Clean up the name of the script for storage in the queue + type = addList[0].match(/\.([cjs]{2,4})$|\?.+/i); + type = (type) ? type[1] : "i"; + tag = queue[name] = document.createElement(k[type].tagname); + tag.setAttribute(k[type].attr, addList[0]); + // Fix: CSS links do not fire onload events - Richard Lopes + // Images do. Limitation: no callback function possible after CSS loads + if (k[type].relat) tag.setAttribute("rel", k[type].relat); + else { + // When this script completes loading, it will trigger a callback function consisting of two things: + // 1. It will call nbl.l() with the remaining items in u[1] (if there are any) + // 2. It will execute the function l (if it is a function) + tag.onload = tag.onreadystatechange = function(){ + var s = this, + loaded = function(){ + var scriptHandler = addList[1]; + queue[name] = true; // Set the entry for this script in the script-queue to true + scriptHandler && load(scriptHandler); // Call nbl.l() with the remaining elements of the original array + handler && handler(); // Call the callback function l + pendingCount--; + }; + if ( !s.readyState || /de|te/.test( s.readyState ) ) { + s.onload = s.onreadystatechange = nul; loaded() // On completion execute the callback function as defined above + } + }; + pendingCount++ + } + head.appendChild(tag) // Add the script to the document + }; + window['nbl'] = {'l':load}; + load(); +}(document, null)); diff --git a/nbl.plus.min.js b/nbl.plus.min.js index 3f70090..b310dbe 100644 --- a/nbl.plus.min.js +++ b/nbl.plus.min.js @@ -1 +1 @@ -this.nbl={c:document,q:{},n:null,l:function(a){var b,c,x,y,z,s,l,i=j=0,m=this;m.h=m.c.head||m.c.body||m.c.documentElement||m.h,m.i||(m.s=m.f=0,m.i=setInterval(function(){if(m.o<0||m.s==0)m.i=clearInterval(m.i),m.s>0&&m.f&&m.f(m.q);m.o--},m.o=50));if(a==m.n){s=m.c.getElementsByTagName("script");while(jk||0==i)j=clearInterval(j),0 Date: Tue, 28 Feb 2012 09:36:22 +0330 Subject: [PATCH 02/10] jslinted --- nbl.plus.js | 59 ++++++++++++++++++++++++++++--------------------- nbl.plus.min.js | 2 +- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/nbl.plus.js b/nbl.plus.js index 4770173..ee78b23 100644 --- a/nbl.plus.js +++ b/nbl.plus.js @@ -11,14 +11,18 @@ // @compilation_level ADVANCED_OPTIMIZATIONS // ==/ClosureCompiler== +/*jslint browser: true, white: true, eqeq: true, plusplus: true */ + /** * @constructor */ -(function (document, nul) { +(function (document) { + "use strict"; var queue = {}, // The dictionary that will hold the script-queue timeout, ticks, timeoutHandler, pendingCount, + addScripts, head = document.head || document.body || document.documentElement, // The loader function // @@ -37,27 +41,29 @@ function() { // If the timeout counter dips below zero, or the amount of completed scripts equals the amount // of created script-tags, we can clear the interval - if (ticks < 0 || pendingCount == 0) { + if (ticks < 0 || !pendingCount) { timeout = clearInterval(timeout); // If the amount of completed scripts is smaller than the amount of created script-tags, // and there is a timeout function available, call it with the current script-queue. - (pendingCount > 0 && timeoutHandler) && timeoutHandler(queue); + if(pendingCount > 0 && timeoutHandler) {timeoutHandler(queue);} } - ticks-- + ticks--; }, ticks = 50 // Set the initial ticks at 50, as well as the interval at 50ms ); } // If no arguments were given (a == l, which is null), try to load the options from the script tag - if (params == nul) { + if (!params) { scripts = document.getElementsByTagName("script"); // Get all script tags in the current document while (j < scripts.length) { - if ((params = eval("("+scripts[j].getAttribute("data-nbl")+")")) && params) { // Check for the data-nbl attribute + /*jslint evil:true */ + params = eval("("+scripts[j].getAttribute("data-nbl")+")"); + if (params) { // Check for the data-nbl attribute head = scripts[j].parentNode; - break + break; } - j++ + j++; } } @@ -71,21 +77,22 @@ next_type = typeof next_element; handler = (next_type == function_type) ? next_element : - (cur_type == function_type) ? cur_element : nul; // Check whether the current or next element is a function and store it - if (cur_type == 'number') ticks = cur_element/50; // If the current element is a number, set the timeout interval to this number/50 + (cur_type == function_type) ? cur_element : 0; // Check whether the current or next element is a function and store it + if (cur_type == 'number') {ticks = cur_element/50;} // If the current element is a number, set the timeout interval to this number/50 // If the current element is a string, call this.addScripts() with the string as a one-element array and the callback function l - if (cur_type == 'string') addScripts([cur_element], handler); + if (cur_type == 'string') {addScripts([cur_element], handler);} // If the current element is an array, call this.addScripts() with a two-element array of the string and the next element // as well as the callback function l - cur_element.shift && addScripts([cur_element.shift(), cur_element], handler); + if(cur_element.shift) { addScripts([cur_element.shift(), cur_element], handler);} - if (!timeoutHandler && handler) timeoutHandler = handler; // Store the function l as the timeout function if it hasn't been set yet - i++ + if (!timeoutHandler && handler) {timeoutHandler = handler;} // Store the function l as the timeout function if it hasn't been set yet + i++; } } - }, + }; addScripts = function(addList, handler) { - var tag, type, m = this, + /*jslint regexp: true*/ + var tag, type, name = addList[0].replace(/.+\/|\.min\.js|\.js|\?.+|\W/gi, ''), k = {'js': {tagname: "script", attr: "src"}, 'css': {tagname: "link", attr: "href", relat: "stylesheet"}, @@ -96,8 +103,9 @@ tag.setAttribute(k[type].attr, addList[0]); // Fix: CSS links do not fire onload events - Richard Lopes // Images do. Limitation: no callback function possible after CSS loads - if (k[type].relat) tag.setAttribute("rel", k[type].relat); - else { + if (k[type].relat) { + tag.setAttribute("rel", k[type].relat); + } else { // When this script completes loading, it will trigger a callback function consisting of two things: // 1. It will call nbl.l() with the remaining items in u[1] (if there are any) // 2. It will execute the function l (if it is a function) @@ -106,18 +114,19 @@ loaded = function(){ var scriptHandler = addList[1]; queue[name] = true; // Set the entry for this script in the script-queue to true - scriptHandler && load(scriptHandler); // Call nbl.l() with the remaining elements of the original array - handler && handler(); // Call the callback function l + if(scriptHandler){ load(scriptHandler);} // Call nbl.l() with the remaining elements of the original array + if(handler){ handler();} // Call the callback function l pendingCount--; }; if ( !s.readyState || /de|te/.test( s.readyState ) ) { - s.onload = s.onreadystatechange = nul; loaded() // On completion execute the callback function as defined above + s.onload = (s.onreadystatechange = 0); + loaded(); // On completion execute the callback function as defined above } }; - pendingCount++ + pendingCount++; } - head.appendChild(tag) // Add the script to the document + head.appendChild(tag); // Add the script to the document }; - window['nbl'] = {'l':load}; + window["nbl"] = {'l':load}; load(); -}(document, null)); +}(document)); diff --git a/nbl.plus.min.js b/nbl.plus.min.js index b310dbe..f2a0cb3 100644 --- a/nbl.plus.min.js +++ b/nbl.plus.min.js @@ -1 +1 @@ -(function(h,l){function n(d,b){var c,a,f=d[0].replace(/.+\/|\.min\.js|\.js|\?.+|\W/gi,""),e={js:{b:"script",a:"src"},css:{b:"link",a:"href",c:"stylesheet"},i:{b:"img",a:"src"}};a=(a=d[0].match(/\.([cjs]{2,4})$|\?.+/i))?a[1]:"i";c=m[f]=h.createElement(e[a].b);c.setAttribute(e[a].a,d[0]);e[a].c?c.setAttribute("rel",e[a].c):(c.onload=c.onreadystatechange=function(){if(!this.readyState||/de|te/.test(this.readyState)){this.onload=this.onreadystatechange=l;var a=d[1];m[f]=!0;a&&g(a);b&&b();i--}},i++);o.appendChild(c)}function g(d){var b,c,a,g,e=0;a=0;j||(i=f=0,j=setInterval(function(){if(0>k||0==i)j=clearInterval(j),0j||!h)i=clearInterval(i),0 Date: Tue, 28 Feb 2012 10:06:35 +0330 Subject: [PATCH 03/10] dont add same script twice, with multiple calls to nbl.l() --- nbl.plus.js | 23 ++++++++++++++--------- nbl.plus.min.js | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/nbl.plus.js b/nbl.plus.js index ee78b23..736691b 100644 --- a/nbl.plus.js +++ b/nbl.plus.js @@ -96,31 +96,36 @@ name = addList[0].replace(/.+\/|\.min\.js|\.js|\?.+|\W/gi, ''), k = {'js': {tagname: "script", attr: "src"}, 'css': {tagname: "link", attr: "href", relat: "stylesheet"}, - 'i': {tagname: "img", attr: "src"}}; // Clean up the name of the script for storage in the queue + 'i': {tagname: "img", attr: "src"}}, // Clean up the name of the script for storage in the queue + loaded = function(){ + var scriptHandler = addList[1]; + queue[name] = true; // Set the entry for this script in the script-queue to true + if(scriptHandler){ load(scriptHandler);} // Call nbl.l() with the remaining elements of the original array + if(handler){ handler();} // Call the callback function l + }; type = addList[0].match(/\.([cjs]{2,4})$|\?.+/i); type = (type) ? type[1] : "i"; + if(queue[name] === true) { + // don't readd script if script already added + return loaded(); + } tag = queue[name] = document.createElement(k[type].tagname); tag.setAttribute(k[type].attr, addList[0]); // Fix: CSS links do not fire onload events - Richard Lopes // Images do. Limitation: no callback function possible after CSS loads if (k[type].relat) { tag.setAttribute("rel", k[type].relat); + loaded(); } else { // When this script completes loading, it will trigger a callback function consisting of two things: // 1. It will call nbl.l() with the remaining items in u[1] (if there are any) // 2. It will execute the function l (if it is a function) tag.onload = tag.onreadystatechange = function(){ - var s = this, - loaded = function(){ - var scriptHandler = addList[1]; - queue[name] = true; // Set the entry for this script in the script-queue to true - if(scriptHandler){ load(scriptHandler);} // Call nbl.l() with the remaining elements of the original array - if(handler){ handler();} // Call the callback function l - pendingCount--; - }; + var s = this; if ( !s.readyState || /de|te/.test( s.readyState ) ) { s.onload = (s.onreadystatechange = 0); loaded(); // On completion execute the callback function as defined above + pendingCount--; } }; pendingCount++; diff --git a/nbl.plus.min.js b/nbl.plus.min.js index f2a0cb3..a917268 100644 --- a/nbl.plus.min.js +++ b/nbl.plus.min.js @@ -1 +1 @@ -(function(g){function k(d){var b,c,a,n,e=0;a=0;i||(h=f=0,i=setInterval(function(){if(0>j||!h)i=clearInterval(i),0k||!i)j=clearInterval(j),0 Date: Wed, 6 Jun 2012 13:30:19 +0430 Subject: [PATCH 04/10] README updated to reflect version 3 --- README.textile | 77 ++++++++++++++++---------------------------------- 1 file changed, 25 insertions(+), 52 deletions(-) diff --git a/README.textile b/README.textile index a4b326c..906662a 100644 --- a/README.textile +++ b/README.textile @@ -1,8 +1,7 @@ -h1. NBL.js 2.0 - a tiny non-blocking JavaScript lazy loader +h1. NBL.js 3.0 - a tiny non-blocking JavaScript/CSS/Image lazy loader -*GZipped*: 593 bytes -*Minified*: 977 bytes -*Single line*: 816 bytes +*GZipped*: 631 bytes +*Minified*: 1079 bytes *Compatibility*: Tested in Safari, Chrome, Firefox, IE6+, basically doing nothing too fancy. *Examples*: "http://berklee.github.com/nbl/example.html":http://berklee.github.com/nbl/example.html @@ -19,17 +18,22 @@ h3. Features * Load scripts asynchronously or in order, or any combination of both * Every script can have its own callback -* Trigger a callback on a user definable timeout period +* A callback for loading all items +* Can be mixed with your provided function for loading some data +* [TODO: Trigger a callback on a user definable timeout period] * Uses HTML5's data attribute for configuration, so load all scripts with one script-tag -* Also available as a single line for inclusion in your HTML page -* Less than 1kb! +* Very small code! h2. Usage Include NBL.js in your pages and let it dynamically load all your JavaScript files by simply including the following tag: -@@ +@@ This will do the following: @@ -39,17 +43,6 @@ This will do the following: * When jQuery has loaded, it will call the @jquery_loaded()@ function. * Finally, when Urchin has loaded, it will call the @urchin_loaded()@ function. -h3. Verifying the results - -After NBL.js has done its job you can verify a few things through the global @nbl@ object. Every script will be placed in the @nbl.q@ object, referred to by the filename of the script minus the ".js" or .min.js" extension or any non-word characters. - -In the above example "jquery.lightbox.min.js" will become "jquerylightbox". If it has loaded successfully, @nbl.q.jquerylightbox@ will return true, otherwise you'll get the script element of the script you queried. - -When a script fails to load, NBL will fire the first defined function it encounters after a default timeout of 2500ms. In the above example that function is @jquery_loaded()@. If jQuery loads fine, but one of the plugins doesn't, the timeout will expire and call @jquery_loaded()@ once again, only this time it will provide the @nbl.q@ object as its only argument @e@. - -That way you can distinguish between a normal call and a timed out call, check out the example for more -information on this feature. - h2. Options NBL.js is rather flexible in its options, so let's dissect a few examples. @@ -62,55 +55,35 @@ This will simply load all three scripts in parallel. h3. Loading two scripts asynchronously, and two plugins asynchronously after the first script: -@[ [ 'script1.js', 'plugin1.js', 'plugin2.js' ], 'script2.js' ]@ +@[ {load:'script1.js', then:['plugin1.js', 'plugin2.js' ]}, 'script2.js' ]@ This will load @script1.js@ and @script2.js@ in parallel. After @script1.js@ has loaded, @plugin1.js@ and @plugin2.js@ will load in parallel. -When NBL.js encounters an array of scripts, it will immediately load the first script (@script1.js@ in -this case) and load the remaining scripts (@plugin1.js@ and @plugin2.js@) after completion. The -@plugin1.js@ and @plugin2.js@ scripts have a lower priority than the @script1.js@ and @script2.js@ -scripts and will be loaded after @script1.js@ completes. - h3. Loading four scripts in order: -@[ [ 'script1.js', [ 'script2.js', [ 'script3.js, 'script4.js ] ] ] ]@ +@{ load:'script1.js', then:{load:'script2.js', then:{load:'script3.js, then:'script4.js } } }@ -It's a bit crazy, but nesting the arrays like this will allow you to load all scripts sequentially. After -the first array with @script1.js@, NBL.js encounters a second array starting with @script2.js@, which it -will load after @script1.js@ has completed. - -After @script2.js@ completes, NBL.js will continue the iteration with the third array that starts with -@script3.js@, finally ending with loading @script4.js@ after @script3.js@ has completed. +It's a bit crazy, but nesting the objects like this will allow you to load all scripts sequentially. h3. Three scripts with their own callbacks: -@[ 'script1.js', function(e){ script1_callback(e) }, 'script2.js', function(){ script2_callback() }, 'script3.js', function(){ script3_callback() } ]@ - -The basic rule of callbacks is: declare the callback function directly after the script. - -In this example, the three scripts will load in parallel and upon completion of each script, the -corresponding callback will be called. In case of a timeout, the first defined function -(@script1_callback(e)@) will be called with @nbl.q@ as argument @e@ (as explained above). +```@[ {load:'script1.js', callback:function(e){ script1_callback(e) }}, + {load:'script2.js', callback:function(){ script2_callback() }}, + {load:'script3.js', callback:function(){ script3_callback() }} ]@``` h3. Two scripts and a plugin with their own callbacks: -@[ [ 'script1.js', 'plugin1.js', function(){ plugin1_callback() } ], function(e){ script1_callback(e) }, 'script2.js', function(){ script2_callback() } ]@ - -Following the basic rule of callbacks as mentioned above, we place the callback function for @script1.js@ -*outside* the array that contains @script1.js@ and @plugin1.js@, since to NBL.js @script1.js@ and -@script2.js@ are on equal footing, the callbacks for both must be placed in the main array. - -h3. Defining a global timeout function and a new timeout: +```@[ { load:'script1.js', + then: {load:'plugin1.js', callback:function(){ plugin1_callback() } }, + callback: function(e){ script1_callback(e) } + }, + {load:'script2.js', callback:function(){ script2_callback() }} ]@``` -@[ 3200, function(e){ global_timeout(e) }, 'script1.js', function(){ script1_callback() }, 'script2.js', function(){ script2_callback() } ]@ +h3. [TODO:] Defining a global timeout function and a new timeout: -First off, by specifying a number anywhere in the options, NBL.js will assume you want to change the timeout -from the default 2500ms to the provided number. Second, by putting a function before any scripts, it will -define it as the global timeout function. +@{ load: ['script1.js', 'script2.js', 'script3.js' ], timeout: function(){global_timeout()} }{@ -In this case @script1_callback()@ and @script2_callback()@ will be called when @script1.js@ and @script2.js@ -are finished loading. And in case of an error, @global_timeout()@ will be called after approximately 3500ms. h2. Alternative usage From 53d1ca3a571734482ada77f25a41c59bae9f6307 Mon Sep 17 00:00:00 2001 From: Taha Jahangir Date: Wed, 6 Jun 2012 14:51:34 +0430 Subject: [PATCH 05/10] New syntax for NBL 3 --- nbl.js | 94 ------------------- nbl.min.js | 1 - nbl.single.js | 3 - readme.html | 251 -------------------------------------------------- 4 files changed, 349 deletions(-) delete mode 100644 nbl.js delete mode 100644 nbl.min.js delete mode 100644 nbl.single.js delete mode 100644 readme.html diff --git a/nbl.js b/nbl.js deleted file mode 100644 index e0fc0ab..0000000 --- a/nbl.js +++ /dev/null @@ -1,94 +0,0 @@ -/* - * NBL: Non Blocking Lazy loader v2.0 - * Copyright (c) 2010 Berklee. - * Licensed under the MIT license. - * - * Date: 2010-09-24 - */ -this.nbl = { - c: document, - q: {}, // The dictionary that will hold the script-queue - n: null, - - // The loader function - // - // Called with an array, it will interpret the options array - // Called without an array it will try to load the options from the script-tag's data-nbl attribute - l: function(a) { - var b, c, x, y, z, s, l, i = j = 0, m = this; m.h = m.c.head || m.c.body || m.c.documentElement || m.h; - - // The timeout counter, counts backwards every 50ms from 50 ticks (50*50=2500ms by default) - if (!m.i) { - m.s = m.f = 0; // Reset counters: completed, created, timeout function - m.i = setInterval( - function() { - // If the timeout counter dips below zero, or the amount of completed scripts equals the amount - // of created script-tags, we can clear the interval - if (m.o < 0 || m.s == 0) { - m.i = clearInterval(m.i); - // If the amount of completed scripts is smaller than the amount of created script-tags, - // and there is a timeout function available, call it with the current script-queue. - (m.s > 0 && m.f) && m.f(m.q) - } - m.o-- - }, - m.o = 50 // Set the initial ticks at 50, as well as the interval at 50ms - ); - } - - // If no arguments were given (a == l, which is null), try to load the options from the script tag - if (a == m.n) { - s = m.c.getElementsByTagName("script"); // Get all script tags in the current document - while (j < s.length) { - if ((a = eval("("+s[j].getAttribute("data-nbl")+")")) && a) { // Check for the data-nbl attribute - m.h = s[j].parentNode; - break - } - j++ - } - } - - // If an options array was provided, proceed to interpret it - if (a&&a.shift) { - while (i < a.length) { // Loop through the options - b = a[i]; // Get the current element - c = a[i+1]; // Get the next element - x = 'function'; - y = typeof b; - z = typeof c; - l = (z == x) ? c : (y == x) ? b : m.n; // Check whether the current or next element is a function and store it - if (y == 'number') m.o = b/50; // If the current element is a number, set the timeout interval to this number/50 - // If the current element is a string, call this.a() with the string as a one-element array and the callback function l - if (y == 'string') m.a([b], l); - // If the current element is an array, call this.a() with a two-element array of the string and the next element - // as well as the callback function l - b.shift && m.a([b.shift(), b], l); - if (!m.f && l) m.f = l; // Store the function l as the timeout function if it hasn't been set yet - i++ - } - } - }, - a: function(u,l) { - var s, m = this, n = u[0].replace(/.+\/|\.min\.js|\.js|\?.+|\W/gi, ''); // Clean up the name of the script for storage in the queue - s = m.q[n] = m.c.createElement("script"); - s.setAttribute("src", u[0]); - // When this script completes loading, it will trigger a callback function consisting of two things: - // 1. It will call nbl.l() with the remaining items in u[1] (if there are any) - // 2. It will execute the function l (if it is a function) - s.onload = s.onreadystatechange = function(){ - var s = this, d = function(){ - var s = m, r = u[1]; - s.q[n] = true; // Set the entry for this script in the script-queue to true - r && s.l(r); // Call nbl.l() with the remaining elements of the original array - l && l(); // Call the callback function l - s.s-- - }; - if ( !s.readyState || /de|te/.test( s.readyState ) ) { - s.onload = s.onreadystatechange = m.n; d() // On completion execute the callback function as defined above - } - }; - m.s++; - m.h.appendChild(s) // Add the script to the document - } -}; -nbl.l() diff --git a/nbl.min.js b/nbl.min.js deleted file mode 100644 index 1f5acdc..0000000 --- a/nbl.min.js +++ /dev/null @@ -1 +0,0 @@ -this.nbl={c:document,q:{},n:null,l:function(a){var b,c,x,y,z,s,l,i=j=0,m=this;m.h=m.c.head||m.c.body||m.c.documentElement||m.h,m.i||(m.s=m.f=0,m.i=setInterval(function(){if(m.o<0||m.s==0)m.i=clearInterval(m.i),m.s>0&&m.f&&m.f(m.q);m.o--},m.o=50));if(a==m.n){s=m.c.getElementsByTagName("script");while(j0&&m.f&&m.f(m.q);m.o--},m.o=50));if(a&&a.shift)while(i - - - - NBL.js 2.0 — a tiny non-blocking JavaScript lazy loader - - - - - -
-

NBL.js 2.0

-

(a tiny non-blocking JavaScript lazy loader)

- Fork me on GitHub -
- -
-
    -
  • GZipped: 593 bytes
  • -
  • Minified: 977 bytes
  • -
  • Single line: 816 bytes
  • -
-
- -
-

What is NBL.js?

-

- NBL.js is a tiny script that will make your HTML pages load faster by loading all your JavaScript files - asynchronously (in parallel) with the rest of your page. Normally if you include two or three scripts - in your page, the browser will wait for them to be executed before your page is shown. -

-

- By using NBL.js the browser can start showing the HTML while loading and executing the scripts, resulting - in a faster, more responsive website. All in less than 1kb! Check out some examples. -

-

Features:

-
    -
  • Load scripts asynchronously or in order, or any combination of both
  • -
  • Every script can have its own callback
  • -
  • Trigger a callback on a user definable timeout period
  • -
  • Uses HTML5's data attribute for configuration, so load all scripts with one script-tag
  • -
  • Also available as a single line for inclusion in your HTML page
  • -
  • Tested in Safari, Chrome, Firefox, IE6+, basically doing nothing too fancy.
  • -
  • Less than 1kb!
  • -
- -
- -
-

Usage

-

- Include NBL.js in your pages and let it dynamically load all your JavaScript files by simply including - the following tag: -

- -

- <script src="nbl.js" data-nbl="[ [ 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js', 'jquery.lightbox.min.js', 'jquery.carousel.min.js' ], function(e){ jquery_loaded(e) }, 'http://www.google-analytics.com/urchin.js', function(){ urchin_loaded() } ]"></script> -

- -

- This will do the following: -

    -
  1. It will load the latest version of jQuery.
  2. -
  3. It will load the Urchin script from Google Analytics.
  4. -
  5. After jQuery has loaded, it will start loading the jQuery plugins as defined after jQuery in parallel.
  6. -
  7. When jQuery has loaded, it will call the jquery_loaded() function.
  8. -
  9. Finally, when Urchin has loaded, it will call the urchin_loaded() function.
  10. -
-

- -

Verifying the results

- -

- After NBL.js has done its job you can verify a few things through the global nbl object. Every - script will be placed in the nbl.q object, referred to by the filename of the script minus the - ".js" or .min.js" extension or any non-word characters. -

-

- In the above example "jquery.lightbox.min.js" will become "jquerylightbox". If it has loaded successfully, - nbl.q.jquerylightbox will return true, otherwise you'll get the script element of the script you queried. -

-

- When a script fails to load, NBL will fire the first defined function it encounters after a default timeout of 2500ms. - In the above example that function is jquery_loaded(). If jQuery loads fine, but one of the plugins doesn't, - the timeout will expire and call jquery_loaded() once again, only this time it will provide the nbl.q - object as its only argument e. -

-

- That way you can distinguish between a normal call and a timed out call, check out the - examples for more information on this feature. -

- -

Options

- -

- NBL.js is rather flexible in its options, so let's dissect a few examples. -

- -

Loading three scripts asynchronously:

- -

[ 'script1.js', 'script2.js', 'script3.js' ]

- -

This will simply load all three scripts in parallel.

- -

Loading two scripts asynchronously, and two plugins asynchronously after the first script:

- -

[ [ 'script1.js', 'plugin1.js', 'plugin2.js' ], 'script2.js' ]

- -

This will load script1.js and script2.js in parallel. After script1.js - has loaded, plugin1.js and plugin2.js will load in parallel.

- -

When NBL.js encounters an array of scripts, it will immediately load the first script (script1.js - in this case) and load the remaining scripts (plugin1.js and plugin2.js) after - completion. The plugin1.js and plugin2.js scripts have a lower priority than the - script1.js and script2.js scripts and will be loaded after script1.js - completes.

- -

Loading four scripts in order:

- -

[ [ 'script1.js', [ 'script2.js', [ 'script3.js, 'script4.js ] ] ] ]

- -

It's a bit crazy, but nesting the arrays like this will allow you to load all scripts sequentially. After - the first array with script1.js, NBL.js encounters a second array starting with script2.js, - which it will load after script1.js has completed.

- -

After script2.js completes, NBL.js will continue the iteration with the third array that starts with - script3.js, finally ending with loading script4.js after script3.js has - completed.

- -

Three scripts with their own callbacks:

- -

[ 'script1.js', function(e){ script1_callback(e) }, 'script2.js', function(){ script2_callback() }, 'script3.js', function(){ script3_callback() } ]

- -

The basic rule of callbacks is: declare the callback function directly after the script.

- -

In this example, the three scripts will load in parallel and upon completion of each script, the - corresponding callback will be called. In case of a timeout, the first defined function - (script1_callback(e)) will be called with nbl.q as argument e (as explained above).

- -

Two scripts and a plugin with their own callbacks:

- -

[ [ 'script1.js', 'plugin1.js', function(){ plugin1_callback() } ], function(e){ script1_callback(e) }, 'script2.js', function(){ script2_callback() } ]

- -

Following the basic rule of callbacks as mentioned above, we place the callback function for script1.js - outside the array that contains script1.js and plugin1.js, since to NBL.js - script1.js and script2.js are on equal footing, the callbacks for both must be placed in the main array.

- -

Defining a global timeout function and a new timeout:

- -

[ 3200, function(e){ global_timeout(e) }, 'script1.js', function(){ script1_callback() }, 'script2.js', function(){ script2_callback() } ]

- -

First off, by specifying a number anywhere in the options, NBL.js will assume you want to change the timeout from the - default 2500ms to the provided number. Second, by putting a function before any scripts, it will define it as the global - timeout function.

- -

In this case script1_callback() and script2_callback() will be called when script1.js - and script2.js are finished loading. And in case of an error, global_timeout() will be called after - approximately 3500ms.

- -

Alternative usage

- -

- If you prefer you can choose to simply include NBL.js in a single line in your HTML pages. This way you can - save a HTTP-call from the browser, and it will only add 882 bytes to your HTML page. Simply include the code - in nbl.single.js into a <script> tag at the end of your page, and replace - ['your', 'scripts', 'here'] with your own options. -

- -

- You can't use the data-nbl attribute of the script tag if you use this method. -

- -

Final note

- -

- All options are case-sensitive, if you include a file called urCHin.js, the corresponding - nbl.q object will be nbl.q.urCHin. I advise you to simply use lowercase for all options. -

- -

- If you do not specify any options in the script tag, NBL.js will instantiate the default nbl object - and will do nothing. You will have to do a manual nbl.l( [ 'your', 'options', 'here' ] ). -

- -

- You can find more examples in the included examples. -

- -

- I hope you find NBL.js useful, thanks for reading this! -

- -

- Berklee -

- -

- (@Berklee on Twitter or feedback at berkl.ee) -

- -

NBL Plus: support for images and CSS *updated*

- -

- GitHub user Knowlecules mailed me with modifications to preload CSS and images using NBL.js. I've incorporated - that code into nbl.plus.js (and nbl.plus.min.js). Thanks to some additional bug squashing by Richard Lopes, - the latest version of NBL Plus is now better than ever! Clocking in at 1154 bytes for the minified version and - 694 bytes for the gzipped one, there's no reason not to use NBL Plus for your asynchronous media loading needs. -

- -

MIT License

- -
-	Copyright (c) 2009-2011 Berklee
-
-	Permission is hereby granted, free of charge, to any person obtaining a copy
-	of this software and associated documentation files (the "Software"), to deal
-	in the Software without restriction, including without limitation the rights
-	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-	copies of the Software, and to permit persons to whom the Software is
-	furnished to do so, subject to the following conditions:
-
-	The above copyright notice and this permission notice shall be included in
-	all copies or substantial portions of the Software.
-
-	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-	THE SOFTWARE.
-			
-
- -
- - - \ No newline at end of file From fe84e4f0a95897f52dc65dd4806c46abe277b3b1 Mon Sep 17 00:00:00 2001 From: Taha Jahangir Date: Wed, 6 Jun 2012 15:04:45 +0430 Subject: [PATCH 06/10] New syntax for NBL 3 --- nbl.plus.js | 260 +++++++++++++++++++++++++++--------------------- nbl.plus.js~ | 138 +++++++++++++++++++++++++ nbl.plus.min.js | 2 +- 3 files changed, 286 insertions(+), 114 deletions(-) create mode 100644 nbl.plus.js~ diff --git a/nbl.plus.js b/nbl.plus.js index 736691b..1cde62f 100644 --- a/nbl.plus.js +++ b/nbl.plus.js @@ -17,121 +17,155 @@ * @constructor */ (function (document) { - "use strict"; - var queue = {}, // The dictionary that will hold the script-queue - timeout, ticks, - timeoutHandler, - pendingCount, - addScripts, - head = document.head || document.body || document.documentElement, - // The loader function - // - // Called with an array, it will interpret the options array - // Called without an array it will try to load the options from the script-tag's data-nbl attribute - /** - * @param {*=} params - */ - load = function(params) { - var cur_element, next_element, function_type, cur_type, next_type, scripts, handler, i=0,j = 0; + "use strict"; + var loadedScripts = {}, // The dictionary that will hold the loaded script + addScript, loadItems, + head = document.head || document.body || document.documentElement, + // The loader function + // + // Called with an array, it will interpret the options array + // Called without an array it will try to load the options from the script-tag's data-nbl attribute + /** + * @param {*=} params + * @param {*=} callback + */ + load = function(params, callback) { + //var cur_element, next_element, function_type, cur_type, next_type, scripts, handler, i=0,j = 0; - // The timeout counter, counts backwards every 50ms from 50 ticks (50*50=2500ms by default) - if (!timeout) { - pendingCount = timeoutHandler = 0; // Reset counters: completed, created, timeout function - timeout = setInterval( - function() { - // If the timeout counter dips below zero, or the amount of completed scripts equals the amount - // of created script-tags, we can clear the interval - if (ticks < 0 || !pendingCount) { - timeout = clearInterval(timeout); - // If the amount of completed scripts is smaller than the amount of created script-tags, - // and there is a timeout function available, call it with the current script-queue. - if(pendingCount > 0 && timeoutHandler) {timeoutHandler(queue);} - } - ticks--; - }, - ticks = 50 // Set the initial ticks at 50, as well as the interval at 50ms - ); - } + // The timeout counter, counts backwards every 50ms from 50 ticks (50*50=2500ms by default) + /*if (!timeout) { + pendingCount = timeoutHandler = 0; // Reset counters: completed, created, timeout function + timeout = setInterval( + function() { + // If the timeout counter dips below zero, or the amount of completed scripts equals the amount + // of created script-tags, we can clear the interval + if (ticks < 0 || !pendingCount) { + timeout = clearInterval(timeout); + // If the amount of completed scripts is smaller than the amount of created script-tags, + // and there is a timeout function available, call it with the current script-queue. + if(pendingCount > 0 && timeoutHandler) {timeoutHandler(loadedScripts);} + } + ticks--; + }, + ticks = 50 // Set the initial ticks at 50, as well as the interval at 50ms + ); + }*/ - // If no arguments were given (a == l, which is null), try to load the options from the script tag - if (!params) { - scripts = document.getElementsByTagName("script"); // Get all script tags in the current document - while (j < scripts.length) { - /*jslint evil:true */ - params = eval("("+scripts[j].getAttribute("data-nbl")+")"); - if (params) { // Check for the data-nbl attribute - head = scripts[j].parentNode; - break; - } - j++; - } - } + // If no arguments were given (a == l, which is null), try to load the options from the script tag + if (!params) { + var j=0, scripts = document.getElementsByTagName("script"); // Get all script tags in the current document + while (j < scripts.length) { + /*jslint evil:true */ + params = eval("("+scripts[j].getAttribute("data-nbl")+")"); + if (params) { // Check for the data-nbl attribute + head = scripts[j].parentNode; + break; + } + j++; + } + } + if (params) { + loadItems(params, callback||function(){}); + } - // If an options array was provided, proceed to interpret it - if (params && params.shift) { - while (i < params.length) { // Loop through the options - cur_element = params[i]; // Get the current element - next_element = params[i+1]; // Get the next element - function_type = 'function'; - cur_type = typeof cur_element; - next_type = typeof next_element; - handler = (next_type == function_type) ? - next_element : - (cur_type == function_type) ? cur_element : 0; // Check whether the current or next element is a function and store it - if (cur_type == 'number') {ticks = cur_element/50;} // If the current element is a number, set the timeout interval to this number/50 - // If the current element is a string, call this.addScripts() with the string as a one-element array and the callback function l - if (cur_type == 'string') {addScripts([cur_element], handler);} - // If the current element is an array, call this.addScripts() with a two-element array of the string and the next element - // as well as the callback function l - if(cur_element.shift) { addScripts([cur_element.shift(), cur_element], handler);} + // If an options array was provided, proceed to interpret it + /*if (params && params.shift) { + while (i < params.length) { // Loop through the options + cur_element = params[i]; // Get the current element + next_element = params[i+1]; // Get the next element + function_type = 'function'; + cur_type = typeof cur_element; + next_type = typeof next_element; + handler = (next_type == function_type) ? + next_element : + (cur_type == function_type) ? cur_element : 0; // Check whether the current or next element is a function and store it + if (cur_type == 'number') {ticks = cur_element/50;} // If the current element is a number, set the timeout interval to this number/50 + // If the current element is a string, call this.addScripts() with the string as a one-element array and the callback function l + if (cur_type == 'string') {addScripts([cur_element], handler);} + // If the current element is an array, call this.addScripts() with a two-element array of the string and the next element + // as well as the callback function l + if(cur_element.shift) { addScripts([cur_element.shift(), cur_element], handler);} - if (!timeoutHandler && handler) {timeoutHandler = handler;} // Store the function l as the timeout function if it hasn't been set yet - i++; - } - } - }; - addScripts = function(addList, handler) { - /*jslint regexp: true*/ - var tag, type, - name = addList[0].replace(/.+\/|\.min\.js|\.js|\?.+|\W/gi, ''), - k = {'js': {tagname: "script", attr: "src"}, - 'css': {tagname: "link", attr: "href", relat: "stylesheet"}, - 'i': {tagname: "img", attr: "src"}}, // Clean up the name of the script for storage in the queue - loaded = function(){ - var scriptHandler = addList[1]; - queue[name] = true; // Set the entry for this script in the script-queue to true - if(scriptHandler){ load(scriptHandler);} // Call nbl.l() with the remaining elements of the original array - if(handler){ handler();} // Call the callback function l - }; - type = addList[0].match(/\.([cjs]{2,4})$|\?.+/i); - type = (type) ? type[1] : "i"; - if(queue[name] === true) { - // don't readd script if script already added - return loaded(); - } - tag = queue[name] = document.createElement(k[type].tagname); - tag.setAttribute(k[type].attr, addList[0]); - // Fix: CSS links do not fire onload events - Richard Lopes - // Images do. Limitation: no callback function possible after CSS loads - if (k[type].relat) { - tag.setAttribute("rel", k[type].relat); - loaded(); - } else { - // When this script completes loading, it will trigger a callback function consisting of two things: - // 1. It will call nbl.l() with the remaining items in u[1] (if there are any) - // 2. It will execute the function l (if it is a function) - tag.onload = tag.onreadystatechange = function(){ - var s = this; - if ( !s.readyState || /de|te/.test( s.readyState ) ) { - s.onload = (s.onreadystatechange = 0); - loaded(); // On completion execute the callback function as defined above - pendingCount--; - } - }; - pendingCount++; - } - head.appendChild(tag); // Add the script to the document - }; - window["nbl"] = {'l':load}; - load(); + if (!timeoutHandler && handler) {timeoutHandler = handler;} // Store the function l as the timeout function if it hasn't been set yet + i++; + } + }*/ + }; + loadItems = function(item, callback){ + var itemType = typeof item; + if (itemType == 'string') { + addScript(item, callback); + } else if (item instanceof Array) { // list + /*jslint vars: true */ + var i, pending = item.length, + loaded = function(){ + if (!--pending) { + callback(); + } + }; + for (i=0; i 0 && timeoutHandler) {timeoutHandler(queue);} + } + ticks--; + }, + ticks = 50 // Set the initial ticks at 50, as well as the interval at 50ms + ); + } + + // If no arguments were given (a == l, which is null), try to load the options from the script tag + if (!params) { + scripts = document.getElementsByTagName("script"); // Get all script tags in the current document + while (j < scripts.length) { + /*jslint evil:true */ + params = eval("("+scripts[j].getAttribute("data-nbl")+")"); + if (params) { // Check for the data-nbl attribute + head = scripts[j].parentNode; + break; + } + j++; + } + } + + // If an options array was provided, proceed to interpret it + if (params && params.shift) { + while (i < params.length) { // Loop through the options + cur_element = params[i]; // Get the current element + next_element = params[i+1]; // Get the next element + function_type = 'function'; + cur_type = typeof cur_element; + next_type = typeof next_element; + handler = (next_type == function_type) ? + next_element : + (cur_type == function_type) ? cur_element : 0; // Check whether the current or next element is a function and store it + if (cur_type == 'number') {ticks = cur_element/50;} // If the current element is a number, set the timeout interval to this number/50 + // If the current element is a string, call this.addScripts() with the string as a one-element array and the callback function l + if (cur_type == 'string') {addScripts([cur_element], handler);} + // If the current element is an array, call this.addScripts() with a two-element array of the string and the next element + // as well as the callback function l + if(cur_element.shift) { addScripts([cur_element.shift(), cur_element], handler);} + + if (!timeoutHandler && handler) {timeoutHandler = handler;} // Store the function l as the timeout function if it hasn't been set yet + i++; + } + } + }; + addScripts = function(addList, handler) { + /*jslint regexp: true*/ + var tag, type, + name = addList[0].replace(/.+\/|\.min\.js|\.js|\?.+|\W/gi, ''), + k = {'js': {tagname: "script", attr: "src"}, + 'css': {tagname: "link", attr: "href", relat: "stylesheet"}, + 'i': {tagname: "img", attr: "src"}}, // Clean up the name of the script for storage in the queue + loaded = function(){ + var scriptHandler = addList[1]; + queue[name] = true; // Set the entry for this script in the script-queue to true + if(scriptHandler){ load(scriptHandler);} // Call nbl.l() with the remaining elements of the original array + if(handler){ handler();} // Call the callback function l + }; + type = addList[0].match(/\.([cjs]{2,4})$|\?.+/i); + type = (type) ? type[1] : "i"; + if(queue[name] === true) { + // don't readd script if script already added + return loaded(); + } + tag = queue[name] = document.createElement(k[type].tagname); + tag.setAttribute(k[type].attr, addList[0]); + // Fix: CSS links do not fire onload events - Richard Lopes + // Images do. Limitation: no callback function possible after CSS loads + if (k[type].relat) { + tag.setAttribute("rel", k[type].relat); + loaded(); + } else { + // When this script completes loading, it will trigger a callback function consisting of two things: + // 1. It will call nbl.l() with the remaining items in u[1] (if there are any) + // 2. It will execute the function l (if it is a function) + tag.onload = tag.onreadystatechange = function(){ + var s = this; + console.log(this.readyState); + if ( !s.readyState || /de|te/.test( s.readyState ) ) { + s.onload = (s.onreadystatechange = 0); + loaded(); // On completion execute the callback function as defined above + pendingCount--; + } + }; + pendingCount++; + } + head.appendChild(tag); // Add the script to the document + }; + window["nbl"] = {'l':load}; + load(); +}(document)); diff --git a/nbl.plus.min.js b/nbl.plus.min.js index a917268..586297c 100644 --- a/nbl.plus.min.js +++ b/nbl.plus.min.js @@ -1 +1 @@ -(function(h){function m(c){var b,d,a,e,g=0;a=0;j||(i=f=0,j=setInterval(function(){if(0>k||!i)j=clearInterval(j),0 Date: Wed, 6 Jun 2012 15:04:45 +0430 Subject: [PATCH 07/10] New syntax for NBL 3 --- nbl.plus.js~ | 138 --------------------------------------------------- 1 file changed, 138 deletions(-) delete mode 100644 nbl.plus.js~ diff --git a/nbl.plus.js~ b/nbl.plus.js~ deleted file mode 100644 index de3d0ae..0000000 --- a/nbl.plus.js~ +++ /dev/null @@ -1,138 +0,0 @@ -/* - * NBL: Non Blocking Lazy loader v2.0 - * Copyright (c) 2010 Berklee. Additional input from Knowlecules. CSS callback bug fix, additional code reduction by Richard Lopes. - * Licensed under the MIT license. - * - * Date: 2010-09-24 - */ - -// ==ClosureCompiler== -// @output_file_name default.js -// @compilation_level ADVANCED_OPTIMIZATIONS -// ==/ClosureCompiler== - -/*jslint browser: true, white: true, eqeq: true, plusplus: true */ - -/** - * @constructor - */ -(function (document) { - "use strict"; - var queue = {}, // The dictionary that will hold the script-queue - timeout, ticks, - timeoutHandler, - pendingCount, - addScripts, - head = document.head || document.body || document.documentElement, - // The loader function - // - // Called with an array, it will interpret the options array - // Called without an array it will try to load the options from the script-tag's data-nbl attribute - /** - * @param {*=} params - */ - load = function(params) { - var cur_element, next_element, function_type, cur_type, next_type, scripts, handler, i=0,j = 0; - - // The timeout counter, counts backwards every 50ms from 50 ticks (50*50=2500ms by default) - if (!timeout) { - pendingCount = timeoutHandler = 0; // Reset counters: completed, created, timeout function - timeout = setInterval( - function() { - // If the timeout counter dips below zero, or the amount of completed scripts equals the amount - // of created script-tags, we can clear the interval - if (ticks < 0 || !pendingCount) { - timeout = clearInterval(timeout); - // If the amount of completed scripts is smaller than the amount of created script-tags, - // and there is a timeout function available, call it with the current script-queue. - if(pendingCount > 0 && timeoutHandler) {timeoutHandler(queue);} - } - ticks--; - }, - ticks = 50 // Set the initial ticks at 50, as well as the interval at 50ms - ); - } - - // If no arguments were given (a == l, which is null), try to load the options from the script tag - if (!params) { - scripts = document.getElementsByTagName("script"); // Get all script tags in the current document - while (j < scripts.length) { - /*jslint evil:true */ - params = eval("("+scripts[j].getAttribute("data-nbl")+")"); - if (params) { // Check for the data-nbl attribute - head = scripts[j].parentNode; - break; - } - j++; - } - } - - // If an options array was provided, proceed to interpret it - if (params && params.shift) { - while (i < params.length) { // Loop through the options - cur_element = params[i]; // Get the current element - next_element = params[i+1]; // Get the next element - function_type = 'function'; - cur_type = typeof cur_element; - next_type = typeof next_element; - handler = (next_type == function_type) ? - next_element : - (cur_type == function_type) ? cur_element : 0; // Check whether the current or next element is a function and store it - if (cur_type == 'number') {ticks = cur_element/50;} // If the current element is a number, set the timeout interval to this number/50 - // If the current element is a string, call this.addScripts() with the string as a one-element array and the callback function l - if (cur_type == 'string') {addScripts([cur_element], handler);} - // If the current element is an array, call this.addScripts() with a two-element array of the string and the next element - // as well as the callback function l - if(cur_element.shift) { addScripts([cur_element.shift(), cur_element], handler);} - - if (!timeoutHandler && handler) {timeoutHandler = handler;} // Store the function l as the timeout function if it hasn't been set yet - i++; - } - } - }; - addScripts = function(addList, handler) { - /*jslint regexp: true*/ - var tag, type, - name = addList[0].replace(/.+\/|\.min\.js|\.js|\?.+|\W/gi, ''), - k = {'js': {tagname: "script", attr: "src"}, - 'css': {tagname: "link", attr: "href", relat: "stylesheet"}, - 'i': {tagname: "img", attr: "src"}}, // Clean up the name of the script for storage in the queue - loaded = function(){ - var scriptHandler = addList[1]; - queue[name] = true; // Set the entry for this script in the script-queue to true - if(scriptHandler){ load(scriptHandler);} // Call nbl.l() with the remaining elements of the original array - if(handler){ handler();} // Call the callback function l - }; - type = addList[0].match(/\.([cjs]{2,4})$|\?.+/i); - type = (type) ? type[1] : "i"; - if(queue[name] === true) { - // don't readd script if script already added - return loaded(); - } - tag = queue[name] = document.createElement(k[type].tagname); - tag.setAttribute(k[type].attr, addList[0]); - // Fix: CSS links do not fire onload events - Richard Lopes - // Images do. Limitation: no callback function possible after CSS loads - if (k[type].relat) { - tag.setAttribute("rel", k[type].relat); - loaded(); - } else { - // When this script completes loading, it will trigger a callback function consisting of two things: - // 1. It will call nbl.l() with the remaining items in u[1] (if there are any) - // 2. It will execute the function l (if it is a function) - tag.onload = tag.onreadystatechange = function(){ - var s = this; - console.log(this.readyState); - if ( !s.readyState || /de|te/.test( s.readyState ) ) { - s.onload = (s.onreadystatechange = 0); - loaded(); // On completion execute the callback function as defined above - pendingCount--; - } - }; - pendingCount++; - } - head.appendChild(tag); // Add the script to the document - }; - window["nbl"] = {'l':load}; - load(); -}(document)); From 333db4d62bdbb33b38abafcf1e90b5bbfe46c989 Mon Sep 17 00:00:00 2001 From: Taha Jahangir Date: Fri, 8 Jun 2012 19:23:00 +0430 Subject: [PATCH 08/10] Bugfix when scripts has query string part in address --- nbl.plus.js | 2 +- nbl.plus.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nbl.plus.js b/nbl.plus.js index 1cde62f..302da95 100644 --- a/nbl.plus.js +++ b/nbl.plus.js @@ -139,7 +139,7 @@ loadedScripts[name] = true; // add this script to loaded scripts list if(handler){ handler();} // Call the callback function l }; - type = item.match(/\.([cjs]{2,4})$|\?.+/i); + type = item.match(/\.([cjs]{2,4})($|\?)/i); type = type ? type[1] : "i"; if(loadedScripts[name]) { // don't readd script if script already added diff --git a/nbl.plus.min.js b/nbl.plus.min.js index 586297c..4ff29c0 100644 --- a/nbl.plus.min.js +++ b/nbl.plus.min.js @@ -1 +1 @@ -(function(g){function i(a,d){if(!a)for(var b=0,c=g.getElementsByTagName("script");b Date: Thu, 31 Jan 2013 18:42:25 +0330 Subject: [PATCH 09/10] Bugfix in unsettings onreadystatechange in IE6 --- nbl.plus.js | 12 +++++++----- nbl.plus.min.js | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/nbl.plus.js b/nbl.plus.js index 302da95..5bf7be6 100644 --- a/nbl.plus.js +++ b/nbl.plus.js @@ -19,6 +19,7 @@ (function (document) { "use strict"; var loadedScripts = {}, // The dictionary that will hold the loaded script + noop = function(){}, addScript, loadItems, head = document.head || document.body || document.documentElement, // The loader function @@ -65,7 +66,7 @@ } } if (params) { - loadItems(params, callback||function(){}); + loadItems(params, callback||noop); } // If an options array was provided, proceed to interpret it @@ -132,14 +133,14 @@ // remove extension name = item.replace(/.+\/|\.min\.js|\.js|\?.+|\W/gi, ''), // - loadTypes = {'js': {tagname: "script", attr: "src"}, - 'css': {tagname: "link", attr: "href", relat: "stylesheet"}, + loadTypes = {'j': {tagname: "script", attr: "src"}, + 'cs': {tagname: "link", attr: "href", relat: "stylesheet"}, 'i': {tagname: "img", attr: "src"}}, // Clean up the name of the script for storage in the queue loaded = function(){ loadedScripts[name] = true; // add this script to loaded scripts list if(handler){ handler();} // Call the callback function l }; - type = item.match(/\.([cjs]{2,4})($|\?)/i); + type = item.match(/\.(cs|j)s($|\?)/i); type = type ? type[1] : "i"; if(loadedScripts[name]) { // don't readd script if script already added @@ -159,7 +160,8 @@ tag.onload = tag.onreadystatechange = function(){ var s = this; if ( !s.readyState || /de|te/.test( s.readyState ) ) { - s.onload = (s.onreadystatechange = 0); + // setting `onreadystatechange` to 0 raises an error in IE6/7/8 + s.onload = s.onreadystatechange = noop; loaded(); // On completion execute the callback function as defined above } }; diff --git a/nbl.plus.min.js b/nbl.plus.min.js index 4ff29c0..a7da873 100644 --- a/nbl.plus.min.js +++ b/nbl.plus.min.js @@ -1 +1 @@ -(function(g){function i(a,d){if(!a)for(var b=0,c=g.getElementsByTagName("script");b Date: Mon, 29 Apr 2013 10:08:19 +0430 Subject: [PATCH 10/10] Added safe files (disable loading scripts via data attributes) --- nbl.safe.js | 160 ++++++++++++++++++++++++++++++++++++++++++++++++ nbl.safe.min.js | 1 + 2 files changed, 161 insertions(+) create mode 100644 nbl.safe.js create mode 100644 nbl.safe.min.js diff --git a/nbl.safe.js b/nbl.safe.js new file mode 100644 index 0000000..1d852e1 --- /dev/null +++ b/nbl.safe.js @@ -0,0 +1,160 @@ +/* + * NBL: Non Blocking Lazy loader v2.0 + * Copyright (c) 2010 Berklee. Additional input from Knowlecules. CSS callback bug fix, additional code reduction by Richard Lopes. + * Licensed under the MIT license. + * + * Date: 2010-09-24 + */ + +// ==ClosureCompiler== +// @output_file_name default.js +// @compilation_level ADVANCED_OPTIMIZATIONS +// ==/ClosureCompiler== + +/*jslint browser: true, white: true, eqeq: true, plusplus: true */ + +/** + * @constructor + */ +(function (document) { + "use strict"; + var loadedScripts = {}, // The dictionary that will hold the loaded script + noop = function(){}, + addScript, loadItems, + head = document.head || document.body || document.documentElement, + // The loader function + // + // Called with an array, it will interpret the options array + // Called without an array it will try to load the options from the script-tag's data-nbl attribute + /** + * @param {*=} params + * @param {*=} callback + */ + load = function(params, callback) { + //var cur_element, next_element, function_type, cur_type, next_type, scripts, handler, i=0,j = 0; + + // The timeout counter, counts backwards every 50ms from 50 ticks (50*50=2500ms by default) + /*if (!timeout) { + pendingCount = timeoutHandler = 0; // Reset counters: completed, created, timeout function + timeout = setInterval( + function() { + // If the timeout counter dips below zero, or the amount of completed scripts equals the amount + // of created script-tags, we can clear the interval + if (ticks < 0 || !pendingCount) { + timeout = clearInterval(timeout); + // If the amount of completed scripts is smaller than the amount of created script-tags, + // and there is a timeout function available, call it with the current script-queue. + if(pendingCount > 0 && timeoutHandler) {timeoutHandler(loadedScripts);} + } + ticks--; + }, + ticks = 50 // Set the initial ticks at 50, as well as the interval at 50ms + ); + }*/ + + if (params) { + loadItems(params, callback||noop); + } + + // If an options array was provided, proceed to interpret it + /*if (params && params.shift) { + while (i < params.length) { // Loop through the options + cur_element = params[i]; // Get the current element + next_element = params[i+1]; // Get the next element + function_type = 'function'; + cur_type = typeof cur_element; + next_type = typeof next_element; + handler = (next_type == function_type) ? + next_element : + (cur_type == function_type) ? cur_element : 0; // Check whether the current or next element is a function and store it + if (cur_type == 'number') {ticks = cur_element/50;} // If the current element is a number, set the timeout interval to this number/50 + // If the current element is a string, call this.addScripts() with the string as a one-element array and the callback function l + if (cur_type == 'string') {addScripts([cur_element], handler);} + // If the current element is an array, call this.addScripts() with a two-element array of the string and the next element + // as well as the callback function l + if(cur_element.shift) { addScripts([cur_element.shift(), cur_element], handler);} + + if (!timeoutHandler && handler) {timeoutHandler = handler;} // Store the function l as the timeout function if it hasn't been set yet + i++; + } + }*/ + }; + loadItems = function(item, callback){ + var itemType = typeof item; + if (itemType == 'string') { + addScript(item, callback); + } else if (item instanceof Array) { // list + /*jslint vars: true */ + var i, pending = item.length, + loaded = function(){ + if (!--pending) { + callback(); + } + }; + for (i=0; i