Merge branch '4.0'

This commit is contained in:
Dan Stillman 2013-03-17 01:24:50 -04:00
commit 6915b7aa56
13 changed files with 554 additions and 367 deletions

View file

@ -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;
}
// Modifying existing tag with a single new one
if (oldTagID && tagArray.length < 2) {
if (value) {
tagsbox.replace(oldTagID, value);
}
// // 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;
}
// 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;

View file

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

View file

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

View file

@ -961,7 +961,7 @@ Zotero.Collection.prototype.erase = function(deleteItems) {
}
}
if (del.length) {
Zotero.Items.erase(del);
Zotero.Items.trash(del);
}
// Remove relations

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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"]