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