|
495
|
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*/ |
|
|
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*/ |
|
|
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*/ |
|
|
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) { |
|
|
341 |
if (is_func(chain[exec_cursor])) { |
|
|
342 |
/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("$LAB.wait() executing: "+chain[exec_cursor]);/*!END_DEBUG*/ |
|
|
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 |
|
|
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)); |
|
|
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); |