From e555df871f766631bacc23d200be7a44c94a016e Mon Sep 17 00:00:00 2001 From: xiangyu <3170102889@zju.edu.cn> Date: Sat, 3 Sep 2022 13:49:54 +0800 Subject: [PATCH] resolve: #133 jump to note line resolve: #108 resolve: #99 refresh note fix: min version bug --- addon/install.rdf | 4 +- src/events.ts | 316 +++++++++++++++++++++++++++++++--------------- src/knowledge.ts | 64 ++++++++-- src/views.ts | 31 ++--- update.rdf | 4 +- 5 files changed, 284 insertions(+), 135 deletions(-) diff --git a/addon/install.rdf b/addon/install.rdf index 055f164..888f98a 100644 --- a/addon/install.rdf +++ b/addon/install.rdf @@ -19,14 +19,14 @@ zotero@chnm.gmu.edu - 6.14 + 6.0.14 * juris-m@juris-m.github.io - 6.14 + 6.0.14 * diff --git a/src/events.ts b/src/events.ts index 8cb3e15..82d11ab 100644 --- a/src/events.ts +++ b/src/events.ts @@ -486,6 +486,7 @@ class AddonEvents extends AddonBase { "middle" ); if (addLinkDropDown) { + addLinkDropDown.classList.add("more-dropdown"); // If the editor initialization fails, the addLinkDropDown does not exist if (isMainNote) { // This is a main knowledge, hide all buttons except the export button and add title @@ -758,13 +759,96 @@ class AddonEvents extends AddonBase { _window.document.head.append(messageScript); } - editor._knowledgeUIInitialized = true; - if (isPrint) { editor._iframeWindow.postMessage({ type: "exportPDF" }, "*"); this._Addon.knowledge._pdfNoteId = -1; return; } + + const moreDropdown: HTMLElement = Array.prototype.filter.call( + _window.document.querySelectorAll(".more-dropdown"), + (e) => e.id !== "knowledge-addlink" + )[0]; + if (!moreDropdown.getAttribute("ob")) { + moreDropdown.setAttribute("ob", "true"); + const dropdownOb = new MutationObserver((e) => { + if ( + e[0].addedNodes.length && + (e[0].addedNodes[0] as HTMLElement).classList.contains("popup") + ) { + const dropdownPopup = moreDropdown.querySelector(".popup"); + if (dropdownPopup) { + const refreshButton = _window.document.createElement("button"); + refreshButton.classList.add("option"); + refreshButton.innerText = "Refresh Editor"; + refreshButton.addEventListener("click", (e) => { + editor.init({ + item: editor._item, + viewMode: editor._viewMode, + readOnly: editor._readOnly, + disableUI: editor._disableUI, + onReturn: editor._onReturn, + iframeWindow: editor._iframeWindow, + popup: editor._popup, + state: editor._state, + }); + }); + const copyLinkButton = _window.document.createElement("button"); + copyLinkButton.classList.add("option"); + copyLinkButton.innerText = "Copy Note Link"; + copyLinkButton.addEventListener("click", (e) => { + const link = this._Addon.knowledge.getNoteLink(noteItem); + const linkTemplate = this._Addon.template.renderTemplate( + "[QuickInsert]", + "link, subNoteItem, noteItem", + [link, noteItem, noteItem] + ); + new CopyHelper() + .addText(link, "text/unicode") + .addText(linkTemplate, "text/html") + .copy(); + this._Addon.views.showProgressWindow( + "Better Notes", + "Note Link Copied" + ); + }); + + const copyLinkAtLineButton = + _window.document.createElement("button"); + copyLinkAtLineButton.classList.add("option"); + copyLinkAtLineButton.innerText = "Copy Note Link of Current Line"; + copyLinkAtLineButton.addEventListener("click", (e) => { + const link = this._Addon.knowledge.getNoteLink(noteItem, { + withLine: true, + }); + const linkTemplate = this._Addon.template.renderTemplate( + "[QuickInsert]", + "link, subNoteItem, noteItem", + [link, noteItem, noteItem] + ); + new CopyHelper() + .addText(link, "text/unicode") + .addText(linkTemplate, "text/html") + .copy(); + this._Addon.views.showProgressWindow( + "Better Notes", + `Note Link of Line ${ + this._Addon.knowledge.currentLine[noteItem.id] + 1 + } Copied` + ); + }); + dropdownPopup.append( + refreshButton, + copyLinkButton, + copyLinkAtLineButton + ); + } + } + }); + dropdownOb.observe(moreDropdown, { childList: true }); + } + + editor._knowledgeUIInitialized = true; } else if (message.type === "insertCitation") { /* message.content = { @@ -870,15 +954,18 @@ class AddonEvents extends AddonBase { annotations, -1 ); + let currentLine = + this._Addon.knowledge.currentLine[ + this._Addon.knowledge.getWorkspaceNote().id + ]; + currentLine = currentLine ? currentLine : -1; this._Addon.views.showProgressWindow( "Better Notes", `[Auto] Insert Annotation to ${ - this._Addon.knowledge.currentLine >= 0 - ? `line ${this._Addon.knowledge.currentLine} in` - : "end of" + currentLine >= 0 ? `line ${currentLine} in` : "end of" } main note` ); - if (this._Addon.knowledge.currentLine >= 0) { + if (currentLine >= 0) { // Compute annotation lines length const temp = document.createElementNS( "http://www.w3.org/1999/xhtml", @@ -887,7 +974,9 @@ class AddonEvents extends AddonBase { temp.innerHTML = html; const elementList = this._Addon.parse.parseHTMLElements(temp); // Move cursor foward - this._Addon.knowledge.currentLine += elementList.length; + this._Addon.knowledge.currentLine[ + this._Addon.knowledge.getWorkspaceNote().id + ] += elementList.length; } } else if (message.type === "jumpNode") { /* @@ -949,6 +1038,7 @@ class AddonEvents extends AddonBase { params: { item: Zotero.Item | boolean, infoText: string + args: {} } } */ @@ -958,6 +1048,10 @@ class AddonEvents extends AddonBase { Zotero.debug( `Knowledge4Zotero: onNoteLink ${message.content.params.item.id}` ); + // Copy and save + const oldEditors = Zotero.Notes._editorInstances.map( + (e): string => e.instanceID + ); let _window = this._Addon.knowledge.getWorkspaceWindow(); if (_window) { if ( @@ -973,109 +1067,125 @@ class AddonEvents extends AddonBase { } else { ZoteroPane.openNoteWindow(message.content.params.item.id); } + if (message.content.params.args.line) { + let t = 0; + let newEditors = Zotero.Notes._editorInstances.filter( + (e) => !oldEditors.includes(e.instanceID) && e._knowledgeUIInitialized + ); + while (newEditors.length === 0 && t < 500) { + t += 1; + await Zotero.Promise.delay(10); + newEditors = Zotero.Notes._editorInstances.filter( + (e) => !oldEditors.includes(e.instanceID) + ); + } + newEditors.forEach((e) => { + this._Addon.views.scrollToLine( + e, + // Scroll to line + message.content.params.args.line + ); + }); + } } else if (message.type === "noteEditorSelectionChange") { const editor = message.content.editorInstance as Zotero.EditorInstance; - if ( - editor._item.id === Zotero.Prefs.get("Knowledge4Zotero.mainKnowledgeID") - ) { - // Update current line index - const _window = editor._iframeWindow; - const selection = _window.document.getSelection(); - if (!selection || !selection.focusNode) { - return; - } - const realElement = selection.focusNode.parentElement; - let focusNode = selection.focusNode as XUL.Element; - if (!focusNode || !realElement) { - return; - } - function getChildIndex(node) { - return Array.prototype.indexOf.call(node.parentNode.childNodes, node); - } + // Update current line index + const _window = editor._iframeWindow; + const selection = _window.document.getSelection(); + if (!selection || !selection.focusNode) { + return; + } + const realElement = selection.focusNode.parentElement; + let focusNode = selection.focusNode as XUL.Element; + if (!focusNode || !realElement) { + return; + } - // Make sure this is a direct child node of editor - try { + function getChildIndex(node) { + return Array.prototype.indexOf.call(node.parentNode.childNodes, node); + } + + // Make sure this is a direct child node of editor + try { + while ( + focusNode.parentElement && + (!focusNode.parentElement.className || + focusNode.parentElement.className.indexOf("primary-editor") === -1) + ) { + focusNode = focusNode.parentNode as XUL.Element; + } + } catch (e) { + return; + } + + if (!focusNode.parentElement) { + return; + } + + let currentLineIndex = getChildIndex(focusNode); + + // Parse list + const diveTagNames = ["OL", "UL", "LI"]; + + // Find list elements before current line + const listElements = Array.prototype.filter.call( + Array.prototype.slice.call( + focusNode.parentElement.childNodes, + 0, + currentLineIndex + ), + (e) => diveTagNames.includes(e.tagName) + ); + + for (const e of listElements) { + currentLineIndex += this._Addon.parse.parseListElements(e).length - 1; + } + + // Find list index if current line is inside a list + if (diveTagNames.includes(focusNode.tagName)) { + const eleList = this._Addon.parse.parseListElements(focusNode); + for (const i in eleList) { + if (realElement.parentElement === eleList[i]) { + currentLineIndex += Number(i); + break; + } + } + } + Zotero.debug(`Knowledge4Zotero: line ${currentLineIndex} selected.`); + console.log(currentLineIndex); + Zotero.debug( + `Current Element: ${focusNode.outerHTML}; Real Element: ${realElement.outerHTML}` + ); + this._Addon.knowledge.currentLine[editor._item.id] = currentLineIndex; + if (realElement.tagName === "A") { + let link = (realElement as HTMLLinkElement).href; + let linkedNote = (await this._Addon.knowledge.getNoteFromLink(link)) + .item; + if (linkedNote) { + let t = 0; + let linkPopup = _window.document.querySelector(".link-popup"); while ( - focusNode.parentElement && - (!focusNode.parentElement.className || - focusNode.parentElement.className.indexOf("primary-editor") === - -1) + !( + linkPopup && + (linkPopup.querySelector("a") as unknown as HTMLLinkElement) + .href === link + ) && + t < 100 ) { - focusNode = focusNode.parentNode as XUL.Element; - } - } catch (e) { - return; - } - - if (!focusNode.parentElement) { - return; - } - - let currentLineIndex = getChildIndex(focusNode); - - // Parse list - const diveTagNames = ["OL", "UL", "LI"]; - - // Find list elements before current line - const listElements = Array.prototype.filter.call( - Array.prototype.slice.call( - focusNode.parentElement.childNodes, - 0, - currentLineIndex - ), - (e) => diveTagNames.includes(e.tagName) - ); - - for (const e of listElements) { - currentLineIndex += this._Addon.parse.parseListElements(e).length - 1; - } - - // Find list index if current line is inside a list - if (diveTagNames.includes(focusNode.tagName)) { - const eleList = this._Addon.parse.parseListElements(focusNode); - for (const i in eleList) { - if (realElement.parentElement === eleList[i]) { - currentLineIndex += Number(i); - break; - } - } - } - Zotero.debug(`Knowledge4Zotero: line ${currentLineIndex} selected.`); - console.log(currentLineIndex); - Zotero.debug( - `Current Element: ${focusNode.outerHTML}; Real Element: ${realElement.outerHTML}` - ); - this._Addon.knowledge.currentLine = currentLineIndex; - if (realElement.tagName === "A") { - let link = (realElement as HTMLLinkElement).href; - let linkedNote = (await this._Addon.knowledge.getNoteFromLink(link)) - .item; - if (linkedNote) { - let t = 0; - let linkPopup = _window.document.querySelector(".link-popup"); - while ( - !( - linkPopup && - (linkPopup.querySelector("a") as unknown as HTMLLinkElement) - .href === link - ) && - t < 100 - ) { - t += 1; - linkPopup = _window.document.querySelector(".link-popup"); - await Zotero.Promise.delay(30); - } - await this._Addon.views.updateEditorPopupButtons( - editor._iframeWindow, - link - ); - } else { - await this._Addon.views.updateEditorPopupButtons( - editor._iframeWindow, - undefined - ); + t += 1; + linkPopup = _window.document.querySelector(".link-popup"); + await Zotero.Promise.delay(30); } + await this._Addon.views.updateEditorPopupButtons( + editor._iframeWindow, + link + ); + } else { + await this._Addon.views.updateEditorPopupButtons( + editor._iframeWindow, + undefined + ); } } } else if (message.type === "addHeading") { diff --git a/src/knowledge.ts b/src/knowledge.ts index 2b1af40..72cb5f1 100644 --- a/src/knowledge.ts +++ b/src/knowledge.ts @@ -5,7 +5,7 @@ import { pick } from "./file_picker"; import AddonBase from "./module"; class Knowledge extends AddonBase { - currentLine: number; + currentLine: object; currentNodeID: number; workspaceWindow: Window; workspaceTabId: string; @@ -21,7 +21,7 @@ class Knowledge extends AddonBase { constructor(parent: Knowledge4Zotero) { super(parent); this._firstInit = true; - this.currentLine = -1; + this.currentLine = {}; this.currentNodeID = -1; this._pdfNoteId = -1; } @@ -72,7 +72,7 @@ class Knowledge extends AddonBase { this.workspaceTabId = ""; await this.waitWorkspaceReady(); this.setWorkspaceNote("main"); - this.currentLine = -1; + this.currentLine[this.getWorkspaceNote().id] = -1; this._Addon.views.initKnowledgeWindow(win); this._Addon.views.switchView(OutlineType.treeView); this._Addon.views.updateOutline(); @@ -88,7 +88,7 @@ class Knowledge extends AddonBase { index: 1, data: {}, select: select, - onClose: undefined + onClose: undefined, }); this.workspaceTabId = id; const _iframe = window.document.createElement("browser"); @@ -107,7 +107,7 @@ class Knowledge extends AddonBase { this._Addon.views.hideMenuBar(this.workspaceWindow.document); - this.currentLine = -1; + this.currentLine[this.getWorkspaceNote().id] = -1; this._Addon.views.initKnowledgeWindow(this.workspaceWindow); this._Addon.views.switchView(OutlineType.treeView); this._Addon.views.updateOutline(); @@ -204,7 +204,7 @@ class Knowledge extends AddonBase { this.previewItemID = note.id; } else { // Set line to default - this.currentLine = -1; + this.currentLine[note.id] = -1; } await this.waitWorkspaceReady(); let noteEditor: any = await this.getWorkspaceEditor(type); @@ -237,12 +237,19 @@ class Knowledge extends AddonBase { } await noteEditor._editorInstance._initPromise; + const position = this._Addon.views.getEditorElement( + noteEditor._editorInstance._iframeWindow.document + ).parentNode.scrollTop; // Due to unknown reasons, only after the second init the editor will be correctly loaded. // Thus we must init it twice if (this._firstInit) { this._firstInit = false; await noteEditor.initEditor(); } + await this._Addon.views.scrollToPosition( + noteEditor._editorInstance, + position + ); if (type === "main") { this._Addon.views.updateOutline(); this._Addon.views.updateWordCount(); @@ -292,10 +299,8 @@ class Knowledge extends AddonBase { } let noteLines = this.getLinesInNote(note); if (lineIndex < 0) { - lineIndex = - this.getWorkspaceNote().id === note.id && this.currentLine >= 0 - ? this.currentLine - : noteLines.length; + lineIndex = this.currentLine[note.id]; + lineIndex = lineIndex && lineIndex >= 0 ? lineIndex : noteLines.length; } else if (lineIndex >= noteLines.length) { lineIndex = noteLines.length; } @@ -456,7 +461,13 @@ class Knowledge extends AddonBase { ); } - getNoteLink(note: Zotero.Item) { + getNoteLink( + note: Zotero.Item, + options: { + ignore?: boolean; + withLine?: boolean; + } = { ignore: false, withLine: false } + ) { let libraryID = note.libraryID; let library = Zotero.Libraries.get(libraryID); let groupID: string; @@ -468,7 +479,28 @@ class Knowledge extends AddonBase { return ""; } let noteKey = note.key; - return `zotero://note/${groupID}/${noteKey}/`; + let link = `zotero://note/${groupID}/${noteKey}/`; + const addParam = (link: string, param: string): string => { + const lastChar = link[link.length - 1]; + if (lastChar === "/") { + link += "?"; + } else if (lastChar !== "?" && lastChar !== "&") { + link += "&"; + } + return `${link}${param}`; + }; + if (options.ignore || options.withLine) { + if (options.ignore) { + link = addParam(link, "ignore=1"); + } + if (options.withLine) { + if (!this.currentLine[note.id]) { + this.currentLine[note.id] = 0; + } + link = addParam(link, `line=${this.currentLine[note.id]}`); + } + } + return link; } getAnnotationLink(annotation: Zotero.Item) { @@ -694,7 +726,11 @@ class Knowledge extends AddonBase { lineIndex: number = -1 ): TreeModel.Node { if (lineIndex < 0) { - lineIndex = this.currentLine; + lineIndex = this.currentLine[note.id]; + lineIndex = + lineIndex && lineIndex >= 0 + ? lineIndex + : this.getLinesInNote(note).length; } let nodes = this.getNoteTreeAsList(note); if (!nodes.length || nodes[0].model.lineIndex > lineIndex) { @@ -1156,11 +1192,13 @@ class Knowledge extends AddonBase { if (!item || !item.isNote()) { return { item: false, + args: params, infoText: "Note does not exist or is not a note.", }; } return { item: item, + args: params, infoText: "OK", }; } diff --git a/src/views.ts b/src/views.ts index ec14757..1eca535 100644 --- a/src/views.ts +++ b/src/views.ts @@ -135,7 +135,7 @@ class AddonViews extends AddonBase { ); } const dropdown = _document.createElement("div"); - dropdown.setAttribute("class", "dropdown more-dropdown"); + dropdown.setAttribute("class", "dropdown"); dropdown.setAttribute("id", id); const button = _document.createElement("button"); button.setAttribute("class", "toolbar-button"); @@ -252,22 +252,12 @@ class AddonViews extends AddonBase { // @ts-ignore const scrollNum = eleList[lineIndex].offsetTop; (editorElement.parentNode as HTMLElement).scrollTo(0, scrollNum); - - const texView = instance._iframeWindow.document.getElementById("texView"); - if (texView) { - texView.scrollTo(0, scrollNum); - } } scrollToPosition(instance: Zotero.EditorInstance, offset: number) { let editorElement = this.getEditorElement(instance._iframeWindow.document); // @ts-ignore (editorElement.parentNode as HTMLElement).scrollTo(0, offset); - - const texView = instance._iframeWindow.document.getElementById("texView"); - if (texView) { - texView.scrollTo(0, offset); - } } addNewKnowledgeButton() { @@ -427,7 +417,9 @@ class AddonViews extends AddonBase { linkIndex[1] )}\n${newLineString}`; }, - this._Addon.knowledge.currentLine, + this._Addon.knowledge.currentLine[ + this._Addon.knowledge.getWorkspaceNote().id + ], true ); // wait the first modify finish @@ -447,7 +439,9 @@ class AddonViews extends AddonBase { } }); await this._Addon.knowledge.scrollWithRefresh( - this._Addon.knowledge.currentLine + this._Addon.knowledge.currentLine[ + this._Addon.knowledge.getWorkspaceNote().id + ] ); } }); @@ -466,7 +460,12 @@ class AddonViews extends AddonBase { updateButton.addEventListener("click", async (e) => { Zotero.debug("ZBN: Update Link Text"); const noteLines = this._Addon.knowledge.getLinesInNote(); - let line = noteLines[this._Addon.knowledge.currentLine]; + let line = + noteLines[ + this._Addon.knowledge.currentLine[ + this._Addon.knowledge.getWorkspaceNote().id + ] + ]; Zotero.debug(line); let linkStart = line.search(/ zotero@chnm.gmu.edu - 6.14 + 6.0.14 * https://github.com/windingwind/zotero-better-notes/releases/latest/download/zotero-better-notes.xpi @@ -17,7 +17,7 @@ juris-m@juris-m.github.io - 6.14 + 6.0.14 * https://github.com/windingwind/zotero-better-notes/releases/latest/download/zotero-better-notes.xpi