]> git.proxmox.com Git - extjs.git/blame - extjs/modern/theme-device-base/.sencha/package/Boot.js
add extjs 6.0.1 sources
[extjs.git] / extjs / modern / theme-device-base / .sencha / package / Boot.js
CommitLineData
6527f429
DM
1// @tag core\r
2// @define Ext.Boot\r
3\r
4var Ext = Ext || {};\r
5\r
6//<editor-fold desc="Boot">\r
7/*\r
8 * @class Ext.Boot\r
9 * @singleton\r
10 */\r
11Ext.Boot = Ext.Boot || (function (emptyFn) {\r
12\r
13 var doc = document,\r
14 _emptyArray = [],\r
15 _config = {\r
16 /*\r
17 * @cfg {Boolean} [disableCaching=true]\r
18 * If `true` current timestamp is added to script URL's to prevent caching.\r
19 * In debug builds, adding a "cache" or "disableCacheBuster" query parameter\r
20 * to the page's URL will set this to `false`.\r
21 */\r
22 disableCaching: (/[?&](?:cache|disableCacheBuster)\b/i.test(location.search) ||\r
23 !(/http[s]?\:/i.test(location.href)) ||\r
24 /(^|[ ;])ext-cache=1/.test(doc.cookie)) ? false :\r
25 true,\r
26\r
27 /*\r
28 * @cfg {String} [disableCachingParam="_dc"]\r
29 * The query parameter name for the cache buster's timestamp.\r
30 */\r
31 disableCachingParam: '_dc',\r
32\r
33 /*\r
34 * @cfg {Boolean} loadDelay\r
35 * Millisecond delay between asynchronous script injection (prevents stack\r
36 * overflow on some user agents) 'false' disables delay but potentially\r
37 * increases stack load.\r
38 */\r
39 loadDelay: false,\r
40\r
41 /*\r
42 * @cfg {Boolean} preserveScripts\r
43 * `false` to remove asynchronously loaded scripts, `true` to retain script\r
44 * element for browser debugger compatibility and improved load performance.\r
45 */\r
46 preserveScripts: true,\r
47\r
48 /*\r
49 * @cfg {String} charset\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
1472Ext.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
1480if (!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