parent
8ceb0faf4c
commit
017e6d4721
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
@ -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;
|
||||
})();
|
||||
18
src/views.ts
18
src/views.ts
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue