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:
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;
+ });
}
/**