More tags box fixes (follow-up to f932f312eb)

Use the Notifier for all tags box updates to ensure that it always updates.

Also fix the tag count and some other things.

Post-tab focus still isn't 100% correct in all situations, but it's real close.
This commit is contained in:
Dan Stillman 2013-03-15 04:13:14 -04:00
parent 4e1fbf9747
commit 1e59c5ab7e
2 changed files with 226 additions and 173 deletions

View file

@ -118,7 +118,7 @@
this.mode = this.getAttribute('mode'); this.mode = this.getAttribute('mode');
} }
this._notifierID = Zotero.Notifier.registerObserver(this, ['setting']); this._notifierID = Zotero.Notifier.registerObserver(this, ['item-tag', 'setting']);
]]> ]]>
</constructor> </constructor>
@ -142,6 +142,55 @@
} }
return; return;
} }
else if (type == 'item-tag') {
let itemID, tagID;
for (var i=0; i<ids.length; i++) {
[itemID, tagID] = ids[i].split('-');
if (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> </body>
</method> </method>
@ -166,14 +215,11 @@
while(rows.hasChildNodes()) { while(rows.hasChildNodes()) {
rows.removeChild(rows.firstChild); rows.removeChild(rows.firstChild);
} }
var tags = self.item.getTags(); var tags = self.item.getTags() || [];
if (tags) { for (var i=0; i<tags.length; i++) {
for (var i=0; i<tags.length; i++) { self.addDynamicRow(tags[i], i+1);
self.addDynamicRow(tags[i], i+1);
}
} }
self.updateCount(tags.length);
self.updateCount(0);
self._reloading = false; self._reloading = false;
self._focusField(); self._focusField();
@ -187,6 +233,7 @@
<method name="addDynamicRow"> <method name="addDynamicRow">
<parameter name="tagObj"/> <parameter name="tagObj"/>
<parameter name="tabindex"/> <parameter name="tabindex"/>
<parameter name="skipAppend"/>
<body> <body>
<![CDATA[ <![CDATA[
if (tagObj) { if (tagObj) {
@ -224,9 +271,11 @@
row.appendChild(remove); 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; return row;
]]> ]]>
@ -234,10 +283,13 @@
</method> </method>
<!--
Update various attributes of a row to match the given tag
and current editability
-->
<method name="updateRow"> <method name="updateRow">
<parameter name="row"/> <parameter name="row"/>
<parameter name="tagObj"/> <parameter name="tagObj"/>
<parameter name="tabindex"/>
<body><![CDATA[ <body><![CDATA[
if (tagObj) { if (tagObj) {
var tagID = tagObj.id; var tagID = tagObj.id;
@ -274,7 +326,7 @@
var self = this; var self = this;
remove.addEventListener('click', function () { remove.addEventListener('click', function () {
self._lastTabIndex = false; self._lastTabIndex = false;
document.getBindingParent(this).remove(tagID); document.getBindingParent(this).item.removeTag(tagID);
// Return focus to items pane // Return focus to items pane
var tree = document.getElementById('zotero-items-tree'); var tree = document.getElementById('zotero-items-tree');
@ -302,7 +354,9 @@
valueElement.className = 'zotero-box-label'; valueElement.className = 'zotero-box-label';
if (this.clickable) { if (this.clickable) {
valueElement.setAttribute('ztabindex', tabindex); if (tabindex) {
valueElement.setAttribute('ztabindex', tabindex);
}
valueElement.addEventListener('click', function (event) { valueElement.addEventListener('click', function (event) {
/* Skip right-click on Windows */ /* Skip right-click on Windows */
if (event.button) { if (event.button) {
@ -476,12 +530,7 @@
case event.DOM_VK_ESCAPE: case event.DOM_VK_ESCAPE:
// Reset field to original value // Reset field to original value
if (target.getAttribute('multiline')) { target.value = target.getAttribute('value');
target.value = "";
}
else {
target.value = target.getAttribute('value');
}
var tagsbox = Zotero.getAncestorByTagName(focused, 'tagsbox'); var tagsbox = Zotero.getAncestorByTagName(focused, 'tagsbox');
@ -571,7 +620,8 @@
if (!rows) { if (!rows) {
rows = value.match(/\n/g).length + 1; 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 // Move cursor to end
textbox.selectionStart = value.length; textbox.selectionStart = value.length;
]]></body> ]]></body>
@ -583,21 +633,11 @@
<body> <body>
<![CDATA[ <![CDATA[
Zotero.debug('Hiding editor'); 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 fieldName = 'tag';
var tabindex = textbox.getAttribute('ztabindex'); var tabindex = textbox.getAttribute('ztabindex');
var oldValue = textbox.getAttribute('value');
var value = textbox.value; var value = textbox.value;
var tagsbox = Zotero.getAncestorByTagName(textbox, 'tagsbox'); var tagsbox = Zotero.getAncestorByTagName(textbox, 'tagsbox');
@ -611,147 +651,67 @@
var rows = row.parentNode; var rows = row.parentNode;
// Tag id encoded as 'tag-1234' // 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/); var tagArray = value.split(/\r\n?|\n/);
if (saveChanges) { // Modifying existing tag with a single new one
// Modifying existing tag if (oldTagID && tagArray.length < 2) {
if (id && tagArray.length < 2) { if (value) {
if (value) { tagsbox.replace(oldTagID, 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 // Existing tag cleared
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
else { else {
id = tagsbox.add(value); this.item.removeTag(oldTagID);
// New tag }
if (id) { }
// Stay put, since a tag was added above // Multiple tags
if (this._tabDirection == -1) { else if (tagArray.length > 1) {
this._tabDirection = false; 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 { else {
// Go back one, since we'll remove this below textbox.value = textbox.getAttribute('value');
if (this._tabDirection == 1) { this.textboxToLabel(textbox);
this._lastTabIndex--;
}
} }
} }
}
if (id) { this.item.addTags(tagArray);
var elem = this.createValueElement(
value,
tabindex
);
var row = textbox.parentNode; Zotero.DB.commitTransaction();
row.replaceChild(elem, textbox);
this.updateRow(row, Zotero.Tags.get(id), tabindex); if (lastTag) {
this._lastTabIndex = this.item.getTags().length;
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);
}
} }
this.reload();
} }
// Single tag at end
else { else {
// Just remove the row row.parentNode.removeChild(row);
// this.item.addTag(value);
// If there's an open popup, this throws NODE CANNOT BE FOUND
try {
var row = rows.removeChild(row);
}
catch (e) {}
} }
]]> ]]>
</body> </body>
@ -769,16 +729,84 @@
</method> </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"> <method name="add">
<parameter name="value"/> <parameter name="tagID"/>
<body> <body><![CDATA[
<![CDATA[ var rowsElement = this.id('tagRows');
if (value) { var rows = rowsElement.childNodes;
return this.item.addTag(value);
// 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;
]]> if (collation.compareString(1, name, labels[i].textContent) > 0) {
</body> 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> </method>
@ -803,12 +831,35 @@
<method name="remove"> <method name="remove">
<parameter name="id"/> <parameter name="id"/>
<body> <body><![CDATA[
<![CDATA[ var rowsElement = this.id('tagRows');
this.item.removeTag(id);
this.reload(); var row = rowsElement.getElementsByAttribute('id', 'tag-' + id);
]]> row = row.length ? row[0] : false;
</body> 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> </method>
@ -816,7 +867,7 @@
<parameter name="count"/> <parameter name="count"/>
<body> <body>
<![CDATA[ <![CDATA[
if(count === null) { if(typeof count == 'undefined') {
var tags = this.item.getTags(); var tags = this.item.getTags();
if(tags) if(tags)
count = tags.length; count = tags.length;

View file

@ -3869,7 +3869,9 @@ Zotero.Item.prototype.replaceTag = function(oldTagID, newTag) {
Zotero.DB.commitTransaction(); Zotero.DB.commitTransaction();
Zotero.Notifier.trigger('modify', 'item', this.id); Zotero.Notifier.trigger('modify', 'item', this.id);
Zotero.Notifier.trigger('remove', 'item-tag', this.id + '-' + oldTagID); 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; return id;
} }