diff --git a/chrome/content/zotero/xpcom/data/tags.js b/chrome/content/zotero/xpcom/data/tags.js index 565f6a4827..8ec7858726 100644 --- a/chrome/content/zotero/xpcom/data/tags.js +++ b/chrome/content/zotero/xpcom/data/tags.js @@ -757,6 +757,32 @@ Zotero.Tags = new function() { }; + /** + * @param {Zotero.Item[]} + * @return {Promise} + */ + this.removeColoredTagsFromItems = async function (items) { + return Zotero.DB.executeTransaction(async function () { + for (let item of items) { + let colors = this.getColors(item.libraryID); + let tags = item.getTags(); + let changed = false; + for (let tag of tags) { + if (colors.has(tag.tag)) { + item.removeTag(tag.tag); + changed = true; + } + } + if (changed) { + await item.save({ + skipDateModifiedUpdate: true + }); + } + } + }.bind(this)); + }; + + /** * A tree cell can show only one image, and (as of Fx19) it can't be an SVG, * so we need to generate a composite image containing the existing item type diff --git a/chrome/content/zotero/xpcom/itemTreeView.js b/chrome/content/zotero/xpcom/itemTreeView.js index 6b24925013..70acc80c4a 100644 --- a/chrome/content/zotero/xpcom/itemTreeView.js +++ b/chrome/content/zotero/xpcom/itemTreeView.js @@ -148,7 +148,7 @@ Zotero.ItemTreeView.prototype.setTree = async function (treebox) { // Add a keypress listener for expand/collapse var tree = this._getTreeElement(); var self = this; - var coloredTagsRE = new RegExp("^[1-" + Zotero.Tags.MAX_COLORED_TAGS + "]{1}$"); + var coloredTagsRE = new RegExp("^[0-" + Zotero.Tags.MAX_COLORED_TAGS + "]{1}$"); var listener = function(event) { if (self._skipKeyPress) { self._skipKeyPress = false; @@ -199,6 +199,11 @@ Zotero.ItemTreeView.prototype.setTree = async function (treebox) { if (coloredTagsRE.test(key)) { let libraryID = self.collectionTreeRow.ref.libraryID; let position = parseInt(key) - 1; + // When 0 is pressed, remove all colored tags + if (position == -1) { + let items = self.getSelectedItems(); + return Zotero.Tags.removeColoredTagsFromItems(items); + } let colorData = Zotero.Tags.getColorByPosition(libraryID, position); // If a color isn't assigned to this number or any // other numbers, allow key navigation diff --git a/test/tests/tagsTest.js b/test/tests/tagsTest.js index 92fd906da5..30da777391 100644 --- a/test/tests/tagsTest.js +++ b/test/tests/tagsTest.js @@ -198,4 +198,28 @@ describe("Zotero.Tags", function () { assert.isNull(o); }); }); + + + describe("#removeColoredTagsFromItems()", function () { + it("shouldn't remove regular tags", async function () { + var libraryID = Zotero.Libraries.userLibraryID; + var item = await createDataObject('item', { + tags: [ + { tag: 'A' }, + { tag: 'B', type: 1 }, + { tag: 'C' }, + { tag: 'D', type: 1 } + ] + }); + await Zotero.Tags.setColor(libraryID, 'C', '#111111', 0); + await Zotero.Tags.setColor(libraryID, 'D', '#222222', 1); + + await Zotero.Tags.removeColoredTagsFromItems([item]); + + assert.sameDeepMembers(item.getTags(), [ + { tag: 'A' }, + { tag: 'B', type: 1 } + ]); + }); + }); })