resolve: #133 jump to note line

resolve: #108
resolve: #99 refresh note
fix: min version bug
This commit is contained in:
xiangyu 2022-09-03 13:49:54 +08:00
parent 24524b36cf
commit e555df871f
5 changed files with 284 additions and 135 deletions

View File

@ -19,14 +19,14 @@
<em:targetApplication>
<Description>
<em:id>zotero@chnm.gmu.edu</em:id>
<em:minVersion>6.14</em:minVersion>
<em:minVersion>6.0.14</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<em:targetApplication>
<Description>
<em:id>juris-m@juris-m.github.io</em:id>
<em:minVersion>6.14</em:minVersion>
<em:minVersion>6.0.14</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>

View File

@ -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") {

View File

@ -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<object> {
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",
};
}

View File

@ -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(/<a /g);
@ -511,7 +510,9 @@ class AddonViews extends AddonBase {
await this._Addon.knowledge.setLinesToNote(undefined, noteLines);
this._Addon.knowledge.scrollWithRefresh(
this._Addon.knowledge.currentLine
this._Addon.knowledge.currentLine[
this._Addon.knowledge.getWorkspaceNote().id
]
);
});

View File

@ -9,7 +9,7 @@
<em:targetApplication>
<rdf:Description>
<em:id>zotero@chnm.gmu.edu</em:id>
<em:minVersion>6.14</em:minVersion>
<em:minVersion>6.0.14</em:minVersion>
<em:maxVersion>*</em:maxVersion>
<em:updateLink>https://github.com/windingwind/zotero-better-notes/releases/latest/download/zotero-better-notes.xpi</em:updateLink>
</rdf:Description>
@ -17,7 +17,7 @@
<em:targetApplication>
<rdf:Description>
<em:id>juris-m@juris-m.github.io</em:id>
<em:minVersion>6.14</em:minVersion>
<em:minVersion>6.0.14</em:minVersion>
<em:maxVersion>*</em:maxVersion>
<em:updateLink>https://github.com/windingwind/zotero-better-notes/releases/latest/download/zotero-better-notes.xpi</em:updateLink>
</rdf:Description>