From 6b1940cebbb3c41d15be027309684c6b7d58e72e Mon Sep 17 00:00:00 2001 From: Abe Jellinek Date: Mon, 29 Jan 2024 15:07:14 -0500 Subject: [PATCH] Add generic menu for all unfocused editable-texts --- chrome/content/zotero/editMenuOverlay.js | 16 ++--- .../content/zotero/elements/editableText.js | 35 +++++++++-- chrome/content/zotero/elements/itemBox.js | 39 ++++++++---- .../zotero/xpcom/utilities_internal.js | 60 ++++++++++--------- 4 files changed, 96 insertions(+), 54 deletions(-) diff --git a/chrome/content/zotero/editMenuOverlay.js b/chrome/content/zotero/editMenuOverlay.js index 340f09fcf1..47a1ad53d6 100644 --- a/chrome/content/zotero/editMenuOverlay.js +++ b/chrome/content/zotero/editMenuOverlay.js @@ -101,16 +101,16 @@ function goBuildEditContextMenu() { MozXULElement.parseXULToFragment(` - - + + - - - - - + + + + + - + `) diff --git a/chrome/content/zotero/elements/editableText.js b/chrome/content/zotero/elements/editableText.js index 4ed10e0213..35f398c60a 100644 --- a/chrome/content/zotero/elements/editableText.js +++ b/chrome/content/zotero/elements/editableText.js @@ -224,12 +224,6 @@ event.preventDefault(); } }); - input.addEventListener('contextmenu', (event) => { - // Prevent the text editing context menu from opening when unfocused - if (document.activeElement !== this._input) { - event.preventDefault(); - } - }); let focused = false; let selectionStart = this._input?.selectionStart; @@ -298,4 +292,33 @@ } } customElements.define("editable-text", EditableText); + + document.addEventListener('contextmenu', (event) => { + if (event.defaultPrevented + || !event.target.closest('editable-text') + || document.activeElement && event.target.contains(document.activeElement)) { + return; + } + + event.preventDefault(); + + let editableText = event.target.closest('editable-text'); + let menupopup = document.getElementById('zotero-editable-text-menu'); + if (!menupopup) { + menupopup = document.createXULElement('menupopup'); + menupopup.id = 'zotero-editable-text-menu'; + + let popupset = document.querySelector('popupset'); + if (!popupset) { + popupset = document.createXULElement('popupset'); + document.documentElement.append(popupset); + } + popupset.append(menupopup); + } + + menupopup.addEventListener('popupshowing', () => { + Zotero.Utilities.Internal.updateEditContextMenu(menupopup, editableText); + }, { once: true }); + menupopup.openPopupAtScreen(event.screenX + 1, event.screenY + 1, true); + }); } diff --git a/chrome/content/zotero/elements/itemBox.js b/chrome/content/zotero/elements/itemBox.js index 1a0f61cb7d..d0f5ce2861 100644 --- a/chrome/content/zotero/elements/itemBox.js +++ b/chrome/content/zotero/elements/itemBox.js @@ -95,7 +95,7 @@
-
+
@@ -207,9 +207,24 @@ event => this.capitalizeCreatorName(event)); this._linkMenu.addEventListener('popupshowing', () => { - let disabled = !this._linkMenu.dataset.link; - this._id('zotero-link-menu-view-online').disabled = disabled; - this._id('zotero-link-menu-copy').disabled = disabled; + let menu = this._linkMenu; + let link = menu.dataset.link; + let val = menu.dataset.val; + + let viewOnline = this._id('zotero-link-menu-view-online'); + let copy = this._id('zotero-link-menu-copy'); + + viewOnline.disabled = !link; + copy.disabled = !link; + copy.hidden = link === val; + + let existingCopyMenuitem = menu.querySelector('menuitem[data-action="copy"]'); + if (existingCopyMenuitem) { + existingCopyMenuitem.after(copy); + } + else { + menu.append(copy); + } }); this._id('zotero-link-menu-view-online').addEventListener( @@ -535,12 +550,11 @@ } } - for (let i = 0; i < fieldNames.length; i++) { - var fieldName = fieldNames[i]; + for (let fieldName of fieldNames) { if (this._ignoreFields.includes(fieldName)) { continue; } - var val = ''; + let val = ''; if (fieldName) { var fieldID = Zotero.ItemFields.getID(fieldName); @@ -604,13 +618,13 @@ } let openLinkButton; let link = val; - let addContextMenu = false; + let addLinkContextMenu = false; // TEMP - NSF (homepage) if ((fieldName == 'url' || fieldName == 'homepage') // Only make plausible HTTP URLs clickable && Zotero.Utilities.isHTTPURL(val, true)) { openLinkButton = this.createOpenLinkIcon(val); - addContextMenu = true; + addLinkContextMenu = true; } else if (fieldName == 'DOI' && val && typeof val == 'string') { // Pull out DOI, in case there's a prefix @@ -625,7 +639,7 @@ .replace(/"/g, '%22'); openLinkButton = this.createOpenLinkIcon(doi); link = doi; - addContextMenu = true; + addLinkContextMenu = true; } } let rowData = document.createElement('div'); @@ -634,9 +648,10 @@ if (openLinkButton) { rowData.appendChild(openLinkButton); } - if (addContextMenu) { + if (addLinkContextMenu) { rowData.oncontextmenu = (event) => { this._linkMenu.dataset.link = link; + this._linkMenu.dataset.val = val; document.popupNode = rowLabel.parentElement; let menupopup = this._id('zotero-link-menu'); @@ -2405,7 +2420,9 @@ } handlePopupOpening(event, popup) { + event.preventDefault(); event.stopPropagation(); + let isRightClick = event.type == 'contextmenu'; if (!isRightClick) { event.target.style.visibility = "visible"; diff --git a/chrome/content/zotero/xpcom/utilities_internal.js b/chrome/content/zotero/xpcom/utilities_internal.js index c7ea5a7943..3dee2310a1 100644 --- a/chrome/content/zotero/xpcom/utilities_internal.js +++ b/chrome/content/zotero/xpcom/utilities_internal.js @@ -2334,10 +2334,9 @@ Zotero.Utilities.Internal = { win.goUpdateGlobalEditMenuItems(true); for (let menuitem of Array.from(menupopup.children)) { - if (!menuitem.hasAttribute('data-edit-menu-item')) { - break; + if (menuitem.hasAttribute('data-edit-menu-item')) { + menuitem.remove(); } - menuitem.remove(); } if (target && target.tagName === 'editable-text') { @@ -2345,11 +2344,9 @@ Zotero.Utilities.Internal = { } let targetInput = target?.closest('input, textarea'); let showEdit = targetInput - && !targetInput.readOnly && targetInput.ownerDocument.activeElement && targetInput.ownerDocument.activeElement === targetInput; - let showCopyField = targetInput && !showEdit; - let showPasteField = showCopyField && !targetInput.readOnly; + let disablePasteField = targetInput && targetInput.readOnly; let editMenuItems = []; if (showEdit) { @@ -2360,29 +2357,34 @@ Zotero.Utilities.Internal = { editMenuItems.push(menuitem); } } - else { - if (showCopyField) { - let copyMenuitem = win.document.createXULElement('menuitem'); - copyMenuitem.setAttribute('data-l10n-id', 'text-action-copy'); - copyMenuitem.setAttribute('data-edit-menu-item', 'true'); - copyMenuitem.disabled = !targetInput.value; - copyMenuitem.addEventListener('command', () => { - Zotero.Utilities.Internal.copyTextToClipboard(targetInput.value); - }); - editMenuItems.push(copyMenuitem); - } - if (showPasteField) { - let pasteMenuitem = win.document.createXULElement('menuitem'); - pasteMenuitem.setAttribute('data-l10n-id', 'text-action-paste'); - pasteMenuitem.setAttribute('data-edit-menu-item', 'true'); - pasteMenuitem.disabled = win.document.getElementById('cmd_paste')?.disabled; - pasteMenuitem.addEventListener('command', () => { - targetInput.focus(); - targetInput.value = Zotero.Utilities.Internal.getClipboard('text/unicode') || ''; - targetInput.dispatchEvent(new Event('input')); - }); - editMenuItems.push(pasteMenuitem); - } + else if (targetInput) { + let targetInputWeak = new WeakRef(targetInput); + + let copyMenuitem = win.document.createXULElement('menuitem'); + copyMenuitem.setAttribute('data-l10n-id', 'text-action-copy'); + copyMenuitem.setAttribute('data-edit-menu-item', 'true'); + copyMenuitem.setAttribute('data-action', 'copy'); + copyMenuitem.disabled = !targetInput.value; + copyMenuitem.addEventListener('command', () => { + let targetInput = targetInputWeak.deref(); + if (!targetInput) return; + Zotero.Utilities.Internal.copyTextToClipboard(targetInput.value); + }); + editMenuItems.push(copyMenuitem); + + let pasteMenuitem = win.document.createXULElement('menuitem'); + pasteMenuitem.setAttribute('data-l10n-id', 'text-action-paste'); + pasteMenuitem.setAttribute('data-edit-menu-item', 'true'); + pasteMenuitem.setAttribute('data-action', 'paste'); + pasteMenuitem.disabled = disablePasteField || win.document.getElementById('cmd_paste')?.disabled; + pasteMenuitem.addEventListener('command', () => { + let targetInput = targetInputWeak.deref(); + if (!targetInput) return; + targetInput.focus(); + targetInput.value = Zotero.Utilities.Internal.getClipboard('text/unicode') || ''; + targetInput.dispatchEvent(new Event('input')); + }); + editMenuItems.push(pasteMenuitem); } if (editMenuItems.length) { if (menupopup.childElementCount) {