function create_chain() {
			var chainedAPI,
				chain_opts = merge_objs(global_defaults,{}),
				chain = [],
				exec_cursor = 0,
				scripts_currently_loading = false,
				chain_is_corked = false,
				group
			;

			// called when a script has finished preloading
			function chain_script_ready(script_obj,exec_trigger) {
				if (chain_opts[_Debug]) log_msg("script preload finished: "+script_obj.real_src);
				script_obj.ready = true;
				script_obj.exec_trigger = function() {
					if (chain_opts[_Debug]) log_msg("script execute start: "+script_obj.real_src);
					exec_trigger();
				}
				advance_exec_cursor(); // will only check for 'ready' scripts to be executed
			}

			// called when a script has finished executing
			function chain_script_executed(script_obj,chain_group) {
				if (chain_opts[_Debug]) log_msg("script execution finished: "+script_obj.real_src);
				script_obj.ready = script_obj.finished = true;
				script_obj.exec_trigger = null;
				// check if chain group is all finished
				for (var i=0; i<chain_group.scripts.length; i++) {
					if (!chain_group.scripts[i].finished) return;
				}
				// chain_group is all finished if we get this far
				chain_group.finished = true;
				advance_exec_cursor();
			}

			// main driver for executing each part of the chain
			function advance_exec_cursor() {
				if (chain_is_corked) return;
				while (exec_cursor < chain.length) {
					if (is_func(chain[exec_cursor])) {
						if (chain_opts[_Debug]) log_msg("$LAB.wait() executing: "+chain[exec_cursor]);
						try { chain[exec_cursor++](); } catch (err) {
							if (chain_opts[_Debug]) log_error("$LAB.wait() error caught: ",err);
						}
						continue;
					}
					else if (!chain[exec_cursor].finished) {
						if (check_chain_group_scripts_ready(chain[exec_cursor])) continue;
						break;
					}
					exec_cursor++;
				}
				// we've reached the end of the chain (so far)
				if (exec_cursor == chain.length) {
					scripts_currently_loading = false;
					group = false;
				}
			}

			// setup next chain script group
			function init_script_chain_group() {
				if (!group || !group.scripts) {
					chain.push(group = {scripts:[],finished:true});
				}
			}

			// API for $LAB chains
			chainedAPI = {
				// start loading one or more scripts
				script:function(){
					for (var i=0; i<arguments.length; i++) {
						(function(script_obj,script_list){
							var splice_args;

							if (!is_array(script_obj)) {
								script_list = [script_obj];
							}
							for (var j=0; j<script_list.length; j++) {
								init_script_chain_group();
								script_obj = script_list[j];

								if (is_func(script_obj)) script_obj = script_obj();
								if (!script_obj) continue;
								if (is_array(script_obj)) {
									// set up an array of arguments to pass to splice()
									splice_args = [].slice.call(script_obj); // first include the actual array elements we want to splice in
									splice_args.unshift(j,1); // next, put the `index` and `howMany` parameters onto the beginning of the splice-arguments array
									[].splice.apply(script_list,splice_args); // use the splice-arguments array as arguments for splice()
									j--; // adjust `j` to account for the loop's subsequent `j++`, so that the next loop iteration uses the same `j` index value
									continue;
								}
								if (typeof script_obj == "string") script_obj = {src:script_obj};
								script_obj = merge_objs(script_obj,{
									ready:false,
									ready_cb:chain_script_ready,
									finished:false,
									finished_cb:chain_script_executed
								});
								group.finished = false;
								group.scripts.push(script_obj);
								do_script(chain_opts,script_obj,group,(
									(can_use_preloading && scripts_currently_loading) || chain_is_corked
								));
								scripts_currently_loading = true;

								if (chain_opts[_AlwaysPreserveOrder]) chainedAPI.wait();
							}
						})(arguments[i],arguments[i]);
					}
					return chainedAPI;
				},
				// force LABjs to pause in execution at this point in the chain, until the execution thus far finishes, before proceeding
				wait:function(){
					if (arguments.length > 0) {
						for (var i=0; i<arguments.length; i++) {
							chain.push(arguments[i]);
						}
						group = chain[chain.length-1];
					}
					else group = false;

					advance_exec_cursor();

					return chainedAPI;
				},
				cork:function(){
					if (chain_opts[_Debug]) log_msg("$LAB.cork()");
					chain_is_corked = true;
					return chainedAPI;
				},
				uncork:function(){
					if (chain_opts[_Debug]) log_msg("$LAB.uncork()");
					if (chain_is_corked) {
						chain_is_corked = false;
						advance_exec_cursor();
					}
					return chainedAPI;
				}
			};

			// the first chain link API (includes `setOptions` only this first time)
			return {
				script:chainedAPI.script,
				wait:chainedAPI.wait,
				cork:chainedAPI.cork,
				uncork:chainedAPI.uncork,
				setOptions:function(opts){
					merge_objs(opts,chain_opts);
					return chainedAPI;
				}
			};
		}
