]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | // @tag core\r |
2 | // @define Ext.Boot\r | |
3 | \r | |
4 | var Ext = Ext || {};\r | |
5 | \r | |
6 | //<editor-fold desc="Boot">\r | |
7 | /*\r | |
8 | * @class Ext.Boot\r | |
9 | * @singleton\r | |
10 | */\r | |
11 | Ext.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 | |
1694 | Ext.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 | |
1702 | if (!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 | |
1733 | Ext.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 | |
1747 | Ext.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 | }; |