diff --git a/chrome/content/zotero/contextPane.js b/chrome/content/zotero/contextPane.js index 05cf3c31a4..7309dbfc85 100644 --- a/chrome/content/zotero/contextPane.js +++ b/chrome/content/zotero/contextPane.js @@ -48,7 +48,10 @@ var ZoteroContextPane = new function () { var _itemContexts = []; var _notesContexts = []; - + + var _globalDeckIndex = []; + var _preventGlobalDeckChange = false; + // Using attribute instead of property to set 'selectedIndex' // is more reliable @@ -173,6 +176,20 @@ var ZoteroContextPane = new function () { if (Zotero_Tabs.deck.children.length == 1) { _notesContexts.forEach(x => x.notesListRef.current.setExpanded(false)); } + // Close tab specific notes if tab id no longer exists, but + // do that only when unloaded tab is reloaded + setTimeout(() => { + var contextNodes = Array.from(_notesPaneDeck.children); + for (let contextNode of contextNodes) { + var nodes = Array.from(contextNode.children[2].children); + for (let node of nodes) { + var tabID = node.getAttribute('data-tab-id'); + if (!document.getElementById(tabID)) { + node.remove(); + } + } + } + }); } else if (action == 'select') { // It seems that changing `hidden` or `collapsed` values might @@ -185,6 +202,14 @@ var ZoteroContextPane = new function () { _tabCover.classList.add('hidden'); } else if (Zotero_Tabs.selectedType == 'reader') { + if (_panesDeck.selectedIndex == 1 + && _notesPaneDeck.selectedPanel.selectedIndex != 2 + && !_preventGlobalDeckChange) { + let libraryID = _notesPaneDeck.selectedPanel.getAttribute('data-library-id'); + _globalDeckIndex[libraryID] = _notesPaneDeck.selectedPanel.selectedIndex; + } + _preventGlobalDeckChange = false; + var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID); if (reader) { _tabCover.classList.remove('hidden'); @@ -204,6 +229,17 @@ var ZoteroContextPane = new function () { var notesContext = _getNotesContext(attachment.libraryID); notesContext.updateFromCache(); } + + let tabNotesDeck = _notesPaneDeck.selectedPanel.children[2]; + let selectedIndex = Array.from(tabNotesDeck.children).findIndex(x => x.getAttribute('data-tab-id') == ids[0]); + if (selectedIndex != -1) { + tabNotesDeck.setAttribute('selectedIndex', selectedIndex); + _notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 2); + } + else { + let libraryID = _notesPaneDeck.selectedPanel.getAttribute('data-library-id'); + _notesPaneDeck.selectedPanel.setAttribute('selectedIndex', _globalDeckIndex[libraryID]); + } })(); } @@ -416,12 +452,17 @@ var ZoteroContextPane = new function () { editor.className = 'zotero-context-pane-pinned-note'; editor.setAttribute('flex', 1); noteContainer.append(title, editor); + + let tabNotesDeck = document.createElement('deck'); + tabNotesDeck.className = 'zotero-context-pane-tab-notes-deck'; + tabNotesDeck.setAttribute('flex', 1); let contextNode = document.createXULElement('deck'); - contextNode.append(list, noteContainer); + contextNode.append(list, noteContainer, tabNotesDeck); _notesPaneDeck.append(contextNode); contextNode.className = 'context-node'; + contextNode.setAttribute('data-library-id', libraryID); contextNode.setAttribute('selectedIndex', 0); editor.returnHandler = () => { @@ -448,7 +489,7 @@ var ZoteroContextPane = new function () { input.value = ''; _updateNotesList(); - _setPinnedNote(note.id); + _setPinnedNote(note); } function _createNote(child) { @@ -462,10 +503,7 @@ var ZoteroContextPane = new function () { } item.parentID = attachment.parentID; } - editor.mode = 'edit'; - editor.item = item; - editor.parentItem = null; - editor.focus(); + _setPinnedNote(item); _updateAddToNote(); input.value = ''; @@ -670,7 +708,10 @@ var ZoteroContextPane = new function () { { - _setPinnedNote(id); + let item = Zotero.Items.get(id); + if (item) { + _setPinnedNote(item); + } }} onContextMenu={(id, event) => { document.getElementById('context-pane-list-move-to-trash').setAttribute('disabled', readOnly); @@ -725,19 +766,56 @@ var ZoteroContextPane = new function () { return !Zotero.Libraries.get(libraryID).editable; } - function _setPinnedNote(itemID) { - var item = Zotero.Items.get(itemID); - if (!item) { - return; - } + function _setPinnedNote(item) { var readOnly = _isLibraryReadOnly(item.libraryID); var context = _getNotesContext(item.libraryID); if (context) { var { editor, node } = context; - node.setAttribute('selectedIndex', 1); - editor.mode = readOnly ? 'view' : 'edit'; - editor.item = item; - editor.parentItem = null; + + let isChild = false; + var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID); + if (reader) { + let attachment = Zotero.Items.get(reader.itemID); + if (attachment.parentItemID == item.parentItemID) { + isChild = true; + } + } + + var tabNotesDeck = _notesPaneDeck.selectedPanel.children[2]; + if (isChild) { + var vbox = document.createElement('vbox'); + vbox.setAttribute('data-tab-id', Zotero_Tabs.selectedID); + vbox.style.display = 'flex'; + + editor = document.createElement('zoteronoteeditor'); + editor.style.display = 'flex'; + editor.style.width = '100%'; + vbox.append(editor); + + tabNotesDeck.append(vbox); + + editor.mode = readOnly ? 'view' : 'edit'; + editor.item = item; + editor.parentItem = null; + editor.returnHandler = () => { + _panesDeck.setAttribute('selectedIndex', 1); + _notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 0); + vbox.remove(); + _updateAddToNote(); + _preventGlobalDeckChange = true; + }; + + _notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 2); + tabNotesDeck.setAttribute('selectedIndex', tabNotesDeck.children.length - 1); + } + else { + node.setAttribute('selectedIndex', 1); + editor.mode = readOnly ? 'view' : 'edit'; + editor.item = item; + editor.parentItem = null; + } + + editor.focus(); node.querySelector('.zotero-context-pane-editor-parent-line').innerHTML = ''; var parentItem = item.parentItem; diff --git a/chrome/content/zotero/elements/noteEditor.js b/chrome/content/zotero/elements/noteEditor.js index 0b9a2a270d..a4a229e53f 100644 --- a/chrome/content/zotero/elements/noteEditor.js +++ b/chrome/content/zotero/elements/noteEditor.js @@ -306,13 +306,13 @@ } async focusFirst() { - let n = 0; - while (!this._editorInstance && n++ < 100) { - await Zotero.Promise.delay(10); - } - await this._editorInstance._initPromise; - this._iframe.focus(); try { + let n = 0; + while (!this._editorInstance && n++ < 100) { + await Zotero.Promise.delay(10); + } + await this._editorInstance._initPromise; + this._iframe.focus(); this._editorInstance._iframeWindow.document.querySelector('.toolbar-button-return').focus(); } catch(e) { diff --git a/chrome/content/zotero/tabs.js b/chrome/content/zotero/tabs.js index b59b0995c4..1c80a601e2 100644 --- a/chrome/content/zotero/tabs.js +++ b/chrome/content/zotero/tabs.js @@ -152,7 +152,7 @@ var Zotero_Tabs = new function () { * @param {Function} onClose * @return {{ id: string, container: XULElement}} id - tab id, container - a new tab container created in the deck */ - this.add = function ({ type, data, title, index, select, onClose }) { + this.add = function ({ id, type, data, title, index, select, onClose }) { if (typeof type != 'string') { } if (typeof title != 'string') { @@ -164,7 +164,7 @@ var Zotero_Tabs = new function () { if (onClose !== undefined && typeof onClose != 'function') { throw new Error(`'onClose' should be a function (was ${typeof onClose})`); } - var id = 'tab-' + Zotero.Utilities.randomString(); + id = id || 'tab-' + Zotero.Utilities.randomString(); var container = document.createXULElement('vbox'); container.id = id; this.deck.appendChild(container); @@ -251,7 +251,7 @@ var Zotero_Tabs = new function () { closedIDs.push(id); } this._history.push(historyEntry); - Zotero.Notifier.trigger('close', 'tab', [closedIDs]); + Zotero.Notifier.trigger('close', 'tab', [closedIDs], true); this._update(); }; @@ -333,6 +333,7 @@ var Zotero_Tabs = new function () { if (tab.type === 'reader-unloaded') { this.close(tab.id); Zotero.Reader.open(tab.data.itemID, null, { + tabID: tab.id, title: tab.title, tabIndex, allowDuplicate: true @@ -388,6 +389,7 @@ var Zotero_Tabs = new function () { } this.close(tab.id); this.add({ + id: tab.id, type: 'reader-unloaded', title: tab.title, index: tabIndex, diff --git a/chrome/content/zotero/xpcom/reader.js b/chrome/content/zotero/xpcom/reader.js index d20c8d07ca..c5622fd441 100644 --- a/chrome/content/zotero/xpcom/reader.js +++ b/chrome/content/zotero/xpcom/reader.js @@ -1017,7 +1017,7 @@ class ReaderInstance { } class ReaderTab extends ReaderInstance { - constructor({ itemID, title, sidebarWidth, sidebarOpen, bottomPlaceholderHeight, index, background }) { + constructor({ itemID, title, sidebarWidth, sidebarOpen, bottomPlaceholderHeight, index, tabID, background }) { super(); this._itemID = itemID; this._sidebarWidth = sidebarWidth; @@ -1026,6 +1026,7 @@ class ReaderTab extends ReaderInstance { this._showItemPaneToggle = true; this._window = Services.wm.getMostRecentWindow('navigator:browser'); let { id, container } = this._window.Zotero_Tabs.add({ + id: tabID, type: 'reader', title: title || '', index, @@ -1060,20 +1061,24 @@ class ReaderTab extends ReaderInstance { // events in PDF reader iframe when mouse up happens over another iframe // i.e. note-editor. There should be a better way to solve this this._window.addEventListener('pointerup', (event) => { - if (this._window.Zotero_Tabs.selectedID === this.tabID - && this._iframeWindow - && event.target - && event.target.closest - && !event.target.closest('#outerContainer')) { - let evt = new this._iframeWindow.CustomEvent('mouseup', { bubbles: false }); - evt.clientX = event.clientX; - evt.clientY = event.clientY; - this._iframeWindow.dispatchEvent(evt); + try { + if (this._window.Zotero_Tabs.selectedID === this.tabID + && this._iframeWindow + && event.target + && event.target.closest + && !event.target.closest('#outerContainer')) { + let evt = new this._iframeWindow.CustomEvent('mouseup', { bubbles: false }); + evt.clientX = event.clientX; + evt.clientY = event.clientY; + this._iframeWindow.dispatchEvent(evt); - evt = new this._iframeWindow.CustomEvent('pointerup', { bubbles: false }); - evt.clientX = event.clientX; - evt.clientY = event.clientY; - this._iframeWindow.dispatchEvent(evt); + evt = new this._iframeWindow.CustomEvent('pointerup', { bubbles: false }); + evt.clientX = event.clientX; + evt.clientY = event.clientY; + this._iframeWindow.dispatchEvent(evt); + } + } + catch(e) { } }); } @@ -1362,7 +1367,7 @@ class Reader { await this.open(item.id, location, options); } - async open(itemID, location, { title, tabIndex, openInBackground, openInWindow, allowDuplicate } = {}) { + async open(itemID, location, { title, tabIndex, tabID, openInBackground, openInWindow, allowDuplicate } = {}) { this._loadSidebarState(); this.triggerAnnotationsImportCheck(itemID); let reader; @@ -1404,6 +1409,7 @@ class Reader { itemID, title, index: tabIndex, + tabID, background: openInBackground, sidebarWidth: this._sidebarWidth, sidebarOpen: this._sidebarOpen,