function create_chain() {
			var chainedAPI,
				chain_opts = merge_objs(global_defaults,{}),
				chain = [],
				exec_cursor = 0,
				scripts_currently_loading = false,
				chain_is_corked = false,
				group
			;

			
			function chain_script_ready(script_obj,exec_trigger) {
				if (chain_opts[_Debug]) log_msg("script preload finished: "+script_obj.real_src);
				script_obj.ready = true;
				script_obj.exec_trigger = function() {
					if (chain_opts[_Debug]) log_msg("script execute start: "+script_obj.real_src);
					exec_trigger();
				}
				advance_exec_cursor(); 
			}

			
			function chain_script_executed(script_obj,chain_group) {
				if (chain_opts[_Debug]) log_msg("script execution finished: "+script_obj.real_src);
				script_obj.ready = script_obj.finished = true;
				script_obj.exec_trigger = null;
				
				for (var i=0; i<chain_group.scripts.length; i++) {
					if (!chain_group.scripts[i].finished) return;
				}
				
				chain_group.finished = true;
				advance_exec_cursor();
			}

			
			function advance_exec_cursor() {
				if (chain_is_corked) return;
				while (exec_cursor < chain.length) {
					if (is_func(chain[exec_cursor])) {
						if (chain_opts[_Debug]) log_msg("$LAB.wait() executing: "+chain[exec_cursor]);
						try { chain[exec_cursor++](); } catch (err) {
							if (chain_opts[_Debug]) log_error("$LAB.wait() error caught: ",err);
						}
						continue;
					}
					else if (!chain[exec_cursor].finished) {
						if (check_chain_group_scripts_ready(chain[exec_cursor])) continue;
						break;
					}
					exec_cursor++;
				}
				
				if (exec_cursor == chain.length) {
					scripts_currently_loading = false;
					group = false;
				}
			}

			
			function init_script_chain_group() {
				if (!group || !group.scripts) {
					chain.push(group = {scripts:[],finished:true});
				}
			}

			
			chainedAPI = {
				
				script:function(){
					for (var i=0; i<arguments.length; i++) {
						(function(script_obj,script_list){
							var splice_args;

							if (!is_array(script_obj)) {
								script_list = [script_obj];
							}
							for (var j=0; j<script_list.length; j++) {
								init_script_chain_group();
								script_obj = script_list[j];

								if (is_func(script_obj)) script_obj = script_obj();
								if (!script_obj) continue;
								if (is_array(script_obj)) {
									
									splice_args = [].slice.call(script_obj); 
									splice_args.unshift(j,1); 
									[].splice.apply(script_list,splice_args); 
									j--; 
									continue;
								}
								if (typeof script_obj == "string") script_obj = {src:script_obj};
								script_obj = merge_objs(script_obj,{
									ready:false,
									ready_cb:chain_script_ready,
									finished:false,
									finished_cb:chain_script_executed
								});
								group.finished = false;
								group.scripts.push(script_obj);
								do_script(chain_opts,script_obj,group,(
									(can_use_preloading && scripts_currently_loading) || chain_is_corked
								));
								scripts_currently_loading = true;

								if (chain_opts[_AlwaysPreserveOrder]) chainedAPI.wait();
							}
						})(arguments[i],arguments[i]);
					}
					return chainedAPI;
				},
				
				wait:function(){
					if (arguments.length > 0) {
						for (var i=0; i<arguments.length; i++) {
							chain.push(arguments[i]);
						}
						group = chain[chain.length-1];
					}
					else group = false;

					advance_exec_cursor();

					return chainedAPI;
				},
				cork:function(){
					if (chain_opts[_Debug]) log_msg("$LAB.cork()");
					chain_is_corked = true;
					return chainedAPI;
				},
				uncork:function(){
					if (chain_opts[_Debug]) log_msg("$LAB.uncork()");
					if (chain_is_corked) {
						chain_is_corked = false;
						advance_exec_cursor();
					}
					return chainedAPI;
				}
			};

			
			return {
				script:chainedAPI.script,
				wait:chainedAPI.wait,
				cork:chainedAPI.cork,
				uncork:chainedAPI.uncork,
				setOptions:function(opts){
					merge_objs(opts,chain_opts);
					return chainedAPI;
				}
			};
		}
