Update zotero://select to use new URLs and wait for items list load

Closes #541
This commit is contained in:
Dan Stillman 2014-09-23 01:11:41 -04:00
parent c917d9e30e
commit e2d3cc3f0d
7 changed files with 288 additions and 159 deletions

View File

@ -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<params.itemKey.length; i++) {
let itemKey = params.itemKey[i];
yield s.addCondition('key', 'is', itemKey);
}
yield s.addCondition('blockEnd');
var ids = yield s.search();
}
else {
// Display all items
var s = new Zotero.Search();
yield s.addCondition('libraryID', 'is', params.libraryID);
yield s.addCondition('noChildren', 'true');
var ids = yield s.search();
}
if (!params.objectType) {
throw new Error("objectType not specified");
}
if (results) {
// Filter results by item key
if (params.itemKey) {
results = results.filter(function (result) {
return params.itemKey.indexOf(result.key) !== -1;
});
var results;
if (params.objectType == 'item') {
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 + "'");
}
var s = new Zotero.Search;
if (params.libraryID !== undefined) {
yield s.addCondition('libraryID', 'is', params.libraryID);
}
if (params.objectKey) {
yield s.addCondition('key', 'is', params.objectKey);
}
else if (params.objectID) {
Zotero.debug('adding ' + params.objectID);
yield s.addCondition('itemID', 'is', params.objectID);
}
if (params.itemKey) {
yield s.addCondition('blockStart');
for (let i=0; i<params.itemKey.length; i++) {
let itemKey = params.itemKey[i];
yield s.addCondition('key', 'is', itemKey);
}
yield s.addCondition('blockEnd');
}
// Display all top-level items
/*if (params.onlyTopLevel) {
yield s.addCondition('noChildren', 'true');
}*/
var ids = yield s.search();
}
if (results) {
// Filter results by item key
if (params.itemKey) {
results = results.filter(function (result) {
return params.itemKey.indexOf(result.key) !== -1;
});
}
}
else if (ids) {
// Filter results by item key
if (params.itemKey) {
ids = ids.filter(function (id) {
var [libraryID, key] = Zotero.Items.getLibraryAndKeyFromID(id);
return params.itemKey.indexOf(key) !== -1;
});
}
results = yield Zotero.Items.getAsync(ids);
}
}
else if (ids) {
// Filter results by item key
if (params.itemKey) {
ids = ids.filter(function (id) {
var [libraryID, key] = Zotero.Items.getLibraryAndKeyFromID(id);
return params.itemKey.indexOf(key) !== -1;
});
}
results = yield Zotero.Items.getAsync(ids);
else {
throw new Error("Unsupported object type '" + params.objectType + "'");
}
return results;

View File

@ -36,10 +36,11 @@
*/
Zotero.CollectionTreeView = function()
{
Zotero.LibraryTreeView.apply(this);
this.itemToSelect = null;
this.hideSources = [];
this._treebox = null;
this._highlightedRows = {};
this._unregisterID = Zotero.Notifier.registerObserver(this, ['collection', 'search', 'share', 'group', 'trash', 'bucket'], 'collectionTreeView');
this._containerState = {};
@ -57,6 +58,8 @@ Object.defineProperty(Zotero.CollectionTreeView.prototype, "selectedTreeRow", {
}
});
/*
* Called by the tree itself
*/
@ -93,6 +96,9 @@ Zotero.CollectionTreeView.prototype.setTree = Zotero.Promise.coroutine(function*
var row = yield this.getLastViewedRow();
this.selection.select(row);
this._treebox.ensureRowIsVisible(row);
yield this._runListeners('load');
this._initialized = true;
}
catch (e) {
Zotero.debug(e, 1);

View File

@ -35,18 +35,16 @@
* Constructor for the ItemTreeView object
*/
Zotero.ItemTreeView = function (collectionTreeRow, sourcesOnly) {
Zotero.LibraryTreeView.apply(this);
this.wrappedJSObject = this;
this.rowCount = 0;
this.collectionTreeRow = collectionTreeRow;
this._initialized = false;
this._skipKeypress = false;
this._sourcesOnly = sourcesOnly;
this._callbacks = [];
this._treebox = null;
this._ownerDocument = null;
this._needsSort = false;
@ -62,17 +60,6 @@ Zotero.ItemTreeView = function (collectionTreeRow, sourcesOnly) {
Zotero.ItemTreeView.prototype = Object.create(Zotero.LibraryTreeView.prototype);
Zotero.ItemTreeView.prototype.type = 'item';
Zotero.ItemTreeView.prototype.addCallback = function(callback) {
this._callbacks.push(callback);
}
Zotero.ItemTreeView.prototype._runCallbacks = Zotero.Promise.coroutine(function* () {
for each(var cb in this._callbacks) {
yield Zotero.Promise.resolve(cb());
}
});
/**
* Called by the tree itself
@ -251,12 +238,12 @@ Zotero.ItemTreeView.prototype.setTree = Zotero.Promise.coroutine(function* (tree
yield this.sort();
// Only yield if there are callbacks; otherwise, we're almost done
if(this._callbacks.length && this._waitAfter && Date.now() > 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');
});

View File

@ -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
*/

View File

@ -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: {

View File

@ -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();
}

View File

@ -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);