From e2d3cc3f0d95ede10bfbe778e312f649f1b8007d Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Tue, 23 Sep 2014 01:11:41 -0400 Subject: [PATCH] Update zotero://select to use new URLs and wait for items list load Closes #541 --- chrome/content/zotero/xpcom/api.js | 178 ++++++++++-------- .../zotero/xpcom/collectionTreeView.js | 8 +- chrome/content/zotero/xpcom/itemTreeView.js | 26 +-- .../content/zotero/xpcom/libraryTreeView.js | 29 ++- chrome/content/zotero/xpcom/search.js | 12 ++ chrome/content/zotero/zoteroPane.js | 93 ++++++--- components/zotero-protocol-handler.js | 101 ++++++---- 7 files changed, 288 insertions(+), 159 deletions(-) diff --git a/chrome/content/zotero/xpcom/api.js b/chrome/content/zotero/xpcom/api.js index c3225dc7b..2a38a569b 100644 --- a/chrome/content/zotero/xpcom/api.js +++ b/chrome/content/zotero/xpcom/api.js @@ -36,89 +36,109 @@ Zotero.API = { getResultsFromParams: Zotero.Promise.coroutine(function* (params) { - var results; - switch (params.scopeObject) { - case 'collections': - if (params.scopeObjectKey) { - var col = yield Zotero.Collections.getByLibraryAndKeyAsync( - params.libraryID, params.scopeObjectKey - ); - } - else { - var col = yield Zotero.Collections.getAsync(params.scopeObjectID); - } - if (!col) { - throw new Error('Invalid collection ID or key'); - } - yield col.loadChildItems(); - results = col.getChildItems(); - break; - - case 'searches': - if (params.scopeObjectKey) { - var s = yield Zotero.Searches.getByLibraryAndKeyAsync( - params.libraryID, params.scopeObjectKey - ); - } - else { - var s = yield Zotero.Searches.getAsync(params.scopeObjectID); - } - if (!s) { - throw new Error('Invalid search ID or key'); - } - - // FIXME: Hack to exclude group libraries for now - var s2 = new Zotero.Search(); - s2.setScope(s); - var groups = Zotero.Groups.getAll(); - for each(var group in groups) { - yield s2.addCondition('libraryID', 'isNot', group.libraryID); - } - var ids = yield s2.search(); - break; - - default: - if (params.scopeObject) { - throw new Error("Invalid scope object '" + params.scopeObject + "'"); - } - - if (params.itemKey) { - var s = new Zotero.Search; - yield s.addCondition('libraryID', 'is', params.libraryID); - yield s.addCondition('blockStart'); - for (let i=0; i this._waitAfter) yield Zotero.Promise.resolve(); + if (this._listeners.load.length && this._waitAfter && Date.now() > this._waitAfter) yield Zotero.Promise.resolve(); yield this.expandMatchParents(); - //Zotero.debug('Running callbacks in itemTreeView.setTree()', 4); - yield this._runCallbacks(); + yield this._runListeners('load'); + this._initialized = true; if (this._ownerDocument.defaultView.ZoteroPane_Local) { this._ownerDocument.defaultView.ZoteroPane_Local.clearItemsPaneMessage(); @@ -1805,8 +1792,7 @@ Zotero.ItemTreeView.prototype.setFilter = Zotero.Promise.coroutine(function* (ty //this._treebox.endUpdateBatch(); this.selection.selectEventsSuppressed = false; - //Zotero.debug('Running callbacks in itemTreeView.setFilter()', 4); - yield this._runCallbacks(); + yield this._runListeners('load'); }); diff --git a/chrome/content/zotero/xpcom/libraryTreeView.js b/chrome/content/zotero/xpcom/libraryTreeView.js index 76229df00..91f608e88 100644 --- a/chrome/content/zotero/xpcom/libraryTreeView.js +++ b/chrome/content/zotero/xpcom/libraryTreeView.js @@ -23,8 +23,35 @@ ***** END LICENSE BLOCK ***** */ -Zotero.LibraryTreeView = function () {}; +Zotero.LibraryTreeView = function () { + this._initialized = false; + this._listeners = { + load: [] + }; +}; + Zotero.LibraryTreeView.prototype = { + addEventListener: function(event, listener) { + if (event == 'load') { + // If already initialized run now + if (this._initialized) { + listener(); + } + else { + this._listeners[event].push(listener); + } + } + }, + + + _runListeners: Zotero.Promise.coroutine(function* (event) { + var listener; + while (listener = this._listeners[event].shift()) { + yield Zotero.Promise.resolve(listener()); + } + }), + + /** * Called while a drag is over the tree */ diff --git a/chrome/content/zotero/xpcom/search.js b/chrome/content/zotero/xpcom/search.js index fe0674a9a..ebeae812c 100644 --- a/chrome/content/zotero/xpcom/search.js +++ b/chrome/content/zotero/xpcom/search.js @@ -2189,6 +2189,18 @@ Zotero.SearchConditions = new function(){ noLoad: true }, + { + name: 'itemID', + operators: { + is: true, + isNot: true + }, + table: 'items', + field: 'itemID', + special: true, + noLoad: true + }, + { name: 'annotation', operators: { diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index f75d11f59..c2789be76 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -32,6 +32,7 @@ var ZoteroPane = new function() var _unserialized = false; this.collectionsView = false; this.itemsView = false; + this._listeners = {}; this.__defineGetter__('loaded', function () _loaded); //Privileged methods @@ -1099,7 +1100,7 @@ var ZoteroPane = new function() this.onCollectionSelected = Zotero.Promise.coroutine(function* () { var collectionTreeRow = this.getCollectionTreeRow(); - if (this.itemsView.collectionTreeRow == collectionTreeRow) { + if (this.itemsView && this.itemsView.collectionTreeRow == collectionTreeRow) { Zotero.debug("Collection selection hasn't changed"); return; } @@ -1167,7 +1168,14 @@ var ZoteroPane = new function() this.itemsView.onError = function () { ZoteroPane_Local.displayErrorMessage(); }; - this.itemsView.addCallback(this.setTagScope); + // If any queued load listeners, set them to run when the tree is ready + if (this._listeners.itemsLoaded) { + let listener; + while (listener = this._listeners.itemsLoaded.shift()) { + this.itemsView.addEventListener('load', listener); + } + } + this.itemsView.addEventListener('load', this.setTagScope); document.getElementById('zotero-items-tree').view = this.itemsView; // Add events to treecolpicker to update menu before showing/hiding @@ -1953,34 +1961,77 @@ var ZoteroPane = new function() return false; } - if (!this.itemsView) { - Components.utils.reportError("Items view not set in ZoteroPane_Local.selectItem()"); - return false; + // Restore window if it's in the dock + if (window.windowState == Components.interfaces.nsIDOMChromeWindow.STATE_MINIMIZED) { + window.restore(); } - var currentLibraryID = this.getSelectedLibraryID(); - // If in a different library - if (item.libraryID != currentLibraryID) { - Zotero.debug("Library ID differs; switching library"); - yield this.collectionsView.selectLibrary(item.libraryID); - } - // Force switch to library view - else if (!this.collectionsView.selectedTreeRow.isLibrary() && inLibrary) { - Zotero.debug("Told to select in library; switching to library"); - yield this.collectionsView.selectLibrary(item.libraryID); + if (!this.collectionsView) { + throw new Error("Collections view not loaded"); } - var selected = yield this.itemsView.selectItem(itemID, expand); - if (!selected) { - Zotero.debug("Item was not selected; switching to library"); - yield this.collectionsView.selectLibrary(item.libraryID); - yield this.itemsView.selectItem(itemID, expand); - } + var self = this; + this.collectionsView.addEventListener('load', function () { + Zotero.spawn(function* () { + var currentLibraryID = self.getSelectedLibraryID(); + // If in a different library + if (item.libraryID != currentLibraryID) { + Zotero.debug("Library ID differs; switching library"); + yield self.collectionsView.selectLibrary(item.libraryID); + } + // Force switch to library view + else if (!self.collectionsView.selectedTreeRow.isLibrary() && inLibrary) { + Zotero.debug("Told to select in library; switching to library"); + yield self.collectionsView.selectLibrary(item.libraryID); + } + + self.addEventListener('itemsLoaded', function () { + Zotero.spawn(function* () { + var selected = yield self.itemsView.selectItem(itemID, expand); + if (!selected) { + Zotero.debug("Item was not selected; switching to library"); + yield self.collectionsView.selectLibrary(item.libraryID); + yield self.itemsView.selectItem(itemID, expand); + } + }); + }); + }); + }); + + // open Zotero pane + this.show(); return true; }); + this.addEventListener = function (event, listener) { + if (event == 'itemsLoaded') { + if (this.itemsView) { + this.itemsView.addEventListener('load', listener); + } + else { + if (!this._listeners.itemsLoaded) { + this._listeners.itemsLoaded = []; + } + this._listeners.itemsLoaded.push(listener); + } + } + }; + + + this._runListeners = Zotero.Promise.coroutine(function* (event) { + if (!this._listeners[event]) { + return; + } + + var listener; + while (listener = this._listeners[event].shift()) { + yield Zotero.Promise.resolve(listener()); + } + }); + + this.getSelectedLibraryID = function () { return this.collectionsView.getSelectedLibraryID(); } diff --git a/components/zotero-protocol-handler.js b/components/zotero-protocol-handler.js index 319fdadb4..9a78202cd 100644 --- a/components/zotero-protocol-handler.js +++ b/components/zotero-protocol-handler.js @@ -110,6 +110,7 @@ function ZoteroProtocolHandler() { } var params = { + objectType: 'item', format: 'html', sort: 'title' }; @@ -793,47 +794,68 @@ function ZoteroProtocolHandler() { var SelectExtension = { newChannel: function (uri) { return new AsyncChannel(uri, function* () { - generateContent:try { - var mimeType, content = ''; - - var [path, queryString] = uri.path.substr(1).split('?'); - var [type, id] = path.split('/'); - - // currently only able to select one item - var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] - .getService(Components.interfaces.nsIWindowMediator); - var win = wm.getMostRecentWindow("navigator:browser"); - - // restore window if it's in the dock - if(win.windowState == Components.interfaces.nsIDOMChromeWindow.STATE_MINIMIZED) { - win.restore(); - } - - // open Zotero pane - win.ZoteroPane.show(); - - if(!id) return; - - var lkh = Zotero.Items.parseLibraryKeyHash(id); + var path = uri.path; + if (!path) { + return 'Invalid URL'; + } + // Strip leading '/' + path = path.substr(1); + var mimeType, content = ''; + + var params = { + objectType: 'item' + }; + var router = new Zotero.Router(params); + + // Item within a collection or search + router.add('library/:scopeObject/:scopeObjectKey/items/:objectKey', function () { + params.libraryID = 0; + }); + router.add('groups/:groupID/:scopeObject/:scopeObjectKey/items/:objectKey'); + + // All items + router.add('library/items/:objectKey', function () { + params.libraryID = 0; + }); + router.add('groups/:groupID/items/:objectKey'); + + // Old-style URLs + router.add('item/:id', function () { + var lkh = Zotero.Items.parseLibraryKeyHash(params.id); if (lkh) { - var item = Zotero.Items.getByLibraryAndKey(lkh.libraryID, lkh.key); + params.libraryID = lkh.libraryID; + params.objectKey = lkh.key; } else { - var item = Zotero.Items.get(id); + params.objectID = params.id; } - if (!item) { - var msg = "Item " + id + " not found in zotero://select"; - Zotero.debug(msg, 2); - Components.utils.reportError(msg); - return; - } - - win.ZoteroPane.selectItem(item.id); + delete params.id; + }); + router.run(path); + + try { + Zotero.API.parseParams(params); + var results = yield Zotero.API.getResultsFromParams(params); } - catch (e){ - Zotero.debug(e); - throw (e); + catch (e) { + Zotero.debug(e, 1); + return e.toString(); } + + + if (!results.length) { + var msg = "Selected items not found"; + Zotero.debug(msg, 2); + Components.utils.reportError(msg); + return; + } + + var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + var win = wm.getMostRecentWindow("navigator:browser"); + + // TODO: Currently only able to select one item + yield win.ZoteroPane.selectItem(results[0].id); }); } }; @@ -1226,13 +1248,18 @@ AsyncChannel.prototype = { }); return promise; } + else if (data === undefined) { + this.cancel(0x804b0002); // BINDING_ABORTED + } else { throw new Error("Invalid return type (" + typeof data + ") from generator passed to AsyncChannel"); } }.bind(this)) .then(function () { - Zotero.debug("AsyncChannel request succeeded in " + (new Date - t) + " ms"); - channel._isPending = false; + if (this._isPending) { + Zotero.debug("AsyncChannel request succeeded in " + (new Date - t) + " ms"); + channel._isPending = false; + } }) .catch(function (e) { Zotero.debug(e, 1);