function create_chain ( ) { var chainedAPI , chain_opts = merge_objs ( global_defaults , { } ) , chain = [ ] , exec_cursor = 0 , scripts_currently_loading = false , chain_is_corked = false , group ; // called when a script has finished preloading function chain_script_ready ( script_obj , exec_trigger ) { if ( chain_opts [ _Debug ] ) log_msg ( "script preload finished: " + script_obj . real_src ) ; script_obj . ready = true ; script_obj . exec_trigger = function ( ) { if ( chain_opts [ _Debug ] ) log_msg ( "script execute start: " + script_obj . real_src ) ; exec_trigger ( ) ; } advance_exec_cursor ( ) ; // will only check for 'ready' scripts to be executed } // called when a script has finished executing function chain_script_executed ( script_obj , chain_group ) { if ( chain_opts [ _Debug ] ) log_msg ( "script execution finished: " + script_obj . real_src ) ; script_obj . ready = script_obj . finished = true ; script_obj . exec_trigger = null ; // check if chain group is all finished for ( var i = 0 ; i < chain_group . scripts . length ; i ++ ) { if ( ! chain_group . scripts [ i ] . finished ) return ; } // chain_group is all finished if we get this far chain_group . finished = true ; advance_exec_cursor ( ) ; } // main driver for executing each part of the chain function advance_exec_cursor ( ) { if ( chain_is_corked ) return ; while ( exec_cursor < chain . length ) { if ( is_func ( chain [ exec_cursor ] ) ) { if ( chain_opts [ _Debug ] ) log_msg ( "$LAB.wait() executing: " + chain [ exec_cursor ] ) ; try { chain [ exec_cursor ++ ] ( ) ; } catch ( err ) { if ( chain_opts [ _Debug ] ) log_error ( "$LAB.wait() error caught: " , err ) ; } continue ; } else if ( ! chain [ exec_cursor ] . finished ) { if ( check_chain_group_scripts_ready ( chain [ exec_cursor ] ) ) continue ; break ; } exec_cursor ++ ; } // we've reached the end of the chain (so far) if ( exec_cursor == chain . length ) { scripts_currently_loading = false ; group = false ; } } // setup next chain script group function init_script_chain_group ( ) { if ( ! group || ! group . scripts ) { chain . push ( group = { scripts : [ ] , finished : true } ) ; } } // API for $LAB chains chainedAPI = { // start loading one or more scripts script : function ( ) { for ( var i = 0 ; i < arguments . length ; i ++ ) { ( function ( script_obj , script_list ) { var splice_args ; if ( ! is_array ( script_obj ) ) { script_list = [ script_obj ] ; } for ( var j = 0 ; j < script_list . length ; j ++ ) { init_script_chain_group ( ) ; script_obj = script_list [ j ] ; if ( is_func ( script_obj ) ) script_obj = script_obj ( ) ; if ( ! script_obj ) continue ; if ( is_array ( script_obj ) ) { // set up an array of arguments to pass to splice() splice_args = [ ] . slice . call ( script_obj ) ; // first include the actual array elements we want to splice in splice_args . unshift ( j , 1 ) ; // next, put the `index` and `howMany` parameters onto the beginning of the splice-arguments array [ ] . splice . apply ( script_list , splice_args ) ; // use the splice-arguments array as arguments for splice() j -- ; // adjust `j` to account for the loop's subsequent `j++`, so that the next loop iteration uses the same `j` index value continue ; } if ( typeof script_obj == "string" ) script_obj = { src : script_obj } ; script_obj = merge_objs ( script_obj , { ready : false , ready_cb : chain_script_ready , finished : false , finished_cb : chain_script_executed } ) ; group . finished = false ; group . scripts . push ( script_obj ) ; do_script ( chain_opts , script_obj , group , ( ( can_use_preloading && scripts_currently_loading ) || chain_is_corked ) ) ; scripts_currently_loading = true ; if ( chain_opts [ _AlwaysPreserveOrder ] ) chainedAPI . wait ( ) ; } } ) ( arguments [ i ] , arguments [ i ] ) ; } return chainedAPI ; } , // force LABjs to pause in execution at this point in the chain, until the execution thus far finishes, before proceeding wait : function ( ) { if ( arguments . length > 0 ) { for ( var i = 0 ; i < arguments . length ; i ++ ) { chain . push ( arguments [ i ] ) ; } group = chain [ chain . length - 1 ] ; } else group = false ; advance_exec_cursor ( ) ; return chainedAPI ; } , cork : function ( ) { if ( chain_opts [ _Debug ] ) log_msg ( "$LAB.cork()" ) ; chain_is_corked = true ; return chainedAPI ; } , uncork : function ( ) { if ( chain_opts [ _Debug ] ) log_msg ( "$LAB.uncork()" ) ; if ( chain_is_corked ) { chain_is_corked = false ; advance_exec_cursor ( ) ; } return chainedAPI ; } } ; // the first chain link API (includes `setOptions` only this first time) return { script : chainedAPI . script , wait : chainedAPI . wait , cork : chainedAPI . cork , uncork : chainedAPI . uncork , setOptions : function ( opts ) { merge_objs ( opts , chain_opts ) ; return chainedAPI ; } } ; }
creates a closure for each separate chain spawned from this $LAB instance, to keep state cleanly separated between chains
creates a closure for each separate chain spawned from this $LAB instance to keep state cleanly separated between chains