windowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacoslinux
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
959 lines
39 KiB
959 lines
39 KiB
// here, the extra check for window['Ext'] is needed for use with cmd-test |
|
// code injection. we need to make that this file will sync up with page global |
|
// scope to avoid duplicate Ext.Boot state. That check is after the initial Ext check |
|
// to allow the sandboxing template to inject an appropriate Ext var and prevent the |
|
// global detection. |
|
var Ext = Ext || window['Ext'] || {}; |
|
|
|
|
|
//<editor-fold desc="Microloader"> |
|
/** |
|
* @class Ext.Microloader |
|
* @private |
|
* @singleton |
|
*/ |
|
Ext.Microloader = Ext.Microloader || (function () { |
|
var Boot = Ext.Boot, |
|
//<debug> |
|
_debug = function (message) { |
|
//console.log(message); |
|
}, |
|
//</debug> |
|
_warn = function (message) { |
|
console.log("[WARN] " + message); |
|
}, |
|
_privatePrefix = '_ext:' + location.pathname, |
|
|
|
/** |
|
* @method getStorageKey |
|
* The Following combination is used to create isolated local storage keys |
|
* '_ext' is used to scope all the local storage keys that we internally by Ext |
|
* 'location.pathname' is used to force each assets to cache by an absolute URL (/build/MyApp) (dev vs prod) |
|
* 'url' is used to force each asset to cache relative to the page (app.json vs resources/app.css) |
|
* 'profileId' is used to differentiate the builds of an application (neptune vs crisp) |
|
* 'Microloader.appId' is unique to the application and will differentiate apps on the same host (dev mode running app watch against multiple apps) |
|
*/ |
|
getStorageKey = function(url, profileId) { |
|
return _privatePrefix + url + '-' + (profileId ? profileId + '-' : '') + Microloader.appId; |
|
}, |
|
postProcessor, _storage; |
|
|
|
try { |
|
_storage = window['localStorage']; |
|
} catch(ex) { |
|
// ignore |
|
} |
|
|
|
var _cache = window['applicationCache'], |
|
// Local Storage Controller |
|
LocalStorage = { |
|
clearAllPrivate: function(manifest) { |
|
if(_storage) { |
|
|
|
//Remove the entry for the manifest first |
|
_storage.removeItem(manifest.key); |
|
|
|
var i, key, |
|
removeKeys = [], |
|
suffix = manifest.profile + '-' + Microloader.appId, |
|
ln = _storage.length; |
|
for (i = 0; i < ln; i++) { |
|
key = _storage.key(i); |
|
// If key starts with the private key and the suffix is present we can clear this entry |
|
if (key.indexOf(_privatePrefix) === 0 && key.indexOf(suffix) !== -1) { |
|
removeKeys.push(key); |
|
} |
|
} |
|
|
|
for(i in removeKeys) { |
|
//<debug> |
|
_debug("Removing "+ removeKeys[i] + " from Local Storage"); |
|
//</debug> |
|
_storage.removeItem(removeKeys[i]); |
|
} |
|
} |
|
}, |
|
/** |
|
* @private |
|
*/ |
|
retrieveAsset: function (key) { |
|
try { |
|
return _storage.getItem(key); |
|
} |
|
catch (e) { |
|
// Private browsing mode |
|
return null; |
|
} |
|
}, |
|
|
|
setAsset: function(key, content) { |
|
try { |
|
if (content === null || content == '') { |
|
_storage.removeItem(key); |
|
} else { |
|
_storage.setItem(key, content); |
|
} |
|
} |
|
catch (e) { |
|
if (_storage && e.code == e.QUOTA_EXCEEDED_ERR) { |
|
//<debug> |
|
_warn("LocalStorage Quota exceeded, cannot store " + key + " locally"); |
|
//</debug> |
|
} |
|
} |
|
} |
|
}; |
|
|
|
var Asset = function (cfg) { |
|
if (typeof cfg.assetConfig === 'string') { |
|
this.assetConfig = { |
|
path: cfg.assetConfig |
|
}; |
|
} else { |
|
this.assetConfig = cfg.assetConfig; |
|
} |
|
|
|
this.type = cfg.type; |
|
this.key = getStorageKey(this.assetConfig.path, cfg.manifest.profile); |
|
|
|
if (cfg.loadFromCache) { |
|
this.loadFromCache(); |
|
} |
|
}; |
|
|
|
Asset.prototype = { |
|
shouldCache: function() { |
|
return _storage && this.assetConfig.update && this.assetConfig.hash && !this.assetConfig.remote; |
|
}, |
|
|
|
is: function (asset) { |
|
return (!!asset && this.assetConfig && asset.assetConfig && (this.assetConfig.hash === asset.assetConfig.hash)) |
|
}, |
|
|
|
cache: function(content) { |
|
if (this.shouldCache()) { |
|
LocalStorage.setAsset(this.key, content || this.content); |
|
} |
|
}, |
|
|
|
uncache: function() { |
|
LocalStorage.setAsset(this.key, null); |
|
}, |
|
|
|
updateContent: function (content) { |
|
this.content = content; |
|
}, |
|
|
|
getSize: function () { |
|
return this.content ? this.content.length : 0; |
|
}, |
|
|
|
loadFromCache: function() { |
|
if (this.shouldCache()) { |
|
this.content = LocalStorage.retrieveAsset(this.key); |
|
} |
|
} |
|
}; |
|
|
|
var Manifest = function (cfg) { |
|
if (typeof cfg.content === "string") { |
|
this.content = JSON.parse(cfg.content); |
|
} else { |
|
this.content = cfg.content; |
|
} |
|
this.assetMap = {}; |
|
|
|
this.url = cfg.url; |
|
this.fromCache = !!cfg.cached; |
|
this.assetCache = !(cfg.assetCache === false); |
|
this.key = getStorageKey(this.url); |
|
|
|
// Pull out select properties for repetitive use |
|
this.profile = this.content.profile; |
|
this.hash = this.content.hash; |
|
this.loadOrder = this.content.loadOrder; |
|
this.deltas = this.content.cache ? this.content.cache.deltas : null; |
|
this.cacheEnabled = this.content.cache ? this.content.cache.enable : false; |
|
|
|
this.loadOrderMap = (this.loadOrder) ? Boot.createLoadOrderMap(this.loadOrder) : null; |
|
|
|
var tags = this.content.tags, |
|
platformTags = Ext.platformTags; |
|
|
|
if (tags) { |
|
if (tags instanceof Array) { |
|
for (var i = 0; i < tags.length; i++) { |
|
platformTags[tags[i]] = true; |
|
} |
|
} else { |
|
Boot.apply(platformTags, tags); |
|
} |
|
|
|
// re-apply the query parameters, so that the params as specified |
|
// in the url always has highest priority |
|
Boot.apply(platformTags, Boot.loadPlatformsParam()); |
|
} |
|
|
|
// Convert all assets into Assets |
|
this.js = this.processAssets(this.content.js, 'js'); |
|
this.css = this.processAssets(this.content.css, 'css'); |
|
}; |
|
|
|
Manifest.prototype = { |
|
processAsset: function(assetConfig, type) { |
|
var processedAsset = new Asset({ |
|
manifest: this, |
|
assetConfig: assetConfig, |
|
type: type, |
|
loadFromCache: this.assetCache |
|
}); |
|
this.assetMap[assetConfig.path] = processedAsset; |
|
return processedAsset; |
|
}, |
|
|
|
processAssets: function(assets, type) { |
|
var results = [], |
|
ln = assets.length, |
|
i, assetConfig; |
|
|
|
for (i = 0; i < ln; i++) { |
|
assetConfig = assets[i]; |
|
results.push(this.processAsset(assetConfig, type)); |
|
} |
|
|
|
return results; |
|
}, |
|
|
|
useAppCache: function() { |
|
return true; |
|
}, |
|
|
|
// Concatenate all assets for easy access |
|
getAssets: function () { |
|
return this.css.concat(this.js); |
|
}, |
|
|
|
getAsset: function (path) { |
|
return this.assetMap[path]; |
|
}, |
|
|
|
shouldCache: function() { |
|
return this.hash && this.cacheEnabled; |
|
}, |
|
|
|
cache: function(content) { |
|
if (this.shouldCache()) { |
|
LocalStorage.setAsset(this.key, JSON.stringify(content || this.content)); |
|
} |
|
//<debug> |
|
else { |
|
_debug("Manifest caching is disabled."); |
|
} |
|
//</debug> |
|
}, |
|
|
|
is: function(manifest) { |
|
//<debug> |
|
_debug("Testing Manifest: " + this.hash + " VS " + manifest.hash); |
|
//</debug> |
|
return this.hash === manifest.hash; |
|
}, |
|
|
|
// Clear the manifest from local storage |
|
uncache: function() { |
|
LocalStorage.setAsset(this.key, null); |
|
}, |
|
|
|
exportContent: function() { |
|
return Boot.apply({ |
|
loadOrderMap: this.loadOrderMap |
|
}, this.content); |
|
} |
|
}; |
|
|
|
/** |
|
* Microloader |
|
* @type {Array} |
|
* @private |
|
*/ |
|
var _listeners = [], |
|
_loaded = false, |
|
Microloader = { |
|
init: function () { |
|
Ext.microloaded = true; |
|
|
|
// data-app is in the dev template for an application and is also |
|
// injected into the app my CMD for production |
|
// We use this to prefix localStorage cache to prevent collisions |
|
var microloaderElement = document.getElementById('microloader'); |
|
Microloader.appId = microloaderElement ? microloaderElement.getAttribute('data-app') : ''; |
|
|
|
if (Ext.beforeLoad) { |
|
postProcessor = Ext.beforeLoad(Ext.platformTags); |
|
} |
|
|
|
var readyHandler = Ext._beforereadyhandler; |
|
|
|
Ext._beforereadyhandler = function () { |
|
if (Ext.Boot !== Boot) { |
|
Ext.apply(Ext.Boot, Boot); |
|
Ext.Boot = Boot; |
|
} |
|
if (readyHandler) { |
|
readyHandler(); |
|
} |
|
}; |
|
}, |
|
|
|
applyCacheBuster: function(url) { |
|
var tstamp = new Date().getTime(), |
|
sep = url.indexOf('?') === -1 ? '?' : '&'; |
|
url = url + sep + "_dc=" + tstamp; |
|
return url; |
|
}, |
|
|
|
run: function() { |
|
Microloader.init(); |
|
var manifest = Ext.manifest; |
|
|
|
if (typeof manifest === "string") { |
|
var extension = ".json", |
|
url = manifest.indexOf(extension) === manifest.length - extension.length |
|
? manifest |
|
: manifest + ".json", |
|
key = getStorageKey(url), |
|
content = LocalStorage.retrieveAsset(key); |
|
|
|
// Manifest found in local storage, use this for immediate boot except in PhantomJS environments for building. |
|
if (content) { |
|
//<debug> |
|
_debug("Manifest file, '" + url + "', was found in Local Storage"); |
|
//</debug> |
|
manifest = new Manifest({ |
|
url: url, |
|
content: content, |
|
cached: true |
|
}); |
|
if (postProcessor) { |
|
postProcessor(manifest); |
|
} |
|
Microloader.load(manifest); |
|
|
|
|
|
// Manifest is not in local storage. Fetch it from the server |
|
} else { |
|
Boot.fetch(Microloader.applyCacheBuster(url), function (result) { |
|
//<debug> |
|
_debug("Manifest file was not found in Local Storage, loading: " + url); |
|
//</debug> |
|
manifest = new Manifest({ |
|
url: url, |
|
content: result.content |
|
}); |
|
|
|
manifest.cache(); |
|
if (postProcessor) { |
|
postProcessor(manifest); |
|
} |
|
Microloader.load(manifest); |
|
}); |
|
} |
|
|
|
// Embedded Manifest into JS file |
|
} else { |
|
//<debug> |
|
_debug("Manifest was embedded into application javascript file"); |
|
//</debug> |
|
manifest = new Manifest({ |
|
content: manifest |
|
}); |
|
Microloader.load(manifest); |
|
} |
|
}, |
|
|
|
/** |
|
* @param {Manifest} manifest |
|
*/ |
|
load: function (manifest) { |
|
Microloader.urls = []; |
|
Microloader.manifest = manifest; |
|
Ext.manifest = Microloader.manifest.exportContent(); |
|
|
|
var assets = manifest.getAssets(), |
|
cachedAssets = [], |
|
asset, i, len, include, entry; |
|
|
|
for (len = assets.length, i = 0; i < len; i++) { |
|
asset = assets[i]; |
|
include = Microloader.filterAsset(asset); |
|
if (include) { |
|
// Asset is using the localStorage caching system |
|
if (manifest.shouldCache() && asset.shouldCache()) { |
|
// Asset already has content from localStorage, instantly seed that into boot |
|
if (asset.content) { |
|
//<debug> |
|
_debug("Asset: " + asset.assetConfig.path + " was found in local storage. No remote load for this file"); |
|
//</debug> |
|
entry = Boot.registerContent(asset.assetConfig.path, asset.type, asset.content); |
|
if (entry.evaluated) { |
|
_warn("Asset: " + asset.assetConfig.path + " was evaluated prior to local storage being consulted."); |
|
} |
|
//load via AJAX and seed content into Boot |
|
} else { |
|
//<debug> |
|
_debug("Asset: " + asset.assetConfig.path + " was NOT found in local storage. Adding to load queue"); |
|
//</debug> |
|
cachedAssets.push(asset); |
|
} |
|
} |
|
Microloader.urls.push(asset.assetConfig.path); |
|
Boot.assetConfig[asset.assetConfig.path] = Boot.apply({type: asset.type}, asset.assetConfig); |
|
} |
|
} |
|
|
|
// If any assets are using the caching system and do not have local versions load them first via AJAX |
|
if (cachedAssets.length > 0) { |
|
Microloader.remainingCachedAssets = cachedAssets.length; |
|
while (cachedAssets.length > 0) { |
|
asset = cachedAssets.pop(); |
|
//<debug> |
|
_debug("Preloading/Fetching Cached Assets from: " + asset.assetConfig.path); |
|
//</debug> |
|
Boot.fetch(asset.assetConfig.path, (function(asset) { |
|
return function(result) { |
|
Microloader.onCachedAssetLoaded(asset, result); |
|
} |
|
})(asset)); |
|
} |
|
} else { |
|
Microloader.onCachedAssetsReady(); |
|
} |
|
}, |
|
|
|
// Load the asset and seed its content into Boot to be evaluated in sequence |
|
onCachedAssetLoaded: function (asset, result) { |
|
var checksum; |
|
result = Microloader.parseResult(result); |
|
Microloader.remainingCachedAssets--; |
|
|
|
if (!result.error) { |
|
checksum = Microloader.checksum(result.content, asset.assetConfig.hash); |
|
if (!checksum) { |
|
_warn("Cached Asset '" + asset.assetConfig.path + "' has failed checksum. This asset will be uncached for future loading"); |
|
|
|
// Un cache this asset so it is loaded next time |
|
asset.uncache(); |
|
} |
|
|
|
//<debug> |
|
_debug("Checksum for Cached Asset: " + asset.assetConfig.path + " is " + checksum); |
|
//</debug> |
|
Boot.registerContent(asset.assetConfig.path, asset.type, result.content); |
|
asset.updateContent(result.content); |
|
asset.cache(); |
|
} else { |
|
_warn("There was an error pre-loading the asset '" + asset.assetConfig.path + "'. This asset will be uncached for future loading"); |
|
|
|
// Un cache this asset so it is loaded next time |
|
asset.uncache(); |
|
} |
|
|
|
if (Microloader.remainingCachedAssets === 0) { |
|
Microloader.onCachedAssetsReady(); |
|
} |
|
}, |
|
|
|
onCachedAssetsReady: function(){ |
|
Boot.load({ |
|
url: Microloader.urls, |
|
loadOrder: Microloader.manifest.loadOrder, |
|
loadOrderMap: Microloader.manifest.loadOrderMap, |
|
sequential: true, |
|
success: Microloader.onAllAssetsReady, |
|
failure: Microloader.onAllAssetsReady |
|
}); |
|
}, |
|
|
|
onAllAssetsReady: function() { |
|
_loaded = true; |
|
Microloader.notify(); |
|
|
|
if (navigator.onLine !== false) { |
|
//<debug> |
|
_debug("Application is online, checking for updates"); |
|
//</debug> |
|
Microloader.checkAllUpdates(); |
|
} |
|
else { |
|
//<debug> |
|
_debug("Application is offline, adding online listener to check for updates"); |
|
//</debug> |
|
if(window['addEventListener']) { |
|
window.addEventListener('online', Microloader.checkAllUpdates, false); |
|
} |
|
} |
|
}, |
|
|
|
onMicroloaderReady: function (listener) { |
|
if (_loaded) { |
|
listener(); |
|
} else { |
|
_listeners.push(listener); |
|
} |
|
}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
notify: function () { |
|
//<debug> |
|
_debug("notifying microloader ready listeners."); |
|
//</debug> |
|
var listener; |
|
while((listener = _listeners.shift())) { |
|
listener(); |
|
} |
|
}, |
|
|
|
// Delta patches content |
|
patch: function (content, delta) { |
|
var output = [], |
|
chunk, i, ln; |
|
|
|
if (delta.length === 0) { |
|
return content; |
|
} |
|
|
|
for (i = 0,ln = delta.length; i < ln; i++) { |
|
chunk = delta[i]; |
|
|
|
if (typeof chunk === 'number') { |
|
output.push(content.substring(chunk, chunk + delta[++i])); |
|
} |
|
else { |
|
output.push(chunk); |
|
} |
|
} |
|
|
|
return output.join(''); |
|
}, |
|
|
|
checkAllUpdates: function() { |
|
//<debug> |
|
_debug("Checking for All Updates"); |
|
//</debug> |
|
if(window['removeEventListener']) { |
|
window.removeEventListener('online', Microloader.checkAllUpdates, false); |
|
} |
|
|
|
if(_cache) { |
|
Microloader.checkForAppCacheUpdate(); |
|
} |
|
|
|
// Manifest came from a cached instance, check for updates |
|
if (Microloader.manifest.fromCache) { |
|
Microloader.checkForUpdates(); |
|
} |
|
}, |
|
|
|
checkForAppCacheUpdate: function() { |
|
//<debug> |
|
_debug("Checking App Cache status"); |
|
//</debug> |
|
if (_cache.status === _cache.UPDATEREADY || _cache.status === _cache.OBSOLETE) { |
|
//<debug> |
|
_debug("App Cache is already in an updated"); |
|
//</debug> |
|
Microloader.appCacheState = 'updated'; |
|
} else if (_cache.status !== _cache.IDLE && _cache.status !== _cache.UNCACHED) { |
|
//<debug> |
|
_debug("App Cache is checking or downloading updates, adding listeners"); |
|
//</debug> |
|
Microloader.appCacheState = 'checking'; |
|
_cache.addEventListener('error', Microloader.onAppCacheError); |
|
_cache.addEventListener('noupdate', Microloader.onAppCacheNotUpdated); |
|
_cache.addEventListener('cached', Microloader.onAppCacheNotUpdated); |
|
_cache.addEventListener('updateready', Microloader.onAppCacheReady); |
|
_cache.addEventListener('obsolete', Microloader.onAppCacheObsolete); |
|
} else { |
|
//<debug> |
|
_debug("App Cache is current or uncached"); |
|
//</debug> |
|
Microloader.appCacheState = 'current'; |
|
} |
|
}, |
|
|
|
checkForUpdates: function() { |
|
// Fetch the Latest Manifest from the server |
|
//<debug> |
|
_debug("Checking for updates at: " + Microloader.manifest.url); |
|
//</debug> |
|
Boot.fetch(Microloader.applyCacheBuster(Microloader.manifest.url), Microloader.onUpdatedManifestLoaded); |
|
}, |
|
|
|
onAppCacheError: function(e) { |
|
_warn(e.message); |
|
|
|
Microloader.appCacheState = 'error'; |
|
Microloader.notifyUpdateReady(); |
|
}, |
|
|
|
onAppCacheReady: function() { |
|
_cache.swapCache(); |
|
Microloader.appCacheUpdated(); |
|
}, |
|
|
|
onAppCacheObsolete: function() { |
|
Microloader.appCacheUpdated(); |
|
}, |
|
|
|
appCacheUpdated: function() { |
|
//<debug> |
|
_debug("App Cache Updated"); |
|
//</debug> |
|
Microloader.appCacheState = 'updated'; |
|
Microloader.notifyUpdateReady(); |
|
}, |
|
|
|
onAppCacheNotUpdated: function() { |
|
//<debug> |
|
_debug("App Cache Not Updated Callback"); |
|
//</debug> |
|
Microloader.appCacheState = 'current'; |
|
Microloader.notifyUpdateReady(); |
|
}, |
|
|
|
|
|
filterAsset: function(asset) { |
|
var cfg = (asset && asset.assetConfig) || {}; |
|
if(cfg.platform || cfg.exclude) { |
|
return Boot.filterPlatform(cfg.platform, cfg.exclude); |
|
} |
|
return true; |
|
}, |
|
|
|
onUpdatedManifestLoaded: function (result) { |
|
result = Microloader.parseResult(result); |
|
|
|
if (!result.error) { |
|
var currentAssets, newAssets, currentAsset, newAsset, prop, |
|
assets, deltas, deltaPath, include, |
|
updatingAssets = [], |
|
manifest = new Manifest({ |
|
url: Microloader.manifest.url, |
|
content: result.content, |
|
assetCache: false |
|
}); |
|
|
|
Microloader.remainingUpdatingAssets = 0; |
|
Microloader.updatedAssets = []; |
|
Microloader.removedAssets = []; |
|
Microloader.updatedManifest = null; |
|
Microloader.updatedAssetsReady = false; |
|
|
|
// If the updated manifest has turned off caching we need to clear out all local storage |
|
// and trigger a appupdate as all content is now uncached |
|
if (!manifest.shouldCache()) { |
|
//<debug> |
|
_debug("New Manifest has caching disabled, clearing out any private storage"); |
|
//</debug> |
|
|
|
Microloader.updatedManifest = manifest; |
|
LocalStorage.clearAllPrivate(manifest); |
|
Microloader.onAllUpdatedAssetsReady(); |
|
return; |
|
} |
|
|
|
// Manifest itself has changed |
|
if (!Microloader.manifest.is(manifest)) { |
|
Microloader.updatedManifest = manifest; |
|
|
|
currentAssets = Microloader.manifest.getAssets(); |
|
newAssets = manifest.getAssets(); |
|
|
|
// Look through new assets for assets that do not exist or assets that have different versions |
|
for (prop in newAssets) { |
|
newAsset = newAssets[prop]; |
|
currentAsset = Microloader.manifest.getAsset(newAsset.assetConfig.path); |
|
include = Microloader.filterAsset(newAsset); |
|
|
|
if (include && (!currentAsset || (newAsset.shouldCache() && (!currentAsset.is(newAsset))))) { |
|
//<debug> |
|
_debug("New/Updated Version of Asset: " + newAsset.assetConfig.path + " was found in new manifest"); |
|
//</debug> |
|
updatingAssets.push({_new: newAsset, _current: currentAsset}); |
|
} |
|
} |
|
|
|
// Look through current assets for stale/old assets that have been removed |
|
for (prop in currentAssets) { |
|
currentAsset = currentAssets[prop]; |
|
newAsset = manifest.getAsset(currentAsset.assetConfig.path); |
|
|
|
//New version of this asset has been filtered out |
|
include = !Microloader.filterAsset(newAsset); |
|
|
|
if (!include || !newAsset || (currentAsset.shouldCache() && !newAsset.shouldCache())) { |
|
//<debug> |
|
_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"); |
|
//</debug> |
|
Microloader.removedAssets.push(currentAsset); |
|
} |
|
} |
|
|
|
// Loop through all assets that need updating |
|
if (updatingAssets.length > 0) { |
|
Microloader.remainingUpdatingAssets = updatingAssets.length; |
|
while (updatingAssets.length > 0) { |
|
assets = updatingAssets.pop(); |
|
newAsset = assets._new; |
|
currentAsset = assets._current; |
|
|
|
// Full Updates will simply download the file and replace its current content |
|
if (newAsset.assetConfig.update === "full" || !currentAsset) { |
|
|
|
//<debug> |
|
if (newAsset.assetConfig.update === "delta") { |
|
_debug("Delta updated asset found without current asset available: " + newAsset.assetConfig.path + " fetching full file"); |
|
} else { |
|
_debug("Full update found for: " + newAsset.assetConfig.path + " fetching"); |
|
} |
|
//</debug> |
|
|
|
// Load the asset and cache its its content into Boot to be evaluated in sequence |
|
Boot.fetch(newAsset.assetConfig.path, (function (asset) { |
|
return function (result) { |
|
Microloader.onFullAssetUpdateLoaded(asset, result) |
|
}; |
|
}(newAsset)) |
|
); |
|
|
|
// Delta updates will be given a delta patch |
|
} else if (newAsset.assetConfig.update === "delta") { |
|
deltas = manifest.deltas; |
|
deltaPath = deltas + "/" + newAsset.assetConfig.path + "/" + currentAsset.assetConfig.hash + ".json"; |
|
// Fetch the Delta Patch and update the contents of the asset |
|
//<debug> |
|
_debug("Delta update found for: " + newAsset.assetConfig.path + " fetching"); |
|
//</debug> |
|
Boot.fetch(deltaPath, |
|
(function (asset, oldAsset) { |
|
return function (result) { |
|
Microloader.onDeltaAssetUpdateLoaded(asset, oldAsset, result) |
|
}; |
|
}(newAsset, currentAsset)) |
|
); |
|
} |
|
} |
|
} else { |
|
//<debug> |
|
_debug("No Assets needed updating"); |
|
//</debug> |
|
Microloader.onAllUpdatedAssetsReady(); |
|
} |
|
} else { |
|
//<debug> |
|
_debug("Manifest files have matching hash's"); |
|
//</debug> |
|
Microloader.onAllUpdatedAssetsReady(); |
|
} |
|
} else { |
|
_warn("Error loading manifest file to check for updates"); |
|
Microloader.onAllUpdatedAssetsReady(); |
|
} |
|
}, |
|
|
|
onFullAssetUpdateLoaded: function(asset, result) { |
|
var checksum; |
|
result = Microloader.parseResult(result); |
|
Microloader.remainingUpdatingAssets--; |
|
|
|
if (!result.error) { |
|
checksum = Microloader.checksum(result.content, asset.assetConfig.hash); |
|
//<debug> |
|
_debug("Checksum for Full asset: " + asset.assetConfig.path + " is " + checksum); |
|
//</debug> |
|
if (!checksum) { |
|
//<debug> |
|
_debug("Full Update Asset: " + asset.assetConfig.path + " has failed checksum. This asset will be uncached for future loading"); |
|
//</debug> |
|
|
|
// uncache this asset as there is a new version somewhere that has not been loaded. |
|
asset.uncache(); |
|
} else { |
|
asset.updateContent(result.content); |
|
Microloader.updatedAssets.push(asset); |
|
} |
|
} else { |
|
//<debug> |
|
_debug("Error loading file at" + asset.assetConfig.path + ". This asset will be uncached for future loading"); |
|
//</debug> |
|
|
|
// uncache this asset as there is a new version somewhere that has not been loaded. |
|
asset.uncache(); |
|
} |
|
|
|
if (Microloader.remainingUpdatingAssets === 0) { |
|
Microloader.onAllUpdatedAssetsReady(); |
|
} |
|
}, |
|
|
|
onDeltaAssetUpdateLoaded: function(asset, oldAsset, result) { |
|
var json, checksum, content; |
|
result = Microloader.parseResult(result); |
|
Microloader.remainingUpdatingAssets--; |
|
|
|
if (!result.error) { |
|
//<debug> |
|
_debug("Delta patch loaded successfully, patching content"); |
|
//</debug> |
|
try { |
|
json = JSON.parse(result.content); |
|
content = Microloader.patch(oldAsset.content, json); |
|
checksum = Microloader.checksum(content, asset.assetConfig.hash); |
|
//<debug> |
|
_debug("Checksum for Delta Patched asset: " + asset.assetConfig.path + " is " + checksum); |
|
//</debug> |
|
if (!checksum) { |
|
//<debug> |
|
_debug("Delta Update Asset: " + asset.assetConfig.path + " has failed checksum. This asset will be uncached for future loading"); |
|
//</debug> |
|
|
|
// uncache this asset as there is a new version somewhere that has not been loaded. |
|
asset.uncache(); |
|
} else { |
|
asset.updateContent(content); |
|
Microloader.updatedAssets.push(asset); |
|
} |
|
} catch (e) { |
|
_warn("Error parsing delta patch for " + asset.assetConfig.path + " with hash " + oldAsset.assetConfig.hash + " . This asset will be uncached for future loading"); |
|
// uncache this asset as there is a new version somewhere that has not been loaded. |
|
asset.uncache(); |
|
} |
|
} else { |
|
_warn("Error loading delta patch for " + asset.assetConfig.path + " with hash " + oldAsset.assetConfig.hash + " . This asset will be uncached for future loading"); |
|
|
|
// uncache this asset as there is a new version somewhere that has not been loaded. |
|
asset.uncache(); |
|
} |
|
if (Microloader.remainingUpdatingAssets === 0) { |
|
Microloader.onAllUpdatedAssetsReady(); |
|
} |
|
}, |
|
|
|
//TODO: Make this all transaction based to allow for reverting if quota is exceeded |
|
onAllUpdatedAssetsReady: function() { |
|
var asset; |
|
Microloader.updatedAssetsReady = true; |
|
|
|
if (Microloader.updatedManifest) { |
|
while (Microloader.removedAssets.length > 0) { |
|
asset = Microloader.removedAssets.pop(); |
|
//<debug> |
|
_debug("Asset: " + asset.assetConfig.path + " was removed, un-caching"); |
|
//</debug> |
|
asset.uncache(); |
|
} |
|
|
|
if (Microloader.updatedManifest) { |
|
//<debug> |
|
_debug("Manifest was updated, re-caching"); |
|
//</debug> |
|
Microloader.updatedManifest.cache(); |
|
} |
|
|
|
while (Microloader.updatedAssets.length > 0) { |
|
asset = Microloader.updatedAssets.pop(); |
|
//<debug> |
|
_debug("Asset: " + asset.assetConfig.path + " was updated, re-caching"); |
|
//</debug> |
|
asset.cache(); |
|
} |
|
|
|
} |
|
|
|
Microloader.notifyUpdateReady(); |
|
}, |
|
|
|
notifyUpdateReady: function () { |
|
if (Microloader.appCacheState !== 'checking' && Microloader.updatedAssetsReady) { |
|
if (Microloader.appCacheState === 'updated' || Microloader.updatedManifest) { |
|
//<debug> |
|
_debug("There was an update here you will want to reload the app, trigger an event"); |
|
//</debug> |
|
Microloader.appUpdate = { |
|
updated: true, |
|
app: Microloader.appCacheState === 'updated', |
|
manifest: Microloader.updatedManifest && Microloader.updatedManifest.exportContent() |
|
}; |
|
|
|
Microloader.fireAppUpdate(); |
|
} |
|
//<debug> |
|
else { |
|
_debug("AppCache and LocalStorage Cache are current, no updating needed"); |
|
Microloader.appUpdate = {}; |
|
} |
|
//</debug> |
|
} |
|
}, |
|
|
|
fireAppUpdate: function() { |
|
if (Ext.GlobalEvents) { |
|
// We defer dispatching this event slightly in order to let the application finish loading |
|
// as we are still very early in the lifecycle |
|
Ext.defer(function() { |
|
Ext.GlobalEvents.fireEvent('appupdate', Microloader.appUpdate); |
|
}, 100); |
|
} |
|
}, |
|
|
|
checksum: function(content, hash) { |
|
if(!content || !hash) { |
|
return false; |
|
} |
|
|
|
var passed = true, |
|
hashLn = hash.length, |
|
checksumType = content.substring(0, 1); |
|
|
|
if (checksumType == '/') { |
|
if (content.substring(2, hashLn + 2) !== hash) { |
|
passed = false; |
|
} |
|
} else if (checksumType == 'f') { |
|
if (content.substring(10, hashLn + 10) !== hash) { |
|
passed = false; |
|
} |
|
} else if (checksumType == '.') { |
|
if (content.substring(1, hashLn + 1) !== hash) { |
|
passed = false; |
|
} |
|
} |
|
return passed; |
|
}, |
|
parseResult: function(result) { |
|
var rst = {}; |
|
if ((result.exception || result.status === 0) && !Boot.env.phantom) { |
|
rst.error = true; |
|
} else if ((result.status >= 200 && result.status < 300) || result.status === 304 |
|
|| Boot.env.phantom |
|
|| (result.status === 0 && result.content.length > 0) |
|
) { |
|
rst.content = result.content; |
|
} else { |
|
rst.error = true; |
|
} |
|
return rst; |
|
} |
|
}; |
|
|
|
return Microloader; |
|
}()); |
|
|
|
/** |
|
* @type {String/Object} |
|
*/ |
|
Ext.manifest = Ext.manifest || "bootstrap"; |
|
|
|
Ext.Microloader.run(); |