1 | /*! LAB.js (LABjs :: Loading And Blocking JavaScript) |
||
2 | v2.0.3 (c) Kyle Simpson |
||
3 | MIT License |
||
4 | */ |
||
5 | |||
6 | (function(global){ |
||
7 | var _$LAB = global.$LAB, |
||
8 | |||
9 | // constants for the valid keys of the options object |
||
10 | _UseLocalXHR = "UseLocalXHR", |
||
11 | _AlwaysPreserveOrder = "AlwaysPreserveOrder", |
||
12 | _AllowDuplicates = "AllowDuplicates", |
||
13 | _CacheBust = "CacheBust", |
||
14 | /*!START_DEBUG*/_Debug = "Debug",/*!END_DEBUG*/ |
||
15 | _BasePath = "BasePath", |
||
16 | |||
17 | // stateless variables used across all $LAB instances |
||
18 | root_page = /^[^?#]*\//.exec(location.href)[0], |
||
19 | root_domain = /^\w+\:\/\/\/?[^\/]+/.exec(root_page)[0], |
||
20 | append_to = document.head || document.getElementsByTagName("head"), |
||
21 | |||
22 | // inferences... ick, but still necessary |
||
23 | opera_or_gecko = (global.opera && Object.prototype.toString.call(global.opera) == "[object Opera]") || ("MozAppearance" in document.documentElement.style), |
||
24 | |||
25 | /*!START_DEBUG*/ |
||
26 | // console.log() and console.error() wrappers |
||
27 | log_msg = function(){}, |
||
28 | log_error = log_msg, |
||
29 | /*!END_DEBUG*/ |
||
30 | |||
31 | // feature sniffs (yay!) |
||
32 | test_script_elem = document.createElement("script"), |
||
33 | explicit_preloading = typeof test_script_elem.preload == "boolean", // http://wiki.whatwg.org/wiki/Script_Execution_Control#Proposal_1_.28Nicholas_Zakas.29 |
||
34 | real_preloading = explicit_preloading || (test_script_elem.readyState && test_script_elem.readyState == "uninitialized"), // will a script preload with `src` set before DOM append? |
||
35 | script_ordered_async = !real_preloading && test_script_elem.async === true, // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order |
||
36 | |||
37 | // XHR preloading (same-domain) and cache-preloading (remote-domain) are the fallbacks (for some browsers) |
||
38 | xhr_or_cache_preloading = !real_preloading && !script_ordered_async && !opera_or_gecko |
||
39 | ; |
||
40 | |||
41 | /*!START_DEBUG*/ |
||
42 | // define console wrapper functions if applicable |
||
43 | if (global.console && global.console.log) { |
||
44 | if (!global.console.error) global.console.error = global.console.log; |
||
45 | log_msg = function(msg) { global.console.log(msg); }; |
||
46 | log_error = function(msg,err) { global.console.error(msg,err); }; |
||
47 | } |
||
48 | /*!END_DEBUG*/ |
||
49 | |||
50 | // test for function |
||
51 | function is_func(func) { return Object.prototype.toString.call(func) == "[object Function]"; } |
||
52 | |||
53 | // test for array |
||
54 | function is_array(arr) { return Object.prototype.toString.call(arr) == "[object Array]"; } |
||
55 | |||
56 | // make script URL absolute/canonical |
||
57 | function canonical_uri(src,base_path) { |
||
58 | var absolute_regex = /^\w+\:\/\//; |
||
59 | |||
60 | // is `src` is protocol-relative (begins with // or ///), prepend protocol |
||
61 | if (/^\/\/\/?/.test(src)) { |
||
62 | src = location.protocol + src; |
||
63 | } |
||
64 | // is `src` page-relative? (not an absolute URL, and not a domain-relative path, beginning with /) |
||
65 | else if (!absolute_regex.test(src) && src.charAt(0) != "/") { |
||
66 | // prepend `base_path`, if any |
||
67 | src = (base_path || "") + src; |
||
68 | } |
||
69 | // make sure to return `src` as absolute |
||
70 | return absolute_regex.test(src) ? src : ((src.charAt(0) == "/" ? root_domain : root_page) + src); |
||
71 | } |
||
72 | |||
73 | // merge `source` into `target` |
||
74 | function merge_objs(source,target) { |
||
75 | for (var k in source) { if (source.hasOwnProperty(k)) { |
||
76 | target[k] = source[k]; // TODO: does this need to be recursive for our purposes? |
||
77 | }} |
||
78 | return target; |
||
79 | } |
||
80 | |||
81 | // does the chain group have any ready-to-execute scripts? |
||
82 | function check_chain_group_scripts_ready(chain_group) { |
||
83 | var any_scripts_ready = false; |
||
84 | for (var i=0; i<chain_group.scripts.length; i++) { |
||
85 | if (chain_group.scripts[i].ready && chain_group.scripts[i].exec_trigger) { |
||
86 | any_scripts_ready = true; |
||
87 | chain_group.scripts[i].exec_trigger(); |
||
88 | chain_group.scripts[i].exec_trigger = null; |
||
89 | } |
||
90 | } |
||
91 | return any_scripts_ready; |
||
92 | } |
||
93 | |||
94 | // creates a script load listener |
||
95 | function create_script_load_listener(elem,registry_item,flag,onload) { |
||
96 | elem.onload = elem.onreadystatechange = function() { |
||
97 | if ((elem.readyState && elem.readyState != "complete" && elem.readyState != "loaded") || registry_item[flag]) return; |
||
98 | elem.onload = elem.onreadystatechange = null; |
||
99 | onload(); |
||
100 | }; |
||
101 | } |
||
102 | |||
103 | // script executed handler |
||
104 | function script_executed(registry_item) { |
||
105 | registry_item.ready = registry_item.finished = true; |
||
106 | for (var i=0; i<registry_item.finished_listeners.length; i++) { |
||
107 | registry_item.finished_listeners[i](); |
||
108 | } |
||
109 | registry_item.ready_listeners = []; |
||
110 | registry_item.finished_listeners = []; |
||
111 | } |
||
112 | |||
113 | // make the request for a scriptha |
||
114 | function request_script(chain_opts,script_obj,registry_item,onload,preload_this_script) { |
||
115 | // setTimeout() "yielding" prevents some weird race/crash conditions in older browsers |
||
116 | setTimeout(function(){ |
||
117 | var script, src = script_obj.real_src, xhr; |
||
118 | |||
119 | // don't proceed until `append_to` is ready to append to |
||
120 | if ("item" in append_to) { // check if `append_to` ref is still a live node list |
||
121 | if (!append_to[0]) { // `append_to` node not yet ready |
||
122 | // try again in a little bit -- note: will re-call the anonymous function in the outer setTimeout, not the parent `request_script()` |
||
123 | setTimeout(arguments.callee,25); |
||
124 | return; |
||
125 | } |
||
126 | // reassign from live node list ref to pure node ref -- avoids nasty IE bug where changes to DOM invalidate live node lists |
||
127 | append_to = append_to[0]; |
||
128 | } |
||
129 | script = document.createElement("script"); |
||
130 | if (script_obj.type) script.type = script_obj.type; |
||
131 | if (script_obj.charset) script.charset = script_obj.charset; |
||
132 | |||
133 | // should preloading be used for this script? |
||
134 | if (preload_this_script) { |
||
135 | // real script preloading? |
||
136 | if (real_preloading) { |
||
137 | /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload: "+src);/*!END_DEBUG*/ |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
138 | registry_item.elem = script; |
||
139 | if (explicit_preloading) { // explicit preloading (aka, Zakas' proposal) |
||
140 | script.preload = true; |
||
141 | script.onpreload = onload; |
||
142 | } |
||
143 | else { |
||
144 | script.onreadystatechange = function(){ |
||
145 | if (script.readyState == "loaded") onload(); |
||
146 | }; |
||
147 | } |
||
148 | script.src = src; |
||
149 | // NOTE: no append to DOM yet, appending will happen when ready to execute |
||
150 | } |
||
151 | // same-domain and XHR allowed? use XHR preloading |
||
152 | else if (preload_this_script && src.indexOf(root_domain) == 0 && chain_opts[_UseLocalXHR]) { |
||
153 | xhr = new XMLHttpRequest(); // note: IE never uses XHR (it supports true preloading), so no more need for ActiveXObject fallback for IE <= 7 |
||
154 | /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload (xhr): "+src);/*!END_DEBUG*/ |
||
155 | xhr.onreadystatechange = function() { |
||
156 | if (xhr.readyState == 4) { |
||
157 | xhr.onreadystatechange = function(){}; // fix a memory leak in IE |
||
158 | registry_item.text = xhr.responseText + "\n//@ sourceURL=" + src; // http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/ |
||
159 | onload(); |
||
160 | } |
||
161 | }; |
||
162 | xhr.open("GET",src); |
||
163 | xhr.send(); |
||
164 | } |
||
165 | // as a last resort, use cache-preloading |
||
166 | else { |
||
167 | /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload (cache): "+src);/*!END_DEBUG*/ |
||
168 | script.type = "text/cache-script"; |
||
169 | create_script_load_listener(script,registry_item,"ready",function() { |
||
170 | append_to.removeChild(script); |
||
171 | onload(); |
||
172 | }); |
||
173 | script.src = src; |
||
174 | append_to.insertBefore(script,append_to.firstChild); |
||
175 | } |
||
176 | } |
||
177 | // use async=false for ordered async? parallel-load-serial-execute http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order |
||
178 | else if (script_ordered_async) { |
||
179 | /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script load (ordered async): "+src);/*!END_DEBUG*/ |
||
180 | script.async = false; |
||
181 | create_script_load_listener(script,registry_item,"finished",onload); |
||
182 | script.src = src; |
||
183 | append_to.insertBefore(script,append_to.firstChild); |
||
184 | } |
||
185 | // otherwise, just a normal script element |
||
186 | else { |
||
187 | /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script load: "+src);/*!END_DEBUG*/ |
||
188 | create_script_load_listener(script,registry_item,"finished",onload); |
||
189 | script.src = src; |
||
190 | append_to.insertBefore(script,append_to.firstChild); |
||
191 | } |
||
192 | },0); |
||
193 | } |
||
194 | |||
195 | // create a clean instance of $LAB |
||
196 | function create_sandbox() { |
||
197 | var global_defaults = {}, |
||
198 | can_use_preloading = real_preloading || xhr_or_cache_preloading, |
||
199 | queue = [], |
||
200 | registry = {}, |
||
201 | instanceAPI |
||
202 | ; |
||
203 | |||
204 | // global defaults |
||
205 | global_defaults[_UseLocalXHR] = true; |
||
206 | global_defaults[_AlwaysPreserveOrder] = false; |
||
207 | global_defaults[_AllowDuplicates] = false; |
||
208 | global_defaults[_CacheBust] = false; |
||
209 | /*!START_DEBUG*/global_defaults[_Debug] = false;/*!END_DEBUG*/ |
||
210 | global_defaults[_BasePath] = ""; |
||
211 | |||
212 | // execute a script that has been preloaded already |
||
213 | function execute_preloaded_script(chain_opts,script_obj,registry_item) { |
||
214 | var script; |
||
215 | |||
216 | function preload_execute_finished() { |
||
217 | if (script != null) { // make sure this only ever fires once |
||
218 | script = null; |
||
219 | script_executed(registry_item); |
||
220 | } |
||
221 | } |
||
222 | |||
223 | if (registry[script_obj.src].finished) return; |
||
224 | if (!chain_opts[_AllowDuplicates]) registry[script_obj.src].finished = true; |
||
225 | |||
226 | script = registry_item.elem || document.createElement("script"); |
||
227 | if (script_obj.type) script.type = script_obj.type; |
||
228 | if (script_obj.charset) script.charset = script_obj.charset; |
||
229 | create_script_load_listener(script,registry_item,"finished",preload_execute_finished); |
||
230 | |||
231 | // script elem was real-preloaded |
||
232 | if (registry_item.elem) { |
||
233 | registry_item.elem = null; |
||
234 | } |
||
235 | // script was XHR preloaded |
||
236 | else if (registry_item.text) { |
||
237 | script.onload = script.onreadystatechange = null; // script injection doesn't fire these events |
||
238 | script.text = registry_item.text; |
||
239 | } |
||
240 | // script was cache-preloaded |
||
241 | else { |
||
242 | script.src = script_obj.real_src; |
||
243 | } |
||
244 | append_to.insertBefore(script,append_to.firstChild); |
||
245 | |||
246 | // manually fire execution callback for injected scripts, since events don't fire |
||
247 | if (registry_item.text) { |
||
248 | preload_execute_finished(); |
||
249 | } |
||
250 | } |
||
251 | |||
252 | // process the script request setup |
||
253 | function do_script(chain_opts,script_obj,chain_group,preload_this_script) { |
||
254 | var registry_item, |
||
255 | registry_items, |
||
256 | ready_cb = function(){ script_obj.ready_cb(script_obj,function(){ execute_preloaded_script(chain_opts,script_obj,registry_item); }); }, |
||
257 | finished_cb = function(){ script_obj.finished_cb(script_obj,chain_group); } |
||
258 | ; |
||
259 | |||
260 | script_obj.src = canonical_uri(script_obj.src,chain_opts[_BasePath]); |
||
261 | script_obj.real_src = script_obj.src + |
||
262 | // append cache-bust param to URL? |
||
263 | (chain_opts[_CacheBust] ? ((/\?.*$/.test(script_obj.src) ? "&_" : "?_") + ~~(Math.random()*1E9) + "=") : "") |
||
264 | ; |
||
265 | |||
266 | if (!registry[script_obj.src]) registry[script_obj.src] = {items:[],finished:false}; |
||
267 | registry_items = registry[script_obj.src].items; |
||
268 | |||
269 | // allowing duplicates, or is this the first recorded load of this script? |
||
270 | if (chain_opts[_AllowDuplicates] || registry_items.length == 0) { |
||
271 | registry_item = registry_items[registry_items.length] = { |
||
272 | ready:false, |
||
273 | finished:false, |
||
274 | ready_listeners:[ready_cb], |
||
275 | finished_listeners:[finished_cb] |
||
276 | }; |
||
277 | |||
278 | request_script(chain_opts,script_obj,registry_item, |
||
279 | // which callback type to pass? |
||
280 | ( |
||
281 | (preload_this_script) ? // depends on script-preloading |
||
282 | function(){ |
||
283 | registry_item.ready = true; |
||
284 | for (var i=0; i<registry_item.ready_listeners.length; i++) { |
||
285 | registry_item.ready_listeners[i](); |
||
286 | } |
||
287 | registry_item.ready_listeners = []; |
||
288 | } : |
||
289 | function(){ script_executed(registry_item); } |
||
290 | ), |
||
291 | // signal if script-preloading should be used or not |
||
292 | preload_this_script |
||
293 | ); |
||
294 | } |
||
295 | else { |
||
296 | registry_item = registry_items[0]; |
||
297 | if (registry_item.finished) { |
||
298 | finished_cb(); |
||
299 | } |
||
300 | else { |
||
301 | registry_item.finished_listeners.push(finished_cb); |
||
302 | } |
||
303 | } |
||
304 | } |
||
305 | |||
306 | // creates a closure for each separate chain spawned from this $LAB instance, to keep state cleanly separated between chains |
||
307 | function create_chain() { |
||
308 | var chainedAPI, |
||
309 | chain_opts = merge_objs(global_defaults,{}), |
||
310 | chain = [], |
||
311 | exec_cursor = 0, |
||
312 | scripts_currently_loading = false, |
||
313 | group |
||
314 | ; |
||
315 | |||
316 | // called when a script has finished preloading |
||
317 | function chain_script_ready(script_obj,exec_trigger) { |
||
318 | /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("script preload finished: "+script_obj.real_src);/*!END_DEBUG*/ |
||
0 ignored issues
–
show
|
|||
319 | script_obj.ready = true; |
||
320 | script_obj.exec_trigger = exec_trigger; |
||
321 | advance_exec_cursor(); // will only check for 'ready' scripts to be executed |
||
322 | } |
||
323 | |||
324 | // called when a script has finished executing |
||
325 | function chain_script_executed(script_obj,chain_group) { |
||
326 | /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("script execution finished: "+script_obj.real_src);/*!END_DEBUG*/ |
||
0 ignored issues
–
show
|
|||
327 | script_obj.ready = script_obj.finished = true; |
||
328 | script_obj.exec_trigger = null; |
||
329 | // check if chain group is all finished |
||
330 | for (var i=0; i<chain_group.scripts.length; i++) { |
||
331 | if (!chain_group.scripts[i].finished) return; |
||
332 | } |
||
333 | // chain_group is all finished if we get this far |
||
334 | chain_group.finished = true; |
||
335 | advance_exec_cursor(); |
||
336 | } |
||
337 | |||
338 | // main driver for executing each part of the chain |
||
339 | function advance_exec_cursor() { |
||
340 | while (exec_cursor < chain.length) { |
||
0 ignored issues
–
show
|
|||
341 | if (is_func(chain[exec_cursor])) { |
||
342 | /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("$LAB.wait() executing: "+chain[exec_cursor]);/*!END_DEBUG*/ |
||
0 ignored issues
–
show
|
|||
343 | try { chain[exec_cursor++](); } catch (err) { |
||
344 | /*!START_DEBUG*/if (chain_opts[_Debug]) log_error("$LAB.wait() error caught: ",err);/*!END_DEBUG*/ |
||
345 | } |
||
346 | continue; |
||
347 | } |
||
348 | else if (!chain[exec_cursor].finished) { |
||
349 | if (check_chain_group_scripts_ready(chain[exec_cursor])) continue; |
||
350 | break; |
||
351 | } |
||
352 | exec_cursor++; |
||
353 | } |
||
354 | // we've reached the end of the chain (so far) |
||
355 | if (exec_cursor == chain.length) { |
||
356 | scripts_currently_loading = false; |
||
357 | group = false; |
||
358 | } |
||
359 | } |
||
360 | |||
361 | // setup next chain script group |
||
362 | function init_script_chain_group() { |
||
363 | if (!group || !group.scripts) { |
||
364 | chain.push(group = {scripts:[],finished:true}); |
||
365 | } |
||
366 | } |
||
367 | |||
368 | // API for $LAB chains |
||
369 | chainedAPI = { |
||
370 | // start loading one or more scripts |
||
371 | script:function(){ |
||
372 | for (var i=0; i<arguments.length; i++) { |
||
373 | (function(script_obj,script_list){ |
||
374 | var splice_args; |
||
375 | |||
376 | if (!is_array(script_obj)) { |
||
377 | script_list = [script_obj]; |
||
378 | } |
||
379 | for (var j=0; j<script_list.length; j++) { |
||
380 | init_script_chain_group(); |
||
381 | script_obj = script_list[j]; |
||
382 | |||
383 | if (is_func(script_obj)) script_obj = script_obj(); |
||
384 | if (!script_obj) continue; |
||
385 | if (is_array(script_obj)) { |
||
386 | // set up an array of arguments to pass to splice() |
||
387 | splice_args = [].slice.call(script_obj); // first include the actual array elements we want to splice in |
||
388 | splice_args.unshift(j,1); // next, put the `index` and `howMany` parameters onto the beginning of the splice-arguments array |
||
389 | [].splice.apply(script_list,splice_args); // use the splice-arguments array as arguments for splice() |
||
390 | j--; // adjust `j` to account for the loop's subsequent `j++`, so that the next loop iteration uses the same `j` index value |
||
0 ignored issues
–
show
|
|||
391 | continue; |
||
392 | } |
||
393 | if (typeof script_obj == "string") script_obj = {src:script_obj}; |
||
394 | script_obj = merge_objs(script_obj,{ |
||
395 | ready:false, |
||
396 | ready_cb:chain_script_ready, |
||
397 | finished:false, |
||
398 | finished_cb:chain_script_executed |
||
399 | }); |
||
400 | group.finished = false; |
||
401 | group.scripts.push(script_obj); |
||
402 | |||
403 | do_script(chain_opts,script_obj,group,(can_use_preloading && scripts_currently_loading)); |
||
0 ignored issues
–
show
|
|||
404 | scripts_currently_loading = true; |
||
405 | |||
406 | if (chain_opts[_AlwaysPreserveOrder]) chainedAPI.wait(); |
||
407 | } |
||
408 | })(arguments[i],arguments[i]); |
||
409 | } |
||
410 | return chainedAPI; |
||
411 | }, |
||
412 | // force LABjs to pause in execution at this point in the chain, until the execution thus far finishes, before proceeding |
||
413 | wait:function(){ |
||
414 | if (arguments.length > 0) { |
||
415 | for (var i=0; i<arguments.length; i++) { |
||
416 | chain.push(arguments[i]); |
||
417 | } |
||
418 | group = chain[chain.length-1]; |
||
419 | } |
||
420 | else group = false; |
||
421 | |||
422 | advance_exec_cursor(); |
||
423 | |||
424 | return chainedAPI; |
||
425 | } |
||
426 | }; |
||
427 | |||
428 | // the first chain link API (includes `setOptions` only this first time) |
||
429 | return { |
||
430 | script:chainedAPI.script, |
||
431 | wait:chainedAPI.wait, |
||
432 | setOptions:function(opts){ |
||
433 | merge_objs(opts,chain_opts); |
||
434 | return chainedAPI; |
||
435 | } |
||
436 | }; |
||
437 | } |
||
438 | |||
439 | // API for each initial $LAB instance (before chaining starts) |
||
440 | instanceAPI = { |
||
441 | // main API functions |
||
442 | setGlobalDefaults:function(opts){ |
||
443 | merge_objs(opts,global_defaults); |
||
444 | return instanceAPI; |
||
445 | }, |
||
446 | setOptions:function(){ |
||
447 | return create_chain().setOptions.apply(null,arguments); |
||
448 | }, |
||
449 | script:function(){ |
||
450 | return create_chain().script.apply(null,arguments); |
||
451 | }, |
||
452 | wait:function(){ |
||
453 | return create_chain().wait.apply(null,arguments); |
||
454 | }, |
||
455 | |||
456 | // built-in queuing for $LAB `script()` and `wait()` calls |
||
457 | // useful for building up a chain programmatically across various script locations, and simulating |
||
458 | // execution of the chain |
||
459 | queueScript:function(){ |
||
460 | queue[queue.length] = {type:"script", args:[].slice.call(arguments)}; |
||
461 | return instanceAPI; |
||
462 | }, |
||
463 | queueWait:function(){ |
||
464 | queue[queue.length] = {type:"wait", args:[].slice.call(arguments)}; |
||
465 | return instanceAPI; |
||
466 | }, |
||
467 | runQueue:function(){ |
||
468 | var $L = instanceAPI, len=queue.length, i=len, val; |
||
469 | for (;--i>=0;) { |
||
470 | val = queue.shift(); |
||
471 | $L = $L[val.type].apply(null,val.args); |
||
472 | } |
||
473 | return $L; |
||
474 | }, |
||
475 | |||
476 | // rollback `[global].$LAB` to what it was before this file was loaded, the return this current instance of $LAB |
||
477 | noConflict:function(){ |
||
478 | global.$LAB = _$LAB; |
||
479 | return instanceAPI; |
||
480 | }, |
||
481 | |||
482 | // create another clean instance of $LAB |
||
483 | sandbox:function(){ |
||
484 | return create_sandbox(); |
||
485 | } |
||
486 | }; |
||
487 | |||
488 | return instanceAPI; |
||
489 | } |
||
490 | |||
491 | // create the main instance of $LAB |
||
492 | global.$LAB = create_sandbox(); |
||
493 | |||
494 | |||
495 | /* The following "hack" was suggested by Andrea Giammarchi and adapted from: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html |
||
496 | NOTE: this hack only operates in FF and then only in versions where document.readyState is not present (FF < 3.6?). |
||
497 | |||
498 | The hack essentially "patches" the **page** that LABjs is loaded onto so that it has a proper conforming document.readyState, so that if a script which does |
||
499 | proper and safe dom-ready detection is loaded onto a page, after dom-ready has passed, it will still be able to detect this state, by inspecting the now hacked |
||
500 | document.readyState property. The loaded script in question can then immediately trigger any queued code executions that were waiting for the DOM to be ready. |
||
501 | For instance, jQuery 1.4+ has been patched to take advantage of document.readyState, which is enabled by this hack. But 1.3.2 and before are **not** safe or |
||
502 | fixed by this hack, and should therefore **not** be lazy-loaded by script loader tools such as LABjs. |
||
503 | */ |
||
504 | (function(addEvent,domLoaded,handler){ |
||
505 | if (document.readyState == null && document[addEvent]){ |
||
506 | document.readyState = "loading"; |
||
507 | document[addEvent](domLoaded,handler = function(){ |
||
508 | document.removeEventListener(domLoaded,handler,false); |
||
509 | document.readyState = "complete"; |
||
510 | },false); |
||
511 | } |
||
512 | })("addEventListener","DOMContentLoaded"); |
||
513 | |||
514 | })(this); |