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