commit 6915b7aa56ba9cd3e997150232508465ed632fd3 parent 9a4e83dc781089abd08a024c0007a49bd801f182 Author: Dan Stillman <dstillman@zotero.org> Date: Sun, 17 Mar 2013 01:24:50 -0400 Merge branch '4.0' Diffstat:
13 files changed, 554 insertions(+), 367 deletions(-)
diff --git a/chrome/content/zotero/bindings/tagsbox.xml b/chrome/content/zotero/bindings/tagsbox.xml @@ -118,7 +118,7 @@ this.mode = this.getAttribute('mode'); } - this._notifierID = Zotero.Notifier.registerObserver(this, ['setting']); + this._notifierID = Zotero.Notifier.registerObserver(this, ['item-tag', 'setting']); ]]> </constructor> @@ -142,6 +142,55 @@ } return; } + else if (type == 'item-tag') { + let itemID, tagID; + + for (var i=0; i<ids.length; i++) { + [itemID, tagID] = ids[i].split('-'); + if (!this.item || itemID != this.item.id) { + continue; + } + if (event == 'add') { + var newTabIndex = this.add(tagID); + if (newTabIndex == -1) { + return; + } + if (this._tabDirection == -1) { + if (this._lastTabIndex > newTabIndex) { + this._lastTabIndex++; + } + } + else if (this._tabDirection == 1) { + if (this._lastTabIndex > newTabIndex) { + this._lastTabIndex++; + } + } + } + else if (event == 'remove') { + var oldTabIndex = this.remove(tagID); + if (oldTabIndex == -1) { + return; + } + if (this._tabDirection == -1) { + if (this._lastTabIndex > oldTabIndex) { + this._lastTabIndex--; + } + } + else if (this._tabDirection == 1) { + if (this._lastTabIndex >= oldTabIndex) { + this._lastTabIndex--; + } + } + } + } + + this.updateCount(); + } + else if (type == 'tag') { + if (event == 'modify') { + this.reload(); + } + } ]]> </body> </method> @@ -166,14 +215,11 @@ while(rows.hasChildNodes()) { rows.removeChild(rows.firstChild); } - var tags = self.item.getTags(); - if (tags) { - for (var i=0; i<tags.length; i++) { - self.addDynamicRow(tags[i], i+1); - } + var tags = self.item.getTags() || []; + for (var i=0; i<tags.length; i++) { + self.addDynamicRow(tags[i], i+1); } - - self.updateCount(0); + self.updateCount(tags.length); self._reloading = false; self._focusField(); @@ -187,6 +233,7 @@ <method name="addDynamicRow"> <parameter name="tagObj"/> <parameter name="tabindex"/> + <parameter name="skipAppend"/> <body> <![CDATA[ if (tagObj) { @@ -224,9 +271,11 @@ row.appendChild(remove); } - this.updateRow(row, tagObj, tabindex); + this.updateRow(row, tagObj); - this.id('tagRows').appendChild(row); + if (!skipAppend) { + this.id('tagRows').appendChild(row); + } return row; ]]> @@ -234,10 +283,13 @@ </method> + <!-- + Update various attributes of a row to match the given tag + and current editability + --> <method name="updateRow"> <parameter name="row"/> <parameter name="tagObj"/> - <parameter name="tabindex"/> <body><![CDATA[ if (tagObj) { var tagID = tagObj.id; @@ -274,7 +326,7 @@ var self = this; remove.addEventListener('click', function () { self._lastTabIndex = false; - document.getBindingParent(this).remove(tagID); + document.getBindingParent(this).item.removeTag(tagID); // Return focus to items pane var tree = document.getElementById('zotero-items-tree'); @@ -302,7 +354,9 @@ valueElement.className = 'zotero-box-label'; if (this.clickable) { - valueElement.setAttribute('ztabindex', tabindex); + if (tabindex) { + valueElement.setAttribute('ztabindex', tabindex); + } valueElement.addEventListener('click', function (event) { /* Skip right-click on Windows */ if (event.button) { @@ -476,12 +530,7 @@ case event.DOM_VK_ESCAPE: // Reset field to original value - if (target.getAttribute('multiline')) { - target.value = ""; - } - else { - target.value = target.getAttribute('value'); - } + target.value = target.getAttribute('value'); var tagsbox = Zotero.getAncestorByTagName(focused, 'tagsbox'); @@ -571,7 +620,8 @@ if (!rows) { rows = value.match(/\n/g).length + 1; } - textbox = this.showEditor(textbox, rows, value); + textbox = this.showEditor(textbox, rows, textbox.getAttribute('value')); + textbox.value = value; // Move cursor to end textbox.selectionStart = value.length; ]]></body> @@ -583,21 +633,12 @@ <body> <![CDATA[ Zotero.debug('Hiding editor'); - /* - var textbox = Zotero.getAncestorByTagName(t, 'textbox'); - if (!textbox){ - Zotero.debug('Textbox not found in hideEditor'); - return; - } - */ - - // TODO: get rid of this? - //var saveChanges = this.saveOnEdit; - var saveChanges = true; var fieldName = 'tag'; var tabindex = textbox.getAttribute('ztabindex'); + var oldValue = textbox.getAttribute('value'); + textbox.value = textbox.value.trim(); var value = textbox.value; var tagsbox = Zotero.getAncestorByTagName(textbox, 'tagsbox'); @@ -611,147 +652,67 @@ var rows = row.parentNode; // Tag id encoded as 'tag-1234' - var id = row.getAttribute('id').split('-')[1]; + var oldTagID = row.getAttribute('id').split('-')[1]; + + // Remove empty row at end + if (!oldTagID && !value) { + row.parentNode.removeChild(row); + return; + } + + // If row hasn't changed, change back to label + if (oldValue == value) { + this.textboxToLabel(textbox); + return; + } var tagArray = value.split(/\r\n?|\n/); - if (saveChanges) { - // Modifying existing tag - if (id && tagArray.length < 2) { - if (value) { - var newTagID = tagsbox.replace(id, value); - if (newTagID) { - id = newTagID; - } - // Changed tag to existing - else if (value != Zotero.Tags.getName(id)) { - if (this._tabDirection == 1) { - this._lastTabIndex -= 1; - } - this.reload(); - return; - } - else { - var unchanged = true; - } - } - // Existing tag cleared - else { - tagsbox.remove(id); - return; - } - } - // // Multiple tags - else if (tagArray.length > 1) { - var lastTag = row == row.parentNode.lastChild; - - Zotero.DB.beginTransaction(); - - // If old tag isn't in array, remove it - if (id) { - var oldValue = Zotero.Tags.getName(id); - if (tagArray.indexOf(oldValue) == -1) { - this.item.removeTag(id); - } - } - - this.item.addTags(tagArray); - - Zotero.DB.commitTransaction(); - - // TODO: get tab index right - - if (lastTag) { - this._lastTabIndex = this.item.getTags().length; - } - - this.reload(); - return; + // Modifying existing tag with a single new one + if (oldTagID && tagArray.length < 2) { + if (value) { + tagsbox.replace(oldTagID, value); } - // Single tag at end + // Existing tag cleared else { - id = tagsbox.add(value); - // New tag - if (id) { - // Stay put, since a tag was added above - if (this._tabDirection == -1) { - this._tabDirection = false; - } + this.item.removeTag(oldTagID); + } + } + // Multiple tags + else if (tagArray.length > 1) { + var lastTag = row == row.parentNode.lastChild; + + Zotero.DB.beginTransaction(); + + if (oldTagID) { + var oldValue = Zotero.Tags.getName(oldTagID); + // If old tag isn't in array, remove it + if (tagArray.indexOf(oldValue) == -1) { + this.item.removeTag(oldTagID); } - // Already exists + // If old tag is staying, restore the textbox + // immediately. This isn't strictly necessary, but it + // makes the transition nicer. else { - // Go back one, since we'll remove this below - if (this._tabDirection == 1) { - this._lastTabIndex--; - } + textbox.value = textbox.getAttribute('value'); + this.textboxToLabel(textbox); } } - } - - if (id) { - var elem = this.createValueElement( - value, - tabindex - ); - var row = textbox.parentNode; - row.replaceChild(elem, textbox); + this.item.addTags(tagArray); - this.updateRow(row, Zotero.Tags.get(id), tabindex); + Zotero.DB.commitTransaction(); - if (!unchanged) { - // Move row to appropriate place, alphabetically - var collation = Zotero.getLocaleCollation(); - var rows = row.parentNode; - var labels = rows.getElementsByAttribute('fieldname', 'tag'); - - rows.removeChild(row); - var currentTabIndex = elem.getAttribute('ztabindex'); - - var before = null; - var inserted = false; - for (var i=0; i<labels.length; i++) { - let newTabIndex = i + 1; - if (inserted) { - labels[i].setAttribute('ztabindex', newTabIndex); - continue; - } - - if (collation.compareString(1, value, labels[i].textContent) > 0) { - labels[i].setAttribute('ztabindex', newTabIndex); - continue; - } - - elem.setAttribute('ztabindex', newTabIndex); - rows.insertBefore(row, labels[i].parentNode); - inserted = true; - - // Adjust last tab index - if (this._tabDirection == -1) { - if (this._lastTabIndex > newTabIndex) { - this._lastTabIndex++; - } - } - else if (this._tabDirection == 1) { - if (this._lastTabIndex < newTabIndex) { - this._lastTabIndex--; - } - } - } - if (!inserted) { - elem.setAttribute('ztabindex', i + 1); - rows.appendChild(row); - } + if (lastTag) { + this._lastTabIndex = this.item.getTags().length; } + + this.reload(); } + // Single tag at end else { - // Just remove the row - // - // If there's an open popup, this throws NODE CANNOT BE FOUND - try { - var row = rows.removeChild(row); - } - catch (e) {} + row.parentNode.removeChild(row); + this.item.addTag(value); } ]]> </body> @@ -769,16 +730,84 @@ </method> + <method name="textboxToLabel"> + <parameter name="textbox"/> + <body><![CDATA[ + var elem = this.createValueElement( + textbox.value, textbox.getAttribute('ztabindex') + ); + var row = textbox.parentNode; + row.replaceChild(elem, textbox); + ]]></body> + </method> + + <method name="add"> - <parameter name="value"/> - <body> - <![CDATA[ - if (value) { - return this.item.addTag(value); + <parameter name="tagID"/> + <body><![CDATA[ + var rowsElement = this.id('tagRows'); + var rows = rowsElement.childNodes; + + // Get this tag's existing row, if there is one + var row = rowsElement.getElementsByAttribute('id', 'tag-' + tagID); + row = row.length ? row[0] : false; + + var tagObj = Zotero.Tags.get(tagID); + var name = tagObj.name; + + if (row) { + // Update row and label + this.updateRow(row, tagObj); + var elem = this.createValueElement(name); + + // Remove the old row, which we'll reinsert at the correct place + rowsElement.removeChild(row); + + // Find the current label or textbox within the row + // and replace it with the new element -- this is used + // both when creating new rows and when hiding the + // entry textbox + var oldElem = row.getElementsByAttribute('fieldname', 'tag')[0]; + row.replaceChild(elem, oldElem); + } + else { + // Create new row, but don't insert it + row = this.addDynamicRow(tagObj, false, true); + var elem = row.getElementsByAttribute('fieldname', 'tag')[0]; + } + + // Move row to appropriate place, alphabetically + var collation = Zotero.getLocaleCollation(); + var labels = rowsElement.getElementsByAttribute('fieldname', 'tag'); + + var before = null; + var inserted = false; + var newTabIndex = false; + for (var i=0; i<labels.length; i++) { + let index = i + 1; + if (inserted) { + labels[i].setAttribute('ztabindex', index); + continue; } - return false; - ]]> - </body> + + if (collation.compareString(1, name, labels[i].textContent) > 0) { + labels[i].setAttribute('ztabindex', index); + continue; + } + + elem.setAttribute('ztabindex', index); + rowsElement.insertBefore(row, labels[i].parentNode); + newTabIndex = index; + inserted = true; + } + if (!inserted) { + newTabIndex = i + 1; + elem.setAttribute('ztabindex', newTabIndex); + rowsElement.appendChild(row); + } + + return newTabIndex; + ]]></body> </method> @@ -803,12 +832,35 @@ <method name="remove"> <parameter name="id"/> - <body> - <![CDATA[ - this.item.removeTag(id); - this.reload(); - ]]> - </body> + <body><![CDATA[ + var rowsElement = this.id('tagRows'); + + var row = rowsElement.getElementsByAttribute('id', 'tag-' + id); + row = row.length ? row[0] : false; + if (!row) { + return -1; + } + + var rows = rowsElement.childNodes; + var removed = false; + var oldTabIndex = -1; + for (var i=0; i<rows.length; i++) { + let tagID = rows[i].getAttribute('id').split('-')[1]; + if (tagID == id) { + oldTabIndex = i + 1; + removed = true; + rowsElement.removeChild(rows[i]); + i--; + continue; + } + // After the removal, update tab indexes + if (removed && tagID) { + var elem = rows[i].getElementsByAttribute('fieldname', 'tag')[0]; + elem.setAttribute('ztabindex', i + 1); + } + } + return oldTabIndex; + ]]></body> </method> @@ -816,7 +868,11 @@ <parameter name="count"/> <body> <![CDATA[ - if(count === null) { + if (!this.item) { + return; + } + + if(typeof count == 'undefined') { var tags = this.item.getTags(); if(tags) count = tags.length; diff --git a/chrome/content/zotero/bindings/tagselector.xml b/chrome/content/zotero/bindings/tagselector.xml @@ -100,21 +100,13 @@ </getter> </property> - <field name="_hasFilter">false</field> - <field name="_filter">null</field> - <method name="setFilterTags"> + <field name="_search">null</field> + <method name="setSearch"> <parameter name="val"/> <parameter name="skipRefresh"/> <body> <![CDATA[ - if (!Zotero.Utilities.isEmpty(val)) { - this._hasFilter = true; - this._filter = val; - } - else { - this._hasFilter = !!val; - this._filter = {}; - } + this._search = val ? val.toLowerCase() : false; if (!skipRefresh) { this.refresh(); @@ -128,12 +120,12 @@ <property name="scope" onget="return this._scope"> <setter> <![CDATA[ - if (!Zotero.Utilities.isEmpty(val)) { + if (val && !Zotero.Utilities.isEmpty(val)) { this._hasScope = true; this._scope = val; } else { - this._hasScope = !!val; + this._hasScope = false; this._scope = {}; } @@ -159,8 +151,8 @@ <constructor> <![CDATA[ - this.id('display-all-tags').setAttribute('checked', !this.filterToScope); this.id('show-automatic').setAttribute('checked', this.showAutomatic); + this.id('display-all-tags').setAttribute('checked', !this.filterToScope); this.dragObserver = new this._dragObserverConstructor; ]]> </constructor> @@ -226,9 +218,45 @@ // Remove children tagsToggleBox.textContent = ""; - var lastTag; + // Sort by name + var orderedTags = []; + var collation = Zotero.getLocaleCollation(); for (let tagID in self._tags) { - let tagButton = self._makeClickableTag(tagID, lastTag, self.editable); + orderedTags.push(self._tags[tagID]) + } + orderedTags.sort(function(a, b) { + return collation.compareString(1, a.name, b.name); + }); + + var tagColorsLowerCase = {}; + var colorTags = []; + for (let name in tagColors) { + colorTags[tagColors[name].position] = name; + tagColorsLowerCase[name.toLowerCase()] = true; + } + var positions = Object.keys(colorTags); + for (let i=positions.length-1; i>=0; i--) { + let name = colorTags[positions[i]]; + let ids = Zotero.Tags.getIDs(name, self.libraryID); + orderedTags.unshift({ + id: ids ? ids.join('-') : null, + name: name, + type: 0, + hasColor: true + }); + } + + var lastTag; + for (let i=0; i<orderedTags.length; i++) { + let tagObj = orderedTags[i]; + + // Skip colored tags in the regular section, + // since we add them to the beginning above + if (!tagObj.hasColor && tagColorsLowerCase[tagObj.name.toLowerCase()]) { + continue; + } + + let tagButton = self._makeClickableTag(orderedTags[i], lastTag, self.editable); if (tagButton) { tagButton.addEventListener('click', function(event) { self.handleTagClick(event, this); @@ -245,53 +273,77 @@ self._dirty = false; } + var searchTags = self._search ? Zotero.Tags.search(self._search) : {}; + // Set attributes var colorTags = {}; var labels = tagsToggleBox.getElementsByTagName('label'); for (let i=0; i<labels.length; i++) { - var tagIDs = labels[i].getAttribute('tagID').split('-'); + var tagIDs = labels[i].getAttribute('tagID'); + tagIDs = tagIDs ? tagIDs.split('-') : []; + + let name = labels[i].value; + let lcname = name.toLowerCase(); + + let colorData = tagColors[name]; + if (colorData) { + labels[i].setAttribute( + 'style', 'color:' + colorData.color + '; ' + 'font-weight: bold' + ); + } + else { + labels[i].removeAttribute('style'); + } // Restore selection - if (self.selection[labels[i].value]){ + if (self.selection[name]){ labels[i].setAttribute('selected', 'true'); } else { labels[i].setAttribute('selected', 'false'); } - // Check tags against filter - if (self._hasFilter) { - var inFilter = false; - for each(var tagID in tagIDs) { - if (self._filter[tagID]) { - inFilter = true; - break; + // Check tags against search + if (self._search) { + var inSearch = false; + if (tagIDs.length) { + for (let i=0; i<tagIDs.length; i++) { + if (searchTags[tagIDs[i]]) { + inSearch = true; + break; + } } } + // For colored tags, compare by name + else if (lcname.indexOf(self._search) != -1) { + inSearch = true; + } } // Check tags against scope if (self._hasScope) { var inScope = false; - for each(var tagID in tagIDs) { - if (self._scope[tagID]) { + for (let i=0; i<tagIDs.length; i++) { + if (self._scope[tagIDs[i]]) { inScope = true; break; } } } - // If not in filter, hide - if (self._hasFilter && !inFilter) { + // If not in search, hide + if (self._search && !inSearch) { labels[i].setAttribute('hidden', true); } else if (self.filterToScope) { if (self._hasScope && inScope) { + labels[i].className = 'zotero-clicky'; labels[i].setAttribute('inScope', true); labels[i].setAttribute('hidden', false); empty = false; } else { + labels[i].className = ''; labels[i].setAttribute('hidden', true); labels[i].setAttribute('inScope', false); } @@ -299,48 +351,36 @@ // Display all else { if (self._hasScope && inScope) { + labels[i].className = 'zotero-clicky'; labels[i].setAttribute('inScope', true); } else { + labels[i].className = ''; labels[i].setAttribute('inScope', false); - - // If out of scope, make sure it's not selected (otherwise a tag - // stays selected after removing an item with that tag from the - // current collection) - if (self.selection[labels[i].value]) { - labels[i].setAttribute('selected', false); - delete self.selection[labels[i].value]; - var doCommand = true; - } - } labels[i].setAttribute('hidden', false); empty = false; } - let colorData = tagColors[labels[i].value]; + // If tag isn't in scope and is still selected, deselect it + if (labels[i].getAttribute('hidden') == 'true' && self.selection[name]) { + labels[i].setAttribute('selected', false); + delete self.selection[name]; + var doCommand = true; + } + + + // Always show colored tags at top if (colorData) { - labels[i].setAttribute( - 'style', 'color:' + colorData.color + '; ' + 'font-weight: bold' - ); - colorTags[colorData.position] = tagsToggleBox.removeChild(labels[i]); - // The HTMLCollection returned by getElementsByTagName() is live, - // so since we removed something we need to decrement the counter - i--; + labels[i].setAttribute('hidden', false); + labels[i].setAttribute('hasColor', true); } else { - labels[i].removeAttribute('style'); + labels[i].removeAttribute('hasColor'); } } - // Add color tags to beginning in order - var positions = Object.keys(colorTags); - positions.sort(); - for (var i=positions.length-1; i>=0; i--) { - tagsToggleBox.insertBefore(colorTags[positions[i]], tagsToggleBox.firstChild); - } - //start tag cloud code var tagCloud = Zotero.Prefs.get('tagCloud'); @@ -516,7 +556,12 @@ // This could be more optimized to insert new/changed tags at the appropriate // spot if we cared, but we probably don't var t = me.id('tags-search').inputField; - me.setFilterTags(Zotero.Tags.search(t.value), true); + if (t.value) { + me.setSearch(t.value, true); + } + else { + me.setSearch(false, true); + } me._dirty = true; me.doCommand(); @@ -590,7 +635,7 @@ if (typeof clear != 'undefined') { if (clear){ t.value = ''; - this.setFilterTags(false); + this.setSearch(); return false; } else { @@ -598,7 +643,12 @@ } } - this.setFilterTags(Zotero.Tags.search(t.value)); + if (t.value) { + this.setSearch(t.value); + } + else { + this.setSearch(); + } return true; ]]> </body> @@ -639,16 +689,9 @@ <method name="rename"> - <parameter name="tagIDs"/> + <parameter name="oldName"/> <body> <![CDATA[ - tagIDs = tagIDs.split('-'); - // Convert to ints - for (var i=0; i<tagIDs.length; i++) { - tagIDs[i] = parseInt(tagIDs[i]); - } - var oldName = Zotero.Tags.getName(tagIDs[0]); - var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); @@ -658,16 +701,13 @@ Zotero.getString('pane.tagSelector.rename.message'), newName, '', {}); - if (result && newName.value) { - // Add other ids with same tag - var ids = Zotero.Tags.getIDs(oldName, this.libraryID); - - for (var i=0; i<ids.length; i++) { - if (tagIDs.indexOf(ids[i]) == -1) { - tagIDs.push(ids[i]); - } - } - + if (!result || !newName.value || oldName == newName.value) { + return; + } + + // Get current tagIDs with the old name + var tagIDs = Zotero.Tags.getIDs(oldName, this.libraryID) || []; + if (tagIDs.length) { if (this.selection[oldName]) { var wasSelected = true; delete this.selection[oldName]; @@ -688,18 +728,47 @@ Q.all(promises) .done(); } + // Colored tags don't need to exist, so in that case + // just rename the color setting + else { + var self = this; + Zotero.Tags.getColor(this.libraryID, oldName) + .then(function (color) { + if (color) { + if (self.selection[oldName]) { + var wasSelected = true; + delete self.selection[oldName]; + } + + return Zotero.Tags.setColor( + self.libraryID, oldName, false + ) + .then(function () { + return Zotero.Tags.setColor( + self.libraryID, newName, color + ) + .then(function () { + if (wasSelected) { + self.selection[newName.value] = true; + } + }); + }); + } + else { + throw new Error("Can't rename missing tag"); + } + }) + .done(); + } ]]> </body> </method> <method name="delete"> - <parameter name="tagIDs"/> + <parameter name="name"/> <body> <![CDATA[ - tagIDs = tagIDs.split('-'); - var oldName = Zotero.Tags.getName(tagIDs[0]); - var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); @@ -711,17 +780,26 @@ Zotero.DB.beginTransaction(); // Add other ids with same tag - var ids = Zotero.Tags.getIDs(oldName, this.libraryID); + var ids = Zotero.Tags.getIDs(name, this.libraryID); + var tagIDs = []; for each(var id in ids) { if (tagIDs.indexOf(id) == -1) { tagIDs.push(id); } } - Zotero.Tags.erase(tagIDs); - Zotero.Tags.purge(tagIDs); + if (tagIDs.length) { + Zotero.Tags.erase(tagIDs); + Zotero.Tags.purge(tagIDs); + } Zotero.DB.commitTransaction() + + // If only a tag color setting, remove that + if (!tagIDs.length) { + Zotero.Tags.setColor(this.libraryID, name, false); + } + } ]]> </body> @@ -743,25 +821,27 @@ <method name="_makeClickableTag"> - <parameter name="tagID"/> + <parameter name="tagObj"/> <parameter name="lastTag"/> <parameter name="editable"/> <body> <![CDATA[ - var tagInfo = this._tags[tagID], tagName = tagInfo.name; + var tagID = tagObj.id, tagName = tagObj.name, tagType = tagObj.type; // If the last tag was the same, add this tagID and tagType to it if(lastTag && lastTag.value === tagName) { lastTag.setAttribute('tagID', lastTag.getAttribute('tagID') + '-' + tagID); - lastTag.setAttribute('tagType', lastTag.getAttribute('tagType') + '-' + tagInfo.type); + lastTag.setAttribute('tagType', lastTag.getAttribute('tagType') + '-' + tagType); return false; } var label = document.createElement('label'); - label.className = 'zotero-clicky'; label.setAttribute('value', tagName); - label.setAttribute('tagID', tagID); - label.setAttribute('tagType', tagInfo.type); + // Not used for color tags + if (tagID) { + label.setAttribute('tagID', tagID); + } + label.setAttribute('tagType', tagType); if (editable) { label.setAttribute('context', 'tag-menu'); } @@ -772,14 +852,12 @@ <method name="_openColorPickerWindow"> - <parameter name="tagIDs"/> + <parameter name="name"/> <body> <![CDATA[ - tagIDs = tagIDs.split('-'); - var io = { libraryID: this.libraryID, - name: Zotero.Tags.getName(tagIDs[0]) + name: name }; var self = this; @@ -868,7 +946,8 @@ // Find a manual tag if there is one var tagID = null; - var tagIDs = node.getAttribute('tagID').split(/\-/); + var tagIDs = node.getAttribute('tagID'); + tagIDs = tagIDs ? node.getAttribute('tagID').split(/\-/) : []; var tagTypes = node.getAttribute('tagType').split(/\-/); for (var i=0; i<tagIDs.length; i++) { if (tagTypes[i] == 0) { @@ -911,9 +990,12 @@ <content> <groupbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1"> <menupopup id="tag-menu"> - <menuitem label="&zotero.tagSelector.assignColor;" oncommand="_openColorPickerWindow(document.popupNode.getAttribute('tagID')); event.stopPropagation()"/> - <menuitem label="&zotero.tagSelector.renameTag;" oncommand="document.getBindingParent(this).rename(document.popupNode.getAttribute('tagID')); event.stopPropagation()"/> - <menuitem label="&zotero.tagSelector.deleteTag;" oncommand="document.getBindingParent(this).delete(document.popupNode.getAttribute('tagID')); event.stopPropagation()"/> + <menuitem label="&zotero.tagSelector.assignColor;" + oncommand="_openColorPickerWindow(document.popupNode.getAttribute('value')); event.stopPropagation()"/> + <menuitem label="&zotero.tagSelector.renameTag;" + oncommand="document.getBindingParent(this).rename(document.popupNode.getAttribute('value')); event.stopPropagation()"/> + <menuitem label="&zotero.tagSelector.deleteTag;" + oncommand="document.getBindingParent(this).delete(document.popupNode.getAttribute('value')); event.stopPropagation()"/> </menupopup> <vbox id="no-tags-box" align="center" pack="center" flex="1"> @@ -930,15 +1012,28 @@ onkeypress="if (event.keyCode == event.DOM_VK_ESCAPE) { document.getBindingParent(this).handleKeyPress(true); }"/> <toolbarbutton id="view-settings-menu" tooltiptext="&zotero.toolbar.actions.label;" image="chrome://zotero/skin/tag-selector-menu.png" type="menu"> - <menupopup id="view-settings-popup"> + <menupopup id="view-settings-popup" + onpopupshown="/* + This is necessary to fix a bug with Display All Tags not + being checked if enabled before menuu is shown (OS X only?) + */ + document.getElementById('show-automatic').setAttribute('checked', document.getBindingParent(this).showAutomatic); + document.getElementById('display-all-tags').setAttribute('checked', !document.getBindingParent(this).filterToScope);"> <menuitem id="num-selected" disabled="true"/> <menuitem id="deselect-all" label="&zotero.tagSelector.clearAll;" oncommand="document.getBindingParent(this).clearAll(); event.stopPropagation();"/> <menuseparator/> - <menuitem id="show-automatic" label="&zotero.tagSelector.showAutomatic;" autocheck="true" type="checkbox" - oncommand="var ts = document.getBindingParent(this); ts._dirty = true; ts.setAttribute('showAutomatic', this.getAttribute('checked') == 'true')"/> - <menuitem id="display-all-tags" label="&zotero.tagSelector.displayAllInLibrary;" autocheck="true" type="checkbox" - oncommand="document.getBindingParent(this).filterToScope = !(this.getAttribute('checked') == 'true'); event.stopPropagation();"/> + <menuitem id="show-automatic" label="&zotero.tagSelector.showAutomatic;" type="checkbox" + oncommand="var ts = document.getBindingParent(this); + ts._dirty = true; + var showAutomatic = this.getAttribute('checked') == 'true'; + ts.setAttribute('showAutomatic', showAutomatic); + this.setAttribute('checked', showAutomatic);"/> + <menuitem id="display-all-tags" label="&zotero.tagSelector.displayAllInLibrary;" type="checkbox" + oncommand="var displayAll = this.getAttribute('checked') == 'true'; + this.setAttribute('checked', !displayAll); + document.getBindingParent(this).filterToScope = !displayAll; + event.stopPropagation();"/> </menupopup> </toolbarbutton> </hbox> diff --git a/chrome/content/zotero/xpcom/collectionTreeView.js b/chrome/content/zotero/xpcom/collectionTreeView.js @@ -814,7 +814,7 @@ Zotero.CollectionTreeView.prototype.getLastViewedRow = function () { /* * Delete the selection */ -Zotero.CollectionTreeView.prototype.deleteSelection = function() +Zotero.CollectionTreeView.prototype.deleteSelection = function(deleteItems) { if(this.selection.count == 0) return; @@ -842,14 +842,12 @@ Zotero.CollectionTreeView.prototype.deleteSelection = function() for (var i=0; i<rows.length; i++) { //erase collection from DB: - var group = this._getItemAtRow(rows[i]-i); - if(group.isCollection()) - { - group.ref.erase(); + var itemGroup = this._getItemAtRow(rows[i]-i); + if (itemGroup.isCollection()) { + itemGroup.ref.erase(deleteItems); } - else if(group.isSearch()) - { - Zotero.Searches.erase(group.ref['id']); + else if (itemGroup.isSearch()) { + Zotero.Searches.erase(itemGroup.ref.id); } } this._treebox.endUpdateBatch(); diff --git a/chrome/content/zotero/xpcom/data/collection.js b/chrome/content/zotero/xpcom/data/collection.js @@ -961,7 +961,7 @@ Zotero.Collection.prototype.erase = function(deleteItems) { } } if (del.length) { - Zotero.Items.erase(del); + Zotero.Items.trash(del); } // Remove relations diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js @@ -3869,7 +3869,9 @@ Zotero.Item.prototype.replaceTag = function(oldTagID, newTag) { Zotero.DB.commitTransaction(); Zotero.Notifier.trigger('modify', 'item', this.id); Zotero.Notifier.trigger('remove', 'item-tag', this.id + '-' + oldTagID); - Zotero.Notifier.trigger('add', 'item-tag', this.id + '-' + id); + if (id) { + Zotero.Notifier.trigger('add', 'item-tag', this.id + '-' + id); + } return id; } diff --git a/chrome/content/zotero/xpcom/data/tags.js b/chrome/content/zotero/xpcom/data/tags.js @@ -186,11 +186,6 @@ Zotero.Tags = new function() { return {}; } - var collation = Zotero.getLocaleCollation(); - tags.sort(function(a, b) { - return collation.compareString(1, a.name, b.name); - }); - var indexed = {}; for (var i=0; i<tags.length; i++) { var tag = this.get(tags[i].tagID, true); @@ -475,13 +470,6 @@ Zotero.Tags = new function() { tagColors = tagColors || []; - // Remove colors for tags that don't exist - tagColors = tagColors.filter(function (val) { - var tagIDs = self.getIDs(val.name, libraryID); - // TEMP: handle future getIDs return format change - return tagIDs && tagIDs.length; - }); - _libraryColors[libraryID] = tagColors; _libraryColorsByName[libraryID] = {}; @@ -515,12 +503,6 @@ Zotero.Tags = new function() { var tagColors = _libraryColors[libraryID]; var tagIDs = self.getIDs(name, libraryID); - // Just to be safe, remove colors for tags that don't exist - tagColors = tagColors.filter(function (val) { - // TEMP: handle future getIDs return format change - return tagIDs && tagIDs.length; - }); - // Unset if (!color) { // Trying to clear color on tag that doesn't have one @@ -634,7 +616,12 @@ Zotero.Tags = new function() { this.toggleItemsListTags = function (libraryID, items, name) { var self = this; return Q.fcall(function () { - var tagIDs = self.getIDs(name, libraryID); + var tagIDs = self.getIDs(name, libraryID) || []; + // If there's a color setting but no matching tag, don't throw + // an error (though ideally this wouldn't be possible). + if (!tagIDs.length) { + return; + } var tags = tagIDs.map(function (tagID) { return Zotero.Tags.get(tagID, true); }); @@ -834,14 +821,29 @@ Zotero.Tags = new function() { function erase(ids) { ids = Zotero.flattenArguments(ids); + var deleted = []; + Zotero.DB.beginTransaction(); for each(var id in ids) { var tag = this.get(id); if (tag) { + deleted.push({ + libraryID: tag.libraryID ? parseInt(tag.libraryID) : 0, + name: tag.name + }); tag.erase(); } } Zotero.DB.commitTransaction(); + + // Also delete tag color setting + // + // Note that this isn't done in purge(), so the setting will not + // be removed if the tag is just removed from all items without + // without being explicitly deleted. + for (var i in deleted) { + this.setColor(deleted[i].libraryID, deleted[i].name, false); + } } diff --git a/chrome/content/zotero/xpcom/itemTreeView.js b/chrome/content/zotero/xpcom/itemTreeView.js @@ -188,10 +188,13 @@ Zotero.ItemTreeView.prototype._setTreeGenerator = function(treebox) let position = parseInt(key) - 1; return Zotero.Tags.getColorByPosition(libraryID, position) .then(function (colorData) { - // If a color isn't assigned to this number, allow key navigation, - // though I'm not sure this is a good idea. + // If a color isn't assigned to this number or any + // other numbers, allow key navigation if (!colorData) { - return true; + return Zotero.Tags.getColors(libraryID) + .then(function (colors) { + return !Object.keys(colors).length; + }); } var items = self.getSelectedItems(); @@ -316,7 +319,6 @@ Zotero.ItemTreeView.prototype._refreshGenerator = function() } var savedSelection = this.saveSelection(); var savedOpenState = this.saveOpenState(); - var savedFirstRow = this.saveFirstRow(); var oldRows = this.rowCount; this._dataItems = []; @@ -405,7 +407,6 @@ Zotero.ItemTreeView.prototype._refreshGenerator = function() } this.rememberOpenState(savedOpenState); - this.rememberFirstRow(savedFirstRow); this.rememberSelection(savedSelection); this.expandMatchParents(); if (unsuppress) { diff --git a/chrome/content/zotero/xpcom/utilities.js b/chrome/content/zotero/xpcom/utilities.js @@ -408,29 +408,15 @@ Zotero.Utilities = { // Create a node and use the textContent property to do unescaping where // possible, because this approach preserves <br/> if(node === undefined) { - var platformVersion = Components.classes["@mozilla.org/xre/app-info;1"] - .getService(Components.interfaces.nsIXULAppInfo).platformVersion; - if(Components.classes["@mozilla.org/xpcom/version-comparator;1"] - .getService(Components.interfaces.nsIVersionComparator) - .compare(platformVersion, "12.0") >= 0) { - var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"] - .createInstance(Components.interfaces.nsIDOMParser); - var domDocument = parser.parseFromString("<!DOCTYPE html><html></html>", - "text/html"); - node = domDocument.createElement("div"); - } else { - node = false; - } + var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"] + .createInstance(Components.interfaces.nsIDOMParser); + var domDocument = parser.parseFromString("<!DOCTYPE html><html></html>", + "text/html"); + node = domDocument.createElement("div"); } - if(node) { - node.innerHTML = str; - return node.textContent.replace(/ {2,}/g, " "); - } else if(!nsIScriptableUnescapeHTML) { - nsIScriptableUnescapeHTML = Components.classes["@mozilla.org/feed-unescapehtml;1"] - .getService(Components.interfaces.nsIScriptableUnescapeHTML); - } - return nsIScriptableUnescapeHTML.unescape(str); + node.innerHTML = str; + return node.textContent.replace(/ {2,}/g, " "); } else if(Zotero.isNode) { /*var doc = require('jsdom').jsdom(str, null, { "features":{ diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js @@ -663,7 +663,8 @@ var ZoteroPane = new function() if (from == 'zotero-collections-tree') { if ((event.keyCode == event.DOM_VK_BACK_SPACE && Zotero.isMac) || event.keyCode == event.DOM_VK_DELETE) { - ZoteroPane_Local.deleteSelectedCollection(); + var deleteItems = event.metaKey || (!Zotero.isMac && event.shiftKey); + ZoteroPane_Local.deleteSelectedCollection(deleteItems); event.preventDefault(); return; } @@ -673,7 +674,7 @@ var ZoteroPane = new function() event.keyCode == event.DOM_VK_DELETE) { // If Cmd/Ctrl delete, use forced mode, which does different // things depending on the context - var force = event.metaKey || (!Zotero.isMac && event.ctrlKey); + var force = event.metaKey || (!Zotero.isMac && event.shiftKey); ZoteroPane_Local.deleteSelectedItems(force); event.preventDefault(); return; @@ -1587,7 +1588,7 @@ var ZoteroPane = new function() } - this.deleteSelectedCollection = function () { + this.deleteSelectedCollection = function (deleteItems) { var itemGroup = this.getItemGroup(); // Remove virtual duplicates collection @@ -1606,18 +1607,52 @@ var ZoteroPane = new function() return; } + + var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING + + ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL; if (this.collectionsView.selection.count == 1) { if (itemGroup.isCollection()) { - if (confirm(Zotero.getString('pane.collections.delete'))) - { - this.collectionsView.deleteSelection(); + if (deleteItems) { + var index = ps.confirmEx( + null, + Zotero.getString('pane.collections.deleteWithItems.title'), + Zotero.getString('pane.collections.deleteWithItems'), + buttonFlags, + Zotero.getString('pane.collections.deleteWithItems.title'), + "", "", "", {} + ); + } + else { + var index = ps.confirmEx( + null, + Zotero.getString('pane.collections.delete.title'), + Zotero.getString('pane.collections.delete') + + "\n\n" + + Zotero.getString('pane.collections.delete.keepItems'), + buttonFlags, + Zotero.getString('pane.collections.delete.title'), + "", "", "", {} + ); + } + if (index == 0) { + this.collectionsView.deleteSelection(deleteItems); } } else if (itemGroup.isSearch()) { - if (confirm(Zotero.getString('pane.collections.deleteSearch'))) - { + + var index = ps.confirmEx( + null, + Zotero.getString('pane.collections.deleteSearch.title'), + Zotero.getString('pane.collections.deleteSearch'), + buttonFlags, + Zotero.getString('pane.collections.deleteSearch.title'), + "", "", "", {} + ); + if (index == 0) { this.collectionsView.deleteSelection(); } } @@ -1962,7 +1997,8 @@ var ZoteroPane = new function() "showDuplicates", "showUnfiled", "editSelectedCollection", - "removeCollection", + "deleteCollection", + "deleteCollectionAndItems", "sep2", "exportCollection", "createBibCollection", @@ -1992,7 +2028,8 @@ var ZoteroPane = new function() m.newSubcollection, m.sep1, m.editSelectedCollection, - m.removeCollection, + m.deleteCollection, + m.deleteCollectionAndItems, m.sep2, m.exportCollection, m.createBibCollection, @@ -2010,7 +2047,8 @@ var ZoteroPane = new function() // Adjust labels menu.childNodes[m.editSelectedCollection].setAttribute('label', Zotero.getString('pane.collections.menu.rename.collection')); - menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('pane.collections.menu.remove.collection')); + menu.childNodes[m.deleteCollection].setAttribute('label', Zotero.getString('pane.collections.menu.delete.collection')); + menu.childNodes[m.deleteCollectionAndItems].setAttribute('label', Zotero.getString('pane.collections.menu.delete.collectionAndItems')); menu.childNodes[m.exportCollection].setAttribute('label', Zotero.getString('pane.collections.menu.export.collection')); menu.childNodes[m.createBibCollection].setAttribute('label', Zotero.getString('pane.collections.menu.createBib.collection')); menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.collection')); @@ -2018,14 +2056,14 @@ var ZoteroPane = new function() else if (itemGroup.isSearch()) { show = [ m.editSelectedCollection, - m.removeCollection, + m.deleteCollection, m.sep2, m.exportCollection, m.createBibCollection, m.loadReport ]; - menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('pane.collections.menu.remove.savedSearch')); + menu.childNodes[m.deleteCollection].setAttribute('label', Zotero.getString('pane.collections.menu.delete.savedSearch')); var s = [m.exportCollection, m.createBibCollection, m.loadReport]; if (!this.itemsView.rowCount) { @@ -2046,10 +2084,10 @@ var ZoteroPane = new function() } else if (itemGroup.isDuplicates() || itemGroup.isUnfiled()) { show = [ - m.removeCollection + m.deleteCollection ]; - menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('general.remove')); + menu.childNodes[m.deleteCollection].setAttribute('label', Zotero.getString('general.hide')); } else if (itemGroup.isHeader()) { if (itemGroup.ref.id == 'commons-header') { @@ -2068,7 +2106,7 @@ var ZoteroPane = new function() // Disable some actions if user doesn't have write access // // Some actions are disabled via their commands in onCollectionSelected() - var s = [m.newSubcollection, m.editSelectedCollection, m.removeCollection]; + var s = [m.newSubcollection, m.editSelectedCollection, m.deleteCollection, m.deleteCollectionAndItems]; if (itemGroup.isWithinGroup() && !itemGroup.editable && !itemGroup.isDuplicates() && !itemGroup.isUnfiled()) { disable = disable.concat(s); } @@ -2337,7 +2375,7 @@ var ZoteroPane = new function() } // Plural if necessary - menu.childNodes[m.deleteFromLibrary].setAttribute('label', Zotero.getString('pane.items.menu.erase' + multiple)); + menu.childNodes[m.deleteFromLibrary].setAttribute('label', Zotero.getString('pane.items.menu.moveToTrash' + multiple)); menu.childNodes[m.exportItems].setAttribute('label', Zotero.getString('pane.items.menu.export' + multiple)); menu.childNodes[m.createBib].setAttribute('label', Zotero.getString('pane.items.menu.createBib' + multiple)); menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.items.menu.generateReport' + multiple)); diff --git a/chrome/content/zotero/zoteroPane.xul b/chrome/content/zotero/zoteroPane.xul @@ -245,6 +245,7 @@ <menuitem label="&zotero.collections.showUnfiledItems;" oncommand="ZoteroPane_Local.setVirtual(ZoteroPane_Local.getSelectedLibraryID(), 'unfiled', true)"/> <menuitem oncommand="ZoteroPane_Local.editSelectedCollection();"/> <menuitem oncommand="ZoteroPane_Local.deleteSelectedCollection();"/> + <menuitem oncommand="ZoteroPane_Local.deleteSelectedCollection(true);"/> <menuseparator/> <menuitem oncommand="Zotero_File_Interface.exportCollection();"/> <menuitem oncommand="Zotero_File_Interface.bibliographyFromCollection();"/> diff --git a/chrome/locale/en-US/zotero/zotero.dtd b/chrome/locale/en-US/zotero/zotero.dtd @@ -85,7 +85,7 @@ <!ENTITY zotero.items.menu.attach.fileLink "Attach Link to File…"> <!ENTITY zotero.items.menu.restoreToLibrary "Restore to Library"> -<!ENTITY zotero.items.menu.duplicateItem "Duplicate Selected Item"> +<!ENTITY zotero.items.menu.duplicateItem "Duplicate Item"> <!ENTITY zotero.items.menu.mergeItems "Merge Items…"> <!ENTITY zotero.duplicatesMerge.versionSelect "Choose the version of the item to use as the master item:"> diff --git a/chrome/locale/en-US/zotero/zotero.properties b/chrome/locale/en-US/zotero/zotero.properties @@ -34,11 +34,13 @@ general.permissionDenied = Permission Denied general.character.singular = character general.character.plural = characters general.create = Create +general.delete = Delete general.seeForMoreInformation = See %S for more information. general.enable = Enable general.disable = Disable general.remove = Remove general.reset = Reset +general.hide = Hide general.quit = Quit general.useDefault = Use Default general.openDocumentation = Open Documentation @@ -132,8 +134,14 @@ date.relative.daysAgo.multiple = %S days ago date.relative.yearsAgo.one = 1 year ago date.relative.yearsAgo.multiple = %S years ago +pane.collections.delete.title = Delete Collection pane.collections.delete = Are you sure you want to delete the selected collection? -pane.collections.deleteSearch = Are you sure you want to delete the selected search? +pane.collections.delete.keepItems = Items within this collection will not be deleted. +pane.collections.deleteWithItems.title = Delete Collection and Items +pane.collections.deleteWithItems = Are you sure you want to delete the selected collection and move all items within it to the Trash? + +pane.collections.deleteSearch.title = Delete Search +pane.collections.deleteSearch = Are you sure you want to delete the selected search? pane.collections.emptyTrash = Are you sure you want to permanently remove items in the Trash? pane.collections.newCollection = New Collection pane.collections.name = Enter a name for this collection: @@ -149,8 +157,9 @@ pane.collections.duplicate = Duplicate Items pane.collections.menu.rename.collection = Rename Collection… pane.collections.menu.edit.savedSearch = Edit Saved Search -pane.collections.menu.remove.collection = Remove Collection… -pane.collections.menu.remove.savedSearch = Remove Saved Search… +pane.collections.menu.delete.collection = Delete Collection… +pane.collections.menu.delete.collectionAndItems = Delete Collection and Items… +pane.collections.menu.delete.savedSearch = Delete Saved Search… pane.collections.menu.export.collection = Export Collection… pane.collections.menu.export.savedSearch = Export Saved Search… pane.collections.menu.createBib.collection = Create Bibliography From Collection… @@ -180,22 +189,22 @@ pane.items.trash.multiple = Are you sure you want to move the selected items to pane.items.delete.title = Delete pane.items.delete = Are you sure you want to delete the selected item? pane.items.delete.multiple = Are you sure you want to delete the selected items? -pane.items.menu.remove = Remove Selected Item -pane.items.menu.remove.multiple = Remove Selected Items -pane.items.menu.erase = Delete Selected Item from Library… -pane.items.menu.erase.multiple = Delete Selected Items from Library… -pane.items.menu.export = Export Selected Item… -pane.items.menu.export.multiple = Export Selected Items… -pane.items.menu.createBib = Create Bibliography from Selected Item… -pane.items.menu.createBib.multiple = Create Bibliography from Selected Items… -pane.items.menu.generateReport = Generate Report from Selected Item… -pane.items.menu.generateReport.multiple = Generate Report from Selected Items… +pane.items.menu.remove = Remove Item from Collection +pane.items.menu.remove.multiple = Remove Items from Collection +pane.items.menu.moveToTrash = Move Item to Trash… +pane.items.menu.moveToTrash.multiple = Move Items to Trash… +pane.items.menu.export = Export Item… +pane.items.menu.export.multiple = Export Items… +pane.items.menu.createBib = Create Bibliography from Item… +pane.items.menu.createBib.multiple = Create Bibliography from Items… +pane.items.menu.generateReport = Generate Report from Item… +pane.items.menu.generateReport.multiple = Generate Report from Items… pane.items.menu.reindexItem = Reindex Item pane.items.menu.reindexItem.multiple = Reindex Items pane.items.menu.recognizePDF = Retrieve Metadata for PDF pane.items.menu.recognizePDF.multiple = Retrieve Metadata for PDFs -pane.items.menu.createParent = Create Parent Item from Selected Item -pane.items.menu.createParent.multiple = Create Parent Items from Selected Items +pane.items.menu.createParent = Create Parent Item +pane.items.menu.createParent.multiple = Create Parent Items pane.items.menu.renameAttachments = Rename File from Parent Metadata pane.items.menu.renameAttachments.multiple = Rename Files from Parent Metadata diff --git a/chrome/skin/default/zotero/bindings/tagselector.css b/chrome/skin/default/zotero/bindings/tagselector.css @@ -25,19 +25,18 @@ checkbox padding: 0 .25em 0 .25em !important; -moz-user-focus: ignore; max-width: 250px; + border: 1px solid transparent; /* always include border so height is same as zotero-clicky */ } /* Visible out-of-scope tags should be grey */ -#tags-toggle label[inScope="false"] +#tags-toggle label[inScope=false]:not([hasColor=true]) { color: #666 !important; } -/* Don't display clicky effect to out-of-scope icons */ -label.zotero-clicky[inScope="false"]:hover, -label.zotero-clicky[inScope="false"]:active +#tags-toggle label[inScope=false][hasColor=true] { - background: inherit !important; + opacity: .6; } #tags-toggle label[draggedOver="true"]