diff --git a/addon/chrome/content/overlay.xul b/addon/chrome/content/overlay.xul index ad4d396..8afa9d6 100644 --- a/addon/chrome/content/overlay.xul +++ b/addon/chrome/content/overlay.xul @@ -25,30 +25,30 @@ - - - - - + + + + + - - - - - + + + + + - - + + - - - + + + @@ -99,9 +99,9 @@ - - - + + + diff --git a/addon/chrome/content/wizard.xul b/addon/chrome/content/wizard.xul index c5d4e4c..6b9ee8e 100644 --- a/addon/chrome/content/wizard.xul +++ b/addon/chrome/content/wizard.xul @@ -11,7 +11,7 @@ %knowledgeDTD; ]> - + + \ No newline at end of file diff --git a/addon/chrome/content/workspace.xul b/addon/chrome/content/workspace.xul index 5b683a9..54e4c76 100644 --- a/addon/chrome/content/workspace.xul +++ b/addon/chrome/content/workspace.xul @@ -33,25 +33,25 @@ - - - - - + + + + + - - - - - - - + + + + + + + - - + + diff --git a/src/addon.ts b/src/addon.ts index c75c7b2..4ae645a 100644 --- a/src/addon.ts +++ b/src/addon.ts @@ -2,10 +2,10 @@ * This file defines the plugin's structure. */ -import AddonEvents from "./zotero/events"; +import ZoteroEvents from "./zotero/events"; import ZoteroViews from "./zotero/views"; -import ReaderViews from "./reader/annotationButton"; -import AddonWizard from "./wizard"; +import ReaderViews from "./reader/readerViews"; +import WizardWindow from "./wizard/wizardWindow"; import { TemplateController, TemplateAPI } from "./template/templateController"; import SyncInfoWindow from "./sync/syncInfoWindow"; import SyncListWindow from "./sync/syncListWindow"; @@ -13,16 +13,16 @@ import SyncController from "./sync/syncController"; import WorkspaceWindow from "./workspace/workspaceWindow"; import WorkspaceOutline from "./workspace/workspaceOutline"; import WorkspaceMenu from "./workspace/workspaceMenu"; -import NoteUtils from "./editor/noteUtils"; -import NoteParse from "./editor/noteParse"; -import NoteExportWindow from "./editor/noteExportWindow"; -import NoteExport from "./editor/noteExportController"; -import EditorViews from "./editor/editorUI"; +import NoteUtils from "./note/noteUtils"; +import NoteParse from "./note/noteParse"; +import NoteExportWindow from "./note/noteExportWindow"; +import NoteExport from "./note/noteExportController"; +import EditorViews from "./editor/editorViews"; import EditorController from "./editor/editorController"; import TemplateWindow from "./template/templateWindow"; class Knowledge4Zotero { - public events: AddonEvents; + public ZoteroEvents: ZoteroEvents; // Zotero UI public ZoteroViews: ZoteroViews; // Reader UI @@ -32,7 +32,7 @@ class Knowledge4Zotero { public WorkspaceWindow: WorkspaceWindow; public WorkspaceMenu: WorkspaceMenu; // First-run wizard - public wizard: AddonWizard; + public WizardWindow: WizardWindow; // Sync tools public SyncInfoWindow: SyncInfoWindow; public SyncListWindow: SyncListWindow; @@ -51,7 +51,7 @@ class Knowledge4Zotero { public EditorController: EditorController; constructor() { - this.events = new AddonEvents(this); + this.ZoteroEvents = new ZoteroEvents(this); this.ZoteroViews = new ZoteroViews(this); this.ReaderViews = new ReaderViews(this); this.WorkspaceOutline = new WorkspaceOutline(this); @@ -59,7 +59,7 @@ class Knowledge4Zotero { this.WorkspaceMenu = new WorkspaceMenu(this); this.EditorViews = new EditorViews(this); this.EditorController = new EditorController(this); - this.wizard = new AddonWizard(this); + this.WizardWindow = new WizardWindow(this); this.SyncInfoWindow = new SyncInfoWindow(this); this.SyncListWindow = new SyncListWindow(this); this.SyncController = new SyncController(this); diff --git a/src/editor/editorUI.ts b/src/editor/editorViews.ts similarity index 99% rename from src/editor/editorUI.ts rename to src/editor/editorViews.ts index e844146..003e056 100644 --- a/src/editor/editorUI.ts +++ b/src/editor/editorViews.ts @@ -261,7 +261,7 @@ class EditorViews extends AddonBase { } addCitationButton.addEventListener("click", async (e) => { - this._Addon.events.onEditorEvent( + this._Addon.ZoteroEvents.onEditorEvent( new EditorMessage("insertCitation", { params: { noteItem: noteItem, @@ -549,7 +549,7 @@ class EditorViews extends AddonBase { message.type = e.target.getAttribute("eventType"); message.content.event = e as XUL.XULEvent; message.content.editorInstance = editorInstances; - this._Addon.events.onEditorEvent(message); + this._Addon.ZoteroEvents.onEditorEvent(message); }); return dropdown; } @@ -594,7 +594,7 @@ class EditorViews extends AddonBase { message.type = e.target.getAttribute("eventType"); message.content.event = e as XUL.XULEvent; message.content.editorInstance = editorInstances; - this._Addon.events.onEditorEvent(message); + this._Addon.ZoteroEvents.onEditorEvent(message); e.stopPropagation(); popup.remove(); }); @@ -661,7 +661,7 @@ class EditorViews extends AddonBase { const newLineString = newLines.join("\n"); const notifyFlag: ZoteroPromise = Zotero.Promise.defer(); const notifierName = "insertLinkWait"; - this._Addon.events.addNotifyListener( + this._Addon.ZoteroEvents.addNotifyListener( notifierName, ( event: string, @@ -675,7 +675,7 @@ class EditorViews extends AddonBase { ids.includes(targetNote.id) ) { notifyFlag.resolve(); - this._Addon.events.removeNotifyListener(notifierName); + this._Addon.ZoteroEvents.removeNotifyListener(notifierName); } } ); diff --git a/src/index.ts b/src/index.ts index a223088..d71e881 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ Zotero.Knowledge4Zotero = new Knowledge4Zotero(); window.addEventListener( "load", async function (e) { - Zotero.Knowledge4Zotero.events.onInit(); + Zotero.Knowledge4Zotero.ZoteroEvents.onInit(); }, false ); diff --git a/src/editor/convertMD.js b/src/note/convertMD.js similarity index 100% rename from src/editor/convertMD.js rename to src/note/convertMD.js diff --git a/src/note/noteController.ts b/src/note/noteController.ts new file mode 100644 index 0000000..072c458 --- /dev/null +++ b/src/note/noteController.ts @@ -0,0 +1,140 @@ +/* + * This file realizes note tools. + */ + +import Knowledge4Zotero from "../addon"; +import AddonBase from "../module"; + +class NoteController extends AddonBase { + public currentLine: any; + constructor(parent: Knowledge4Zotero) { + super(parent); + this.currentLine = []; + } + + async getNoteFromLink(uri: string) { + const params = this._Addon.NoteParse.parseParamsFromLink(uri); + if (!params.libraryID) { + return { + item: false, + infoText: "Library does not exist or access denied.", + }; + } + Zotero.debug(params); + let item = await Zotero.Items.getByLibraryAndKeyAsync( + params.libraryID, + params.noteKey + ); + 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", + }; + } + + public async onSelectionChange(editor: Zotero.EditorInstance) { + // 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); + } + + // 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.NoteParse.parseListElements(e).length - 1; + } + + // Find list index if current line is inside a list + if (diveTagNames.includes(focusNode.tagName)) { + const eleList = this._Addon.NoteParse.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.currentLine[editor._item.id] = currentLineIndex; + if (realElement.tagName === "A") { + let link = (realElement as HTMLLinkElement).href; + let linkedNote = (await this.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.EditorViews.updateEditorPopupButtons(editor, link); + } else { + await this._Addon.EditorViews.updateEditorPopupButtons( + editor, + undefined + ); + } + } + } +} + +export default NoteController; diff --git a/src/editor/noteExportController.ts b/src/note/noteExportController.ts similarity index 100% rename from src/editor/noteExportController.ts rename to src/note/noteExportController.ts diff --git a/src/editor/noteExportWindow.ts b/src/note/noteExportWindow.ts similarity index 100% rename from src/editor/noteExportWindow.ts rename to src/note/noteExportWindow.ts diff --git a/src/editor/noteParse.ts b/src/note/noteParse.ts similarity index 100% rename from src/editor/noteParse.ts rename to src/note/noteParse.ts diff --git a/src/editor/noteUtils.ts b/src/note/noteUtils.ts similarity index 100% rename from src/editor/noteUtils.ts rename to src/note/noteUtils.ts diff --git a/src/reader/annotationButton.ts b/src/reader/readerViews.ts similarity index 99% rename from src/reader/annotationButton.ts rename to src/reader/readerViews.ts index 8266969..369f978 100644 --- a/src/reader/annotationButton.ts +++ b/src/reader/readerViews.ts @@ -117,7 +117,7 @@ class ReaderViews extends AddonBase { if (link) { const note = (await this._Addon.NoteUtils.getNoteFromLink(link)).item; if (note && note.id) { - await this._Addon.events.onEditorEvent( + await this._Addon.ZoteroEvents.onEditorEvent( new EditorMessage("onNoteLink", { params: { item: note, diff --git a/src/wizard.ts b/src/wizard/wizardWindow.ts similarity index 98% rename from src/wizard.ts rename to src/wizard/wizardWindow.ts index 62d89b7..ba864ed 100644 --- a/src/wizard.ts +++ b/src/wizard/wizardWindow.ts @@ -2,11 +2,11 @@ * This file contains the first-run wizard window code. */ -import Knowledge4Zotero from "./addon"; -import { EditorMessage } from "./utils"; -import AddonBase from "./module"; +import Knowledge4Zotero from "../addon"; +import { EditorMessage } from "../utils"; +import AddonBase from "../module"; -class AddonWizard extends AddonBase { +class WizardWindow extends AddonBase { enableSetup: boolean; enableCollection: boolean; collectionName: string; @@ -275,7 +275,7 @@ class AddonWizard extends AddonBase { (Zotero.Items.get(noteID) as Zotero.Item).setNote( Zotero.locale === "zh-CN" ? this.templateCN : this.template ); - await this._Addon.events.onEditorEvent( + await this._Addon.ZoteroEvents.onEditorEvent( new EditorMessage("setMainNote", { params: { itemID: noteID, enableConfirm: false, enableOpen: true }, }) @@ -285,4 +285,4 @@ class AddonWizard extends AddonBase { } } -export default AddonWizard; +export default WizardWindow; diff --git a/src/workspace/workspaceWindow.ts b/src/workspace/workspaceWindow.ts index 870a7be..bd0982a 100644 --- a/src/workspace/workspaceWindow.ts +++ b/src/workspace/workspaceWindow.ts @@ -166,20 +166,20 @@ class WorkspaceWindow extends AddonBase { } else if (e.data.type === "getMindMapData") { this._Addon.WorkspaceOutline.updateOutline(); } else if (e.data.type === "jumpNode") { - this._Addon.events.onEditorEvent( + this._Addon.ZoteroEvents.onEditorEvent( new EditorMessage("jumpNode", { params: e.data, }) ); } else if (e.data.type === "jumpNote") { Zotero.debug(e.data); - this._Addon.events.onEditorEvent( + this._Addon.ZoteroEvents.onEditorEvent( new EditorMessage("onNoteLink", { params: await this._Addon.NoteUtils.getNoteFromLink(e.data.link), }) ); } else if (e.data.type === "moveNode") { - this._Addon.events.onEditorEvent( + this._Addon.ZoteroEvents.onEditorEvent( new EditorMessage("moveNode", { params: e.data, }) diff --git a/src/zotero/events.ts b/src/zotero/events.ts index 525553f..b37d421 100644 --- a/src/zotero/events.ts +++ b/src/zotero/events.ts @@ -6,7 +6,7 @@ import Knowledge4Zotero from "../addon"; import { CopyHelper, EditorMessage } from "../utils"; import AddonBase from "../module"; -class AddonEvents extends AddonBase { +class ZoteroEvents extends AddonBase { notifierCallback: any; notifierCbkDict: any; constructor(parent: Knowledge4Zotero) { @@ -233,7 +233,7 @@ class AddonEvents extends AddonBase { params: await this._Addon.NoteUtils.getNoteFromLink(uri.spec), }, }; - await this._Addon.events.onEditorEvent(message); + await this._Addon.ZoteroEvents.onEditorEvent(message); }, newChannel: function (uri: any) { this.doAction(uri); @@ -1403,4 +1403,4 @@ class AddonEvents extends AddonBase { } } -export default AddonEvents; +export default ZoteroEvents; diff --git a/src/zotero/views.ts b/src/zotero/views.ts index 7913dc1..03be511 100644 --- a/src/zotero/views.ts +++ b/src/zotero/views.ts @@ -72,7 +72,7 @@ class ZoteroViews extends AddonBase { button.setAttribute("id", "zotero-tb-knowledge-openwindow"); button.setAttribute("label", "New Main Note"); button.addEventListener("click", (e) => { - this._Addon.events.onEditorEvent( + this._Addon.ZoteroEvents.onEditorEvent( new EditorMessage("createWorkspace", {}) ); }); @@ -112,7 +112,7 @@ class ZoteroViews extends AddonBase { span1.append(span2, span3, span4); treeRow.append(span1); treeRow.addEventListener("click", (e) => { - this._Addon.events.onEditorEvent( + this._Addon.ZoteroEvents.onEditorEvent( new EditorMessage("openWorkspace", { event: e }) ); }); @@ -182,7 +182,7 @@ class ZoteroViews extends AddonBase { menuitem.setAttribute( "oncommand", ` - Zotero.Knowledge4Zotero.events.onEditorEvent({ + Zotero.Knowledge4Zotero.ZoteroEvents.onEditorEvent({ type: "insert${type}UsingTemplate", content: { params: { templateName: "${template.name}", copy: ${copyMode} },