diff --git a/chrome/content/zotero/fileInterface.js b/chrome/content/zotero/fileInterface.js index 225f52fc62..31c9c0282c 100644 --- a/chrome/content/zotero/fileInterface.js +++ b/chrome/content/zotero/fileInterface.js @@ -41,55 +41,56 @@ var Zotero_File_Exporter = function() { * Performs the actual export operation **/ Zotero_File_Exporter.prototype.save = function() { - var translation = new Zotero.Translate.Export(); - var translators = translation.getTranslators(); - - // present options dialog - var io = {translators:translators} - window.openDialog("chrome://zotero/content/exportOptions.xul", - "_blank", "chrome,modal,centerscreen,resizable=no", io); - if(!io.selectedTranslator) { - return false; - } - - const nsIFilePicker = Components.interfaces.nsIFilePicker; - var fp = Components.classes["@mozilla.org/filepicker;1"] - .createInstance(nsIFilePicker); - fp.init(window, Zotero.getString("fileInterface.export"), nsIFilePicker.modeSave); - - // set file name and extension - if(io.displayOptions.exportFileData) { - // if the result will be a folder, don't append any extension or use - // filters - fp.defaultString = this.name; - fp.appendFilters(Components.interfaces.nsIFilePicker.filterAll); - } else { - // if the result will be a file, append an extension and use filters - fp.defaultString = this.name+(io.selectedTranslator.target ? "."+io.selectedTranslator.target : ""); - fp.defaultExtension = io.selectedTranslator.target; - fp.appendFilter(io.selectedTranslator.label, "*."+(io.selectedTranslator.target ? io.selectedTranslator.target : "*")); - } - - var rv = fp.show(); - if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) { - if(this.collection) { - translation.setCollection(this.collection); - } else if(this.items) { - translation.setItems(this.items); + var translation = new Zotero.Translate.Export(), + me = this; + translation.getTranslators().then(function(translators) { + // present options dialog + var io = {translators:translators} + window.openDialog("chrome://zotero/content/exportOptions.xul", + "_blank", "chrome,modal,centerscreen,resizable=no", io); + if(!io.selectedTranslator) { + return false; } - translation.setLocation(fp.file); - translation.setTranslator(io.selectedTranslator); - translation.setDisplayOptions(io.displayOptions); - translation.setHandler("itemDone", Zotero_File_Interface.updateProgress); - translation.setHandler("done", this._exportDone); - Zotero.UnresponsiveScriptIndicator.disable(); - Zotero_File_Interface.Progress.show( - Zotero.getString("fileInterface.itemsExported") - ); - translation.translate() - } - return false; + const nsIFilePicker = Components.interfaces.nsIFilePicker; + var fp = Components.classes["@mozilla.org/filepicker;1"] + .createInstance(nsIFilePicker); + fp.init(window, Zotero.getString("fileInterface.export"), nsIFilePicker.modeSave); + + // set file name and extension + if(io.displayOptions.exportFileData) { + // if the result will be a folder, don't append any extension or use + // filters + fp.defaultString = me.name; + fp.appendFilters(Components.interfaces.nsIFilePicker.filterAll); + } else { + // if the result will be a file, append an extension and use filters + fp.defaultString = me.name+(io.selectedTranslator.target ? "."+io.selectedTranslator.target : ""); + fp.defaultExtension = io.selectedTranslator.target; + fp.appendFilter(io.selectedTranslator.label, "*."+(io.selectedTranslator.target ? io.selectedTranslator.target : "*")); + } + + var rv = fp.show(); + if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) { + if(me.collection) { + translation.setCollection(me.collection); + } else if(me.items) { + translation.setItems(me.items); + } + + translation.setLocation(fp.file); + translation.setTranslator(io.selectedTranslator); + translation.setDisplayOptions(io.displayOptions); + translation.setHandler("itemDone", Zotero_File_Interface.updateProgress); + translation.setHandler("done", me._exportDone); + Zotero.UnresponsiveScriptIndicator.disable(); + Zotero_File_Interface.Progress.show( + Zotero.getString("fileInterface.itemsExported") + ); + translation.translate() + } + return false; + }).done(); } /* @@ -207,9 +208,7 @@ var Zotero_File_Interface = new function() { } var translation = new Zotero.Translate.Import(); - if(!file) { - var translators = translation.getTranslators(); - + (file ? Q(file) : translation.getTranslators().then(function(translators) { const nsIFilePicker = Components.interfaces.nsIFilePicker; var fp = Components.classes["@mozilla.org/filepicker;1"] .createInstance(nsIFilePicker); @@ -225,15 +224,17 @@ var Zotero_File_Interface = new function() { return false; } - file = fp.file; - } - - translation.setLocation(file); - // get translators again, bc now we can check against the file - translation.setHandler("translators", function(obj, item) { - _importTranslatorsAvailable(obj, item, createNewCollection); - }); - translators = translation.getTranslators(); + return fp.file; + })).then(function(file) { + if(!file) return; // no file if user cancelled + + translation.setLocation(file); + // get translators again, bc now we can check against the file + translation.setHandler("translators", function(obj, item) { + _importTranslatorsAvailable(obj, item, createNewCollection); + }); + translators = translation.getTranslators(); + }).done(); } diff --git a/chrome/content/zotero/lookup.js b/chrome/content/zotero/lookup.js index 6fc52b44dd..21cf2c96f3 100644 --- a/chrome/content/zotero/lookup.js +++ b/chrome/content/zotero/lookup.js @@ -115,31 +115,32 @@ const Zotero_Lookup = new function () { translate.setSearch(item); // be lenient about translators - var translators = translate.getTranslators(); - translate.setTranslator(translators); + translate.getTranslators().then(function(translators) { + translate.setTranslator(translators); - translate.setHandler("done", function(translate, success) { - notDone--; - successful += success; + translate.setHandler("done", function(translate, success) { + notDone--; + successful += success; - if(!notDone) { //i.e. done - Zotero_Lookup.toggleProgress(false); - if(successful) { - document.getElementById("zotero-lookup-panel").hidePopup(); - } else { - var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - prompts.alert(window, Zotero.getString("lookup.failure.title"), - Zotero.getString("lookup.failure.description")); + if(!notDone) { //i.e. done + Zotero_Lookup.toggleProgress(false); + if(successful) { + document.getElementById("zotero-lookup-panel").hidePopup(); + } else { + var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + prompts.alert(window, Zotero.getString("lookup.failure.title"), + Zotero.getString("lookup.failure.description")); + } } - } - }); + }); - translate.setHandler("itemDone", function(obj, item) { - if(collection) collection.addItem(item.id); + translate.setHandler("itemDone", function(obj, item) { + if(collection) collection.addItem(item.id); + }); + + translate.translate(libraryID); }); - - translate.translate(libraryID); })(item); } diff --git a/chrome/content/zotero/preferences/preferences_export.js b/chrome/content/zotero/preferences/preferences_export.js index 4a4befbe5a..72e86a4595 100644 --- a/chrome/content/zotero/preferences/preferences_export.js +++ b/chrome/content/zotero/preferences/preferences_export.js @@ -45,8 +45,8 @@ Zotero_Preferences.Export = { // Initialize default format drop-down var format = Zotero.Prefs.get("export.quickCopy.setting"); var menulist = document.getElementById("zotero-quickCopy-menu"); - this.buildQuickCopyFormatDropDown(menulist, Zotero.QuickCopy.getContentType(format), format); menulist.setAttribute('preference', "pref-quickCopy-setting"); + this.buildQuickCopyFormatDropDown(menulist, Zotero.QuickCopy.getContentType(format), format); this.updateQuickCopyHTMLCheckbox(); if (!Zotero.isStandalone) { @@ -106,30 +106,30 @@ Zotero_Preferences.Export = { // add export formats to list var translation = new Zotero.Translate("export"); - var translators = translation.getTranslators(); - - for (var i=0; i b.priority) { - return 1; - } - else if (a.priority < b.priority) { - return -1; - } - return collation.compareString(1, a.label, b.label); - } - for(var type in _cache) { - _cache[type].sort(cmp); - } - - Zotero.debug("Cached "+i+" translators in "+((new Date()).getTime() - start)+" ms"); - }); - - /** - * Gets the translator that corresponds to a given ID - * @param {String} id The ID of the translator - * @param {Function} [callback] An optional callback to be executed when translators have been - * retrieved. If no callback is specified, translators are - * returned. - */ - this.get = function(id, callback) { - if(!_initialized) this.init(); - var translator = _translators[id] ? _translators[id] : false; - - if(callback) { - callback(translator); - return true; - } - return translator; - } - - /** - * Gets all translators for a specific type of translation - * @param {String} type The type of translators to get (import, export, web, or search) - * @param {Function} [callback] An optional callback to be executed when translators have been - * retrieved. If no callback is specified, translators are - * returned. - */ - this.getAllForType = function(type, callback) { - if(!_initialized) this.init() - - var translators = _cache[type].slice(0); - if(callback) { - callback(translators); - return true; - } - return translators; - } - - /** - * Gets all translators for a specific type of translation - */ - this.getAll = function() { - if(!_initialized) this.init(); - return [translator for each(translator in _translators)]; - } - - /** - * Gets web translators for a specific location - * @param {String} uri The URI for which to look for translators - * @param {Function} [callback] An optional callback to be executed when translators have been - * retrieved. If no callback is specified, translators are - * returned. The callback is passed a set of functions for - * converting URLs from proper to proxied forms as the second - * argument. - */ - this.getWebTranslatorsForLocation = function(uri, callback) { - var allTranslators = this.getAllForType("web"); - var potentialTranslators = []; - - var properHosts = []; - var proxyHosts = []; - - var properURI = Zotero.Proxies.proxyToProper(uri); - var knownProxy = properURI !== uri; - if(knownProxy) { - // if we know this proxy, just use the proper URI for detection - var searchURIs = [properURI]; - } else { - var searchURIs = [uri]; - - // if there is a subdomain that is also a TLD, also test against URI with the domain - // dropped after the TLD - // (i.e., www.nature.com.mutex.gmu.edu => www.nature.com) - var m = /^(https?:\/\/)([^\/]+)/i.exec(uri); - if(m) { - // First, drop the 0- if it exists (this is an III invention) - var host = m[2]; - if(host.substr(0, 2) === "0-") host = host.substr(2); - var hostnames = host.split("."); - for(var i=1; i} - */ - this.save = function(metadata, code) { - if (!metadata.translatorID) { - throw ("metadata.translatorID not provided in Zotero.Translators.save()"); - } - - if (!metadata.translatorType) { - var found = false; - for each(var type in TRANSLATOR_TYPES) { - if (metadata.translatorType & type) { - found = true; - break; - } - } - if (!found) { - throw ("Invalid translatorType '" + metadata.translatorType + "' in Zotero.Translators.save()"); - } - } - - if (!metadata.label) { - throw ("metadata.label not provided in Zotero.Translators.save()"); - } - - if (!metadata.priority) { - throw ("metadata.priority not provided in Zotero.Translators.save()"); - } - - if (!metadata.lastUpdated) { - throw ("metadata.lastUpdated not provided in Zotero.Translators.save()"); - } - - if (!code) { - throw ("code not provided in Zotero.Translators.save()"); - } - - var fileName = Zotero.Translators.getFileNameFromLabel( - metadata.label, metadata.translatorID - ); - var destFile = Zotero.getTranslatorsDirectory(); - destFile.append(fileName); - - // JSON.stringify has the benefit of indenting JSON - var metadataJSON = JSON.stringify(metadata, null, "\t"); - - var str = metadataJSON + "\n\n" + code; - - var translator = Zotero.Translators.get(metadata.translatorID); - if (translator && destFile.equals(translator.file)) { - var sameFile = true; - } - - return Q.fcall(function () { - if (sameFile) return; - - return Q(OS.File.exists(destFile.path)) - .then(function (exists) { - if (exists) { - var msg = "Overwriting translator with same filename '" - + fileName + "'"; - Zotero.debug(msg, 1); - Zotero.debug(metadata, 1); - Components.utils.reportError(msg); - } - }); - }) - .then(function () { - if (!translator) return; - - return Q(OS.File.exists(translator.file.path)) - .then(function (exists) { - translator.file.remove(false); - }); - }) - .then(function () { - Zotero.debug("Saving translator '" + metadata.label + "'"); - Zotero.debug(str); - return Zotero.File.putContentsAsync(destFile, str) - .thenResolve(destFile); - }); - } - - this.cacheInDB = function(fileName, metadataJSON, code, lastModifiedTime) { - return Zotero.DB.queryAsync( - "REPLACE INTO translatorCache VALUES (?, ?, ?, ?)", - [fileName, metadataJSON, code, lastModifiedTime] - ); - } -} +// Properties required for every translator +var TRANSLATOR_REQUIRED_PROPERTIES = ["translatorID", "translatorType", "label", "creator", + "target", "priority", "lastUpdated"]; +// Properties that are preserved if present +var TRANSLATOR_OPTIONAL_PROPERTIES = ["browserSupport", "minVersion", "maxVersion", + "inRepository", "configOptions", "displayOptions", + "hiddenPrefs"]; +// Properties that are passed from background to inject page in connector +var TRANSLATOR_PASSING_PROPERTIES = TRANSLATOR_REQUIRED_PROPERTIES. + concat(["browserSupport", "code", "runMode"]); +// Properties that are saved in connector if set but not required +var TRANSLATOR_SAVE_PROPERTIES = TRANSLATOR_REQUIRED_PROPERTIES.concat(["browserSupport"]); /** * @class Represents an individual translator * @constructor - * @param {nsIFile} file File from which to generate a translator object * @property {String} translatorID Unique GUID of the translator * @property {Integer} translatorType Type of the translator (use bitwise & with TRANSLATOR_TYPES to read) * @property {String} label Human-readable name of the translator @@ -436,124 +60,118 @@ Zotero.Translators = new function() { * @property {Boolean} inRepository Whether the translator may be found in the repository * @property {String} lastUpdated SQL-style date and time of translator's last update * @property {String} code The executable JavaScript for the translator + * @property {Boolean} cacheCode Whether to cache code for this session (non-connector only) + * @property {nsIFile} [file] File corresponding to this translator (non-connector only) */ -Zotero.Translator = function(file, json, code) { - const codeGetterFunction = function() { return Zotero.File.getContents(this.file); } - // Maximum length for the info JSON in a translator - const MAX_INFO_LENGTH = 4096; - const infoRe = /^\s*{[\S\s]*?}\s*?[\r\n]/; - - this.file = file; - - var fStream, cStream; - if(json) { - var info = JSON.parse(json); - } else { - fStream = Components.classes["@mozilla.org/network/file-input-stream;1"]. - createInstance(Components.interfaces.nsIFileInputStream); - cStream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. - createInstance(Components.interfaces.nsIConverterInputStream); - fStream.init(file, -1, -1, 0); - cStream.init(fStream, "UTF-8", 8192, - Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); - - var str = {}; - cStream.readString(MAX_INFO_LENGTH, str); - - var m = infoRe.exec(str.value); - if (!m) { - this.logError("Invalid or missing translator metadata JSON object in " + file.leafName); - fStream.close(); - return; - } - - this.metadataString = m[0]; - - try { - var info = JSON.parse(this.metadataString); - } catch(e) { - this.logError("Invalid or missing translator metadata JSON object in " + file.leafName); - fStream.close(); - return; - } - } - - var haveMetadata = true; +Zotero.Translator = function(info) { + this.init(info); +} + +/** + * Initializes a translator from a set of info, clearing code if it is set + */ +Zotero.Translator.prototype.init = function(info) { // make sure we have all the properties - for each(var property in ["translatorID", "translatorType", "label", "creator", "target", "minVersion", "maxVersion", "priority", "lastUpdated", "inRepository"]) { + for(var i=0; i. + + ***** END LICENSE BLOCK ***** +*/ + +// Enumeration of types of translators +const TRANSLATOR_TYPES = {"import":1, "export":2, "web":4, "search":8}; + +/** + * Singleton to handle loading and caching of translators + * @namespace + */ +Zotero.Translators = new function() { + var _cache, _translators; + var _initialized = false; + + /** + * Initializes translator cache, loading all relevant translators into memory + */ + this.reinit = Q.async(function() { + var start = (new Date()).getTime(); + var transactionStarted = false; + + _cache = {"import":[], "export":[], "web":[], "search":[]}; + _translators = {}; + + var dbCacheResults = yield Zotero.DB.queryAsync("SELECT leafName, translatorJSON, "+ + "code, lastModifiedTime FROM translatorCache"); + var dbCache = {}; + for each(var cacheEntry in dbCacheResults) { + dbCache[cacheEntry.leafName] = cacheEntry; + } + + var i = 0; + var filesInCache = {}; + var contents = Zotero.getTranslatorsDirectory().directoryEntries; + while(contents.hasMoreElements()) { + var file = contents.getNext().QueryInterface(Components.interfaces.nsIFile); + var leafName = file.leafName; + if(!(/^[^.].*\.js$/.test(leafName))) continue; + var lastModifiedTime = file.lastModifiedTime; + + var dbCacheEntry = false; + if(dbCache[leafName]) { + filesInCache[leafName] = true; + if(dbCache[leafName].lastModifiedTime == lastModifiedTime) { + dbCacheEntry = dbCache[file.leafName]; + } + } + + if(dbCacheEntry) { + // get JSON from cache if possible + var translator = Zotero.Translators.load(file, dbCacheEntry.translatorJSON, dbCacheEntry.code); + filesInCache[leafName] = true; + } else { + // otherwise, load from file + var translator = yield Zotero.Translators.loadFromFile(file); + } + + if(translator.translatorID) { + if(_translators[translator.translatorID]) { + // same translator is already cached + translator.logError('Translator with ID '+ + translator.translatorID+' already loaded from "'+ + _translators[translator.translatorID].file.leafName+'"'); + } else { + // add to cache + _translators[translator.translatorID] = translator; + for(var type in TRANSLATOR_TYPES) { + if(translator.translatorType & TRANSLATOR_TYPES[type]) { + _cache[type].push(translator); + } + } + + if(!dbCacheEntry) { + var code = yield translator.getCode(); + yield Zotero.Translators.cacheInDB( + leafName, + translator.serialize(TRANSLATOR_REQUIRED_PROPERTIES. + concat(TRANSLATOR_OPTIONAL_PROPERTIES)), + translator.cacheCode ? translator.code : null, + lastModifiedTime + ); + delete translator.metadataString; + } + } + } + + i++; + } + + // Remove translators from DB as necessary + for(var leafName in dbCache) { + if(!filesInCache[leafName]) { + yield Zotero.DB.queryAsync( + "DELETE FROM translatorCache WHERE leafName = ?", [leafName] + ); + } + } + + // Sort by priority + var collation = Zotero.getLocaleCollation(); + var cmp = function (a, b) { + if (a.priority > b.priority) { + return 1; + } + else if (a.priority < b.priority) { + return -1; + } + return collation.compareString(1, a.label, b.label); + } + for(var type in _cache) { + _cache[type].sort(cmp); + } + + Zotero.debug("Cached "+i+" translators in "+((new Date()).getTime() - start)+" ms"); + }); + this.init = Zotero.lazy(this.reinit); + + /** + * Loads a translator from JSON, with optional code + */ + this.load = function(file, json, code) { + var info = JSON.parse(json); + info.file = file; + info.code = code; + return new Zotero.Translator(info); + } + + /** + * Loads a translator from the disk + */ + this.loadFromDisk = function(file) { + const infoRe = /^\s*{[\S\s]*?}\s*?[\r\n]/; + Zotero.File.getContentsAsync(this.file).then(function(source) { + return Zotero.Translators.load(file, infoRe.exec(source)[0], source); + }).fail(function() { + throw "Invalid or missing translator metadata JSON object in " + file.leafName; + }); + } + + /** + * Gets the translator that corresponds to a given ID + * @param {String} id The ID of the translator + * @param {Function} [callback] An optional callback to be executed when translators have been + * retrieved. If no callback is specified, translators are + * returned. + */ + this.get = function(id) { + return this.init().then(function() { + return _translators[id] ? _translators[id] : false + }); + } + + /** + * Gets all translators for a specific type of translation + * @param {String} type The type of translators to get (import, export, web, or search) + * @param {Function} [callback] An optional callback to be executed when translators have been + * retrieved. If no callback is specified, translators are + * returned. + */ + this.getAllForType = function(type) { + return this.init().then(function() { + return _cache[type].slice(); + }); + } + + /** + * Gets all translators for a specific type of translation + */ + this.getAll = function() { + return this.init().then(function() { + return [translator for each(translator in _translators)]; + }); + } + + /** + * Gets web translators for a specific location + * @param {String} uri The URI for which to look for translators + * @param {Function} [callback] An optional callback to be executed when translators have been + * retrieved. If no callback is specified, translators are + * returned. The callback is passed a set of functions for + * converting URLs from proper to proxied forms as the second + * argument. + */ + this.getWebTranslatorsForLocation = function(uri, callback) { + return this.getAllForType("web").then(function(allTranslators) { + var potentialTranslators = []; + + var properHosts = []; + var proxyHosts = []; + + var properURI = Zotero.Proxies.proxyToProper(uri); + var knownProxy = properURI !== uri; + if(knownProxy) { + // if we know this proxy, just use the proper URI for detection + var searchURIs = [properURI]; + } else { + var searchURIs = [uri]; + + // if there is a subdomain that is also a TLD, also test against URI with the domain + // dropped after the TLD + // (i.e., www.nature.com.mutex.gmu.edu => www.nature.com) + var m = /^(https?:\/\/)([^\/]+)/i.exec(uri); + if(m) { + // First, drop the 0- if it exists (this is an III invention) + var host = m[2]; + if(host.substr(0, 2) === "0-") host = host.substr(2); + var hostnames = host.split("."); + for(var i=1; i} + */ + this.save = function(metadata, code) { + if (!metadata.translatorID) { + throw ("metadata.translatorID not provided in Zotero.Translators.save()"); + } + + if (!metadata.translatorType) { + var found = false; + for each(var type in TRANSLATOR_TYPES) { + if (metadata.translatorType & type) { + found = true; + break; + } + } + if (!found) { + throw ("Invalid translatorType '" + metadata.translatorType + "' in Zotero.Translators.save()"); + } + } + + if (!metadata.label) { + throw ("metadata.label not provided in Zotero.Translators.save()"); + } + + if (!metadata.priority) { + throw ("metadata.priority not provided in Zotero.Translators.save()"); + } + + if (!metadata.lastUpdated) { + throw ("metadata.lastUpdated not provided in Zotero.Translators.save()"); + } + + if (!code) { + throw ("code not provided in Zotero.Translators.save()"); + } + + var fileName = Zotero.Translators.getFileNameFromLabel( + metadata.label, metadata.translatorID + ); + var destFile = Zotero.getTranslatorsDirectory(); + destFile.append(fileName); + + // JSON.stringify has the benefit of indenting JSON + var metadataJSON = JSON.stringify(metadata, null, "\t"); + + var str = metadataJSON + "\n\n" + code, + translator; + + return Zotero.Translators.get(metadata.translatorID) + .then(function(gTranslator) { + translator = gTranslator; + var sameFile = translator && destFile.equals(translator.file); + if (sameFile) return; + + return Q(OS.File.exists(destFile.path)) + .then(function (exists) { + if (exists) { + var msg = "Overwriting translator with same filename '" + + fileName + "'"; + Zotero.debug(msg, 1); + Zotero.debug(metadata, 1); + Components.utils.reportError(msg); + } + }); + }) + .then(function () { + if (!translator) return; + + return Q(OS.File.exists(translator.file.path)) + .then(function (exists) { + translator.file.remove(false); + }); + }) + .then(function () { + Zotero.debug("Saving translator '" + metadata.label + "'"); + Zotero.debug(str); + return Zotero.File.putContentsAsync(destFile, str) + .thenResolve(destFile); + }); + } + + this.cacheInDB = function(fileName, metadataJSON, code, lastModifiedTime) { + return Zotero.DB.queryAsync( + "REPLACE INTO translatorCache VALUES (?, ?, ?, ?)", + [fileName, metadataJSON, code, lastModifiedTime] + ); + } +} diff --git a/chrome/content/zotero/xpcom/utilities_internal.js b/chrome/content/zotero/xpcom/utilities_internal.js index af9ed28d2a..b363d2dc3b 100644 --- a/chrome/content/zotero/xpcom/utilities_internal.js +++ b/chrome/content/zotero/xpcom/utilities_internal.js @@ -96,7 +96,6 @@ Zotero.Utilities.Internal = { * rather than hex string */ "md5Async": function (file, base64) { - Components.utils.import("resource://gre/modules/osfile.jsm"); const CHUNK_SIZE = 16384; var deferred = Q.defer(); diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js index aa80c47a52..5c760502ea 100644 --- a/chrome/content/zotero/xpcom/zotero.js +++ b/chrome/content/zotero/xpcom/zotero.js @@ -44,6 +44,7 @@ const ZOTERO_CONFIG = { Components.utils.import("resource://zotero/q.js"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/osfile.jsm"); /* * Core functions diff --git a/components/zotero-service.js b/components/zotero-service.js index c3270846f8..4569b3e0f7 100644 --- a/components/zotero-service.js +++ b/components/zotero-service.js @@ -44,6 +44,7 @@ const xpcomFilesAll = [ 'progressWindow', 'translation/translate', 'translation/translate_firefox', + 'translation/translator', 'translation/tlds', 'utilities', 'utilities_internal', @@ -104,7 +105,7 @@ const xpcomFilesLocal = [ 'timeline', 'uri', 'translation/translate_item', - 'translation/translator', + 'translation/translators', 'server_connector' ];