]>
git.proxmox.com Git - extjs.git/blob - extjs/examples/modern/energy/sass/example/bootstrap.js
6 //<editor-fold desc="Boot">
11 Ext
. Boot
= Ext
. Boot
|| ( function ( emptyFn
) {
17 * @cfg {Boolean} [disableCaching=true]
18 * If `true` current timestamp is added to script URL's to prevent caching.
19 * In debug builds, adding a "cache" or "disableCacheBuster" query parameter
20 * to the page's URL will set this to `false`.
22 disableCaching
: ( /[?&](?:cache|disableCacheBuster)\b/i . test ( location
. search
) ||
23 !( /http[s]?\:/i . test ( location
. href
)) ||
24 /(^|[ ;])ext-cache=1/ . test ( doc
. cookie
)) ? false :
28 * @cfg {String} [disableCachingParam="_dc"]
29 * The query parameter name for the cache buster's timestamp.
31 disableCachingParam
: '_dc' ,
34 * @cfg {Boolean} loadDelay
35 * Millisecond delay between asynchronous script injection (prevents stack
36 * overflow on some user agents) 'false' disables delay but potentially
37 * increases stack load.
42 * @cfg {Boolean} preserveScripts
43 * `false` to remove asynchronously loaded scripts, `true` to retain script
44 * element for browser debugger compatibility and improved load performance.
46 preserveScripts
: true ,
49 * @cfg {String} [charset=UTF-8]
50 * Optional charset to specify encoding of dynamic content.
57 cssRe
= /\.css(?:\?|$)/i ,
58 resolverEl
= doc
. createElement ( 'a' ),
59 isBrowser
= typeof window
!== 'undefined' ,
62 node
: ! isBrowser
&& ( typeof require
=== 'function' ),
63 phantom
: ( window
&& ( window
. _phantom
|| window
. callPhantom
)) || /PhantomJS/ . test ( window
. navigator
. userAgent
)
65 _tags
= ( Ext
. platformTags
= {}),
67 _apply = function ( object
, config
, defaults
) {
69 _apply ( object
, defaults
);
71 if ( object
&& config
&& typeof config
=== 'object' ) {
72 for ( var i
in config
) {
73 object
[ i
] = config
[ i
];
79 var lowerCase
= false ,
80 obj
= Array
. prototype . shift
. call ( arguments
),
83 if ( typeof arguments
[ arguments
. length
- 1 ] === 'boolean' ) {
84 lowerCase
= Array
. prototype . pop
. call ( arguments
);
87 len
= arguments
. length
;
88 for ( index
= 0 ; index
< len
; index
++) {
89 value
= arguments
[ index
];
90 if ( typeof value
=== 'object' ) {
92 obj
[ lowerCase
? i
. toLowerCase () : i
] = value
[ i
];
99 _getKeys
= ( typeof Object
. keys
== 'function' ) ?
104 return Object
. keys ( object
);
110 for ( property
in object
) {
111 if ( object
. hasOwnProperty ( property
)) {
119 * The Boot loader class manages Request objects that contain one or
120 * more individual urls that need to be loaded. Requests can be performed
121 * synchronously or asynchronously, but will always evaluate urls in the
122 * order specified on the request object.
132 * @cfg {Object} assetConfig
133 * A map (url->assetConfig) that contains information about assets loaded by the Microlaoder.
135 assetConfig
: _assetConfig
,
137 // Keyed by absolute URL this object holds "true" if that URL is already loaded
138 // or an array of callbacks to call once it loads.
143 'http://foo.com/bar/baz/Thing.js': {
145 el: scriptEl || linkEl,
147 requests: [ request1, ... ]
153 * contains the current script name being loaded
154 * (loadSync or sequential load only)
158 currentRequest
: null ,
160 // when loadSync is called, need to cause subsequent load requests to also be loadSync,
161 // eg, when Ext.require(...) is called
165 * simple helper method for debugging
169 * enables / disables loading scripts via script / link elements rather
170 * than using ajax / eval
180 allowMultipleBrowsers
: false ,
190 webosbrowser
: 'webOSBrowser' ,
191 chromeMobile
: 'ChromeMobile' ,
192 chromeiOS
: 'ChromeiOS' ,
200 windowsPhone
: 'WindowsPhone' ,
202 blackberry
: 'BlackBerry' ,
203 rimTablet
: 'RIMTablet' ,
209 chromeOS
: 'ChromeOS' ,
221 webosbrowser
: 'wOSBrowser/' ,
222 chromeMobile
: 'CrMo/' ,
227 // When a UA reports multiple browsers this list is used to prioritize the 'real' browser
228 // lower index number will win
245 ios
: 'i(?:Pad|Phone|Pod)(?:.*)CPU(?: iPhone)? OS ' ,
246 android
: '(Android |HTC_|Silk/)' , // Some HTC devices ship with an OSX userAgent by default,
247 // so we need to add a direct check for HTC_
248 windowsPhone
: 'Windows Phone ' ,
249 blackberry
: '(?:BlackBerry|BB)(?:.*)Version\/' ,
250 rimTablet
: 'RIM Tablet OS ' ,
251 webos
: '(?:webOS|hpwOS)\/' ,
256 fallbackOSPrefixes
: {
272 * The default function that detects various platforms and sets tags
273 * in the platform map accordingly. Examples are iOS, android, tablet, etc.
274 * @param tags the set of tags to populate
276 detectPlatformTags : function () {
278 ua
= navigator
. userAgent
,
279 isMobile
= /Mobile(\/|\s)/ . test ( ua
),
280 element
= document
. createElement ( 'div' ),
281 isEventSupported = function ( name
, tag
) {
282 if ( tag
=== undefined ) {
286 var eventName
= 'on' + name
. toLowerCase (),
287 isSupported
= ( eventName
in element
);
290 if ( element
. setAttribute
&& element
. removeAttribute
) {
291 element
. setAttribute ( eventName
, '' );
292 isSupported
= typeof element
[ eventName
] === 'function' ;
294 if ( typeof element
[ eventName
] !== 'undefined' ) {
295 element
[ eventName
] = undefined ;
298 element
. removeAttribute ( eventName
);
306 getBrowsers = function () {
308 maxIEVersion
, prefix
,
309 value
, key
, index
, len
, match
, version
, matched
;
311 // MS Edge browser (and possibly others) can report multiple browsers in the UserAgent
312 // "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"
313 // we use this to prioritize the actual browser in this situation
314 len
= me
. browserPriority
. length
;
315 for ( index
= 0 ; index
< len
; index
++) {
316 key
= me
. browserPriority
[ index
];
318 value
= me
. browserPrefixes
[ key
];
319 match
= ua
. match ( new RegExp ( '(' + value
+ ')([ \\ w \\ ._]+)' ));
320 version
= match
&& match
. length
> 1 ? parseInt ( match
[ 2 ]) : 0 ;
327 browsers
[ key
] = version
;
330 //Deal with IE document mode
332 var mode
= document
. documentMode
;
339 // Fancy IE greater than and less then quick tags
340 version
= browsers
. ie
|| false ;
341 maxIEVersion
= Math
. max ( version
, me
. maxIEVersion
);
343 for ( index
= 8 ; index
<= maxIEVersion
; ++ index
) {
344 prefix
= 'ie' + index
;
345 browsers
[ prefix
+ 'm' ] = version
? version
<= index
: 0 ;
346 browsers
[ prefix
] = version
? version
=== index
: 0 ;
347 browsers
[ prefix
+ 'p' ] = version
? version
>= index
: 0 ;
354 getOperatingSystems = function () {
356 value
, key
, keys
, index
, len
, match
, matched
, version
, activeCount
;
358 keys
= _getKeys ( me
. osPrefixes
);
360 for ( index
= 0 , activeCount
= 0 ; index
< len
; index
++) {
362 value
= me
. osPrefixes
[ key
];
363 match
= ua
. match ( new RegExp ( '(' + value
+ ')([^ \\ s;]+)' ));
364 matched
= match
? match
[ 1 ] : null ;
366 // This is here because some HTC android devices show an OSX Snow Leopard userAgent by default.
367 // And the Kindle Fire doesn't have any indicator of Android as the OS in its User Agent
368 if ( matched
&& ( matched
=== 'HTC_' || matched
=== 'Silk/' )) {
371 version
= match
&& match
. length
> 1 ? parseFloat ( match
[ match
. length
- 1 ]) : 0 ;
377 systems
[ key
] = version
;
380 keys
= _getKeys ( me
. fallbackOSPrefixes
);
382 // If no OS could be found we resort to the fallbacks, otherwise we just
383 // falsify the fallbacks
385 for ( index
= 0 ; index
< len
; index
++) {
388 // No OS was detected from osPrefixes
389 if ( activeCount
=== 0 ) {
390 value
= me
. fallbackOSPrefixes
[ key
];
391 match
= ua
. toLowerCase (). match ( new RegExp ( value
));
392 systems
[ key
] = match
? true : 0 ;
402 getDevices = function () {
404 value
, key
, keys
, index
, len
, match
;
406 keys
= _getKeys ( me
. devicePrefixes
);
408 for ( index
= 0 ; index
< len
; index
++) {
410 value
= me
. devicePrefixes
[ key
];
411 match
= ua
. match ( new RegExp ( value
));
412 devices
[ key
] = match
? true : 0 ;
417 browsers
= getBrowsers (),
418 systems
= getOperatingSystems (),
419 devices
= getDevices (),
420 platformParams
= Boot
. loadPlatformsParam ();
422 // We apply platformParams from the query here first to allow for forced user valued
423 // to be used in calculation of generated tags
424 _merge ( _tags
, browsers
, systems
, devices
, platformParams
, true );
426 _tags
. phone
= ( _tags
. iphone
|| _tags
. ipod
) ||
427 (! _tags
. silk
&& ( _tags
. android
&& ( _tags
. android
< 3 || isMobile
))) ||
428 ( _tags
. blackberry
&& isMobile
) ||
429 ( _tags
. windowsphone
);
431 _tags
. tablet
= ! _tags
. phone
&& (
436 ( _tags
. ie10
&& /; Touch/ . test ( ua
))
440 // if the browser has touch events we can be reasonably sure the device has
442 isEventSupported ( 'touchend' ) ||
443 // browsers that use pointer event have maxTouchPoints > 0 if the
444 // device supports touch input
445 // http://www.w3.org/TR/pointerevents/#widl-Navigator-maxTouchPoints
446 navigator
. maxTouchPoints
||
447 // IE10 uses a vendor-prefixed maxTouchPoints property
448 navigator
. msMaxTouchPoints
;
450 _tags
. desktop
= ! _tags
. phone
&& ! _tags
. tablet
;
451 _tags
. cordova
= _tags
. phonegap
= !!( window
. PhoneGap
|| window
. Cordova
|| window
. cordova
);
452 _tags
. webview
= /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)(?!.*FBAN)/i . test ( ua
);
453 _tags
. androidstock
= ( _tags
. android
<= 4.3 ) && ( _tags
. safari
|| _tags
. silk
);
455 // Re-apply any query params here to allow for user override of generated tags (desktop, touch, tablet, etc)
456 _merge ( _tags
, platformParams
, true );
460 * Extracts user supplied platform tags from the "platformTags" query parameter
463 * ?platformTags=name:state,name:state,...
465 * (each tag defaults to true when state is unspecified)
468 * ?platformTags=isTablet,isPhone:false,isDesktop:0,iOS:1,Safari:true, ...
470 * @returns {Object} the platform tags supplied by the query string
472 loadPlatformsParam : function () {
473 // Check if the ?platform parameter is set in the URL
474 var paramsString
= window
. location
. search
. substr ( 1 ),
475 paramsArray
= paramsString
. split ( "&" ),
478 tmpArray
, tmplen
, platform
, name
, enabled
;
480 for ( i
= 0 ; i
< paramsArray
. length
; i
++) {
481 tmpArray
= paramsArray
[ i
]. split ( "=" );
482 params
[ tmpArray
[ 0 ]] = tmpArray
[ 1 ];
485 if ( params
. platformTags
) {
486 tmpArray
= params
. platformTags
. split ( "," );
487 for ( tmplen
= tmpArray
. length
, i
= 0 ; i
< tmplen
; i
++) {
488 platform
= tmpArray
[ i
]. split ( ":" );
491 if ( platform
. length
> 1 ) {
492 enabled
= platform
[ 1 ];
493 if ( enabled
=== 'false' || enabled
=== '0' ) {
497 platforms
[ name
] = enabled
;
503 filterPlatform : function ( platform
, excludes
) {
504 platform
= _emptyArray
. concat ( platform
|| _emptyArray
);
505 excludes
= _emptyArray
. concat ( excludes
|| _emptyArray
);
507 var plen
= platform
. length
,
508 elen
= excludes
. length
,
509 include
= (! plen
&& elen
), // default true if only excludes specified
512 for ( i
= 0 ; i
< plen
&& ! include
; i
++) {
514 include
= !! _tags
[ tag
];
517 for ( i
= 0 ; i
< elen
&& include
; i
++) {
519 include
= ! _tags
[ tag
];
526 var scriptEls
= doc
. getElementsByTagName ( 'script' ),
527 len
= scriptEls
. length
,
528 re
= /\/ext(\-[a-z\-]+)?\.js$/ ,
529 entry
, script
, src
, state
, baseUrl
, key
, n
, origin
;
531 // Since we are loading after other scripts, and we needed to gather them
532 // anyway, we track them in _scripts so we don't have to ask for them all
534 for ( n
= 0 ; n
< len
; n
++) {
535 src
= ( script
= scriptEls
[ n
]). src
;
539 state
= script
. readyState
|| null ;
541 // If we find a script file called "ext-*.js", then the base path is that file's base path.
544 Boot
. hasReadyState
= ( "readyState" in script
);
545 Boot
. hasAsync
= ( "async" in script
) || ! Boot
. hasReadyState
;
550 if (! Boot
. scripts
[ key
= Boot
. canonicalUrl ( src
)]) {
554 done
: state
=== null || // non-IE
555 state
=== 'loaded' || state
=== 'complete' , // IE only
563 script
= scriptEls
[ scriptEls
. length
- 1 ];
564 baseUrl
= script
. src
;
565 Boot
. hasReadyState
= ( 'readyState' in script
);
566 Boot
. hasAsync
= ( "async" in script
) || ! Boot
. hasReadyState
;
569 Boot
. baseUrl
= baseUrl
. substring ( 0 , baseUrl
. lastIndexOf ( '/' ) + 1 );
570 origin
= window
. location
. origin
||
571 window
. location
. protocol
+
573 window
. location
. hostname
+
574 ( window
. location
. port
? ':' + window
. location
. port
: '' );
575 Boot
. origin
= origin
;
577 Boot
. detectPlatformTags ();
578 Ext
. filterPlatform
= Boot
. filterPlatform
;
582 * This method returns a canonical URL for the given URL.
584 * For example, the following all produce the same canonical URL (which is the
587 * http://foo.com/bar/baz/zoo/derp/../../goo/Thing.js?_dc=12345
588 * http://foo.com/bar/baz/zoo/derp/../../goo/Thing.js
589 * http://foo.com/bar/baz/zoo/derp/../jazz/../../goo/Thing.js
590 * http://foo.com/bar/baz/zoo/../goo/Thing.js
591 * http://foo.com/bar/baz/goo/Thing.js
595 canonicalUrl : function ( url
) {
596 // @TODO - see if we need this fallback logic
597 // http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
598 resolverEl
. href
= url
;
600 var ret
= resolverEl
. href
,
601 dc
= _config
. disableCachingParam
,
602 pos
= dc
? ret
. indexOf ( dc
+ '=' ) : - 1 ,
605 // If we have a _dc query parameter we need to remove it from the canonical
607 if ( pos
> 0 && (( c
= ret
. charAt ( pos
- 1 )) === '?' || c
=== '&' )) {
608 end
= ret
. indexOf ( '&' , pos
);
609 end
= ( end
< 0 ) ? '' : ret
. substring ( end
);
610 if ( end
&& c
=== '?' ) {
611 ++ pos
; // keep the '?'
612 end
= end
. substring ( 1 ); // remove the '&'
614 ret
= ret
. substring ( 0 , pos
- 1 ) + end
;
621 * Get the config value corresponding to the specified name. If no name is given, will return the config object
622 * @param {String} name The config property name
625 getConfig : function ( name
) {
626 return name
? Boot
. config
[ name
] : Boot
. config
;
630 * Set the configuration.
631 * @param {Object} config The config object to override the default values.
632 * @return {Ext.Boot} this
634 setConfig : function ( name
, value
) {
635 if ( typeof name
=== 'string' ) {
636 Boot
. config
[ name
] = value
;
638 for ( var s
in name
) {
639 Boot
. setConfig ( s
, name
[ s
]);
645 getHead : function () {
646 return Boot
. docHead
||
647 ( Boot
. docHead
= doc
. head
||
648 doc
. getElementsByTagName ( 'head' )[ 0 ]);
651 create : function ( url
, key
, cfg
) {
652 var config
= cfg
|| {};
655 return Boot
. scripts
[ key
] = new Entry ( config
);
658 getEntry : function ( url
, cfg
) {
659 var key
= Boot
. canonicalUrl ( url
),
660 entry
= Boot
. scripts
[ key
];
662 entry
= Boot
. create ( url
, key
, cfg
);
667 registerContent : function ( url
, type
, content
) {
674 return Boot
. getEntry ( url
, cfg
);
677 processRequest : function ( request
, sync
) {
678 request
. loadEntries ( sync
);
681 load : function ( request
) {
682 var request
= new Request ( request
);
684 if ( request
. sync
|| Boot
. syncMode
) {
685 return Boot
. loadSync ( request
);
688 // If there is a request in progress, we must
689 // queue this new request to be fired when the current request completes.
690 if ( Boot
. currentRequest
) {
691 // trigger assignment of entries now to ensure that overlapping
692 // entries with currently running requests will synchronize state
693 // with this pending one as they complete
694 request
. getEntries ();
695 Boot
. suspendedQueue
. push ( request
);
697 Boot
. currentRequest
= request
;
698 Boot
. processRequest ( request
, false );
703 loadSync : function ( request
) {
704 var request
= new Request ( request
);
707 Boot
. processRequest ( request
, true );
712 loadBasePrefix : function ( request
) {
713 request
= new Request ( request
);
714 request
. prependBaseUrl
= true ;
715 return Boot
. load ( request
);
718 loadSyncBasePrefix : function ( request
) {
719 request
= new Request ( request
);
720 request
. prependBaseUrl
= true ;
721 return Boot
. loadSync ( request
);
724 requestComplete : function ( request
) {
727 if ( Boot
. currentRequest
=== request
) {
728 Boot
. currentRequest
= null ;
729 while ( Boot
. suspendedQueue
. length
> 0 ) {
730 next
= Boot
. suspendedQueue
. shift ();
737 if (! Boot
. currentRequest
&& Boot
. suspendedQueue
. length
== 0 ) {
738 Boot
. fireListeners ();
742 isLoading : function () {
743 return ! Boot
. currentRequest
&& Boot
. suspendedQueue
. length
== 0 ;
746 fireListeners : function () {
748 while ( Boot
. isLoading () && ( listener
= Boot
. listeners
. shift ())) {
753 onBootReady : function ( listener
) {
754 if (! Boot
. isLoading ()) {
757 Boot
. listeners
. push ( listener
);
762 * this is a helper function used by Ext.Loader to flush out
763 * 'uses' arrays for classes
765 getPathsFromIndexes : function ( indexMap
, loadOrder
) {
766 return Request
. prototype . getPathsFromIndexes ( indexMap
, loadOrder
);
769 createLoadOrderMap : function ( loadOrder
) {
770 return Request
. prototype . createLoadOrderMap ( loadOrder
);
773 fetch : function ( url
, complete
, scope
, async
) {
774 async
= ( async
=== undefined ) ? !! complete
: async
;
776 var xhr
= new XMLHttpRequest (),
777 result
, status
, content
, exception
= false ,
778 readyStateChange = function () {
779 if ( xhr
&& xhr
. readyState
== 4 ) {
780 status
= ( xhr
. status
=== 1223 ) ? 204 :
781 ( xhr
. status
=== 0 && (( self
. location
|| {}). protocol
=== 'file:' ||
782 ( self
. location
|| {}). protocol
=== 'ionp:' )) ? 200 : xhr
. status
;
783 content
= xhr
. responseText
;
790 complete
. call ( scope
, result
);
797 xhr
. onreadystatechange
= readyStateChange
;
801 xhr
. open ( 'GET' , url
, async
);
816 notifyAll : function ( entry
) {
817 entry
. notifyRequests ();
822 * The request class encapsulates a series of Entry objects
823 * and provides notification around the completion of all Entries
826 function Request ( cfg
) {
831 var cfg
= cfg
. url
? cfg
: { url
: cfg
},
833 urls
= url
. charAt
? [ url
] : url
,
834 charset
= cfg
. charset
|| Boot
. config
. charset
;
842 Request
. prototype = {
850 createLoadOrderMap : function ( loadOrder
) {
851 var len
= loadOrder
. length
,
855 for ( i
= 0 ; i
< len
; i
++) {
856 element
= loadOrder
[ i
];
857 loadOrderMap
[ element
. path
] = element
;
869 getLoadIndexes : function ( index
, indexMap
, loadOrder
, includeUses
, skipLoaded
) {
870 var item
= loadOrder
[ index
],
871 len
, i
, reqs
, entry
, stop
, added
, idx
, ridx
, url
;
873 if ( indexMap
[ index
]) {
878 indexMap
[ index
] = true ;
884 // iterate the requirements for each index and
885 // accumulate in the index map
886 for ( idx
in indexMap
) {
887 if ( indexMap
. hasOwnProperty ( idx
)) {
888 item
= loadOrder
[ idx
];
892 url
= this . prepareUrl ( item
. path
);
893 entry
= Boot
. getEntry ( url
);
894 if (! skipLoaded
|| ! entry
|| ! entry
. done
) {
895 reqs
= item
. requires
;
896 if ( includeUses
&& item
. uses
) {
897 reqs
= reqs
. concat ( item
. uses
);
899 for ( len
= reqs
. length
, i
= 0 ; i
< len
; i
++) {
901 // if we find a requirement that wasn't
902 // already in the index map,
903 // set the added flag to indicate we need to
905 if (! indexMap
[ ridx
]) {
906 indexMap
[ ridx
] = true ;
914 // if we made a pass through the index map and didn't add anything
924 getPathsFromIndexes : function ( indexMap
, loadOrder
) {
929 for ( index
in indexMap
) {
930 if ( indexMap
. hasOwnProperty ( index
) && indexMap
[ index
]) {
935 indexes
. sort ( function ( a
, b
) {
939 // convert indexes back into load paths
940 for ( len
= indexes
. length
, i
= 0 ; i
< len
; i
++) {
941 paths
. push ( loadOrder
[ indexes
[ i
]]. path
);
947 expandUrl : function ( url
, indexMap
, includeUses
, skipLoaded
) {
948 if ( typeof url
== 'string' ) {
953 loadOrder
= me
. loadOrder
,
954 loadOrderMap
= me
. loadOrderMap
;
957 loadOrderMap
= loadOrderMap
|| me
. createLoadOrderMap ( loadOrder
);
958 me
. loadOrderMap
= loadOrderMap
;
959 indexMap
= indexMap
|| {};
960 var len
= url
. length
,
964 for ( i
= 0 ; i
< len
; i
++) {
965 item
= loadOrderMap
[ url
[ i
]];
967 me
. getLoadIndexes ( item
. idx
, indexMap
, loadOrder
, includeUses
, skipLoaded
);
969 unmapped
. push ( url
[ i
]);
974 return me
. getPathsFromIndexes ( indexMap
, loadOrder
). concat ( unmapped
);
979 expandUrls : function ( urls
, includeUses
) {
980 if ( typeof urls
== "string" ) {
990 for ( i
= 0 ; i
< len
; i
++) {
991 tmpExpanded
= this . expandUrl ( urls
[ i
], {}, includeUses
, true );
992 for ( t
= 0 , tlen
= tmpExpanded
. length
; t
< tlen
; t
++) {
993 tUrl
= tmpExpanded
[ t
];
994 if (! expandMap
[ tUrl
]) {
995 expandMap
[ tUrl
] = true ;
1001 if ( expanded
. length
== 0 ) {
1008 expandLoadOrder : function () {
1014 expanded
= this . expandUrls ( urls
, true );
1022 // if we added some urls to the request to honor the indicated
1023 // load order, the request needs to be sequential
1024 if ( urls
. length
!= expanded
. length
) {
1025 me
. sequential
= true ;
1031 getUrls : function () {
1032 this . expandLoadOrder ();
1036 prepareUrl : function ( url
) {
1037 if ( this . prependBaseUrl
) {
1038 return Boot
. baseUrl
+ url
;
1043 getEntries : function () {
1045 entries
= me
. entries
,
1046 i
, entry
, urls
, url
;
1049 urls
= me
. getUrls ();
1050 for ( i
= 0 ; i
< urls
. length
; i
++) {
1051 url
= me
. prepareUrl ( urls
[ i
]);
1052 entry
= Boot
. getEntry ( url
, {
1056 entry
. requests
. push ( me
);
1057 entries
. push ( entry
);
1059 me
. entries
= entries
;
1064 loadEntries : function ( sync
) {
1066 entries
= me
. getEntries (),
1067 len
= entries
. length
,
1068 start
= me
. loadStart
|| 0 ,
1069 continueLoad
, entry
, i
;
1071 if ( sync
!== undefined ) {
1075 me
. loaded
= me
. loaded
|| 0 ;
1076 me
. loading
= me
. loading
|| len
;
1078 for ( i
= start
; i
< len
; i
++) {
1081 continueLoad
= entries
[ i
]. load ( me
. sync
);
1083 continueLoad
= true ;
1087 entry
. onDone ( function (){
1088 me
. loadEntries ( sync
);
1093 me
. processLoadedEntries ();
1096 processLoadedEntries : function () {
1098 entries
= me
. getEntries (),
1099 len
= entries
. length
,
1100 start
= me
. startIndex
|| 0 ,
1104 for ( i
= start
; i
< len
; i
++) {
1107 if (! entry
. loaded
) {
1112 if (! entry
. evaluated
) {
1124 notify : function () {
1127 var error
= me
. error
,
1128 fn
= me
[ error
? 'failure' : 'success' ],
1129 delay
= ( 'delay' in me
)
1131 : ( error
? 1 : Boot
. config
. chainDelay
),
1132 scope
= me
. scope
|| me
;
1135 if ( delay
=== 0 || delay
> 0 ) {
1136 // Free the stack (and defer the next script)
1137 setTimeout ( function () {
1145 Boot
. requestComplete ( me
);
1149 onDone : function ( listener
) {
1151 listeners
= me
. listeners
|| ( me
. listeners
= []);
1155 listeners
. push ( listener
);
1159 fireListeners : function () {
1160 var listeners
= this . listeners
,
1163 while (( listener
= listeners
. shift ())) {
1171 * The Entry class is a token to manage the load and evaluation
1172 * state of a particular url. It is used to notify all Requests
1173 * interested in this url that the content is available.
1175 function Entry ( cfg
) {
1181 var charset
= cfg
. charset
|| Boot
. config
. charset
,
1182 manifest
= Ext
. manifest
,
1183 loader
= manifest
&& manifest
. loader
,
1184 cache
= ( cfg
. cache
!== undefined ) ? cfg
. cache
: ( loader
&& loader
. cache
),
1185 buster
, busterParam
;
1187 if ( Boot
. config
. disableCaching
) {
1188 if ( cache
=== undefined ) {
1189 cache
= ! Boot
. config
. disableCaching
;
1192 if ( cache
=== false ) {
1193 buster
= + new Date ();
1194 } else if ( cache
!== true ) {
1199 busterParam
= ( loader
&& loader
. cacheParam
) || Boot
. config
. disableCachingParam
;
1200 buster
= busterParam
+ "=" + buster
;
1217 isCrossDomain : function () {
1219 if ( me
. crossDomain
=== undefined ) {
1220 me
. crossDomain
= ( me
. getLoadUrl (). indexOf ( Boot
. origin
) !== 0 );
1222 return me
. crossDomain
;
1225 isCss : function () {
1227 if ( me
. css
=== undefined ) {
1229 var assetConfig
= Boot
. assetConfig
[ me
. url
];
1230 me
. css
= assetConfig
? assetConfig
. type
=== "css" : cssRe
. test ( me
. url
);
1238 getElement : function ( tag
) {
1243 tag
= tag
|| "link" ;
1244 el
= doc
. createElement ( tag
);
1246 el
. rel
= 'stylesheet' ;
1249 me
. prop
= "textContent" ;
1251 el
. type
= "text/css" ;
1253 tag
= tag
|| "script" ;
1254 el
= doc
. createElement ( tag
);
1255 el
. type
= 'text/javascript' ;
1259 el
. charset
= me
. charset
;
1262 if ( Boot
. hasAsync
) {
1271 getLoadUrl : function () {
1273 url
= Boot
. canonicalUrl ( me
. url
);
1275 me
. loadUrl
= !! me
. buster
1276 ? ( url
+ ( url
. indexOf ( '?' ) === - 1 ? '?' : '&' ) + me
. buster
)
1282 fetch : function ( req
) {
1283 var url
= this . getLoadUrl (),
1284 async
= !! req
. async
,
1285 complete
= req
. complete
;
1287 Boot
. fetch ( url
, complete
, this , async
);
1290 onContentLoaded : function ( response
) {
1292 status
= response
. status
,
1293 content
= response
. content
,
1294 exception
= response
. exception
,
1295 url
= this . getLoadUrl ();
1297 if (( exception
|| status
=== 0 ) && ! _environment
. phantom
) {
1300 me
. evaluated
= true ;
1302 else if (( status
>= 200 && status
< 300 ) || status
=== 304
1303 || _environment
. phantom
1304 || ( status
=== 0 && content
. length
> 0 )
1306 me
. content
= content
;
1311 me
. evaluated
= true ;
1315 createLoadElement : function ( callback
) {
1317 el
= me
. getElement (),
1318 readyStateChange = function (){
1319 if ( this . readyState
=== 'loaded' || this . readyState
=== 'complete' ) {
1325 errorFn = function () {
1332 el
. onerror
= errorFn
;
1333 if ( Boot
. hasReadyState
) {
1334 el
. onreadystatechange
= readyStateChange
;
1336 el
. onload
= callback
;
1338 // IE starts loading here
1339 el
[ me
. prop
] = me
. getLoadUrl ();
1342 onLoadElementReady : function () {
1343 Boot
. getHead (). appendChild ( this . getElement ());
1344 this . evaluated
= true ;
1347 inject : function ( content
, asset
) {
1349 head
= Boot
. getHead (),
1352 base
, el
, ieMode
, basePath
;
1356 basePath
= key
. substring ( 0 , key
. lastIndexOf ( "/" ) + 1 );
1357 base
= doc
. createElement ( 'base' );
1358 base
. href
= basePath
;
1359 if ( head
. firstChild
) {
1360 head
. insertBefore ( base
, head
. firstChild
);
1362 head
. appendChild ( base
);
1364 // reset the href attribute to cuase IE to pick up the change
1365 base
. href
= base
. href
;
1368 content
+= " \n /*# sourceURL=" + key
+ " */" ;
1371 // create element after setting base
1372 el
= me
. getElement ( "style" );
1374 ieMode
= ( 'styleSheet' in el
);
1376 head
. appendChild ( base
);
1378 head
. appendChild ( el
);
1379 el
. styleSheet
. cssText
= content
;
1381 el
. textContent
= content
;
1382 head
. appendChild ( el
);
1384 head
. removeChild ( base
);
1387 // Debugger friendly, file names are still shown even though they're
1388 // eval'ed code. Breakpoints work on both Firebug and Chrome's Web
1391 content
+= " \n //# sourceURL=" + key
;
1393 Ext
. globalEval ( content
);
1398 loadCrossDomain : function () {
1400 complete = function (){
1401 me
. loaded
= me
. evaluated
= me
. done
= true ;
1402 me
. notifyRequests ();
1404 me
. createLoadElement ( function (){
1407 me
. evaluateLoadElement ();
1408 // at this point, we need sequential evaluation,
1409 // which means we can't advance the load until
1410 // this entry has fully completed
1414 loadElement : function () {
1416 complete = function (){
1417 me
. loaded
= me
. evaluated
= me
. done
= true ;
1418 me
. notifyRequests ();
1420 me
. createLoadElement ( function (){
1423 me
. evaluateLoadElement ();
1427 loadSync : function () {
1431 complete : function ( response
) {
1432 me
. onContentLoaded ( response
);
1436 me
. notifyRequests ();
1439 load : function ( sync
) {
1443 // if we're calling back through load and we're loading but haven't
1444 // yet loaded, then we should be in a sequential, cross domain
1445 // load scenario which means we can't continue the load on the
1446 // request until this entry has fully evaluated, which will mean
1447 // loaded = evaluated = done = true in one step. For css files, this
1448 // will happen immediately upon <link> element creation / insertion,
1449 // but <script> elements will set this upon load notification
1454 // for async modes, we have some options
1456 // if cross domain, just inject the script tag and let the onload
1457 // events drive the progression
1458 if ( me
. isCrossDomain ()) {
1459 return me
. loadCrossDomain ();
1461 // for IE, use the readyStateChange allows us to load scripts in parallel
1462 // but serialize the evaluation by appending the script node to the
1464 else if (! me
. isCss () && Boot
. hasReadyState
) {
1465 me
. createLoadElement ( function () {
1467 me
. notifyRequests ();
1471 else if ( Boot
. useElements
&&
1472 // older webkit, phantomjs included, won't fire load for link elements
1473 !( me
. isCss () && _environment
. phantom
)) {
1474 return me
. loadElement ();
1476 // for other browsers, just ajax the content down in parallel, and use
1477 // globalEval to serialize evaluation
1481 complete : function ( response
) {
1482 me
. onContentLoaded ( response
);
1483 me
. notifyRequests ();
1489 // for sync mode in js, global eval FTW. IE won't honor the comment
1490 // paths in the debugger, so eventually we need a sync mode for IE that
1491 // uses the readyStateChange mechanism
1496 // signal that the load process can continue
1500 evaluateContent : function () {
1501 this . inject ( this . content
);
1502 this . content
= null ;
1505 evaluateLoadElement : function () {
1506 Boot
. getHead (). appendChild ( this . getElement ());
1509 evaluate : function () {
1515 me
. evaluating
= true ;
1516 if ( me
. content
!== undefined ) {
1517 me
. evaluateContent ();
1518 } else if (! me
. error
) {
1519 me
. evaluateLoadElement ();
1521 me
. evaluated
= me
. done
= true ;
1529 cleanup : function () {
1541 el
. parentNode
. removeChild ( el
); // Remove, since its useless now
1545 if ( prop
!== me
. prop
) {
1546 // If we set the src property to null IE
1547 // will try and request a script at './null'
1550 delete el
[ prop
]; // and prepare for GC
1557 // Setting to null can cause exceptions if IE ever needs to call these
1558 // again (like onreadystatechange). This emptyFn has nothing locked in
1559 // closure scope so it is about as safe as null for memory leaks.
1560 el
. onload
= el
. onerror
= el
. onreadystatechange
= emptyFn
;
1563 notifyRequests : function () {
1564 var requests
= this . requests
,
1565 len
= requests
. length
,
1567 for ( i
= 0 ; i
< len
; i
++) {
1568 request
= requests
[ i
];
1569 request
. processLoadedEntries ();
1572 this . fireListeners ();
1576 onDone : function ( listener
) {
1578 listeners
= me
. listeners
|| ( me
. listeners
= []);
1582 listeners
. push ( listener
);
1586 fireListeners : function () {
1587 var listeners
= this . listeners
,
1589 if ( listeners
&& listeners
. length
> 0 ) {
1590 while (( listener
= listeners
. shift ())) {
1598 * Turns on or off the "cache buster" applied to dynamically loaded scripts. Normally
1599 * dynamically loaded scripts have an extra query parameter appended to avoid stale
1600 * cached scripts. This method can be used to disable this mechanism, and is primarily
1601 * useful for testing. This is done using a cookie.
1602 * @param {Boolean} disable True to disable the cache buster.
1603 * @param {String} [path="/"] An optional path to scope the cookie.
1605 Ext
. disableCacheBuster = function ( disable
, path
) {
1606 var date
= new Date ();
1607 date
. setTime ( date
. getTime () + ( disable
? 10 * 365 : - 1 ) * 24 * 60 * 60 * 1000 );
1608 date
= date
. toGMTString ();
1609 doc
. cookie
= 'ext-cache=1; expires=' + date
+ '; path=' + ( path
|| '/' );
1616 // NOTE: We run the eval at global scope to protect the body of the function and allow
1617 // compressors to still process it.
1619 })); //(eval("/*@cc_on!@*/!1"));
1622 * This method evaluates the given code free of any local variable. This
1623 * will be at global scope, in others it will be in a function.
1624 * @parma {String} code The code to evaluate.
1628 Ext
. globalEval
= Ext
. globalEval
|| ( this . execScript
1629 ? function ( code
) { execScript ( code
); }
1630 : function ($$ code
) { eval
. call ( window
, $$ code
); });
1632 //<feature legacyBrowser>
1634 * Only IE8 & IE/Quirks lack Function.prototype.bind so we polyfill that here.
1636 if (! Function
. prototype . bind
) {
1638 var slice
= Array
. prototype . slice
,
1639 // To reduce overhead on call of the bound fn we have two flavors based on
1640 // whether we have args to prepend or not:
1641 bind = function ( me
) {
1642 var args
= slice
. call ( arguments
, 1 ),
1646 return function () {
1648 // avoid the slice/concat if the caller does not supply args
1649 return method
. apply ( me
, t
. length
? args
. concat ( slice
. call ( t
)) : args
);
1652 // this is the majority use case - just fn.bind(this) and no args
1655 return function () {
1656 return method
. apply ( me
, arguments
);
1659 Function
. prototype . bind
= bind
;
1660 bind
.$ extjs
= true ; // to detect this polyfill if one want to improve it
1667 Ext
. setResourcePath = function ( poolName
, path
) {
1668 var manifest
= Ext
. manifest
|| ( Ext
. manifest
= {}),
1669 paths
= manifest
. resources
|| ( manifest
. resources
= {});
1672 if ( typeof poolName
!== 'string' ) {
1673 Ext
. apply ( paths
, poolName
);
1675 paths
[ poolName
] = path
;
1677 manifest
. resources
= paths
;
1681 Ext
. getResourcePath = function ( path
, poolName
, packageName
) {
1682 if ( typeof path
!== 'string' ) {
1683 poolName
= path
. pool
;
1684 packageName
= path
. packageName
;
1687 var manifest
= Ext
. manifest
,
1688 paths
= manifest
&& manifest
. resources
,
1689 poolPath
= paths
[ poolName
],
1692 if ( poolPath
== null ) {
1693 poolPath
= paths
. path
;
1694 if ( poolPath
== null ) {
1695 poolPath
= 'resources' ;
1700 output
. push ( poolPath
);
1704 output
. push ( packageName
);
1708 return output
. join ( '/' );
1710 // here, the extra check for window['Ext'] is needed for use with cmd-test
1711 // code injection. we need to make that this file will sync up with page global
1712 // scope to avoid duplicate Ext.Boot state. That check is after the initial Ext check
1713 // to allow the sandboxing template to inject an appropriate Ext var and prevent the
1714 // global detection.
1715 var Ext
= Ext
|| window
[ 'Ext' ] || {};
1718 //<editor-fold desc="Microloader">
1720 * @Class Ext.Microloader
1723 Ext
. Microloader
= Ext
. Microloader
|| ( function () {
1724 var Boot
= Ext
. Boot
,
1725 _warn = function ( message
) {
1726 console
. log ( "[WARN] " + message
);
1728 _privatePrefix
= '_ext:' + location
. pathname
,
1731 * The Following combination is used to create isolated local storage keys
1732 * '_ext' is used to scope all the local storage keys that we internally by Ext
1733 * 'location.pathname' is used to force each assets to cache by an absolute URL (/build/MyApp) (dev vs prod)
1734 * 'url' is used to force each asset to cache relative to the page (app.json vs resources/app.css)
1735 * 'profileId' is used to differentiate the builds of an application (neptune vs crisp)
1736 * 'Microloader.appId' is unique to the application and will differentiate apps on the same host (dev mode running app watch against multiple apps)
1738 getStorageKey = function ( url
, profileId
) {
1739 return _privatePrefix
+ url
+ '-' + ( profileId
? profileId
+ '-' : '' ) + Microloader
. appId
;
1741 postProcessor
, _storage
;
1744 _storage
= window
[ 'localStorage' ];
1749 var _cache
= window
[ 'applicationCache' ],
1750 // Local Storage Controller
1752 clearAllPrivate : function ( manifest
) {
1755 //Remove the entry for the manifest first
1756 _storage
. removeItem ( manifest
. key
);
1760 suffix
= manifest
. profile
+ '-' + Microloader
. appId
,
1761 ln
= _storage
. length
;
1762 for ( i
= 0 ; i
< ln
; i
++) {
1763 key
= _storage
. key ( i
);
1764 // If key starts with the private key and the suffix is present we can clear this entry
1765 if ( key
. indexOf ( _privatePrefix
) === 0 && key
. indexOf ( suffix
) !== - 1 ) {
1766 removeKeys
. push ( key
);
1770 for ( i
in removeKeys
) {
1771 _storage
. removeItem ( removeKeys
[ i
]);
1778 retrieveAsset : function ( key
) {
1780 return _storage
. getItem ( key
);
1783 // Private browsing mode
1788 setAsset : function ( key
, content
) {
1790 if ( content
=== null || content
== '' ) {
1791 _storage
. removeItem ( key
);
1793 _storage
. setItem ( key
, content
);
1797 if ( _storage
&& e
. code
== e
. QUOTA_EXCEEDED_ERR
) {
1803 var Asset = function ( cfg
) {
1804 if ( typeof cfg
. assetConfig
=== 'string' ) {
1805 this . assetConfig
= {
1806 path
: cfg
. assetConfig
1809 this . assetConfig
= cfg
. assetConfig
;
1812 this . type
= cfg
. type
;
1813 this . key
= getStorageKey ( this . assetConfig
. path
, cfg
. manifest
. profile
);
1815 if ( cfg
. loadFromCache
) {
1816 this . loadFromCache ();
1821 shouldCache : function () {
1822 return _storage
&& this . assetConfig
. update
&& this . assetConfig
. hash
&& ! this . assetConfig
. remote
;
1825 is : function ( asset
) {
1826 return (!! asset
&& this . assetConfig
&& asset
. assetConfig
&& ( this . assetConfig
. hash
=== asset
. assetConfig
. hash
))
1829 cache : function ( content
) {
1830 if ( this . shouldCache ()) {
1831 LocalStorage
. setAsset ( this . key
, content
|| this . content
);
1835 uncache : function () {
1836 LocalStorage
. setAsset ( this . key
, null );
1839 updateContent : function ( content
) {
1840 this . content
= content
;
1843 getSize : function () {
1844 return this . content
? this . content
. length
: 0 ;
1847 loadFromCache : function () {
1848 if ( this . shouldCache ()) {
1849 this . content
= LocalStorage
. retrieveAsset ( this . key
);
1854 var Manifest = function ( cfg
) {
1855 if ( typeof cfg
. content
=== "string" ) {
1856 this . content
= JSON
. parse ( cfg
. content
);
1858 this . content
= cfg
. content
;
1863 this . fromCache
= !! cfg
. cached
;
1864 this . assetCache
= !( cfg
. assetCache
=== false );
1865 this . key
= getStorageKey ( this . url
);
1867 // Pull out select properties for repetitive use
1868 this . profile
= this . content
. profile
;
1869 this . hash
= this . content
. hash
;
1870 this . loadOrder
= this . content
. loadOrder
;
1871 this . deltas
= this . content
. cache
? this . content
. cache
. deltas
: null ;
1872 this . cacheEnabled
= this . content
. cache
? this . content
. cache
. enable
: false ;
1874 this . loadOrderMap
= ( this . loadOrder
) ? Boot
. createLoadOrderMap ( this . loadOrder
) : null ;
1876 var tags
= this . content
. tags
,
1877 platformTags
= Ext
. platformTags
;
1880 if ( tags
instanceof Array
) {
1881 for ( var i
= 0 ; i
< tags
. length
; i
++) {
1882 platformTags
[ tags
[ i
]] = true ;
1885 Boot
. apply ( platformTags
, tags
);
1888 // re-apply the query parameters, so that the params as specified
1889 // in the url always has highest priority
1890 Boot
. apply ( platformTags
, Boot
. loadPlatformsParam ());
1893 // Convert all assets into Assets
1894 this . js
= this . processAssets ( this . content
. js
, 'js' );
1895 this . css
= this . processAssets ( this . content
. css
, 'css' );
1898 Manifest
. prototype = {
1899 processAsset : function ( assetConfig
, type
) {
1900 var processedAsset
= new Asset ({
1902 assetConfig
: assetConfig
,
1904 loadFromCache
: this . assetCache
1906 this . assetMap
[ assetConfig
. path
] = processedAsset
;
1907 return processedAsset
;
1910 processAssets : function ( assets
, type
) {
1915 for ( i
= 0 ; i
< ln
; i
++) {
1916 assetConfig
= assets
[ i
];
1917 results
. push ( this . processAsset ( assetConfig
, type
));
1923 useAppCache : function () {
1927 // Concatenate all assets for easy access
1928 getAssets : function () {
1929 return this . css
. concat ( this . js
);
1932 getAsset : function ( path
) {
1933 return this . assetMap
[ path
];
1936 shouldCache : function () {
1937 return this . hash
&& this . cacheEnabled
;
1940 cache : function ( content
) {
1941 if ( this . shouldCache ()) {
1942 LocalStorage
. setAsset ( this . key
, JSON
. stringify ( content
|| this . content
));
1946 is : function ( manifest
) {
1947 return this . hash
=== manifest
. hash
;
1950 // Clear the manifest from local storage
1951 uncache : function () {
1952 LocalStorage
. setAsset ( this . key
, null );
1955 exportContent : function () {
1957 loadOrderMap
: this . loadOrderMap
1967 var _listeners
= [],
1971 Ext
. microloaded
= true ;
1973 // data-app is in the dev template for an application and is also
1974 // injected into the app my CMD for production
1975 // We use this to prefix localStorage cache to prevent collisions
1976 var microloaderElement
= document
. getElementById ( 'microloader' );
1977 Microloader
. appId
= microloaderElement
? microloaderElement
. getAttribute ( 'data-app' ) : '' ;
1979 if ( Ext
. beforeLoad
) {
1980 postProcessor
= Ext
. beforeLoad ( Ext
. platformTags
);
1983 var readyHandler
= Ext
. _beforereadyhandler
;
1985 Ext
. _beforereadyhandler = function () {
1986 if ( Ext
. Boot
!== Boot
) {
1987 Ext
. apply ( Ext
. Boot
, Boot
);
1996 applyCacheBuster : function ( url
) {
1997 var tstamp
= new Date (). getTime (),
1998 sep
= url
. indexOf ( '?' ) === - 1 ? '?' : '&' ;
1999 url
= url
+ sep
+ "_dc=" + tstamp
;
2005 var manifest
= Ext
. manifest
;
2007 if ( typeof manifest
=== "string" ) {
2008 var extension
= ".json" ,
2009 url
= manifest
. indexOf ( extension
) === manifest
. length
- extension
. length
2011 : manifest
+ ".json" ,
2012 key
= getStorageKey ( url
),
2013 content
= LocalStorage
. retrieveAsset ( key
);
2015 // Manifest found in local storage, use this for immediate boot except in PhantomJS environments for building.
2017 manifest
= new Manifest ({
2022 if ( postProcessor
) {
2023 postProcessor ( manifest
);
2025 Microloader
. load ( manifest
);
2028 // Manifest is not in local storage. Fetch it from the server
2030 Boot
. fetch ( Microloader
. applyCacheBuster ( url
), function ( result
) {
2031 manifest
= new Manifest ({
2033 content
: result
. content
2037 if ( postProcessor
) {
2038 postProcessor ( manifest
);
2040 Microloader
. load ( manifest
);
2044 // Embedded Manifest into JS file
2046 manifest
= new Manifest ({
2049 Microloader
. load ( manifest
);
2055 * @param {Manifest} manifest
2057 load : function ( manifest
) {
2058 Microloader
. urls
= [];
2059 Microloader
. manifest
= manifest
;
2060 Ext
. manifest
= Microloader
. manifest
. exportContent ();
2062 var assets
= manifest
. getAssets (),
2064 asset
, i
, len
, include
, entry
;
2066 for ( len
= assets
. length
, i
= 0 ; i
< len
; i
++) {
2068 include
= Microloader
. filterAsset ( asset
);
2070 // Asset is using the localStorage caching system
2071 if ( manifest
. shouldCache () && asset
. shouldCache ()) {
2072 // Asset already has content from localStorage, instantly seed that into boot
2073 if ( asset
. content
) {
2074 entry
= Boot
. registerContent ( asset
. assetConfig
. path
, asset
. type
, asset
. content
);
2075 if ( entry
. evaluated
) {
2076 _warn ( "Asset: " + asset
. assetConfig
. path
+ " was evaluated prior to local storage being consulted." );
2078 //load via AJAX and seed content into Boot
2080 cachedAssets
. push ( asset
);
2083 Microloader
. urls
. push ( asset
. assetConfig
. path
);
2084 Boot
. assetConfig
[ asset
. assetConfig
. path
] = Boot
. apply ({ type
: asset
. type
}, asset
. assetConfig
);
2088 // If any assets are using the caching system and do not have local versions load them first via AJAX
2089 if ( cachedAssets
. length
> 0 ) {
2090 Microloader
. remainingCachedAssets
= cachedAssets
. length
;
2091 while ( cachedAssets
. length
> 0 ) {
2092 asset
= cachedAssets
. pop ();
2093 Boot
. fetch ( asset
. assetConfig
. path
, ( function ( asset
) {
2094 return function ( result
) {
2095 Microloader
. onCachedAssetLoaded ( asset
, result
);
2100 Microloader
. onCachedAssetsReady ();
2104 // Load the asset and seed its content into Boot to be evaluated in sequence
2105 onCachedAssetLoaded : function ( asset
, result
) {
2107 result
= Microloader
. parseResult ( result
);
2108 Microloader
. remainingCachedAssets
--;
2110 if (! result
. error
) {
2111 checksum
= Microloader
. checksum ( result
. content
, asset
. assetConfig
. hash
);
2113 _warn ( "Cached Asset '" + asset
. assetConfig
. path
+ "' has failed checksum. This asset will be uncached for future loading" );
2115 // Un cache this asset so it is loaded next time
2119 Boot
. registerContent ( asset
. assetConfig
. path
, asset
. type
, result
. content
);
2120 asset
. updateContent ( result
. content
);
2123 _warn ( "There was an error pre-loading the asset '" + asset
. assetConfig
. path
+ "'. This asset will be uncached for future loading" );
2125 // Un cache this asset so it is loaded next time
2129 if ( Microloader
. remainingCachedAssets
=== 0 ) {
2130 Microloader
. onCachedAssetsReady ();
2134 onCachedAssetsReady : function (){
2136 url
: Microloader
. urls
,
2137 loadOrder
: Microloader
. manifest
. loadOrder
,
2138 loadOrderMap
: Microloader
. manifest
. loadOrderMap
,
2140 success
: Microloader
. onAllAssetsReady
,
2141 failure
: Microloader
. onAllAssetsReady
2145 onAllAssetsReady : function () {
2147 Microloader
. notify ();
2149 if ( navigator
. onLine
!== false ) {
2150 Microloader
. checkAllUpdates ();
2153 if ( window
[ 'addEventListener' ]) {
2154 window
. addEventListener ( 'online' , Microloader
. checkAllUpdates
, false );
2159 onMicroloaderReady : function ( listener
) {
2163 _listeners
. push ( listener
);
2170 notify : function () {
2172 while (( listener
= _listeners
. shift ())) {
2177 // Delta patches content
2178 patch : function ( content
, delta
) {
2182 if ( delta
. length
=== 0 ) {
2186 for ( i
= 0 , ln
= delta
. length
; i
< ln
; i
++) {
2189 if ( typeof chunk
=== 'number' ) {
2190 output
. push ( content
. substring ( chunk
, chunk
+ delta
[++ i
]));
2197 return output
. join ( '' );
2200 checkAllUpdates : function () {
2201 if ( window
[ 'removeEventListener' ]) {
2202 window
. removeEventListener ( 'online' , Microloader
. checkAllUpdates
, false );
2206 Microloader
. checkForAppCacheUpdate ();
2209 // Manifest came from a cached instance, check for updates
2210 if ( Microloader
. manifest
. fromCache
) {
2211 Microloader
. checkForUpdates ();
2215 checkForAppCacheUpdate : function () {
2216 if ( _cache
. status
=== _cache
. UPDATEREADY
|| _cache
. status
=== _cache
. OBSOLETE
) {
2217 Microloader
. appCacheState
= 'updated' ;
2218 } else if ( _cache
. status
!== _cache
. IDLE
&& _cache
. status
!== _cache
. UNCACHED
) {
2219 Microloader
. appCacheState
= 'checking' ;
2220 _cache
. addEventListener ( 'error' , Microloader
. onAppCacheError
);
2221 _cache
. addEventListener ( 'noupdate' , Microloader
. onAppCacheNotUpdated
);
2222 _cache
. addEventListener ( 'cached' , Microloader
. onAppCacheNotUpdated
);
2223 _cache
. addEventListener ( 'updateready' , Microloader
. onAppCacheReady
);
2224 _cache
. addEventListener ( 'obsolete' , Microloader
. onAppCacheObsolete
);
2226 Microloader
. appCacheState
= 'current' ;
2230 checkForUpdates : function () {
2231 // Fetch the Latest Manifest from the server
2232 Boot
. fetch ( Microloader
. applyCacheBuster ( Microloader
. manifest
. url
), Microloader
. onUpdatedManifestLoaded
);
2235 onAppCacheError : function ( e
) {
2238 Microloader
. appCacheState
= 'error' ;
2239 Microloader
. notifyUpdateReady ();
2242 onAppCacheReady : function () {
2244 Microloader
. appCacheUpdated ();
2247 onAppCacheObsolete : function () {
2248 Microloader
. appCacheUpdated ();
2251 appCacheUpdated : function () {
2252 Microloader
. appCacheState
= 'updated' ;
2253 Microloader
. notifyUpdateReady ();
2256 onAppCacheNotUpdated : function () {
2257 Microloader
. appCacheState
= 'current' ;
2258 Microloader
. notifyUpdateReady ();
2262 filterAsset : function ( asset
) {
2263 var cfg
= ( asset
&& asset
. assetConfig
) || {};
2264 if ( cfg
. platform
|| cfg
. exclude
) {
2265 return Boot
. filterPlatform ( cfg
. platform
, cfg
. exclude
);
2270 onUpdatedManifestLoaded : function ( result
) {
2271 result
= Microloader
. parseResult ( result
);
2273 if (! result
. error
) {
2274 var currentAssets
, newAssets
, currentAsset
, newAsset
, prop
,
2275 assets
, deltas
, deltaPath
, include
,
2276 updatingAssets
= [],
2277 manifest
= new Manifest ({
2278 url
: Microloader
. manifest
. url
,
2279 content
: result
. content
,
2283 Microloader
. remainingUpdatingAssets
= 0 ;
2284 Microloader
. updatedAssets
= [];
2285 Microloader
. removedAssets
= [];
2286 Microloader
. updatedManifest
= null ;
2287 Microloader
. updatedAssetsReady
= false ;
2289 // If the updated manifest has turned off caching we need to clear out all local storage
2290 // and trigger a appupdate as all content is now uncached
2291 if (! manifest
. shouldCache ()) {
2293 Microloader
. updatedManifest
= manifest
;
2294 LocalStorage
. clearAllPrivate ( manifest
);
2295 Microloader
. onAllUpdatedAssetsReady ();
2299 // Manifest itself has changed
2300 if (! Microloader
. manifest
. is ( manifest
)) {
2301 Microloader
. updatedManifest
= manifest
;
2303 currentAssets
= Microloader
. manifest
. getAssets ();
2304 newAssets
= manifest
. getAssets ();
2306 // Look through new assets for assets that do not exist or assets that have different versions
2307 for ( prop
in newAssets
) {
2308 newAsset
= newAssets
[ prop
];
2309 currentAsset
= Microloader
. manifest
. getAsset ( newAsset
. assetConfig
. path
);
2310 include
= Microloader
. filterAsset ( newAsset
);
2312 if ( include
&& (! currentAsset
|| ( newAsset
. shouldCache () && (! currentAsset
. is ( newAsset
))))) {
2313 updatingAssets
. push ({ _new
: newAsset
, _current
: currentAsset
});
2317 // Look through current assets for stale/old assets that have been removed
2318 for ( prop
in currentAssets
) {
2319 currentAsset
= currentAssets
[ prop
];
2320 newAsset
= manifest
. getAsset ( currentAsset
. assetConfig
. path
);
2322 //New version of this asset has been filtered out
2323 include
= ! Microloader
. filterAsset ( newAsset
);
2325 if (! include
|| ! newAsset
|| ( currentAsset
. shouldCache () && ! newAsset
. shouldCache ())) {
2326 Microloader
. removedAssets
. push ( currentAsset
);
2330 // Loop through all assets that need updating
2331 if ( updatingAssets
. length
> 0 ) {
2332 Microloader
. remainingUpdatingAssets
= updatingAssets
. length
;
2333 while ( updatingAssets
. length
> 0 ) {
2334 assets
= updatingAssets
. pop ();
2335 newAsset
= assets
. _new
;
2336 currentAsset
= assets
. _current
;
2338 // Full Updates will simply download the file and replace its current content
2339 if ( newAsset
. assetConfig
. update
=== "full" || ! currentAsset
) {
2342 // Load the asset and cache its its content into Boot to be evaluated in sequence
2343 Boot
. fetch ( newAsset
. assetConfig
. path
, ( function ( asset
) {
2344 return function ( result
) {
2345 Microloader
. onFullAssetUpdateLoaded ( asset
, result
)
2350 // Delta updates will be given a delta patch
2351 } else if ( newAsset
. assetConfig
. update
=== "delta" ) {
2352 deltas
= manifest
. deltas
;
2353 deltaPath
= deltas
+ "/" + newAsset
. assetConfig
. path
+ "/" + currentAsset
. assetConfig
. hash
+ ".json" ;
2354 // Fetch the Delta Patch and update the contents of the asset
2355 Boot
. fetch ( deltaPath
,
2356 ( function ( asset
, oldAsset
) {
2357 return function ( result
) {
2358 Microloader
. onDeltaAssetUpdateLoaded ( asset
, oldAsset
, result
)
2360 }( newAsset
, currentAsset
))
2365 Microloader
. onAllUpdatedAssetsReady ();
2368 Microloader
. onAllUpdatedAssetsReady ();
2371 _warn ( "Error loading manifest file to check for updates" );
2372 Microloader
. onAllUpdatedAssetsReady ();
2376 onFullAssetUpdateLoaded : function ( asset
, result
) {
2378 result
= Microloader
. parseResult ( result
);
2379 Microloader
. remainingUpdatingAssets
--;
2381 if (! result
. error
) {
2382 checksum
= Microloader
. checksum ( result
. content
, asset
. assetConfig
. hash
);
2385 // uncache this asset as there is a new version somewhere that has not been loaded.
2388 asset
. updateContent ( result
. content
);
2389 Microloader
. updatedAssets
. push ( asset
);
2393 // uncache this asset as there is a new version somewhere that has not been loaded.
2397 if ( Microloader
. remainingUpdatingAssets
=== 0 ) {
2398 Microloader
. onAllUpdatedAssetsReady ();
2402 onDeltaAssetUpdateLoaded : function ( asset
, oldAsset
, result
) {
2403 var json
, checksum
, content
;
2404 result
= Microloader
. parseResult ( result
);
2405 Microloader
. remainingUpdatingAssets
--;
2407 if (! result
. error
) {
2409 json
= JSON
. parse ( result
. content
);
2410 content
= Microloader
. patch ( oldAsset
. content
, json
);
2411 checksum
= Microloader
. checksum ( content
, asset
. assetConfig
. hash
);
2414 // uncache this asset as there is a new version somewhere that has not been loaded.
2417 asset
. updateContent ( content
);
2418 Microloader
. updatedAssets
. push ( asset
);
2421 _warn ( "Error parsing delta patch for " + asset
. assetConfig
. path
+ " with hash " + oldAsset
. assetConfig
. hash
+ " . This asset will be uncached for future loading" );
2422 // uncache this asset as there is a new version somewhere that has not been loaded.
2426 _warn ( "Error loading delta patch for " + asset
. assetConfig
. path
+ " with hash " + oldAsset
. assetConfig
. hash
+ " . This asset will be uncached for future loading" );
2428 // uncache this asset as there is a new version somewhere that has not been loaded.
2431 if ( Microloader
. remainingUpdatingAssets
=== 0 ) {
2432 Microloader
. onAllUpdatedAssetsReady ();
2436 //TODO: Make this all transaction based to allow for reverting if quota is exceeded
2437 onAllUpdatedAssetsReady : function () {
2439 Microloader
. updatedAssetsReady
= true ;
2441 if ( Microloader
. updatedManifest
) {
2442 while ( Microloader
. removedAssets
. length
> 0 ) {
2443 asset
= Microloader
. removedAssets
. pop ();
2447 if ( Microloader
. updatedManifest
) {
2448 Microloader
. updatedManifest
. cache ();
2451 while ( Microloader
. updatedAssets
. length
> 0 ) {
2452 asset
= Microloader
. updatedAssets
. pop ();
2458 Microloader
. notifyUpdateReady ();
2461 notifyUpdateReady : function () {
2462 if ( Microloader
. appCacheState
!== 'checking' && Microloader
. updatedAssetsReady
) {
2463 if ( Microloader
. appCacheState
=== 'updated' || Microloader
. updatedManifest
) {
2464 Microloader
. appUpdate
= {
2466 app
: Microloader
. appCacheState
=== 'updated' ,
2467 manifest
: Microloader
. updatedManifest
&& Microloader
. updatedManifest
. exportContent ()
2470 Microloader
. fireAppUpdate ();
2475 fireAppUpdate : function () {
2476 if ( Ext
. GlobalEvents
) {
2477 // We defer dispatching this event slightly in order to let the application finish loading
2478 // as we are still very early in the lifecycle
2479 Ext
. defer ( function () {
2480 Ext
. GlobalEvents
. fireEvent ( 'appupdate' , Microloader
. appUpdate
);
2485 checksum : function ( content
, hash
) {
2486 if (! content
|| ! hash
) {
2491 hashLn
= hash
. length
,
2492 checksumType
= content
. substring ( 0 , 1 );
2494 if ( checksumType
== '/' ) {
2495 if ( content
. substring ( 2 , hashLn
+ 2 ) !== hash
) {
2498 } else if ( checksumType
== 'f' ) {
2499 if ( content
. substring ( 10 , hashLn
+ 10 ) !== hash
) {
2502 } else if ( checksumType
== '.' ) {
2503 if ( content
. substring ( 1 , hashLn
+ 1 ) !== hash
) {
2509 parseResult : function ( result
) {
2511 if (( result
. exception
|| result
. status
=== 0 ) && ! Boot
. env
. phantom
) {
2513 } else if (( result
. status
>= 200 && result
. status
< 300 ) || result
. status
=== 304
2515 || ( result
. status
=== 0 && result
. content
. length
> 0 )
2517 rst
. content
= result
. content
;
2529 * @type {String/Object}
2531 Ext
. manifest
= Ext
. manifest
|| "bootstrap" ;
2533 Ext
. Microloader
. run ();