var Scaffold_Translators = { // Keep in sync with translator.js TRANSLATOR_TYPES: { import: 1, export: 2, web: 4, search: 8 }, _provider: null, _translators: new Map(), _translatorFiles: new Map(), _onLoadBeginListener: null, _onLoadCompleteListener: null, load: Zotero.serial(async function (reload) { if (this._translators.size && !reload) { Zotero.debug("Scaffold: Translators already loaded"); return { numLoaded: 0, numDeleted: 0 }; } if (this._onLoadBeginListener) this._onLoadBeginListener(); var t = new Date(); var dir = this.getDirectory(); var numLoaded = 0; var deletedTranslators = new Set(this._translatorFiles.keys()); await Zotero.File.iterateDirectory(dir, async function (entry) { if (entry.isDir || entry.name.startsWith('.') || !entry.name.endsWith('.js')) { return; } deletedTranslators.delete(entry.name); try { let fmtime; if ('winLastWriteDate' in entry) { fmtime = entry.winLastWriteDate.getTime(); } else { fmtime = (await IOUtils.stat(entry.path)).lastModified; } let translatorID = this._translatorFiles.get(entry.name); let loadFile = true; // If translator is already loaded, see if mtime has changed if (translatorID) { let mtime = this._translators.get(translatorID).mtime; if (mtime == fmtime) { loadFile = false; } } if (loadFile) { let translator = await Zotero.Translators.loadFromFile(entry.path); this._translators.set( translator.translatorID, { translator, filename: entry.name, mtime: fmtime, } ); this._translatorFiles.set(entry.name, translator.translatorID); numLoaded++; } } catch (e) { Zotero.logError(e); } }.bind(this)); Zotero.debug(`Scaffold: Loaded ${numLoaded} ${Zotero.Utilities.pluralize(numLoaded, 'translator')} ` + `in ${new Date() - t} ms`); for (let filename of deletedTranslators) { let id = this._translatorFiles.get(filename); let translator = this._translators.get(id); this._translatorFiles.delete(filename); // Filename won't match if translator was renamed if (translator.filename == filename) { this._translators.delete(id); } } if (this._onLoadCompleteListener) this._onLoadCompleteListener(); return { numLoaded, numDeleted: deletedTranslators.size }; }), deleteByID: async function (translatorID) { var translator = this._translators.get(translatorID); if (!translator) { Zotero.debug("Scaffold: Can't delete missing translator"); return; } await IOUtils.remove(PathUtils.join(this.getDirectory(), translator.filename)); this._translators.delete(translatorID); this._translatorFiles.delete(translator.filename); }, getDirectory: function () { return Zotero.Prefs.get('scaffold.translatorsDir'); }, getModifiedTime: function (translatorID) { return this._translators.get(translatorID)?.mtime; }, getProvider: function () { if (this._provider) { return this._provider; } this._provider = Zotero.Translators.makeTranslatorProvider({ get: function (translatorID) { if (!this._translators.size) { throw new Error("Scaffold: Translators not loaded"); } var translator = this._translators.get(translatorID); return translator ? translator.translator : false; }.bind(this), getCodeForTranslator: async function (translator) { if (translator.code) return translator.code; return Zotero.File.getContentsAsync(translator.path).then(function(code) { if (translator.cacheCode) { // See Translator.init() for cache rules translator.code = code; } return code; }); }.bind(this), getAllForType: async function (type) { if (!this._translators.size) { await this.load(); } return [...this._translators.values()] .map(x => x.translator) .filter(translator => translator.translatorType & this.TRANSLATOR_TYPES[type]); }.bind(this), getTranslatorsDirectory: function () { return this.getDirectory(); }.bind(this), reinit: async function (options = {}) { return this.load( true, options.filenames ); }.bind(this) }); return this._provider; }, setLoadListener({ onLoadBegin, onLoadComplete }) { this._onLoadBeginListener = onLoadBegin; this._onLoadCompleteListener = onLoadComplete; } };