// LAB.js (LABjs :: Loading And Blocking JavaScript) // v1.0.2rc1 (c) Kyle Simpson // MIT License (function(global){ var sSTRING = "string", // constants used for compression optimization sHEAD = "head", sBODY = "body", sSCRIPT = "script", sREADYSTATE = "readyState", sPRELOADDONE = "preloaddone", sLOADTRIGGER = "loadtrigger", sSRCURI = "srcuri", sPRELOAD = "preload", sCOMPLETE = "complete", sDONE = "done", sWHICH = "which", sPRESERVE = "preserve", sONREADYSTATECHANGE = "onreadystatechange", sONLOAD = "onload", sHASOWNPROPERTY = "hasOwnProperty", sSCRIPTCACHE = "script/cache", sTYPEOBJ = "[object ", sTYPEFUNC = sTYPEOBJ+"Function]", sTYPEARRAY = sTYPEOBJ+"Array]", nNULL = null, bTRUE = true, bFALSE = false, oDOC = global.document, oWINLOC = global.location, oACTIVEX = global.ActiveXObject, fSETTIMEOUT = global.setTimeout, fCLEARTIMEOUT = global.clearTimeout, fGETELEMENTSBYTAGNAME = function(tn){return oDOC.getElementsByTagName(tn);}, fOBJTOSTRING = Object.prototype.toString, fNOOP = function(){}, append_to = {}, all_scripts = {}, PAGEROOT = /^[^?#]*\//.exec(oWINLOC.href)[0], // these ROOTs do not support file:/// usage, only http:// type usage DOCROOT = /^\w+\:\/\/\/?[^\/]+/.exec(PAGEROOT)[0], // optional third / in the protocol portion of this regex so that LABjs doesn't blow up when used in file:/// usage docScripts = fGETELEMENTSBYTAGNAME(sSCRIPT), // Ah-ha hush that fuss, feature inference is used to detect specific browsers // because the techniques used in LABjs have no known feature detection. If // you know of a feature test please contact me ASAP. Feature inference is used // instead of user agent sniffing because the UA string can be easily // spoofed and is not adequate for such a mission critical part of the code. is_opera = global.opera && fOBJTOSTRING.call(global.opera) == sTYPEOBJ+"Opera]", is_gecko = (function(o) { o[o] = o+""; return o[o] != o+""; })(new String("__count__")), global_defs = { cache:!(is_gecko||is_opera), // browsers like IE/Safari/Chrome can use the "cache" trick to preload order:is_gecko||is_opera, // FF/Opera preserve execution order with script tags automatically, so just add all scripts as fast as possible xhr:bTRUE, // use XHR trick to preload local scripts dupe:bTRUE, // allow duplicate scripts? defaults to true now 'cause is slightly more performant that way (less checks) base:"", // base path to prepend to all non-absolute-path scripts which:sHEAD // which DOM object ("head" or "body") to append scripts to } ; global_defs[sPRESERVE] = bFALSE; // force preserve execution order of all loaded scripts (regardless of preloading) global_defs[sPRELOAD] = bTRUE; // use various tricks for "preloading" scripts append_to[sHEAD] = fGETELEMENTSBYTAGNAME(sHEAD); append_to[sBODY] = fGETELEMENTSBYTAGNAME(sBODY); function isFunc(func) { return fOBJTOSTRING.call(func) === sTYPEFUNC; } function canonicalScriptURI(src,base_path) { var regex = /^\w+\:\/\//, ret; if (typeof src !== sSTRING) src = ""; if (typeof base_path !== sSTRING) base_path = ""; ret = (regex.test(src) ? "" : base_path) + src; return ((regex.test(ret) ? "" : (ret.charAt(0) === "/" ? DOCROOT : PAGEROOT)) + ret); } function sameDomain(src) { return (canonicalScriptURI(src).indexOf(DOCROOT) === 0); } function scriptTagExists(uri) { // checks if a script uri has ever been loaded into this page's DOM var script, idx=-1; while (script = docScripts[++idx]) { if (typeof script.src === sSTRING && uri === canonicalScriptURI(script.src) && script.type !== sSCRIPTCACHE) return bTRUE; } return bFALSE; } function engine(queueExec,opts) { queueExec = !(!queueExec); if (opts == nNULL) opts = global_defs; var ready = bFALSE, _use_preload = queueExec && opts[sPRELOAD], _use_cache_preload = _use_preload && opts.cache, _use_script_order = _use_preload && opts.order, _use_xhr_preload = _use_preload && opts.xhr, _auto_wait = opts[sPRESERVE], _which = opts.which, _base_path = opts.base, waitFunc = fNOOP, scripts_loading = bFALSE, publicAPI, first_pass = bTRUE, scripts = {}, exec = [], end_of_chain_check_interval = nNULL ; _use_preload = _use_cache_preload || _use_xhr_preload || _use_script_order; // if all flags are turned off, preload is moot so disable it function isScriptLoaded(elem,scriptentry) { if ((elem[sREADYSTATE] && elem[sREADYSTATE]!==sCOMPLETE && elem[sREADYSTATE]!=="loaded") || scriptentry[sDONE]) { return bFALSE; } elem[sONLOAD] = elem[sONREADYSTATECHANGE] = nNULL; // prevent memory leak return bTRUE; } function handleScriptLoad(elem,scriptentry,skipReadyCheck) { skipReadyCheck = !(!skipReadyCheck); // used to override ready check when script text was injected from XHR preload if (!skipReadyCheck && !(isScriptLoaded(elem,scriptentry))) return; scriptentry[sDONE] = bTRUE; for (var key in scripts) { if (scripts[sHASOWNPROPERTY](key) && !(scripts[key][sDONE])) return; } ready = bTRUE; waitFunc(); } function loadTriggerExecute(scriptentry) { if (isFunc(scriptentry[sLOADTRIGGER])) { scriptentry[sLOADTRIGGER](); scriptentry[sLOADTRIGGER] = nNULL; // prevent memory leak } } function handleScriptPreload(elem,scriptentry) { if (!isScriptLoaded(elem,scriptentry)) return; scriptentry[sPRELOADDONE] = bTRUE; fSETTIMEOUT(function(){ append_to[scriptentry[sWHICH]].removeChild(elem); // remove preload script node loadTriggerExecute(scriptentry); },0); } function handleXHRPreload(xhr,scriptentry) { if (xhr[sREADYSTATE] === 4) { xhr[sONREADYSTATECHANGE] = fNOOP; // fix a memory leak in IE scriptentry[sPRELOADDONE] = bTRUE; fSETTIMEOUT(function(){ loadTriggerExecute(scriptentry); },0); } } function createScriptTag(scriptentry,src,type,charset,onload,scriptText) { var _script_which = scriptentry[sWHICH]; fSETTIMEOUT(function() { // this setTimeout waiting "hack" prevents a nasty race condition browser hang (IE) when the document.write("