www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit 540a54515ed3d06360a6ac991187b0425539955a
parent 5040fba8bbb8485cf783ef96112bda1ab2bba857
Author: Simon Kornblith <simon@simonster.com>
Date:   Fri, 16 Aug 2013 13:43:22 -0400

Async Zotero.Translators.get* and Zotero.Translate.getTranslators

This mostly works, but there is still much to be done:

- getTranslators() calls in preferences_export.js and quickCopy.js need
  to be made asynchronous
- getTranslators() calls in mimeTypeHandler.js need to be made
  asynchronous. This looks hard, since there is a synchronous Mozilla
  API involved
- I need to test lookup, the connector server, and export more to make
  sure that they actually work
- This API needs to be implemented for the connector and bookmarklet
- Translators should be read off the disk using async IO

Diffstat:
Mchrome/content/zotero/fileInterface.js | 119++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mchrome/content/zotero/lookup.js | 45+++++++++++++++++++++++----------------------
Mchrome/content/zotero/tools/testTranslators/testTranslators.js | 5+++--
Mchrome/content/zotero/tools/testTranslators/translatorTester.js | 5+++--
Mchrome/content/zotero/xpcom/schema.js | 6+++---
Mchrome/content/zotero/xpcom/server_connector.js | 11++++++++---
Mchrome/content/zotero/xpcom/translation/translate.js | 301++++++++++++++++++++++++++++++++++++-------------------------------------------
Mchrome/content/zotero/xpcom/translation/translator.js | 190+++++++++++++++++++++++++++++++++++++------------------------------------------
8 files changed, 325 insertions(+), 357 deletions(-)

