|
11 | 11 | _AlwaysPreserveOrder = "AlwaysPreserveOrder",
|
12 | 12 | _AllowDuplicates = "AllowDuplicates",
|
13 | 13 | _CacheBust = "CacheBust",
|
| 14 | + /*!START_DEBUG*/_Debug = "Debug",/*!END_DEBUG*/ |
14 | 15 | _BasePath = "BasePath",
|
15 | 16 |
|
16 | 17 | // stateless variables used across all $LAB instances
|
|
21 | 22 | // inferences... ick, but still necessary
|
22 | 23 | opera_or_gecko = (global.opera && Object.prototype.toString.call(global.opera) == "[object Opera]") || ("MozAppearance" in document.documentElement.style),
|
23 | 24 |
|
| 25 | +/*!START_DEBUG*/ |
| 26 | + // console.log() and console.error() wrappers |
| 27 | + log_msg = function(){}, |
| 28 | + log_error = log_msg, |
| 29 | +/*!END_DEBUG*/ |
| 30 | + |
24 | 31 | // feature sniffs (yay!)
|
25 | 32 | test_script_elem = document.createElement("script"),
|
26 | 33 | script_ordered_async = test_script_elem.async === true, // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
|
27 | 34 | explicit_script_preload = typeof test_script_elem.preload == "boolean", // http://wiki.whatwg.org/wiki/Script_Execution_Control#Proposal_1_.28Nicholas_Zakas.29
|
28 | 35 | script_preload = explicit_script_preload || (test_script_elem.readyState && test_script_elem.readyState == "uninitialized") // will a script preload with `src` set before DOM append?
|
29 | 36 | ;
|
30 |
| - |
| 37 | + |
| 38 | +/*!START_DEBUG*/ |
| 39 | + // define console wrapper functions if applicable |
| 40 | + if (global.console && global.console.log) { |
| 41 | + if (!global.console.error) global.console.error = global.console.log; |
| 42 | + log_msg = function(msg) { global.console.log(msg); }; |
| 43 | + log_error = function(msg,err) { global.console.error(msg,err); }; |
| 44 | + } |
| 45 | +/*!END_DEBUG*/ |
| 46 | + |
31 | 47 | // test for function
|
32 | 48 | function is_func(func) { return Object.prototype.toString.call(func) == "[object Function]"; }
|
33 | 49 |
|
|
113 | 129 |
|
114 | 130 | // make the request for a script
|
115 | 131 | function request_script(chain_opts,script_obj,chain_group,registry_item,onload) {
|
| 132 | + // setTimeout() "yielding" prevents some weird race/crash conditions in older browsers |
116 | 133 | setTimeout(function(){
|
117 |
| - if ("item" in append_to) { // check if ref is still a live node list |
118 |
| - if (!append_to[0]) { // append_to node not yet ready |
119 |
| - setTimeout(arguments.callee,25); // try again in a little bit -- note, will recall the anonymous function in the outer setTimeout, not the parent `request_script()` |
| 134 | + // don't proceed until `append_to` is ready to append to |
| 135 | + if ("item" in append_to) { // check if `append_to` ref is still a live node list |
| 136 | + if (!append_to[0]) { // `append_to` node not yet ready |
| 137 | + // try again in a little bit -- note: will re-call the anonymous function in the outer setTimeout, not the parent `request_script()` |
| 138 | + setTimeout(arguments.callee,25); |
120 | 139 | return;
|
121 | 140 | }
|
122 |
| - append_to = append_to[0]; // reassign from live node list ref to pure node ref -- avoids nasty IE bug where changes to DOM invalidate live node lists |
| 141 | + // reassign from live node list ref to pure node ref -- avoids nasty IE bug where changes to DOM invalidate live node lists |
| 142 | + append_to = append_to[0]; |
123 | 143 | }
|
124 | 144 | var script = document.createElement("script"),
|
125 |
| - src = script_obj.real_src = script_obj.src + (chain_opts[_CacheBust] ? (/\?.*$/.test(script_obj.src) ? "&_" : "?_") + ~~(Math.random()*1E9) + "=" : "") |
| 145 | + src = script_obj.real_src |
126 | 146 | ;
|
127 | 147 | if (script_obj.type) script.type = script_obj.type;
|
128 | 148 | if (script_obj.charset) script.charset = script_obj.charset;
|
129 | 149 |
|
130 | 150 | // no preloading, just normal script element
|
131 | 151 | if (!chain_group.preload && !script_ordered_async) {
|
| 152 | + /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script load: "+src);/*!END_DEBUG*/ |
132 | 153 | if (script_ordered_async) script.async = false;
|
133 | 154 | create_script_load_listener(script,registry_item,"finished",onload);
|
134 | 155 | script.src = src;
|
135 | 156 | append_to.insertBefore(script,append_to.firstChild);
|
136 | 157 | }
|
137 | 158 | // real script preloading
|
138 | 159 | else if (script_preload) {
|
| 160 | + /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload: "+src);/*!END_DEBUG*/ |
139 | 161 | registry_item.elem = script;
|
140 | 162 | if (explicit_script_preload) { // Zakas style preloading (aka, explicit preloading)
|
141 | 163 | script.preload = true;
|
|
150 | 172 | script.src = src;
|
151 | 173 | // NOTE: no append to DOM yet, appending will happen when ready to execute
|
152 | 174 | }
|
153 |
| - // use async=false parallel-load-serial-execute |
| 175 | + // use async=false parallel-load-serial-execute http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order |
154 | 176 | else if (script_ordered_async) {
|
| 177 | + /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script load (ordered async): "+src);/*!END_DEBUG*/ |
155 | 178 | script.async = false;
|
156 | 179 | create_script_load_listener(script,registry_item,"finished",onload);
|
157 | 180 | script.src = src;
|
|
164 | 187 | global_defaults[_UseLocalXHR] = chain_opts[_UseLocalXHR] = false; // can't use XHR for some reason, so don't try anymore
|
165 | 188 | return request_script(chain_opts,registry_item,onload);
|
166 | 189 | }
|
| 190 | + /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload (xhr): "+src);/*!END_DEBUG*/ |
167 | 191 | xhr.onreadystatechange = function() {
|
168 |
| - if (xhr.readyState === 4) { |
| 192 | + if (xhr.readyState == 4) { |
169 | 193 | xhr.onreadystatechange = function(){}; // fix a memory leak in IE
|
170 | 194 | registry_item.text = xhr.responseText + "\n//@ sourceURL=" + src; // http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
|
171 | 195 | onload();
|
172 | 196 | }
|
173 | 197 | };
|
174 | 198 | xhr.open("GET",src);
|
175 |
| - xhr.send(""); |
| 199 | + xhr.send(); |
176 | 200 | }
|
177 | 201 | // as a last resort, use cache-preloading
|
178 | 202 | else {
|
| 203 | + /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload (cache): "+src);/*!END_DEBUG*/ |
179 | 204 | script.type = "text/cache-script";
|
180 | 205 | create_script_load_listener(script,registry_item,"ready",function() {
|
181 | 206 | append_to.removeChild(script);
|
|
195 | 220 | registry = {},
|
196 | 221 | instanceAPI
|
197 | 222 | ;
|
198 |
| - |
| 223 | + |
199 | 224 | // global defaults
|
200 | 225 | global_defaults[_UseLocalXHR] = true;
|
201 | 226 | global_defaults[_AlwaysPreserveOrder] = false;
|
202 | 227 | global_defaults[_AllowDuplicates] = false;
|
203 | 228 | global_defaults[_CacheBust] = false;
|
| 229 | + /*!START_DEBUG*/global_defaults[_Debug] = false;/*!END_DEBUG*/ |
204 | 230 | global_defaults[_BasePath] = "";
|
205 | 231 |
|
206 | 232 | // execute a script that has been preloaded already
|
|
248 | 274 | ;
|
249 | 275 |
|
250 | 276 | script_obj.src = canonical_uri(script_obj.src,chain_opts[_BasePath]);
|
| 277 | + script_obj.real_src = script_obj.src + |
| 278 | + // append cache-bust param to URL? |
| 279 | + (chain_opts[_CacheBust] ? (/\?.*$/.test(script_obj.src) ? "&_" : "?_") + ~~(Math.random()*1E9) + "=" : ""); |
251 | 280 |
|
252 | 281 | if (!registry[script_obj.src]) registry[script_obj.src] = {items:[],finished:false};
|
253 | 282 | registry_items = registry[script_obj.src].items;
|
|
295 | 324 |
|
296 | 325 | // called when a script has finished preloading
|
297 | 326 | function chain_script_ready(script_obj,chain_group,exec_trigger) {
|
| 327 | + /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("script preload finished: "+script_obj.real_src);/*!END_DEBUG*/ |
298 | 328 | script_obj.ready = true;
|
299 | 329 | script_obj.exec_trigger = exec_trigger;
|
300 | 330 | advance_exec_cursor(); // will only check for 'ready' scripts to be executed
|
301 | 331 | }
|
302 | 332 |
|
303 | 333 | // called when a script has finished executing
|
304 | 334 | function chain_script_executed(script_obj,chain_group) {
|
| 335 | + /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("script execution finished: "+script_obj.real_src);/*!END_DEBUG*/ |
305 | 336 | script_obj.ready = script_obj.finished = true;
|
306 | 337 | script_obj.exec_trigger = null;
|
307 | 338 | if (check_chain_group_complete(chain_group)) {
|
|
313 | 344 | function advance_exec_cursor() {
|
314 | 345 | while (exec_cursor < chain.length) {
|
315 | 346 | if (is_func(chain[exec_cursor])) {
|
316 |
| - try { chain[exec_cursor](); } catch (err) { } // TODO: wire up error logging |
| 347 | + /*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("$LAB.wait() executing: "+chain[exec_cursor]);/*!END_DEBUG*/ |
| 348 | + try { chain[exec_cursor](); } catch (err) { |
| 349 | + /*!START_DEBUG*/if (chain_opts[_Debug]) log_error("$LAB.wait() error caught: ",err);/*!END_DEBUG*/ |
| 350 | + } |
317 | 351 | }
|
318 | 352 | else if (!chain[exec_cursor].finished) {
|
319 | 353 | if (check_chain_group_scripts_ready(chain[exec_cursor])) continue;
|
|
335 | 369 | }
|
336 | 370 | }
|
337 | 371 |
|
| 372 | + // API for $LAB chains |
338 | 373 | chainedAPI = {
|
| 374 | + // start loading one or more scripts |
339 | 375 | script:function(){
|
340 | 376 | init_script_chain_group();
|
341 | 377 | scripts_currently_loading = true;
|
|
374 | 410 | }
|
375 | 411 | return chainedAPI;
|
376 | 412 | },
|
| 413 | + // force LABjs to pause in execution at this point in the chain, until the execution thus far finishes, before proceeding |
377 | 414 | wait:function(){
|
378 | 415 | if (arguments.length > 0) {
|
379 | 416 | for (var i=0; i<arguments.length; i++) {
|
|
389 | 426 | }
|
390 | 427 | };
|
391 | 428 |
|
| 429 | + // the first chain link API (includes `setOptions` only this first time) |
392 | 430 | return {
|
393 | 431 | script:chainedAPI.script,
|
394 | 432 | wait:chainedAPI.wait,
|
|
399 | 437 | };
|
400 | 438 | }
|
401 | 439 |
|
| 440 | + // API for each initial $LAB instance (before chaining starts) |
402 | 441 | instanceAPI = {
|
| 442 | + // main API functions |
403 | 443 | setGlobalDefaults:function(opts){
|
404 | 444 | merge_objs(opts,global_defaults);
|
405 | 445 | return instanceAPI;
|
|
413 | 453 | wait:function(){
|
414 | 454 | return create_chain().wait.apply(null,arguments);
|
415 | 455 | },
|
| 456 | + |
416 | 457 | // built-in queuing for $LAB `script()` and `wait()` calls
|
417 | 458 | // useful for building up a chain programmatically across various script locations, and simulating
|
418 | 459 | // execution of the chain
|
|
432 | 473 | }
|
433 | 474 | return $L;
|
434 | 475 | },
|
| 476 | + |
435 | 477 | // rollback `[global].$LAB` to what it was before this file was loaded, the return this current instance of $LAB
|
436 | 478 | noConflict:function(){
|
437 | 479 | global.$LAB = _$LAB;
|
438 | 480 | return instanceAPI;
|
439 | 481 | },
|
| 482 | + |
440 | 483 | // create another clean instance of $LAB
|
441 | 484 | sandbox:function(){
|
442 | 485 | return create_sandbox();
|
|
0 commit comments