@softvisio/ext
Version:
ExtJS patches
1,069 lines (1,067 loc) • 9.26 MB
JavaScript
// @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