diff --git 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 @@ -115,31 +115,32 @@ const Zotero_Lookup = new function () { translate.setSearch(item); // be lenient about translators - var translators = translate.getTranslators(); - translate.setTranslator(translators); - - 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")); + translate.getTranslators().then(function(translators) { + translate.setTranslator(translators); + + 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")); + } } - } - }); + }); - 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/tools/testTranslators/testTranslators.js b/chrome/content/zotero/tools/testTranslators/testTranslators.js @@ -466,12 +466,13 @@ function init() { // get translators, with code for unsupported translators if(!viewerMode) { - Zotero.Translators.getAllForType(translatorType, new function() { + Zotero.Translators.getAllForType(translatorType, true). + then(new function() { var type = translatorType; return function(translators) { haveTranslators(translators, type); } - }, true); + }); } } diff --git a/chrome/content/zotero/tools/testTranslators/translatorTester.js b/chrome/content/zotero/tools/testTranslators/translatorTester.js @@ -48,7 +48,8 @@ Zotero_TranslatorTesters = new function() { var testers = []; var waitingForTranslators = TEST_TYPES.length; for(var i=0; i<TEST_TYPES.length; i++) { - Zotero.Translators.getAllForType(TEST_TYPES[i], new function() { + Zotero.Translators.getAllForType(TEST_TYPES[i], true). + then(new function() { var type = TEST_TYPES[i]; return function(translators) { try { @@ -66,7 +67,7 @@ Zotero_TranslatorTesters = new function() { Zotero.logError(e); } }; - }, true); + }); }; }; diff --git a/chrome/content/zotero/xpcom/schema.js b/chrome/content/zotero/xpcom/schema.js @@ -1033,7 +1033,7 @@ Zotero.Schema = new function(){ var translatorsDir = Zotero.getTranslatorsDirectory(); translatorsDir.remove(true); Zotero.getTranslatorsDirectory(); // recreate directory - return Zotero.Translators.init() + return Zotero.Translators.reinit() .then(function () self.updateBundledFiles('translators', null, false)) .then(function () { var stylesDir = Zotero.getStylesDirectory(); @@ -1058,7 +1058,7 @@ Zotero.Schema = new function(){ var translatorsDir = Zotero.getTranslatorsDirectory(); translatorsDir.remove(true); Zotero.getTranslatorsDirectory(); // recreate directory - return Zotero.Translators.init() + return Zotero.Translators.reinit() .then(function () self.updateBundledFiles('translators', null, true)) .then(callback); } @@ -1535,7 +1535,7 @@ Zotero.Schema = new function(){ } // Rebuild caches - yield Zotero.Translators.init(); + yield Zotero.Translators.reinit(); Zotero.Styles.init(); } catch (e) { diff --git a/chrome/content/zotero/xpcom/server_connector.js b/chrome/content/zotero/xpcom/server_connector.js @@ -89,13 +89,18 @@ Zotero.Server.Connector.GetTranslators.prototype = { // Translator data if(data.url) { var me = this; - Zotero.Translators.getWebTranslatorsForLocation(data.url, function(data) { + Zotero.Translators.getWebTranslatorsForLocation(data.url).then(function(data) { sendResponseCallback(200, "application/json", JSON.stringify(me._serializeTranslators(data[0]))); }); } else { - var responseData = this._serializeTranslators(Zotero.Translators.getAll()); - sendResponseCallback(200, "application/json", JSON.stringify(responseData)); + Zotero.Translators.getAll().then(function(translators) { + var responseData = this._serializeTranslators(translators); + sendResponseCallback(200, "application/json", JSON.stringify(responseData)); + }).fail(function() { + sendResponseCallback(500); + throw e; + }).done(); } }, diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js @@ -295,13 +295,8 @@ Zotero.Translate.Sandbox = { var translatorsHandlerSet = false; safeTranslator.getTranslators = function() { if(!translation._handlers["translators"] || !translation._handlers["translators"].length) { - if(Zotero.isConnector) { - throw new Error('Translator must register a "translators" handler to '+ - 'call getTranslators() in this translation environment.'); - } else { - translate._debug('COMPAT WARNING: Translator must register a "translators" handler to '+ - 'call getTranslators() in connector'); - } + throw new Error('Translator must register a "translators" handler to '+ + 'call getTranslators() in this translation environment.'); } if(!translatorsHandlerSet) { translation.setHandler("translators", function() { @@ -331,76 +326,53 @@ Zotero.Translate.Sandbox = { if(callback) { translate.incrementAsyncProcesses("safeTranslator#getTranslatorObject()"); } else { - translate._debug("COMPAT WARNING: Translator must pass a callback to getTranslatorObject() to operate in connector"); + throw new Error("Translator must pass a callback to getTranslatorObject() to "+ + "operate in this translation environment."); } - var sandbox; - var haveTranslatorFunction = function(translator) { - translation.translator[0] = translator; - translation._loadTranslator(translator, function() { - if(Zotero.isFx && !Zotero.isBookmarklet) { - // do same origin check - var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"] - .getService(Components.interfaces.nsIScriptSecurityManager); - var ioService = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); - - var outerSandboxURI = ioService.newURI(typeof translate._sandboxLocation === "object" ? - translate._sandboxLocation.location : translate._sandboxLocation, null, null); - var innerSandboxURI = ioService.newURI(typeof translation._sandboxLocation === "object" ? - translation._sandboxLocation.location : translation._sandboxLocation, null, null); - - try { - secMan.checkSameOriginURI(outerSandboxURI, innerSandboxURI, false); - } catch(e) { - throw new Error("getTranslatorObject() may not be called from web or search "+ - "translators to web or search translators from different origins."); - } - } + var translator = translation.translator[0]; + translation._loadTranslator(translator, function() { + if(Zotero.isFx && !Zotero.isBookmarklet) { + // do same origin check + var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(Components.interfaces.nsIScriptSecurityManager); + var ioService = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); - translation._prepareTranslation(); - setDefaultHandlers(translate, translation); - sandbox = translation._sandboxManager.sandbox; - if(!Zotero.Utilities.isEmpty(sandbox.exports)) { - sandbox.exports.Zotero = sandbox.Zotero; - sandbox = sandbox.exports; - } else { - translate._debug("COMPAT WARNING: "+translation.translator[0].label+" does "+ - "not export any properties. Only detect"+translation._entryFunctionSuffix+ - " and do"+translation._entryFunctionSuffix+" will be available in "+ - "connectors."); - } + var outerSandboxURI = ioService.newURI(typeof translate._sandboxLocation === "object" ? + translate._sandboxLocation.location : translate._sandboxLocation, null, null); + var innerSandboxURI = ioService.newURI(typeof translation._sandboxLocation === "object" ? + translation._sandboxLocation.location : translation._sandboxLocation, null, null); - if(callback) { - try { - callback(sandbox); - } catch(e) { - translate.complete(false, e); - return; - } - translate.decrementAsyncProcesses("safeTranslator#getTranslatorObject()"); + try { + secMan.checkSameOriginURI(outerSandboxURI, innerSandboxURI, false); + } catch(e) { + throw new Error("getTranslatorObject() may not be called from web or search "+ + "translators to web or search translators from different origins."); } - }); - }; - - if(typeof translation.translator[0] === "object") { - haveTranslatorFunction(translation.translator[0]); - return translation._sandboxManager.sandbox; - } else { - if(Zotero.isConnector && (!Zotero.isFx || Zotero.isBookmarklet) && !callback) { - throw new Error("Translator must pass a callback to getTranslatorObject() to "+ - "operate in this translation environment."); } - Zotero.Translators.get(translation.translator[0], haveTranslatorFunction); - if(Zotero.isConnector && Zotero.isFx && !callback) { - while(!sandbox && translate._currentState) { - // This processNextEvent call is used to handle a deprecated case - Zotero.mainThread.processNextEvent(true); - } + translation._prepareTranslation(); + setDefaultHandlers(translate, translation); + sandbox = translation._sandboxManager.sandbox; + if(!Zotero.Utilities.isEmpty(sandbox.exports)) { + sandbox.exports.Zotero = sandbox.Zotero; + sandbox = sandbox.exports; + } else { + translate._debug("COMPAT WARNING: "+translation.translator[0].label+" does "+ + "not export any properties. Only detect"+translation._entryFunctionSuffix+ + " and do"+translation._entryFunctionSuffix+" will be available in "+ + "connectors."); } - if(sandbox) return sandbox; - } + + try { + callback(sandbox); + } catch(e) { + translate.complete(false, e); + return; + } + translate.decrementAsyncProcesses("safeTranslator#getTranslatorObject()"); + }); }; // TODO security is not super-tight here, as someone could pass something into arg @@ -1010,9 +982,11 @@ Zotero.Translate.Base.prototype = { * @param {Boolean} [checkSetTranslator] If true, the appropriate detect function is run on the * set document/text/etc. using the translator set by setTranslator. * getAllTranslators parameter is meaningless in this context. - * @return {Zotero.Translator[]} An array of {@link Zotero.Translator} objects + * @return {Promise} Promise for an array of {@link Zotero.Translator} objects */ "getTranslators":function(getAllTranslators, checkSetTranslator) { + var potentialTranslators; + // do not allow simultaneous instances of getTranslators if(this._currentState === "detect") throw new Error("getTranslators: detection is already running"); this._currentState = "detect"; @@ -1021,10 +995,10 @@ Zotero.Translate.Base.prototype = { if(checkSetTranslator) { // setTranslator must be called beforehand if checkSetTranslator is set if( !this.translator || !this.translator[0] ) { - throw new Error("getTranslators: translator must be set via setTranslator before calling" + - " getTranslators with the checkSetTranslator flag"); + return Q.reject(new Error("getTranslators: translator must be set via setTranslator before calling" + + " getTranslators with the checkSetTranslator flag")); } - var translators = new Array(); + var promises = new Array(); var t; for(var i=0, n=this.translator.length; i<n; i++) { if(typeof(this.translator[i]) == 'string') { @@ -1034,80 +1008,80 @@ Zotero.Translate.Base.prototype = { t = this.translator[i]; } /**TODO: check that the translator is of appropriate type?*/ - if(t) translators.push(t); + if(t) promises.push(t); } - if(!translators.length) throw new Error("getTranslators: no valid translators were set."); - this._getTranslatorsTranslatorsReceived(translators); + if(!promises.length) return Q.reject(new Error("getTranslators: no valid translators were set")); + potentialTranslators = Q.all(promises); } else { - this._getTranslatorsGetPotentialTranslators(); + potentialTranslators = this._getTranslatorsGetPotentialTranslators(); } // if detection returns immediately, return found translators - if(!this._currentState) return this._foundTranslators; - }, - - /** - * Get all potential translators - * @return {Zotero.Translator[]} - */ - "_getTranslatorsGetPotentialTranslators":function() { - var me = this; - Zotero.Translators.getAllForType(this.type, - function(translators) { me._getTranslatorsTranslatorsReceived(translators) }); - }, - - /** - * Called on completion of {@link #_getTranslatorsGetPotentialTranslators} call - */ - "_getTranslatorsTranslatorsReceived":function(allPotentialTranslators, properToProxyFunctions) { - this._potentialTranslators = []; - this._foundTranslators = []; - - // this gets passed out by Zotero.Translators.getWebTranslatorsForLocation() because it is - // specific for each translator, but we want to avoid making a copy of a translator whenever - // possible. - this._properToProxyFunctions = properToProxyFunctions ? properToProxyFunctions : null; - this._waitingForRPC = false; - - for(var i=0, n=allPotentialTranslators.length; i<n; i++) { - var translator = allPotentialTranslators[i]; - if(translator.runMode === Zotero.Translator.RUN_MODE_IN_BROWSER) { - this._potentialTranslators.push(translator); - } else if(this instanceof Zotero.Translate.Web && Zotero.Connector) { - this._waitingForRPC = true; + var me = this, deferred = Q.defer(); + return potentialTranslators.spread(function(allPotentialTranslators, properToProxyFunctions) { + me._potentialTranslators = []; + me._foundTranslators = []; + + // this gets passed out by Zotero.Translators.getWebTranslatorsForLocation() because it is + // specific for each translator, but we want to avoid making a copy of a translator whenever + // possible. + me._properToProxyFunctions = properToProxyFunctions ? properToProxyFunctions : null; + me._waitingForRPC = false; + + for(var i=0, n=allPotentialTranslators.length; i<n; i++) { + var translator = allPotentialTranslators[i]; + if(translator.runMode === Zotero.Translator.RUN_MODE_IN_BROWSER) { + me._potentialTranslators.push(translator); + } else if(me instanceof Zotero.Translate.Web && Zotero.Connector) { + me._waitingForRPC = true; + } } - } - - if(this._waitingForRPC) { - var me = this; - Zotero.Connector.callMethod("detect", {"uri":this.location.toString(), - "cookie":this.document.cookie, - "html":this.document.documentElement.innerHTML}, - function(returnValue) { me._getTranslatorsRPCComplete(returnValue) }); - } - - this._detect(); + + // Attach handler for translators, so that we can return a + // promise that provides them. + // TODO make me._detect() return a promise + var translatorsHandler = function(obj, translators) { + me.removeHandler("translators", translatorsHandler); + deferred.resolve(translators); + } + me.setHandler("translators", translatorsHandler); + me._detect(); + + if(me._waitingForRPC) { + // Try detect in Zotero Standalone. If this fails, it fails; we shouldn't + // get hung up about it. + Zotero.Connector.callMethod("detect", {"uri":me.location.toString(), + "cookie":me.document.cookie, + "html":me.document.documentElement.innerHTML}).then(function(rpcTranslators) { + me._waitingForRPC = false; + + // if there are translators, add them to the list of found translators + if(rpcTranslators) { + for(var i=0, n=rpcTranslators.length; i<n; i++) { + rpcTranslators[i].runMode = Zotero.Translator.RUN_MODE_ZOTERO_STANDALONE; + } + me._foundTranslators = me._foundTranslators.concat(rpcTranslators); + } + + // call _detectTranslatorsCollected to return detected translators + if(me._currentState === null) { + me._detectTranslatorsCollected(); + } + }); + } + }).fail(function(e) { + Zotero.logError(e); + me.complete(false, e); + }).then(deferred.promise); }, - + /** - * Called on completion of detect RPC for - * {@link Zotero.Translate.Base#_getTranslatorsTranslatorsReceived} + * Get all potential translators (without running detect) + * @return {Promise} Promise for an array of {@link Zotero.Translator} objects */ - "_getTranslatorsRPCComplete":function(rpcTranslators) { - this._waitingForRPC = false; - - // if there are translators, add them to the list of found translators - if(rpcTranslators) { - for(var i=0, n=rpcTranslators.length; i<n; i++) { - rpcTranslators[i].runMode = Zotero.Translator.RUN_MODE_ZOTERO_STANDALONE; - } - this._foundTranslators = this._foundTranslators.concat(rpcTranslators); - } - - // call _detectTranslatorsCollected to return detected translators - if(this._currentState === null) { - this._detectTranslatorsCollected(); - } + "_getTranslatorsGetPotentialTranslators":function() { + return Zotero.Translators.getAllForType(this.type). + then(function(translators) { return [translators] }); }, /** @@ -1139,11 +1113,11 @@ Zotero.Translate.Base.prototype = { this._loadTranslator(this.translator[0], function() { me._translateTranslatorLoaded() }); } else { // need to get translator first - Zotero.Translators.get(this.translator[0], - function(translator) { - me.translator[0] = translator; - me._loadTranslator(translator, function() { me._translateTranslatorLoaded() }); - }); + Zotero.Translators.get(this.translator[0]). + then(function(translator) { + me.translator[0] = translator; + me._loadTranslator(translator, function() { me._translateTranslatorLoaded() }); + }); } }, @@ -1682,13 +1656,7 @@ Zotero.Translate.Web.prototype.setLocation = function(location) { * Get potential web translators */ Zotero.Translate.Web.prototype._getTranslatorsGetPotentialTranslators = function() { - var me = this; - Zotero.Translators.getWebTranslatorsForLocation(this.location, - function(data) { - // data[0] = list of translators - // data[1] = list of functions to convert proper URIs to proxied URIs - me._getTranslatorsTranslatorsReceived(data[0], data[1]); - }); + return Zotero.Translators.getWebTranslatorsForLocation(this.location); } /** @@ -1920,13 +1888,10 @@ Zotero.Translate.Import.prototype.complete = function(returnValue, error) { * Get all potential import translators, ordering translators with the right file extension first */ Zotero.Translate.Import.prototype._getTranslatorsGetPotentialTranslators = function() { - if(this.location) { - var me = this; - Zotero.Translators.getImportTranslatorsForLocation(this.location, - function(translators) { me._getTranslatorsTranslatorsReceived(translators) }); - } else { - Zotero.Translate.Base.prototype._getTranslatorsGetPotentialTranslators.call(this); - } + return (this.location ? + Zotero.Translators.getImportTranslatorsForLocation(this.location) : + Zotero.Translators.getAllForType(this.type)). + then(function(translators) { return [translators] });; } /** @@ -1938,12 +1903,13 @@ Zotero.Translate.Import.prototype.getTranslators = function() { if(this._currentState === "detect") throw new Error("getTranslators: detection is already running"); this._currentState = "detect"; var me = this; - Zotero.Translators.getAllForType(this.type, function(translators) { + return Zotero.Translators.getAllForType(this.type). + then(function(translators) { me._potentialTranslators = []; me._foundTranslators = translators; me.complete(true); + return me._foundTranslators; }); - if(this._currentState === null) return this._foundTranslators; } else { return Zotero.Translate.Base.prototype.getTranslators.call(this); } @@ -2113,12 +2079,17 @@ Zotero.Translate.Export.prototype.complete = function(returnValue, error) { * Overload {@link Zotero.Translate.Base#getTranslators} to return all translators immediately */ Zotero.Translate.Export.prototype.getTranslators = function() { - if(this._currentState === "detect") throw new Error("getTranslators: detection is already running"); - this._currentState = "detect"; - this._foundTranslators = Zotero.Translators.getAllForType(this.type); - this._potentialTranslators = []; - this.complete(true); - return this._foundTranslators; + if(this._currentState === "detect") { + return Q.reject(new Error("getTranslators: detection is already running")); + } + var me = this; + return Zotero.Translators.getAllForType(this.type).then(function(translators) { + me._currentState = "detect"; + me._foundTranslators = translators; + me._potentialTranslators = []; + me.complete(true); + return me._foundTranslators; + }); } /** diff --git a/chrome/content/zotero/xpcom/translation/translator.js b/chrome/content/zotero/xpcom/translation/translator.js @@ -37,9 +37,7 @@ Zotero.Translators = new function() { /** * Initializes translator cache, loading all relevant translators into memory */ - this.init = Q.async(function() { - _initialized = true; - + this.reinit = Q.async(function() { var start = (new Date()).getTime(); var transactionStarted = false; @@ -136,6 +134,7 @@ Zotero.Translators = new function() { Zotero.debug("Cached "+i+" translators in "+((new Date()).getTime() - start)+" ms"); }); + this.init = Zotero.lazy(this.reinit); /** * Gets the translator that corresponds to a given ID @@ -144,15 +143,10 @@ Zotero.Translators = new function() { * 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; + this.get = function(id) { + return this.init().then(function() { + return _translators[id] ? _translators[id] : false + }); } /** @@ -162,23 +156,19 @@ Zotero.Translators = new function() { * 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; + 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() { - if(!_initialized) this.init(); - return [translator for each(translator in _translators)]; + return this.init().then(function() { + return [translator for each(translator in _translators)]; + }); } /** @@ -191,76 +181,73 @@ Zotero.Translators = new function() { * 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]; + return this.getAllForType("web").then(function(allTranslators) { + var potentialTranslators = []; + + var properHosts = []; + var proxyHosts = []; - // 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<hostnames.length-2; i++) { - if(TLDS[hostnames[i].toLowerCase()]) { - var properHost = hostnames.slice(0, i+1).join("."); - searchURIs.push(m[1]+properHost+uri.substr(m[0].length)); - properHosts.push(properHost); - proxyHosts.push(hostnames.slice(i+1).join(".")); + 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<hostnames.length-2; i++) { + if(TLDS[hostnames[i].toLowerCase()]) { + var properHost = hostnames.slice(0, i+1).join("."); + searchURIs.push(m[1]+properHost+uri.substr(m[0].length)); + properHosts.push(properHost); + proxyHosts.push(hostnames.slice(i+1).join(".")); + } } } } - } - - Zotero.debug("Translators: Looking for translators for "+searchURIs.join(", ")); - - var converterFunctions = []; - for(var i=0; i<allTranslators.length; i++) { - for(var j=0; j<searchURIs.length; j++) { - if((!allTranslators[i].webRegexp - && allTranslators[i].runMode === Zotero.Translator.RUN_MODE_IN_BROWSER) - || (uri.length < 8192 && allTranslators[i].webRegexp.test(searchURIs[j]))) { - // add translator to list - potentialTranslators.push(allTranslators[i]); - - if(j === 0) { - if(knownProxy) { - converterFunctions.push(Zotero.Proxies.properToProxy); + + Zotero.debug("Translators: Looking for translators for "+searchURIs.join(", ")); + + var converterFunctions = []; + for(var i=0; i<allTranslators.length; i++) { + for(var j=0; j<searchURIs.length; j++) { + if((!allTranslators[i].webRegexp + && allTranslators[i].runMode === Zotero.Translator.RUN_MODE_IN_BROWSER) + || (uri.length < 8192 && allTranslators[i].webRegexp.test(searchURIs[j]))) { + // add translator to list + potentialTranslators.push(allTranslators[i]); + + if(j === 0) { + if(knownProxy) { + converterFunctions.push(Zotero.Proxies.properToProxy); + } else { + converterFunctions.push(null); + } } else { - converterFunctions.push(null); + converterFunctions.push(new function() { + var re = new RegExp('^https?://(?:[^/]\\.)?'+Zotero.Utilities.quotemeta(properHosts[j-1]), "gi"); + var proxyHost = proxyHosts[j-1].replace(/\$/g, "$$$$"); + return function(uri) { return uri.replace(re, "$&."+proxyHost) }; + }); } - } else { - converterFunctions.push(new function() { - var re = new RegExp('^https?://(?:[^/]\\.)?'+Zotero.Utilities.quotemeta(properHosts[j-1]), "gi"); - var proxyHost = proxyHosts[j-1].replace(/\$/g, "$$$$"); - return function(uri) { return uri.replace(re, "$&."+proxyHost) }; - }); + + // don't add translator more than once + break; } - - // don't add translator more than once - break; } } - } - - if(callback) { - callback([potentialTranslators, converterFunctions]); - return true; - } - return potentialTranslators; + + return [potentialTranslators, converterFunctions]; + }); } /** @@ -271,24 +258,25 @@ Zotero.Translators = new function() { * returned. */ this.getImportTranslatorsForLocation = function(location, callback) { - var allTranslators = Zotero.Translators.getAllForType("import"); - var tier1Translators = []; - var tier2Translators = []; - - for(var i=0; i<allTranslators.length; i++) { - if(allTranslators[i].importRegexp && allTranslators[i].importRegexp.test(location)) { - tier1Translators.push(allTranslators[i]); - } else { - tier2Translators.push(allTranslators[i]); + return Zotero.Translators.getAllForType("import").then(function(allTranslators) { + var tier1Translators = []; + var tier2Translators = []; + + for(var i=0; i<allTranslators.length; i++) { + if(allTranslators[i].importRegexp && allTranslators[i].importRegexp.test(location)) { + tier1Translators.push(allTranslators[i]); + } else { + tier2Translators.push(allTranslators[i]); + } } - } - - var translators = tier1Translators.concat(tier2Translators); - if(callback) { - callback(translators); - return true; - } - return translators; + + var translators = tier1Translators.concat(tier2Translators); + if(callback) { + callback(translators); + return true; + } + return translators; + }); } /**