add: note link event

add: knowledge class
This commit is contained in:
xiangyu 2022-04-29 00:28:26 +08:00
parent 8ceb0faf4c
commit 017e6d4721
7 changed files with 426 additions and 3 deletions

View File

@ -24,7 +24,8 @@
"dependencies": {
"compressing": "^1.5.1",
"esbuild": "^0.14.34",
"replace-in-file": "^6.3.2"
"replace-in-file": "^6.3.2",
"tree-model": "^1.0.7"
},
"devDependencies": {
"release-it": "^14.14.0"

View File

@ -23,8 +23,23 @@ class AddonEvents extends AddonBase {
};
}
public async addEditorEventListener(
instance: EditorInstance,
event: string,
message: EditorMessage
) {
let editor: Element = await this._Addon.views.getEditor(instance);
editor.addEventListener(event, (e: XULEvent) => {
message.content.event = e;
message.content.editorInstance = instance;
this.onEditorEvent(message);
});
}
public async onEditorEvent(message: EditorMessage) {
Zotero.debug(`Knowledge4Zotero: onEditorEvent\n${String(message)}`);
Zotero.debug(
`Knowledge4Zotero: onEditorEvent\n${message.type}\n${message.content}`
);
if (message.type === "addNoteInstance") {
let mainKnowledgeID = parseInt(
Zotero.Prefs.get("Knowledge4Zotero.mainKnowledgeID")
@ -51,6 +66,11 @@ class AddonEvents extends AddonBase {
"Add Note Link to Knowledge Workspace",
new EditorMessage("addToKnowledge", {})
);
this.addEditorEventListener(
message.content.editorInstance,
"click",
new EditorMessage("noteEditorClick", {})
);
} else if (message.type === "addToKnowledge") {
/*
message.content = {
@ -105,6 +125,24 @@ class AddonEvents extends AddonBase {
}
}
}
} else if (message.type === "noteEditorClick") {
let el: XUL.Element = message.content.event.target;
if (el.children.length !== 0) {
return;
}
let urlIndex = el.innerHTML.search(/zotero:\/\/note\//g);
if (urlIndex >= 0) {
let noteID = parseInt(
el.innerHTML.substring(urlIndex + "zotero://note/".length)
);
let note = Zotero.Items.get(noteID);
if (note && note.isNote()) {
// TODO: Open note
Zotero.debug(`Knowledge4Zotero: noteEditorClick ${note.id}`);
}
}
} else {
Zotero.debug(`Knowledge4Zotero: message not handled.`);
}
}

View File

@ -1,7 +1,10 @@
import Knowledge4Zotero from "./addon";
import { Knowledge } from "./knowledge";
Zotero.Knowledge4Zotero = new Knowledge4Zotero();
Zotero.Knowledge = Knowledge
window.addEventListener(
"load",
async function (e) {

View File

@ -0,0 +1,68 @@
const TreeModel = require("./treemodel");
class Knowledge {
_note: ZoteroItem;
constructor(noteItem: ZoteroItem) {
this._note = noteItem;
// this.createKnowledgeItem();
}
// createKnowledgeItem() {
// return;
// }
addNoteLink(noteItem: ZoteroItem) {}
getOutline(): Node {
let parser = Components.classes[
"@mozilla.org/xmlextras/domparser;1"
].createInstance(Components.interfaces.nsIDOMParser);
let doc = parser.parseFromString(this._note.getNote(), "text/html");
let metadataContainer: Element = doc.querySelector(
"body > div[data-schema-version]"
);
let tree = new TreeModel();
/*
tree-model/index.js: line 40
TreeModel.prototype.parse = function (model) {
var i, childCount, node;
Annotate the line 40 of:
// if (!(model instanceof Object)) {
// throw new TypeError('Model must be of type object.');
// }
*/
let root = tree.parse({
rank: 0,
lineIndex: -1,
});
let currentNode = root;
for (let i = 0; i < metadataContainer.children.length; i++) {
let currentRank = 7;
let lineElement = metadataContainer.children[i];
if (lineElement.tagName[0] === "H" && lineElement.tagName.length === 2) {
let _rank = parseInt(lineElement.tagName[1]);
if (_rank >= 1 && _rank <= 6) {
currentRank = _rank;
}
while (currentNode.model.rank >= currentRank) {
currentNode = currentNode.parent;
}
currentNode.addChild(
tree.parse({
rank: currentRank,
lineIndex: i,
})
);
}
}
return root;
}
jumpToNote(noteLinke: string) {}
export() {}
}
export { Knowledge };

291
src/treemodel.js Normal file
View File

@ -0,0 +1,291 @@
var mergeSort, findInsertIndex;
mergeSort = require('mergesort');
findInsertIndex = require('find-insert-index');
module.exports = (function () {
'use strict';
var walkStrategies;
walkStrategies = {};
function k(result) {
return function () {
return result;
};
}
function TreeModel(config) {
config = config || {};
this.config = config;
this.config.childrenPropertyName = config.childrenPropertyName || 'children';
this.config.modelComparatorFn = config.modelComparatorFn;
}
function addChildToNode(node, child) {
child.parent = node;
node.children.push(child);
return child;
}
function Node(config, model) {
this.config = config;
this.model = model;
this.children = [];
}
TreeModel.prototype.parse = function (model) {
var i, childCount, node;
// if (!(model instanceof Object)) {
// throw new TypeError('Model must be of type object.');
// }
node = new Node(this.config, model);
if (model[this.config.childrenPropertyName] instanceof Array) {
if (this.config.modelComparatorFn) {
model[this.config.childrenPropertyName] = mergeSort(
this.config.modelComparatorFn,
model[this.config.childrenPropertyName]);
}
for (i = 0, childCount = model[this.config.childrenPropertyName].length; i < childCount; i++) {
addChildToNode(node, this.parse(model[this.config.childrenPropertyName][i]));
}
}
return node;
};
function hasComparatorFunction(node) {
return typeof node.config.modelComparatorFn === 'function';
}
Node.prototype.isRoot = function () {
return this.parent === undefined;
};
Node.prototype.hasChildren = function () {
return this.children.length > 0;
};
function addChild(self, child, insertIndex) {
var index;
if (!(child instanceof Node)) {
throw new TypeError('Child must be of type Node.');
}
child.parent = self;
if (!(self.model[self.config.childrenPropertyName] instanceof Array)) {
self.model[self.config.childrenPropertyName] = [];
}
if (hasComparatorFunction(self)) {
// Find the index to insert the child
index = findInsertIndex(
self.config.modelComparatorFn,
self.model[self.config.childrenPropertyName],
child.model);
// Add to the model children
self.model[self.config.childrenPropertyName].splice(index, 0, child.model);
// Add to the node children
self.children.splice(index, 0, child);
} else {
if (insertIndex === undefined) {
self.model[self.config.childrenPropertyName].push(child.model);
self.children.push(child);
} else {
if (insertIndex < 0 || insertIndex > self.children.length) {
throw new Error('Invalid index.');
}
self.model[self.config.childrenPropertyName].splice(insertIndex, 0, child.model);
self.children.splice(insertIndex, 0, child);
}
}
return child;
}
Node.prototype.addChild = function (child) {
return addChild(this, child);
};
Node.prototype.addChildAtIndex = function (child, index) {
if (hasComparatorFunction(this)) {
throw new Error('Cannot add child at index when using a comparator function.');
}
return addChild(this, child, index);
};
Node.prototype.setIndex = function (index) {
if (hasComparatorFunction(this)) {
throw new Error('Cannot set node index when using a comparator function.');
}
if (this.isRoot()) {
if (index === 0) {
return this;
}
throw new Error('Invalid index.');
}
if (index < 0 || index >= this.parent.children.length) {
throw new Error('Invalid index.');
}
var oldIndex = this.parent.children.indexOf(this);
this.parent.children.splice(index, 0, this.parent.children.splice(oldIndex, 1)[0]);
this.parent.model[this.parent.config.childrenPropertyName]
.splice(index, 0, this.parent.model[this.parent.config.childrenPropertyName].splice(oldIndex, 1)[0]);
return this;
};
Node.prototype.getPath = function () {
var path = [];
(function addToPath(node) {
path.unshift(node);
if (!node.isRoot()) {
addToPath(node.parent);
}
})(this);
return path;
};
Node.prototype.getIndex = function () {
if (this.isRoot()) {
return 0;
}
return this.parent.children.indexOf(this);
};
/**
* Parse the arguments of traversal functions. These functions can take one optional
* first argument which is an options object. If present, this object will be stored
* in args.options. The only mandatory argument is the callback function which can
* appear in the first or second position (if an options object is given). This
* function will be saved to args.fn. The last optional argument is the context on
* which the callback function will be called. It will be available in args.ctx.
*
* @returns Parsed arguments.
*/
function parseArgs() {
var args = {};
if (arguments.length === 1) {
if (typeof arguments[0] === 'function') {
args.fn = arguments[0];
} else {
args.options = arguments[0];
}
} else if (arguments.length === 2) {
if (typeof arguments[0] === 'function') {
args.fn = arguments[0];
args.ctx = arguments[1];
} else {
args.options = arguments[0];
args.fn = arguments[1];
}
} else {
args.options = arguments[0];
args.fn = arguments[1];
args.ctx = arguments[2];
}
args.options = args.options || {};
if (!args.options.strategy) {
args.options.strategy = 'pre';
}
if (!walkStrategies[args.options.strategy]) {
throw new Error('Unknown tree walk strategy. Valid strategies are \'pre\' [default], \'post\' and \'breadth\'.');
}
return args;
}
Node.prototype.walk = function () {
var args;
args = parseArgs.apply(this, arguments);
walkStrategies[args.options.strategy].call(this, args.fn, args.ctx);
};
walkStrategies.pre = function depthFirstPreOrder(callback, context) {
var i, childCount, keepGoing;
keepGoing = callback.call(context, this);
for (i = 0, childCount = this.children.length; i < childCount; i++) {
if (keepGoing === false) {
return false;
}
keepGoing = depthFirstPreOrder.call(this.children[i], callback, context);
}
return keepGoing;
};
walkStrategies.post = function depthFirstPostOrder(callback, context) {
var i, childCount, keepGoing;
for (i = 0, childCount = this.children.length; i < childCount; i++) {
keepGoing = depthFirstPostOrder.call(this.children[i], callback, context);
if (keepGoing === false) {
return false;
}
}
keepGoing = callback.call(context, this);
return keepGoing;
};
walkStrategies.breadth = function breadthFirst(callback, context) {
var queue = [this];
(function processQueue() {
var i, childCount, node;
if (queue.length === 0) {
return;
}
node = queue.shift();
for (i = 0, childCount = node.children.length; i < childCount; i++) {
queue.push(node.children[i]);
}
if (callback.call(context, node) !== false) {
processQueue();
}
})();
};
Node.prototype.all = function () {
var args, all = [];
args = parseArgs.apply(this, arguments);
args.fn = args.fn || k(true);
walkStrategies[args.options.strategy].call(this, function (node) {
if (args.fn.call(args.ctx, node)) {
all.push(node);
}
}, args.ctx);
return all;
};
Node.prototype.first = function () {
var args, first;
args = parseArgs.apply(this, arguments);
args.fn = args.fn || k(true);
walkStrategies[args.options.strategy].call(this, function (node) {
if (args.fn.call(args.ctx, node)) {
first = node;
return false;
}
}, args.ctx);
return first;
};
Node.prototype.drop = function () {
var indexOfChild;
if (!this.isRoot()) {
indexOfChild = this.parent.children.indexOf(this);
this.parent.children.splice(indexOfChild, 1);
this.parent.model[this.config.childrenPropertyName].splice(indexOfChild, 1);
this.parent = undefined;
delete this.parent;
}
return this;
};
return TreeModel;
})();

View File

@ -18,6 +18,15 @@ class AddonViews extends AddonBase {
};
}
async getEditor(instance: EditorInstance) {
await instance._initPromise;
let editor =
instance._iframeWindow.document.getElementsByClassName(
"primary-editor"
)[0];
return editor;
}
async addEditorButton(
editorInstances: EditorInstance,
id: string,
@ -48,6 +57,15 @@ class AddonViews extends AddonBase {
button.setAttribute("title", title);
}
async scrollToLine(instance: EditorInstance, lineIndex: number) {
let editor = await this.getEditor(instance);
if (lineIndex > editor.children.length) {
lineIndex = editor.children.length - 1;
}
// @ts-ignore
editor.parentNode.scrollTo(0, editor.children[lineIndex].offsetTop);
}
showProgressWindow(
header: string,
context: string,

6
typing/global.d.ts vendored
View File

@ -83,6 +83,7 @@ declare interface ZoteroItem {
id: number;
isRegularItem: () => boolean;
isNote: () => boolean;
getNote: () => string;
isAttachment: () => boolean;
isAnnotation?: () => boolean;
itemTypeID: number;
@ -126,6 +127,9 @@ declare const Zotero: {
get: (key: string) => any;
set: (key: string, value: any) => any;
};
Items: {
get: (key: string | number) => ZoteroItem;
};
Reader: Reader;
Notes: Notes;
Knowledge4Zotero: import("../src/addon");
@ -161,7 +165,7 @@ declare class ReaderObj {
declare class EditorInstance {
_iframeWindow: XULWindow;
_item: ZoteroItem;
_initPromise: Promise
_initPromise: Promise;
}
declare class Notes {