creates a closure for each separate chain spawned from this $LAB instance, to keep state cleanly separated between chains
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;
				}
			};
		}