]> git.proxmox.com Git - extjs.git/blame - extjs/modern/theme-base/.sencha/package/Boot.js
add extjs 6.0.1 sources
[extjs.git] / extjs / modern / theme-base / .sencha / package / Boot.js
CommitLineData
6527f429
DM
1// @tag core\r
2// @define Ext.Boot\r
3\r
4var Ext = Ext || {};\r
5\r
6//<editor-fold desc="Boot">\r
7/*\r
8 * @class Ext.Boot\r
9 * @singleton\r
10 */\r
11Ext.Boot = Ext.Boot || (function (emptyFn) {\r
12\r
13 var doc = document,\r
14 _emptyArray = [],\r
15 _config = {\r
16 /*\r
17 * @cfg {Boolean} [disableCaching=true]\r
18 * If `true` current timestamp is added to script URL's to prevent caching.\r
19 * In debug builds, adding a "cache" or "disableCacheBuster" query parameter\r
20 * to the page's URL will set this to `false`.\r
21 */\r
22 disableCaching: (/[?&](?:cache|disableCacheBuster)\b/i.test(location.search) ||\r
23 !(/http[s]?\:/i.test(location.href)) ||\r
24 /(^|[ ;])ext-cache=1/.test(doc.cookie)) ? false :\r
25 true,\r
26\r
27 /*\r
28 * @cfg {String} [disableCachingParam="_dc"]\r
29 * The query parameter name for the cache buster's timestamp.\r
30 */\r
31 disableCachingParam: '_dc',\r
32\r
33 /*\r
34 * @cfg {Boolean} loadDelay\r
35 * Millisecond delay between asynchronous script injection (prevents stack\r
36 * overflow on some user agents) 'false' disables delay but potentially\r
37 * increases stack load.\r
38 */\r
39 loadDelay: false,\r
40\r
41 /*\r
42 * @cfg {Boolean} preserveScripts\r
43 * `false` to remove asynchronously loaded scripts, `true` to retain script\r
44 * element for browser debugger compatibility and improved load performance.\r
45 */\r
46 preserveScripts: true,\r
47\r
48 /*\r
49 * @cfg {String} [charset=UTF-8]\r
50 * Optional charset to specify encoding of dynamic content.\r
51 */\r
52 charset: 'UTF-8'\r
53 },\r
54\r
55 _assetConfig= {},\r
56\r
57 cssRe = /\.css(?:\?|$)/i,\r
58 resolverEl = doc.createElement('a'),\r
59 isBrowser = typeof window !== 'undefined',\r
60 _environment = {\r
61 browser: isBrowser,\r
62 node: !isBrowser && (typeof require === 'function'),\r
63 phantom: (window && (window._phantom || window.callPhantom)) || /PhantomJS/.test(window.navigator.userAgent)\r
64 },\r
65 _tags = (Ext.platformTags = {}),\r
66\r
67 //<debug>\r
68 _debug = function (message) {\r
69 //console.log(message);\r
70 },\r
71 //</debug>\r
72 _apply = function (object, config, defaults) {\r
73 if (defaults) {\r
74 _apply(object, defaults);\r
75 }\r
76 if (object && config && typeof config === 'object') {\r
77 for (var i in config) {\r
78 object[i] = config[i];\r
79 }\r
80 }\r
81 return object;\r
82 },\r
83 _merge = function() {\r
84 var lowerCase = false,\r
85 obj = Array.prototype.shift.call(arguments),\r
86 index, i, len, value;\r
87\r
88 if (typeof arguments[arguments.length - 1] === 'boolean') {\r
89 lowerCase = Array.prototype.pop.call(arguments);\r
90 }\r
91\r
92 len = arguments.length;\r
93 for (index = 0; index < len; index++) {\r
94 value = arguments[index];\r
95 if (typeof value === 'object') {\r
96 for (i in value) {\r
97 obj[lowerCase ? i.toLowerCase() : i] = value[i];\r
98 }\r
99 }\r
100 }\r
101\r
102 return obj;\r
103 },\r
104 _getKeys = (typeof Object.keys == 'function') ?\r
105 function(object){\r
106 if (!object) {\r
107 return [];\r
108 }\r
109 return Object.keys(object);\r
110 } :\r
111 function(object) {\r
112 var keys = [],\r
113 property;\r
114\r
115 for (property in object) {\r
116 if (object.hasOwnProperty(property)) {\r
117 keys.push(property);\r
118 }\r
119 }\r
120\r
121 return keys;\r
122 },\r
123 /*\r
124 * The Boot loader class manages Request objects that contain one or\r
125 * more individual urls that need to be loaded. Requests can be performed\r
126 * synchronously or asynchronously, but will always evaluate urls in the\r
127 * order specified on the request object.\r
128 */\r
129 Boot = {\r
130 loading: 0,\r
131 loaded: 0,\r
132 apply: _apply,\r
133 env: _environment,\r
134 config: _config,\r
135\r
136 /**\r
137 * @cfg {Object} assetConfig\r
138 * A map (url->assetConfig) that contains information about assets loaded by the Microlaoder.\r
139 */\r
140 assetConfig: _assetConfig,\r
141\r
142 // Keyed by absolute URL this object holds "true" if that URL is already loaded\r
143 // or an array of callbacks to call once it loads.\r
144 scripts: {\r
145 /*\r
146 Entry objects\r
147\r
148 'http://foo.com/bar/baz/Thing.js': {\r
149 done: true,\r
150 el: scriptEl || linkEl,\r
151 preserve: true,\r
152 requests: [ request1, ... ]\r
153 }\r
154 */\r
155 },\r
156\r
157 /*\r
158 * contains the current script name being loaded\r
159 * (loadSync or sequential load only)\r
160 */\r
161 currentFile: null,\r
162 suspendedQueue: [],\r
163 currentRequest: null,\r
164\r
165 // when loadSync is called, need to cause subsequent load requests to also be loadSync,\r
166 // eg, when Ext.require(...) is called\r
167 syncMode: false,\r
168\r
169 /*\r
170 * simple helper method for debugging\r
171 */\r
172 //<debug>\r
173 debug: _debug,\r
174 //</debug>\r
175\r
176 /*\r
177 * enables / disables loading scripts via script / link elements rather\r
178 * than using ajax / eval\r
179 */\r
180 useElements: true,\r
181\r
182 listeners: [],\r
183\r
184 Request: Request,\r
185\r
186 Entry: Entry,\r
187\r
188 allowMultipleBrowsers: false,\r
189\r
190 browserNames: {\r
191 ie: 'IE',\r
192 firefox: 'Firefox',\r
193 safari: 'Safari',\r
194 chrome: 'Chrome',\r
195 opera: 'Opera',\r
196 dolfin: 'Dolfin',\r
197 edge: 'Edge',\r
198 webosbrowser: 'webOSBrowser',\r
199 chromeMobile: 'ChromeMobile',\r
200 chromeiOS: 'ChromeiOS',\r
201 silk: 'Silk',\r
202 other: 'Other'\r
203 },\r
204\r
205 osNames: {\r
206 ios: 'iOS',\r
207 android: 'Android',\r
208 windowsPhone: 'WindowsPhone',\r
209 webos: 'webOS',\r
210 blackberry: 'BlackBerry',\r
211 rimTablet: 'RIMTablet',\r
212 mac: 'MacOS',\r
213 win: 'Windows',\r
214 tizen: 'Tizen',\r
215 linux: 'Linux',\r
216 bada: 'Bada',\r
217 chromeOS: 'ChromeOS',\r
218 other: 'Other'\r
219 },\r
220\r
221 browserPrefixes: {\r
222 ie: 'MSIE ',\r
223 edge: 'Edge/',\r
224 firefox: 'Firefox/',\r
225 chrome: 'Chrome/',\r
226 safari: 'Version/',\r
227 opera: 'OPR/',\r
228 dolfin: 'Dolfin/',\r
229 webosbrowser: 'wOSBrowser/',\r
230 chromeMobile: 'CrMo/',\r
231 chromeiOS: 'CriOS/',\r
232 silk: 'Silk/'\r
233 },\r
234\r
235 // When a UA reports multiple browsers this list is used to prioritize the 'real' browser\r
236 // lower index number will win\r
237 browserPriority: [\r
238 'edge',\r
239 'opera',\r
240 'dolfin',\r
241 'webosbrowser',\r
242 'silk',\r
243 'chromeiOS',\r
244 'chromeMobile',\r
245 'ie',\r
246 'firefox',\r
247 'safari',\r
248 'chrome'\r
249 ],\r
250\r
251 osPrefixes: {\r
252 tizen: '(Tizen )',\r
253 ios: 'i(?:Pad|Phone|Pod)(?:.*)CPU(?: iPhone)? OS ',\r
254 android: '(Android |HTC_|Silk/)', // Some HTC devices ship with an OSX userAgent by default,\r
255 // so we need to add a direct check for HTC_\r
256 windowsPhone: 'Windows Phone ',\r
257 blackberry: '(?:BlackBerry|BB)(?:.*)Version\/',\r
258 rimTablet: 'RIM Tablet OS ',\r
259 webos: '(?:webOS|hpwOS)\/',\r
260 bada: 'Bada\/',\r
261 chromeOS: 'CrOS '\r
262 },\r
263\r
264 fallbackOSPrefixes: {\r
265 windows: 'win',\r
266 mac: 'mac',\r
267 linux: 'linux'\r
268 },\r
269\r
270 devicePrefixes: {\r
271 iPhone: 'iPhone',\r
272 iPod: 'iPod',\r
273 iPad: 'iPad'\r
274 },\r
275\r
276 maxIEVersion: 12,\r
277\r
278\r
279 /**\r
280 * The default function that detects various platforms and sets tags\r
281 * in the platform map accordingly. Examples are iOS, android, tablet, etc.\r
282 * @param tags the set of tags to populate\r
283 */\r
284 detectPlatformTags: function () {\r
285 var me = this,\r
286 ua = navigator.userAgent,\r
287 isMobile = /Mobile(\/|\s)/.test(ua),\r
288 element = document.createElement('div'),\r
289 isEventSupported = function (name, tag) {\r
290 if (tag === undefined) {\r
291 tag = window;\r
292 }\r
293\r
294 var eventName = 'on' + name.toLowerCase(),\r
295 isSupported = (eventName in element);\r
296\r
297 if (!isSupported) {\r
298 if (element.setAttribute && element.removeAttribute) {\r
299 element.setAttribute(eventName, '');\r
300 isSupported = typeof element[eventName] === 'function';\r
301\r
302 if (typeof element[eventName] !== 'undefined') {\r
303 element[eventName] = undefined;\r
304 }\r
305\r
306 element.removeAttribute(eventName);\r
307 }\r
308 }\r
309\r
310 return isSupported;\r
311 },\r
312\r
313 // Browser Detection\r
314 getBrowsers = function () {\r
315 var browsers = {},\r
316 maxIEVersion, prefix,\r
317 value, key, index, len, match, version, matched;\r
318\r
319 // MS Edge browser (and possibly others) can report multiple browsers in the UserAgent\r
320 // "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"\r
321 // we use this to prioritize the actual browser in this situation\r
322 len = me.browserPriority.length;\r
323 for (index = 0; index < len; index++) {\r
324 key = me.browserPriority[index];\r
325 if (!matched) {\r
326 value = me.browserPrefixes[key];\r
327 match = ua.match(new RegExp('(' + value + ')([\\w\\._]+)'));\r
328 version = match && match.length > 1 ? parseInt(match[2]) : 0;\r
329 if (version) {\r
330 matched = true;\r
331 }\r
332 } else {\r
333 version = 0;\r
334 }\r
335 browsers[key] = version;\r
336 }\r
337\r
338 //Deal with IE document mode\r
339 if (browsers.ie) {\r
340 var mode = document.documentMode;\r
341\r
342 if (mode >= 8) {\r
343 browsers.ie = mode;\r
344 }\r
345 }\r
346\r
347 // Fancy IE greater than and less then quick tags\r
348 version = browsers.ie || false;\r
349 maxIEVersion = Math.max(version, me.maxIEVersion);\r
350\r
351 for (index = 8; index <= maxIEVersion; ++index) {\r
352 prefix = 'ie' + index;\r
353 browsers[prefix + 'm'] = version ? version <= index : 0;\r
354 browsers[prefix] = version ? version === index : 0;\r
355 browsers[prefix + 'p'] = version ? version >= index : 0;\r
356 }\r
357\r
358 return browsers;\r
359 },\r
360\r
361 //OS Detection\r
362 getOperatingSystems = function () {\r
363 var systems = {},\r
364 value, key, keys, index, len, match, matched, version, activeCount;\r
365\r
366 keys = _getKeys(me.osPrefixes);\r
367 len = keys.length;\r
368 for (index = 0, activeCount = 0; index < len; index++) {\r
369 key = keys[index];\r
370 value = me.osPrefixes[key];\r
371 match = ua.match(new RegExp('(' + value + ')([^\\s;]+)'));\r
372 matched = match ? match[1] : null;\r
373\r
374 // This is here because some HTC android devices show an OSX Snow Leopard userAgent by default.\r
375 // And the Kindle Fire doesn't have any indicator of Android as the OS in its User Agent\r
376 if (matched && (matched === 'HTC_' || matched === 'Silk/')) {\r
377 version = 2.3;\r
378 } else {\r
379 version = match && match.length > 1 ? parseFloat(match[match.length - 1]) : 0;\r
380 }\r
381\r
382 if (version) {\r
383 activeCount++;\r
384 }\r
385 systems[key] = version;\r
386 }\r
387\r
388 keys = _getKeys(me.fallbackOSPrefixes);\r
389\r
390 // If no OS could be found we resort to the fallbacks, otherwise we just\r
391 // falsify the fallbacks\r
392 len = keys.length;\r
393 for (index = 0; index < len; index++) {\r
394 key = keys[index];\r
395\r
396 // No OS was detected from osPrefixes\r
397 if (activeCount === 0) {\r
398 value = me.fallbackOSPrefixes[key];\r
399 match = ua.toLowerCase().match(new RegExp(value));\r
400 systems[key] = match ? true : 0;\r
401 } else {\r
402 systems[key] = 0;\r
403 }\r
404 }\r
405\r
406 return systems;\r
407 },\r
408\r
409 // Device Detection\r
410 getDevices = function () {\r
411 var devices = {},\r
412 value, key, keys, index, len, match;\r
413\r
414 keys = _getKeys(me.devicePrefixes);\r
415 len = keys.length;\r
416 for (index = 0; index < len; index++) {\r
417 key = keys[index];\r
418 value = me.devicePrefixes[key];\r
419 match = ua.match(new RegExp(value));\r
420 devices[key] = match ? true : 0;\r
421 }\r
422\r
423 return devices;\r
424 },\r
425 browsers = getBrowsers(),\r
426 systems = getOperatingSystems(),\r
427 devices = getDevices(),\r
428 platformParams = Boot.loadPlatformsParam();\r
429\r
430 // We apply platformParams from the query here first to allow for forced user valued\r
431 // to be used in calculation of generated tags\r
432 _merge(_tags, browsers, systems, devices, platformParams, true);\r
433\r
434 _tags.phone = (_tags.iphone || _tags.ipod) ||\r
435 (!_tags.silk && (_tags.android && (_tags.android < 3 || isMobile))) ||\r
436 (_tags.blackberry && isMobile) ||\r
437 (_tags.windowsphone);\r
438\r
439 _tags.tablet = !_tags.phone && (\r
440 _tags.ipad ||\r
441 _tags.android ||\r
442 _tags.silk ||\r
443 _tags.rimtablet ||\r
444 (_tags.ie10 && /; Touch/.test(ua))\r
445 );\r
446\r
447 _tags.touch =\r
448 // if the browser has touch events we can be reasonably sure the device has\r
449 // a touch screen\r
450 isEventSupported('touchend') ||\r
451 // browsers that use pointer event have maxTouchPoints > 0 if the\r
452 // device supports touch input\r
453 // http://www.w3.org/TR/pointerevents/#widl-Navigator-maxTouchPoints\r
454 navigator.maxTouchPoints ||\r
455 // IE10 uses a vendor-prefixed maxTouchPoints property\r
456 navigator.msMaxTouchPoints;\r
457\r
458 _tags.desktop = !_tags.phone && !_tags.tablet;\r
459 _tags.cordova = _tags.phonegap = !!(window.PhoneGap || window.Cordova || window.cordova);\r
460 _tags.webview = /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)(?!.*FBAN)/i.test(ua);\r
461 _tags.androidstock = (_tags.android <= 4.3) && (_tags.safari || _tags.silk);\r
462\r
463 // Re-apply any query params here to allow for user override of generated tags (desktop, touch, tablet, etc)\r
464 _merge(_tags, platformParams, true);\r
465 },\r
466\r
467 /**\r
468 * Extracts user supplied platform tags from the "platformTags" query parameter\r
469 * of the form:\r
470 *\r
471 * ?platformTags=name:state,name:state,...\r
472 *\r
473 * (each tag defaults to true when state is unspecified)\r
474 *\r
475 * Example:\r
476 * ?platformTags=isTablet,isPhone:false,isDesktop:0,iOS:1,Safari:true, ...\r
477 *\r
478 * @returns {Object} the platform tags supplied by the query string\r
479 */\r
480 loadPlatformsParam: function () {\r
481 // Check if the ?platform parameter is set in the URL\r
482 var paramsString = window.location.search.substr(1),\r
483 paramsArray = paramsString.split("&"),\r
484 params = {}, i,\r
485 platforms = {},\r
486 tmpArray, tmplen, platform, name, enabled;\r
487\r
488 for (i = 0; i < paramsArray.length; i++) {\r
489 tmpArray = paramsArray[i].split("=");\r
490 params[tmpArray[0]] = tmpArray[1];\r
491 }\r
492\r
493 if (params.platformTags) {\r
494 tmpArray = params.platformTags.split(",");\r
495 for (tmplen = tmpArray.length, i = 0; i < tmplen; i++) {\r
496 platform = tmpArray[i].split(":");\r
497 name = platform[0];\r
498 enabled=true;\r
499 if (platform.length > 1) {\r
500 enabled = platform[1];\r
501 if (enabled === 'false' || enabled === '0') {\r
502 enabled = false;\r
503 }\r
504 }\r
505 platforms[name] = enabled;\r
506 }\r
507 }\r
508 return platforms;\r
509 },\r
510\r
511 filterPlatform: function (platform, excludes) {\r
512 platform = _emptyArray.concat(platform || _emptyArray);\r
513 excludes = _emptyArray.concat(excludes || _emptyArray);\r
514\r
515 var plen = platform.length,\r
516 elen = excludes.length,\r
517 include = (!plen && elen), // default true if only excludes specified\r
518 i, tag;\r
519\r
520 for (i = 0; i < plen && !include; i++) {\r
521 tag = platform[i];\r
522 include = !!_tags[tag];\r
523 }\r
524\r
525 for (i = 0; i < elen && include; i++) {\r
526 tag = excludes[i];\r
527 include = !_tags[tag];\r
528 }\r
529\r
530 return include;\r
531 },\r
532\r
533 init: function () {\r
534 var scriptEls = doc.getElementsByTagName('script'),\r
535 len = scriptEls.length,\r
536 re = /\/ext(\-[a-z\-]+)?\.js$/,\r
537 entry, script, src, state, baseUrl, key, n, origin;\r
538\r
539 // Since we are loading after other scripts, and we needed to gather them\r
540 // anyway, we track them in _scripts so we don't have to ask for them all\r
541 // repeatedly.\r
542 for (n = 0; n < len; n++) {\r
543 src = (script = scriptEls[n]).src;\r
544 if (!src) {\r
545 continue;\r
546 }\r
547 state = script.readyState || null;\r
548\r
549 // If we find a script file called "ext-*.js", then the base path is that file's base path.\r
550 if (!baseUrl) {\r
551 if (re.test(src)) {\r
552 Boot.hasReadyState = ("readyState" in script);\r
553 Boot.hasAsync = ("async" in script) || !Boot.hasReadyState;\r
554 baseUrl = src;\r
555 }\r
556 }\r
557\r
558 if (!Boot.scripts[key = Boot.canonicalUrl(src)]) {\r
559 //<debug>\r
560 _debug("creating entry " + key + " in Boot.init");\r
561 //</debug>\r
562 entry = new Entry({\r
563 key: key,\r
564 url: src,\r
565 done: state === null || // non-IE\r
566 state === 'loaded' || state === 'complete', // IE only\r
567 el: script,\r
568 prop: 'src'\r
569 });\r
570 }\r
571 }\r
572\r
573 if (!baseUrl) {\r
574 script = scriptEls[scriptEls.length - 1];\r
575 baseUrl = script.src;\r
576 Boot.hasReadyState = ('readyState' in script);\r
577 Boot.hasAsync = ("async" in script) || !Boot.hasReadyState;\r
578 }\r
579\r
580 Boot.baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);\r
581 origin = window.location.origin ||\r
582 window.location.protocol +\r
583 "//" +\r
584 window.location.hostname +\r
585 (window.location.port ? ':' + window.location.port: '');\r
586 Boot.origin = origin;\r
587\r
588 Boot.detectPlatformTags();\r
589 Ext.filterPlatform = Boot.filterPlatform;\r
590 },\r
591\r
592 /*\r
593 * This method returns a canonical URL for the given URL.\r
594 *\r
595 * For example, the following all produce the same canonical URL (which is the\r
596 * last one):\r
597 *\r
598 * http://foo.com/bar/baz/zoo/derp/../../goo/Thing.js?_dc=12345\r
599 * http://foo.com/bar/baz/zoo/derp/../../goo/Thing.js\r
600 * http://foo.com/bar/baz/zoo/derp/../jazz/../../goo/Thing.js\r
601 * http://foo.com/bar/baz/zoo/../goo/Thing.js\r
602 * http://foo.com/bar/baz/goo/Thing.js\r
603 *\r
604 * @private\r
605 */\r
606 canonicalUrl: function (url) {\r
607 // @TODO - see if we need this fallback logic\r
608 // http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue\r
609 resolverEl.href = url;\r
610\r
611 var ret = resolverEl.href,\r
612 dc = _config.disableCachingParam,\r
613 pos = dc ? ret.indexOf(dc + '=') : -1,\r
614 c, end;\r
615\r
616 // If we have a _dc query parameter we need to remove it from the canonical\r
617 // URL.\r
618 if (pos > 0 && ((c = ret.charAt(pos - 1)) === '?' || c === '&')) {\r
619 end = ret.indexOf('&', pos);\r
620 end = (end < 0) ? '' : ret.substring(end);\r
621 if (end && c === '?') {\r
622 ++pos; // keep the '?'\r
623 end = end.substring(1); // remove the '&'\r
624 }\r
625 ret = ret.substring(0, pos - 1) + end;\r
626 }\r
627\r
628 return ret;\r
629 },\r
630\r
631 /*\r
632 * Get the config value corresponding to the specified name. If no name is given, will return the config object\r
633 * @param {String} name The config property name\r
634 * @return {Object}\r
635 */\r
636 getConfig: function (name) {\r
637 return name ? Boot.config[name] : Boot.config;\r
638 },\r
639\r
640 /*\r
641 * Set the configuration.\r
642 * @param {Object} config The config object to override the default values.\r
643 * @return {Ext.Boot} this\r
644 */\r
645 setConfig: function (name, value) {\r
646 if (typeof name === 'string') {\r
647 Boot.config[name] = value;\r
648 } else {\r
649 for (var s in name) {\r
650 Boot.setConfig(s, name[s]);\r
651 }\r
652 }\r
653 return Boot;\r
654 },\r
655\r
656 getHead: function () {\r
657 return Boot.docHead ||\r
658 (Boot.docHead = doc.head ||\r
659 doc.getElementsByTagName('head')[0]);\r
660 },\r
661\r
662 create: function (url, key, cfg) {\r
663 var config = cfg || {};\r
664 config.url = url;\r
665 config.key = key;\r
666 return Boot.scripts[key] = new Entry(config);\r
667 },\r
668\r
669 getEntry: function (url, cfg) {\r
670 var key = Boot.canonicalUrl(url),\r
671 entry = Boot.scripts[key];\r
672 if (!entry) {\r
673 entry = Boot.create(url, key, cfg);\r
674 }\r
675 return entry;\r
676 },\r
677\r
678 registerContent: function (url, type, content) {\r
679 var cfg = {\r
680 content: content,\r
681 loaded: true,\r
682 css: type === 'css'\r
683 };\r
684\r
685 return Boot.getEntry(url, cfg);\r
686 },\r
687\r
688 processRequest: function(request, sync) {\r
689 request.loadEntries(sync);\r
690 },\r
691\r
692 load: function (request) {\r
693 //<debug>\r
694 _debug("Boot.load called");\r
695 //</debug>\r
696 var request = new Request(request);\r
697\r
698 if (request.sync || Boot.syncMode) {\r
699 return Boot.loadSync(request);\r
700 }\r
701\r
702 // If there is a request in progress, we must\r
703 // queue this new request to be fired when the current request completes.\r
704 if (Boot.currentRequest) {\r
705 //<debug>\r
706 _debug("current active request, suspending this request");\r
707 //</debug>\r
708 // trigger assignment of entries now to ensure that overlapping\r
709 // entries with currently running requests will synchronize state\r
710 // with this pending one as they complete\r
711 request.getEntries();\r
712 Boot.suspendedQueue.push(request);\r
713 } else {\r
714 Boot.currentRequest = request;\r
715 Boot.processRequest(request, false);\r
716 }\r
717 return Boot;\r
718 },\r
719\r
720 loadSync: function (request) {\r
721 //<debug>\r
722 _debug("Boot.loadSync called");\r
723 //</debug>\r
724 var request = new Request(request);\r
725\r
726 Boot.syncMode++;\r
727 Boot.processRequest(request, true);\r
728 Boot.syncMode--;\r
729 return Boot;\r
730 },\r
731\r
732 loadBasePrefix: function(request) {\r
733 request = new Request(request);\r
734 request.prependBaseUrl = true;\r
735 return Boot.load(request);\r
736 },\r
737\r
738 loadSyncBasePrefix: function(request) {\r
739 request = new Request(request);\r
740 request.prependBaseUrl = true;\r
741 return Boot.loadSync(request);\r
742 },\r
743\r
744 requestComplete: function(request) {\r
745 var next;\r
746\r
747 if (Boot.currentRequest === request) {\r
748 Boot.currentRequest = null;\r
749 while(Boot.suspendedQueue.length > 0) {\r
750 next = Boot.suspendedQueue.shift();\r
751 if(!next.done) {\r
752 //<debug>\r
753 _debug("resuming suspended request");\r
754 //</debug>\r
755 Boot.load(next);\r
756 break;\r
757 }\r
758 }\r
759 }\r
760 if (!Boot.currentRequest && Boot.suspendedQueue.length == 0) {\r
761 Boot.fireListeners();\r
762 }\r
763 },\r
764\r
765 isLoading: function () {\r
766 return !Boot.currentRequest && Boot.suspendedQueue.length == 0;\r
767 },\r
768\r
769 fireListeners: function () {\r
770 var listener;\r
771 while (Boot.isLoading() && (listener = Boot.listeners.shift())) {\r
772 listener();\r
773 }\r
774 },\r
775\r
776 onBootReady: function (listener) {\r
777 if (!Boot.isLoading()) {\r
778 listener();\r
779 } else {\r
780 Boot.listeners.push(listener);\r
781 }\r
782 },\r
783\r
784 /*\r
785 * this is a helper function used by Ext.Loader to flush out\r
786 * 'uses' arrays for classes\r
787 */\r
788 getPathsFromIndexes: function (indexMap, loadOrder) {\r
789 return Request.prototype.getPathsFromIndexes(indexMap, loadOrder);\r
790 },\r
791\r
792 createLoadOrderMap: function(loadOrder) {\r
793 return Request.prototype.createLoadOrderMap(loadOrder);\r
794 },\r
795\r
796 fetch: function(url, complete, scope, async) {\r
797 async = (async === undefined) ? !!complete : async;\r
798\r
799 var xhr = new XMLHttpRequest(),\r
800 result, status, content, exception = false,\r
801 readyStateChange = function () {\r
802 if (xhr && xhr.readyState == 4) {\r
803 status = (xhr.status === 1223) ? 204 :\r
804 (xhr.status === 0 && ((self.location || {}).protocol === 'file:' ||\r
805 (self.location || {}).protocol === 'ionp:')) ? 200 : xhr.status;\r
806 content = xhr.responseText;\r
807 result = {\r
808 content: content,\r
809 status: status,\r
810 exception: exception\r
811 };\r
812 if (complete) {\r
813 complete.call(scope, result);\r
814 }\r
815 xhr = null;\r
816 }\r
817 };\r
818\r
819 if (async) {\r
820 xhr.onreadystatechange = readyStateChange;\r
821 }\r
822\r
823 try {\r
824 //<debug>\r
825 _debug("fetching " + url + " " + (async ? "async" : "sync"));\r
826 //</debug>\r
827 xhr.open('GET', url, async);\r
828 xhr.send(null);\r
829 } catch (err) {\r
830 exception = err;\r
831 readyStateChange();\r
832 return result;\r
833 }\r
834\r
835 if (!async) {\r
836 readyStateChange();\r
837 }\r
838\r
839 return result;\r
840 },\r
841\r
842 notifyAll: function(entry) {\r
843 entry.notifyRequests();\r
844 }\r
845 };\r
846\r
847 /*\r
848 * The request class encapsulates a series of Entry objects\r
849 * and provides notification around the completion of all Entries\r
850 * in this request.\r
851 */\r
852 function Request(cfg) {\r
853 if(cfg.$isRequest) {\r
854 return cfg;\r
855 }\r
856\r
857 var cfg = cfg.url ? cfg : {url: cfg},\r
858 url = cfg.url,\r
859 urls = url.charAt ? [ url ] : url,\r
860 charset = cfg.charset || Boot.config.charset;\r
861\r
862 _apply(cfg, {\r
863 urls: urls,\r
864 charset: charset\r
865 });\r
866 _apply(this, cfg);\r
867 };\r
868 Request.prototype = {\r
869 $isRequest: true,\r
870\r
871 /*\r
872 * @private\r
873 * @param manifest\r
874 * @returns {*}\r
875 */\r
876 createLoadOrderMap: function (loadOrder) {\r
877 var len = loadOrder.length,\r
878 loadOrderMap = {},\r
879 i, element;\r
880\r
881 for (i = 0; i < len; i++) {\r
882 element = loadOrder[i];\r
883 loadOrderMap[element.path] = element;\r
884 }\r
885\r
886 return loadOrderMap;\r
887 },\r
888\r
889 /*\r
890 * @private\r
891 * @param index\r
892 * @param indexMap\r
893 * @returns {{}}\r
894 */\r
895 getLoadIndexes: function (index, indexMap, loadOrder, includeUses, skipLoaded) {\r
896 var item = loadOrder[index],\r
897 len, i, reqs, entry, stop, added, idx, ridx, url;\r
898\r
899 if (indexMap[index]) {\r
900 // prevent cycles\r
901 return indexMap;\r
902 }\r
903\r
904 indexMap[index] = true;\r
905\r
906 stop = false;\r
907 while (!stop) {\r
908 added = false;\r
909\r
910 // iterate the requirements for each index and\r
911 // accumulate in the index map\r
912 for (idx in indexMap) {\r
913 if (indexMap.hasOwnProperty(idx)) {\r
914 item = loadOrder[idx];\r
915 if (!item) {\r
916 continue;\r
917 }\r
918 url = this.prepareUrl(item.path);\r
919 entry = Boot.getEntry(url);\r
920 if (!skipLoaded || !entry || !entry.done) {\r
921 reqs = item.requires;\r
922 if (includeUses && item.uses) {\r
923 reqs = reqs.concat(item.uses);\r
924 }\r
925 for (len = reqs.length, i = 0; i < len; i++) {\r
926 ridx = reqs[i];\r
927 // if we find a requirement that wasn't\r
928 // already in the index map,\r
929 // set the added flag to indicate we need to\r
930 // reprocess\r
931 if (!indexMap[ridx]) {\r
932 indexMap[ridx] = true;\r
933 added = true;\r
934 }\r
935 }\r
936 }\r
937 }\r
938 }\r
939\r
940 // if we made a pass through the index map and didn't add anything\r
941 // then we can stop\r
942 if (!added) {\r
943 stop = true;\r
944 }\r
945 }\r
946\r
947 return indexMap;\r
948 },\r
949\r
950 getPathsFromIndexes: function (indexMap, loadOrder) {\r
951 var indexes = [],\r
952 paths = [],\r
953 index, len, i;\r
954\r
955 for (index in indexMap) {\r
956 if (indexMap.hasOwnProperty(index) && indexMap[index]) {\r
957 indexes.push(index);\r
958 }\r
959 }\r
960\r
961 indexes.sort(function (a, b) {\r
962 return a - b;\r
963 });\r
964\r
965 // convert indexes back into load paths\r
966 for (len = indexes.length, i = 0; i < len; i++) {\r
967 paths.push(loadOrder[indexes[i]].path);\r
968 }\r
969\r
970 return paths;\r
971 },\r
972\r
973 expandUrl: function (url, indexMap, includeUses, skipLoaded) {\r
974 if (typeof url == 'string') {\r
975 url = [url];\r
976 }\r
977\r
978 var me = this,\r
979 loadOrder = me.loadOrder,\r
980 loadOrderMap = me.loadOrderMap;\r
981\r
982 if (loadOrder) {\r
983 loadOrderMap = loadOrderMap || me.createLoadOrderMap(loadOrder);\r
984 me.loadOrderMap = loadOrderMap;\r
985 indexMap = indexMap || {};\r
986 var len = url.length,\r
987 unmapped = [],\r
988 i, item;\r
989\r
990 for (i = 0; i < len; i++) {\r
991 item = loadOrderMap[url[i]];\r
992 if (item) {\r
993 me.getLoadIndexes(item.idx, indexMap, loadOrder, includeUses, skipLoaded);\r
994 } else {\r
995 unmapped.push(url[i]);\r
996 }\r
997 }\r
998\r
999\r
1000 return me.getPathsFromIndexes(indexMap, loadOrder).concat(unmapped);\r
1001 }\r
1002 return url;\r
1003 },\r
1004\r
1005 expandUrls: function (urls, includeUses) {\r
1006 if (typeof urls == "string") {\r
1007 urls = [urls];\r
1008 }\r
1009\r
1010 var expanded = [],\r
1011 expandMap = {},\r
1012 tmpExpanded,\r
1013 len = urls.length,\r
1014 i, t, tlen, tUrl;\r
1015\r
1016 for (i = 0; i < len; i++) {\r
1017 tmpExpanded = this.expandUrl(urls[i], {}, includeUses, true);\r
1018 for (t = 0, tlen = tmpExpanded.length; t < tlen; t++) {\r
1019 tUrl = tmpExpanded[t];\r
1020 if (!expandMap[tUrl]) {\r
1021 expandMap[tUrl] = true;\r
1022 expanded.push(tUrl);\r
1023 }\r
1024 }\r
1025 }\r
1026\r
1027 if (expanded.length == 0) {\r
1028 expanded = urls;\r
1029 }\r
1030\r
1031 return expanded;\r
1032 },\r
1033\r
1034 expandLoadOrder: function () {\r
1035 var me = this,\r
1036 urls = me.urls,\r
1037 expanded;\r
1038\r
1039 if (!me.expanded) {\r
1040 expanded = this.expandUrls(urls, true);\r
1041 me.expanded = true;\r
1042 } else {\r
1043 expanded = urls;\r
1044 }\r
1045\r
1046 me.urls = expanded;\r
1047\r
1048 // if we added some urls to the request to honor the indicated\r
1049 // load order, the request needs to be sequential\r
1050 if (urls.length != expanded.length) {\r
1051 me.sequential = true;\r
1052 }\r
1053\r
1054 return me;\r
1055 },\r
1056\r
1057 getUrls: function () {\r
1058 this.expandLoadOrder();\r
1059 return this.urls;\r
1060 },\r
1061\r
1062 prepareUrl: function(url) {\r
1063 if(this.prependBaseUrl) {\r
1064 return Boot.baseUrl + url;\r
1065 }\r
1066 return url;\r
1067 },\r
1068\r
1069 getEntries: function () {\r
1070 var me = this,\r
1071 entries = me.entries,\r
1072 i, entry, urls, url;\r
1073 if (!entries) {\r
1074 entries = [];\r
1075 urls = me.getUrls();\r
1076 for (i = 0; i < urls.length; i++) {\r
1077 url = me.prepareUrl(urls[i]);\r
1078 entry = Boot.getEntry(url, {\r
1079 buster: me.buster,\r
1080 charset: me.charset\r
1081 });\r
1082 entry.requests.push(me);\r
1083 entries.push(entry);\r
1084 }\r
1085 me.entries = entries;\r
1086 }\r
1087 return entries;\r
1088 },\r
1089\r
1090 loadEntries: function(sync) {\r
1091 var me = this,\r
1092 entries = me.getEntries(),\r
1093 len = entries.length,\r
1094 start = me.loadStart || 0,\r
1095 continueLoad, entry, i;\r
1096\r
1097 if(sync !== undefined) {\r
1098 me.sync = sync;\r
1099 }\r
1100\r
1101 me.loaded = me.loaded || 0;\r
1102 me.loading = me.loading || len;\r
1103\r
1104 for(i = start; i < len; i++) {\r
1105 entry = entries[i];\r
1106 if(!entry.loaded) {\r
1107 continueLoad = entries[i].load(me.sync);\r
1108 } else {\r
1109 continueLoad = true;\r
1110 }\r
1111 if(!continueLoad) {\r
1112 me.loadStart = i;\r
1113 entry.onDone(function(){\r
1114 me.loadEntries(sync);\r
1115 });\r
1116 break;\r
1117 }\r
1118 }\r
1119 me.processLoadedEntries();\r
1120 },\r
1121\r
1122 processLoadedEntries: function () {\r
1123 var me = this,\r
1124 entries = me.getEntries(),\r
1125 len = entries.length,\r
1126 start = me.startIndex || 0,\r
1127 i, entry;\r
1128\r
1129 if (!me.done) {\r
1130 for (i = start; i < len; i++) {\r
1131 entry = entries[i];\r
1132\r
1133 if (!entry.loaded) {\r
1134 me.startIndex = i;\r
1135 return;\r
1136 }\r
1137\r
1138 if (!entry.evaluated) {\r
1139 entry.evaluate();\r
1140 }\r
1141\r
1142 if (entry.error) {\r
1143 me.error = true;\r
1144 }\r
1145 }\r
1146 me.notify();\r
1147 }\r
1148 },\r
1149\r
1150 notify: function () {\r
1151 var me = this;\r
1152 if (!me.done) {\r
1153 var error = me.error,\r
1154 fn = me[error ? 'failure' : 'success'],\r
1155 delay = ('delay' in me)\r
1156 ? me.delay\r
1157 : (error ? 1 : Boot.config.chainDelay),\r
1158 scope = me.scope || me;\r
1159 me.done = true;\r
1160 if (fn) {\r
1161 if (delay === 0 || delay > 0) {\r
1162 // Free the stack (and defer the next script)\r
1163 setTimeout(function () {\r
1164 fn.call(scope, me);\r
1165 }, delay);\r
1166 } else {\r
1167 fn.call(scope, me);\r
1168 }\r
1169 }\r
1170 me.fireListeners();\r
1171 Boot.requestComplete(me);\r
1172 }\r
1173 },\r
1174\r
1175 onDone: function(listener) {\r
1176 var me = this,\r
1177 listeners = me.listeners || (me.listeners = []);\r
1178 if(me.done) {\r
1179 listener(me);\r
1180 } else {\r
1181 listeners.push(listener);\r
1182 }\r
1183 },\r
1184\r
1185 fireListeners: function() {\r
1186 var listeners = this.listeners,\r
1187 listener;\r
1188 if(listeners) {\r
1189 //<debug>\r
1190 _debug("firing request listeners");\r
1191 //</debug>\r
1192 while((listener = listeners.shift())) {\r
1193 listener(this);\r
1194 }\r
1195 }\r
1196 }\r
1197 };\r
1198\r
1199 /*\r
1200 * The Entry class is a token to manage the load and evaluation\r
1201 * state of a particular url. It is used to notify all Requests\r
1202 * interested in this url that the content is available.\r
1203 */\r
1204 function Entry(cfg) {\r
1205 if(cfg.$isEntry) {\r
1206 return cfg;\r
1207 }\r
1208\r
1209 //<debug>\r
1210 _debug("creating entry for " + cfg.url);\r
1211 //</debug>\r
1212\r
1213 var charset = cfg.charset || Boot.config.charset,\r
1214 manifest = Ext.manifest,\r
1215 loader = manifest && manifest.loader,\r
1216 cache = (cfg.cache !== undefined) ? cfg.cache : (loader && loader.cache),\r
1217 buster, busterParam;\r
1218\r
1219 if (Boot.config.disableCaching) {\r
1220 if (cache === undefined) {\r
1221 cache = !Boot.config.disableCaching;\r
1222 }\r
1223\r
1224 if (cache === false) {\r
1225 buster = +new Date();\r
1226 } else if (cache !== true) {\r
1227 buster = cache;\r
1228 }\r
1229\r
1230 if (buster) {\r
1231 busterParam = (loader && loader.cacheParam) || Boot.config.disableCachingParam;\r
1232 buster = busterParam + "=" + buster;\r
1233 }\r
1234 }\r
1235\r
1236 _apply(cfg, {\r
1237 charset: charset,\r
1238 buster: buster,\r
1239 requests: []\r
1240 });\r
1241 _apply(this, cfg);\r
1242 };\r
1243 Entry.prototype = {\r
1244 $isEntry: true,\r
1245 done: false,\r
1246 evaluated: false,\r
1247 loaded: false,\r
1248\r
1249 isCrossDomain: function() {\r
1250 var me = this;\r
1251 if(me.crossDomain === undefined) {\r
1252 //<debug>\r
1253 _debug("checking " + me.getLoadUrl() + " for prefix " + Boot.origin);\r
1254 //</debug>\r
1255 me.crossDomain = (me.getLoadUrl().indexOf(Boot.origin) !== 0);\r
1256 }\r
1257 return me.crossDomain;\r
1258 },\r
1259\r
1260 isCss: function () {\r
1261 var me = this;\r
1262 if (me.css === undefined) {\r
1263 if (me.url) {\r
1264 var assetConfig = Boot.assetConfig[me.url];\r
1265 me.css = assetConfig ? assetConfig.type === "css" : cssRe.test(me.url);\r
1266 } else {\r
1267 me.css = false;\r
1268 }\r
1269 }\r
1270 return this.css;\r
1271 },\r
1272\r
1273 getElement: function (tag) {\r
1274 var me = this,\r
1275 el = me.el;\r
1276 if (!el) {\r
1277 //<debug>\r
1278 _debug("creating element for " + me.url);\r
1279 //</debug>\r
1280 if (me.isCss()) {\r
1281 tag = tag || "link";\r
1282 el = doc.createElement(tag);\r
1283 if(tag == "link") {\r
1284 el.rel = 'stylesheet';\r
1285 me.prop = 'href';\r
1286 } else {\r
1287 me.prop="textContent";\r
1288 }\r
1289 el.type = "text/css";\r
1290 } else {\r
1291 tag = tag || "script";\r
1292 el = doc.createElement(tag);\r
1293 el.type = 'text/javascript';\r
1294 me.prop = 'src';\r
1295\r
1296 if (me.charset) {\r
1297 el.charset = me.charset;\r
1298 }\r
1299\r
1300 if (Boot.hasAsync) {\r
1301 el.async = false;\r
1302 }\r
1303 }\r
1304 me.el = el;\r
1305 }\r
1306 return el;\r
1307 },\r
1308\r
1309 getLoadUrl: function () {\r
1310 var me = this,\r
1311 url = Boot.canonicalUrl(me.url);\r
1312 if (!me.loadUrl) {\r
1313 me.loadUrl = !!me.buster\r
1314 ? (url + (url.indexOf('?') === -1 ? '?' : '&') + me.buster)\r
1315 : url;\r
1316 }\r
1317 return me.loadUrl;\r
1318 },\r
1319\r
1320 fetch: function (req) {\r
1321 var url = this.getLoadUrl(),\r
1322 async = !!req.async,\r
1323 complete = req.complete;\r
1324\r
1325 Boot.fetch(url, complete, this, async);\r
1326 },\r
1327\r
1328 onContentLoaded: function (response) {\r
1329 var me = this,\r
1330 status = response.status,\r
1331 content = response.content,\r
1332 exception = response.exception,\r
1333 url = this.getLoadUrl();\r
1334 me.loaded = true;\r
1335 if ((exception || status === 0) && !_environment.phantom) {\r
1336 me.error =\r
1337 //<debug>\r
1338 ("Failed loading synchronously via XHR: '" + url +\r
1339 "'. It's likely that the file is either being loaded from a " +\r
1340 "different domain or from the local file system where cross " +\r
1341 "origin requests are not allowed for security reasons. Try " +\r
1342 "asynchronous loading instead.") ||\r
1343 //</debug>\r
1344 true;\r
1345 me.evaluated = true;\r
1346 }\r
1347 else if ((status >= 200 && status < 300) || status === 304\r
1348 || _environment.phantom\r
1349 || (status === 0 && content.length > 0)\r
1350 ) {\r
1351 me.content = content;\r
1352 }\r
1353 else {\r
1354 me.error =\r
1355 //<debug>\r
1356 ("Failed loading synchronously via XHR: '" + url +\r
1357 "'. Please verify that the file exists. XHR status code: " +\r
1358 status) ||\r
1359 //</debug>\r
1360 true;\r
1361 me.evaluated = true;\r
1362 }\r
1363 },\r
1364\r
1365 createLoadElement: function(callback) {\r
1366 var me = this,\r
1367 el = me.getElement(),\r
1368 readyStateChange = function(){\r
1369 if (this.readyState === 'loaded' || this.readyState === 'complete') {\r
1370 if(callback) {\r
1371 callback();\r
1372 }\r
1373 }\r
1374 },\r
1375 errorFn = function() {\r
1376 me.error = true;\r
1377 if(callback) {\r
1378 callback();\r
1379 }\r
1380 };\r
1381 me.preserve = true;\r
1382 el.onerror = errorFn;\r
1383 if(Boot.hasReadyState) {\r
1384 el.onreadystatechange = readyStateChange;\r
1385 } else {\r
1386 el.onload = callback;\r
1387 }\r
1388 // IE starts loading here\r
1389 el[me.prop] = me.getLoadUrl();\r
1390 },\r
1391\r
1392 onLoadElementReady: function() {\r
1393 Boot.getHead().appendChild(this.getElement());\r
1394 this.evaluated = true;\r
1395 },\r
1396\r
1397 inject: function (content, asset) {\r
1398 //<debug>\r
1399 _debug("injecting content for " + this.url);\r
1400 //</debug>\r
1401 var me = this,\r
1402 head = Boot.getHead(),\r
1403 url = me.url,\r
1404 key = me.key,\r
1405 base, el, ieMode, basePath;\r
1406\r
1407 if (me.isCss()) {\r
1408 me.preserve = true;\r
1409 basePath = key.substring(0, key.lastIndexOf("/") + 1);\r
1410 base = doc.createElement('base');\r
1411 base.href = basePath;\r
1412 if(head.firstChild) {\r
1413 head.insertBefore(base, head.firstChild);\r
1414 } else {\r
1415 head.appendChild(base);\r
1416 }\r
1417 // reset the href attribute to cuase IE to pick up the change\r
1418 base.href = base.href;\r
1419\r
1420 if (url) {\r
1421 content += "\n/*# sourceURL=" + key + " */";\r
1422 }\r
1423\r
1424 // create element after setting base\r
1425 el = me.getElement("style");\r
1426\r
1427 ieMode = ('styleSheet' in el);\r
1428\r
1429 head.appendChild(base);\r
1430 if(ieMode) {\r
1431 head.appendChild(el);\r
1432 el.styleSheet.cssText = content;\r
1433 } else {\r
1434 el.textContent = content;\r
1435 head.appendChild(el);\r
1436 }\r
1437 head.removeChild(base);\r
1438\r
1439 } else {\r
1440 // Debugger friendly, file names are still shown even though they're\r
1441 // eval'ed code. Breakpoints work on both Firebug and Chrome's Web\r
1442 // Inspector.\r
1443 if (url) {\r
1444 content += "\n//# sourceURL=" + key;\r
1445 }\r
1446 Ext.globalEval(content);\r
1447 }\r
1448 return me;\r
1449 },\r
1450\r
1451 loadCrossDomain: function() {\r
1452 var me = this,\r
1453 complete = function(){\r
1454 me.loaded = me.evaluated = me.done = true;\r
1455 me.notifyRequests();\r
1456 };\r
1457 me.createLoadElement(function(){\r
1458 complete();\r
1459 });\r
1460 me.evaluateLoadElement();\r
1461 // at this point, we need sequential evaluation,\r
1462 // which means we can't advance the load until\r
1463 // this entry has fully completed\r
1464 return false;\r
1465 },\r
1466\r
1467 loadElement: function() {\r
1468 var me = this,\r
1469 complete = function(){\r
1470 me.loaded = me.evaluated = me.done = true;\r
1471 me.notifyRequests();\r
1472 };\r
1473 me.createLoadElement(function(){\r
1474 complete();\r
1475 });\r
1476 me.evaluateLoadElement();\r
1477 return true;\r
1478 },\r
1479\r
1480 loadSync: function() {\r
1481 var me = this;\r
1482 me.fetch({\r
1483 async: false,\r
1484 complete: function (response) {\r
1485 me.onContentLoaded(response);\r
1486 }\r
1487 });\r
1488 me.evaluate();\r
1489 me.notifyRequests();\r
1490 },\r
1491\r
1492 load: function (sync) {\r
1493 var me = this;\r
1494 if (!me.loaded) {\r
1495 if(me.loading) {\r
1496 // if we're calling back through load and we're loading but haven't\r
1497 // yet loaded, then we should be in a sequential, cross domain\r
1498 // load scenario which means we can't continue the load on the\r
1499 // request until this entry has fully evaluated, which will mean\r
1500 // loaded = evaluated = done = true in one step. For css files, this\r
1501 // will happen immediately upon <link> element creation / insertion,\r
1502 // but <script> elements will set this upon load notification\r
1503 return false;\r
1504 }\r
1505 me.loading = true;\r
1506\r
1507 // for async modes, we have some options\r
1508 if (!sync) {\r
1509 // if cross domain, just inject the script tag and let the onload\r
1510 // events drive the progression\r
1511 if(me.isCrossDomain()) {\r
1512 return me.loadCrossDomain();\r
1513 }\r
1514 // for IE, use the readyStateChange allows us to load scripts in parallel\r
1515 // but serialize the evaluation by appending the script node to the\r
1516 // document\r
1517 else if(!me.isCss() && Boot.hasReadyState) {\r
1518 me.createLoadElement(function () {\r
1519 me.loaded = true;\r
1520 me.notifyRequests();\r
1521 });\r
1522 }\r
1523\r
1524 else if(Boot.useElements &&\r
1525 // older webkit, phantomjs included, won't fire load for link elements\r
1526 !(me.isCss() && _environment.phantom)) {\r
1527 return me.loadElement();\r
1528 }\r
1529 // for other browsers, just ajax the content down in parallel, and use\r
1530 // globalEval to serialize evaluation\r
1531 else {\r
1532 me.fetch({\r
1533 async: !sync,\r
1534 complete: function (response) {\r
1535 me.onContentLoaded(response);\r
1536 me.notifyRequests();\r
1537 }\r
1538 });\r
1539 }\r
1540 }\r
1541\r
1542 // for sync mode in js, global eval FTW. IE won't honor the comment\r
1543 // paths in the debugger, so eventually we need a sync mode for IE that\r
1544 // uses the readyStateChange mechanism\r
1545 else {\r
1546 me.loadSync();\r
1547 }\r
1548 }\r
1549 // signal that the load process can continue\r
1550 return true;\r
1551 },\r
1552\r
1553 evaluateContent: function () {\r
1554 this.inject(this.content);\r
1555 this.content = null;\r
1556 },\r
1557\r
1558 evaluateLoadElement: function() {\r
1559 Boot.getHead().appendChild(this.getElement());\r
1560 },\r
1561\r
1562 evaluate: function () {\r
1563 var me = this;\r
1564 if(!me.evaluated) {\r
1565 if(me.evaluating) {\r
1566 return;\r
1567 }\r
1568 me.evaluating = true;\r
1569 if(me.content !== undefined) {\r
1570 me.evaluateContent();\r
1571 } else if(!me.error) {\r
1572 me.evaluateLoadElement();\r
1573 }\r
1574 me.evaluated = me.done = true;\r
1575 me.cleanup();\r
1576 }\r
1577 },\r
1578\r
1579 /*\r
1580 * @private\r
1581 */\r
1582 cleanup: function () {\r
1583 var me = this,\r
1584 el = me.el,\r
1585 prop;\r
1586\r
1587 if (!el) {\r
1588 return;\r
1589 }\r
1590\r
1591 if (!me.preserve) {\r
1592 me.el = null;\r
1593\r
1594 el.parentNode.removeChild(el); // Remove, since its useless now\r
1595\r
1596 for (prop in el) {\r
1597 try {\r
1598 if (prop !== me.prop) {\r
1599 // If we set the src property to null IE\r
1600 // will try and request a script at './null'\r
1601 el[prop] = null;\r
1602 }\r
1603 delete el[prop]; // and prepare for GC\r
1604 } catch (cleanEx) {\r
1605 //ignore\r
1606 }\r
1607 }\r
1608 }\r
1609\r
1610 // Setting to null can cause exceptions if IE ever needs to call these\r
1611 // again (like onreadystatechange). This emptyFn has nothing locked in\r
1612 // closure scope so it is about as safe as null for memory leaks.\r
1613 el.onload = el.onerror = el.onreadystatechange = emptyFn;\r
1614 },\r
1615\r
1616 notifyRequests: function () {\r
1617 var requests = this.requests,\r
1618 len = requests.length,\r
1619 i, request;\r
1620 for (i = 0; i < len; i++) {\r
1621 request = requests[i];\r
1622 request.processLoadedEntries();\r
1623 }\r
1624 if(this.done) {\r
1625 this.fireListeners();\r
1626 }\r
1627 },\r
1628\r
1629 onDone: function(listener) {\r
1630 var me = this,\r
1631 listeners = me.listeners || (me.listeners = []);\r
1632 if(me.done) {\r
1633 listener(me);\r
1634 } else {\r
1635 listeners.push(listener);\r
1636 }\r
1637 },\r
1638\r
1639 fireListeners: function() {\r
1640 var listeners = this.listeners,\r
1641 listener;\r
1642 if(listeners && listeners.length > 0) {\r
1643 //<debug>\r
1644 _debug("firing event listeners for url " + this.url);\r
1645 //</debug>\r
1646 while((listener = listeners.shift())) {\r
1647 listener(this);\r
1648 }\r
1649 }\r
1650 }\r
1651 };\r
1652\r
1653 /*\r
1654 * Turns on or off the "cache buster" applied to dynamically loaded scripts. Normally\r
1655 * dynamically loaded scripts have an extra query parameter appended to avoid stale\r
1656 * cached scripts. This method can be used to disable this mechanism, and is primarily\r
1657 * useful for testing. This is done using a cookie.\r
1658 * @param {Boolean} disable True to disable the cache buster.\r
1659 * @param {String} [path="/"] An optional path to scope the cookie.\r
1660 */\r
1661 Ext.disableCacheBuster = function (disable, path) {\r
1662 var date = new Date();\r
1663 date.setTime(date.getTime() + (disable ? 10 * 365 : -1) * 24 * 60 * 60 * 1000);\r
1664 date = date.toGMTString();\r
1665 doc.cookie = 'ext-cache=1; expires=' + date + '; path=' + (path || '/');\r
1666 };\r
1667\r
1668//<if nonBrowser>\r
1669 if (_environment.node) {\r
1670 Boot.prototype.load = Boot.prototype.loadSync = function (request) {\r
1671 // @TODO\r
1672 require(filePath);\r
1673 onLoad.call(scope);\r
1674 };\r
1675 Boot.prototype.init = emptyFn;\r
1676 }\r
1677//</if>\r
1678\r
1679 Boot.init();\r
1680 return Boot;\r
1681\r
1682// NOTE: We run the eval at global scope to protect the body of the function and allow\r
1683// compressors to still process it.\r
1684}(function () {\r
1685}));//(eval("/*@cc_on!@*/!1"));\r
1686\r
1687/*\r
1688 * This method evaluates the given code free of any local variable. This\r
1689 * will be at global scope, in others it will be in a function.\r
1690 * @parma {String} code The code to evaluate.\r
1691 * @private\r
1692 * @method\r
1693 */\r
1694Ext.globalEval = Ext.globalEval || (this.execScript\r
1695 ? function (code) { execScript(code); }\r
1696 : function ($$code) { eval.call(window, $$code); });\r
1697\r
1698//<feature legacyBrowser>\r
1699/*\r
1700 * Only IE8 & IE/Quirks lack Function.prototype.bind so we polyfill that here.\r
1701 */\r
1702if (!Function.prototype.bind) {\r
1703 (function () {\r
1704 var slice = Array.prototype.slice,\r
1705 // To reduce overhead on call of the bound fn we have two flavors based on\r
1706 // whether we have args to prepend or not:\r
1707 bind = function (me) {\r
1708 var args = slice.call(arguments, 1),\r
1709 method = this;\r
1710\r
1711 if (args.length) {\r
1712 return function () {\r
1713 var t = arguments;\r
1714 // avoid the slice/concat if the caller does not supply args\r
1715 return method.apply(me, t.length ? args.concat(slice.call(t)) : args);\r
1716 };\r
1717 }\r
1718 // this is the majority use case - just fn.bind(this) and no args\r
1719\r
1720 args = null;\r
1721 return function () {\r
1722 return method.apply(me, arguments);\r
1723 };\r
1724 };\r
1725 Function.prototype.bind = bind;\r
1726 bind.$extjs = true; // to detect this polyfill if one want to improve it\r
1727 }());\r
1728}\r
1729//</feature>\r
1730\r
1731//</editor-fold>\r
1732\r
1733Ext.setResourcePath = function (poolName, path) {\r
1734 var manifest = Ext.manifest || (Ext.manifest = {}),\r
1735 paths = manifest.resources || (manifest.resources = {});\r
1736\r
1737 if (manifest) {\r
1738 if (typeof poolName !== 'string') {\r
1739 Ext.apply(paths, poolName);\r
1740 } else {\r
1741 paths[poolName] = path;\r
1742 }\r
1743 manifest.resources = paths;\r
1744 }\r
1745};\r
1746\r
1747Ext.getResourcePath = function (path, poolName, packageName) {\r
1748 if (typeof path !== 'string') {\r
1749 poolName = path.pool;\r
1750 packageName = path.packageName;\r
1751 path = path.path;\r
1752 }\r
1753 var manifest = Ext.manifest,\r
1754 paths = manifest && manifest.resources,\r
1755 poolPath = paths[poolName],\r
1756 output = [];\r
1757\r
1758 if (poolPath == null) {\r
1759 poolPath = paths.path;\r
1760 if (poolPath == null) {\r
1761 poolPath = 'resources';\r
1762 }\r
1763 }\r
1764\r
1765 if (poolPath) {\r
1766 output.push(poolPath);\r
1767 }\r
1768\r
1769 if (packageName) {\r
1770 output.push(packageName);\r
1771 }\r
1772\r
1773 output.push(path);\r
1774 return output.join('/');\r
1775};