]> git.proxmox.com Git - extjs.git/blob - extjs/examples/classic/desktop/.sencha/app/Microloader.js
add extjs 6.0.1 sources
[extjs.git] / extjs / examples / classic / desktop / .sencha / app / Microloader.js
1 // here, the extra check for window['Ext'] is needed for use with cmd-test
2 // code injection. we need to make that this file will sync up with page global
3 // scope to avoid duplicate Ext.Boot state. That check is after the initial Ext check
4 // to allow the sandboxing template to inject an appropriate Ext var and prevent the
5 // global detection.
6 var Ext = Ext || window['Ext'] || {};
7
8
9 //<editor-fold desc="Microloader">
10 /**
11 * @Class Ext.Microloader
12 * @singleton
13 */
14 Ext.Microloader = Ext.Microloader || (function () {
15 var Boot = Ext.Boot,
16 //<debug>
17 _debug = function (message) {
18 //console.log(message);
19 },
20 //</debug>
21 _warn = function (message) {
22 console.log("[WARN] " + message);
23 },
24 _privatePrefix = '_ext:' + location.pathname,
25
26 /**
27 * The Following combination is used to create isolated local storage keys
28 * '_ext' is used to scope all the local storage keys that we internally by Ext
29 * 'location.pathname' is used to force each assets to cache by an absolute URL (/build/MyApp) (dev vs prod)
30 * 'url' is used to force each asset to cache relative to the page (app.json vs resources/app.css)
31 * 'profileId' is used to differentiate the builds of an application (neptune vs crisp)
32 * 'Microloader.appId' is unique to the application and will differentiate apps on the same host (dev mode running app watch against multiple apps)
33 */
34 getStorageKey = function(url, profileId) {
35 return _privatePrefix + url + '-' + (profileId ? profileId + '-' : '') + Microloader.appId;
36 },
37 postProcessor, _storage;
38
39 try {
40 _storage = window['localStorage'];
41 } catch(ex) {
42 // ignore
43 }
44
45 var _cache = window['applicationCache'],
46 // Local Storage Controller
47 LocalStorage = {
48 clearAllPrivate: function(manifest) {
49 if(_storage) {
50
51 //Remove the entry for the manifest first
52 _storage.removeItem(manifest.key);
53
54 var i, key,
55 removeKeys = [],
56 suffix = manifest.profile + '-' + Microloader.appId,
57 ln = _storage.length;
58 for (i = 0; i < ln; i++) {
59 key = _storage.key(i);
60 // If key starts with the private key and the suffix is present we can clear this entry
61 if (key.indexOf(_privatePrefix) === 0 && key.indexOf(suffix) !== -1) {
62 removeKeys.push(key);
63 }
64 }
65
66 for(i in removeKeys) {
67 //<debug>
68 _debug("Removing "+ removeKeys[i] + " from Local Storage");
69 //</debug>
70 _storage.removeItem(removeKeys[i]);
71 }
72 }
73 },
74 /**
75 * private
76 */
77 retrieveAsset: function (key) {
78 try {
79 return _storage.getItem(key);
80 }
81 catch (e) {
82 // Private browsing mode
83 return null;
84 }
85 },
86
87 setAsset: function(key, content) {
88 try {
89 if (content === null || content == '') {
90 _storage.removeItem(key);
91 } else {
92 _storage.setItem(key, content);
93 }
94 }
95 catch (e) {
96 if (_storage && e.code == e.QUOTA_EXCEEDED_ERR) {
97 //<debug>
98 _warn("LocalStorage Quota exceeded, cannot store " + key + " locally");
99 //</debug>
100 }
101 }
102 }
103 };
104
105 var Asset = function (cfg) {
106 if (typeof cfg.assetConfig === 'string') {
107 this.assetConfig = {
108 path: cfg.assetConfig
109 };
110 } else {
111 this.assetConfig = cfg.assetConfig;
112 }
113
114 this.type = cfg.type;
115 this.key = getStorageKey(this.assetConfig.path, cfg.manifest.profile);
116
117 if (cfg.loadFromCache) {
118 this.loadFromCache();
119 }
120 };
121
122 Asset.prototype = {
123 shouldCache: function() {
124 return _storage && this.assetConfig.update && this.assetConfig.hash && !this.assetConfig.remote;
125 },
126
127 is: function (asset) {
128 return (!!asset && this.assetConfig && asset.assetConfig && (this.assetConfig.hash === asset.assetConfig.hash))
129 },
130
131 cache: function(content) {
132 if (this.shouldCache()) {
133 LocalStorage.setAsset(this.key, content || this.content);
134 }
135 },
136
137 uncache: function() {
138 LocalStorage.setAsset(this.key, null);
139 },
140
141 updateContent: function (content) {
142 this.content = content;
143 },
144
145 getSize: function () {
146 return this.content ? this.content.length : 0;
147 },
148
149 loadFromCache: function() {
150 if (this.shouldCache()) {
151 this.content = LocalStorage.retrieveAsset(this.key);
152 }
153 }
154 };
155
156 var Manifest = function (cfg) {
157 if (typeof cfg.content === "string") {
158 this.content = JSON.parse(cfg.content);
159 } else {
160 this.content = cfg.content;
161 }
162 this.assetMap = {};
163
164 this.url = cfg.url;
165 this.fromCache = !!cfg.cached;
166 this.assetCache = !(cfg.assetCache === false);
167 this.key = getStorageKey(this.url);
168
169 // Pull out select properties for repetitive use
170 this.profile = this.content.profile;
171 this.hash = this.content.hash;
172 this.loadOrder = this.content.loadOrder;
173 this.deltas = this.content.cache ? this.content.cache.deltas : null;
174 this.cacheEnabled = this.content.cache ? this.content.cache.enable : false;
175
176 this.loadOrderMap = (this.loadOrder) ? Boot.createLoadOrderMap(this.loadOrder) : null;
177
178 var tags = this.content.tags,
179 platformTags = Ext.platformTags;
180
181 if (tags) {
182 if (tags instanceof Array) {
183 for (var i = 0; i < tags.length; i++) {
184 platformTags[tags[i]] = true;
185 }
186 } else {
187 Boot.apply(platformTags, tags);
188 }
189
190 // re-apply the query parameters, so that the params as specified
191 // in the url always has highest priority
192 Boot.apply(platformTags, Boot.loadPlatformsParam());
193 }
194
195 // Convert all assets into Assets
196 this.js = this.processAssets(this.content.js, 'js');
197 this.css = this.processAssets(this.content.css, 'css');
198 };
199
200 Manifest.prototype = {
201 processAsset: function(assetConfig, type) {
202 var processedAsset = new Asset({
203 manifest: this,
204 assetConfig: assetConfig,
205 type: type,
206 loadFromCache: this.assetCache
207 });
208 this.assetMap[assetConfig.path] = processedAsset;
209 return processedAsset;
210 },
211
212 processAssets: function(assets, type) {
213 var results = [],
214 ln = assets.length,
215 i, assetConfig;
216
217 for (i = 0; i < ln; i++) {
218 assetConfig = assets[i];
219 results.push(this.processAsset(assetConfig, type));
220 }
221
222 return results;
223 },
224
225 useAppCache: function() {
226 return true;
227 },
228
229 // Concatenate all assets for easy access
230 getAssets: function () {
231 return this.css.concat(this.js);
232 },
233
234 getAsset: function (path) {
235 return this.assetMap[path];
236 },
237
238 shouldCache: function() {
239 return this.hash && this.cacheEnabled;
240 },
241
242 cache: function(content) {
243 if (this.shouldCache()) {
244 LocalStorage.setAsset(this.key, JSON.stringify(content || this.content));
245 }
246 //<debug>
247 else {
248 _debug("Manifest caching is disabled.");
249 }
250 //</debug>
251 },
252
253 is: function(manifest) {
254 //<debug>
255 _debug("Testing Manifest: " + this.hash + " VS " + manifest.hash);
256 //</debug>
257 return this.hash === manifest.hash;
258 },
259
260 // Clear the manifest from local storage
261 uncache: function() {
262 LocalStorage.setAsset(this.key, null);
263 },
264
265 exportContent: function() {
266 return Boot.apply({
267 loadOrderMap: this.loadOrderMap
268 }, this.content);
269 }
270 };
271
272 /**
273 * Microloader
274 * @type {Array}
275 * @private
276 */
277 var _listeners = [],
278 _loaded = false,
279 Microloader = {
280 init: function () {
281 Ext.microloaded = true;
282
283 // data-app is in the dev template for an application and is also
284 // injected into the app my CMD for production
285 // We use this to prefix localStorage cache to prevent collisions
286 var microloaderElement = document.getElementById('microloader');
287 Microloader.appId = microloaderElement ? microloaderElement.getAttribute('data-app') : '';
288
289 if (Ext.beforeLoad) {
290 postProcessor = Ext.beforeLoad(Ext.platformTags);
291 }
292
293 var readyHandler = Ext._beforereadyhandler;
294
295 Ext._beforereadyhandler = function () {
296 if (Ext.Boot !== Boot) {
297 Ext.apply(Ext.Boot, Boot);
298 Ext.Boot = Boot;
299 }
300 if (readyHandler) {
301 readyHandler();
302 }
303 };
304 },
305
306 applyCacheBuster: function(url) {
307 var tstamp = new Date().getTime(),
308 sep = url.indexOf('?') === -1 ? '?' : '&';
309 url = url + sep + "_dc=" + tstamp;
310 return url;
311 },
312
313 run: function() {
314 Microloader.init();
315 var manifest = Ext.manifest;
316
317 if (typeof manifest === "string") {
318 var extension = ".json",
319 url = manifest.indexOf(extension) === manifest.length - extension.length
320 ? manifest
321 : manifest + ".json",
322 key = getStorageKey(url),
323 content = LocalStorage.retrieveAsset(key);
324
325 // Manifest found in local storage, use this for immediate boot except in PhantomJS environments for building.
326 if (content) {
327 //<debug>
328 _debug("Manifest file, '" + url + "', was found in Local Storage");
329 //</debug>
330 manifest = new Manifest({
331 url: url,
332 content: content,
333 cached: true
334 });
335 if (postProcessor) {
336 postProcessor(manifest);
337 }
338 Microloader.load(manifest);
339
340
341 // Manifest is not in local storage. Fetch it from the server
342 } else {
343 Boot.fetch(Microloader.applyCacheBuster(url), function (result) {
344 //<debug>
345 _debug("Manifest file was not found in Local Storage, loading: " + url);
346 //</debug>
347 manifest = new Manifest({
348 url: url,
349 content: result.content
350 });
351
352 manifest.cache();
353 if (postProcessor) {
354 postProcessor(manifest);
355 }
356 Microloader.load(manifest);
357 });
358 }
359
360 // Embedded Manifest into JS file
361 } else {
362 //<debug>
363 _debug("Manifest was embedded into application javascript file");
364 //</debug>
365 manifest = new Manifest({
366 content: manifest
367 });
368 Microloader.load(manifest);
369 }
370 },
371
372 /**
373 *
374 * @param {Manifest} manifest
375 */
376 load: function (manifest) {
377 Microloader.urls = [];
378 Microloader.manifest = manifest;
379 Ext.manifest = Microloader.manifest.exportContent();
380
381 var assets = manifest.getAssets(),
382 cachedAssets = [],
383 asset, i, len, include, entry;
384
385 for (len = assets.length, i = 0; i < len; i++) {
386 asset = assets[i];
387 include = Microloader.filterAsset(asset);
388 if (include) {
389 // Asset is using the localStorage caching system
390 if (manifest.shouldCache() && asset.shouldCache()) {
391 // Asset already has content from localStorage, instantly seed that into boot
392 if (asset.content) {
393 //<debug>
394 _debug("Asset: " + asset.assetConfig.path + " was found in local storage. No remote load for this file");
395 //</debug>
396 entry = Boot.registerContent(asset.assetConfig.path, asset.type, asset.content);
397 if (entry.evaluated) {
398 _warn("Asset: " + asset.assetConfig.path + " was evaluated prior to local storage being consulted.");
399 }
400 //load via AJAX and seed content into Boot
401 } else {
402 //<debug>
403 _debug("Asset: " + asset.assetConfig.path + " was NOT found in local storage. Adding to load queue");
404 //</debug>
405 cachedAssets.push(asset);
406 }
407 }
408 Microloader.urls.push(asset.assetConfig.path);
409 Boot.assetConfig[asset.assetConfig.path] = Boot.apply({type: asset.type}, asset.assetConfig);
410 }
411 }
412
413 // If any assets are using the caching system and do not have local versions load them first via AJAX
414 if (cachedAssets.length > 0) {
415 Microloader.remainingCachedAssets = cachedAssets.length;
416 while (cachedAssets.length > 0) {
417 asset = cachedAssets.pop();
418 //<debug>
419 _debug("Preloading/Fetching Cached Assets from: " + asset.assetConfig.path);
420 //</debug>
421 Boot.fetch(asset.assetConfig.path, (function(asset) {
422 return function(result) {
423 Microloader.onCachedAssetLoaded(asset, result);
424 }
425 })(asset));
426 }
427 } else {
428 Microloader.onCachedAssetsReady();
429 }
430 },
431
432 // Load the asset and seed its content into Boot to be evaluated in sequence
433 onCachedAssetLoaded: function (asset, result) {
434 var checksum;
435 result = Microloader.parseResult(result);
436 Microloader.remainingCachedAssets--;
437
438 if (!result.error) {
439 checksum = Microloader.checksum(result.content, asset.assetConfig.hash);
440 if (!checksum) {
441 _warn("Cached Asset '" + asset.assetConfig.path + "' has failed checksum. This asset will be uncached for future loading");
442
443 // Un cache this asset so it is loaded next time
444 asset.uncache();
445 }
446
447 //<debug>
448 _debug("Checksum for Cached Asset: " + asset.assetConfig.path + " is " + checksum);
449 //</debug>
450 Boot.registerContent(asset.assetConfig.path, asset.type, result.content);
451 asset.updateContent(result.content);
452 asset.cache();
453 } else {
454 _warn("There was an error pre-loading the asset '" + asset.assetConfig.path + "'. This asset will be uncached for future loading");
455
456 // Un cache this asset so it is loaded next time
457 asset.uncache();
458 }
459
460 if (Microloader.remainingCachedAssets === 0) {
461 Microloader.onCachedAssetsReady();
462 }
463 },
464
465 onCachedAssetsReady: function(){
466 Boot.load({
467 url: Microloader.urls,
468 loadOrder: Microloader.manifest.loadOrder,
469 loadOrderMap: Microloader.manifest.loadOrderMap,
470 sequential: true,
471 success: Microloader.onAllAssetsReady,
472 failure: Microloader.onAllAssetsReady
473 });
474 },
475
476 onAllAssetsReady: function() {
477 _loaded = true;
478 Microloader.notify();
479
480 if (navigator.onLine !== false) {
481 //<debug>
482 _debug("Application is online, checking for updates");
483 //</debug>
484 Microloader.checkAllUpdates();
485 }
486 else {
487 //<debug>
488 _debug("Application is offline, adding online listener to check for updates");
489 //</debug>
490 if(window['addEventListener']) {
491 window.addEventListener('online', Microloader.checkAllUpdates, false);
492 }
493 }
494 },
495
496 onMicroloaderReady: function (listener) {
497 if (_loaded) {
498 listener();
499 } else {
500 _listeners.push(listener);
501 }
502 },
503
504 /**
505 * @private
506 */
507 notify: function () {
508 //<debug>
509 _debug("notifying microloader ready listeners.");
510 //</debug>
511 var listener;
512 while((listener = _listeners.shift())) {
513 listener();
514 }
515 },
516
517 // Delta patches content
518 patch: function (content, delta) {
519 var output = [],
520 chunk, i, ln;
521
522 if (delta.length === 0) {
523 return content;
524 }
525
526 for (i = 0,ln = delta.length; i < ln; i++) {
527 chunk = delta[i];
528
529 if (typeof chunk === 'number') {
530 output.push(content.substring(chunk, chunk + delta[++i]));
531 }
532 else {
533 output.push(chunk);
534 }
535 }
536
537 return output.join('');
538 },
539
540 checkAllUpdates: function() {
541 //<debug>
542 _debug("Checking for All Updates");
543 //</debug>
544 if(window['removeEventListener']) {
545 window.removeEventListener('online', Microloader.checkAllUpdates, false);
546 }
547
548 if(_cache) {
549 Microloader.checkForAppCacheUpdate();
550 }
551
552 // Manifest came from a cached instance, check for updates
553 if (Microloader.manifest.fromCache) {
554 Microloader.checkForUpdates();
555 }
556 },
557
558 checkForAppCacheUpdate: function() {
559 //<debug>
560 _debug("Checking App Cache status");
561 //</debug>
562 if (_cache.status === _cache.UPDATEREADY || _cache.status === _cache.OBSOLETE) {
563 //<debug>
564 _debug("App Cache is already in an updated");
565 //</debug>
566 Microloader.appCacheState = 'updated';
567 } else if (_cache.status !== _cache.IDLE && _cache.status !== _cache.UNCACHED) {
568 //<debug>
569 _debug("App Cache is checking or downloading updates, adding listeners");
570 //</debug>
571 Microloader.appCacheState = 'checking';
572 _cache.addEventListener('error', Microloader.onAppCacheError);
573 _cache.addEventListener('noupdate', Microloader.onAppCacheNotUpdated);
574 _cache.addEventListener('cached', Microloader.onAppCacheNotUpdated);
575 _cache.addEventListener('updateready', Microloader.onAppCacheReady);
576 _cache.addEventListener('obsolete', Microloader.onAppCacheObsolete);
577 } else {
578 //<debug>
579 _debug("App Cache is current or uncached");
580 //</debug>
581 Microloader.appCacheState = 'current';
582 }
583 },
584
585 checkForUpdates: function() {
586 // Fetch the Latest Manifest from the server
587 //<debug>
588 _debug("Checking for updates at: " + Microloader.manifest.url);
589 //</debug>
590 Boot.fetch(Microloader.applyCacheBuster(Microloader.manifest.url), Microloader.onUpdatedManifestLoaded);
591 },
592
593 onAppCacheError: function(e) {
594 _warn(e.message);
595
596 Microloader.appCacheState = 'error';
597 Microloader.notifyUpdateReady();
598 },
599
600 onAppCacheReady: function() {
601 _cache.swapCache();
602 Microloader.appCacheUpdated();
603 },
604
605 onAppCacheObsolete: function() {
606 Microloader.appCacheUpdated();
607 },
608
609 appCacheUpdated: function() {
610 //<debug>
611 _debug("App Cache Updated");
612 //</debug>
613 Microloader.appCacheState = 'updated';
614 Microloader.notifyUpdateReady();
615 },
616
617 onAppCacheNotUpdated: function() {
618 //<debug>
619 _debug("App Cache Not Updated Callback");
620 //</debug>
621 Microloader.appCacheState = 'current';
622 Microloader.notifyUpdateReady();
623 },
624
625
626 filterAsset: function(asset) {
627 var cfg = (asset && asset.assetConfig) || {};
628 if(cfg.platform || cfg.exclude) {
629 return Boot.filterPlatform(cfg.platform, cfg.exclude);
630 }
631 return true;
632 },
633
634 onUpdatedManifestLoaded: function (result) {
635 result = Microloader.parseResult(result);
636
637 if (!result.error) {
638 var currentAssets, newAssets, currentAsset, newAsset, prop,
639 assets, deltas, deltaPath, include,
640 updatingAssets = [],
641 manifest = new Manifest({
642 url: Microloader.manifest.url,
643 content: result.content,
644 assetCache: false
645 });
646
647 Microloader.remainingUpdatingAssets = 0;
648 Microloader.updatedAssets = [];
649 Microloader.removedAssets = [];
650 Microloader.updatedManifest = null;
651 Microloader.updatedAssetsReady = false;
652
653 // If the updated manifest has turned off caching we need to clear out all local storage
654 // and trigger a appupdate as all content is now uncached
655 if (!manifest.shouldCache()) {
656 //<debug>
657 _debug("New Manifest has caching disabled, clearing out any private storage");
658 //</debug>
659
660 Microloader.updatedManifest = manifest;
661 LocalStorage.clearAllPrivate(manifest);
662 Microloader.onAllUpdatedAssetsReady();
663 return;
664 }
665
666 // Manifest itself has changed
667 if (!Microloader.manifest.is(manifest)) {
668 Microloader.updatedManifest = manifest;
669
670 currentAssets = Microloader.manifest.getAssets();
671 newAssets = manifest.getAssets();
672
673 // Look through new assets for assets that do not exist or assets that have different versions
674 for (prop in newAssets) {
675 newAsset = newAssets[prop];
676 currentAsset = Microloader.manifest.getAsset(newAsset.assetConfig.path);
677 include = Microloader.filterAsset(newAsset);
678
679 if (include && (!currentAsset || (newAsset.shouldCache() && (!currentAsset.is(newAsset))))) {
680 //<debug>
681 _debug("New/Updated Version of Asset: " + newAsset.assetConfig.path + " was found in new manifest");
682 //</debug>
683 updatingAssets.push({_new: newAsset, _current: currentAsset});
684 }
685 }
686
687 // Look through current assets for stale/old assets that have been removed
688 for (prop in currentAssets) {
689 currentAsset = currentAssets[prop];
690 newAsset = manifest.getAsset(currentAsset.assetConfig.path);
691
692 //New version of this asset has been filtered out
693 include = !Microloader.filterAsset(newAsset);
694
695 if (!include || !newAsset || (currentAsset.shouldCache() && !newAsset.shouldCache())) {
696 //<debug>
697 _debug("Asset: " + currentAsset.assetConfig.path + " was not found in new manifest, has been filtered out or has been switched to not cache. Marked for removal");
698 //</debug>
699 Microloader.removedAssets.push(currentAsset);
700 }
701 }
702
703 // Loop through all assets that need updating
704 if (updatingAssets.length > 0) {
705 Microloader.remainingUpdatingAssets = updatingAssets.length;
706 while (updatingAssets.length > 0) {
707 assets = updatingAssets.pop();
708 newAsset = assets._new;
709 currentAsset = assets._current;
710
711 // Full Updates will simply download the file and replace its current content
712 if (newAsset.assetConfig.update === "full" || !currentAsset) {
713
714 //<debug>
715 if (newAsset.assetConfig.update === "delta") {
716 _debug("Delta updated asset found without current asset available: " + newAsset.assetConfig.path + " fetching full file");
717 } else {
718 _debug("Full update found for: " + newAsset.assetConfig.path + " fetching");
719 }
720 //</debug>
721
722 // Load the asset and cache its its content into Boot to be evaluated in sequence
723 Boot.fetch(newAsset.assetConfig.path, (function (asset) {
724 return function (result) {
725 Microloader.onFullAssetUpdateLoaded(asset, result)
726 };
727 }(newAsset))
728 );
729
730 // Delta updates will be given a delta patch
731 } else if (newAsset.assetConfig.update === "delta") {
732 deltas = manifest.deltas;
733 deltaPath = deltas + "/" + newAsset.assetConfig.path + "/" + currentAsset.assetConfig.hash + ".json";
734 // Fetch the Delta Patch and update the contents of the asset
735 //<debug>
736 _debug("Delta update found for: " + newAsset.assetConfig.path + " fetching");
737 //</debug>
738 Boot.fetch(deltaPath,
739 (function (asset, oldAsset) {
740 return function (result) {
741 Microloader.onDeltaAssetUpdateLoaded(asset, oldAsset, result)
742 };
743 }(newAsset, currentAsset))
744 );
745 }
746 }
747 } else {
748 //<debug>
749 _debug("No Assets needed updating");
750 //</debug>
751 Microloader.onAllUpdatedAssetsReady();
752 }
753 } else {
754 //<debug>
755 _debug("Manifest files have matching hash's");
756 //</debug>
757 Microloader.onAllUpdatedAssetsReady();
758 }
759 } else {
760 _warn("Error loading manifest file to check for updates");
761 Microloader.onAllUpdatedAssetsReady();
762 }
763 },
764
765 onFullAssetUpdateLoaded: function(asset, result) {
766 var checksum;
767 result = Microloader.parseResult(result);
768 Microloader.remainingUpdatingAssets--;
769
770 if (!result.error) {
771 checksum = Microloader.checksum(result.content, asset.assetConfig.hash);
772 //<debug>
773 _debug("Checksum for Full asset: " + asset.assetConfig.path + " is " + checksum);
774 //</debug>
775 if (!checksum) {
776 //<debug>
777 _debug("Full Update Asset: " + asset.assetConfig.path + " has failed checksum. This asset will be uncached for future loading");
778 //</debug>
779
780 // uncache this asset as there is a new version somewhere that has not been loaded.
781 asset.uncache();
782 } else {
783 asset.updateContent(result.content);
784 Microloader.updatedAssets.push(asset);
785 }
786 } else {
787 //<debug>
788 _debug("Error loading file at" + asset.assetConfig.path + ". This asset will be uncached for future loading");
789 //</debug>
790
791 // uncache this asset as there is a new version somewhere that has not been loaded.
792 asset.uncache();
793 }
794
795 if (Microloader.remainingUpdatingAssets === 0) {
796 Microloader.onAllUpdatedAssetsReady();
797 }
798 },
799
800 onDeltaAssetUpdateLoaded: function(asset, oldAsset, result) {
801 var json, checksum, content;
802 result = Microloader.parseResult(result);
803 Microloader.remainingUpdatingAssets--;
804
805 if (!result.error) {
806 //<debug>
807 _debug("Delta patch loaded successfully, patching content");
808 //</debug>
809 try {
810 json = JSON.parse(result.content);
811 content = Microloader.patch(oldAsset.content, json);
812 checksum = Microloader.checksum(content, asset.assetConfig.hash);
813 //<debug>
814 _debug("Checksum for Delta Patched asset: " + asset.assetConfig.path + " is " + checksum);
815 //</debug>
816 if (!checksum) {
817 //<debug>
818 _debug("Delta Update Asset: " + asset.assetConfig.path + " has failed checksum. This asset will be uncached for future loading");
819 //</debug>
820
821 // uncache this asset as there is a new version somewhere that has not been loaded.
822 asset.uncache();
823 } else {
824 asset.updateContent(content);
825 Microloader.updatedAssets.push(asset);
826 }
827 } catch (e) {
828 _warn("Error parsing delta patch for " + asset.assetConfig.path + " with hash " + oldAsset.assetConfig.hash + " . This asset will be uncached for future loading");
829 // uncache this asset as there is a new version somewhere that has not been loaded.
830 asset.uncache();
831 }
832 } else {
833 _warn("Error loading delta patch for " + asset.assetConfig.path + " with hash " + oldAsset.assetConfig.hash + " . This asset will be uncached for future loading");
834
835 // uncache this asset as there is a new version somewhere that has not been loaded.
836 asset.uncache();
837 }
838 if (Microloader.remainingUpdatingAssets === 0) {
839 Microloader.onAllUpdatedAssetsReady();
840 }
841 },
842
843 //TODO: Make this all transaction based to allow for reverting if quota is exceeded
844 onAllUpdatedAssetsReady: function() {
845 var asset;
846 Microloader.updatedAssetsReady = true;
847
848 if (Microloader.updatedManifest) {
849 while (Microloader.removedAssets.length > 0) {
850 asset = Microloader.removedAssets.pop();
851 //<debug>
852 _debug("Asset: " + asset.assetConfig.path + " was removed, un-caching");
853 //</debug>
854 asset.uncache();
855 }
856
857 if (Microloader.updatedManifest) {
858 //<debug>
859 _debug("Manifest was updated, re-caching");
860 //</debug>
861 Microloader.updatedManifest.cache();
862 }
863
864 while (Microloader.updatedAssets.length > 0) {
865 asset = Microloader.updatedAssets.pop();
866 //<debug>
867 _debug("Asset: " + asset.assetConfig.path + " was updated, re-caching");
868 //</debug>
869 asset.cache();
870 }
871
872 }
873
874 Microloader.notifyUpdateReady();
875 },
876
877 notifyUpdateReady: function () {
878 if (Microloader.appCacheState !== 'checking' && Microloader.updatedAssetsReady) {
879 if (Microloader.appCacheState === 'updated' || Microloader.updatedManifest) {
880 //<debug>
881 _debug("There was an update here you will want to reload the app, trigger an event");
882 //</debug>
883 Microloader.appUpdate = {
884 updated: true,
885 app: Microloader.appCacheState === 'updated',
886 manifest: Microloader.updatedManifest && Microloader.updatedManifest.exportContent()
887 };
888
889 Microloader.fireAppUpdate();
890 }
891 //<debug>
892 else {
893 _debug("AppCache and LocalStorage Cache are current, no updating needed");
894 Microloader.appUpdate = {};
895 }
896 //</debug>
897 }
898 },
899
900 fireAppUpdate: function() {
901 if (Ext.GlobalEvents) {
902 // We defer dispatching this event slightly in order to let the application finish loading
903 // as we are still very early in the lifecycle
904 Ext.defer(function() {
905 Ext.GlobalEvents.fireEvent('appupdate', Microloader.appUpdate);
906 }, 100);
907 }
908 },
909
910 checksum: function(content, hash) {
911 if(!content || !hash) {
912 return false;
913 }
914
915 var passed = true,
916 hashLn = hash.length,
917 checksumType = content.substring(0, 1);
918
919 if (checksumType == '/') {
920 if (content.substring(2, hashLn + 2) !== hash) {
921 passed = false;
922 }
923 } else if (checksumType == 'f') {
924 if (content.substring(10, hashLn + 10) !== hash) {
925 passed = false;
926 }
927 } else if (checksumType == '.') {
928 if (content.substring(1, hashLn + 1) !== hash) {
929 passed = false;
930 }
931 }
932 return passed;
933 },
934 parseResult: function(result) {
935 var rst = {};
936 if ((result.exception || result.status === 0) && !Boot.env.phantom) {
937 rst.error = true;
938 } else if ((result.status >= 200 && result.status < 300) || result.status === 304
939 || Boot.env.phantom
940 || (result.status === 0 && result.content.length > 0)
941 ) {
942 rst.content = result.content;
943 } else {
944 rst.error = true;
945 }
946 return rst;
947 }
948 };
949
950 return Microloader;
951 }());
952
953 /**
954 * @type {String/Object}
955 */
956 Ext.manifest = Ext.manifest || "bootstrap";
957
958 Ext.Microloader.run();