From 59d5013952b789f2146eaab80e91ed96d85f0fb5 Mon Sep 17 00:00:00 2001 From: Abe Jellinek Date: Tue, 31 May 2022 11:23:19 -0700 Subject: [PATCH] fx-compat: Item box: Fix menu actions, plus/minus buttons --- chrome/content/zotero/elements/itemBox.js | 306 ++++++++++++---------- 1 file changed, 162 insertions(+), 144 deletions(-) diff --git a/chrome/content/zotero/elements/itemBox.js b/chrome/content/zotero/elements/itemBox.js index fd37af316f..60e2f01089 100644 --- a/chrome/content/zotero/elements/itemBox.js +++ b/chrome/content/zotero/elements/itemBox.js @@ -62,91 +62,15 @@ this.content = MozXULElement.parseXULToFragment(`
- + - - + + - - + + @@ -198,6 +122,98 @@ shadow.append(s2); shadow.appendChild(document.importNode(this.content, true)); + + this._creatorTypeMenu.addEventListener('popupshowing', () => { + var typeBox = document.popupNode.localName == 'th' ? document.popupNode : document.popupNode.parentNode; + var index = parseInt(typeBox.getAttribute('fieldname').split('-')[1]); + + var item = this.item; + var exists = item.hasCreatorAt(index); + var moreCreators = item.numCreators() > index + 1; + + var hideMoveToTop = !exists || index < 2; + var hideMoveUp = !exists || index == 0; + var hideMoveDown = !exists || !moreCreators; + var hideMoveSep = hideMoveUp && hideMoveDown; + + this._id('zotero-creator-move-sep').setAttribute('hidden', hideMoveSep); + this._id('zotero-creator-move-to-top').setAttribute('hidden', hideMoveToTop); + this._id('zotero-creator-move-up').setAttribute('hidden', hideMoveUp); + this._id('zotero-creator-move-down').setAttribute('hidden', hideMoveDown); + }); + + this._creatorTypeMenu.addEventListener('command', async (event) => { + var typeBox = document.popupNode.localName == 'th' ? document.popupNode : document.popupNode.parentNode; + var index = parseInt(typeBox.getAttribute('fieldname').split('-')[1]); + + if (event.explicitOriginalTarget.className == 'zotero-creator-move') { + let dir; + switch (event.explicitOriginalTarget.id) { + case 'zotero-creator-move-to-top': + dir = 'top'; + break; + + case 'zotero-creator-move-up': + dir = 'up'; + break; + + case 'zotero-creator-move-down': + dir = 'down'; + break; + } + this.moveCreator(index, dir); + return; + } + + var typeID = event.explicitOriginalTarget.getAttribute('typeid'); + var row = typeBox.parentNode; + var fields = this.getCreatorFields(row); + fields.creatorTypeID = typeID; + typeBox.getElementsByTagName('label')[0].setAttribute( + 'value', + Zotero.getString( + 'creatorTypes.' + Zotero.CreatorTypes.getName(typeID) + ) + ); + typeBox.setAttribute('typeid', typeID); + + /* If a creator textbox is already open, we need to + change its autocomplete parameters so that it + completes on a creator with a different creator type */ + var changedParams = { + creatorTypeID: typeID + }; + this._updateAutoCompleteParams(row, changedParams); + + this.modifyCreator(index, fields); + if (this.saveOnEdit) { + await this.blurOpenField(); + await this.item.saveTx(); + } + }); + + this._id('creator-transform-title-case').addEventListener('command', + () => this.textTransform(document.popupNode, 'title')); + this._id('creator-transform-sentence-case').addEventListener('command', + () => this.textTransform(document.popupNode, 'sentence')); + + this._id('zotero-creator-transform-menu').addEventListener('popupshowing', (event) => { + var row = document.popupNode.closest('tr'); + var typeBox = row.querySelector('.creator-type-label'); + var index = parseInt(typeBox.getAttribute('fieldname').split('-')[1]); + var item = this.item; + var exists = item.hasCreatorAt(index); + if (exists) { + var fieldMode = item.getCreator(index).name !== undefined ? 1 : 0; + } + var hideTransforms = !exists || !!fieldMode; + if (hideTransforms) { + event.preventDefault(); + } + }); + + this._id('creator-transform-swap-names').addEventListener('command', + event => this.swapNames(event)); this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'itembox'); } @@ -687,9 +703,9 @@ // Place, in order of preference, after title, after type, // or at beginning var titleFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(this.item.itemTypeID, 'title'); - var field = this._infoTable.getElementsByAttribute('fieldname', Zotero.ItemFields.getName(titleFieldID)).item(0); + var field = this._infoTable.querySelector(`[fieldname="${Zotero.ItemFields.getName(titleFieldID)}"]`); if (!field) { - var field = this._infoTable.getElementsByAttribute('fieldname', 'itemType').item(0); + var field = this._infoTable.querySelector('[fieldName="itemType"]'); } if (field) { this._beforeRow = field.parentNode.nextSibling; @@ -832,15 +848,20 @@ ? Zotero.CreatorTypes.getID(creatorTypeIDOrName) : this._creatorTypeMenu.childNodes[0].getAttribute('typeid'); + var rowIndex = this._creatorCount; + var th = document.createElement("th"); th.setAttribute("typeid", typeID); - th.setAttribute("popup", "creator-type-menu"); - th.setAttribute("fieldname", 'creator-' + this._creatorCount + '-typeID'); + th.setAttribute("fieldname", 'creator-' + rowIndex + '-typeID'); if (this.editable) { th.className = 'creator-type-label zotero-clicky'; let span = document.createElement('span'); span.className = 'creator-type-dropmarker'; th.appendChild(span); + th.addEventListener('click', () => { + document.popupNode = th; + this._creatorTypeMenu.openPopup(th); + }); } else { th.className = 'creator-type-label'; @@ -858,8 +879,8 @@ var firstlast = document.createElement("span"); firstlast.className = 'creator-name-box'; firstlast.setAttribute("flex","1"); - var tabindex = this._tabIndexMinCreators + (this._creatorCount * 2); - var fieldName = 'creator-' + this._creatorCount + '-lastName'; + var tabindex = this._tabIndexMinCreators + (rowIndex * 2); + var fieldName = 'creator-' + rowIndex + '-lastName'; var lastNameElem = firstlast.appendChild( this.createValueElement( lastName, @@ -874,7 +895,7 @@ comma.className = 'comma'; firstlast.appendChild(comma); - var fieldName = 'creator-' + this._creatorCount + '-firstName'; + var fieldName = 'creator-' + rowIndex + '-firstName'; firstlast.appendChild( this.createValueElement( firstName, @@ -900,7 +921,7 @@ // Single/double field toggle var toggleButton = document.createElement('span'); toggleButton.setAttribute('fieldname', - 'creator-' + this._creatorCount + '-fieldMode'); + 'creator-' + rowIndex + '-fieldMode'); toggleButton.className = 'zotero-field-toggle zotero-clicky'; td.appendChild(toggleButton); @@ -913,10 +934,9 @@ this.disableButton(removeButton); } else { - removeButton.setAttribute("onclick", - "this.removeCreator(" - + this._creatorCount - + ", this.parentNode.parentNode)"); + removeButton.addEventListener("click", () => { + this.removeCreator(rowIndex, td.parentNode); + }); } td.appendChild(removeButton); @@ -1276,23 +1296,22 @@ disableButton(button) { button.setAttribute('disabled', true); - button.setAttribute('onclick', false); + button.setAttribute('onclick', false); } _enablePlusButton(button, creatorTypeID, fieldMode) { button.setAttribute('disabled', false); - button.onclick = function () { - var parent = this; - parent.disableButton(this); - parent.addCreatorRow(null, creatorTypeID, true); + button.onclick = () => { + this.disableButton(button); + this.addCreatorRow(null, creatorTypeID, true); }; } disableCreatorAddButtons() { // Disable the "+" button on all creator rows - var elems = this._infoTable.getElementsByAttribute('value', '+'); - for (var i = 0, len = elems.length; i < len; i++) { - this.disableButton(elems[i]); + var elems = this._infoTable.getElementsByClassName('zotero-clicky-plus'); + for (let elem of elems) { + this.disableButton(elem); } } @@ -1433,7 +1452,7 @@ labelToDelete.parentNode.removeChild(labelToDelete); // Enable the "+" button on the previous row - var elems = this._infoTable.getElementsByAttribute('value', '+'); + var elems = this._infoTable.getElementsByClassName('zotero-clicky-plus'); var button = elems[elems.length-1]; var creatorFields = this.getCreatorFields(button.closest('tr')); this._enablePlusButton(button, creatorFields.creatorTypeID, creatorFields.fieldMode); @@ -1552,8 +1571,9 @@ } }); // Tab/Shift-Tab - t.setAttribute('onchange', - 'this.handleCreatorAutoCompleteSelect(this)'); + t.addEventListener('change', () => { + this.handleCreatorAutoCompleteSelect(t, true); + }); if (creatorField == 'lastName') { t.setAttribute('fieldMode', elem.getAttribute('fieldMode')); @@ -2021,43 +2041,41 @@ /** * TODO: work with textboxes too */ - textTransform(label, mode) { - return (async function () { - var val = this._getFieldValue(label); - switch (mode) { - case 'title': - var newVal = Zotero.Utilities.capitalizeTitle(val.toLowerCase(), true); - break; - case 'sentence': - // capitalize the first letter, including after beginning punctuation - // capitalize after ?, ! and remove space(s) before those as well as colon analogous to capitalizeTitle function - // also deal with initial punctuation here - open quotes and Spanish beginning punctuation marks - newVal = val.toLowerCase().replace(/\s*:/, ":"); - newVal = newVal.replace(/(([\?!]\s*|^)([\'\"¡¿“‘„«\s]+)?[^\s])/g, function (x) { - return x.replace(/\s+/m, " ").toUpperCase();}); - break; - default: - throw ("Invalid transform mode '" + mode + "' in zoteroitembox.textTransform()"); - } - this._setFieldValue(label, newVal); - var fieldName = label.getAttribute('fieldname'); - this._modifyField(fieldName, newVal); - - // If this is a title field, convert the Short Title too - var isTitle = Zotero.ItemFields.getBaseIDFromTypeAndField( - this.item.itemTypeID, fieldName) == Zotero.ItemFields.getID('title'); - var shortTitleVal = this.item.getField('shortTitle'); - if (isTitle && newVal.toLowerCase().startsWith(shortTitleVal.toLowerCase())) { - this._modifyField('shortTitle', newVal.substr(0, shortTitleVal.length)); - } - - if (this.saveOnEdit) { - // If a field is open, blur it, which will trigger a save and cause - // the saveTx() to be a no-op - await this.blurOpenField(); - await this.item.saveTx(); - } - }); + async textTransform(label, mode) { + var val = this._getFieldValue(label); + switch (mode) { + case 'title': + var newVal = Zotero.Utilities.capitalizeTitle(val.toLowerCase(), true); + break; + case 'sentence': + // capitalize the first letter, including after beginning punctuation + // capitalize after ?, ! and remove space(s) before those as well as colon analogous to capitalizeTitle function + // also deal with initial punctuation here - open quotes and Spanish beginning punctuation marks + newVal = val.toLowerCase().replace(/\s*:/, ":"); + newVal = newVal.replace(/(([\?!]\s*|^)([\'\"¡¿“‘„«\s]+)?[^\s])/g, function (x) { + return x.replace(/\s+/m, " ").toUpperCase();}); + break; + default: + throw new Error("Invalid transform mode '" + mode + "' in ItemBox.textTransform()"); + } + this._setFieldValue(label, newVal); + var fieldName = label.getAttribute('fieldname'); + this._modifyField(fieldName, newVal); + + // If this is a title field, convert the Short Title too + var isTitle = Zotero.ItemFields.getBaseIDFromTypeAndField( + this.item.itemTypeID, fieldName) == Zotero.ItemFields.getID('title'); + var shortTitleVal = this.item.getField('shortTitle'); + if (isTitle && newVal.toLowerCase().startsWith(shortTitleVal.toLowerCase())) { + this._modifyField('shortTitle', newVal.substr(0, shortTitleVal.length)); + } + + if (this.saveOnEdit) { + // If a field is open, blur it, which will trigger a save and cause + // the saveTx() to be a no-op + await this.blurOpenField(); + await this.item.saveTx(); + } } getCreatorFields(row) { @@ -2434,7 +2452,7 @@ } _id(id) { - return this.shadowRoot.querySelector(`[id=${id}]`); + return this.shadowRoot.getElementById(id); } } customElements.define("item-box", ItemBox);