commit 94d0bcf2296de792e79679f372a3355b40f80d6f
parent 2d4c81b1efa95d2e83d2c099c884a86208c4ca5f
Author: Simon Kornblith <simon@simonster.com>
Date: Mon, 20 Jun 2011 18:57:33 +0000
Closes #1783, Eliminate Zotero.done()
Diffstat:
2 files changed, 131 insertions(+), 63 deletions(-)
diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js
@@ -206,12 +206,19 @@ Zotero.Translate.Sandbox = {
}
};
safeTranslator.getTranslators = function() { return translation.getTranslators() };
+ var doneHandlerSet = false;
safeTranslator.translate = function() {
+ translate.incrementAsyncProcesses();
setDefaultHandlers(translate, translation);
+ if(!doneHandlerSet) {
+ doneHandlerSet = true;
+ translation.setHandler("done", function() { translate.decrementAsyncProcesses() });
+ }
return translation.translate(false);
};
// TODO
safeTranslator.getTranslatorObject = function(callback) {
+ if(callback) translate.incrementAsyncProcesses();
var haveTranslatorFunction = function(translator) {
translation.translator[0] = translator;
if(!Zotero._loadTranslator(translator)) throw "Translator could not be loaded";
@@ -239,7 +246,10 @@ Zotero.Translate.Sandbox = {
translation._prepareTranslation();
setDefaultHandlers(translate, translation);
- if(callback) callback(translation._sandboxManager.sandbox);
+ if(callback) {
+ callback(translation._sandboxManager.sandbox);
+ translate.decrementAsyncProcesses();
+ }
};
if(typeof translation.translator[0] === "object") {
@@ -265,26 +275,18 @@ Zotero.Translate.Sandbox = {
/**
* Enables asynchronous detection or translation
* @param {Zotero.Translate} translate
+ * @deprecated
*/
- "wait":function(translate) {
- if(translate._currentState == "translate") {
- translate._waitForCompletion = true;
- } else {
- throw "Translate: cannot call Zotero.wait() in detectCode of non-web translators";
- }
- },
+ "wait":function(translate) {},
/**
* Completes asynchronous detection or translation
*
* @param {Zotero.Translate} translate
- * @param {Boolean|String} [val] Whether detection or translation completed successfully.
- * For detection, this should be a string or false. For translation, this should be
- * boolean.
- * @param {String} [error] The error string, if an error occurred.
+ * @deprecated
*/
- "done":function(translate, val, error) {
- translate.complete(typeof val === "undefined" ? true : val, (error ? error : "No error message specified"));
+ "done":function(translate, returnValue) {
+ this._returnValue = returnValue;
},
/**
@@ -318,42 +320,61 @@ Zotero.Translate.Sandbox = {
// if we have a set of selected items for this translation, use them
return translate._selectedItems;
} else if(translate._handlers.select) {
- var haveAsyncCallback = !!callback;
- var haveAsyncHandler = false;
- var returnedItems = null;
-
- // if this translator doesn't provide an async callback for selectItems, set things
- // up so that we can wait to see if the select handler returns synchronously. If it
- // doesn't, we will need to restart translation.
- if(!haveAsyncCallback) {
- callback = function(selectedItems) {
- if(haveAsyncHandler) {
- translate.translate(this._libraryID, this._saveAttachments, selectedItems);
- } else {
- returnedItems = selectedItems;
- }
- };
- }
-
- translate._runHandler("select", items, callback);
-
- if(!haveAsyncCallback) {
- translate._debug("WARNING: No callback was provided for "+
- "Zotero.selectItems(). When executed outside of Firefox, a selectItems() call "+
- "will require that this translator to be called multiple times.", 1);
+ // whether the translator supports asynchronous selectItems
+ var haveAsyncCallback = !!callback;
+ // whether the handler operates asynchronously
+ var haveAsyncHandler = false;
+ var returnedItems = null;
- if(returnedItems === null) {
- // The select handler is asynchronous, but this translator doesn't support
- // asynchronous select. We return false to abort translation in this
- // instance, and we will restart it later when the selectItems call is
- // complete.
- haveAsyncHandler = true;
+ var callbackExecuted = false;
+ if(haveAsyncCallback) {
+ // if this translator provides an async callback for selectItems, rig things
+ // up to pop off the async process
+ var newCallback = function(selectedItems) {
+ callbackExecuted = true;
+ callback(selectedItems);
+ if(haveAsyncHandler) translate.decrementAsyncProcesses();
+ };
+ } else {
+ // if this translator doesn't provide an async callback for selectItems, set things
+ // up so that we can wait to see if the select handler returns synchronously. If it
+ // doesn't, we will need to restart translation.
+ var newCallback = function(selectedItems) {
+ callbackExecuted = true;
+ if(haveAsyncHandler) {
+ translate.translate(this._libraryID, this._saveAttachments, selectedItems);
+ } else {
+ returnedItems = selectedItems;
+ }
+ };
+ }
+
+ translate._runHandler("select", items, newCallback);
+
+ // if we don't have returnedItems set already, the handler is asynchronous
+ haveAsyncHandler = !callbackExecuted;
+
+ if(haveAsyncCallback) {
+ // we are running asynchronously, so increment async processes
+ if(haveAsyncHandler) translate.incrementAsyncProcesses();
return false;
} else {
- return returnedItems;
+ translate._debug("WARNING: No callback was provided for "+
+ "Zotero.selectItems(). When executed outside of Firefox, a selectItems() call "+
+ "will require that this translator to be called multiple times.", 1);
+
+ if(haveAsyncHandler) {
+ // The select handler is asynchronous, but this translator doesn't support
+ // asynchronous select. We return false to abort translation in this
+ // instance, and we will restart it later when the selectItems call is
+ // complete.
+ translate._aborted = true;
+ return false;
+ } else {
+ return returnedItems;
+ }
}
- }
- } else { // no handler defined; assume they want all of them
+ } else { // no handler defined; assume they want all of them
if(callback) callback(items);
return items;
}
@@ -395,6 +416,10 @@ Zotero.Translate.Sandbox = {
item.accessDate = "CURRENT_TIMESTAMP";
}
+ if(!item.title) {
+ throw "No title specified for item";
+ }
+
// create short title
if(item.shortTitle === undefined && Zotero.Utilities.fieldIsValidForType("shortTitle", item.itemType)) {
// only set if changes have been made
@@ -422,14 +447,6 @@ Zotero.Translate.Sandbox = {
// call super
Zotero.Translate.Sandbox.Base._itemDone(translate, item);
- },
-
- /**
- * Overloads {@link Zotero.Sandbox.Base.wait} to allow asynchronous detect
- * @param {Zotero.Translate} translate
- */
- "wait":function(translate) {
- translate._waitForCompletion = true;
}
},
@@ -535,6 +552,8 @@ Zotero.Translate.Sandbox = {
* @property {String} path The path or URI string of the target
* @property {String} newItems Items created when translate() was called
* @property {String} newCollections Collections created when translate() was called
+ * @property {Number} runningAsyncProcesses The number of async processes that are running. These
+ * need to terminate before Zotero.done() is called.
*/
Zotero.Translate.Base = function() {}
Zotero.Translate.Base.prototype = {
@@ -640,13 +659,34 @@ Zotero.Translate.Base.prototype = {
this._handlers[type].push(handler);
},
- /*
+ /**
* Clears all handlers for a given function
* @param {String} type See {@link Zotero.Translate.Base#setHandler} for valid values
*/
"clearHandlers":function(type) {
this._handlers[type] = new Array();
},
+
+ /**
+ * Indicates that a new async process is running
+ */
+ "incrementAsyncProcesses":function() {
+ this._runningAsyncProcesses++;
+ Zotero.debug("Translate: Incremented asynchronous processes to "+this._runningAsyncProcesses, 4);
+ if(this._parentTranslator) this._parentTranslator.incrementAsyncProcesses();
+ },
+
+ /**
+ * Indicates that a new async process is finished
+ */
+ "decrementAsyncProcesses":function() {
+ this._runningAsyncProcesses--;
+ Zotero.debug("Translate: Decremented asynchronous processes to "+this._runningAsyncProcesses, 4);
+ if(this._runningAsyncProcesses === 0) {
+ this.complete();
+ }
+ if(this._parentTranslator) this._parentTranslator.decrementAsyncProcesses();
+ },
/**
* Clears all handlers for a given function
@@ -821,6 +861,8 @@ Zotero.Translate.Base.prototype = {
Zotero.debug("Translate: Beginning translation with "+this.translator[0].label);
+ this.incrementAsyncProcesses();
+
// translate
try {
this._sandboxManager.sandbox["do"+this._entryFunctionSuffix].apply(null, this._getParameters());
@@ -833,7 +875,7 @@ Zotero.Translate.Base.prototype = {
}
}
- if(!this._waitForCompletion) this.complete(true);
+ this.decrementAsyncProcesses();
},
/**
@@ -845,16 +887,25 @@ Zotero.Translate.Base.prototype = {
* completed successfully.
*/
"complete":function(returnValue, error) {
+ // allow translation to be aborted for re-running after selecting items
+ if(this._aborted) return;
+
// Make sure this isn't called twice
if(this._currentState === null) {
- Zotero.debug("Translate: WARNING: Zotero.done() called after translation completion; please fix your code");
+ Zotero.debug("Translate: WARNING: Zotero.done() called after translation completion; this should never happen");
+ try {
+ a
+ } catch(e) {
+ Zotero.debug(e);
+ }
return;
}
var oldState = this._currentState;
- this._waitForCompletion = false;
+ this._runningAsyncProcesses = 0;
+ if(!returnValue && this._returnValue) returnValue = this._returnValue;
var errorString = null;
- if(!returnValue) errorString = this._generateErrorString(error);
+ if(!returnValue && error) errorString = this._generateErrorString(error);
if(oldState === "detect") {
if(this._potentialTranslators.length) {
@@ -880,6 +931,9 @@ Zotero.Translate.Base.prototype = {
} else {
this._currentState = null;
+ // unset return value is equivalent to true
+ if(returnValue === undefined) returnValue = true;
+
if(returnValue) {
this._debug("Translation successful");
} else {
@@ -919,6 +973,8 @@ Zotero.Translate.Base.prototype = {
}
this._prepareDetection();
+ this.incrementAsyncProcesses();
+
try {
var returnValue = this._sandboxManager.sandbox["detect"+this._entryFunctionSuffix].apply(null, this._getParameters());
} catch(e) {
@@ -926,7 +982,8 @@ Zotero.Translate.Base.prototype = {
return;
}
- if(!this._waitForCompletion) this.complete(returnValue);
+ if(returnValue !== undefined) this._returnValue = returnValue;
+ this.decrementAsyncProcesses();
},
/**
@@ -949,7 +1006,10 @@ Zotero.Translate.Base.prototype = {
this._sandboxLocation = sandboxLocation;
this._generateSandbox();
}
- this._waitForCompletion = false;
+
+ this._runningAsyncProcesses = 0;
+ this._returnValue = undefined;
+ this._aborted = false;
Zotero.debug("Translate: Parsing code for "+translator.label, 4);
@@ -1317,7 +1377,6 @@ Zotero.Translate.Import.prototype._loadTranslator = function(translator) {
var returnVal = Zotero.Translate.Base.prototype._loadTranslator.call(this, translator);
if(!returnVal) return returnVal;
- this._waitForCompletion = false;
var dataMode = (translator ? translator : this._potentialTranslators[0]).configOptions["dataMode"];
var err = false;
diff --git a/chrome/content/zotero/xpcom/utilities.js b/chrome/content/zotero/xpcom/utilities.js
@@ -782,7 +782,12 @@ Zotero.Utilities.Translate.prototype.processDocuments = function(urls, processor
}
}
- Zotero.HTTP.processDocuments(urls, processor, done, exception);
+ var translate = this._translate;
+ translate.incrementAsyncProcesses();
+ Zotero.HTTP.processDocuments(urls, processor, function() {
+ done();
+ translate.decrementAsyncProcesses();
+ }, exception);
}
/**
@@ -897,6 +902,7 @@ Zotero.Utilities.Translate.prototype.doGet = function(urls, processor, done, res
var me = this;
+ this._translate.incrementAsyncProcesses();
Zotero.HTTP.doGet(url, function(xmlhttp) {
try {
if(processor) {
@@ -910,6 +916,7 @@ Zotero.Utilities.Translate.prototype.doGet = function(urls, processor, done, res
done();
}
}
+ me._translate.decrementAsyncProcesses();
} catch(e) {
me._translate.complete(false, e);
}
@@ -924,9 +931,11 @@ Zotero.Utilities.Translate.prototype.doPost = function(url, body, onDone, header
url = this._convertURL(url);
var translate = this._translate;
+ this._translate.incrementAsyncProcesses();
Zotero.HTTP.doPost(url, body, function(xmlhttp) {
try {
onDone(xmlhttp.responseText, xmlhttp);
+ translate.decrementAsyncProcesses();
} catch(e) {
translate.complete(false, e);
}