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