commit e3a9c6779bc0ffea8093cbd19d67f1730325baa2
parent b18c580dac9135053989817b0d616fd21afc8c6d
Author: Dan Stillman <dstillman@zotero.org>
Date: Mon, 5 Sep 2016 20:41:35 -0400
Restore connector mode functionality in Firefox
Non-Zotero for Firefox connector code will probably need to be updated
to handle these changes.
Diffstat:
11 files changed, 189 insertions(+), 157 deletions(-)
diff --git a/chrome/content/zotero/browser.js b/chrome/content/zotero/browser.js
@@ -613,7 +613,9 @@ var Zotero_Browser = new function() {
return;
}
- yield Zotero.DB.waitForTransaction();
+ if (!Zotero.isConnector) {
+ yield Zotero.DB.waitForTransaction();
+ }
Zotero_Browser.progress.show();
Zotero_Browser.isScraping = true;
@@ -635,14 +637,16 @@ var Zotero_Browser = new function() {
collection = ZoteroPane.getSelectedCollection();
}
- if (libraryID === Zotero.Libraries.publicationsLibraryID) {
- Zotero_Browser.progress.Translation.cannotAddToPublications();
- return;
- }
-
- if (Zotero.Feeds.get(libraryID)) {
- Zotero_Browser.progress.Translation.cannotAddToFeed();
- return;
+ if (!Zotero.isConnector) {
+ if (libraryID === Zotero.Libraries.publicationsLibraryID) {
+ Zotero_Browser.progress.Translation.cannotAddToPublications();
+ return;
+ }
+
+ if (Zotero.Feeds.get(libraryID)) {
+ Zotero_Browser.progress.Translation.cannotAddToFeed();
+ return;
+ }
}
Zotero_Browser.progress.Translation.scrapingTo(libraryID, collection);
diff --git a/chrome/content/zotero/icon.js b/chrome/content/zotero/icon.js
@@ -28,6 +28,11 @@
Components.utils.import("resource://zotero/config.js");
Components.utils.import("resource:///modules/CustomizableUI.jsm");
+// Necessary for connector mode, for some reason
+var Zotero = Components.classes["@zotero.org/Zotero;1"]
+ .getService(Components.interfaces.nsISupports)
+ .wrappedJSObject;
+
var comboButtonsID = 'zotero-toolbar-buttons';
CustomizableUI.addListener({
diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js
@@ -42,6 +42,10 @@ var ZoteroOverlay = new function()
var self = this;
var iconLoaded = false;
+ if (Zotero.isConnector) {
+ return;
+ }
+
Zotero.Promise.try(function () {
if (!Zotero) {
throw new Error("No Zotero object");
diff --git a/chrome/content/zotero/xpcom/connector/repo.js b/chrome/content/zotero/xpcom/connector/repo.js
@@ -55,34 +55,54 @@ Zotero.Repo = new function() {
/**
* Get translator code from repository
* @param {String} translatorID ID of the translator to retrieve code for
- * @param {Function} callback Callback to pass code when retreived
*/
- this.getTranslatorCode = function(translatorID, callback) {
+ this.getTranslatorCode = Zotero.Promise.method(function (translatorID) {
+ var deferred = Zotero.Promise.defer();
+
// try standalone
Zotero.Connector.callMethod("getTranslatorCode", {"translatorID":translatorID}, function(result) {
if(result) {
- _haveCode(result, translatorID, Zotero.Repo.SOURCE_ZOTERO_STANDALONE, callback);
+ deferred.resolve(
+ Zotero.Promise.all(
+ [
+ _haveCode(result, translatorID),
+ Zotero.Repo.SOURCE_ZOTERO_STANDALONE
+ ]
+ )
+ );
return;
}
+
// then try repo
- Zotero.HTTP.doGet(ZOTERO_CONFIG.REPOSITORY_URL + "code/" + translatorID + "?version=" + Zotero.version,
+ Zotero.HTTP.doGet(
+ ZOTERO_CONFIG.REPOSITORY_URL + "code/" + translatorID + "?version=" + Zotero.version,
function(xmlhttp) {
- _haveCode(xmlhttp.status === 200 ? xmlhttp.responseText : false, translatorID,
- Zotero.Repo.SOURCE_REPO, callback);
+ deferred.resolve(
+ Zotero.Promise.all(
+ [
+ _haveCode(
+ xmlhttp.status === 200 ? xmlhttp.responseText : false,
+ translatorID
+ ),
+ Zotero.Repo.SOURCE_REPO
+ ]
+ )
+ );
}
);
});
- };
+
+ return deferred.promise;
+ });
/**
* Called when code has been retrieved from standalone or repo
*/
- function _haveCode(code, translatorID, source, callback) {
+ function _haveCode(code, translatorID) {
if(!code) {
Zotero.logError(new Error("Code could not be retrieved for " + translatorID));
- callback(false);
- return;
+ return false;
}
if(!Zotero.isFx) {
@@ -91,16 +111,14 @@ Zotero.Repo = new function() {
var m = infoRe.exec(code);
if (!m) {
Zotero.logError(new Error("Invalid or missing translator metadata JSON object for " + translatorID));
- callback(false);
- return;
+ return false;
}
try {
var metadata = JSON.parse(m[0]);
} catch(e) {
Zotero.logError(new Error("Invalid or missing translator metadata JSON object for " + translatorID));
- callback(false);
- return;
+ return false;
}
var translator = Zotero.Translators.getWithoutCode(translatorID);
@@ -114,7 +132,7 @@ Zotero.Repo = new function() {
}
}
}
- callback(code, source);
+ return code;
}
/**
diff --git a/chrome/content/zotero/xpcom/connector/translator.js b/chrome/content/zotero/xpcom/connector/translator.js
@@ -24,7 +24,7 @@
*/
// Enumeration of types of translators
-const TRANSLATOR_TYPES = {"import":1, "export":2, "web":4, "search":8};
+var TRANSLATOR_TYPES = {"import":1, "export":2, "web":4, "search":8};
/**
* Singleton to handle loading and caching of translators
@@ -92,9 +92,6 @@ Zotero.Translators = new function() {
/**
* Gets the translator that corresponds to a given ID, without attempting to retrieve code
* @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.getWithoutCode = function(id) {
if(!_initialized) Zotero.Translators.init();
@@ -103,54 +100,46 @@ Zotero.Translators = new function() {
/**
* 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) {
+ this.get = Zotero.Promise.method(function (id) {
if(!_initialized) Zotero.Translators.init();
var translator = _translators[id];
if(!translator) {
- callback(false);
return false;
}
// only need to get code if it is of some use
if(translator.runMode === Zotero.Translator.RUN_MODE_IN_BROWSER
&& !translator.hasOwnProperty("code")) {
- translator.getCode(function() { callback(translator) });
+ return translator.getCode().then(() => translator);
} else {
- callback(translator);
+ 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 A required callback to be executed when translators have been
- * retrieved.
* @param {Boolean} [debugMode] Whether to assume debugging mode. If true, code is included for
* unsupported translators, and code originally retrieved from the
* repo is re-retrieved from Zotero Standalone.
*/
- this.getAllForType = function(type, callback, debugMode) {
+ this.getAllForType = Zotero.Promise.method(function (type, debugMode) {
if(!_initialized) Zotero.Translators.init()
var translators = _cache[type].slice(0);
- new Zotero.Translators.CodeGetter(translators, callback, translators, debugMode);
- return true;
- }
+ var codeGetter = new Zotero.Translators.CodeGetter(translators, debugMode);
+ return codeGetter.getAll();
+ });
/**
* 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.
+ * @return {Promise<Array[]>} - A promise for a 2-item array containing an array of translators and
+ * an array of functions for converting URLs from proper to proxied forms
*/
- this.getWebTranslatorsForLocation = function(uri, callback) {
+ this.getWebTranslatorsForLocation = Zotero.Promise.method(function (uri) {
if(!_initialized) Zotero.Translators.init();
var allTranslators = _cache["web"];
var potentialTranslators = [];
@@ -215,10 +204,11 @@ Zotero.Translators = new function() {
}
}
- new Zotero.Translators.CodeGetter(potentialTranslators, callback,
- [potentialTranslators, converterFunctions]);
- return true;
- }
+ var codeGetter = new Zotero.Translators.CodeGetter(potentialTranslators);
+ return codeGetter.getAll().then(function () {
+ return [potentialTranslators, converterFunctions];
+ });
+ });
/**
* Converts translators to JSON-serializable objects
@@ -331,29 +321,16 @@ Zotero.Translators = new function() {
* A class to get the code for a set of translators at once
*
* @param {Zotero.Translator[]} translators Translators for which to retrieve code
- * @param {Function} callback Callback to call once code has been retrieved
- * @param {Function} callbackArgs All arguments to be passed to callback (including translators)
* @param {Boolean} [debugMode] If true, include code for unsupported translators
*/
-Zotero.Translators.CodeGetter = function(translators, callback, callbackArgs, debugMode) {
+Zotero.Translators.CodeGetter = function(translators, debugMode) {
this._translators = translators;
- this._callbackArgs = callbackArgs;
- this._callback = callback;
this._debugMode = debugMode;
- this.getCodeFor(0);
}
-Zotero.Translators.CodeGetter.prototype.getCodeFor = function(i) {
- var me = this;
- while(true) {
- if(i === this._translators.length) {
- // all done; run callback
- this._callback(this._callbackArgs);
- return;
- }
-
- var translator = this._translators[i];
-
+Zotero.Translators.CodeGetter.prototype.getAll = Zotero.Promise.method(function () {
+ var translators = [];
+ for (let translator of this._translators) {
// retrieve code if no code and translator is supported locally
if((translator.runMode === Zotero.Translator.RUN_MODE_IN_BROWSER && !translator.hasOwnProperty("code"))
// or if debug mode is enabled (even if unsupported locally)
@@ -362,17 +339,13 @@ Zotero.Translators.CodeGetter.prototype.getCodeFor = function(i) {
// include test cases)
|| (Zotero.Repo && translator.codeSource === Zotero.Repo.SOURCE_REPO)))) {
// get next translator
- translator.getCode(function() { me.getCodeFor(i+1) });
- return;
+ translators.push(translator.getCode());
}
-
- // if we are not at end of list and there is no reason to retrieve the code, keep going
- // through the list of potential translators
- i++;
}
-}
+ return Zotero.Promise.all(translators);
+});
-const TRANSLATOR_REQUIRED_PROPERTIES = ["translatorID", "translatorType", "label", "creator", "target",
+var TRANSLATOR_REQUIRED_PROPERTIES = ["translatorID", "translatorType", "label", "creator", "target",
"priority", "lastUpdated"];
var TRANSLATOR_PASSING_PROPERTIES = TRANSLATOR_REQUIRED_PROPERTIES
.concat(["browserSupport", "code", "runMode", "itemType"]);
@@ -453,21 +426,23 @@ Zotero.Translator.prototype.init = function(info) {
/**
* Retrieves code for this translator
+ *
+ * @return {Promise<String|false>} - Promise for translator code or false if none
*/
-Zotero.Translator.prototype.getCode = function(callback) {
- var me = this;
- Zotero.Repo.getTranslatorCode(this.translatorID,
- function(code, source) {
- if(!code) {
- callback(false);
- } else {
- // cache code for session only (we have standalone anyway)
- me.code = code;
- me.codeSource = source;
- callback(true);
- }
+Zotero.Translator.prototype.getCode = function () {
+ return Zotero.Repo.getTranslatorCode(this.translatorID)
+ .then(function (args) {
+ var code = args[0];
+ var source = args[1];
+ if (!code) {
+ return false;
}
- );
+
+ // cache code for session only (we have standalone anyway)
+ this.code = code;
+ this.codeSource = source;
+ return code;
+ }.bind(this));
}
/**
diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js
@@ -1082,7 +1082,7 @@ Zotero.Translate.Base.prototype = {
* getAllTranslators parameter is meaningless in this context.
* @return {Promise} Promise for an array of {@link Zotero.Translator} objects
*/
- "getTranslators":function(getAllTranslators, checkSetTranslator) {
+ getTranslators: Zotero.Promise.method(function (getAllTranslators, checkSetTranslator) {
var potentialTranslators;
// do not allow simultaneous instances of getTranslators
@@ -1115,68 +1115,74 @@ Zotero.Translate.Base.prototype = {
}
// if detection returns immediately, return found translators
- var me = this;
return potentialTranslators.then(function(result) {
var allPotentialTranslators = result[0];
var properToProxyFunctions = result[1];
- me._potentialTranslators = [];
- me._foundTranslators = [];
+ 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.
- me._properToProxyFunctions = properToProxyFunctions ? properToProxyFunctions : null;
- me._waitingForRPC = false;
+ 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) {
- me._potentialTranslators.push(translator);
- } else if(me instanceof Zotero.Translate.Web && Zotero.Connector) {
- me._waitingForRPC = true;
+ this._potentialTranslators.push(translator);
+ } else if (this instanceof Zotero.Translate.Web && Zotero.Connector) {
+ this._waitingForRPC = true;
}
}
// Attach handler for translators, so that we can return a
// promise that provides them.
- // TODO make me._detect() return a promise
- var deferred = Zotero.Promise.defer(),
- translatorsHandler = function(obj, translators) {
- me.removeHandler("translators", translatorsHandler);
- deferred.resolve(translators);
- }
- me.setHandler("translators", translatorsHandler);
- me._detect();
-
- if(me._waitingForRPC) {
+ // TODO make this._detect() return a promise
+ var deferred = Zotero.Promise.defer();
+ var translatorsHandler = function(obj, translators) {
+ this.removeHandler("translators", translatorsHandler);
+ deferred.resolve(translators);
+ }.bind(this);
+ this.setHandler("translators", translatorsHandler);
+ this._detect();
+
+ if(this._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;
+ Zotero.Connector.callMethod(
+ "detect",
+ {
+ uri: this.location.toString(),
+ cookie: this.document.cookie,
+ html: this.document.documentElement.innerHTML
+ },
+ 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;
}
- me._foundTranslators = me._foundTranslators.concat(rpcTranslators);
+ this._foundTranslators = this._foundTranslators.concat(rpcTranslators);
}
// call _detectTranslatorsCollected to return detected translators
- if(me._currentState === null) {
- me._detectTranslatorsCollected();
+ if (this._currentState === null) {
+ this._detectTranslatorsCollected();
}
- });
+ }.bind(this)
+ );
}
return deferred.promise;
- }).catch(function(e) {
+ }.bind(this))
+ .catch(function(e) {
Zotero.logError(e);
- me.complete(false, e);
- });
- },
+ this.complete(false, e);
+ }.bind(this));
+ }),
/**
* Get all potential translators (without running detect)
@@ -1199,7 +1205,7 @@ Zotero.Translate.Base.prototype = {
* @returns {Promise} Promise resolved with saved items
* when translation complete
*/
- "translate": function (options = {}, ...args) { // initialize properties specific to each translation
+ translate: Zotero.Promise.method(function (options = {}, ...args) { // initialize properties specific to each translation
if (typeof options == 'number') {
Zotero.debug("Translate: translate() now takes an object -- update your code", 2);
options = {
@@ -1256,19 +1262,28 @@ Zotero.Translate.Base.prototype = {
this.translator[0] = Zotero.Translators.get(this.translator[0]);
}
- var loadPromise = this._loadTranslator(this.translator[0]);
- if (this.noWait) {
- if (!loadPromise.isResolved()) {
- return Zotero.Promise.reject(new Error("Load promise is not resolved in noWait mode"));
- }
- this._translateTranslatorLoaded();
- }
- else {
- loadPromise.then(() => this._translateTranslatorLoaded());
+ // Zotero.Translators.get() returns a promise in the connectors
+ if (this.noWait && this.translator[0].then && !this.translator[0].isResolved()) {
+ throw new Error("Translator promise is not resolved in noWait mode");
}
-
+
+ Zotero.Promise.resolve(this.translator[0])
+ .then(function (translator) {
+ this.translator[0] = translator;
+ var loadPromise = this._loadTranslator(translator);
+ if (this.noWait) {
+ if (!loadPromise.isResolved()) {
+ return Zotero.Promise.reject(new Error("Load promise is not resolved in noWait mode"));
+ }
+ this._translateTranslatorLoaded();
+ }
+ else {
+ loadPromise.then(() => this._translateTranslatorLoaded());
+ }
+ }.bind(this));
+
return deferred.promise;
- },
+ }),
/**
* Called when translator has been retrieved and loaded
@@ -1586,7 +1601,7 @@ Zotero.Translate.Base.prototype = {
*/
"_checkIfDone":function() {
if(!this._savingItems && !this._savingAttachments.length && (!this._currentState || this._waitingForSave)) {
- if(this.newCollections) {
+ if(this.newCollections && this._itemSaver.saveCollections) {
var me = this;
this._itemSaver.saveCollections(this.newCollections).then(function (newCollections) {
me.newCollections = newCollections;
@@ -1659,7 +1674,7 @@ Zotero.Translate.Base.prototype = {
/**
* Loads the translator into its sandbox
* @param {Zotero.Translator} translator
- * @return {Boolean} Whether the translator could be successfully loaded
+ * @return {Promise<Boolean>} Whether the translator could be successfully loaded
*/
"_loadTranslator": Zotero.Promise.method(function (translator) {
var sandboxLocation = this._getSandboxLocation();
diff --git a/chrome/content/zotero/xpcom/translation/translator.js b/chrome/content/zotero/xpcom/translation/translator.js
@@ -140,36 +140,36 @@ Zotero.Translator.prototype.init = function(info) {
/**
* Load code for a translator
*/
-Zotero.Translator.prototype.getCode = function() {
- if(this.code) return Zotero.Promise.resolve(this.code);
+Zotero.Translator.prototype.getCode = Zotero.Promise.method(function () {
+ if (this.code) return this.code;
- var me = this;
if(Zotero.isConnector) {
- // TODO make this a promise
- return Zotero.Repo.getTranslatorCode(this.translatorID).
- spread(function(code, source) {
+ return Zotero.Repo.getTranslatorCode(this.translatorID)
+ .then(function (args) {
+ var code = args[0];
+ var source = args[1];
if(!code) {
- throw "Code for "+me.label+" could not be retrieved";
+ throw new Error("Code for " + this.label + " could not be retrieved");
}
// Cache any translators for session, since retrieving via
// HTTP may be expensive
- me.code = code;
- me.codeSource = source;
+ this.code = code;
+ this.codeSource = source;
return code;
- });
+ }.bind(this));
} else {
var promise = Zotero.File.getContentsAsync(this.path);
if(this.cacheCode) {
// Cache target-less web translators for session, since we
// will use them a lot
- promise.then(function(code) {
- me.code = code;
+ return promise.then(function(code) {
+ this.code = code;
return code;
- });
+ }.bind(this));
}
return promise;
}
-}
+});
/**
* Get metadata block for a translator
diff --git a/chrome/content/zotero/xpcom/translation/translators.js b/chrome/content/zotero/xpcom/translation/translators.js
@@ -260,7 +260,7 @@ Zotero.Translators = new function() {
*/
this.getAll = function() {
return this.init().then(function () {
- return Object.keys(_translators);
+ return Object.keys(_translators).map(id => _translators[id]);
});
}
diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js
@@ -460,7 +460,16 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
this.initializationDeferred.resolve();
if(Zotero.isConnector) {
+ // Add toolbar icon
+ try {
+ Services.scriptloader.loadSubScript("chrome://zotero/content/icon.js", {}, "UTF-8");
+ }
+ catch (e) {
+ Zotero.logError(e);
+ }
+
Zotero.Repo.init();
+ Zotero.locked = false;
}
if(!Zotero.isFirstLoadThisSession) {
diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js
@@ -140,7 +140,7 @@ var ZoteroPane = new function()
}
/**
- * Called on window load or when has been reloaded after switching into or out of connector
+ * Called on window load or when pane has been reloaded after switching into or out of connector
* mode
*/
function _loadPane() {
@@ -160,6 +160,10 @@ var ZoteroPane = new function()
collectionsTree.addEventListener("mousedown", ZoteroPane_Local.onTreeMouseDown, true);
collectionsTree.addEventListener("click", ZoteroPane_Local.onTreeClick, true);
+ // Clear items view, so that the load registers as a new selected collection when switching
+ // between modes
+ ZoteroPane_Local.itemsView = null;
+
var itemsTree = document.getElementById('zotero-items-tree');
itemsTree.controllers.appendController(new Zotero.ItemTreeCommandController(itemsTree));
itemsTree.addEventListener("mousedown", ZoteroPane_Local.onTreeMouseDown, true);
diff --git a/components/zotero-service.js b/components/zotero-service.js
@@ -336,9 +336,6 @@ function ZoteroService() {
makeZoteroContext(false);
zContext.Zotero.init(zInitOptions)
.catch(function (e) {
- dump(e + "\n\n");
- Components.utils.reportError(e);
-
if (e === "ZOTERO_SHOULD_START_AS_CONNECTOR") {
// if Zotero should start as a connector, reload it
return zContext.Zotero.shutdown()
@@ -347,9 +344,10 @@ function ZoteroService() {
return zContext.Zotero.init(zInitOptions);
})
}
- else {
- throw e;
- }
+
+ dump(e + "\n\n");
+ Components.utils.reportError(e);
+ throw e;
})
.then(function () {
zContext.Zotero.debug("Initialized in "+(Date.now() - start)+" ms");