/*jsl:declare distil*/ /** A resource module defined in the module that provides the module. */ (function(distil, window, document) { var moduleIndex = {}; var fetched = {}; var root = document.documentElement; var head = document.getElementsByTagName('head')[0] || root; var XHR = window.XMLHttpRequest || (function() { var progIdCandidates = ['Msxml2.XMLHTTP.4.0', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP']; var len = progIdCandidates.length; var progId; var xhr; function ConstructXhr() { return new window.ActiveXObject(ConstructXhr.progId); } while (len--) { try { progId = progIdCandidates[len]; xhr = new window.ActiveXObject(progId); // ActiveXObject constructor throws an exception // if the component isn't available. xhr = null; ConstructXhr.progId = progId; return ConstructXhr; } catch (e) { // Ignore the error } } throw new Error('No XMLHttpRequest implementation found'); })(); var noop = function() {}; var fetchAsset = function(url, callback, scope, userData, sync) { if (url in fetched) { // The callback will ALWAYS be called after the calling program // finishes executing. window.setTimeout(function() { callback.call(scope, userData); }, 0); return; } var xhr = new XHR(); xhr.open('GET', url, !sync); xhr.onreadystatechange = function() { if (4 !== xhr.readyState) return; fetched[url] = true; var status = xhr.status; var succeeded = (0 === status) || (status >= 200 && status < 300) || 304 == status; if (!succeeded) throw new Error('Failed to load resource: status=' + status + ' url=' + url); callback.call(scope, userData, xhr); xhr.onreadystatechange = noop; xhr = null; } xhr.send(null); }; var SCRIPT_TYPE = 'js'; var JSNIB_TYPE = 'jsnib'; var CSS_TYPE = 'css'; var MODULE_TYPE = 'module'; var NO_MODULE_ERROR = 'No module with name: '; var injectScript = distil.injectScript = function(url, callback, scope, userData) { var tag = document.createElement('script'); var complete = function() { if (callback) callback.call(scope, userData); tag.onreadystatechage = noop; tag = null; }; window.__filename__ = url; tag.onreadystatechange = function() { var readyState = tag && tag.readyState; if ('complete' === readyState || 'loaded' === readyState) complete(); } tag.onload = complete; tag.type = "text/javascript"; tag.src = url; // root.insertBefore(tag, root.firstChild); head.appendChild(tag); }; var injectStylesheet = distil.injectStylesheet = function(url, callback, scope, userData) { var link = document.createElement('link'); link.type = 'text/css'; link.rel = 'stylesheet'; link.href = 'url'; head.appendChild(link); if (callback) callback.call(scope, url, userData); }; var getRunningScriptSource = function() { var scripts = document.getElementsByTagName("script"); if (!scripts || !scripts.length) throw new Error("Could not find script"); var l = scripts.length; var s, src, lastSlash; while (l--) { if ((src = scripts[l].src)) return src; } throw new Error("No script tags with src attribute."); }; var ResourceInfo = function(type, url, callback, scope, userData) { if (!type) { var lastDot = url.lastIndexOf('.'); type = (-1 !== lastDot) ? url.substring(lastDot + 1) : ""; type = type.split('?')[0]; } var lastSlash = url.lastIndexOf('/'); if (-1 === lastSlash) throw new Error("Couldn't determine path from script src: " + url); return { type: type, url: url, callbacks: callback ? [callback] : [], scope: scope, userData: userData, loadQueue: [], fetched: false, injected: false, callbacksExecuted: false, path: url.substring(0, lastSlash + 1), parent: null }; }; var rootResource = ResourceInfo(SCRIPT_TYPE, getRunningScriptSource()); rootResource.fetched = true; rootResource.injected = true; var args = (rootResource.url.split('?')[1] || "").split('&'); var argsLen = args.length; while (argsLen--) { if (args[argsLen] === 'sync=true') distil.sync = true; } /** currentResource is the resource that is currently executing. */ var currentResource = rootResource; var injectResource = function(resource) { resource.injected = true; switch (resource.type) { case SCRIPT_TYPE: case JSNIB_TYPE: injectScript(resource.url, injectionComplete, null, resource); break; case CSS_TYPE: injectStylesheet(resource.url, injectionComplete, null, resource); break; default: throw new Error('Unknown resource type: ' + resource.type); } }; /** The execution complete callback for the script. */ var injectionComplete = function(resource) { while (resource) { if (resource.loadQueue.length) { currentResource = resource = resource.loadQueue.shift(); resource.complete = true; if (!resource.fetched) return; if (MODULE_TYPE === resource.type) continue; injectResource(resource); return; } resource.callbacksExecuted = true; while (resource.callbacks.length) { (resource.callbacks.shift()).call(resource.scope, resource.userData); } resource = resource.parent; } window.__filename__ = null; currentResource = resource ? resource : rootResource; }; var fetchComplete = function(resource) { resource.fetched = true; var parent = resource.parent; if (resource === currentResource) injectResource(resource); }; var loadResource = function(url, callback, scope, userData, parent) { if (distil.debug) url += '?' + (new Date()).valueOf(); var resource = ResourceInfo(null, url, callback, scope, userData); parent = parent || currentResource; resource.parent = parent; parent.loadQueue.push(resource); fetchAsset(url, fetchComplete, null, resource); }; var loadFiles = function(module) { var files = (module.loadQueue || []).concat(module.required); var resource = module.resource; var path = module.path; for (var i = 0, len = files.length; i < len; ++i) { // loadResource(path + files[i], null, null, null, resource); if (distil.sync && '.js' === files[i].slice(-3).toLowerCase()) document.write(''); else loadResource(path + files[i], null, null, null, resource); } }; distil.moduleCount = 0; distil.module = function(name, def) { if (name in moduleIndex) { var module = moduleIndex[name]; for (var p in def) module[p] = def[p]; distil.require(name); return; } distil.moduleCount++; if (distil.sync) { var url = getRunningScriptSource(); var lastSlash = url.lastIndexOf('/'); def.path = url.substring(0, lastSlash + 1); } else def.path = currentResource.path; if ('/' !== def.path.slice(-1)) def.path += '/'; def.callbacks = []; def.loadQueue = []; if (!distil.mainModule) distil.mainModule = def; else { // @HACK: This makes resources work in secondary bundles var key; var value; var main = distil.mainModule; for (key in def.assets || {}) { if (key in main.assets) continue; main.assets[key] = def.assets[key]; } for (key in def.asset_map || {}) { if (key in main.asset_map) continue; main.asset_map[key] = def.asset_map[key]; } } moduleIndex[name] = def; if (def.required && def.required.length) distil.require(name); }; distil.queue = function(name, fragment) { var module = moduleIndex[name]; if (module.resource) loadResource(currentResource.path + fragment, null, null, null, module.resource); else module.loadQueue.push(fragment); }; distil.onready = function(callback) { if (rootResource.callbacksExecuted) window.setTimeout(callback, 0); else rootResource.callbacks.push(callback); } distil.complete = function(name) { var module = moduleIndex[name]; if (module.loadQueue.length) distil.require(name); } distil.require = function(name, callback, scope, userData) { var module = moduleIndex[name]; if (!module) throw new Error(NO_MODULE_ERROR + name); var complete = function() { if (callback) callback.call(scope, userData); module.loaded = true; module.resource = null; }; if (module.loaded) { window.setTimeout(complete, 0); return; } var resource = module.resource = ResourceInfo(MODULE_TYPE, module.path, complete); resource.parent = currentResource; resource.fetched = true; currentResource.loadQueue.push(resource); loadFiles(module); if (rootResource === currentResource) injectionComplete(currentResource); } distil.moduleDidLoad = function(moduleName) { distil.moduleCount--; if (!distil.moduleCount && rootResource === currentResource) injectionComplete(currentResource); } distil.kick = distil.moduleDidLoad; /** pathToAssetInModule(asset, module) -> String - asset (String): The name or path of the asset. - module (Object): The module definition from which to pull the asset */ var pathToAssetInModule = function(asset, module) { // If the asset is in the asset_map, it is presumed to be relative to the // module rather than the currently running script. if (module.asset_map && module.asset_map[asset]) return module.path + module.asset_map[asset]; var path, url; if (distil.sync) url = getRunningScriptSource(); else url = window.__filename__; if (url) { var lastSlash = url.lastIndexOf('/'); path = url.substring(0, lastSlash + 1); } else path= module.path; return path + asset; }; distil.urlForAssetWithNameInModule = function(asset, moduleName) { var module = moduleName ? moduleIndex[moduleName] : distil.mainModule; if (!module) throw new Error(NO_MODULE_ERROR + moduleName); return pathToAssetInModule(asset, module); } distil.dataForAssetWithNameInModule = function(asset, moduleName) { var module = moduleName ? moduleIndex[moduleName] : distil.mainModule; if (!module) throw new Error(NO_MODULE_ERROR + moduleName); if (!module.assets) module.assets = {}; var data = module.assets[asset]; if (void(0) != data) return data; if (!module.asset_map) return null; var path= pathToAssetInModule(asset, module); function oncomplete(unused, xhr) { module.assets[asset] = data = xhr.responseText; } fetchAsset(path, oncomplete, null, null, true); return data; } distil.asset = distil.dataForAssetWithNameInModule; distil.assetUrl = distil.urlForAssetWithNameInModule; })(window.distil = {}, window, document);