Collection/item tree selection improvements
Wait for the pane's collectionSelected() to finish before returning from collectionTreeView select methods (e.g., selectLibrary()), and wait for previous items view to finish loading before creating a new one in collectionSelected(). This ensures that the items view has been created (though not loaded) before returning from a select. The tree can still get a bit confused switching between collections, but I think we're getting closer to fixing that. Also switch the items tree to use the same pattern. This also fixes dragging items to collections (#731).
This commit is contained in:
parent
a2d4b05064
commit
61cb01b7c2
6 changed files with 297 additions and 190 deletions
|
@ -71,7 +71,6 @@ Object.defineProperty(Zotero.CollectionTreeView.prototype, "selectedTreeRow", {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called by the tree itself
|
* Called by the tree itself
|
||||||
*/
|
*/
|
||||||
|
@ -248,6 +247,29 @@ Zotero.CollectionTreeView.prototype.reload = function()
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a row and wait for its items view to be created
|
||||||
|
*
|
||||||
|
* Note that this doesn't wait for the items view to be loaded. For that, add a 'load' event
|
||||||
|
* listener to the items view.
|
||||||
|
*
|
||||||
|
* @param {Integer} row
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
Zotero.CollectionTreeView.prototype.selectWait = Zotero.Promise.method(function (row) {
|
||||||
|
if (this.selection.selectEventsSuppressed) {
|
||||||
|
this.selection.select(row);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var deferred = Zotero.Promise.defer();
|
||||||
|
this.addEventListener('select', () => deferred.resolve());
|
||||||
|
this.selection.select(row);
|
||||||
|
return deferred.promise;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called by Zotero.Notifier on any changes to collections in the data layer
|
* Called by Zotero.Notifier on any changes to collections in the data layer
|
||||||
*/
|
*/
|
||||||
|
@ -389,7 +411,10 @@ Zotero.CollectionTreeView.prototype.notify = Zotero.Promise.coroutine(function*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deferred = Zotero.Promise.defer();
|
||||||
|
this.addEventListener('select', () => deferred.resolve());
|
||||||
this.selection.selectEventsSuppressed = false;
|
this.selection.selectEventsSuppressed = false;
|
||||||
|
return deferred.promise;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -823,7 +848,7 @@ Zotero.CollectionTreeView.prototype.selectByID = Zotero.Promise.coroutine(functi
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'L':
|
case 'L':
|
||||||
this.selectLibrary(id);
|
yield this.selectLibrary(id);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 'C':
|
case 'C':
|
||||||
|
@ -841,14 +866,14 @@ Zotero.CollectionTreeView.prototype.selectByID = Zotero.Promise.coroutine(functi
|
||||||
}
|
}
|
||||||
var row = this._rowMap[type + id];
|
var row = this._rowMap[type + id];
|
||||||
this._treebox.ensureRowIsVisible(row);
|
this._treebox.ensureRowIsVisible(row);
|
||||||
this.selection.select(row);
|
yield this.selectWait(row);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Integer} libraryID Library to select
|
* @param {Integer} libraryID Library to select
|
||||||
*/
|
*/
|
||||||
Zotero.CollectionTreeView.prototype.selectLibrary = function (libraryID) {
|
Zotero.CollectionTreeView.prototype.selectLibrary = Zotero.Promise.coroutine(function* (libraryID) {
|
||||||
if (Zotero.suppressUIUpdates) {
|
if (Zotero.suppressUIUpdates) {
|
||||||
Zotero.debug("UI updates suppressed -- not changing library selection");
|
Zotero.debug("UI updates suppressed -- not changing library selection");
|
||||||
return false;
|
return false;
|
||||||
|
@ -857,7 +882,7 @@ Zotero.CollectionTreeView.prototype.selectLibrary = function (libraryID) {
|
||||||
// Select local library
|
// Select local library
|
||||||
if (!libraryID) {
|
if (!libraryID) {
|
||||||
this._treebox.ensureRowIsVisible(0);
|
this._treebox.ensureRowIsVisible(0);
|
||||||
this.selection.select(0);
|
yield this.selectWait(0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -874,12 +899,12 @@ Zotero.CollectionTreeView.prototype.selectLibrary = function (libraryID) {
|
||||||
var row = this._rowMap['L' + libraryID];
|
var row = this._rowMap['L' + libraryID];
|
||||||
if (row !== undefined) {
|
if (row !== undefined) {
|
||||||
this._treebox.ensureRowIsVisible(row);
|
this._treebox.ensureRowIsVisible(row);
|
||||||
this.selection.select(row);
|
this.selectWait(row);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
});
|
||||||
|
|
||||||
|
|
||||||
Zotero.CollectionTreeView.prototype.selectCollection = function (id) {
|
Zotero.CollectionTreeView.prototype.selectCollection = function (id) {
|
||||||
|
|
|
@ -360,23 +360,22 @@ Zotero.Collection.prototype.addItems = Zotero.Promise.coroutine(function* (itemI
|
||||||
yield this.loadChildItems();
|
yield this.loadChildItems();
|
||||||
var current = this.getChildItems(true);
|
var current = this.getChildItems(true);
|
||||||
|
|
||||||
return Zotero.DB.executeTransaction(function* () {
|
Zotero.DB.requireTransaction();
|
||||||
for (let i=0; i<itemIDs.length; i++) {
|
for (let i = 0; i < itemIDs.length; i++) {
|
||||||
let itemID = itemIDs[i];
|
let itemID = itemIDs[i];
|
||||||
|
|
||||||
if (current && current.indexOf(itemID) != -1) {
|
if (current && current.indexOf(itemID) != -1) {
|
||||||
Zotero.debug("Item " + itemID + " already a child of collection " + this.id);
|
Zotero.debug("Item " + itemID + " already a child of collection " + this.id);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
let item = yield this.ChildObjects.getAsync(itemID);
|
|
||||||
yield item.loadCollections();
|
|
||||||
item.addToCollection(this.id);
|
|
||||||
yield item.save({
|
|
||||||
skipDateModifiedUpdate: true
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}.bind(this));
|
|
||||||
|
let item = yield this.ChildObjects.getAsync(itemID);
|
||||||
|
yield item.loadCollections();
|
||||||
|
item.addToCollection(this.id);
|
||||||
|
yield item.save({
|
||||||
|
skipDateModifiedUpdate: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
yield this.loadChildItems(true);
|
yield this.loadChildItems(true);
|
||||||
});
|
});
|
||||||
|
|
|
@ -68,7 +68,7 @@ Zotero.ItemTreeView.prototype.type = 'item';
|
||||||
Zotero.ItemTreeView.prototype.setTree = Zotero.serial(Zotero.Promise.coroutine(function* (treebox)
|
Zotero.ItemTreeView.prototype.setTree = Zotero.serial(Zotero.Promise.coroutine(function* (treebox)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Zotero.debug("Setting item tree");
|
Zotero.debug("Setting tree for items view " + this.id);
|
||||||
var start = Date.now();
|
var start = Date.now();
|
||||||
// Try to set the window document if not yet set
|
// Try to set the window document if not yet set
|
||||||
if (treebox && !this._ownerDocument) {
|
if (treebox && !this._ownerDocument) {
|
||||||
|
@ -256,7 +256,7 @@ Zotero.ItemTreeView.prototype.setTree = Zotero.serial(Zotero.Promise.coroutine(f
|
||||||
this.collectionTreeRow.itemToSelect = null;
|
this.collectionTreeRow.itemToSelect = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.debug("Set tree in "+(Date.now()-start)+" ms");
|
Zotero.debug("Set tree for items view " + this.id + " in " + (Date.now() - start) + " ms");
|
||||||
|
|
||||||
this._initialized = true;
|
this._initialized = true;
|
||||||
yield this._runListeners('load');
|
yield this._runListeners('load');
|
||||||
|
@ -434,7 +434,7 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
||||||
yield this._refreshPromise;
|
yield this._refreshPromise;
|
||||||
|
|
||||||
if (!this._treebox || !this._treebox.treeBody) {
|
if (!this._treebox || !this._treebox.treeBody) {
|
||||||
Components.utils.reportError("Treebox didn't exist in itemTreeView.notify()");
|
Zotero.debug("Treebox didn't exist in itemTreeView.notify()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -934,12 +934,13 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
||||||
|
|
||||||
//this._treebox.endUpdateBatch();
|
//this._treebox.endUpdateBatch();
|
||||||
if (madeChanges) {
|
if (madeChanges) {
|
||||||
var promise = this._getItemSelectedPromise();
|
var deferred = Zotero.Promise.defer();
|
||||||
|
this.addEventListener('select', () => deferred.resolve());
|
||||||
}
|
}
|
||||||
this.selection.selectEventsSuppressed = false;
|
this.selection.selectEventsSuppressed = false;
|
||||||
if (madeChanges) {
|
if (madeChanges) {
|
||||||
Zotero.debug("Yielding for select promise"); // TEMP
|
Zotero.debug("Yielding for select promise"); // TEMP
|
||||||
yield promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1630,6 +1631,12 @@ Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (i
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var selected = this.getSelectedItems(true);
|
||||||
|
if (selected.length == 1 && selected[0] == id) {
|
||||||
|
Zotero.debug("Item " + id + " is already selected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var row = this._rowMap[id];
|
var row = this._rowMap[id];
|
||||||
|
|
||||||
// Get the row of the parent, if there is one
|
// Get the row of the parent, if there is one
|
||||||
|
@ -1674,17 +1681,20 @@ Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (i
|
||||||
row = this._rowMap[id];
|
row = this._rowMap[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function calls nsITreeSelection.select(), which triggers the <tree>'s 'onselect'
|
// this.selection.select() triggers the <tree>'s 'onselect' attribute, which calls
|
||||||
// attribute, which calls ZoteroPane.itemSelected(), which calls ZoteroItemPane.viewItem(),
|
// ZoteroPane.itemSelected(), which calls ZoteroItemPane.viewItem(), which refreshes the
|
||||||
// which refreshes the itembox. But since the 'onselect' doesn't handle promises,
|
// itembox. But since the 'onselect' doesn't handle promises, itemSelected() isn't waited for
|
||||||
// itemSelected() isn't waited for and 'yield selectItem(itemID)' continues before the
|
// here, which means that 'yield selectItem(itemID)' continues before the itembox has been
|
||||||
// itembox has been refreshed. To get around this, we make a promise resolver that's
|
// refreshed. To get around this, we wait for a select event that's triggered by
|
||||||
// triggered by itemSelected() when it's done.
|
// itemSelected() when it's done.
|
||||||
if (!this.selection.selectEventsSuppressed) {
|
if (this.selection.selectEventsSuppressed) {
|
||||||
var itemSelectedPromise = this._getItemSelectedPromise(id);
|
this.selection.select(row);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var deferred = Zotero.Promise.defer();
|
||||||
|
this.addEventListener('select', () => deferred.resolve());
|
||||||
|
this.selection.select(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selection.select(row);
|
|
||||||
|
|
||||||
// If |expand|, open row if container
|
// If |expand|, open row if container
|
||||||
if (expand && this.isContainer(row) && !this.isContainerOpen(row)) {
|
if (expand && this.isContainer(row) && !this.isContainerOpen(row)) {
|
||||||
|
@ -1692,8 +1702,8 @@ Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (i
|
||||||
}
|
}
|
||||||
this.selection.select(row);
|
this.selection.select(row);
|
||||||
|
|
||||||
if (!this.selection.selectEventsSuppressed) {
|
if (deferred) {
|
||||||
yield itemSelectedPromise;
|
yield deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We aim for a row 5 below the target row, since ensureRowIsVisible() does
|
// We aim for a row 5 below the target row, since ensureRowIsVisible() does
|
||||||
|
@ -1718,23 +1728,6 @@ Zotero.ItemTreeView.prototype.selectItem = Zotero.Promise.coroutine(function* (i
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Zotero.ItemTreeView.prototype._getItemSelectedPromise = function (itemID) {
|
|
||||||
if (itemID) {
|
|
||||||
var selected = this.getSelectedItems(true);
|
|
||||||
var alreadySelected = selected.length == 1 && selected[0] == itemID;
|
|
||||||
if (alreadySelected) {
|
|
||||||
return Zotero.Promise.resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Zotero.Promise(function () {
|
|
||||||
this._itemSelectedPromiseResolver = {
|
|
||||||
resolve: arguments[0],
|
|
||||||
reject: arguments[1]
|
|
||||||
};
|
|
||||||
}.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select multiple top-level items
|
* Select multiple top-level items
|
||||||
*
|
*
|
||||||
|
|
|
@ -26,10 +26,14 @@
|
||||||
Zotero.LibraryTreeView = function () {
|
Zotero.LibraryTreeView = function () {
|
||||||
this._initialized = false;
|
this._initialized = false;
|
||||||
this._listeners = {
|
this._listeners = {
|
||||||
load: []
|
load: [],
|
||||||
|
select: []
|
||||||
};
|
};
|
||||||
this._rows = [];
|
this._rows = [];
|
||||||
this._rowMap = {};
|
this._rowMap = {};
|
||||||
|
|
||||||
|
this.id = Zotero.Utilities.randomString();
|
||||||
|
Zotero.debug("Creating " + this.type + "s view with id " + this.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
Zotero.LibraryTreeView.prototype = {
|
Zotero.LibraryTreeView.prototype = {
|
||||||
|
@ -43,10 +47,17 @@ Zotero.LibraryTreeView.prototype = {
|
||||||
this._listeners[event].push(listener);
|
this._listeners[event].push(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if (!this._listeners[event]) {
|
||||||
|
this._listeners[event] = [];
|
||||||
|
}
|
||||||
|
this._listeners[event].push(listener);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
_runListeners: Zotero.Promise.coroutine(function* (event) {
|
_runListeners: Zotero.Promise.coroutine(function* (event) {
|
||||||
|
if (!this._listeners[event]) return;
|
||||||
var listener;
|
var listener;
|
||||||
while (listener = this._listeners[event].shift()) {
|
while (listener = this._listeners[event].shift()) {
|
||||||
yield Zotero.Promise.resolve(listener());
|
yield Zotero.Promise.resolve(listener());
|
||||||
|
@ -55,13 +66,33 @@ Zotero.LibraryTreeView.prototype = {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a reference to the tree row at a given row
|
* Return a reference to the tree row at a given row
|
||||||
|
*
|
||||||
|
* @return {Zotero.CollectionTreeRow|Zotero.ItemTreeRow}
|
||||||
*/
|
*/
|
||||||
getRow: function(row) {
|
getRow: function(row) {
|
||||||
return this._rows[row];
|
return this._rows[row];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the index of the row with a given ID (e.g., "C123" for collection 123)
|
||||||
|
*
|
||||||
|
* @param {String} - Row id
|
||||||
|
* @return {Integer}
|
||||||
|
*/
|
||||||
|
getRowByID: function (id) {
|
||||||
|
var type = id[0];
|
||||||
|
id = ('' + id).substr(1);
|
||||||
|
return this._rowMap[type + id];
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
onSelect: function () {
|
||||||
|
return this._runListeners('select');
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a tree row to the main array, update the row count, tell the treebox that the row
|
* Add a tree row to the main array, update the row count, tell the treebox that the row
|
||||||
* count changed, and update the row map
|
* count changed, and update the row map
|
||||||
|
|
|
@ -1107,110 +1107,127 @@ var ZoteroPane = new function()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.onCollectionSelected = Zotero.Promise.coroutine(function* () {
|
this.onCollectionSelected = function () {
|
||||||
var collectionTreeRow = this.getCollectionTreeRow();
|
return Zotero.spawn(function* () {
|
||||||
if (!collectionTreeRow) {
|
var collectionTreeRow = this.getCollectionTreeRow();
|
||||||
return;
|
if (!collectionTreeRow) {
|
||||||
}
|
return;
|
||||||
|
|
||||||
if (this.itemsView && this.itemsView.collectionTreeRow == collectionTreeRow) {
|
|
||||||
Zotero.debug("Collection selection hasn't changed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.itemsView) {
|
|
||||||
this.itemsView.unregister();
|
|
||||||
document.getElementById('zotero-items-tree').view = this.itemsView = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.collectionsView.selection.count != 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear quick search and tag selector when switching views
|
|
||||||
document.getElementById('zotero-tb-search').value = "";
|
|
||||||
|
|
||||||
// XBL functions might not yet be available
|
|
||||||
var tagSelector = document.getElementById('zotero-tag-selector');
|
|
||||||
if (tagSelector.clearAll) {
|
|
||||||
tagSelector.clearAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not necessary with seltype="cell", which calls nsITreeView::isSelectable()
|
|
||||||
/*if (collectionTreeRow.isSeparator()) {
|
|
||||||
document.getElementById('zotero-items-tree').view = this.itemsView = null;
|
|
||||||
return;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
collectionTreeRow.setSearch('');
|
|
||||||
collectionTreeRow.setTags(getTagSelection());
|
|
||||||
|
|
||||||
// Enable or disable toolbar icons and menu options as necessary
|
|
||||||
const disableIfNoEdit = [
|
|
||||||
"cmd_zotero_newCollection",
|
|
||||||
"cmd_zotero_newSavedSearch",
|
|
||||||
"zotero-tb-add",
|
|
||||||
"cmd_zotero_newItemFromCurrentPage",
|
|
||||||
"zotero-tb-lookup",
|
|
||||||
"cmd_zotero_newStandaloneNote",
|
|
||||||
"zotero-tb-note-add",
|
|
||||||
"zotero-tb-attachment-add"
|
|
||||||
];
|
|
||||||
for(var i=0; i<disableIfNoEdit.length; i++) {
|
|
||||||
var el = document.getElementById(disableIfNoEdit[i]);
|
|
||||||
|
|
||||||
// If a trash is selected, new collection depends on the
|
|
||||||
// editability of the library
|
|
||||||
if (collectionTreeRow.isTrash() &&
|
|
||||||
disableIfNoEdit[i] == 'cmd_zotero_newCollection') {
|
|
||||||
var overrideEditable = Zotero.Libraries.isEditable(collectionTreeRow.ref.libraryID);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var overrideEditable = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collectionTreeRow.editable || overrideEditable) {
|
if (this.itemsView && this.itemsView.collectionTreeRow == collectionTreeRow) {
|
||||||
if(el.hasAttribute("disabled")) el.removeAttribute("disabled");
|
Zotero.debug("Collection selection hasn't changed");
|
||||||
} else {
|
return;
|
||||||
el.setAttribute("disabled", "true");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (this.itemsView) {
|
||||||
this.itemsView = new Zotero.ItemTreeView(collectionTreeRow);
|
// Wait for existing items view to finish loading before unloading it
|
||||||
this.itemsView.onError = function () {
|
//
|
||||||
ZoteroPane_Local.displayErrorMessage();
|
// TODO: Cancel loading
|
||||||
};
|
let deferred = Zotero.Promise.defer();
|
||||||
// If any queued load listeners, set them to run when the tree is ready
|
this.itemsView.addEventListener('load', function () {
|
||||||
if (this._listeners.itemsLoaded) {
|
deferred.resolve();
|
||||||
let listener;
|
});
|
||||||
while (listener = this._listeners.itemsLoaded.shift()) {
|
if (deferred.promise.isPending()) {
|
||||||
this.itemsView.addEventListener('load', listener);
|
Zotero.debug("Waiting for items view " + this.itemsView.id + " to finish loading");
|
||||||
|
}
|
||||||
|
yield deferred.promise;
|
||||||
|
|
||||||
|
this.itemsView.unregister();
|
||||||
|
document.getElementById('zotero-items-tree').view = this.itemsView = null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
this.itemsView.addEventListener('load', this.setTagScope);
|
if (this.collectionsView.selection.count != 1) {
|
||||||
document.getElementById('zotero-items-tree').view = this.itemsView;
|
return;
|
||||||
|
|
||||||
// Add events to treecolpicker to update menu before showing/hiding
|
|
||||||
try {
|
|
||||||
let treecols = document.getElementById('zotero-items-columns-header');
|
|
||||||
let treecolpicker = treecols.boxObject.firstChild.nextSibling;
|
|
||||||
let menupopup = treecolpicker.boxObject.firstChild.nextSibling;
|
|
||||||
let attr = menupopup.getAttribute('onpopupshowing');
|
|
||||||
if (attr.indexOf('Zotero') == -1) {
|
|
||||||
menupopup.setAttribute('onpopupshowing', 'ZoteroPane.itemsView.onColumnPickerShowing(event);')
|
|
||||||
// Keep whatever else is there
|
|
||||||
+ ' ' + attr;
|
|
||||||
menupopup.setAttribute('onpopuphidden', 'ZoteroPane.itemsView.onColumnPickerHidden(event);')
|
|
||||||
// Keep whatever else is there
|
|
||||||
+ ' ' + menupopup.getAttribute('onpopuphidden');
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (e) {
|
// Clear quick search and tag selector when switching views
|
||||||
Zotero.debug(e);
|
document.getElementById('zotero-tb-search').value = "";
|
||||||
}
|
|
||||||
|
// XBL functions might not yet be available
|
||||||
Zotero.Prefs.set('lastViewedFolder', collectionTreeRow.id);
|
var tagSelector = document.getElementById('zotero-tag-selector');
|
||||||
});
|
if (tagSelector.clearAll) {
|
||||||
|
tagSelector.clearAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not necessary with seltype="cell", which calls nsITreeView::isSelectable()
|
||||||
|
/*if (collectionTreeRow.isSeparator()) {
|
||||||
|
document.getElementById('zotero-items-tree').view = this.itemsView = null;
|
||||||
|
return;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
collectionTreeRow.setSearch('');
|
||||||
|
collectionTreeRow.setTags(getTagSelection());
|
||||||
|
|
||||||
|
// Enable or disable toolbar icons and menu options as necessary
|
||||||
|
const disableIfNoEdit = [
|
||||||
|
"cmd_zotero_newCollection",
|
||||||
|
"cmd_zotero_newSavedSearch",
|
||||||
|
"zotero-tb-add",
|
||||||
|
"cmd_zotero_newItemFromCurrentPage",
|
||||||
|
"zotero-tb-lookup",
|
||||||
|
"cmd_zotero_newStandaloneNote",
|
||||||
|
"zotero-tb-note-add",
|
||||||
|
"zotero-tb-attachment-add"
|
||||||
|
];
|
||||||
|
for(var i=0; i<disableIfNoEdit.length; i++) {
|
||||||
|
var el = document.getElementById(disableIfNoEdit[i]);
|
||||||
|
|
||||||
|
// If a trash is selected, new collection depends on the
|
||||||
|
// editability of the library
|
||||||
|
if (collectionTreeRow.isTrash() &&
|
||||||
|
disableIfNoEdit[i] == 'cmd_zotero_newCollection') {
|
||||||
|
var overrideEditable = Zotero.Libraries.isEditable(collectionTreeRow.ref.libraryID);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var overrideEditable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collectionTreeRow.editable || overrideEditable) {
|
||||||
|
if(el.hasAttribute("disabled")) el.removeAttribute("disabled");
|
||||||
|
} else {
|
||||||
|
el.setAttribute("disabled", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.itemsView = new Zotero.ItemTreeView(collectionTreeRow);
|
||||||
|
this.itemsView.onError = function () {
|
||||||
|
ZoteroPane_Local.displayErrorMessage();
|
||||||
|
};
|
||||||
|
// 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
|
||||||
|
try {
|
||||||
|
let treecols = document.getElementById('zotero-items-columns-header');
|
||||||
|
let treecolpicker = treecols.boxObject.firstChild.nextSibling;
|
||||||
|
let menupopup = treecolpicker.boxObject.firstChild.nextSibling;
|
||||||
|
let attr = menupopup.getAttribute('onpopupshowing');
|
||||||
|
if (attr.indexOf('Zotero') == -1) {
|
||||||
|
menupopup.setAttribute('onpopupshowing', 'ZoteroPane.itemsView.onColumnPickerShowing(event);')
|
||||||
|
// Keep whatever else is there
|
||||||
|
+ ' ' + attr;
|
||||||
|
menupopup.setAttribute('onpopuphidden', 'ZoteroPane.itemsView.onColumnPickerHidden(event);')
|
||||||
|
// Keep whatever else is there
|
||||||
|
+ ' ' + menupopup.getAttribute('onpopuphidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.debug(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.Prefs.set('lastViewedFolder', collectionTreeRow.id);
|
||||||
|
}, this)
|
||||||
|
.finally(function () {
|
||||||
|
return this.collectionsView.onSelect();
|
||||||
|
}.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
this.getCollectionTreeRow = function () {
|
this.getCollectionTreeRow = function () {
|
||||||
|
@ -1419,19 +1436,8 @@ var ZoteroPane = new function()
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, this)
|
}, this)
|
||||||
.then(function (result) {
|
.finally(function () {
|
||||||
// See note in itemTreeView.js::selectItem()
|
return this.itemsView.onSelect();
|
||||||
if (this.itemsView && this.itemsView._itemSelectedPromiseResolver) {
|
|
||||||
this.itemsView._itemSelectedPromiseResolver.resolve();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}.bind(this))
|
|
||||||
.catch(function (e) {
|
|
||||||
Zotero.debug(e, 1);
|
|
||||||
if (this.itemsView && this.itemsView._itemSelectedPromiseResolver) {
|
|
||||||
this.itemsView._itemSelectedPromiseResolver.reject(e);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2052,11 +2058,11 @@ var ZoteroPane = new function()
|
||||||
if (!selected) {
|
if (!selected) {
|
||||||
if (item.deleted) {
|
if (item.deleted) {
|
||||||
Zotero.debug("Item is deleted; switching to trash");
|
Zotero.debug("Item is deleted; switching to trash");
|
||||||
self.collectionsView.selectTrash(item.libraryID);
|
yield self.collectionsView.selectTrash(item.libraryID);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Zotero.debug("Item was not selected; switching to library");
|
Zotero.debug("Item was not selected; switching to library");
|
||||||
self.collectionsView.selectLibrary(item.libraryID);
|
yield self.collectionsView.selectLibrary(item.libraryID);
|
||||||
}
|
}
|
||||||
yield self.itemsView.selectItem(itemID, expand);
|
yield self.itemsView.selectItem(itemID, expand);
|
||||||
}
|
}
|
||||||
|
@ -2071,12 +2077,12 @@ var ZoteroPane = new function()
|
||||||
// If in a different library
|
// If in a different library
|
||||||
if (item.libraryID != currentLibraryID) {
|
if (item.libraryID != currentLibraryID) {
|
||||||
Zotero.debug("Library ID differs; switching library");
|
Zotero.debug("Library ID differs; switching library");
|
||||||
self.collectionsView.selectLibrary(item.libraryID);
|
yield self.collectionsView.selectLibrary(item.libraryID);
|
||||||
}
|
}
|
||||||
// Force switch to library view
|
// Force switch to library view
|
||||||
else if (!self.collectionsView.selectedTreeRow.isLibrary() && inLibrary) {
|
else if (!self.collectionsView.selectedTreeRow.isLibrary() && inLibrary) {
|
||||||
Zotero.debug("Told to select in library; switching to library");
|
Zotero.debug("Told to select in library; switching to library");
|
||||||
self.collectionsView.selectLibrary(item.libraryID);
|
yield self.collectionsView.selectLibrary(item.libraryID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
|
|
@ -3,27 +3,31 @@
|
||||||
describe("Zotero.CollectionTreeView", function() {
|
describe("Zotero.CollectionTreeView", function() {
|
||||||
var win, collectionsView;
|
var win, collectionsView;
|
||||||
|
|
||||||
|
// Select library
|
||||||
|
// TODO: Add a selectCollection() function and select a collection instead
|
||||||
|
var resetSelection = Zotero.Promise.coroutine(function* () {
|
||||||
|
yield collectionsView.selectLibrary(Zotero.Libraries.userLibraryID);
|
||||||
|
yield waitForItemsLoad(win);
|
||||||
|
assert.equal(collectionsView.getSelectedLibraryID(), Zotero.Libraries.userLibraryID);
|
||||||
|
});
|
||||||
|
|
||||||
// Load Zotero pane and select library
|
// Load Zotero pane and select library
|
||||||
before(function* () {
|
before(function* () {
|
||||||
win = yield loadZoteroPane();
|
win = yield loadZoteroPane();
|
||||||
collectionsView = win.ZoteroPane.collectionsView;
|
collectionsView = win.ZoteroPane.collectionsView;
|
||||||
});
|
});
|
||||||
|
beforeEach(function () {
|
||||||
|
return resetSelection();
|
||||||
|
})
|
||||||
after(function () {
|
after(function () {
|
||||||
win.close();
|
win.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Select library
|
|
||||||
// TODO: Add a selectCollection() function and select a collection instead
|
|
||||||
var resetSelection = function () {
|
|
||||||
collectionsView.selectLibrary(Zotero.Libraries.userLibraryID);
|
|
||||||
assert.equal(collectionsView.getSelectedLibraryID(), Zotero.Libraries.userLibraryID);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("collapse/expand", function () {
|
describe("collapse/expand", function () {
|
||||||
it("should close and open My Library repeatedly", function* () {
|
it("should close and open My Library repeatedly", function* () {
|
||||||
var libraryID = Zotero.Libraries.userLibraryID;
|
var libraryID = Zotero.Libraries.userLibraryID;
|
||||||
var cv = collectionsView;
|
var cv = collectionsView;
|
||||||
cv.selectLibrary(libraryID);
|
yield cv.selectLibrary(libraryID);
|
||||||
var row = cv.selection.currentIndex;
|
var row = cv.selection.currentIndex;
|
||||||
|
|
||||||
cv.collapseLibrary(libraryID);
|
cv.collapseLibrary(libraryID);
|
||||||
|
@ -54,8 +58,6 @@ describe("Zotero.CollectionTreeView", function() {
|
||||||
|
|
||||||
describe("#notify()", function () {
|
describe("#notify()", function () {
|
||||||
it("should select a new collection", function* () {
|
it("should select a new collection", function* () {
|
||||||
resetSelection();
|
|
||||||
|
|
||||||
// Create collection
|
// Create collection
|
||||||
var collection = new Zotero.Collection;
|
var collection = new Zotero.Collection;
|
||||||
collection.name = "Select new collection";
|
collection.name = "Select new collection";
|
||||||
|
@ -67,8 +69,6 @@ describe("Zotero.CollectionTreeView", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shouldn't select a new collection if skipNotifier is passed", function* () {
|
it("shouldn't select a new collection if skipNotifier is passed", function* () {
|
||||||
resetSelection();
|
|
||||||
|
|
||||||
// Create collection with skipNotifier flag
|
// Create collection with skipNotifier flag
|
||||||
var collection = new Zotero.Collection;
|
var collection = new Zotero.Collection;
|
||||||
collection.name = "No select on skipNotifier";
|
collection.name = "No select on skipNotifier";
|
||||||
|
@ -81,8 +81,6 @@ describe("Zotero.CollectionTreeView", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shouldn't select a new collection if skipSelect is passed", function* () {
|
it("shouldn't select a new collection if skipSelect is passed", function* () {
|
||||||
resetSelection();
|
|
||||||
|
|
||||||
// Create collection with skipSelect flag
|
// Create collection with skipSelect flag
|
||||||
var collection = new Zotero.Collection;
|
var collection = new Zotero.Collection;
|
||||||
collection.name = "No select on skipSelect";
|
collection.name = "No select on skipSelect";
|
||||||
|
@ -100,7 +98,7 @@ describe("Zotero.CollectionTreeView", function() {
|
||||||
collection.name = "No select on modify";
|
collection.name = "No select on modify";
|
||||||
var id = yield collection.saveTx();
|
var id = yield collection.saveTx();
|
||||||
|
|
||||||
resetSelection();
|
yield resetSelection();
|
||||||
|
|
||||||
collection.name = "No select on modify 2";
|
collection.name = "No select on modify 2";
|
||||||
yield collection.saveTx();
|
yield collection.saveTx();
|
||||||
|
@ -157,4 +155,59 @@ describe("Zotero.CollectionTreeView", function() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("#drop()", function () {
|
||||||
|
it("should add an item to a collection", function* () {
|
||||||
|
var collection = yield createDataObject('collection', false, {
|
||||||
|
skipSelect: true
|
||||||
|
});
|
||||||
|
var item = yield createDataObject('item', false, {
|
||||||
|
skipSelect: true
|
||||||
|
});
|
||||||
|
var row = collectionsView.getRowByID("C" + collection.id);
|
||||||
|
|
||||||
|
// Add observer to wait for collection add
|
||||||
|
var deferred = Zotero.Promise.defer();
|
||||||
|
var observerID = Zotero.Notifier.registerObserver({
|
||||||
|
notify: function (event, type, ids) {
|
||||||
|
if (type == 'collection-item' && event == 'add'
|
||||||
|
&& ids[0] == collection.id + "-" + item.id) {
|
||||||
|
setTimeout(function () {
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 'collection-item', 'test');
|
||||||
|
|
||||||
|
// Simulate a drag and drop
|
||||||
|
var stub = sinon.stub(Zotero.DragDrop, "getDragTarget");
|
||||||
|
stub.returns(collectionsView.getRow(row));
|
||||||
|
collectionsView.drop(row, 0, {
|
||||||
|
dropEffect: 'copy',
|
||||||
|
effectAllowed: 'copy',
|
||||||
|
mozSourceNode: win.document.getElementById('zotero-items-tree'),
|
||||||
|
types: {
|
||||||
|
contains: function (type) {
|
||||||
|
return type == 'zotero/item';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getData: function (type) {
|
||||||
|
if (type == 'zotero/item') {
|
||||||
|
return "" + item.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
yield deferred.promise;
|
||||||
|
stub.restore();
|
||||||
|
Zotero.Notifier.unregisterObserver(observerID);
|
||||||
|
yield collectionsView.selectCollection(collection.id);
|
||||||
|
yield waitForItemsLoad(win);
|
||||||
|
|
||||||
|
var itemsView = win.ZoteroPane.itemsView
|
||||||
|
assert.equal(itemsView.rowCount, 1);
|
||||||
|
var treeRow = itemsView.getRow(0);
|
||||||
|
assert.equal(treeRow.ref.id, item.id);
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue