]> git.proxmox.com Git - extjs.git/blame - extjs/modern/theme-device-base/.sencha/package/Microloader.js
add extjs 6.0.1 sources
[extjs.git] / extjs / modern / theme-device-base / .sencha / package / 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 // Convert all assets into Assets\r
179 this.js = this.processAssets(this.content.js, 'js');\r
180 this.css = this.processAssets(this.content.css, 'css');\r
181 };\r
182\r
183 Manifest.prototype = {\r
184 processAsset: function(assetConfig, type) {\r
185 var processedAsset = new Asset({\r
186 manifest: this,\r
187 assetConfig: assetConfig,\r
188 type: type,\r
189 loadFromCache: this.assetCache\r
190 });\r
191 this.assetMap[assetConfig.path] = processedAsset;\r
192 return processedAsset;\r
193 },\r
194\r
195 processAssets: function(assets, type) {\r
196 var results = [],\r
197 ln = assets.length,\r
198 i, assetConfig;\r
199\r
200 for (i = 0; i < ln; i++) {\r
201 assetConfig = assets[i];\r
202 results.push(this.processAsset(assetConfig, type));\r
203 }\r
204\r
205 return results;\r
206 },\r
207\r
208 useAppCache: function() {\r
209 return true;\r
210 },\r
211\r
212 // Concatenate all assets for easy access\r
213 getAssets: function () {\r
214 return this.css.concat(this.js);\r
215 },\r
216\r
217 getAsset: function (path) {\r
218 return this.assetMap[path];\r
219 },\r
220\r
221 shouldCache: function() {\r
222 return this.hash && this.cacheEnabled;\r
223 },\r
224\r
225 cache: function(content) {\r
226 if (this.shouldCache()) {\r
227 LocalStorage.setAsset(this.key, JSON.stringify(content || this.content));\r
228 }\r
229 //<debug>\r
230 else {\r
231 _debug("Manifest caching is disabled.");\r
232 }\r
233 //</debug>\r
234 },\r
235\r
236 is: function(manifest) {\r
237 //<debug>\r
238 _debug("Testing Manifest: " + this.hash + " VS " + manifest.hash);\r
239 //</debug>\r
240 return this.hash === manifest.hash;\r
241 },\r
242\r
243 // Clear the manifest from local storage\r
244 uncache: function() {\r
245 LocalStorage.setAsset(this.key, null);\r
246 },\r
247\r
248 exportContent: function() {\r
249 return Boot.apply({\r
250 loadOrderMap: this.loadOrderMap\r
251 }, this.content);\r
252 }\r
253 };\r
254\r
255 /**\r
256 * Microloader\r
257 * @type {Array}\r
258 * @private\r
259 */\r
260 var _listeners = [],\r
261 _loaded = false,\r
262 Microloader = {\r
263 init: function () {\r
264 Ext.microloaded = true;\r
265\r
266 // data-app is in the dev template for an application and is also\r
267 // injected into the app my CMD for production\r
268 // We use this to prefix localStorage cache to prevent collisions\r
269 var microloaderElement = document.getElementById('microloader');\r
270 Microloader.appId = microloaderElement ? microloaderElement.getAttribute('data-app') : '';\r
271\r
272 if (Ext.beforeLoad) {\r
273 postProcessor = Ext.beforeLoad(Ext.platformTags);\r
274 }\r
275\r
276 var readyHandler = Ext._beforereadyhandler;\r
277\r
278 Ext._beforereadyhandler = function () {\r
279 if (Ext.Boot !== Boot) {\r
280 Ext.apply(Ext.Boot, Boot);\r
281 Ext.Boot = Boot;\r
282 }\r
283 if (readyHandler) {\r
284 readyHandler();\r
285 }\r
286 };\r
287 },\r
288\r
289 run: function() {\r
290 Microloader.init();\r
291 var manifest = Ext.manifest;\r
292\r
293 if (typeof manifest === "string") {\r
294 var extension = ".json",\r
295 url = manifest.indexOf(extension) === manifest.length - extension.length\r
296 ? manifest\r
297 : manifest + ".json",\r
298 key = getStorageKey(url),\r
299 content = LocalStorage.retrieveAsset(key);\r
300\r
301 // Manifest found in local storage, use this for immediate boot except in PhantomJS environments for building.\r
302 if (content) {\r
303 //<debug>\r
304 _debug("Manifest file, '" + url + "', was found in Local Storage");\r
305 //</debug>\r
306 manifest = new Manifest({\r
307 url: url,\r
308 content: content,\r
309 cached: true\r
310 });\r
311 if (postProcessor) {\r
312 postProcessor(manifest);\r
313 }\r
314 Microloader.load(manifest);\r
315\r
316\r
317 // Manifest is not in local storage. Fetch it from the server\r
318 } else {\r
319 Boot.fetch(url, function (result) {\r
320 //<debug>\r
321 _debug("Manifest file was not found in Local Storage, loading: " + url);\r
322 //</debug>\r
323 manifest = new Manifest({\r
324 url: url,\r
325 content: result.content\r
326 });\r
327\r
328 manifest.cache();\r
329 if (postProcessor) {\r
330 postProcessor(manifest);\r
331 }\r
332 Microloader.load(manifest);\r
333 });\r
334 }\r
335\r
336 // Embedded Manifest into JS file\r
337 } else {\r
338 //<debug>\r
339 _debug("Manifest was embedded into application javascript file");\r
340 //</debug>\r
341 manifest = new Manifest({\r
342 content: manifest\r
343 });\r
344 Microloader.load(manifest);\r
345 }\r
346 },\r
347\r
348 /**\r
349 *\r
350 * @param {Manifest} manifest\r
351 */\r
352 load: function (manifest) {\r
353 Microloader.urls = [];\r
354 Microloader.manifest = manifest;\r
355 Ext.manifest = Microloader.manifest.exportContent();\r
356\r
357 var assets = manifest.getAssets(),\r
358 cachedAssets = [],\r
359 asset, i, len, include, entry;\r
360\r
361 for (len = assets.length, i = 0; i < len; i++) {\r
362 asset = assets[i];\r
363 include = Microloader.filterAsset(asset);\r
364 if (include) {\r
365 // Asset is using the localStorage caching system\r
366 if (manifest.shouldCache() && asset.shouldCache()) {\r
367 // Asset already has content from localStorage, instantly seed that into boot\r
368 if (asset.content) {\r
369 //<debug>\r
370 _debug("Asset: " + asset.assetConfig.path + " was found in local storage. No remote load for this file");\r
371 //</debug>\r
372 entry = Boot.registerContent(asset.assetConfig.path, asset.type, asset.content);\r
373 if (entry.evaluated) {\r
374 _warn("Asset: " + asset.assetConfig.path + " was evaluated prior to local storage being consulted.");\r
375 }\r
376 //load via AJAX and seed content into Boot\r
377 } else {\r
378 //<debug>\r
379 _debug("Asset: " + asset.assetConfig.path + " was NOT found in local storage. Adding to load queue");\r
380 //</debug>\r
381 cachedAssets.push(asset);\r
382 }\r
383 }\r
384 Microloader.urls.push(asset.assetConfig.path);\r
385 }\r
386 }\r
387\r
388 // If any assets are using the caching system and do not have local versions load them first via AJAX\r
389 if (cachedAssets.length > 0) {\r
390 Microloader.remainingCachedAssets = cachedAssets.length;\r
391 while (cachedAssets.length > 0) {\r
392 asset = cachedAssets.pop();\r
393 //<debug>\r
394 _debug("Preloading/Fetching Cached Assets from: " + asset.assetConfig.path);\r
395 //</debug>\r
396 Boot.fetch(asset.assetConfig.path, (function(asset) {\r
397 return function(result) {\r
398 Microloader.onCachedAssetLoaded(asset, result);\r
399 }\r
400 })(asset));\r
401 }\r
402 } else {\r
403 Microloader.onCachedAssetsReady();\r
404 }\r
405 },\r
406\r
407 // Load the asset and seed its content into Boot to be evaluated in sequence\r
408 onCachedAssetLoaded: function (asset, result) {\r
409 var checksum;\r
410 result = Microloader.parseResult(result);\r
411 Microloader.remainingCachedAssets--;\r
412\r
413 if (!result.error) {\r
414 checksum = Microloader.checksum(result.content, asset.assetConfig.hash);\r
415 if (!checksum) {\r
416 _warn("Cached Asset '" + asset.assetConfig.path + "' has failed checksum. This asset will be uncached for future loading");\r
417\r
418 // Un cache this asset so it is loaded next time\r
419 asset.uncache();\r
420 }\r
421\r
422 //<debug>\r
423 _debug("Checksum for Cached Asset: " + asset.assetConfig.path + " is " + checksum);\r
424 //</debug>\r
425 Boot.registerContent(asset.assetConfig.path, asset.type, result.content);\r
426 asset.updateContent(result.content);\r
427 asset.cache();\r
428 } else {\r
429 _warn("There was an error pre-loading the asset '" + asset.assetConfig.path + "'. This asset will be uncached for future loading");\r
430\r
431 // Un cache this asset so it is loaded next time\r
432 asset.uncache();\r
433 }\r
434\r
435 if (Microloader.remainingCachedAssets === 0) {\r
436 Microloader.onCachedAssetsReady();\r
437 }\r
438 },\r
439\r
440 onCachedAssetsReady: function(){\r
441 Boot.load({\r
442 url: Microloader.urls,\r
443 loadOrder: Microloader.manifest.loadOrder,\r
444 loadOrderMap: Microloader.manifest.loadOrderMap,\r
445 sequential: true,\r
446 success: Microloader.onAllAssetsReady,\r
447 failure: Microloader.onAllAssetsReady\r
448 });\r
449 },\r
450\r
451 onAllAssetsReady: function() {\r
452 _loaded = true;\r
453 Microloader.notify();\r
454\r
455 if (navigator.onLine !== false) {\r
456 //<debug>\r
457 _debug("Application is online, checking for updates");\r
458 //</debug>\r
459 Microloader.checkAllUpdates();\r
460 }\r
461 else {\r
462 //<debug>\r
463 _debug("Application is offline, adding online listener to check for updates");\r
464 //</debug>\r
465 if(window['addEventListener']) {\r
466 window.addEventListener('online', Microloader.checkAllUpdates, false);\r
467 }\r
468 }\r
469 },\r
470\r
471 onMicroloaderReady: function (listener) {\r
472 if (_loaded) {\r
473 listener();\r
474 } else {\r
475 _listeners.push(listener);\r
476 }\r
477 },\r
478\r
479 /**\r
480 * @private\r
481 */\r
482 notify: function () {\r
483 //<debug>\r
484 _debug("notifying microloader ready listeners.");\r
485 //</debug>\r
486 var listener;\r
487 while((listener = _listeners.shift())) {\r
488 listener();\r
489 }\r
490 },\r
491\r
492 // Delta patches content\r
493 patch: function (content, delta) {\r
494 var output = [],\r
495 chunk, i, ln;\r
496\r
497 if (delta.length === 0) {\r
498 return content;\r
499 }\r
500\r
501 for (i = 0,ln = delta.length; i < ln; i++) {\r
502 chunk = delta[i];\r
503\r
504 if (typeof chunk === 'number') {\r
505 output.push(content.substring(chunk, chunk + delta[++i]));\r
506 }\r
507 else {\r
508 output.push(chunk);\r
509 }\r
510 }\r
511\r
512 return output.join('');\r
513 },\r
514\r
515 checkAllUpdates: function() {\r
516 //<debug>\r
517 _debug("Checking for All Updates");\r
518 //</debug>\r
519 if(window['removeEventListener']) {\r
520 window.removeEventListener('online', Microloader.checkAllUpdates, false);\r
521 }\r
522\r
523 if(_cache) {\r
524 Microloader.checkForAppCacheUpdate();\r
525 }\r
526\r
527 // Manifest came from a cached instance, check for updates\r
528 if (Microloader.manifest.fromCache) {\r
529 Microloader.checkForUpdates();\r
530 }\r
531 },\r
532\r
533 checkForAppCacheUpdate: function() {\r
534 //<debug>\r
535 _debug("Checking App Cache status");\r
536 //</debug>\r
537 if (_cache.status === _cache.UPDATEREADY || _cache.status === _cache.OBSOLETE) {\r
538 //<debug>\r
539 _debug("App Cache is already in an updated");\r
540 //</debug>\r
541 Microloader.appCacheState = 'updated';\r
542 } else if (_cache.status !== _cache.IDLE && _cache.status !== _cache.UNCACHED) {\r
543 //<debug>\r
544 _debug("App Cache is checking or downloading updates, adding listeners");\r
545 //</debug>\r
546 Microloader.appCacheState = 'checking';\r
547 _cache.addEventListener('error', Microloader.onAppCacheError);\r
548 _cache.addEventListener('noupdate', Microloader.onAppCacheNotUpdated);\r
549 _cache.addEventListener('cached', Microloader.onAppCacheNotUpdated);\r
550 _cache.addEventListener('updateready', Microloader.onAppCacheReady);\r
551 _cache.addEventListener('obsolete', Microloader.onAppCacheObsolete);\r
552 } else {\r
553 //<debug>\r
554 _debug("App Cache is current or uncached");\r
555 //</debug>\r
556 Microloader.appCacheState = 'current';\r
557 }\r
558 },\r
559\r
560 checkForUpdates: function() {\r
561 // Fetch the Latest Manifest from the server\r
562 //<debug>\r
563 _debug("Checking for updates at: " + Microloader.manifest.url);\r
564 //</debug>\r
565 Boot.fetch(Microloader.manifest.url, Microloader.onUpdatedManifestLoaded);\r
566 },\r
567\r
568 onAppCacheError: function(e) {\r
569 _warn(e.message);\r
570\r
571 Microloader.appCacheState = 'error';\r
572 Microloader.notifyUpdateReady();\r
573 },\r
574\r
575 onAppCacheReady: function() {\r
576 _cache.swapCache();\r
577 Microloader.appCacheUpdated();\r
578 },\r
579\r
580 onAppCacheObsolete: function() {\r
581 Microloader.appCacheUpdated();\r
582 },\r
583\r
584 appCacheUpdated: function() {\r
585 //<debug>\r
586 _debug("App Cache Updated");\r
587 //</debug>\r
588 Microloader.appCacheState = 'updated';\r
589 Microloader.notifyUpdateReady();\r
590 },\r
591\r
592 onAppCacheNotUpdated: function() {\r
593 //<debug>\r
594 _debug("App Cache Not Updated Callback");\r
595 //</debug>\r
596 Microloader.appCacheState = 'current';\r
597 Microloader.notifyUpdateReady();\r
598 },\r
599\r
600\r
601 filterAsset: function(asset) {\r
602 var cfg = (asset && asset.assetConfig) || {};\r
603 if(cfg.platform || cfg.exclude) {\r
604 return Boot.filterPlatform(cfg.platform, cfg.exclude);\r
605 }\r
606 return true;\r
607 },\r
608\r
609 onUpdatedManifestLoaded: function (result) {\r
610 result = Microloader.parseResult(result);\r
611\r
612 if (!result.error) {\r
613 var currentAssets, newAssets, currentAsset, newAsset, prop,\r
614 assets, deltas, deltaPath, include,\r
615 updatingAssets = [],\r
616 manifest = new Manifest({\r
617 url: Microloader.manifest.url,\r
618 content: result.content,\r
619 assetCache: false\r
620 });\r
621\r
622 Microloader.remainingUpdatingAssets = 0;\r
623 Microloader.updatedAssets = [];\r
624 Microloader.removedAssets = [];\r
625 Microloader.updatedManifest = null;\r
626 Microloader.updatedAssetsReady = false;\r
627\r
628 // If the updated manifest has turned off caching we need to clear out all local storage\r
629 // and trigger a appupdate as all content is now uncached\r
630 if (!manifest.shouldCache()) {\r
631 //<debug>\r
632 _debug("New Manifest has caching disabled, clearing out any private storage");\r
633 //</debug>\r
634\r
635 Microloader.updatedManifest = manifest;\r
636 LocalStorage.clearAllPrivate(manifest);\r
637 Microloader.onAllUpdatedAssetsReady();\r
638 return;\r
639 }\r
640\r
641 // Manifest itself has changed\r
642 if (!Microloader.manifest.is(manifest)) {\r
643 Microloader.updatedManifest = manifest;\r
644\r
645 currentAssets = Microloader.manifest.getAssets();\r
646 newAssets = manifest.getAssets();\r
647\r
648 // Look through new assets for assets that do not exist or assets that have different versions\r
649 for (prop in newAssets) {\r
650 newAsset = newAssets[prop];\r
651 currentAsset = Microloader.manifest.getAsset(newAsset.assetConfig.path);\r
652 include = Microloader.filterAsset(newAsset);\r
653\r
654 if (include && (!currentAsset || (newAsset.shouldCache() && (!currentAsset.is(newAsset))))) {\r
655 //<debug>\r
656 _debug("New/Updated Version of Asset: " + newAsset.assetConfig.path + " was found in new manifest");\r
657 //</debug>\r
658 updatingAssets.push({_new: newAsset, _current: currentAsset});\r
659 }\r
660 }\r
661\r
662 // Look through current assets for stale/old assets that have been removed\r
663 for (prop in currentAssets) {\r
664 currentAsset = currentAssets[prop];\r
665 newAsset = manifest.getAsset(currentAsset.assetConfig.path);\r
666\r
667 //New version of this asset has been filtered out\r
668 include = !Microloader.filterAsset(newAsset);\r
669\r
670 if (!include || !newAsset || (currentAsset.shouldCache() && !newAsset.shouldCache())) {\r
671 //<debug>\r
672 _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
673 //</debug>\r
674 Microloader.removedAssets.push(currentAsset);\r
675 }\r
676 }\r
677\r
678 // Loop through all assets that need updating\r
679 if (updatingAssets.length > 0) {\r
680 Microloader.remainingUpdatingAssets = updatingAssets.length;\r
681 while (updatingAssets.length > 0) {\r
682 assets = updatingAssets.pop();\r
683 newAsset = assets._new;\r
684 currentAsset = assets._current;\r
685\r
686 // Full Updates will simply download the file and replace its current content\r
687 if (newAsset.assetConfig.update === "full" || !currentAsset) {\r
688\r
689 //<debug>\r
690 if (newAsset.assetConfig.update === "delta") {\r
691 _debug("Delta updated asset found without current asset available: " + newAsset.assetConfig.path + " fetching full file");\r
692 } else {\r
693 _debug("Full update found for: " + newAsset.assetConfig.path + " fetching");\r
694 }\r
695 //</debug>\r
696\r
697 // Load the asset and cache its its content into Boot to be evaluated in sequence\r
698 Boot.fetch(newAsset.assetConfig.path, (function (asset) {\r
699 return function (result) {\r
700 Microloader.onFullAssetUpdateLoaded(asset, result)\r
701 };\r
702 }(newAsset))\r
703 );\r
704\r
705 // Delta updates will be given a delta patch\r
706 } else if (newAsset.assetConfig.update === "delta") {\r
707 deltas = manifest.deltas;\r
708 deltaPath = deltas + "/" + newAsset.assetConfig.path + "/" + currentAsset.assetConfig.hash + ".json";\r
709 // Fetch the Delta Patch and update the contents of the asset\r
710 //<debug>\r
711 _debug("Delta update found for: " + newAsset.assetConfig.path + " fetching");\r
712 //</debug>\r
713 Boot.fetch(deltaPath,\r
714 (function (asset, oldAsset) {\r
715 return function (result) {\r
716 Microloader.onDeltaAssetUpdateLoaded(asset, oldAsset, result)\r
717 };\r
718 }(newAsset, currentAsset))\r
719 );\r
720 }\r
721 }\r
722 } else {\r
723 //<debug>\r
724 _debug("No Assets needed updating");\r
725 //</debug>\r
726 Microloader.onAllUpdatedAssetsReady();\r
727 }\r
728 } else {\r
729 //<debug>\r
730 _debug("Manifest files have matching hash's");\r
731 //</debug>\r
732 Microloader.onAllUpdatedAssetsReady();\r
733 }\r
734 } else {\r
735 _warn("Error loading manifest file to check for updates");\r
736 Microloader.onAllUpdatedAssetsReady();\r
737 }\r
738 },\r
739\r
740 onFullAssetUpdateLoaded: function(asset, result) {\r
741 var checksum;\r
742 result = Microloader.parseResult(result);\r
743 Microloader.remainingUpdatingAssets--;\r
744\r
745 if (!result.error) {\r
746 checksum = Microloader.checksum(result.content, asset.assetConfig.hash);\r
747 //<debug>\r
748 _debug("Checksum for Full asset: " + asset.assetConfig.path + " is " + checksum);\r
749 //</debug>\r
750 if (!checksum) {\r
751 //<debug>\r
752 _debug("Full Update Asset: " + asset.assetConfig.path + " has failed checksum. This asset will be uncached for future loading");\r
753 //</debug>\r
754\r
755 // uncache this asset as there is a new version somewhere that has not been loaded.\r
756 asset.uncache();\r
757 } else {\r
758 asset.updateContent(result.content);\r
759 Microloader.updatedAssets.push(asset);\r
760 }\r
761 } else {\r
762 //<debug>\r
763 _debug("Error loading file at" + asset.assetConfig.path + ". This asset will be uncached for future loading");\r
764 //</debug>\r
765\r
766 // uncache this asset as there is a new version somewhere that has not been loaded.\r
767 asset.uncache();\r
768 }\r
769\r
770 if (Microloader.remainingUpdatingAssets === 0) {\r
771 Microloader.onAllUpdatedAssetsReady();\r
772 }\r
773 },\r
774\r
775 onDeltaAssetUpdateLoaded: function(asset, oldAsset, result) {\r
776 var json, checksum, content;\r
777 result = Microloader.parseResult(result);\r
778 Microloader.remainingUpdatingAssets--;\r
779\r
780 if (!result.error) {\r
781 //<debug>\r
782 _debug("Delta patch loaded successfully, patching content");\r
783 //</debug>\r
784 try {\r
785 json = JSON.parse(result.content);\r
786 content = Microloader.patch(oldAsset.content, json);\r
787 checksum = Microloader.checksum(content, asset.assetConfig.hash);\r
788 //<debug>\r
789 _debug("Checksum for Delta Patched asset: " + asset.assetConfig.path + " is " + checksum);\r
790 //</debug>\r
791 if (!checksum) {\r
792 //<debug>\r
793 _debug("Delta Update Asset: " + asset.assetConfig.path + " has failed checksum. This asset will be uncached for future loading");\r
794 //</debug>\r
795\r
796 // uncache this asset as there is a new version somewhere that has not been loaded.\r
797 asset.uncache();\r
798 } else {\r
799 asset.updateContent(content);\r
800 Microloader.updatedAssets.push(asset);\r
801 }\r
802 } catch (e) {\r
803 _warn("Error parsing delta patch for " + asset.assetConfig.path + " with hash " + oldAsset.assetConfig.hash + " . This asset will be uncached for future loading");\r
804 // uncache this asset as there is a new version somewhere that has not been loaded.\r
805 asset.uncache();\r
806 }\r
807 } else {\r
808 _warn("Error loading delta patch for " + asset.assetConfig.path + " with hash " + oldAsset.assetConfig.hash + " . This asset will be uncached for future loading");\r
809\r
810 // uncache this asset as there is a new version somewhere that has not been loaded.\r
811 asset.uncache();\r
812 }\r
813 if (Microloader.remainingUpdatingAssets === 0) {\r
814 Microloader.onAllUpdatedAssetsReady();\r
815 }\r
816 },\r
817\r
818 //TODO: Make this all transaction based to allow for reverting if quota is exceeded\r
819 onAllUpdatedAssetsReady: function() {\r
820 var asset;\r
821 Microloader.updatedAssetsReady = true;\r
822\r
823 if (Microloader.updatedManifest) {\r
824 while (Microloader.removedAssets.length > 0) {\r
825 asset = Microloader.removedAssets.pop();\r
826 //<debug>\r
827 _debug("Asset: " + asset.assetConfig.path + " was removed, un-caching");\r
828 //</debug>\r
829 asset.uncache();\r
830 }\r
831\r
832 if (Microloader.updatedManifest) {\r
833 //<debug>\r
834 _debug("Manifest was updated, re-caching");\r
835 //</debug>\r
836 Microloader.updatedManifest.cache();\r
837 }\r
838\r
839 while (Microloader.updatedAssets.length > 0) {\r
840 asset = Microloader.updatedAssets.pop();\r
841 //<debug>\r
842 _debug("Asset: " + asset.assetConfig.path + " was updated, re-caching");\r
843 //</debug>\r
844 asset.cache();\r
845 }\r
846\r
847 }\r
848\r
849 Microloader.notifyUpdateReady();\r
850 },\r
851\r
852 notifyUpdateReady: function () {\r
853 if (Microloader.appCacheState !== 'checking' && Microloader.updatedAssetsReady) {\r
854 if (Microloader.appCacheState === 'updated' || Microloader.updatedManifest) {\r
855 //<debug>\r
856 _debug("There was an update here you will want to reload the app, trigger an event");\r
857 //</debug>\r
858 Microloader.appUpdate = {\r
859 updated: true,\r
860 app: Microloader.appCacheState === 'updated',\r
861 manifest: Microloader.updatedManifest && Microloader.updatedManifest.exportContent()\r
862 };\r
863\r
864 Microloader.fireAppUpdate();\r
865 }\r
866 //<debug>\r
867 else {\r
868 _debug("AppCache and LocalStorage Cache are current, no updating needed");\r
869 Microloader.appUpdate = {};\r
870 }\r
871 //</debug>\r
872 }\r
873 },\r
874\r
875 fireAppUpdate: function() {\r
876 if (Ext.GlobalEvents) {\r
877 // We defer dispatching this event slightly in order to let the application finish loading\r
878 // as we are still very early in the lifecycle\r
879 Ext.defer(function() {\r
880 Ext.GlobalEvents.fireEvent('appupdate', Microloader.appUpdate);\r
881 }, 100);\r
882 }\r
883 },\r
884\r
885 checksum: function(content, hash) {\r
886 if(!content || !hash) {\r
887 return false;\r
888 }\r
889\r
890 var passed = true,\r
891 hashLn = hash.length,\r
892 checksumType = content.substring(0, 1);\r
893\r
894 if (checksumType == '/') {\r
895 if (content.substring(2, hashLn + 2) !== hash) {\r
896 passed = false;\r
897 }\r
898 } else if (checksumType == 'f') {\r
899 if (content.substring(10, hashLn + 10) !== hash) {\r
900 passed = false;\r
901 }\r
902 } else if (checksumType == '.') {\r
903 if (content.substring(1, hashLn + 1) !== hash) {\r
904 passed = false;\r
905 }\r
906 }\r
907 return passed;\r
908 },\r
909 parseResult: function(result) {\r
910 var rst = {};\r
911 if ((result.exception || result.status === 0) && !Boot.env.phantom) {\r
912 rst.error = true;\r
913 } else if ((result.status >= 200 && result.status < 300) || result.status === 304\r
914 || Boot.env.phantom\r
915 || (result.status === 0 && result.content.length > 0)\r
916 ) {\r
917 rst.content = result.content;\r
918 } else {\r
919 rst.error = true;\r
920 }\r
921 return rst;\r
922 }\r
923 };\r
924\r
925 return Microloader;\r
926}());\r
927\r
928/**\r
929 * @type {String/Object}\r
930 */\r
931Ext.manifest = Ext.manifest || "bootstrap";\r
932\r
933Ext.Microloader.run();