UNPKG

@softvisio/ext

Version:
1,069 lines (1,067 loc) 9.26 MB
// @tag core // @define Ext.Boot var Ext = Ext || {}; //<editor-fold desc="Boot"> /** * @class Ext.Boot * @singleton * @private */ Ext.Boot = Ext.Boot || (function (emptyFn) { var doc = document, _emptyArray = [], _config = { /** * @cfg {Boolean} [disableCaching=true] * If `true` current timestamp is added to script URL's to prevent caching. * In debug builds, adding a "cache" or "disableCacheBuster" query parameter * to the page's URL will set this to `false`. */ disableCaching: /[?&](?:cache|disableCacheBuster)\b/i.test(location.search) || !/http[s]?\:/i.test(location.href) || /(^|[ ;])ext-cache=1/.test(doc.cookie) ? false : true, /** * @cfg {String} [disableCachingParam="_dc"] * The query parameter name for the cache buster's timestamp. */ disableCachingParam: "_dc", /** * @cfg {Boolean} loadDelay * Millisecond delay between asynchronous script injection (prevents stack * overflow on some user agents) 'false' disables delay but potentially * increases stack load. */ loadDelay: false, /** * @cfg {Boolean} preserveScripts * `false` to remove asynchronously loaded scripts, `true` to retain script * element for browser debugger compatibility and improved load performance. */ preserveScripts: true, /** * @cfg {String} [charset=UTF-8] * Optional charset to specify encoding of dynamic content. */ charset: "UTF-8", }, _assetConfig = {}, cssRe = /\.css(?:\?|$)/i, resolverEl = doc.createElement("a"), isBrowser = typeof window !== "undefined", _environment = { browser: isBrowser, node: !isBrowser && typeof require === "function", phantom: (window && (window._phantom || window.callPhantom)) || /PhantomJS/.test(window.navigator.userAgent), }, _tags = (Ext.platformTags = {}), // All calls to _debug are commented out to speed up old browsers a bit; // yes that makes a difference because the cost of concatenating strings // and passing them into _debug() adds up pretty quickly. _debug = function (message) {}, //console.log(message); _apply = function (object, config, defaults) { if (defaults) { _apply(object, defaults); } if (object && config && typeof config === "object") { for (var i in config) { object[i] = config[i]; } } return object; }, _merge = function () { var lowerCase = false, obj = Array.prototype.shift.call(arguments), index, i, len, value; if (typeof arguments[arguments.length - 1] === "boolean") { lowerCase = Array.prototype.pop.call(arguments); } len = arguments.length; for (index = 0; index < len; index++) { value = arguments[index]; if (typeof value === "object") { for (i in value) { obj[lowerCase ? i.toLowerCase() : i] = value[i]; } } } return obj; }, _getKeys = typeof Object.keys == "function" ? function (object) { if (!object) { return []; } return Object.keys(object); } : function (object) { var keys = [], property; for (property in object) { if (object.hasOwnProperty(property)) { keys.push(property); } } return keys; }, /* * The Boot loader class manages Request objects that contain one or * more individual urls that need to be loaded. Requests can be performed * synchronously or asynchronously, but will always evaluate urls in the * order specified on the request object. */ Boot = { loading: 0, loaded: 0, apply: _apply, env: _environment, config: _config, /** * @cfg {Object} assetConfig * A map (url->assetConfig) that contains information about assets loaded by the Microlaoder. */ assetConfig: _assetConfig, // Keyed by absolute URL this object holds "true" if that URL is already loaded // or an array of callbacks to call once it loads. scripts: {}, /* Entry objects 'http://foo.com/bar/baz/Thing.js': { done: true, el: scriptEl || linkEl, preserve: true, requests: [ request1, ... ] } */ /** * contains the current script name being loaded * (loadSync or sequential load only) */ currentFile: null, suspendedQueue: [], currentRequest: null, // when loadSync is called, need to cause subsequent load requests to also be loadSync, // eg, when Ext.require(...) is called syncMode: false, /* * simple helper method for debugging */ debug: _debug, /** * enables / disables loading scripts via script / link elements rather * than using ajax / eval */ useElements: true, listeners: [], Request: Request, Entry: Entry, allowMultipleBrowsers: false, browserNames: { ie: "IE", firefox: "Firefox", safari: "Safari", chrome: "Chrome", opera: "Opera", dolfin: "Dolfin", edge: "Edge", webosbrowser: "webOSBrowser", chromeMobile: "ChromeMobile", chromeiOS: "ChromeiOS", silk: "Silk", other: "Other", }, osNames: { ios: "iOS", android: "Android", windowsPhone: "WindowsPhone", webos: "webOS", blackberry: "BlackBerry", rimTablet: "RIMTablet", mac: "MacOS", win: "Windows", tizen: "Tizen", linux: "Linux", bada: "Bada", chromeOS: "ChromeOS", other: "Other", }, browserPrefixes: { ie: "MSIE ", edge: "Edge/", firefox: "Firefox/", chrome: "Chrome/", safari: "Version/", opera: "OPR/", dolfin: "Dolfin/", webosbrowser: "wOSBrowser/", chromeMobile: "CrMo/", chromeiOS: "CriOS/", silk: "Silk/", }, // When a UA reports multiple browsers this list is used to prioritize the 'real' browser // lower index number will win browserPriority: ["edge", "opera", "dolfin", "webosbrowser", "silk", "chromeiOS", "chromeMobile", "ie", "firefox", "safari", "chrome"], osPrefixes: { tizen: "(Tizen )", ios: "i(?:Pad|Phone|Pod)(?:.*)CPU(?: iPhone)? OS ", android: "(Android |HTC_|Silk/)", // Some HTC devices ship with an OSX userAgent by default, // so we need to add a direct check for HTC_ windowsPhone: "Windows Phone ", blackberry: "(?:BlackBerry|BB)(?:.*)Version/", rimTablet: "RIM Tablet OS ", webos: "(?:webOS|hpwOS)/", bada: "Bada/", chromeOS: "CrOS ", }, fallbackOSPrefixes: { windows: "win", mac: "mac", linux: "linux", }, devicePrefixes: { iPhone: "iPhone", iPod: "iPod", iPad: "iPad", }, maxIEVersion: 12, /** * The default function that detects various platforms and sets tags * in the platform map accordingly. Examples are iOS, android, tablet, etc. * @param tags the set of tags to populate */ detectPlatformTags: function () { var me = this, ua = navigator.userAgent, isMobile = /Mobile(\/|\s)/.test(ua), element = document.createElement("div"), isEventSupported = function (name, tag) { if (tag === undefined) { tag = window; } var eventName = "on" + name.toLowerCase(), isSupported = eventName in element; if (!isSupported) { if (element.setAttribute && element.removeAttribute) { element.setAttribute(eventName, ""); isSupported = typeof element[eventName] === "function"; if (typeof element[eventName] !== "undefined") { element[eventName] = undefined; } element.removeAttribute(eventName); } } return isSupported; }, // Browser Detection getBrowsers = function () { var browsers = {}, maxIEVersion, prefix, value, key, index, len, match, version, matched; // MS Edge browser (and possibly others) can report multiple browsers in the UserAgent // "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240" // we use this to prioritize the actual browser in this situation len = me.browserPriority.length; for (index = 0; index < len; index++) { key = me.browserPriority[index]; if (!matched) { value = me.browserPrefixes[key]; match = ua.match(new RegExp("(" + value + ")([\\w\\._]+)")); version = match && match.length > 1 ? parseInt(match[2]) : 0; if (version) { matched = true; } } else { version = 0; } browsers[key] = version; } //Deal with IE document mode if (browsers.ie) { var mode = document.documentMode; if (mode >= 8) { browsers.ie = mode; } } // Fancy IE greater than and less then quick tags version = browsers.ie || false; maxIEVersion = Math.max(version, me.maxIEVersion); for (index = 8; index <= maxIEVersion; ++index) { prefix = "ie" + index; browsers[prefix + "m"] = version ? version <= index : 0; browsers[prefix] = version ? version === index : 0; browsers[prefix + "p"] = version ? version >= index : 0; } return browsers; }, //OS Detection getOperatingSystems = function () { var systems = {}, value, key, keys, index, len, match, matched, version, activeCount; keys = _getKeys(me.osPrefixes); len = keys.length; for (index = 0, activeCount = 0; index < len; index++) { key = keys[index]; value = me.osPrefixes[key]; match = ua.match(new RegExp("(" + value + ")([^\\s;]+)")); matched = match ? match[1] : null; // This is here because some HTC android devices show an OSX Snow Leopard userAgent by default. // And the Kindle Fire doesn't have any indicator of Android as the OS in its User Agent if (matched && (matched === "HTC_" || matched === "Silk/")) { version = 2.3; } else { version = match && match.length > 1 ? parseFloat(match[match.length - 1]) : 0; } if (version) { activeCount++; } systems[key] = version; } keys = _getKeys(me.fallbackOSPrefixes); // If no OS could be found we resort to the fallbacks, otherwise we just // falsify the fallbacks len = keys.length; for (index = 0; index < len; index++) { key = keys[index]; // No OS was detected from osPrefixes if (activeCount === 0) { value = me.fallbackOSPrefixes[key]; match = ua.toLowerCase().match(new RegExp(value)); systems[key] = match ? true : 0; } else { systems[key] = 0; } } return systems; }, // Device Detection getDevices = function () { var devices = {}, value, key, keys, index, len, match; keys = _getKeys(me.devicePrefixes); len = keys.length; for (index = 0; index < len; index++) { key = keys[index]; value = me.devicePrefixes[key]; match = ua.match(new RegExp(value)); devices[key] = match ? true : 0; } return devices; }, browsers = getBrowsers(), systems = getOperatingSystems(), devices = getDevices(), platformParams = Boot.loadPlatformsParam(); // We apply platformParams from the query here first to allow for forced user valued // to be used in calculation of generated tags _merge(_tags, browsers, systems, devices, platformParams, true); _tags.phone = !!(_tags.iphone || _tags.ipod || (!_tags.silk && _tags.android && (_tags.android < 3 || isMobile)) || (_tags.blackberry && isMobile) || _tags.windowsphone); _tags.tablet = !!(!_tags.phone && (_tags.ipad || _tags.android || _tags.silk || _tags.rimtablet || (_tags.ie10 && /; Touch/.test(ua)))); _tags.touch = // if the browser has touch events we can be reasonably sure the device has // a touch screen isEventSupported("touchend") || // browsers that use pointer event have maxTouchPoints > 0 if the // device supports touch input // http://www.w3.org/TR/pointerevents/#widl-Navigator-maxTouchPoints navigator.maxTouchPoints || // IE10 uses a vendor-prefixed maxTouchPoints property navigator.msMaxTouchPoints; _tags.desktop = !_tags.phone && !_tags.tablet; _tags.cordova = _tags.phonegap = !!(window.PhoneGap || window.Cordova || window.cordova); _tags.webview = /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)(?!.*FBAN)/i.test(ua); _tags.androidstock = _tags.android <= 4.3 && (_tags.safari || _tags.silk); // Re-apply any query params here to allow for user override of generated tags (desktop, touch, tablet, etc) _merge(_tags, platformParams, true); }, /** * Extracts user supplied platform tags from the "platformTags" query parameter * of the form: * * ?platformTags=name:state,name:state,... * * (each tag defaults to true when state is unspecified) * * Example: * * ?platformTags=isTablet,isPhone:false,isDesktop:0,iOS:1,Safari:true, ... * * @returns {Object} the platform tags supplied by the query string */ loadPlatformsParam: function () { // Check if the ?platform parameter is set in the URL var paramsString = window.location.search.substr(1), paramsArray = paramsString.split("&"), params = {}, i, platforms = {}, tmpArray, tmplen, platform, name, enabled; for (i = 0; i < paramsArray.length; i++) { tmpArray = paramsArray[i].split("="); params[tmpArray[0]] = tmpArray[1]; } if (params.platformTags) { tmpArray = params.platformTags.split(","); for (tmplen = tmpArray.length, i = 0; i < tmplen; i++) { platform = tmpArray[i].split(":"); name = platform[0]; enabled = true; if (platform.length > 1) { enabled = platform[1]; if (enabled === "false" || enabled === "0") { enabled = false; } } platforms[name] = enabled; } } return platforms; }, filterPlatform: function (platform, excludes) { platform = _emptyArray.concat(platform || _emptyArray); excludes = _emptyArray.concat(excludes || _emptyArray); var plen = platform.length, elen = excludes.length, include = !plen && elen, // default true if only excludes specified i, tag; for (i = 0; i < plen && !include; i++) { tag = platform[i]; include = !!_tags[tag]; } for (i = 0; i < elen && include; i++) { tag = excludes[i]; include = !_tags[tag]; } return include; }, init: function () { var scriptEls = doc.getElementsByTagName("script"), script = scriptEls[0], len = scriptEls.length, re = /\/ext(\-[a-z\-]+)?\.js$/, entry, src, state, baseUrl, key, n, origin; // No check for script definedness because there always should be at least one Boot.hasReadyState = "readyState" in script; Boot.hasAsync = "async" in script; Boot.hasDefer = "defer" in script; Boot.hasOnLoad = "onload" in script; // Feature detecting IE Boot.isIE8 = Boot.hasReadyState && !Boot.hasAsync && Boot.hasDefer && !Boot.hasOnLoad; Boot.isIE9 = Boot.hasReadyState && !Boot.hasAsync && Boot.hasDefer && Boot.hasOnLoad; Boot.isIE10p = Boot.hasReadyState && Boot.hasAsync && Boot.hasDefer && Boot.hasOnLoad; if (Boot.isIE8) { Boot.isIE10 = false; Boot.isIE10m = true; } else { Boot.isIE10 = navigator.appVersion.indexOf("MSIE 10") !== -1; Boot.isIE10m = Boot.isIE10 || Boot.isIE9 || Boot.isIE8; } // IE11 does not support conditional compilation so we detect it by exclusion Boot.isIE11 = Boot.isIE10p && !Boot.isIE10; // Since we are loading after other scripts, and we needed to gather them // anyway, we track them in _scripts so we don't have to ask for them all // repeatedly. for (n = 0; n < len; n++) { src = (script = scriptEls[n]).src; if (!src) { continue; } state = script.readyState || null; // If we find a script file called "ext-*.js", then the base path is that file's base path. if (!baseUrl && re.test(src)) { baseUrl = src; } if (!Boot.scripts[(key = Boot.canonicalUrl(src))]) { // _debug("creating entry " + key + " in Boot.init"); entry = new Entry({ key: key, url: src, done: state === null || // non-IE state === "loaded" || state === "complete", // IE only el: script, prop: "src", }); } } if (!baseUrl) { script = scriptEls[scriptEls.length - 1]; baseUrl = script.src; } Boot.baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf("/") + 1); origin = window.location.origin || window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : ""); Boot.origin = origin; Boot.detectPlatformTags(); Ext.filterPlatform = Boot.filterPlatform; }, /** * This method returns a canonical URL for the given URL. * * For example, the following all produce the same canonical URL (which is the * last one): * * http://foo.com/bar/baz/zoo/derp/../../goo/Thing.js?_dc=12345 * http://foo.com/bar/baz/zoo/derp/../../goo/Thing.js * http://foo.com/bar/baz/zoo/derp/../jazz/../../goo/Thing.js * http://foo.com/bar/baz/zoo/../goo/Thing.js * http://foo.com/bar/baz/goo/Thing.js * * @private */ canonicalUrl: function (url) { // *WARNING WARNING WARNING* // This method yields the most correct result we can get but it is EXPENSIVE! // In ALL browsers! When called multiple times in a sequence, as if when // we resolve dependencies for entries, it will cause garbage collection events // and overall painful slowness. This is why we try to avoid it as much as we can. // // @TODO - see if we need this fallback logic // http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue resolverEl.href = url; var ret = resolverEl.href, dc = _config.disableCachingParam, pos = dc ? ret.indexOf(dc + "=") : -1, c, end; // If we have a _dc query parameter we need to remove it from the canonical // URL. if (pos > 0 && ((c = ret.charAt(pos - 1)) === "?" || c === "&")) { end = ret.indexOf("&", pos); end = end < 0 ? "" : ret.substring(end); if (end && c === "?") { ++pos; // keep the '?' end = end.substring(1); } // remove the '&' ret = ret.substring(0, pos - 1) + end; } return ret; }, /** * Get the config value corresponding to the specified name. If no name is given, will return the config object * @param {String} name The config property name * @return {Object} */ getConfig: function (name) { return name ? Boot.config[name] : Boot.config; }, /** * Set the configuration. * @param {Object} config The config object to override the default values. * @return {Ext.Boot} this */ setConfig: function (name, value) { if (typeof name === "string") { Boot.config[name] = value; } else { for (var s in name) { Boot.setConfig(s, name[s]); } } return Boot; }, getHead: function () { return Boot.docHead || (Boot.docHead = doc.head || doc.getElementsByTagName("head")[0]); }, create: function (url, key, cfg) { var config = cfg || {}; config.url = url; config.key = key; return (Boot.scripts[key] = new Entry(config)); }, getEntry: function (url, cfg, canonicalPath) { var key, entry; // Canonicalizing URLs via anchor element href yields the most correct result // but is *extremely* resource heavy so we need to avoid it whenever possible key = canonicalPath ? url : Boot.canonicalUrl(url); entry = Boot.scripts[key]; if (!entry) { entry = Boot.create(url, key, cfg); if (canonicalPath) { entry.canonicalPath = true; } } return entry; }, registerContent: function (url, type, content) { var cfg = { content: content, loaded: true, css: type === "css", }; return Boot.getEntry(url, cfg); }, processRequest: function (request, sync) { request.loadEntries(sync); }, load: function (request) { // _debug("Boot.load called"); var request = new Request(request); if (request.sync || Boot.syncMode) { return Boot.loadSync(request); } // If there is a request in progress, we must // queue this new request to be fired when the current request completes. if (Boot.currentRequest) { // _debug("current active request, suspending this request"); // trigger assignment of entries now to ensure that overlapping // entries with currently running requests will synchronize state // with this pending one as they complete request.getEntries(); Boot.suspendedQueue.push(request); } else { Boot.currentRequest = request; Boot.processRequest(request, false); } return Boot; }, loadSync: function (request) { // _debug("Boot.loadSync called"); var request = new Request(request); Boot.syncMode++; Boot.processRequest(request, true); Boot.syncMode--; return Boot; }, loadBasePrefix: function (request) { request = new Request(request); request.prependBaseUrl = true; return Boot.load(request); }, loadSyncBasePrefix: function (request) { request = new Request(request); request.prependBaseUrl = true; return Boot.loadSync(request); }, requestComplete: function (request) { var next; if (Boot.currentRequest === request) { Boot.currentRequest = null; while (Boot.suspendedQueue.length > 0) { next = Boot.suspendedQueue.shift(); if (!next.done) { // _debug("resuming suspended request"); Boot.load(next); break; } } } if (!Boot.currentRequest && Boot.suspendedQueue.length == 0) { Boot.fireListeners(); } }, isLoading: function () { return !Boot.currentRequest && Boot.suspendedQueue.length == 0; }, fireListeners: function () { var listener; while (Boot.isLoading() && (listener = Boot.listeners.shift())) { listener(); } }, onBootReady: function (listener) { if (!Boot.isLoading()) { listener(); } else { Boot.listeners.push(listener); } }, /** * this is a helper function used by Ext.Loader to flush out * 'uses' arrays for classes in some Ext versions */ getPathsFromIndexes: function (indexMap, loadOrder) { // In older versions indexMap was an object instead of a sparse array if (!("length" in indexMap)) { var indexArray = [], index; for (index in indexMap) { if (!isNaN(+index)) { indexArray[+index] = indexMap[index]; } } indexMap = indexArray; } return Request.prototype.getPathsFromIndexes(indexMap, loadOrder); }, createLoadOrderMap: function (loadOrder) { return Request.prototype.createLoadOrderMap(loadOrder); }, fetch: function (url, complete, scope, async) { async = async === undefined ? !!complete : async; var xhr = new XMLHttpRequest(), result, status, content, exception = false, readyStateChange = function () { if (xhr && xhr.readyState == 4) { status = xhr.status === 1223 ? 204 : xhr.status === 0 && ((self.location || {}).protocol === "file:" || (self.location || {}).protocol === "ionp:") ? 200 : xhr.status; content = xhr.responseText; result = { content: content, status: status, exception: exception, }; if (complete) { complete.call(scope, result); } xhr.onreadystatechange = emptyFn; xhr = null; } }; if (async) { xhr.onreadystatechange = readyStateChange; } try { // _debug("fetching " + url + " " + (async ? "async" : "sync")); xhr.open("GET", url, async); xhr.send(null); } catch (err) { exception = err; readyStateChange(); return result; } if (!async) { readyStateChange(); } return result; }, notifyAll: function (entry) { entry.notifyRequests(); }, }; function Request(cfg) { //The request class encapsulates a series of Entry objects //and provides notification around the completion of all Entries //in this request. if (cfg.$isRequest) { return cfg; } var cfg = cfg.url ? cfg : { url: cfg, }, url = cfg.url, urls = url.charAt ? [url] : url, charset = cfg.charset || Boot.config.charset; _apply(this, cfg); delete this.url; this.urls = urls; this.charset = charset; } Request.prototype = { $isRequest: true, createLoadOrderMap: function (loadOrder) { var len = loadOrder.length, loadOrderMap = {}, i, element; for (i = 0; i < len; i++) { element = loadOrder[i]; loadOrderMap[element.path] = element; } return loadOrderMap; }, getLoadIndexes: function (item, indexMap, loadOrder, includeUses, skipLoaded) { var resolved = [], queue = [item], itemIndex = item.idx, queue, entry, dependencies, depIndex, i, len; if (indexMap[itemIndex]) { // prevent cycles return resolved; } // Both indexMap and resolved are sparse arrays keyed by indexes. // This gives us a naturally sorted sequence of indexes later on // when we need to convert them to paths. // indexMap is the map of all indexes we have visited at least once // per the current expandUrls() invocation, and resolved is the map // of all dependencies for the current item that are not included // in indexMap. indexMap[itemIndex] = resolved[itemIndex] = true; while ((item = queue.shift())) { // Canonicalizing URLs is expensive, we try to avoid it if (item.canonicalPath) { entry = Boot.getEntry(item.path, null, true); } else { entry = Boot.getEntry(this.prepareUrl(item.path)); } if (!(skipLoaded && entry.done)) { if (includeUses && item.uses && item.uses.length) { dependencies = item.requires.concat(item.uses); } else { dependencies = item.requires; } for (i = 0, len = dependencies.length; i < len; i++) { depIndex = dependencies[i]; if (!indexMap[depIndex]) { indexMap[depIndex] = resolved[depIndex] = true; queue.push(loadOrder[depIndex]); } } } } return resolved; }, getPathsFromIndexes: function (indexes, loadOrder) { var paths = [], index, len; // indexes is a sparse array with values being true for defined indexes for (index = 0, len = indexes.length; index < len; index++) { if (indexes[index]) { paths.push(loadOrder[index].path); } } return paths; }, expandUrl: function (url, loadOrder, loadOrderMap, indexMap, includeUses, skipLoaded) { var item, resolved; if (loadOrder) { item = loadOrderMap[url]; if (item) { resolved = this.getLoadIndexes(item, indexMap, loadOrder, includeUses, skipLoaded); if (resolved.length) { return this.getPathsFromIndexes(resolved, loadOrder); } } } return [url]; }, expandUrls: function (urls, includeUses) { var me = this, loadOrder = me.loadOrder, expanded = [], expandMap = {}, indexMap = [], loadOrderMap, tmpExpanded, i, len, t, tlen, tUrl; if (typeof urls === "string") { urls = [urls]; } if (loadOrder) { loadOrderMap = me.loadOrderMap; if (!loadOrderMap) { loadOrderMap = me.loadOrderMap = me.createLoadOrderMap(loadOrder); } } for (i = 0, len = urls.length; i < len; i++) { // We don't want to skip loaded entries (last argument === false). // There are some overrides that get loaded before their respective classes, // and when the class dependencies are processed we don't want to skip over // the overrides' dependencies just because they were loaded first. tmpExpanded = this.expandUrl(urls[i], loadOrder, loadOrderMap, indexMap, includeUses, false); for (t = 0, tlen = tmpExpanded.length; t < tlen; t++) { tUrl = tmpExpanded[t]; if (!expandMap[tUrl]) { expandMap[tUrl] = true; expanded.push(tUrl); } } } if (expanded.length === 0) { expanded = urls; } return expanded; }, expandLoadOrder: function () { var me = this, urls = me.urls, expanded; if (!me.expanded) { expanded = this.expandUrls(urls, true); me.expanded = true; } else { expanded = urls; } me.urls = expanded; // if we added some urls to the request to honor the indicated // load order, the request needs to be sequential if (urls.length != expanded.length) { me.sequential = true; } return me; }, getUrls: function () { this.expandLoadOrder(); return this.urls; }, prepareUrl: function (url) { if (this.prependBaseUrl) { return Boot.baseUrl + url; } return url; }, getEntries: function () { var me = this, entries = me.entries, loadOrderMap, item, i, entry, urls, url; if (!entries) { entries = []; urls = me.getUrls(); // If we have loadOrder array then the map will be expanded by now if (me.loadOrder) { loadOrderMap = me.loadOrderMap; } for (i = 0; i < urls.length; i++) { url = me.prepareUrl(urls[i]); if (loadOrderMap) { item = loadOrderMap[url]; } entry = Boot.getEntry( url, { buster: me.buster, charset: me.charset, }, item && item.canonicalPath ); entry.requests.push(me); entries.push(entry); } me.entries = entries; } return entries; }, loadEntries: function (sync) { var me = this, entries = me.getEntries(), len = entries.length, start = me.loadStart || 0, continueLoad, entries, entry, i; if (sync !== undefined) { me.sync = sync; } me.loaded = me.loaded || 0; me.loading = me.loading || len; for (i = start; i < len; i++) { entry = entries[i]; if (!entry.loaded) { continueLoad = entries[i].load(me.sync); } else { continueLoad = true; } if (!continueLoad) { me.loadStart = i; entry.onDone(function () { me.loadEntries(sync); }); break; } } me.processLoadedEntries(); }, processLoadedEntries: function () { var me = this, entries = me.getEntries(), len = entries.length, start = me.startIndex || 0, i, entry; if (!me.done) { for (i = start; i < len; i++) { entry = entries[i]; if (!entry.loaded) { me.startIndex = i; return; } if (!entry.evaluated) { entry.evaluate(); } if (entry.error) { me.error = true; } } me.notify(); } }, notify: function () { var me = this; if (!me.done) { var error = me.error, fn = me[error ? "failure" : "success"], delay = "delay" in me ? me.delay : error ? 1 : Boot.config.chainDelay, scope = me.scope || me; me.done = true; if (fn) { if (delay === 0 || delay > 0) { // Free the stack (and defer the next script) setTimeout(function () { fn.call(scope, me); }, delay); } else { fn.call(scope, me); } } me.fireListeners(); Boot.requestComplete(me); } }, onDone: function (listener) { var me = this, listeners = me.listeners || (me.listeners = []); if (me.done) { listener(me); } else { listeners.push(listener); } }, fireListeners: function () { var listeners = this.li