diff options
Diffstat (limited to 'js/assets.js')
-rw-r--r-- | js/assets.js | 1744 |
1 files changed, 849 insertions, 895 deletions
diff --git a/js/assets.js b/js/assets.js index 286cf0d..9bb2470 100644 --- a/js/assets.js +++ b/js/assets.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2013-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,14 +17,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ 'use strict'; -/******************************************************************************/ - ηMatrix.assets = (function() { let api = {}; @@ -33,21 +31,21 @@ let connectionError = vAPI.i18n('errorCantConnectTo'); let notifyObservers = function (topic, details) { - let result; + let result; - for (let i=0; i<observers.length; ++i) { - result = observers[i](topic, details); - } + for (let i=0; i<observers.length; ++i) { + result = observers[i](topic, details); + } - return result; + return result; } function isEmptyString(s) { - return (typeof s === 'string' && s === ''); + return (typeof s === 'string' && s === ''); } function noOp() { - return; + return; } // Cache Registry @@ -57,308 +55,257 @@ let cacheRegistryStart = Date.now(); let saveCacheRegistry = (function () { - let timer; - - function save() { - timer = undefined; - vAPI.cacheStorage.set({ - assetCacheRegistry: cacheRegistry, - }); - } - - return function (lazy) { - if (timer !== undefined) { - clearTimeout(timer); - } - - if (lazy === true) { - timer = vAPI.setTimeout(save, 500); - } else { - save(); - } - }; + let timer; + + let save = function () { + timer = undefined; + vAPI.cacheStorage.set({ + assetCacheRegistry: cacheRegistry, + }); + } + + return function (lazy) { + if (timer !== undefined) { + clearTimeout(timer); + } + + if (lazy === true) { + timer = vAPI.setTimeout(save, 500); + } else { + save(); + } + }; })(); let getCacheRegistry = function (callback) { - if (cacheRegistryReady == true) { - callback(cacheRegistry); - return; - } - - if (cacheRegistryCallbacks !== undefined) { - // If it's not undefined it's always an array - // - // eMatrix: this block in particular is probably never - // called: originally, it was used because uMatrix called - // some code in a callback that could wait - // - // While waiting, more elements could've been pushed in - // the array - // - // Since the waiting callback is not used here, any - // further callback are likely to be handled by the - // condition above - // This block is left here just in case - cacheRegistryCallbacks.push(callback); - return; - } - - cacheRegistryCallbacks = [callback]; - cacheRegistryReady = true; - - let onRead = function (bin) { - if (!bin || !bin['assetCacheRegistry']) { - cacheRegistry = {}; - } else { - cacheRegistry = bin['assetCacheRegistry']; - } - }; - - vAPI.cacheStorage.get('assetCacheRegistry', onRead); - - let f; - while ((f = cacheRegistryCallbacks.shift())) { - f(cacheRegistry); - } + if (cacheRegistryReady == true) { + callback(cacheRegistry); + return; + } + + if (cacheRegistryCallbacks !== undefined) { + // If it's not undefined it's always an array + // + // eMatrix: this block in particular is probably never + // called: originally, it was used because uMatrix called + // some code in a callback that could wait + // + // While waiting, more elements could've been pushed in + // the array + // + // Since the waiting callback is not used here, any + // further callback are likely to be handled by the + // condition above + // This block is left here just in case + cacheRegistryCallbacks.push(callback); + return; + } + + cacheRegistryCallbacks = [callback]; + + let onRead = function (bin) { + if (!bin || !bin['assetCacheRegistry']) { + cacheRegistry = {}; + } else { + cacheRegistry = bin['assetCacheRegistry']; + } + + cacheRegistryReady = true; + + let f; + while ((f = cacheRegistryCallbacks.shift())) { + f(cacheRegistry); + } + }; + + vAPI.cacheStorage.get('assetCacheRegistry', onRead); }; let readCache = function (key, callback) { - let report = function (content, error) { - let details = { - assetKey: key, - content: content, - }; - - if (error) { - details.error = error; - } - - callback(details); - }; - - let onRead = function (bin) { - if (!bin || !bin[key]) { - report('', 'E_NOTFOUND'); - return; - } - - let entry = cacheRegistry[key]; - if (entry === undefined) { - let onRead2 = function (bin) { - if (!bin || !bin['assetCacheRegistry']) { - cacheRegistry = {}; - } else { - cacheRegistry = bin['assetCacheRegistry']; - } - }; - - vAPI.cacheStorage.get('assetCacheRegistry', onRead2); - - entry = cacheRegistry[key]; - if (entry === undefined) { - report('', 'E_NOTFOUND'); - return; - } - } - - entry.readTime = Date.now(); - saveCacheRegistry(true); - - report(bin[key]); - }; - - let onReady = function () { - vAPI.cacheStorage.get(key, onRead); - }; - - getCacheRegistry(onReady); - }; - - let writeCache = function (key, details, callback) { - let content = ''; - - if (typeof details === 'string') { - content = details; - } else if (details instanceof Object) { - content = details.content || ''; - } - - if (content === '') { - removeCache(key, callback); - return; - } - - let report = function (content) { - let details = { - assetKey: key, - content: content, - }; - - if (typeof callback === 'function') { - callback(details); - } - - notifyObservers('after-asset-updated', details); - }; - - let onReady = function () { - let entry = cacheRegistry[key]; - if (entry === undefined) { - entry = cacheRegistry[key] = {}; - } - - entry.writeTime = entry.readTime = Date.now(); - if (details instanceof Object && typeof details.url === 'string') { - entry.remoteURL = details.url; - } - - let bin = { - assetCacheRegistry: cacheRegistry, - }; - - bin[key] = content; - vAPI.cacheStorage.set(bin); - report(content); - }; - - getCacheRegistry(onReady); - }; - - let cacheRemove = function (pattern, callback) { - let onReady = function (cache) { - let entries = []; + let fullkey = 'cache/' + key; + let cache = { placeholder: true }; - for (let key in cache) { - if (pattern instanceof RegExp && !pattern.test(key)) { - continue; - } + let report = function (content, error) { + let details = { + assetKey: key, + content: content, + }; - if (typeof pattern === 'string' && key !== pattern) { - continue; - } + if (error) { + details.error = error; + } - entries.push(key); + return callback(details); + }; - delete cache[key]; - } + let onRead = function (bin) { + if (!bin || !bin[fullkey]) { + return report('', 'E_NOTFOUND'); + } - if (entries.length !== 0) { - vAPI.cacheStorage.remove(content); + let entry = cache[key]; + if (entry === undefined) { + return report('', 'E_NOTFOUND'); + } - let bin = { - assetCacheRegistry: cache, - }; - vAPI.cacheStorage.set(bin); - } + entry.readTime = Date.now(); + saveCacheRegistry(true); - if (typeof callback === 'function') { - callback(); - } + report(bin[fullkey]); + }; - for (let i=0; i<entries.length; ++i) { - notifyObservers('after-asset-updated', { - assetKey: entries[i], - }); - } - }; + let onReady = function (registry) { + cache = registry; + vAPI.cacheStorage.get(fullkey, onRead); + }; - getCacheRegistry(onReady); + getCacheRegistry(onReady); }; - let markDirtyCache = function (pattern, exclude, callback) { - let onReady = function (registry) { - let entry; - let mustSave = false; - - for (let key in registry) { - if (pattern instanceof RegExp && pattern.test(key) === false) { - continue; - } else if (typeof pattern === 'string' && key !== pattern) { - continue; - } else if (Array.isArray(pattern) - && pattern.indexOf(key) === -1) { - continue; - } - - if (exclude instanceof RegExp && exclude.test(key)) { - continue; - } else if (typeof exclude === 'string' && key === exclude) { - continue; - } else if (Array.isArray(exclude) - && exclude.indexOf(key) !== -1) { - continue; - } - - entry = registry[key]; - if (!entry.writeTime) { - continue; - } - - registry[key].writeTime = 0; - mustSave = true; - } - - if (mustSave) { - let bin = { - assetCacheRegistry: registry, - }; - vAPI.cacheStorage.set(bin); - } - - if (typeof callback === 'function') { - callback(); - } - }; - - if (typeof exclude === 'function') { - callback = exclude; - exclude = undefined; - } - - getCacheRegistry(onReady); + let writeCache = function (key, details, callback) { + let fullkey = 'cache/' + key; + let content = ''; + + if (typeof details === 'string') { + content = details; + } else if (details instanceof Object) { + content = details.content || ''; + } + + if (content === '') { + return removeCache(key, callback); + } + + let report = function (content) { + let details = { + assetKey: key, + content: content, + }; + + if (typeof callback === 'function') { + callback(details); + } + + notifyObservers('after-asset-updated', details); + }; + + let onReady = function (registry) { + let entry = registry[key]; + if (entry === undefined) { + entry = registry[key] = {}; + } + + entry.writeTime = entry.readTime = Date.now(); + if (details instanceof Object && typeof details.url === 'string') { + entry.remoteURL = details.url; + } + + let bin = { + assetCacheRegistry: cacheRegistry, + }; + + bin[fullkey] = content; + vAPI.cacheStorage.set(bin); + report(content); + }; + + getCacheRegistry(onReady); }; - let removeCache = function (key, callback) { - let onReady = function (cacheRegistry) { - let removedEntries = []; - let removedContent = []; + let removeCache = function (pattern, callback) { + let onReady = function (cache) { + let entries = []; + let contents = []; + + for (let key in cache) { + if (pattern instanceof RegExp && !pattern.test(key)) { + continue; + } - for (let k in cacheRegistry) { - if (key instanceof RegExp && !key.test(k)) { - continue; - } - if (typeof key === 'string' && k !== key) { - continue; - } + if (typeof pattern === 'string' && key !== pattern) { + continue; + } - removedEntries.push(k); - removedContent.push(k); + entries.push(key); + contents.push('cache/' + key); - delete cacheRegistry[k]; - } + delete cache[key]; + } - if (removedContent.length > 0) { - vAPI.cacheStorage.remove(removedContent); + if (contents.length !== 0) { + vAPI.cacheStorage.remove(contents); - let bin = { - assetCacheRegistry: cacheRegistry, - }; + let bin = { + assetCacheRegistry: cache, + }; + vAPI.cacheStorage.set(bin); + } - vAPI.cacheStorage.set(bin); - } + if (typeof callback === 'function') { + callback(); + } - if (typeof callback === 'function') { - callback(); - } + for (let i=0; i<entries.length; ++i) { + notifyObservers('after-asset-updated', { + assetKey: entries[i], + }); + } + }; - for (let i=0; i<removedEntries.length; ++i) { - notifyObservers('after-asset-updated', { - assetKey: removedEntries[i], - }); - } - }; + getCacheRegistry(onReady); + }; - getCacheRegistry(onReady); + let markDirtyCache = function (pattern, exclude, callback) { + let onReady = function (registry) { + let entry; + let mustSave = false; + + for (let key in registry) { + if (pattern instanceof RegExp && pattern.test(key) === false) { + continue; + } else if (typeof pattern === 'string' && key !== pattern) { + continue; + } else if (Array.isArray(pattern) + && pattern.indexOf(key) === -1) { + continue; + } + + if (exclude instanceof RegExp && exclude.test(key)) { + continue; + } else if (typeof exclude === 'string' && key === exclude) { + continue; + } else if (Array.isArray(exclude) + && exclude.indexOf(key) !== -1) { + continue; + } + + entry = registry[key]; + if (!entry.writeTime) { + continue; + } + + registry[key].writeTime = 0; + mustSave = true; + } + + if (mustSave) { + let bin = { + assetCacheRegistry: registry, + }; + vAPI.cacheStorage.set(bin); + } + + if (typeof callback === 'function') { + callback(); + } + }; + + if (typeof exclude === 'function') { + callback = exclude; + exclude = undefined; + } + + getCacheRegistry(onReady); }; // Source Registry @@ -367,248 +314,252 @@ let sourceRegistryCallbacks = undefined; let saveSourceRegistry = (function () { - let timer; - - function save() { - timer = undefined; - vAPI.cacheStorage.set({ - assetSourceRegistry: sourceRegistry, - }); - } - - return function (lazy) { - if (timer !== undefined) { - clearTimeout(timer); - } - - if (lazy === true) { - timer = vAPI.setTimeout(save, 500); - } else { - save(); - } - }; + let timer; + + let save = function () { + timer = undefined; + vAPI.cacheStorage.set({ + assetSourceRegistry: sourceRegistry, + }); + } + + return function (lazy) { + if (timer !== undefined) { + clearTimeout(timer); + } + + if (lazy === true) { + timer = vAPI.setTimeout(save, 500); + } else { + save(); + } + }; })(); let registerSource = function (key, details) { - let entry = sourceRegistry[key] || {}; - - for (let p in details) { - if (details.hasOwnProperty(p) === false) { - continue; - } - - if (details[p] !== undefined) { - entry[p] = details[p]; - } else { - delete entry[p]; - } - } - - let contentUrl = details.contentURL; - if (contentUrl) { - if (typeof contentUrl === 'string') { - contentUrl = entry.contentURL = [contentUrl]; - } else if (Array.isArray(contentUrl) === false) { - contentUrl = entry.contentURL = []; - } - - let remoteCount = 0; - - for (let i=0; i<contentUrl.length; ++i) { - if (externalPathRegex.test(contentUrl[i])) { - ++remoteCount; - } - } - - entry.hasLocalURL = (remoteCount !== contentUrl.length); - entry.hasRemoteURL = (remoteCount !== 0); - } else { - entry.contentURL = []; - } - - if (typeof entry.updateAfter !== 'number') { - entry.updateAfter = 13; - } - - if (entry.submitter) { - entry.submitTime = Date.now(); // Detects stale entries - } - - sourceRegistry[key] = entry; + let entry = sourceRegistry[key] || {}; + + for (let p in details) { + if (details.hasOwnProperty(p) === false) { + continue; + } + + if (details[p] !== undefined) { + entry[p] = details[p]; + } else { + delete entry[p]; + } + } + + let contentUrl = details.contentURL; + if (contentUrl) { + if (typeof contentUrl === 'string') { + contentUrl = entry.contentURL = [contentUrl]; + } else if (Array.isArray(contentUrl) === false) { + contentUrl = entry.contentURL = []; + } + + let remoteCount = 0; + + for (let i=0; i<contentUrl.length; ++i) { + if (externalPathRegex.test(contentUrl[i])) { + ++remoteCount; + } + } + + entry.hasLocalURL = (remoteCount !== contentUrl.length); + entry.hasRemoteURL = (remoteCount !== 0); + } else { + entry.contentURL = []; + } + + if (typeof entry.updateAfter !== 'number') { + entry.updateAfter = 13; + } + + if (entry.submitter) { + entry.submitTime = Date.now(); // Detects stale entries + } + + sourceRegistry[key] = entry; }; let unregisterSource = function (key) { - removeCache(key); - delete sourceRegistry[key]; - saveSourceRegistry(); + removeCache(key); + delete sourceRegistry[key]; + saveSourceRegistry(); }; let updateSourceRegistry = function (string, silent) { - let json; - - try { - json = JSON.parse(string); - } catch (e) { - return; - } - - for (let key in sourceRegistry) { - if (json[key] === undefined - && sourceRegistry[key].submitter === undefined) { - unregisterSource(key); - } - } - - for (let key in json) { - if (sourceRegistry[key] === undefined && !silent) { - notifyObservers('builtin-asset-source-added', { - assetKey: key, - entry: json[key], - }); - } - - registerSource(key, json[key]); - } - - saveSourceRegistry(); + let json; + + try { + json = JSON.parse(string); + } catch (e) { + return; + } + + if (json instanceof Object === false) { + return; + } + + for (let key in sourceRegistry) { + if (json[key] === undefined + && sourceRegistry[key].submitter === undefined) { + unregisterSource(key); + } + } + + for (let key in json) { + if (sourceRegistry[key] === undefined && !silent) { + notifyObservers('builtin-asset-source-added', { + assetKey: key, + entry: json[key], + }); + } + + registerSource(key, json[key]); + } + + saveSourceRegistry(); }; let getSourceRegistry = function (callback) { - if (sourceRegistryReady === true) { - callback(sourceRegistry); - return; - } - - if (sourceRegistryCallbacks !== undefined) { - // If it's not undefined it's always an array - sourceRegistryCallbacks.push(callback); - return; - } - - sourceRegistryCallbacks = [callback]; - - let onReady = function () { - sourceRegistryReady = true; - - let f; - while ((f = sourceRegistryCallbacks.shift())) { - f(sourceRegistry); - } - }; - - let createRegistry = function () { - api.fetchText - (ηMatrix.assetsBootstrapLocation || 'assets/assets.json', - function (details) { - updateSourceRegistry(details.content, true); - onReady(); - }); - }; - - let onRead = function (bin) { - if (!bin || !bin.assetSourceRegistry - || Object.keys(bin.assetSourceRegistry).length == 0) { - createRegistry(); - return; - } - - sourceRegistry = bin.assetSourceRegistry; - onReady(); - }; - - vAPI.cacheStorage.get('assetSourceRegistry', onRead); + if (sourceRegistryReady === true) { + callback(sourceRegistry); + return; + } + + if (sourceRegistryCallbacks !== undefined) { + // If it's not undefined it's always an array + sourceRegistryCallbacks.push(callback); + return; + } + + sourceRegistryCallbacks = [callback]; + + let onReady = function () { + sourceRegistryReady = true; + + let f; + while ((f = sourceRegistryCallbacks.shift())) { + f(sourceRegistry); + } + }; + + let createRegistry = function () { + api.fetchText + (ηMatrix.assetsBootstrapLocation || 'assets/assets.json', + function (details) { + updateSourceRegistry(details.content, true); + onReady(); + }); + }; + + let onRead = function (bin) { + if (!bin || !bin.assetSourceRegistry + || Object.keys(bin.assetSourceRegistry).length == 0) { + createRegistry(); + return; + } + + sourceRegistry = bin.assetSourceRegistry; + onReady(); + }; + + vAPI.cacheStorage.get('assetSourceRegistry', onRead); }; // Remote let getRemote = function (key, callback) { - let assetDetails = {}; - let contentUrl; - - let report = function (content, error) { - let details = { - assetKey: key, - content: content, - }; - if (error) { - details.error = assetDetails.lastError = error; - } else { - assetDetails.lastError = undefined; - } - callback(details); - }; - - let tryLoad = function (tries) { - let urls = []; - - let tr = (tries === undefined) ? 10 : tries; - - if (tr <= 0) { - console.warn('ηMatrix could not load the asset ' - +assetDetails.title); - return; - } - - if (typeof assetDetails.contentURL === 'string') { - urls = [assetDetails.contentURL]; - } else if (Array.isArray(assetDetails.contentURL)) { - urls = assetDetails.contentURL.slice(0); - } - - while ((contentUrl = urls.shift())) { - if (externalPathRegex.test(contentUrl)) { - break; - } - } - - if (!contentUrl) { - report('', 'E_NOTFOUND'); - return; - } - - api.fetchText(contentUrl, onRemoteContentLoad, onRemoteContentError, - tr-1); - }; - - let onRemoteContentLoad = function (details, tries) { - if (isEmptyString(details.content) === true) { - registerSource(key, { - error: { - time: Date.now(), - error: 'No content' - } - }); - tryLoad(tries); - } - - writeCache(key, { - content: details.content, - url: contentUrl, - }); - - registerSource(key, {error: undefined}); - report(details.content); - }; - - let onRemoteContentError = function (details, tries) { - let text = details.statusText; - if (details.statusCode === 0) { - text = 'network error'; - } - registerSource(key, { - error: { - time: Date.now(), - error: text, - } - }); - tryLoad(tries); - }; - - let onReady = function (registry) { - assetDetails = registry[key] || {}; - tryLoad(); - }; - - getSourceRegistry(onReady); + let assetDetails = {}; + let contentUrl = ''; + let urls = []; + + let report = function (content, error) { + let details = { + assetKey: key, + content: content, + }; + if (error) { + details.error = assetDetails.lastError = error; + } else { + assetDetails.lastError = undefined; + } + callback(details); + }; + + let tryLoad = function (tries) { + let tr = (tries === undefined) ? 10 : tries; + + if (tr <= 0) { + console.warn('ηMatrix could not load the asset ' + +assetDetails.title); + return; + } + + while ((contentUrl = urls.shift())) { + if (externalPathRegex.test(contentUrl)) { + break; + } + } + + if (!contentUrl) { + report('', 'E_NOTFOUND'); + return; + } + + api.fetchText(contentUrl, + onRemoteContentLoad, + onRemoteContentError, + tr-1); + }; + + let onRemoteContentLoad = function (details, tries) { + if (isEmptyString(details.content) === true) { + registerSource(key, { + error: { + time: Date.now(), + error: 'No content' + } + }); + return tryLoad(tries); + } + + writeCache(key, { + content: details.content, + url: contentUrl, + }); + + registerSource(key, {error: undefined}); + report(details.content); + }; + + let onRemoteContentError = function (details, tries) { + let text = details.statusText; + if (details.statusCode === 0) { + text = 'network error'; + } + registerSource(key, { + error: { + time: Date.now(), + error: text, + } + }); + tryLoad(tries); + }; + + let onReady = function (registry) { + assetDetails = registry[key] || {}; + if (typeof assetDetails.contentURL === 'string') { + urls = [assetDetails.contentURL]; + } else if (Array.isArray(assetDetails.contentURL)) { + urls = assetDetails.contentURL.slice(0); + } + tryLoad(); + }; + + getSourceRegistry(onReady); }; // Updater @@ -620,443 +571,446 @@ let updateFetch = new Set(); let updateStart = function () { - updateStatus = 'running'; - updateFetch.clear(); - updated = []; - notifyObservers('before-assets-updated'); - updateNext(); + updateStatus = 'running'; + updateFetch.clear(); + updated = []; + notifyObservers('before-assets-updated'); + updateNext(); }; let updateNext = function () { - let cacheReg = undefined; - let sourceReg = undefined; - - let gcOne = function (key) { - let entry = cacheReg[key]; - if (entry && entry.readTime < cacheRegistryStart) { - cacheRemove(key); - } - }; - - let findOne = function () { - let now = Date.now(); - let sourceEntry; - let cacheEntry; - - for (let key in sourceReg) { - sourceEntry = sourceReg[key]; - if (sourceEntry.hasRemoteURL !== true) { - continue; - } - if (updateFetch.has(key)) { - continue; - } - - cacheEntry = cacheReg[key]; - if (cacheEntry - && (cacheEntry.writeTime - + sourceEntry.updateAfter*86400000) > now) { - continue; - } - if (notifyObservers('before-asset-updated', {assetKey: key})) { - return key; - } - - gcOne(key); - } - - return undefined; - }; - - let onUpdate = function (details) { - if (details.content !== '') { - updated.push(details.assetKey); - if (details.assetKey === 'assets.json') { - updateSourceRegistry(details.content); - } - } else { - notifyObservers('asset-update-failed', { - assetKey: details.assetKey, - }); - } - - if (findOne() !== undefined) { - vAPI.setTimeout(updateNext, updateDelay); - } else { - updateEnd(); - } - }; - - let updateOne = function () { - let key = findOne(); - if (key === undefined) { - updateEnd(); - return; - } - - updateFetch.add(key); - getRemote(key, onUpdate); - }; - - let onSourceReady = function (registry) { - sourceReg = registry; - updateOne(); - }; - - let onCacheReady = function (registry) { - cacheReg = registry; - getSourceRegistry(onSourceReady); - }; - - getCacheRegistry(onCacheReady); + let cacheReg = undefined; + let sourceReg = undefined; + + let gcOne = function (key) { + let entry = cacheReg[key]; + if (entry && entry.readTime < cacheRegistryStart) { + removeCache(key); + } + }; + + let findOne = function () { + let now = Date.now(); + let sourceEntry; + let cacheEntry; + + for (let key in sourceReg) { + sourceEntry = sourceReg[key]; + if (sourceEntry.hasRemoteURL !== true) { + continue; + } + if (updateFetch.has(key)) { + continue; + } + + cacheEntry = cacheReg[key]; + if (cacheEntry + && (cacheEntry.writeTime + + sourceEntry.updateAfter*86400000) > now) { + continue; + } + if (notifyObservers('before-asset-updated', {assetKey: key})) { + return key; + } + + gcOne(key); + } + + return undefined; + }; + + let onUpdate = function (details) { + if (details.content !== '') { + updated.push(details.assetKey); + if (details.assetKey === 'assets.json') { + updateSourceRegistry(details.content); + } + } else { + notifyObservers('asset-update-failed', { + assetKey: details.assetKey, + }); + } + + if (findOne() !== undefined) { + vAPI.setTimeout(updateNext, updateDelay); + } else { + updateEnd(); + } + }; + + let updateOne = function () { + let key = findOne(); + if (key === undefined) { + updateEnd(); + return; + } + + updateFetch.add(key); + getRemote(key, onUpdate); + }; + + let onSourceReady = function (registry) { + sourceReg = registry; + updateOne(); + }; + + let onCacheReady = function (registry) { + cacheReg = registry; + getSourceRegistry(onSourceReady); + }; + + getCacheRegistry(onCacheReady); }; let updateEnd = function () { - let keys = updated.slice(0); - updateFetch.clear(); - updateStatus = 'stop'; - updateDelay = updateDefaultDelay; - notifyObservers('after-asset-updated', { - assetKeys: keys, - }); + let keys = updated.slice(0); + updateFetch.clear(); + updateStatus = 'stop'; + updateDelay = updateDefaultDelay; + notifyObservers('after-asset-updated', { + assetKeys: keys, + }); }; // Assets API api.addObserver = function (observer) { - if (observers.indexOf(observer) === -1) { - observers.push(observer); - } + if (observers.indexOf(observer) === -1) { + observers.push(observer); + } }; api.removeObserver = function (observer) { - let pos = observers.indexOf(observer); - if (pos !== -1) { - observers.splice(pos, 1); - } + let pos = observers.indexOf(observer); + while (pos !== -1) { + observers.splice(pos, 1); + pos = observers.indexOf(observer); + } }; api.fetchText = function (url, onLoad, onError, tries) { - let iurl = externalPathRegex.test(url) ? url : vAPI.getURL(url); - let tr = (tries === undefined) ? 10 : tries; - - if (typeof onError !== 'function') { - onError = onLoad; - } - - let onResponseReceived = function () { - this.onload = this.onerror = this.ontimeout = null; - - let details = { - url: url, - content: '', - // On local files this.status is 0, but the request - // is successful - statusCode: this.status || 200, - statusText: this.statusText || '', - }; - - if (details.statusCode < 200 || details.statusCode >= 300) { - onError.call(null, details, tr); - return; - } - - if (isEmptyString(this.responseText) === true) { - onError.call(null, details, tr); - return; - } - - let t = this.responseText.trim(); - - // Discard HTML as it's probably an error - // (the request expects plain text as a response) - if (t.startsWith('<') && t.endsWith('>')) { - onError.call(null, details, tr); - return; - } - - details.content = t; - onLoad.call(null, details, tr); - }; - - let onErrorReceived = function () { - this.onload = this.onerror = this.ontimeout = null; - - ηMatrix.logger.writeOne('', 'error', - connectionError.replace('{{url}}', iurl)); - - onError.call(null, { - url: url, - content: '', - }, tr); - }; - - let req = new XMLHttpRequest(); - req.open('GET', iurl, true); - req.timeout = 30000; - req.onload = onResponseReceived; - req.onerror = onErrorReceived; - req.ontimeout = onErrorReceived; - req.responseType = 'text'; - - try { - // This can throw in some cases - req.send(); - } catch (e) { - onErrorReceived.call(req); - } + let iurl = externalPathRegex.test(url) ? url : vAPI.getURL(url); + let tr = (tries === undefined) ? 10 : tries; + + if (typeof onError !== 'function') { + onError = onLoad; + } + + let onResponseReceived = function () { + this.onload = this.onerror = this.ontimeout = null; + + let details = { + url: url, + content: '', + // On local files this.status is 0, but the request + // is successful + statusCode: this.status || 200, + statusText: this.statusText || '', + }; + + if (details.statusCode < 200 || details.statusCode >= 300) { + return onError.call(null, details, tr); + } + + if (isEmptyString(this.responseText) === true) { + return onError.call(null, details, tr); + } + + let t = this.responseText.trim(); + + // Discard HTML as it's probably an error + // (the request expects plain text as a response) + if (t.startsWith('<') && t.endsWith('>')) { + return onError.call(null, details, tr); + } + + details.content = t; + return onLoad.call(null, details, tr); + }; + + let onErrorReceived = function () { + this.onload = this.onerror = this.ontimeout = null; + + ηMatrix.logger.writeOne('', 'error', + connectionError.replace('{{url}}', iurl)); + + onError.call(null, { + url: url, + content: '', + }, tr); + }; + + let req = new XMLHttpRequest(); + req.open('GET', iurl, true); + req.timeout = 30000; + req.onload = onResponseReceived; + req.onerror = onErrorReceived; + req.ontimeout = onErrorReceived; + req.responseType = 'text'; + + try { + // This can throw in some cases + req.send(); + } catch (e) { + onErrorReceived.call(req); + } }; api.registerAssetSource = function (key, details) { - getSourceRegistry(function () { - registerSource(key, details); - saveSourceRegistry(true); - }); + getSourceRegistry(function () { + registerSource(key, details); + saveSourceRegistry(true); + }); }; api.unregisterAssetSource = function (key) { - getSourceRegistry(function () { - unregisterSource(key); - saveSourceRegistry(true); - }); + getSourceRegistry(function () { + unregisterSource(key); + saveSourceRegistry(true); + }); }; api.get = function (key, options, callback) { - let cb; - let opt; - - if (typeof options === 'function') { - cb = options; - opt = {}; - } else if (typeof callback !== 'function') { - cb = noOp; - opt = options; - } else { - cb = callback; - opt = options; - } - - let assetDetails = {}; - let contentUrl = undefined; - - let report = function (content, error) { - let details = { - assetKey: key, - content: content, - }; - - if (error) { - details.error = assetDetails.error = error; - } else { - assetDetails.error = undefined; - } - - cb(details); - }; - - let onContentNotLoaded = function (details, tries) { - let external; - let urls = []; - - let tr = (tries === undefined) ? 10 : tries; - - if (tr <= 0) { - console.warn('ηMatrix couldn\'t download the asset ' - +assetDetails.title); - return; - } - - if (typeof assetDetails.contentURL === 'string') { - urls = [assetDetails.contentURL]; - } else if (Array.isArray(assetDetails.contentURL)) { - urls = assetDetails.contentURL.slice(0); - } - - while ((contentUrl = urls.shift())) { - external = externalPathRegex.test(contentUrl); - if (external === true && assetDetails.loaded !== true) { - break; - } - if (external === false || assetDetails.hasLocalURL !== true) { - break; - } - } - - if (!contentUrl) { - report('', 'E_NOTFOUND'); - return; - } - - api.fetchText(contentUrl, onContentLoaded, onContentNotLoaded, - tr-1); - }; - - let onContentLoaded = function (details, tries) { - if (isEmptyString(details.content) === true) { - onContentNotLoaded(undefined, tries); - return; - } - - if (externalPathRegex.test(details.url) - && opt.dontCache !== true) { - writeCache(key, { - content: details.content, - url: contentUrl, - }); - } - - assetDetails.loaded = true; - - report(details.content); - }; - - let onCachedContentLoad = function (details) { - if (details.content !== '') { - report(details.content); - return; - } - - let onReady = function (registry) { - assetDetails = registry[key] || {}; - onContentNotLoaded(); - } - - getSourceRegistry(onReady); - }; - - readCache(key, onCachedContentLoad); + let cb; + let opt; + + if (typeof options === 'function') { + cb = options; + opt = {}; + } else if (typeof callback !== 'function') { + cb = noOp; + opt = options; + } else { + cb = callback; + opt = options; + } + + let assetDetails = {}; + let urls = []; + let contentUrl = ''; + + let report = function (content, error) { + let details = { + assetKey: key, + content: content, + }; + + if (error) { + details.error = assetDetails.error = error; + } else { + assetDetails.error = undefined; + } + + cb(details); + }; + + let onContentNotLoaded = function (details, tries) { + let external; + + let tr = (tries === undefined) ? 10 : tries; + + if (tr <= 0) { + console.warn('ηMatrix couldn\'t download the asset ' + +assetDetails.title); + return; + } + + while ((contentUrl = urls.shift())) { + external = externalPathRegex.test(contentUrl); + if (external === true && assetDetails.loaded !== true) { + break; + } + if (external === false || assetDetails.hasLocalURL !== true) { + break; + } + } + + if (!contentUrl) { + return report('', 'E_NOTFOUND'); + } + + api.fetchText(contentUrl, + onContentLoaded, + onContentNotLoaded, + tr-1); + }; + + let onContentLoaded = function (details, tries) { + if (isEmptyString(details.content) === true) { + return onContentNotLoaded(undefined, tries); + } + + if (externalPathRegex.test(details.url) + && opt.dontCache !== true) { + writeCache(key, { + content: details.content, + url: contentUrl, + }); + } + + assetDetails.loaded = true; + + report(details.content); + }; + + let onCachedContentLoad = function (details) { + if (details.content !== '') { + return report(details.content); + } + + let onRead = function (details) { + console.debug(details); + report(details.content || 'missing contents'); + } + + let onReady = function (registry) { + assetDetails = registry[key] || {}; + if (typeof assetDetails.contentURL === 'string') { + urls = [assetDetails.contentURL]; + } else if (Array.isArray(assetDetails.contentURL)) { + urls = assetDetails.contentURL.slice(0); + } + if (true === assetDetails.loaded) { + readCache(key, onRead); + } else { + onContentNotLoaded(); + } + } + + getSourceRegistry(onReady); + }; + + readCache(key, onCachedContentLoad); }; api.put = function (key, content, callback) { - writeCache(key, content, callback); + writeCache(key, content, callback); }; api.metadata = function (callback) { - let onSourceReady = function (registry) { - let source = JSON.parse(JSON.stringify(registry)); - let cache = cacheRegistry; - let sourceEntry; - let cacheEntry; - let now = Date.now(); - let obsoleteAfter; - - for (let key in source) { - sourceEntry = source[key]; - cacheEntry = cache[key]; - - if (cacheEntry) { - sourceEntry.cached = true; - sourceEntry.writeTime = cacheEntry.writeTime; - obsoleteAfter = cacheEntry.writeTime - + sourceEntry.updateAfter * 86400000; - sourceEntry.obsolete = obsoleteAfter < now; - sourceEntry.remoteURL = cacheEntry.remoteURL; - } else { - sourceEntry.writeTime = 0; - obsoleteAfter = 0; - sourceEntry.obsolete = true; - } - } - - callback(source); - } - - let onCacheReady = function () { - getSourceRegistry(onSourceReady); - } - - getCacheRegistry(onCacheReady); + let onSourceReady = function (registry) { + let source = JSON.parse(JSON.stringify(registry)); + let cache = cacheRegistry; + let sourceEntry; + let cacheEntry; + let now = Date.now(); + let obsoleteAfter; + + for (let key in source) { + sourceEntry = source[key]; + cacheEntry = cache[key]; + + if (cacheEntry) { + sourceEntry.cached = true; + sourceEntry.writeTime = cacheEntry.writeTime; + obsoleteAfter = cacheEntry.writeTime + + sourceEntry.updateAfter * 86400000; + sourceEntry.obsolete = obsoleteAfter < now; + sourceEntry.remoteURL = cacheEntry.remoteURL; + } else { + sourceEntry.writeTime = 0; + obsoleteAfter = 0; + sourceEntry.obsolete = true; + } + } + + callback(source); + } + + let onCacheReady = function () { + getSourceRegistry(onSourceReady); + } + + getCacheRegistry(onCacheReady); }; api.purge = function (pattern, exclude, callback) { - markDirtyCache(pattern, exclude, callback); + markDirtyCache(pattern, exclude, callback); }; api.remove = function (pattern, callback) { - cacheRemove(pattern, callback); + removeCache(pattern, callback); }; api.rmrf = function () { - cacheRemove(/./); + removeCache(/./); }; api.updateStart = function (details) { - let oldDelay = updateDelay; - let newDelay = details.delay || updateDefaultDelay; + let oldDelay = updateDelay; + let newDelay = details.delay || updateDefaultDelay; - updateDelay = Math.min(oldDelay, newDelay); + updateDelay = Math.min(oldDelay, newDelay); - if (updateStatus !== undefined) { - if (newDelay < oldDelay) { - clearTimeout(updateTimer); - updateTimer = vAPI.setTimeout(updateNext, updateDelay); - } - return; - } + if (updateStatus !== 'stop') { + if (newDelay < oldDelay) { + clearTimeout(updateTimer); + updateTimer = vAPI.setTimeout(updateNext, updateDelay); + } + return; + } - updateStart(); + updateStart(); }; api.updateStop = function () { - if (updateTimer) { - clearTimeout(updateTimer); - updateTimer = undefined; - } - if (updateStatus === 'running') { - updateEnd(); - } + if (updateTimer) { + clearTimeout(updateTimer); + updateTimer = undefined; + } + if (updateStatus === 'running') { + updateEnd(); + } }; api.checkVersion = function () { - let cache; - - let onSourceReady = function (registry) { - let source = JSON.parse(JSON.stringify(registry)); - let version = ηMatrix.userSettings.assetVersion; - - if (!version) { - ηMatrix.userSettings.assetVersion = 1; - version = 1; - } - - if (!source["assets.json"].version - || version > source["assets.json"].version) { - for (let key in source) { - switch (key) { - case "hphosts": - case "malware-0": - case "malware-1": - delete source[key]; - api.remove(key, function () {}); - break; - default: - break; - } - - source["assets.json"].version = version; - } - - let createRegistry = function () { - api.fetchText - (ηMatrix.assetsBootstrapLocation || 'assets/assets.json', - function (details) { - updateSourceRegistry(details.content, true); - }); - }; - - createRegistry(); - } - }; - - let onCacheReady = function (registry) { - cache = JSON.parse(JSON.stringify(registry)); - - getSourceRegistry(onSourceReady); - }; - - getCacheRegistry(onCacheReady); + let cache; + + let onSourceReady = function (registry) { + let source = JSON.parse(JSON.stringify(registry)); + let version = ηMatrix.userSettings.assetVersion; + + if (!version) { + ηMatrix.userSettings.assetVersion = 1; + version = 1; + } + + if (!source["assets.json"].version + || version > source["assets.json"].version) { + for (let key in source) { + switch (key) { + case "hphosts": + case "malware-0": + case "malware-1": + delete source[key]; + api.remove(key, function () {}); + break; + default: + break; + } + + source["assets.json"].version = version; + } + + let createRegistry = function () { + api.fetchText + (ηMatrix.assetsBootstrapLocation || 'assets/assets.json', + function (details) { + updateSourceRegistry(details.content, true); + }); + }; + + createRegistry(); + } + }; + + let onCacheReady = function (registry) { + cache = JSON.parse(JSON.stringify(registry)); + + getSourceRegistry(onSourceReady); + }; + + getCacheRegistry(onCacheReady); }; return api; })(); - -/******************************************************************************/ |