update: plugin template
fix: reopen window bug add: eslint
This commit is contained in:
parent
5a4bdc72bf
commit
15d8cce685
|
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true
|
||||||
|
},
|
||||||
|
"root": true,
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"overrides": [],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": ["@typescript-eslint"],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/ban-ts-comment": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"ts-expect-error": "allow-with-description",
|
||||||
|
"ts-ignore": "allow-with-description",
|
||||||
|
"ts-nocheck": "allow-with-description",
|
||||||
|
"ts-check": "allow-with-description"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": ["off", { "ignoreRestArgs": true }],
|
||||||
|
"@typescript-eslint/no-non-null-assertion": "off"
|
||||||
|
},
|
||||||
|
"ignorePatterns": [
|
||||||
|
"**/build/**",
|
||||||
|
"**/dist/**",
|
||||||
|
"**/node_modules/**",
|
||||||
|
"**/scripts/**",
|
||||||
|
"**/*.js",
|
||||||
|
"**/*.bak"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
addon/chrome/content/lib/** linguist-vendored
|
addon/chrome/content/lib/** linguist-vendored
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
@ -10,4 +10,4 @@ liberapay: windingwind
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
otechie: # Replace with a single Otechie username
|
otechie: # Replace with a single Otechie username
|
||||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||||
custom: ["https://paypal.me/windingwind?country.x=C2&locale.x=zh_XC", ]
|
custom: ["https://paypal.me/windingwind?country.x=C2&locale.x=zh_XC"]
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@ name: Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: "[Bug]"
|
title: "[Bug]"
|
||||||
labels: bug
|
labels: bug
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
|
|
@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
|
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
|
|
@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen.
|
||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
**Desktop (please complete the following information):**
|
||||||
- OS: [e.g. iOS]
|
|
||||||
- Browser [e.g. chrome, safari]
|
- OS: [e.g. iOS]
|
||||||
- Version [e.g. 22]
|
- Browser [e.g. chrome, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
**Smartphone (please complete the following information):**
|
**Smartphone (please complete the following information):**
|
||||||
- Device: [e.g. iPhone6]
|
|
||||||
- OS: [e.g. iOS8.1]
|
- Device: [e.g. iPhone6]
|
||||||
- Browser [e.g. stock browser, safari]
|
- OS: [e.g. iOS8.1]
|
||||||
- Version [e.g. 22]
|
- Browser [e.g. stock browser, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@ name: Feature request
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
title: "[Feature]"
|
title: "[Feature]"
|
||||||
labels: enhancement
|
labels: enhancement
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@ name: Note Template Issue
|
||||||
about: Need help with your custom note template
|
about: Need help with your custom note template
|
||||||
title: "[Note Template]"
|
title: "[Note Template]"
|
||||||
labels: help wanted
|
labels: help wanted
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Template Type
|
## Template Type
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
**/builds
|
**/build
|
||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
package-lock.json
|
||||||
zotero-cmd.json
|
zotero-cmd.json
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"tabWidth": 2
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
},
|
},
|
||||||
"github": {
|
"github": {
|
||||||
"release": true,
|
"release": true,
|
||||||
"assets": ["builds/*.xpi"]
|
"assets": ["build/*.xpi"]
|
||||||
},
|
},
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"after:bump": "npm run build",
|
"after:bump": "npm run build",
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,45 @@
|
||||||
{
|
{
|
||||||
"appendElement - full": {
|
"appendElement - full": {
|
||||||
"scope": "javascript,typescript",
|
"scope": "javascript,typescript",
|
||||||
"prefix": "appendElement",
|
"prefix": "appendElement",
|
||||||
"body": [
|
"body": [
|
||||||
"appendElement({",
|
"appendElement({",
|
||||||
"\ttag: '${1:div}',",
|
"\ttag: '${1:div}',",
|
||||||
"\tid: '${2:id}',",
|
"\tid: '${2:id}',",
|
||||||
"\tnamespace: '${3:html}',",
|
"\tnamespace: '${3:html}',",
|
||||||
"\tclassList: ['${4:class}'],",
|
"\tclassList: ['${4:class}'],",
|
||||||
"\tstyles: {${5:style}: '$6'},",
|
"\tstyles: {${5:style}: '$6'},",
|
||||||
"\tproperties: {},",
|
"\tproperties: {},",
|
||||||
"\tattributes: {},",
|
"\tattributes: {},",
|
||||||
"\t[{ '${7:onload}', (e: Event) => $8, ${9:false} }],",
|
"\t[{ '${7:onload}', (e: Event) => $8, ${9:false} }],",
|
||||||
"\tcheckExistanceParent: ${10:HTMLElement},",
|
"\tcheckExistanceParent: ${10:HTMLElement},",
|
||||||
"\tignoreIfExists: ${11:true},",
|
"\tignoreIfExists: ${11:true},",
|
||||||
"\tskipIfExists: ${12:true},",
|
"\tskipIfExists: ${12:true},",
|
||||||
"\tremoveIfExists: ${13:true},",
|
"\tremoveIfExists: ${13:true},",
|
||||||
"\tcustomCheck: (doc: Document, options: ElementOptions) => ${14:true},",
|
"\tcustomCheck: (doc: Document, options: ElementOptions) => ${14:true},",
|
||||||
"\tchildren: [$15]",
|
"\tchildren: [$15]",
|
||||||
"}, ${16:container});"
|
"}, ${16:container});"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"appendElement - minimum": {
|
"appendElement - minimum": {
|
||||||
"scope": "javascript,typescript",
|
"scope": "javascript,typescript",
|
||||||
"prefix": "appendElement",
|
"prefix": "appendElement",
|
||||||
"body": "appendElement({ tag: '$1' }, $2);"
|
"body": "appendElement({ tag: '$1' }, $2);"
|
||||||
},
|
},
|
||||||
"register Notifier": {
|
"register Notifier": {
|
||||||
"scope": "javascript,typescript",
|
"scope": "javascript,typescript",
|
||||||
"prefix": "registerObserver",
|
"prefix": "registerObserver",
|
||||||
"body": [
|
"body": [
|
||||||
"registerObserver({",
|
"registerObserver({",
|
||||||
"\t notify: (",
|
"\t notify: (",
|
||||||
"\t\tevent: _ZoteroTypes.Notifier.Event,",
|
"\t\tevent: _ZoteroTypes.Notifier.Event,",
|
||||||
"\t\ttype: _ZoteroTypes.Notifier.Type,",
|
"\t\ttype: _ZoteroTypes.Notifier.Type,",
|
||||||
"\t\tids: string[],",
|
"\t\tids: string[],",
|
||||||
"\t\textraData: _ZoteroTypes.anyObj",
|
"\t\textraData: _ZoteroTypes.anyObj",
|
||||||
"\t) => {",
|
"\t) => {",
|
||||||
"\t\t$0",
|
"\t\t$0",
|
||||||
"\t}",
|
"\t}",
|
||||||
"});"
|
"});"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,10 +86,10 @@ and:
|
||||||
## 👋 Install
|
## 👋 Install
|
||||||
|
|
||||||
- Download the latest release (.xpi file) from the [Releases Page](https://github.com/windingwind/zotero-better-notes/releases)
|
- Download the latest release (.xpi file) from the [Releases Page](https://github.com/windingwind/zotero-better-notes/releases)
|
||||||
|
|
||||||
<details style="text-indent: 2em">
|
<details style="text-indent: 2em">
|
||||||
<summary>More Versions</summary>
|
<summary>More Versions</summary>
|
||||||
|
|
||||||
- [Latest Stable](https://github.com/windingwind/zotero-better-notes/releases/latest)
|
- [Latest Stable](https://github.com/windingwind/zotero-better-notes/releases/latest)
|
||||||
- [v1.0.4](https://github.com/windingwind/zotero-better-notes/releases/tag/1.0.4) (last for Zotero 6)
|
- [v1.0.4](https://github.com/windingwind/zotero-better-notes/releases/tag/1.0.4) (last for Zotero 6)
|
||||||
- [v0.8.9](https://github.com/windingwind/zotero-better-notes/releases/tag/0.8.9) (last with auto-insert, tag-insert, math-ocr, for Zotero 6)
|
- [v0.8.9](https://github.com/windingwind/zotero-better-notes/releases/tag/0.8.9) (last with auto-insert, tag-insert, math-ocr, for Zotero 6)
|
||||||
|
|
@ -98,6 +98,7 @@ and:
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
_Note_: If you're using Firefox as your browser, right-click the `.xpi` and select "Save As.."
|
_Note_: If you're using Firefox as your browser, right-click the `.xpi` and select "Save As.."
|
||||||
|
|
||||||
- In Zotero click `Tools` in the top menu bar and then click `Addons`
|
- In Zotero click `Tools` in the top menu bar and then click `Addons`
|
||||||
- Go to the Extensions page and then click the gear icon in the top right.
|
- Go to the Extensions page and then click the gear icon in the top right.
|
||||||
- Select `Install Add-on from file`.
|
- Select `Install Add-on from file`.
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ if (typeof Zotero == "undefined") {
|
||||||
|
|
||||||
var chromeHandle;
|
var chromeHandle;
|
||||||
|
|
||||||
|
var windowListener;
|
||||||
|
|
||||||
// In Zotero 6, bootstrap methods are called before Zotero is initialized, and using include.js
|
// In Zotero 6, bootstrap methods are called before Zotero is initialized, and using include.js
|
||||||
// to get the Zotero XPCOM service would risk breaking Zotero startup. Instead, wait for the main
|
// to get the Zotero XPCOM service would risk breaking Zotero startup. Instead, wait for the main
|
||||||
// Zotero window to open and get the Zotero object from there.
|
// Zotero window to open and get the Zotero object from there.
|
||||||
|
|
@ -18,73 +20,85 @@ var chromeHandle;
|
||||||
// In Zotero 7, bootstrap methods are not called until Zotero is initialized, and the 'Zotero' is
|
// In Zotero 7, bootstrap methods are not called until Zotero is initialized, and the 'Zotero' is
|
||||||
// automatically made available.
|
// automatically made available.
|
||||||
async function waitForZotero() {
|
async function waitForZotero() {
|
||||||
if (typeof Zotero != "undefined") {
|
await new Promise(async (resolve) => {
|
||||||
await Zotero.initializationPromise;
|
if (typeof Zotero != "undefined") {
|
||||||
}
|
resolve();
|
||||||
|
|
||||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
||||||
var windows = Services.wm.getEnumerator("navigator:browser");
|
|
||||||
var found = false;
|
|
||||||
while (windows.hasMoreElements()) {
|
|
||||||
let win = windows.getNext();
|
|
||||||
if (win.Zotero) {
|
|
||||||
Zotero = win.Zotero;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!found) {
|
const { Services } = ChromeUtils.import(
|
||||||
await new Promise((resolve) => {
|
"resource://gre/modules/Services.jsm",
|
||||||
var listener = {
|
);
|
||||||
onOpenWindow: function (aWindow) {
|
const windows = Services.wm.getEnumerator("navigator:browser");
|
||||||
// Wait for the window to finish loading
|
let found = false;
|
||||||
let domWindow = aWindow
|
while (windows.hasMoreElements()) {
|
||||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
let win = windows.getNext();
|
||||||
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
|
if (win.Zotero) {
|
||||||
domWindow.addEventListener(
|
Zotero = win.Zotero;
|
||||||
"load",
|
found = true;
|
||||||
function () {
|
resolve();
|
||||||
domWindow.removeEventListener("load", arguments.callee, false);
|
break;
|
||||||
if (domWindow.Zotero) {
|
}
|
||||||
Services.wm.removeListener(listener);
|
}
|
||||||
Zotero = domWindow.Zotero;
|
windowListener = {
|
||||||
resolve();
|
onOpenWindow: function (aWindow) {
|
||||||
}
|
// Wait for the window to finish loading
|
||||||
},
|
const domWindow = aWindow
|
||||||
false
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
);
|
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
|
||||||
},
|
domWindow.addEventListener(
|
||||||
};
|
"load",
|
||||||
Services.wm.addListener(listener);
|
async function () {
|
||||||
});
|
domWindow.removeEventListener("load", arguments.callee, false);
|
||||||
}
|
if (!found && domWindow.Zotero) {
|
||||||
await Zotero.initializationPromise;
|
Zotero = domWindow.Zotero;
|
||||||
|
resolve();
|
||||||
|
} else if (
|
||||||
|
domWindow.location.href ===
|
||||||
|
"chrome://zotero/content/zoteroPane.xhtml"
|
||||||
|
) {
|
||||||
|
// Call the hook for the main window load event
|
||||||
|
// Note that this is not called the first time the window is opened
|
||||||
|
// (when Zotero is initialized), but only when the window is re-opened
|
||||||
|
// after being closed
|
||||||
|
await Zotero.__addonInstance__?.hooks.onMainWindowLoad(domWindow);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onCloseWindow: function (aWindow) {
|
||||||
|
const domWindow = aWindow
|
||||||
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
|
||||||
|
if (
|
||||||
|
domWindow.location.href === "chrome://zotero/content/zoteroPane.xhtml"
|
||||||
|
) {
|
||||||
|
Zotero.__addonInstance__?.hooks.onMainWindowUnload(domWindow);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Services.wm.addListener(windowListener);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function install(data, reason) {}
|
function install(data, reason) {}
|
||||||
|
|
||||||
async function startup({ id, version, resourceURI, rootURI }, reason) {
|
async function startup({ id, version, resourceURI, rootURI }, reason) {
|
||||||
await waitForZotero();
|
await waitForZotero();
|
||||||
|
await Zotero.initializationPromise;
|
||||||
|
|
||||||
// String 'rootURI' introduced in Zotero 7
|
// String 'rootURI' introduced in Zotero 7
|
||||||
if (!rootURI) {
|
if (!rootURI) {
|
||||||
rootURI = resourceURI.spec;
|
rootURI = resourceURI.spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Zotero.platformMajorVersion >= 102) {
|
var aomStartup = Components.classes[
|
||||||
var aomStartup = Components.classes[
|
"@mozilla.org/addons/addon-manager-startup;1"
|
||||||
"@mozilla.org/addons/addon-manager-startup;1"
|
].getService(Components.interfaces.amIAddonManagerStartup);
|
||||||
].getService(Components.interfaces.amIAddonManagerStartup);
|
var manifestURI = Services.io.newURI(rootURI + "manifest.json");
|
||||||
var manifestURI = Services.io.newURI(rootURI + "manifest.json");
|
chromeHandle = aomStartup.registerChrome(manifestURI, [
|
||||||
chromeHandle = aomStartup.registerChrome(manifestURI, [
|
["content", "__addonRef__", rootURI + "chrome/content/"],
|
||||||
["content", "__addonRef__", rootURI + "chrome/content/"],
|
]);
|
||||||
["locale", "__addonRef__", "en-US", rootURI + "chrome/locale/en-US/"],
|
|
||||||
["locale", "__addonRef__", "zh-CN", rootURI + "chrome/locale/zh-CN/"],
|
|
||||||
["locale", "__addonRef__", "ru-RU", rootURI + "chrome/locale/ru-RU/"],
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
setDefaultPrefs(rootURI);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global variables for plugin code.
|
* Global variables for plugin code.
|
||||||
|
|
@ -94,13 +108,14 @@ async function startup({ id, version, resourceURI, rootURI }, reason) {
|
||||||
*/
|
*/
|
||||||
const ctx = {
|
const ctx = {
|
||||||
rootURI,
|
rootURI,
|
||||||
|
// Define main window's document to create a fake browser environment
|
||||||
document: Zotero.getMainWindow().document,
|
document: Zotero.getMainWindow().document,
|
||||||
};
|
};
|
||||||
ctx._globalThis = ctx;
|
ctx._globalThis = ctx;
|
||||||
|
|
||||||
Services.scriptloader.loadSubScript(
|
Services.scriptloader.loadSubScript(
|
||||||
`${rootURI}/chrome/content/scripts/index.js`,
|
`${rootURI}/chrome/content/scripts/__addonRef__.js`,
|
||||||
ctx
|
ctx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,9 +123,11 @@ function shutdown({ id, version, resourceURI, rootURI }, reason) {
|
||||||
if (reason === APP_SHUTDOWN) {
|
if (reason === APP_SHUTDOWN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Services.wm.removeListener(windowListener);
|
||||||
|
|
||||||
if (typeof Zotero === "undefined") {
|
if (typeof Zotero === "undefined") {
|
||||||
Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
|
Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
|
||||||
Components.interfaces.nsISupports
|
Components.interfaces.nsISupports,
|
||||||
).wrappedJSObject;
|
).wrappedJSObject;
|
||||||
}
|
}
|
||||||
Zotero.__addonInstance__.hooks.onShutdown();
|
Zotero.__addonInstance__.hooks.onShutdown();
|
||||||
|
|
@ -119,7 +136,7 @@ function shutdown({ id, version, resourceURI, rootURI }, reason) {
|
||||||
.getService(Components.interfaces.nsIStringBundleService)
|
.getService(Components.interfaces.nsIStringBundleService)
|
||||||
.flushBundles();
|
.flushBundles();
|
||||||
|
|
||||||
Cu.unload(`${rootURI}/chrome/content/scripts/index.js`);
|
Cu.unload(`${rootURI}/chrome/content/scripts/__addonRef__.js`);
|
||||||
|
|
||||||
if (chromeHandle) {
|
if (chromeHandle) {
|
||||||
chromeHandle.destruct();
|
chromeHandle.destruct();
|
||||||
|
|
@ -128,27 +145,3 @@ function shutdown({ id, version, resourceURI, rootURI }, reason) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function uninstall(data, reason) {}
|
function uninstall(data, reason) {}
|
||||||
|
|
||||||
// Loads default preferences from defaults/preferences/prefs.js in Zotero 6
|
|
||||||
function setDefaultPrefs(rootURI) {
|
|
||||||
var branch = Services.prefs.getDefaultBranch("");
|
|
||||||
var obj = {
|
|
||||||
pref(pref, value) {
|
|
||||||
switch (typeof value) {
|
|
||||||
case "boolean":
|
|
||||||
branch.setBoolPref(pref, value);
|
|
||||||
break;
|
|
||||||
case "string":
|
|
||||||
branch.setStringPref(pref, value);
|
|
||||||
break;
|
|
||||||
case "number":
|
|
||||||
branch.setIntPref(pref, value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Zotero.logError(`Invalid type '${typeof value}' for pref '${pref}'`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Zotero.getMainWindow().console.log(rootURI + "prefs.js");
|
|
||||||
Services.scriptloader.loadSubScript(rootURI + "prefs.js", obj);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
12
package.json
12
package.json
|
|
@ -31,6 +31,7 @@
|
||||||
"reload": "npm run build-dev && node scripts/reload.mjs --z 7",
|
"reload": "npm run build-dev && node scripts/reload.mjs --z 7",
|
||||||
"watch": "chokidar \"src/**\" \"addon/**\" -c \"npm run reload\"",
|
"watch": "chokidar \"src/**\" \"addon/**\" -c \"npm run reload\"",
|
||||||
"release": "release-it",
|
"release": "release-it",
|
||||||
|
"lint": "prettier --write src/** && eslint . --ext .ts --fix",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"update-deps": "npm update --save"
|
"update-deps": "npm update --save"
|
||||||
},
|
},
|
||||||
|
|
@ -63,7 +64,6 @@
|
||||||
"remark-parse": "^10.0.2",
|
"remark-parse": "^10.0.2",
|
||||||
"remark-rehype": "^10.1.0",
|
"remark-rehype": "^10.1.0",
|
||||||
"remark-stringify": "^10.0.3",
|
"remark-stringify": "^10.0.3",
|
||||||
"replace-in-file": "^6.3.5",
|
|
||||||
"seedrandom": "^3.0.5",
|
"seedrandom": "^3.0.5",
|
||||||
"tree-model": "^1.0.7",
|
"tree-model": "^1.0.7",
|
||||||
"unified": "^10.1.2",
|
"unified": "^10.1.2",
|
||||||
|
|
@ -75,21 +75,25 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/browser-or-node": "^1.3.0",
|
"@types/browser-or-node": "^1.3.0",
|
||||||
"@types/diff": "^5.0.3",
|
"@types/diff": "^5.0.3",
|
||||||
"@types/node": "^20.3.0",
|
"@types/node": "^20.4.2",
|
||||||
"@types/seedrandom": "^3.0.5",
|
"@types/seedrandom": "^3.0.5",
|
||||||
"@types/yamljs": "^0.2.31",
|
"@types/yamljs": "^0.2.31",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
"chokidar-cli": "^3.0.0",
|
"chokidar-cli": "^3.0.0",
|
||||||
"compressing": "^1.9.0",
|
"compressing": "^1.9.0",
|
||||||
"concurrently": "^8.2.0",
|
"concurrently": "^8.2.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"esbuild": "^0.18.1",
|
"esbuild": "^0.18.1",
|
||||||
"minimist": "^1.2.8",
|
"eslint": "^8.44.0",
|
||||||
|
"eslint-config-prettier": "^8.8.0",
|
||||||
|
"prettier": "^3.0.0",
|
||||||
"prosemirror-model": "^1.19.2",
|
"prosemirror-model": "^1.19.2",
|
||||||
"prosemirror-state": "^1.4.3",
|
"prosemirror-state": "^1.4.3",
|
||||||
"prosemirror-transform": "^1.7.3",
|
"prosemirror-transform": "^1.7.3",
|
||||||
"prosemirror-view": "^1.31.4",
|
"prosemirror-view": "^1.31.4",
|
||||||
"release-it": "^15.11.0",
|
"release-it": "^15.11.0",
|
||||||
"replace-in-file": "^6.3.5",
|
"replace-in-file": "^7.0.1",
|
||||||
"typescript": "^5.1.3",
|
"typescript": "^5.1.3",
|
||||||
"zotero-types": "^1.0.15"
|
"zotero-types": "^1.0.15"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { build } from "esbuild";
|
import { build } from "esbuild";
|
||||||
import { zip } from "compressing";
|
import { zip } from "compressing";
|
||||||
import { join, basename } from "path";
|
import path from "path";
|
||||||
import {
|
import {
|
||||||
existsSync,
|
existsSync,
|
||||||
lstatSync,
|
lstatSync,
|
||||||
|
|
@ -13,18 +13,22 @@ import {
|
||||||
} from "fs";
|
} from "fs";
|
||||||
import { env, exit } from "process";
|
import { env, exit } from "process";
|
||||||
import replaceInFile from "replace-in-file";
|
import replaceInFile from "replace-in-file";
|
||||||
const { sync: replaceSync } = replaceInFile;
|
const { replaceInFileSync } = replaceInFile;
|
||||||
import details from "../package.json" assert { type: "json" };
|
import details from "../package.json" assert { type: "json" };
|
||||||
|
|
||||||
const { name, author, description, homepage, version, config } = details;
|
const { name, author, description, homepage, version, config } = details;
|
||||||
|
|
||||||
|
const t = new Date();
|
||||||
|
const buildTime = dateFormat("YYYY-mm-dd HH:MM:SS", new Date());
|
||||||
|
const buildDir = "build";
|
||||||
|
|
||||||
function copyFileSync(source, target) {
|
function copyFileSync(source, target) {
|
||||||
var targetFile = target;
|
var targetFile = target;
|
||||||
|
|
||||||
// If target is a directory, a new file with the same name will be created
|
// If target is a directory, a new file with the same name will be created
|
||||||
if (existsSync(target)) {
|
if (existsSync(target)) {
|
||||||
if (lstatSync(target).isDirectory()) {
|
if (lstatSync(target).isDirectory()) {
|
||||||
targetFile = join(target, basename(source));
|
targetFile = path.join(target, path.basename(source));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,7 +39,7 @@ function copyFolderRecursiveSync(source, target) {
|
||||||
var files = [];
|
var files = [];
|
||||||
|
|
||||||
// Check if folder needs to be created or integrated
|
// Check if folder needs to be created or integrated
|
||||||
var targetFolder = join(target, basename(source));
|
var targetFolder = path.join(target, path.basename(source));
|
||||||
if (!existsSync(targetFolder)) {
|
if (!existsSync(targetFolder)) {
|
||||||
mkdirSync(targetFolder);
|
mkdirSync(targetFolder);
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +48,7 @@ function copyFolderRecursiveSync(source, target) {
|
||||||
if (lstatSync(source).isDirectory()) {
|
if (lstatSync(source).isDirectory()) {
|
||||||
files = readdirSync(source);
|
files = readdirSync(source);
|
||||||
files.forEach(function (file) {
|
files.forEach(function (file) {
|
||||||
var curSource = join(source, file);
|
var curSource = path.join(source, file);
|
||||||
if (lstatSync(curSource).isDirectory()) {
|
if (lstatSync(curSource).isDirectory()) {
|
||||||
copyFolderRecursiveSync(curSource, targetFolder);
|
copyFolderRecursiveSync(curSource, targetFolder);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -77,107 +81,21 @@ function dateFormat(fmt, date) {
|
||||||
if (ret) {
|
if (ret) {
|
||||||
fmt = fmt.replace(
|
fmt = fmt.replace(
|
||||||
ret[1],
|
ret[1],
|
||||||
ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0")
|
ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fmt;
|
return fmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
function renameLocaleFiles() {
|
||||||
const t = new Date();
|
const localeDir = path.join(buildDir, "addon/locale");
|
||||||
const buildTime = dateFormat("YYYY-mm-dd HH:MM:SS", t);
|
|
||||||
const buildDir = "builds";
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`[Build] BUILD_DIR=${buildDir}, VERSION=${version}, BUILD_TIME=${buildTime}, ENV=${[
|
|
||||||
env.NODE_ENV,
|
|
||||||
]}`
|
|
||||||
);
|
|
||||||
|
|
||||||
clearFolder(buildDir);
|
|
||||||
|
|
||||||
copyFolderRecursiveSync("addon", buildDir);
|
|
||||||
|
|
||||||
copyFileSync("update-template.json", "update.json");
|
|
||||||
copyFileSync("update-template.rdf", "update.rdf");
|
|
||||||
|
|
||||||
await build({
|
|
||||||
entryPoints: ["src/index.ts"],
|
|
||||||
define: {
|
|
||||||
__env__: `"${env.NODE_ENV}"`,
|
|
||||||
},
|
|
||||||
bundle: true,
|
|
||||||
outfile: join(buildDir, "addon/chrome/content/scripts/index.js"),
|
|
||||||
// Don't turn minify on
|
|
||||||
// minify: true,
|
|
||||||
target: ["firefox60"],
|
|
||||||
}).catch(() => exit(1));
|
|
||||||
|
|
||||||
await build({
|
|
||||||
entryPoints: ["src/extras/editorScript.ts"],
|
|
||||||
bundle: true,
|
|
||||||
outfile: join(buildDir, "addon/chrome/content/scripts/editorScript.js"),
|
|
||||||
target: ["firefox60"],
|
|
||||||
}).catch(() => exit(1));
|
|
||||||
|
|
||||||
await build({
|
|
||||||
entryPoints: ["src/extras/docxWorker.ts"],
|
|
||||||
bundle: true,
|
|
||||||
outfile: join(buildDir, "addon/chrome/content/scripts/docxWorker.js"),
|
|
||||||
target: ["firefox60"],
|
|
||||||
}).catch(() => exit(1));
|
|
||||||
|
|
||||||
console.log("[Build] Run esbuild OK");
|
|
||||||
|
|
||||||
const replaceFrom = [
|
|
||||||
/__author__/g,
|
|
||||||
/__description__/g,
|
|
||||||
/__homepage__/g,
|
|
||||||
/__buildVersion__/g,
|
|
||||||
/__buildTime__/g,
|
|
||||||
];
|
|
||||||
|
|
||||||
const replaceTo = [author, description, homepage, version, buildTime];
|
|
||||||
|
|
||||||
replaceFrom.push(
|
|
||||||
...Object.keys(config).map((k) => new RegExp(`__${k}__`, "g"))
|
|
||||||
);
|
|
||||||
replaceTo.push(...Object.values(config));
|
|
||||||
|
|
||||||
const optionsAddon = {
|
|
||||||
files: [
|
|
||||||
join(buildDir, "**/*.html"),
|
|
||||||
join(buildDir, "**/*.xhtml"),
|
|
||||||
join(buildDir, "**/*.json"),
|
|
||||||
join(buildDir, "addon/prefs.js"),
|
|
||||||
join(buildDir, "addon/bootstrap.js"),
|
|
||||||
"update.json",
|
|
||||||
"update.rdf",
|
|
||||||
],
|
|
||||||
from: replaceFrom,
|
|
||||||
to: replaceTo,
|
|
||||||
countMatches: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const replaceResult = replaceSync(optionsAddon);
|
|
||||||
console.log(
|
|
||||||
"[Build] Run replace in ",
|
|
||||||
replaceResult
|
|
||||||
.filter((f) => f.hasChanged)
|
|
||||||
.map((f) => `${f.file} : ${f.numReplacements} / ${f.numMatches}`)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log("[Build] Replace OK");
|
|
||||||
|
|
||||||
// Walk the builds/addon/locale folder's sub folders and rename *.ftl to addonRef-*.ftl
|
|
||||||
const localeDir = join(buildDir, "addon/locale");
|
|
||||||
const localeFolders = readdirSync(localeDir, { withFileTypes: true })
|
const localeFolders = readdirSync(localeDir, { withFileTypes: true })
|
||||||
.filter((dirent) => dirent.isDirectory())
|
.filter((dirent) => dirent.isDirectory())
|
||||||
.map((dirent) => dirent.name);
|
.map((dirent) => dirent.name);
|
||||||
|
|
||||||
for (const localeSubFolder of localeFolders) {
|
for (const localeSubFolder of localeFolders) {
|
||||||
const localeSubDir = join(localeDir, localeSubFolder);
|
const localeSubDir = path.join(localeDir, localeSubFolder);
|
||||||
const localeSubFiles = readdirSync(localeSubDir, {
|
const localeSubFiles = readdirSync(localeSubDir, {
|
||||||
withFileTypes: true,
|
withFileTypes: true,
|
||||||
})
|
})
|
||||||
|
|
@ -187,22 +105,176 @@ async function main() {
|
||||||
for (const localeSubFile of localeSubFiles) {
|
for (const localeSubFile of localeSubFiles) {
|
||||||
if (localeSubFile.endsWith(".ftl")) {
|
if (localeSubFile.endsWith(".ftl")) {
|
||||||
renameSync(
|
renameSync(
|
||||||
join(localeSubDir, localeSubFile),
|
path.join(localeSubDir, localeSubFile),
|
||||||
join(localeSubDir, `${config.addonRef}-${localeSubFile}`)
|
path.join(localeSubDir, `${config.addonRef}-${localeSubFile}`),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceString() {
|
||||||
|
const replaceFrom = [
|
||||||
|
/__author__/g,
|
||||||
|
/__description__/g,
|
||||||
|
/__homepage__/g,
|
||||||
|
/__buildVersion__/g,
|
||||||
|
/__buildTime__/g,
|
||||||
|
];
|
||||||
|
const replaceTo = [author, description, homepage, version, buildTime];
|
||||||
|
|
||||||
|
replaceFrom.push(
|
||||||
|
...Object.keys(config).map((k) => new RegExp(`__${k}__`, "g")),
|
||||||
|
);
|
||||||
|
replaceTo.push(...Object.values(config));
|
||||||
|
|
||||||
|
const optionsAddon = {
|
||||||
|
files: [
|
||||||
|
`${buildDir}/addon/**/*.xhtml`,
|
||||||
|
`${buildDir}/addon/**/*.html`,
|
||||||
|
`${buildDir}/addon/**/*.json`,
|
||||||
|
`${buildDir}/addon/prefs.js`,
|
||||||
|
`${buildDir}/addon/manifest.json`,
|
||||||
|
`${buildDir}/addon/bootstrap.js`,
|
||||||
|
"update.json",
|
||||||
|
],
|
||||||
|
from: replaceFrom,
|
||||||
|
to: replaceTo,
|
||||||
|
countMatches: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const replaceResult = replaceInFileSync(optionsAddon);
|
||||||
|
|
||||||
|
const localeMessage = new Set();
|
||||||
|
const localeMessageMiss = new Set();
|
||||||
|
|
||||||
|
const replaceResultFlt = replaceInFileSync({
|
||||||
|
files: [`${buildDir}/addon/locale/**/*.ftl`],
|
||||||
|
processor: (fltContent) => {
|
||||||
|
const lines = fltContent.split("\n");
|
||||||
|
const prefixedLines = lines.map((line) => {
|
||||||
|
// https://regex101.com/r/lQ9x5p/1
|
||||||
|
const match = line.match(
|
||||||
|
/^(?<message>[a-zA-Z]\S*)([ ]*=[ ]*)(?<pattern>.*)$/m,
|
||||||
|
);
|
||||||
|
if (match) {
|
||||||
|
localeMessage.add(match.groups.message);
|
||||||
|
return `${config.addonRef}-${line}`;
|
||||||
|
} else {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return prefixedLines.join("\n");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const replaceResultXhtml = replaceInFileSync({
|
||||||
|
files: [`${buildDir}/addon/**/*.xhtml`],
|
||||||
|
processor: (input) => {
|
||||||
|
const matchs = [...input.matchAll(/(data-l10n-id)="(\S*)"/g)];
|
||||||
|
matchs.map((match) => {
|
||||||
|
if (localeMessage.has(match[2])) {
|
||||||
|
input = input.replace(
|
||||||
|
match[0],
|
||||||
|
`${match[1]}="${config.addonRef}-${match[2]}"`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
localeMessageMiss.add(match[2]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return input;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"[Build] Run replace in ",
|
||||||
|
replaceResult
|
||||||
|
.filter((f) => f.hasChanged)
|
||||||
|
.map((f) => `${f.file} : ${f.numReplacements} / ${f.numMatches}`),
|
||||||
|
replaceResultFlt.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`),
|
||||||
|
replaceResultXhtml.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (localeMessageMiss.size !== 0) {
|
||||||
|
console.warn(
|
||||||
|
`[Build] [Warn] Fluent message [${new Array(
|
||||||
|
...localeMessageMiss,
|
||||||
|
)}] do not exsit in addon's locale files.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function esbuild() {
|
||||||
|
await build({
|
||||||
|
entryPoints: ["src/index.ts"],
|
||||||
|
define: {
|
||||||
|
__env__: `"${env.NODE_ENV}"`,
|
||||||
|
},
|
||||||
|
bundle: true,
|
||||||
|
outfile: path.join(
|
||||||
|
buildDir,
|
||||||
|
`addon/chrome/content/scripts/${config.addonRef}.js`,
|
||||||
|
),
|
||||||
|
// Don't turn minify on
|
||||||
|
// minify: true,
|
||||||
|
target: ["firefox102"],
|
||||||
|
}).catch(() => exit(1));
|
||||||
|
|
||||||
|
await build({
|
||||||
|
entryPoints: ["src/extras/editorScript.ts"],
|
||||||
|
bundle: true,
|
||||||
|
outfile: path.join(
|
||||||
|
buildDir,
|
||||||
|
"addon/chrome/content/scripts/editorScript.js",
|
||||||
|
),
|
||||||
|
target: ["firefox102"],
|
||||||
|
}).catch(() => exit(1));
|
||||||
|
|
||||||
|
await build({
|
||||||
|
entryPoints: ["src/extras/docxWorker.ts"],
|
||||||
|
bundle: true,
|
||||||
|
outfile: path.join(buildDir, "addon/chrome/content/scripts/docxWorker.js"),
|
||||||
|
target: ["firefox102"],
|
||||||
|
}).catch(() => exit(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log(
|
||||||
|
`[Build] BUILD_DIR=${buildDir}, VERSION=${version}, BUILD_TIME=${buildTime}, ENV=${[
|
||||||
|
env.NODE_ENV,
|
||||||
|
]}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
clearFolder(buildDir);
|
||||||
|
|
||||||
|
copyFolderRecursiveSync("addon", buildDir);
|
||||||
|
|
||||||
|
copyFileSync("update-template.json", "update.json");
|
||||||
|
|
||||||
|
await esbuild();
|
||||||
|
|
||||||
|
console.log("[Build] Run esbuild OK");
|
||||||
|
|
||||||
|
replaceString();
|
||||||
|
|
||||||
|
console.log("[Build] Replace OK");
|
||||||
|
|
||||||
|
// Walk the builds/addon/locale folder's sub folders and rename *.ftl to addonRef-*.ftl
|
||||||
|
renameLocaleFiles();
|
||||||
|
|
||||||
console.log("[Build] Addon prepare OK");
|
console.log("[Build] Addon prepare OK");
|
||||||
|
|
||||||
zip.compressDir(join(buildDir, "addon"), join(buildDir, `${name}.xpi`), {
|
await zip.compressDir(
|
||||||
ignoreBase: true,
|
path.join(buildDir, "addon"),
|
||||||
});
|
path.join(buildDir, `${name}.xpi`),
|
||||||
|
{
|
||||||
|
ignoreBase: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
console.log("[Build] Addon pack OK");
|
console.log("[Build] Addon pack OK");
|
||||||
console.log(
|
console.log(
|
||||||
`[Build] Finished in ${(new Date().getTime() - t.getTime()) / 1000} s.`
|
`[Build] Finished in ${(new Date().getTime() - t.getTime()) / 1000} s.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,13 @@
|
||||||
import { exit, argv } from "process";
|
import { exit } from "process";
|
||||||
import minimist from "minimist";
|
|
||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
import details from "../package.json" assert { type: "json" };
|
import details from "../package.json" assert { type: "json" };
|
||||||
const { addonID, addonName } = details.config;
|
|
||||||
const version = details.version;
|
|
||||||
import cmd from "./zotero-cmd.json" assert { type: "json" };
|
import cmd from "./zotero-cmd.json" assert { type: "json" };
|
||||||
const { exec } = cmd;
|
|
||||||
|
|
||||||
// Run node reload.js -h for help
|
const { addonID, addonName } = details.config;
|
||||||
const args = minimist(argv.slice(2));
|
const { version } = details;
|
||||||
|
const { zoteroBinPath, profilePath } = cmd.exec;
|
||||||
|
|
||||||
const zoteroPath = exec[args.zotero || args.z || Object.keys(exec)[0]];
|
const startZotero = `"${zoteroBinPath}" --debugger --purgecaches -profile "${profilePath}"`;
|
||||||
const profile = args.profile || args.p;
|
|
||||||
const startZotero = `${zoteroPath} --debugger --purgecaches ${
|
|
||||||
profile ? `-p ${profile}` : ""
|
|
||||||
}`;
|
|
||||||
|
|
||||||
const script = `
|
const script = `
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,74 @@
|
||||||
import process from "process";
|
|
||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
import { exit } from "process";
|
import { exit } from "process";
|
||||||
import minimist from "minimist";
|
import { existsSync, writeFileSync, readFileSync, mkdirSync } from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import details from "../package.json" assert { type: "json" };
|
||||||
import cmd from "./zotero-cmd.json" assert { type: "json" };
|
import cmd from "./zotero-cmd.json" assert { type: "json" };
|
||||||
const { exec } = cmd;
|
|
||||||
|
|
||||||
// Run node start.js -h for help
|
const { addonID } = details.config;
|
||||||
const args = minimist(process.argv.slice(2));
|
const { zoteroBinPath, profilePath, dataDir } = cmd.exec;
|
||||||
|
|
||||||
if (args.help || args.h) {
|
if (!existsSync(zoteroBinPath)) {
|
||||||
console.log("Start Zotero Args:");
|
throw new Error("Zotero binary does not exist.");
|
||||||
console.log(
|
|
||||||
"--zotero(-z): Zotero exec key in zotero-cmd.json. Default the first one."
|
|
||||||
);
|
|
||||||
console.log("--profile(-p): Zotero profile name.");
|
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const zoteroPath = exec[args.zotero || args.z || Object.keys(exec)[0]];
|
if (existsSync(profilePath)) {
|
||||||
const profile = args.profile || args.p;
|
const addonProxyFilePath = path.join(profilePath, `extensions/${addonID}`);
|
||||||
|
const buildPath = path.resolve("build/addon");
|
||||||
|
|
||||||
const startZotero = `${zoteroPath} --debugger --purgecaches ${
|
if (!existsSync(path.join(buildPath, "./manifest.json"))) {
|
||||||
profile ? `-p ${profile}` : ""
|
throw new Error(
|
||||||
}`;
|
`The built file does not exist, maybe you need to build the addon first.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeAddonProxyFile() {
|
||||||
|
writeFileSync(addonProxyFilePath, buildPath);
|
||||||
|
console.log(
|
||||||
|
`[info] Addon proxy file has been updated.
|
||||||
|
File path: ${addonProxyFilePath}
|
||||||
|
Addon path: ${buildPath} `,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existsSync(addonProxyFilePath)) {
|
||||||
|
if (readFileSync(addonProxyFilePath, "utf-8") !== buildPath) {
|
||||||
|
writeAddonProxyFile();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
existsSync(profilePath) &&
|
||||||
|
!existsSync(path.join(profilePath, "extensions"))
|
||||||
|
) {
|
||||||
|
mkdirSync(path.join(profilePath, "extensions"));
|
||||||
|
}
|
||||||
|
writeAddonProxyFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefsPath = path.join(profilePath, "prefs.js");
|
||||||
|
if (existsSync(prefsPath)) {
|
||||||
|
const PrefsLines = readFileSync(prefsPath, "utf-8").split("\n");
|
||||||
|
const filteredLines = PrefsLines.map((line) => {
|
||||||
|
if (
|
||||||
|
line.includes("extensions.lastAppBuildId") ||
|
||||||
|
line.includes("extensions.lastAppVersion")
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (line.includes("extensions.zotero.dataDir") && dataDir !== "") {
|
||||||
|
return `user_pref("extensions.zotero.dataDir", "${dataDir}");`;
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
});
|
||||||
|
const updatedPrefs = filteredLines.join("\n");
|
||||||
|
writeFileSync(prefsPath, updatedPrefs, "utf-8");
|
||||||
|
console.log("[info] The <profile>/prefs.js has been modified.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error("The given Zotero profile does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const startZotero = `"${zoteroBinPath}" --debugger --purgecaches -profile "${profilePath}"`;
|
||||||
|
|
||||||
execSync(startZotero);
|
execSync(startZotero);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,18 @@
|
||||||
"killZoteroWindows": "taskkill /f /im zotero.exe",
|
"killZoteroWindows": "taskkill /f /im zotero.exe",
|
||||||
"killZoteroUnix": "kill -9 $(ps -x | grep zotero)",
|
"killZoteroUnix": "kill -9 $(ps -x | grep zotero)",
|
||||||
"exec": {
|
"exec": {
|
||||||
"6": "/path/to/zotero6.exe",
|
"@comment-zoteroBinPath": "Please input the path of the Zotero binary file in `zoteroBinPath`.",
|
||||||
"7": "/path/to/zotero7.exe"
|
"@comment-zoteroBinPath-tip": "The path delimiter should be escaped as `\\` for win32. The path is `*/Zotero.app/Contents/MacOS/zotero` for MacOS.",
|
||||||
|
"zoteroBinPath": "/path/to/zotero.exe",
|
||||||
|
|
||||||
|
"@comment-profilePath": "Please input the path of the profile used for development in `profilePath`.",
|
||||||
|
"@comment-profilePath-tip": "Start the profile manager by `/path/to/zotero.exe -p` to create a profile for development",
|
||||||
|
"@comment-profilePath-see": "https://www.zotero.org/support/kb/profile_directory",
|
||||||
|
"profilePath": "/path/to/profile",
|
||||||
|
|
||||||
|
"@comment-dataDir": "Please input the directory where the database is located in dataDir",
|
||||||
|
"@comment-dataDir-tip": "If this field is kept empty, Zotero will start with the default data.",
|
||||||
|
"@comment-dataDir-see": "https://www.zotero.org/support/zotero_data",
|
||||||
|
"dataDir": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ class Addon {
|
||||||
recentMainNoteIdsArr
|
recentMainNoteIdsArr
|
||||||
.slice(0, 10)
|
.slice(0, 10)
|
||||||
.filter((id) => Zotero.Items.get(id).isNote())
|
.filter((id) => Zotero.Items.get(id).isNote())
|
||||||
.join(",")
|
.join(","),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
previewId: -1,
|
previewId: -1,
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ declare const _currentEditorInstance: {
|
||||||
};
|
};
|
||||||
|
|
||||||
function fromHTML(schema: Schema, html: string, slice?: boolean) {
|
function fromHTML(schema: Schema, html: string, slice?: boolean) {
|
||||||
let domNode = document.createElement("div");
|
const domNode = document.createElement("div");
|
||||||
domNode.innerHTML = html;
|
domNode.innerHTML = html;
|
||||||
let fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
while (domNode.firstChild) {
|
while (domNode.firstChild) {
|
||||||
fragment.appendChild(domNode.firstChild);
|
fragment.appendChild(domNode.firstChild);
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +63,7 @@ function objectIncludes(object1: any, object2: any) {
|
||||||
function findMarkInSet(
|
function findMarkInSet(
|
||||||
marks: readonly Mark[],
|
marks: readonly Mark[],
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
attributes = {}
|
attributes = {},
|
||||||
) {
|
) {
|
||||||
return marks.find((item) => {
|
return marks.find((item) => {
|
||||||
return item.type === type && objectIncludes(item.attrs, attributes);
|
return item.type === type && objectIncludes(item.attrs, attributes);
|
||||||
|
|
@ -128,7 +128,7 @@ function getMarkRangeAtCursor(state: EditorState, type: MarkType) {
|
||||||
const start = $from.parent.childAfter($from.parentOffset);
|
const start = $from.parent.childAfter($from.parentOffset);
|
||||||
if (start.node) {
|
if (start.node) {
|
||||||
const mark = start.node.marks.find(
|
const mark = start.node.marks.find(
|
||||||
(mark) => mark.type.name === type.name
|
(mark) => mark.type.name === type.name,
|
||||||
);
|
);
|
||||||
if (mark) {
|
if (mark) {
|
||||||
return getMarkRange($from, type, mark.attrs);
|
return getMarkRange($from, type, mark.attrs);
|
||||||
|
|
@ -164,7 +164,7 @@ function replaceRange(
|
||||||
to: number,
|
to: number,
|
||||||
text: string | undefined,
|
text: string | undefined,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
attrs: Attrs | string
|
attrs: Attrs | string,
|
||||||
) {
|
) {
|
||||||
return (state: EditorState, dispatch: EditorView["dispatch"]) => {
|
return (state: EditorState, dispatch: EditorView["dispatch"]) => {
|
||||||
const { tr } = state;
|
const { tr } = state;
|
||||||
|
|
@ -189,7 +189,7 @@ function replaceRangeNode(
|
||||||
nodeAttrs: Attrs | string,
|
nodeAttrs: Attrs | string,
|
||||||
markType?: MarkType,
|
markType?: MarkType,
|
||||||
markAttrs?: Attrs | string,
|
markAttrs?: Attrs | string,
|
||||||
select?: boolean
|
select?: boolean,
|
||||||
) {
|
) {
|
||||||
return (state: EditorState, dispatch: EditorView["dispatch"]) => {
|
return (state: EditorState, dispatch: EditorView["dispatch"]) => {
|
||||||
const { tr } = state;
|
const { tr } = state;
|
||||||
|
|
@ -203,7 +203,7 @@ function replaceRangeNode(
|
||||||
const node = nodeType.create(
|
const node = nodeType.create(
|
||||||
nodeAttrs,
|
nodeAttrs,
|
||||||
state.schema.text(text || state.doc.textBetween(from, to)),
|
state.schema.text(text || state.doc.textBetween(from, to)),
|
||||||
markType ? [markType.create(markAttrs)] : []
|
markType ? [markType.create(markAttrs)] : [],
|
||||||
);
|
);
|
||||||
console.log("Replace Node", from, to, node);
|
console.log("Replace Node", from, to, node);
|
||||||
tr.replaceWith(from, to, node);
|
tr.replaceWith(from, to, node);
|
||||||
|
|
@ -218,7 +218,7 @@ function replaceRangeAtCursor(
|
||||||
text: string | undefined,
|
text: string | undefined,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
attrs: Attrs | string,
|
attrs: Attrs | string,
|
||||||
searchType: MarkType
|
searchType: MarkType,
|
||||||
) {
|
) {
|
||||||
return (state: EditorState, dispatch: EditorView["dispatch"]) => {
|
return (state: EditorState, dispatch: EditorView["dispatch"]) => {
|
||||||
const range = getMarkRangeAtCursor(state, searchType);
|
const range = getMarkRangeAtCursor(state, searchType);
|
||||||
|
|
@ -323,9 +323,9 @@ function updateHeadingsInRange(from: number, to: number, levelOffset: number) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function refocusEditor(callback: Function) {
|
function refocusEditor(callback: (args: void) => void) {
|
||||||
let scrollTop = document.querySelector(".editor-core")!.scrollTop;
|
const scrollTop = document.querySelector(".editor-core")!.scrollTop;
|
||||||
let input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.style.position = "absolute";
|
input.style.position = "absolute";
|
||||||
input.style.opacity = "0";
|
input.style.opacity = "0";
|
||||||
document.body.append(input);
|
document.body.append(input);
|
||||||
|
|
@ -344,9 +344,9 @@ function updateImageDimensions(
|
||||||
width: number,
|
width: number,
|
||||||
height: number | undefined,
|
height: number | undefined,
|
||||||
state: EditorState,
|
state: EditorState,
|
||||||
dispatch: EditorView["dispatch"]
|
dispatch: EditorView["dispatch"],
|
||||||
) {
|
) {
|
||||||
let { tr } = state;
|
const { tr } = state;
|
||||||
state.doc.descendants((node: Node, pos: number) => {
|
state.doc.descendants((node: Node, pos: number) => {
|
||||||
if (node.type.name === "image" && node.attrs.nodeID === nodeID) {
|
if (node.type.name === "image" && node.attrs.nodeID === nodeID) {
|
||||||
// tr.step(new SetAttrsStep(pos, { ...node.attrs, width, height }));
|
// tr.step(new SetAttrsStep(pos, { ...node.attrs, width, height }));
|
||||||
|
|
@ -356,7 +356,7 @@ function updateImageDimensions(
|
||||||
pos,
|
pos,
|
||||||
node.type,
|
node.type,
|
||||||
{ ...node.attrs, width, height },
|
{ ...node.attrs, width, height },
|
||||||
node.marks
|
node.marks,
|
||||||
);
|
);
|
||||||
dispatch(tr);
|
dispatch(tr);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
47
src/hooks.ts
47
src/hooks.ts
|
|
@ -47,6 +47,7 @@ import {
|
||||||
createNoteFromMD,
|
createNoteFromMD,
|
||||||
} from "./modules/createNote";
|
} from "./modules/createNote";
|
||||||
import { annotationTagAction } from "./modules/annotationTagAction";
|
import { annotationTagAction } from "./modules/annotationTagAction";
|
||||||
|
import { createZToolkit } from "./utils/ztoolkit";
|
||||||
|
|
||||||
async function onStartup() {
|
async function onStartup() {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
|
@ -57,26 +58,38 @@ async function onStartup() {
|
||||||
initLocale();
|
initLocale();
|
||||||
ztoolkit.ProgressWindow.setIconURI(
|
ztoolkit.ProgressWindow.setIconURI(
|
||||||
"default",
|
"default",
|
||||||
`chrome://${config.addonRef}/content/icons/favicon.png`
|
`chrome://${config.addonRef}/content/icons/favicon.png`,
|
||||||
);
|
);
|
||||||
|
|
||||||
registerNoteLinkProxyHandler();
|
registerNoteLinkProxyHandler();
|
||||||
|
|
||||||
registerNotify(["tab", "item", "item-tag"]);
|
|
||||||
|
|
||||||
registerEditorInstanceHook();
|
registerEditorInstanceHook();
|
||||||
|
|
||||||
initTemplates();
|
initTemplates();
|
||||||
|
|
||||||
|
registerPrefsWindow();
|
||||||
|
|
||||||
|
setSyncing();
|
||||||
|
|
||||||
|
await onMainWindowLoad(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onMainWindowLoad(win: Window): Promise<void> {
|
||||||
|
// Create ztoolkit for every window
|
||||||
|
addon.data.ztoolkit = createZToolkit();
|
||||||
|
|
||||||
|
registerNotify(["tab", "item", "item-tag"]);
|
||||||
|
|
||||||
registerMenus();
|
registerMenus();
|
||||||
|
|
||||||
registerWorkspaceTab();
|
registerWorkspaceTab();
|
||||||
|
|
||||||
registerReaderInitializer();
|
registerReaderInitializer();
|
||||||
|
}
|
||||||
|
|
||||||
registerPrefsWindow();
|
async function onMainWindowUnload(win: Window): Promise<void> {
|
||||||
|
ztoolkit.unregisterAll();
|
||||||
setSyncing();
|
unregisterReaderInitializer();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onShutdown(): void {
|
function onShutdown(): void {
|
||||||
|
|
@ -96,7 +109,7 @@ function onNotify(
|
||||||
event: string,
|
event: string,
|
||||||
type: string,
|
type: string,
|
||||||
ids: number[] | string[],
|
ids: number[] | string[],
|
||||||
extraData: { [key: string]: any }
|
extraData: { [key: string]: any },
|
||||||
) {
|
) {
|
||||||
// Workspace tab select/unselect callback
|
// Workspace tab select/unselect callback
|
||||||
if (event === "select" && type === "tab") {
|
if (event === "select" && type === "tab") {
|
||||||
|
|
@ -128,7 +141,7 @@ function onNotify(
|
||||||
// Reader annotation buttons update
|
// Reader annotation buttons update
|
||||||
if (event === "add" && type === "item") {
|
if (event === "add" && type === "item") {
|
||||||
const annotationItems = Zotero.Items.get(ids as number[]).filter((item) =>
|
const annotationItems = Zotero.Items.get(ids as number[]).filter((item) =>
|
||||||
item.isAnnotation()
|
item.isAnnotation(),
|
||||||
);
|
);
|
||||||
if (annotationItems.length !== 0) {
|
if (annotationItems.length !== 0) {
|
||||||
checkReaderAnnotationButton(annotationItems);
|
checkReaderAnnotationButton(annotationItems);
|
||||||
|
|
@ -163,7 +176,7 @@ function onOpenNote(
|
||||||
mode: "auto" | "preview" | "workspace" | "standalone" = "auto",
|
mode: "auto" | "preview" | "workspace" | "standalone" = "auto",
|
||||||
options: {
|
options: {
|
||||||
lineIndex?: number;
|
lineIndex?: number;
|
||||||
} = {}
|
} = {},
|
||||||
) {
|
) {
|
||||||
const noteItem = Zotero.Items.get(noteId);
|
const noteItem = Zotero.Items.get(noteId);
|
||||||
if (!noteItem?.isNote()) {
|
if (!noteItem?.isNote()) {
|
||||||
|
|
@ -188,6 +201,7 @@ function onOpenNote(
|
||||||
break;
|
break;
|
||||||
case "workspace":
|
case "workspace":
|
||||||
addon.hooks.onSetWorkspaceNote(noteId, "main", options);
|
addon.hooks.onSetWorkspaceNote(noteId, "main", options);
|
||||||
|
break;
|
||||||
case "standalone":
|
case "standalone":
|
||||||
ZoteroPane.openNoteWindow(noteId);
|
ZoteroPane.openNoteWindow(noteId);
|
||||||
break;
|
break;
|
||||||
|
|
@ -201,7 +215,7 @@ function onSetWorkspaceNote(
|
||||||
type: "main" | "preview" = "main",
|
type: "main" | "preview" = "main",
|
||||||
options: {
|
options: {
|
||||||
lineIndex?: number;
|
lineIndex?: number;
|
||||||
} = {}
|
} = {},
|
||||||
) {
|
) {
|
||||||
if (type === "main") {
|
if (type === "main") {
|
||||||
addon.data.workspace.mainId = noteId;
|
addon.data.workspace.mainId = noteId;
|
||||||
|
|
@ -215,13 +229,13 @@ function onSetWorkspaceNote(
|
||||||
addon.data.workspace.window.container,
|
addon.data.workspace.window.container,
|
||||||
type,
|
type,
|
||||||
noteId,
|
noteId,
|
||||||
options
|
options,
|
||||||
);
|
);
|
||||||
type === "preview" &&
|
type === "preview" &&
|
||||||
addon.hooks.onToggleWorkspacePane(
|
addon.hooks.onToggleWorkspacePane(
|
||||||
"preview",
|
"preview",
|
||||||
true,
|
true,
|
||||||
addon.data.workspace.window.container
|
addon.data.workspace.window.container,
|
||||||
);
|
);
|
||||||
addon.data.workspace.window.window?.focus();
|
addon.data.workspace.window.window?.focus();
|
||||||
}
|
}
|
||||||
|
|
@ -230,13 +244,13 @@ function onSetWorkspaceNote(
|
||||||
addon.data.workspace.tab.container,
|
addon.data.workspace.tab.container,
|
||||||
type,
|
type,
|
||||||
noteId,
|
noteId,
|
||||||
options
|
options,
|
||||||
);
|
);
|
||||||
type === "preview" &&
|
type === "preview" &&
|
||||||
addon.hooks.onToggleWorkspacePane(
|
addon.hooks.onToggleWorkspacePane(
|
||||||
"preview",
|
"preview",
|
||||||
true,
|
true,
|
||||||
addon.data.workspace.tab.container
|
addon.data.workspace.tab.container,
|
||||||
);
|
);
|
||||||
Zotero_Tabs.select(addon.data.workspace.tab.id!);
|
Zotero_Tabs.select(addon.data.workspace.tab.id!);
|
||||||
}
|
}
|
||||||
|
|
@ -263,7 +277,7 @@ const onInitWorkspace = initWorkspace;
|
||||||
function onToggleWorkspacePane(
|
function onToggleWorkspacePane(
|
||||||
type: "outline" | "preview" | "notes",
|
type: "outline" | "preview" | "notes",
|
||||||
visibility?: boolean,
|
visibility?: boolean,
|
||||||
container?: XUL.Box
|
container?: XUL.Box,
|
||||||
) {
|
) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "outline":
|
case "outline":
|
||||||
|
|
@ -274,6 +288,7 @@ function onToggleWorkspacePane(
|
||||||
break;
|
break;
|
||||||
case "notes":
|
case "notes":
|
||||||
toggleNotesPane(visibility);
|
toggleNotesPane(visibility);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -311,6 +326,8 @@ const onCreateNoteFromMD = createNoteFromMD;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
onStartup,
|
onStartup,
|
||||||
|
onMainWindowLoad,
|
||||||
|
onMainWindowUnload,
|
||||||
onShutdown,
|
onShutdown,
|
||||||
onNotify,
|
onNotify,
|
||||||
onPrefsEvent,
|
onPrefsEvent,
|
||||||
|
|
|
||||||
32
src/index.ts
32
src/index.ts
|
|
@ -7,22 +7,26 @@ const basicTool = new BasicTool();
|
||||||
if (!basicTool.getGlobal("Zotero")[config.addonInstance]) {
|
if (!basicTool.getGlobal("Zotero")[config.addonInstance]) {
|
||||||
// Set global variables
|
// Set global variables
|
||||||
_globalThis.Zotero = basicTool.getGlobal("Zotero");
|
_globalThis.Zotero = basicTool.getGlobal("Zotero");
|
||||||
_globalThis.ZoteroPane = basicTool.getGlobal("ZoteroPane");
|
defineGlobal("window");
|
||||||
_globalThis.Zotero_Tabs = basicTool.getGlobal("Zotero_Tabs");
|
defineGlobal("document");
|
||||||
_globalThis.window = basicTool.getGlobal("window");
|
defineGlobal("ZoteroPane");
|
||||||
_globalThis.document = basicTool.getGlobal("document");
|
defineGlobal("Zotero_Tabs");
|
||||||
_globalThis.OS = basicTool.getGlobal("OS") as typeof OS;
|
defineGlobal("OS");
|
||||||
_globalThis.addon = new Addon();
|
_globalThis.addon = new Addon();
|
||||||
_globalThis.ztoolkit = addon.data.ztoolkit;
|
Object.defineProperty(_globalThis, "ztoolkit", {
|
||||||
ztoolkit.basicOptions.log.prefix = `[${config.addonName}]`;
|
get() {
|
||||||
ztoolkit.basicOptions.log.disableConsole = addon.data.env === "production";
|
return _globalThis.addon.data.ztoolkit;
|
||||||
ztoolkit.UI.basicOptions.ui.enableElementJSONLog =
|
},
|
||||||
addon.data.env === "development";
|
});
|
||||||
ztoolkit.UI.basicOptions.ui.enableElementDOMLog =
|
|
||||||
addon.data.env === "development";
|
|
||||||
ztoolkit.basicOptions.debug.disableDebugBridgePassword =
|
|
||||||
addon.data.env === "development";
|
|
||||||
Zotero[config.addonInstance] = addon;
|
Zotero[config.addonInstance] = addon;
|
||||||
// Trigger addon hook for initialization
|
// Trigger addon hook for initialization
|
||||||
addon.hooks.onStartup();
|
addon.hooks.onStartup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function defineGlobal(name: Parameters<BasicTool["getGlobal"]>[0]) {
|
||||||
|
Object.defineProperty(_globalThis, name, {
|
||||||
|
get() {
|
||||||
|
return basicTool.getGlobal(name);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ export { annotationTagAction };
|
||||||
|
|
||||||
async function annotationTagAction(
|
async function annotationTagAction(
|
||||||
ids: Array<number | string>,
|
ids: Array<number | string>,
|
||||||
extraData: Record<string, any>
|
extraData: Record<string, any>,
|
||||||
) {
|
) {
|
||||||
const workspaceNote = Zotero.Items.get(addon.data.workspace.mainId);
|
const workspaceNote = Zotero.Items.get(addon.data.workspace.mainId);
|
||||||
if (!workspaceNote || !workspaceNote.isNote()) {
|
if (!workspaceNote || !workspaceNote.isNote()) {
|
||||||
|
|
@ -14,7 +14,7 @@ async function annotationTagAction(
|
||||||
const headings: string[] = nodes.map((node) => node.model.name);
|
const headings: string[] = nodes.map((node) => node.model.name);
|
||||||
|
|
||||||
for (const tagId of ids.filter((t) =>
|
for (const tagId of ids.filter((t) =>
|
||||||
(extraData[t].tag as string).startsWith("@")
|
(extraData[t].tag as string).startsWith("@"),
|
||||||
)) {
|
)) {
|
||||||
const tagName = (extraData[tagId].tag as string).slice(1).trim();
|
const tagName = (extraData[tagId].tag as string).slice(1).trim();
|
||||||
if (headings.includes(tagName) || tagName === "@") {
|
if (headings.includes(tagName) || tagName === "@") {
|
||||||
|
|
@ -35,7 +35,7 @@ async function annotationTagAction(
|
||||||
await addon.api.convert.annotations2html([annotationItem], {
|
await addon.api.convert.annotations2html([annotationItem], {
|
||||||
noteItem: workspaceNote,
|
noteItem: workspaceNote,
|
||||||
}),
|
}),
|
||||||
lineIndex
|
lineIndex,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,22 +54,22 @@ async function note2md(
|
||||||
keepNoteLink?: boolean;
|
keepNoteLink?: boolean;
|
||||||
withYAMLHeader?: boolean;
|
withYAMLHeader?: boolean;
|
||||||
skipSavingImages?: boolean;
|
skipSavingImages?: boolean;
|
||||||
} = {}
|
} = {},
|
||||||
) {
|
) {
|
||||||
const noteStatus = addon.api.sync.getNoteStatus(noteItem.id)!;
|
const noteStatus = addon.api.sync.getNoteStatus(noteItem.id)!;
|
||||||
const rehype = note2rehype(noteStatus.content);
|
const rehype = note2rehype(noteStatus.content);
|
||||||
processN2MRehypeHighlightNodes(
|
processN2MRehypeHighlightNodes(
|
||||||
getN2MRehypeHighlightNodes(rehype),
|
getN2MRehypeHighlightNodes(rehype),
|
||||||
NodeMode.direct
|
NodeMode.direct,
|
||||||
);
|
);
|
||||||
processN2MRehypeCitationNodes(
|
processN2MRehypeCitationNodes(
|
||||||
getN2MRehypeCitationNodes(rehype),
|
getN2MRehypeCitationNodes(rehype),
|
||||||
NodeMode.direct
|
NodeMode.direct,
|
||||||
);
|
);
|
||||||
await processN2MRehypeNoteLinkNodes(
|
await processN2MRehypeNoteLinkNodes(
|
||||||
getN2MRehypeNoteLinkNodes(rehype),
|
getN2MRehypeNoteLinkNodes(rehype),
|
||||||
dir,
|
dir,
|
||||||
options.keepNoteLink ? NodeMode.default : NodeMode.direct
|
options.keepNoteLink ? NodeMode.default : NodeMode.direct,
|
||||||
);
|
);
|
||||||
await processN2MRehypeImageNodes(
|
await processN2MRehypeImageNodes(
|
||||||
getN2MRehypeImageNodes(rehype),
|
getN2MRehypeImageNodes(rehype),
|
||||||
|
|
@ -77,7 +77,7 @@ async function note2md(
|
||||||
formatPath(OS.Path.join(dir, "attachments")),
|
formatPath(OS.Path.join(dir, "attachments")),
|
||||||
options.skipSavingImages,
|
options.skipSavingImages,
|
||||||
false,
|
false,
|
||||||
NodeMode.direct
|
NodeMode.direct,
|
||||||
);
|
);
|
||||||
const remark = await rehype2remark(rehype);
|
const remark = await rehype2remark(rehype);
|
||||||
let md = remark2md(remark);
|
let md = remark2md(remark);
|
||||||
|
|
@ -89,16 +89,18 @@ async function note2md(
|
||||||
await addon.api.template.runTemplate(
|
await addon.api.template.runTemplate(
|
||||||
"[ExportMDFileHeaderV2]",
|
"[ExportMDFileHeaderV2]",
|
||||||
"noteItem",
|
"noteItem",
|
||||||
[noteItem]
|
[noteItem],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
ztoolkit.log(e);
|
||||||
|
}
|
||||||
Object.assign(header, {
|
Object.assign(header, {
|
||||||
version: noteItem.version,
|
version: noteItem.version,
|
||||||
libraryID: noteItem.libraryID,
|
libraryID: noteItem.libraryID,
|
||||||
itemKey: noteItem.key,
|
itemKey: noteItem.key,
|
||||||
});
|
});
|
||||||
let yamlFrontMatter = `---\n${YAML.stringify(header, 10)}\n---`;
|
const yamlFrontMatter = `---\n${YAML.stringify(header, 10)}\n---`;
|
||||||
md = `${yamlFrontMatter}\n${md}`;
|
md = `${yamlFrontMatter}\n${md}`;
|
||||||
}
|
}
|
||||||
return md;
|
return md;
|
||||||
|
|
@ -107,7 +109,7 @@ async function note2md(
|
||||||
async function md2note(
|
async function md2note(
|
||||||
mdStatus: MDStatus,
|
mdStatus: MDStatus,
|
||||||
noteItem: Zotero.Item,
|
noteItem: Zotero.Item,
|
||||||
options: { isImport?: boolean } = {}
|
options: { isImport?: boolean } = {},
|
||||||
) {
|
) {
|
||||||
const remark = md2remark(mdStatus.content);
|
const remark = md2remark(mdStatus.content);
|
||||||
const _rehype = await remark2rehype(remark);
|
const _rehype = await remark2rehype(remark);
|
||||||
|
|
@ -120,14 +122,14 @@ async function md2note(
|
||||||
processM2NRehypeHighlightNodes(getM2NRehypeHighlightNodes(rehype));
|
processM2NRehypeHighlightNodes(getM2NRehypeHighlightNodes(rehype));
|
||||||
await processM2NRehypeCitationNodes(
|
await processM2NRehypeCitationNodes(
|
||||||
getM2NRehypeCitationNodes(rehype),
|
getM2NRehypeCitationNodes(rehype),
|
||||||
options.isImport
|
options.isImport,
|
||||||
);
|
);
|
||||||
processM2NRehypeNoteLinkNodes(getM2NRehypeNoteLinkNodes(rehype));
|
processM2NRehypeNoteLinkNodes(getM2NRehypeNoteLinkNodes(rehype));
|
||||||
await processM2NRehypeImageNodes(
|
await processM2NRehypeImageNodes(
|
||||||
getM2NRehypeImageNodes(rehype),
|
getM2NRehypeImageNodes(rehype),
|
||||||
noteItem,
|
noteItem,
|
||||||
mdStatus.filedir,
|
mdStatus.filedir,
|
||||||
options.isImport
|
options.isImport,
|
||||||
);
|
);
|
||||||
const noteContent = rehype2note(rehype);
|
const noteContent = rehype2note(rehype);
|
||||||
return noteContent;
|
return noteContent;
|
||||||
|
|
@ -143,7 +145,7 @@ async function note2noteDiff(noteItem: Zotero.Item) {
|
||||||
|
|
||||||
function note2link(
|
function note2link(
|
||||||
noteItem: Zotero.Item,
|
noteItem: Zotero.Item,
|
||||||
options: Parameters<typeof getNoteLink>[1]
|
options: Parameters<typeof getNoteLink>[1],
|
||||||
) {
|
) {
|
||||||
return getNoteLink(noteItem, options);
|
return getNoteLink(noteItem, options);
|
||||||
}
|
}
|
||||||
|
|
@ -154,9 +156,9 @@ function link2note(link: string) {
|
||||||
|
|
||||||
async function link2html(
|
async function link2html(
|
||||||
link: string,
|
link: string,
|
||||||
options: { noteItem?: Zotero.Item; dryRun?: boolean } = {}
|
options: { noteItem?: Zotero.Item; dryRun?: boolean } = {},
|
||||||
) {
|
) {
|
||||||
ztoolkit.log("link2html", arguments);
|
ztoolkit.log("link2html", link, options);
|
||||||
const linkParams = getNoteLinkParams(link);
|
const linkParams = getNoteLinkParams(link);
|
||||||
if (!linkParams.noteItem) {
|
if (!linkParams.noteItem) {
|
||||||
return "";
|
return "";
|
||||||
|
|
@ -173,7 +175,7 @@ async function link2html(
|
||||||
// Only embed the note content
|
// Only embed the note content
|
||||||
html,
|
html,
|
||||||
options.noteItem,
|
options.noteItem,
|
||||||
refNotes
|
refNotes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -194,7 +196,7 @@ async function html2md(html: string) {
|
||||||
|
|
||||||
function annotations2html(
|
function annotations2html(
|
||||||
annotations: Zotero.Item[],
|
annotations: Zotero.Item[],
|
||||||
options: Parameters<typeof parseAnnotationHTML>[1] = {}
|
options: Parameters<typeof parseAnnotationHTML>[1] = {},
|
||||||
) {
|
) {
|
||||||
return parseAnnotationHTML(annotations, options);
|
return parseAnnotationHTML(annotations, options);
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +229,7 @@ function note2rehype(str: string) {
|
||||||
removeBlank(_n, parentNode, -1);
|
removeBlank(_n, parentNode, -1);
|
||||||
removeBlank(_n, parentNode, 1);
|
removeBlank(_n, parentNode, 1);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make sure <span> and <img> wrapped by <p>
|
// Make sure <span> and <img> wrapped by <p>
|
||||||
|
|
@ -245,7 +247,7 @@ function note2rehype(str: string) {
|
||||||
replace(_n, p);
|
replace(_n, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make sure empty <p> under root node is removed
|
// Make sure empty <p> under root node is removed
|
||||||
|
|
@ -259,7 +261,7 @@ function note2rehype(str: string) {
|
||||||
parentNode.children.splice(parentNode.children.indexOf(_n), 1);
|
parentNode.children.splice(parentNode.children.indexOf(_n), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
return rehype;
|
return rehype;
|
||||||
}
|
}
|
||||||
|
|
@ -310,13 +312,13 @@ async function rehype2remark(rehype: HRoot) {
|
||||||
if (node.properties.style) {
|
if (node.properties.style) {
|
||||||
hasStyle = true;
|
hasStyle = true;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
if (0 && hasStyle) {
|
// if (0 && hasStyle) {
|
||||||
return h(node, "styleTable", toHtml(node));
|
// return h(node, "styleTable", toHtml(node));
|
||||||
} else {
|
// } else {
|
||||||
return defaultHandlers.table(h, node);
|
return defaultHandlers.table(h, node);
|
||||||
}
|
// }
|
||||||
},
|
},
|
||||||
wrapper: (h, node) => {
|
wrapper: (h, node) => {
|
||||||
return h(node, "wrapper", toText(node));
|
return h(node, "wrapper", toText(node));
|
||||||
|
|
@ -389,7 +391,7 @@ function remark2md(remark: MRoot) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as any)
|
} as any)
|
||||||
.stringify(remark)
|
.stringify(remark),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -401,7 +403,7 @@ function md2remark(str: string) {
|
||||||
.replace(
|
.replace(
|
||||||
/!\[.*\]\((.*)\)/g,
|
/!\[.*\]\((.*)\)/g,
|
||||||
(s: string) =>
|
(s: string) =>
|
||||||
`/g)![0].slice(1, -1))})`
|
`/g)![0].slice(1, -1))})`,
|
||||||
);
|
);
|
||||||
const remark = unified()
|
const remark = unified()
|
||||||
.use(remarkGfm)
|
.use(remarkGfm)
|
||||||
|
|
@ -439,7 +441,7 @@ function rehype2note(rehype: HRoot) {
|
||||||
(node: any) => {
|
(node: any) => {
|
||||||
node.tagName = "span";
|
node.tagName = "span";
|
||||||
node.properties.style = "text-decoration: line-through";
|
node.properties.style = "text-decoration: line-through";
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Code node
|
// Code node
|
||||||
|
|
@ -454,7 +456,7 @@ function rehype2note(rehype: HRoot) {
|
||||||
node.value = toText(node);
|
node.value = toText(node);
|
||||||
node.type = "text";
|
node.type = "text";
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Table node with style
|
// Table node with style
|
||||||
|
|
@ -472,14 +474,14 @@ function rehype2note(rehype: HRoot) {
|
||||||
if (node.properties.style) {
|
if (node.properties.style) {
|
||||||
hasStyle = true;
|
hasStyle = true;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
if (hasStyle) {
|
if (hasStyle) {
|
||||||
node.value = toHtml(node).replace(/[\r\n]/g, "");
|
node.value = toHtml(node).replace(/[\r\n]/g, "");
|
||||||
node.children = [];
|
node.children = [];
|
||||||
node.type = "raw";
|
node.type = "raw";
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Convert thead to tbody
|
// Convert thead to tbody
|
||||||
|
|
@ -490,7 +492,7 @@ function rehype2note(rehype: HRoot) {
|
||||||
node.value = toHtml(node).slice(7, -8);
|
node.value = toHtml(node).slice(7, -8);
|
||||||
node.children = [];
|
node.children = [];
|
||||||
node.type = "raw";
|
node.type = "raw";
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Wrap lines in list with <p> (for diff)
|
// Wrap lines in list with <p> (for diff)
|
||||||
|
|
@ -522,9 +524,9 @@ function rehype2note(rehype: HRoot) {
|
||||||
node.children = node.children.filter(
|
node.children = node.children.filter(
|
||||||
(_n: { type: string; value: string }) =>
|
(_n: { type: string; value: string }) =>
|
||||||
_n.type === "element" ||
|
_n.type === "element" ||
|
||||||
(_n.type === "text" && _n.value.replace(/[\r\n]/g, ""))
|
(_n.type === "text" && _n.value.replace(/[\r\n]/g, "")),
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Math node
|
// Math node
|
||||||
|
|
@ -550,7 +552,7 @@ function rehype2note(rehype: HRoot) {
|
||||||
node.tagName = "pre";
|
node.tagName = "pre";
|
||||||
}
|
}
|
||||||
node.properties.className = "math";
|
node.properties.className = "math";
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ignore link rel attribute, which exists in note
|
// Ignore link rel attribute, which exists in note
|
||||||
|
|
@ -559,7 +561,7 @@ function rehype2note(rehype: HRoot) {
|
||||||
(node: any) => node.type === "element" && (node as any).tagName === "a",
|
(node: any) => node.type === "element" && (node as any).tagName === "a",
|
||||||
(node: any) => {
|
(node: any) => {
|
||||||
node.properties.rel = undefined;
|
node.properties.rel = undefined;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ignore empty lines, as they are not parsed to md
|
// Ignore empty lines, as they are not parsed to md
|
||||||
|
|
@ -610,7 +612,7 @@ function getN2MRehypeHighlightNodes(rehype: HRoot) {
|
||||||
(node: any) =>
|
(node: any) =>
|
||||||
node.type === "element" &&
|
node.type === "element" &&
|
||||||
node.properties?.className?.includes("highlight"),
|
node.properties?.className?.includes("highlight"),
|
||||||
(node) => nodes.push(node)
|
(node) => nodes.push(node),
|
||||||
);
|
);
|
||||||
return new Array(...new Set(nodes));
|
return new Array(...new Set(nodes));
|
||||||
}
|
}
|
||||||
|
|
@ -622,7 +624,7 @@ function getN2MRehypeCitationNodes(rehype: HRoot) {
|
||||||
(node: any) =>
|
(node: any) =>
|
||||||
node.type === "element" &&
|
node.type === "element" &&
|
||||||
node.properties?.className?.includes("citation"),
|
node.properties?.className?.includes("citation"),
|
||||||
(node) => nodes.push(node)
|
(node) => nodes.push(node),
|
||||||
);
|
);
|
||||||
return new Array(...new Set(nodes));
|
return new Array(...new Set(nodes));
|
||||||
}
|
}
|
||||||
|
|
@ -636,7 +638,7 @@ function getN2MRehypeNoteLinkNodes(rehype: any) {
|
||||||
node.tagName === "a" &&
|
node.tagName === "a" &&
|
||||||
node.properties?.href &&
|
node.properties?.href &&
|
||||||
/zotero:\/\/note\/\w+\/\w+\//.test(node.properties?.href),
|
/zotero:\/\/note\/\w+\/\w+\//.test(node.properties?.href),
|
||||||
(node) => nodes.push(node)
|
(node) => nodes.push(node),
|
||||||
);
|
);
|
||||||
return new Array(...new Set(nodes));
|
return new Array(...new Set(nodes));
|
||||||
}
|
}
|
||||||
|
|
@ -649,14 +651,14 @@ function getN2MRehypeImageNodes(rehype: any) {
|
||||||
node.type === "element" &&
|
node.type === "element" &&
|
||||||
node.tagName === "img" &&
|
node.tagName === "img" &&
|
||||||
node.properties?.dataAttachmentKey,
|
node.properties?.dataAttachmentKey,
|
||||||
(node) => nodes.push(node)
|
(node) => nodes.push(node),
|
||||||
);
|
);
|
||||||
return new Array(...new Set(nodes));
|
return new Array(...new Set(nodes));
|
||||||
}
|
}
|
||||||
|
|
||||||
function processN2MRehypeHighlightNodes(
|
function processN2MRehypeHighlightNodes(
|
||||||
nodes: string | any[],
|
nodes: string | any[],
|
||||||
mode: NodeMode = NodeMode.default
|
mode: NodeMode = NodeMode.default,
|
||||||
) {
|
) {
|
||||||
if (!nodes.length) {
|
if (!nodes.length) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -665,7 +667,7 @@ function processN2MRehypeHighlightNodes(
|
||||||
let annotation;
|
let annotation;
|
||||||
try {
|
try {
|
||||||
annotation = JSON.parse(
|
annotation = JSON.parse(
|
||||||
decodeURIComponent(node.properties.dataAnnotation)
|
decodeURIComponent(node.properties.dataAnnotation),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -674,20 +676,20 @@ function processN2MRehypeHighlightNodes(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// annotation.uri was used before note-editor v4
|
// annotation.uri was used before note-editor v4
|
||||||
let uri = annotation.attachmentURI || annotation.uri;
|
const uri = annotation.attachmentURI || annotation.uri;
|
||||||
let position = annotation.position;
|
const position = annotation.position;
|
||||||
|
|
||||||
if (typeof uri === "string" && typeof position === "object") {
|
if (typeof uri === "string" && typeof position === "object") {
|
||||||
let openURI;
|
let openURI;
|
||||||
let uriParts = uri.split("/");
|
const uriParts = uri.split("/");
|
||||||
let libraryType = uriParts[3];
|
const libraryType = uriParts[3];
|
||||||
let key = uriParts[uriParts.length - 1];
|
const key = uriParts[uriParts.length - 1];
|
||||||
if (libraryType === "users") {
|
if (libraryType === "users") {
|
||||||
openURI = "zotero://open-pdf/library/items/" + key;
|
openURI = "zotero://open-pdf/library/items/" + key;
|
||||||
}
|
}
|
||||||
// groups
|
// groups
|
||||||
else {
|
else {
|
||||||
let groupID = uriParts[4];
|
const groupID = uriParts[4];
|
||||||
openURI = "zotero://open-pdf/groups/" + groupID + "/items/" + key;
|
openURI = "zotero://open-pdf/groups/" + groupID + "/items/" + key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -709,7 +711,7 @@ function processN2MRehypeHighlightNodes(
|
||||||
randomString(
|
randomString(
|
||||||
8,
|
8,
|
||||||
Zotero.Utilities.Internal.md5(node.properties.dataAnnotation),
|
Zotero.Utilities.Internal.md5(node.properties.dataAnnotation),
|
||||||
Zotero.Utilities.allowedKeyChars
|
Zotero.Utilities.allowedKeyChars,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (mode === NodeMode.wrap) {
|
if (mode === NodeMode.wrap) {
|
||||||
|
|
@ -731,7 +733,7 @@ function processN2MRehypeHighlightNodes(
|
||||||
|
|
||||||
function processN2MRehypeCitationNodes(
|
function processN2MRehypeCitationNodes(
|
||||||
nodes: string | any[],
|
nodes: string | any[],
|
||||||
mode: NodeMode = NodeMode.default
|
mode: NodeMode = NodeMode.default,
|
||||||
) {
|
) {
|
||||||
if (!nodes.length) {
|
if (!nodes.length) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -747,19 +749,19 @@ function processN2MRehypeCitationNodes(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let uris: any[] = [];
|
const uris: any[] = [];
|
||||||
for (let citationItem of citation.citationItems) {
|
for (const citationItem of citation.citationItems) {
|
||||||
let uri = citationItem.uris[0];
|
const uri = citationItem.uris[0];
|
||||||
if (typeof uri === "string") {
|
if (typeof uri === "string") {
|
||||||
let uriParts = uri.split("/");
|
const uriParts = uri.split("/");
|
||||||
let libraryType = uriParts[3];
|
const libraryType = uriParts[3];
|
||||||
let key = uriParts[uriParts.length - 1];
|
const key = uriParts[uriParts.length - 1];
|
||||||
if (libraryType === "users") {
|
if (libraryType === "users") {
|
||||||
uris.push("zotero://select/library/items/" + key);
|
uris.push("zotero://select/library/items/" + key);
|
||||||
}
|
}
|
||||||
// groups
|
// groups
|
||||||
else {
|
else {
|
||||||
let groupID = uriParts[4];
|
const groupID = uriParts[4];
|
||||||
uris.push("zotero://select/groups/" + groupID + "/items/" + key);
|
uris.push("zotero://select/groups/" + groupID + "/items/" + key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -772,7 +774,7 @@ function processN2MRehypeCitationNodes(
|
||||||
(_n: any) => _n.properties?.className.includes("citation-item"),
|
(_n: any) => _n.properties?.className.includes("citation-item"),
|
||||||
(_n: any) => {
|
(_n: any) => {
|
||||||
return childNodes?.push(_n);
|
return childNodes?.push(_n);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// For unknown reasons, the element will be duplicated. Remove them.
|
// For unknown reasons, the element will be duplicated. Remove them.
|
||||||
|
|
@ -797,7 +799,7 @@ function processN2MRehypeCitationNodes(
|
||||||
const citationKey = randomString(
|
const citationKey = randomString(
|
||||||
8,
|
8,
|
||||||
Zotero.Utilities.Internal.md5(node.properties.dataCitation),
|
Zotero.Utilities.Internal.md5(node.properties.dataCitation),
|
||||||
Zotero.Utilities.allowedKeyChars
|
Zotero.Utilities.allowedKeyChars,
|
||||||
);
|
);
|
||||||
if (mode === NodeMode.wrap) {
|
if (mode === NodeMode.wrap) {
|
||||||
newNode.children.splice(0, 0, h("wrapperleft", `cite:${citationKey}`));
|
newNode.children.splice(0, 0, h("wrapperleft", `cite:${citationKey}`));
|
||||||
|
|
@ -817,7 +819,7 @@ function processN2MRehypeCitationNodes(
|
||||||
async function processN2MRehypeNoteLinkNodes(
|
async function processN2MRehypeNoteLinkNodes(
|
||||||
nodes: string | any[],
|
nodes: string | any[],
|
||||||
dir: string,
|
dir: string,
|
||||||
mode: NodeMode = NodeMode.default
|
mode: NodeMode = NodeMode.default,
|
||||||
) {
|
) {
|
||||||
if (!nodes.length) {
|
if (!nodes.length) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -835,7 +837,7 @@ async function processN2MRehypeNoteLinkNodes(
|
||||||
const linkKey = randomString(
|
const linkKey = randomString(
|
||||||
8,
|
8,
|
||||||
Zotero.Utilities.Internal.md5(node.properties.href),
|
Zotero.Utilities.Internal.md5(node.properties.href),
|
||||||
Zotero.Utilities.allowedKeyChars
|
Zotero.Utilities.allowedKeyChars,
|
||||||
);
|
);
|
||||||
if (mode === NodeMode.wrap) {
|
if (mode === NodeMode.wrap) {
|
||||||
const newNode = h("span", [
|
const newNode = h("span", [
|
||||||
|
|
@ -843,7 +845,7 @@ async function processN2MRehypeNoteLinkNodes(
|
||||||
h(
|
h(
|
||||||
node.tagName,
|
node.tagName,
|
||||||
Object.assign(node.properties, { href: link }),
|
Object.assign(node.properties, { href: link }),
|
||||||
node.children
|
node.children,
|
||||||
),
|
),
|
||||||
h("wrapperright", `note:${linkKey}`),
|
h("wrapperright", `note:${linkKey}`),
|
||||||
]);
|
]);
|
||||||
|
|
@ -869,25 +871,25 @@ async function processN2MRehypeImageNodes(
|
||||||
dir: string,
|
dir: string,
|
||||||
skipSavingImages: boolean = false,
|
skipSavingImages: boolean = false,
|
||||||
absolutePath: boolean = false,
|
absolutePath: boolean = false,
|
||||||
mode: NodeMode = NodeMode.default
|
mode: NodeMode = NodeMode.default,
|
||||||
) {
|
) {
|
||||||
if (!nodes.length) {
|
if (!nodes.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
let imgKey = node.properties.dataAttachmentKey;
|
const imgKey = node.properties.dataAttachmentKey;
|
||||||
|
|
||||||
const attachmentItem = (await Zotero.Items.getByLibraryAndKeyAsync(
|
const attachmentItem = (await Zotero.Items.getByLibraryAndKeyAsync(
|
||||||
libraryID,
|
libraryID,
|
||||||
imgKey
|
imgKey,
|
||||||
)) as Zotero.Item;
|
)) as Zotero.Item;
|
||||||
if (!attachmentItem) {
|
if (!attachmentItem) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let oldFile = String(await attachmentItem.getFilePathAsync());
|
const oldFile = String(await attachmentItem.getFilePathAsync());
|
||||||
let ext = oldFile.split(".").pop();
|
const ext = oldFile.split(".").pop();
|
||||||
let newAbsPath = formatPath(`${dir}/${imgKey}.${ext}`);
|
const newAbsPath = formatPath(`${dir}/${imgKey}.${ext}`);
|
||||||
let newFile = oldFile;
|
let newFile = oldFile;
|
||||||
try {
|
try {
|
||||||
// Don't overwrite
|
// Don't overwrite
|
||||||
|
|
@ -898,7 +900,7 @@ async function processN2MRehypeImageNodes(
|
||||||
newFile = newFile.replace(/\\/g, "/");
|
newFile = newFile.replace(/\\/g, "/");
|
||||||
}
|
}
|
||||||
newFile = Zotero.File.normalizeToUnix(
|
newFile = Zotero.File.normalizeToUnix(
|
||||||
absolutePath ? newFile : `attachments/${newFile.split(/\//).pop()}`
|
absolutePath ? newFile : `attachments/${newFile.split(/\//).pop()}`,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ztoolkit.log(e);
|
ztoolkit.log(e);
|
||||||
|
|
@ -922,7 +924,7 @@ function getM2NRehypeAnnotationNodes(rehype: any) {
|
||||||
visit(
|
visit(
|
||||||
rehype,
|
rehype,
|
||||||
(node: any) => node.type === "element" && node.properties?.dataAnnotation,
|
(node: any) => node.type === "element" && node.properties?.dataAnnotation,
|
||||||
(node: any) => nodes.push(node)
|
(node: any) => nodes.push(node),
|
||||||
);
|
);
|
||||||
return new Array(...new Set(nodes));
|
return new Array(...new Set(nodes));
|
||||||
}
|
}
|
||||||
|
|
@ -933,7 +935,7 @@ function getM2NRehypeHighlightNodes(rehype: any) {
|
||||||
rehype,
|
rehype,
|
||||||
(node: any) =>
|
(node: any) =>
|
||||||
node.type === "element" && node.properties?.ztype === "zhighlight",
|
node.type === "element" && node.properties?.ztype === "zhighlight",
|
||||||
(node) => nodes.push(node)
|
(node) => nodes.push(node),
|
||||||
);
|
);
|
||||||
return new Array(...new Set(nodes));
|
return new Array(...new Set(nodes));
|
||||||
}
|
}
|
||||||
|
|
@ -945,7 +947,7 @@ function getM2NRehypeCitationNodes(rehype: any) {
|
||||||
(node: any) =>
|
(node: any) =>
|
||||||
node.type === "element" &&
|
node.type === "element" &&
|
||||||
(node.properties?.ztype === "zcitation" || node.properties?.dataCitation),
|
(node.properties?.ztype === "zcitation" || node.properties?.dataCitation),
|
||||||
(node) => nodes.push(node)
|
(node) => nodes.push(node),
|
||||||
);
|
);
|
||||||
return new Array(...new Set(nodes));
|
return new Array(...new Set(nodes));
|
||||||
}
|
}
|
||||||
|
|
@ -956,7 +958,7 @@ function getM2NRehypeNoteLinkNodes(rehype: any) {
|
||||||
rehype,
|
rehype,
|
||||||
(node: any) =>
|
(node: any) =>
|
||||||
node.type === "element" && node.properties?.ztype === "znotelink",
|
node.type === "element" && node.properties?.ztype === "znotelink",
|
||||||
(node) => nodes.push(node)
|
(node) => nodes.push(node),
|
||||||
);
|
);
|
||||||
return new Array(...new Set(nodes));
|
return new Array(...new Set(nodes));
|
||||||
}
|
}
|
||||||
|
|
@ -966,7 +968,7 @@ function getM2NRehypeImageNodes(rehype: any) {
|
||||||
visit(
|
visit(
|
||||||
rehype,
|
rehype,
|
||||||
(node: any) => node.type === "element" && node.tagName === "img",
|
(node: any) => node.type === "element" && node.tagName === "img",
|
||||||
(node) => nodes.push(node)
|
(node) => nodes.push(node),
|
||||||
);
|
);
|
||||||
return new Array(...new Set(nodes));
|
return new Array(...new Set(nodes));
|
||||||
}
|
}
|
||||||
|
|
@ -1002,7 +1004,7 @@ function processM2NRehypeHighlightNodes(nodes: string | any[]) {
|
||||||
|
|
||||||
async function processM2NRehypeCitationNodes(
|
async function processM2NRehypeCitationNodes(
|
||||||
nodes: string | any[],
|
nodes: string | any[],
|
||||||
isImport: boolean = false
|
isImport: boolean = false,
|
||||||
) {
|
) {
|
||||||
if (!nodes.length) {
|
if (!nodes.length) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -1021,10 +1023,10 @@ async function processM2NRehypeCitationNodes(
|
||||||
// "properties": {}
|
// "properties": {}
|
||||||
// }
|
// }
|
||||||
const dataCitation = JSON.parse(
|
const dataCitation = JSON.parse(
|
||||||
decodeURIComponent(node.properties.dataCitation)
|
decodeURIComponent(node.properties.dataCitation),
|
||||||
);
|
);
|
||||||
const ids = dataCitation.citationItems.map((c: { uris: string[] }) =>
|
const ids = dataCitation.citationItems.map((c: { uris: string[] }) =>
|
||||||
Zotero.URI.getURIItemID(c.uris[0])
|
Zotero.URI.getURIItemID(c.uris[0]),
|
||||||
);
|
);
|
||||||
const html = (await parseCitationHTML(ids)) || "";
|
const html = (await parseCitationHTML(ids)) || "";
|
||||||
const newNode = note2rehype(html);
|
const newNode = note2rehype(html);
|
||||||
|
|
@ -1040,7 +1042,7 @@ async function processM2NRehypeCitationNodes(
|
||||||
(_n: any) => _n.properties?.className.includes("citation-item"),
|
(_n: any) => _n.properties?.className.includes("citation-item"),
|
||||||
(_n) => {
|
(_n) => {
|
||||||
_n.children = [{ type: "text", value: toText(_n) }];
|
_n.children = [{ type: "text", value: toText(_n) }];
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
delete node.properties?.ztype;
|
delete node.properties?.ztype;
|
||||||
}
|
}
|
||||||
|
|
@ -1064,7 +1066,7 @@ async function processM2NRehypeImageNodes(
|
||||||
nodes: any[],
|
nodes: any[],
|
||||||
noteItem: Zotero.Item,
|
noteItem: Zotero.Item,
|
||||||
fileDir: string,
|
fileDir: string,
|
||||||
isImport: boolean = false
|
isImport: boolean = false,
|
||||||
) {
|
) {
|
||||||
if (!nodes.length || (isImport && !noteItem)) {
|
if (!nodes.length || (isImport && !noteItem)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -1074,7 +1076,7 @@ async function processM2NRehypeImageNodes(
|
||||||
if (isImport) {
|
if (isImport) {
|
||||||
// We encode the src in md2remark and decode it here.
|
// We encode the src in md2remark and decode it here.
|
||||||
let src = Zotero.File.normalizeToUnix(
|
let src = Zotero.File.normalizeToUnix(
|
||||||
decodeURIComponent(node.properties.src)
|
decodeURIComponent(node.properties.src),
|
||||||
);
|
);
|
||||||
const srcType = (src as string).startsWith("data:")
|
const srcType = (src as string).startsWith("data:")
|
||||||
? "b64"
|
? "b64"
|
||||||
|
|
|
||||||
|
|
@ -11,23 +11,23 @@ async function createWorkspaceNote() {
|
||||||
}
|
}
|
||||||
const confirmOperation = window.confirm(
|
const confirmOperation = window.confirm(
|
||||||
`${getString(
|
`${getString(
|
||||||
"menuAddNote.newMainNote.confirmHead"
|
"menuAddNote.newMainNote.confirmHead",
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
)} '${currentCollection.getName()}' ${getString(
|
)} '${currentCollection.getName()}' ${getString(
|
||||||
"menuAddNote.newMainNote.confirmTail"
|
"menuAddNote.newMainNote.confirmTail",
|
||||||
)}`
|
)}`,
|
||||||
);
|
);
|
||||||
if (!confirmOperation) {
|
if (!confirmOperation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const header = window.prompt(
|
const header = window.prompt(
|
||||||
getString("menuAddNote.newMainNote.enterNoteTitle"),
|
getString("menuAddNote.newMainNote.enterNoteTitle"),
|
||||||
`New Note ${new Date().toLocaleString()}`
|
`New Note ${new Date().toLocaleString()}`,
|
||||||
);
|
);
|
||||||
const noteID = await ZoteroPane.newNote();
|
const noteID = await ZoteroPane.newNote();
|
||||||
const noteItem = Zotero.Items.get(noteID);
|
const noteItem = Zotero.Items.get(noteID);
|
||||||
noteItem.setNote(
|
noteItem.setNote(
|
||||||
`<div data-schema-version="${config.dataSchemaVersion}"><h1>${header}</h1>\n</div>`
|
`<div data-schema-version="${config.dataSchemaVersion}"><h1>${header}</h1>\n</div>`,
|
||||||
);
|
);
|
||||||
await noteItem.saveTx();
|
await noteItem.saveTx();
|
||||||
addon.hooks.onSetWorkspaceNote(noteID, "main");
|
addon.hooks.onSetWorkspaceNote(noteID, "main");
|
||||||
|
|
@ -47,10 +47,8 @@ function getLibraryParentId() {
|
||||||
|
|
||||||
function getReaderParentId() {
|
function getReaderParentId() {
|
||||||
const currentReader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
|
const currentReader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
|
||||||
if (currentReader) {
|
|
||||||
}
|
|
||||||
const parentItemId = Zotero.Items.get(
|
const parentItemId = Zotero.Items.get(
|
||||||
currentReader?.itemID || -1
|
currentReader?.itemID || -1,
|
||||||
).parentItemID;
|
).parentItemID;
|
||||||
return parentItemId;
|
return parentItemId;
|
||||||
}
|
}
|
||||||
|
|
@ -58,11 +56,11 @@ function getReaderParentId() {
|
||||||
async function createNoteFromTemplate(noteType: "standalone"): Promise<void>;
|
async function createNoteFromTemplate(noteType: "standalone"): Promise<void>;
|
||||||
async function createNoteFromTemplate(
|
async function createNoteFromTemplate(
|
||||||
noteType: "item",
|
noteType: "item",
|
||||||
parentType: "reader" | "library"
|
parentType: "reader" | "library",
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
async function createNoteFromTemplate(
|
async function createNoteFromTemplate(
|
||||||
noteType: "standalone" | "item",
|
noteType: "standalone" | "item",
|
||||||
parentType?: "reader" | "library"
|
parentType?: "reader" | "library",
|
||||||
) {
|
) {
|
||||||
if (noteType === "item") {
|
if (noteType === "item") {
|
||||||
const parentItemId =
|
const parentItemId =
|
||||||
|
|
@ -94,7 +92,7 @@ async function createNoteFromMD() {
|
||||||
const filepaths = await new ztoolkit.FilePicker(
|
const filepaths = await new ztoolkit.FilePicker(
|
||||||
"Import MarkDown",
|
"Import MarkDown",
|
||||||
"multiple",
|
"multiple",
|
||||||
[[`MarkDown(*.md)`, `*.md`]]
|
[[`MarkDown(*.md)`, `*.md`]],
|
||||||
).open();
|
).open();
|
||||||
|
|
||||||
if (!filepaths) {
|
if (!filepaths) {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export function initEditorImagePreviewer(editor: Zotero.EditorInstance) {
|
||||||
addon.hooks.onShowImageViewer(
|
addon.hooks.onShowImageViewer(
|
||||||
imageList.map((elem) => elem.src),
|
imageList.map((elem) => elem.src),
|
||||||
imageList.indexOf(e.target as HTMLImageElement),
|
imageList.indexOf(e.target as HTMLImageElement),
|
||||||
editor._item.getNoteTitle()
|
editor._item.getNoteTitle(),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
editor._iframeWindow.document.addEventListener("dblclick", (e) => {
|
editor._iframeWindow.document.addEventListener("dblclick", (e) => {
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,12 @@ export function registerEditorInstanceHook() {
|
||||||
apply: (
|
apply: (
|
||||||
target,
|
target,
|
||||||
thisArg,
|
thisArg,
|
||||||
argumentsList: [instance: Zotero.EditorInstance]
|
argumentsList: [instance: Zotero.EditorInstance],
|
||||||
) => {
|
) => {
|
||||||
target.apply(thisArg, argumentsList);
|
target.apply(thisArg, argumentsList);
|
||||||
argumentsList.forEach(onEditorInstanceCreated);
|
argumentsList.forEach(onEditorInstanceCreated);
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ export async function injectEditorScripts(win: Window) {
|
||||||
id: "betternotes-script",
|
id: "betternotes-script",
|
||||||
properties: {
|
properties: {
|
||||||
innerHTML: await getFileContent(
|
innerHTML: await getFileContent(
|
||||||
rootURI + "chrome/content/scripts/editorScript.js"
|
rootURI + "chrome/content/scripts/editorScript.js",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
ignoreIfExists: true,
|
ignoreIfExists: true,
|
||||||
},
|
},
|
||||||
win.document.head
|
win.document.head,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,6 +60,6 @@ export function injectEditorCSS(win: Window) {
|
||||||
},
|
},
|
||||||
ignoreIfExists: true,
|
ignoreIfExists: true,
|
||||||
},
|
},
|
||||||
win.document.head
|
win.document.head,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ export function initEditorMenu(editor: Zotero.EditorInstance) {
|
||||||
editor._iframeWindow.prompt(
|
editor._iframeWindow.prompt(
|
||||||
getString("editor.resizeImage.prompt"),
|
getString("editor.resizeImage.prompt"),
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
getEditorCore(editor).view.state.selection.node?.attrs?.width
|
getEditorCore(editor).view.state.selection.node?.attrs?.width,
|
||||||
) || ""
|
) || "",
|
||||||
);
|
);
|
||||||
if (newWidth && newWidth > 10) {
|
if (newWidth && newWidth > 10) {
|
||||||
updateImageDimensionsAtCursor(editor, newWidth);
|
updateImageDimensionsAtCursor(editor, newWidth);
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ export function initEditorPopup(editor: Zotero.EditorInstance) {
|
||||||
childList: true,
|
childList: true,
|
||||||
attributes: true,
|
attributes: true,
|
||||||
attributeFilter: ["href"],
|
attributeFilter: ["href"],
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
|
||||||
const templateText = await addon.api.template.runTemplate(
|
const templateText = await addon.api.template.runTemplate(
|
||||||
"[QuickImportV2]",
|
"[QuickImportV2]",
|
||||||
"link, noteItem",
|
"link, noteItem",
|
||||||
[link, editorNote]
|
[link, editorNote],
|
||||||
);
|
);
|
||||||
// auto insert to anchor position
|
// auto insert to anchor position
|
||||||
updateURLAtCursor(
|
updateURLAtCursor(
|
||||||
|
|
@ -76,8 +76,8 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
|
||||||
undefined,
|
undefined,
|
||||||
getNoteLink(
|
getNoteLink(
|
||||||
linkNote,
|
linkNote,
|
||||||
Object.assign({}, linkParams, { ignore: true })
|
Object.assign({}, linkParams, { ignore: true }),
|
||||||
)!
|
)!,
|
||||||
);
|
);
|
||||||
insert(editor, templateText);
|
insert(editor, templateText);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -86,14 +86,14 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
|
||||||
undefined,
|
undefined,
|
||||||
getNoteLink(
|
getNoteLink(
|
||||||
linkNote,
|
linkNote,
|
||||||
Object.assign({}, linkParams, { ignore: null })
|
Object.assign({}, linkParams, { ignore: null }),
|
||||||
)!
|
)!,
|
||||||
);
|
);
|
||||||
const lineIndex = getLineAtCursor(editor);
|
const lineIndex = getLineAtCursor(editor);
|
||||||
del(
|
del(
|
||||||
editor,
|
editor,
|
||||||
getPositionAtLine(editor, lineIndex),
|
getPositionAtLine(editor, lineIndex),
|
||||||
getPositionAtLine(editor, lineIndex + 1)
|
getPositionAtLine(editor, lineIndex + 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -117,7 +117,7 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
|
||||||
updateURLAtCursor(
|
updateURLAtCursor(
|
||||||
editor,
|
editor,
|
||||||
linkNote.getNoteTitle(),
|
linkNote.getNoteTitle(),
|
||||||
getURLAtCursor(editor)
|
getURLAtCursor(editor),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -184,7 +184,7 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
|
||||||
// }
|
// }
|
||||||
} else {
|
} else {
|
||||||
Array.from(_window.document.querySelectorAll(".link-popup-extra")).forEach(
|
Array.from(_window.document.querySelectorAll(".link-popup-extra")).forEach(
|
||||||
(elem) => elem.remove()
|
(elem) => elem.remove(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -220,9 +220,9 @@ function updateEditorImagePopup(editor: Zotero.EditorInstance) {
|
||||||
editor._iframeWindow.document
|
editor._iframeWindow.document
|
||||||
.querySelector(".primary-editor")
|
.querySelector(".primary-editor")
|
||||||
?.querySelector(".selected")
|
?.querySelector(".selected")
|
||||||
?.querySelector("img") as HTMLImageElement
|
?.querySelector("img") as HTMLImageElement,
|
||||||
),
|
),
|
||||||
editor._item.getNoteTitle()
|
editor._item.getNoteTitle(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -245,8 +245,8 @@ function updateEditorImagePopup(editor: Zotero.EditorInstance) {
|
||||||
getString("editor.resizeImage.prompt"),
|
getString("editor.resizeImage.prompt"),
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
getEditorCore(editor).view.state.selection.node?.attrs
|
getEditorCore(editor).view.state.selection.node?.attrs
|
||||||
?.width
|
?.width,
|
||||||
) || ""
|
) || "",
|
||||||
);
|
);
|
||||||
if (newWidth && newWidth > 10) {
|
if (newWidth && newWidth > 10) {
|
||||||
updateImageDimensionsAtCursor(editor, newWidth);
|
updateImageDimensionsAtCursor(editor, newWidth);
|
||||||
|
|
@ -257,6 +257,6 @@ function updateEditorImagePopup(editor: Zotero.EditorInstance) {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
editor._iframeWindow.document.querySelector(".image-popup")!
|
editor._iframeWindow.document.querySelector(".image-popup")!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,14 +25,14 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
|
||||||
ICONS.settings,
|
ICONS.settings,
|
||||||
getString("editor.toolbar.settings.title"),
|
getString("editor.toolbar.settings.title"),
|
||||||
"end",
|
"end",
|
||||||
(e) => {}
|
(e) => {},
|
||||||
);
|
);
|
||||||
|
|
||||||
settingsButton.addEventListener("click", async (ev) => {
|
settingsButton.addEventListener("click", async (ev) => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
function removePopup() {
|
function removePopup() {
|
||||||
const popup = editor._iframeWindow.document.querySelector(
|
const popup = editor._iframeWindow.document.querySelector(
|
||||||
`#${makeId("settings-popup")}`
|
`#${makeId("settings-popup")}`,
|
||||||
);
|
);
|
||||||
if (popup) {
|
if (popup) {
|
||||||
popup.remove();
|
popup.remove();
|
||||||
|
|
@ -116,7 +116,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
|
||||||
`<a href="${link}">${
|
`<a href="${link}">${
|
||||||
e.editor._item.getNoteTitle().trim() || link
|
e.editor._item.getNoteTitle().trim() || link
|
||||||
}</a>`,
|
}</a>`,
|
||||||
"text/html"
|
"text/html",
|
||||||
)
|
)
|
||||||
.copy();
|
.copy();
|
||||||
showHint(`Link ${link} copied`);
|
showHint(`Link ${link} copied`);
|
||||||
|
|
@ -158,7 +158,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
|
||||||
settingsButton,
|
settingsButton,
|
||||||
`${config.addonRef}-settings-popup`,
|
`${config.addonRef}-settings-popup`,
|
||||||
"right",
|
"right",
|
||||||
settingsMenuData
|
settingsMenuData,
|
||||||
).then((popup) => {
|
).then((popup) => {
|
||||||
settingsButton.querySelector(".toolbar-button")?.classList.add("active");
|
settingsButton.querySelector(".toolbar-button")?.classList.add("active");
|
||||||
editor._iframeWindow.document.addEventListener("click", removePopup);
|
editor._iframeWindow.document.addEventListener("click", removePopup);
|
||||||
|
|
@ -173,7 +173,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
|
||||||
"middle",
|
"middle",
|
||||||
ztoolkit.UI.createElement(editor._iframeWindow.document, "div", {
|
ztoolkit.UI.createElement(editor._iframeWindow.document, "div", {
|
||||||
properties: { innerHTML: getString("editor.toolbar.main") },
|
properties: { innerHTML: getString("editor.toolbar.main") },
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const onTriggerMenu = (ev: MouseEvent) => {
|
const onTriggerMenu = (ev: MouseEvent) => {
|
||||||
|
|
@ -188,7 +188,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
|
||||||
linkButton,
|
linkButton,
|
||||||
`${config.addonRef}-link-popup`,
|
`${config.addonRef}-link-popup`,
|
||||||
"middle",
|
"middle",
|
||||||
linkMenu
|
linkMenu,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -207,7 +207,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const lineIndex = parseInt(
|
const lineIndex = parseInt(
|
||||||
(ev.target as HTMLDivElement).id.split("-").pop() || "-1"
|
(ev.target as HTMLDivElement).id.split("-").pop() || "-1",
|
||||||
);
|
);
|
||||||
const forwardLink = getNoteLink(noteItem);
|
const forwardLink = getNoteLink(noteItem);
|
||||||
const backLink = getNoteLink(mainNote, { ignore: true, lineIndex });
|
const backLink = getNoteLink(mainNote, { ignore: true, lineIndex });
|
||||||
|
|
@ -221,9 +221,9 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
|
||||||
noteItem.getNoteTitle().trim() || forwardLink,
|
noteItem.getNoteTitle().trim() || forwardLink,
|
||||||
noteItem,
|
noteItem,
|
||||||
mainNote,
|
mainNote,
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
lineIndex
|
lineIndex,
|
||||||
);
|
);
|
||||||
addLineToNote(
|
addLineToNote(
|
||||||
noteItem,
|
noteItem,
|
||||||
|
|
@ -236,8 +236,8 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
|
||||||
noteItem,
|
noteItem,
|
||||||
mainNote,
|
mainNote,
|
||||||
"",
|
"",
|
||||||
]
|
],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
onExitMenu(ev);
|
onExitMenu(ev);
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
@ -250,7 +250,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
|
||||||
ICONS.addon,
|
ICONS.addon,
|
||||||
getString("editor.toolbar.link.title"),
|
getString("editor.toolbar.link.title"),
|
||||||
"middle",
|
"middle",
|
||||||
onClickMenu
|
onClickMenu,
|
||||||
);
|
);
|
||||||
|
|
||||||
linkButton.addEventListener("mouseenter", onTriggerMenu);
|
linkButton.addEventListener("mouseenter", onTriggerMenu);
|
||||||
|
|
@ -312,7 +312,7 @@ function getLinkMenuData(editor: Zotero.EditorInstance): PopupData[] {
|
||||||
getPref("editor.link.insertPosition")
|
getPref("editor.link.insertPosition")
|
||||||
? node.model.lineIndex - 1
|
? node.model.lineIndex - 1
|
||||||
: node.model.endIndex
|
: node.model.endIndex
|
||||||
}`
|
}`,
|
||||||
),
|
),
|
||||||
text: node.model.name,
|
text: node.model.name,
|
||||||
prefix: "·".repeat(node.model.level - 1),
|
prefix: "·".repeat(node.model.level - 1),
|
||||||
|
|
@ -323,7 +323,7 @@ function getLinkMenuData(editor: Zotero.EditorInstance): PopupData[] {
|
||||||
|
|
||||||
async function registerEditorToolbar(
|
async function registerEditorToolbar(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
id: string
|
id: string,
|
||||||
) {
|
) {
|
||||||
await editor._initPromise;
|
await editor._initPromise;
|
||||||
const _document = editor._iframeWindow.document;
|
const _document = editor._iframeWindow.document;
|
||||||
|
|
@ -359,7 +359,7 @@ async function registerEditorToolbarDropdown(
|
||||||
icon: string,
|
icon: string,
|
||||||
title: string,
|
title: string,
|
||||||
position: "start" | "middle" | "end",
|
position: "start" | "middle" | "end",
|
||||||
callback: (e: MouseEvent & { editor: Zotero.EditorInstance }) => any
|
callback: (e: MouseEvent & { editor: Zotero.EditorInstance }) => any,
|
||||||
) {
|
) {
|
||||||
await editor._initPromise;
|
await editor._initPromise;
|
||||||
const _document = editor._iframeWindow.document;
|
const _document = editor._iframeWindow.document;
|
||||||
|
|
@ -386,7 +386,7 @@ async function registerEditorToolbarDropdown(
|
||||||
Object.assign(e, { editor });
|
Object.assign(e, { editor });
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(
|
callback(
|
||||||
e as any as MouseEvent & { editor: Zotero.EditorInstance }
|
e as any as MouseEvent & { editor: Zotero.EditorInstance },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -414,7 +414,7 @@ async function registerEditorToolbarPopup(
|
||||||
dropdown: HTMLDivElement,
|
dropdown: HTMLDivElement,
|
||||||
id: string,
|
id: string,
|
||||||
align: "middle" | "left" | "right",
|
align: "middle" | "left" | "right",
|
||||||
popupLines: PopupData[]
|
popupLines: PopupData[],
|
||||||
) {
|
) {
|
||||||
await editor._initPromise;
|
await editor._initPromise;
|
||||||
const popup = ztoolkit.UI.appendElement(
|
const popup = ztoolkit.UI.appendElement(
|
||||||
|
|
@ -449,7 +449,7 @@ async function registerEditorToolbarPopup(
|
||||||
props.callback(
|
props.callback(
|
||||||
e as any as MouseEvent & {
|
e as any as MouseEvent & {
|
||||||
editor: Zotero.EditorInstance;
|
editor: Zotero.EditorInstance;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -458,7 +458,7 @@ async function registerEditorToolbarPopup(
|
||||||
}),
|
}),
|
||||||
removeIfExists: true,
|
removeIfExists: true,
|
||||||
},
|
},
|
||||||
dropdown
|
dropdown,
|
||||||
) as HTMLDivElement;
|
) as HTMLDivElement;
|
||||||
let style: string = "";
|
let style: string = "";
|
||||||
if (align === "middle") {
|
if (align === "middle") {
|
||||||
|
|
@ -476,7 +476,7 @@ async function registerEditorToolbarElement(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
toolbar: HTMLDivElement,
|
toolbar: HTMLDivElement,
|
||||||
position: "start" | "middle" | "end",
|
position: "start" | "middle" | "end",
|
||||||
elem: HTMLElement
|
elem: HTMLElement,
|
||||||
) {
|
) {
|
||||||
await editor._initPromise;
|
await editor._initPromise;
|
||||||
toolbar.querySelector(`.${position}`)?.append(elem);
|
toolbar.querySelector(`.${position}`)?.append(elem);
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ async function exportNotes(
|
||||||
exportDocx?: boolean;
|
exportDocx?: boolean;
|
||||||
exportPDF?: boolean;
|
exportPDF?: boolean;
|
||||||
exportFreeMind?: boolean;
|
exportFreeMind?: boolean;
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
let inputNoteItems = noteItems;
|
let inputNoteItems = noteItems;
|
||||||
// If embedLink or exportNote, create a new note item
|
// If embedLink or exportNote, create a new note item
|
||||||
|
|
@ -51,26 +51,26 @@ async function exportNotes(
|
||||||
if (options.standaloneLink) {
|
if (options.standaloneLink) {
|
||||||
const linkedNoteIds = [] as number[];
|
const linkedNoteIds = [] as number[];
|
||||||
for (const noteItem of inputNoteItems) {
|
for (const noteItem of inputNoteItems) {
|
||||||
let linkedIds: number[] = getLinkedNotesRecursively(
|
const linkedIds: number[] = getLinkedNotesRecursively(
|
||||||
getNoteLink(noteItem) || "",
|
getNoteLink(noteItem) || "",
|
||||||
linkedNoteIds
|
linkedNoteIds,
|
||||||
);
|
);
|
||||||
linkedNoteIds.push(...linkedIds);
|
linkedNoteIds.push(...linkedIds);
|
||||||
}
|
}
|
||||||
const targetNoteItemIds = inputNoteItems.map((item) => item.id);
|
const targetNoteItemIds = inputNoteItems.map((item) => item.id);
|
||||||
linkedNoteItems = Zotero.Items.get(
|
linkedNoteItems = Zotero.Items.get(
|
||||||
linkedNoteIds.filter((id) => !targetNoteItemIds.includes(id))
|
linkedNoteIds.filter((id) => !targetNoteItemIds.includes(id)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const allNoteItems = Array.from(
|
const allNoteItems = Array.from(
|
||||||
new Set(inputNoteItems.concat(linkedNoteItems))
|
new Set(inputNoteItems.concat(linkedNoteItems)),
|
||||||
);
|
);
|
||||||
if (options.exportMD) {
|
if (options.exportMD) {
|
||||||
if (options.setAutoSync) {
|
if (options.setAutoSync) {
|
||||||
const raw = await new ztoolkit.FilePicker(
|
const raw = await new ztoolkit.FilePicker(
|
||||||
`${getString("fileInterface.sync")} MarkDown File`,
|
`${getString("fileInterface.sync")} MarkDown File`,
|
||||||
"folder"
|
"folder",
|
||||||
).open();
|
).open();
|
||||||
if (raw) {
|
if (raw) {
|
||||||
const syncDir = formatPath(raw);
|
const syncDir = formatPath(raw);
|
||||||
|
|
@ -136,7 +136,7 @@ async function toMD(
|
||||||
filename?: string;
|
filename?: string;
|
||||||
keepNoteLink?: boolean;
|
keepNoteLink?: boolean;
|
||||||
withYAMLHeader?: boolean;
|
withYAMLHeader?: boolean;
|
||||||
} = {}
|
} = {},
|
||||||
) {
|
) {
|
||||||
let filename = options.filename;
|
let filename = options.filename;
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
|
|
@ -144,7 +144,7 @@ async function toMD(
|
||||||
`${Zotero.getString("fileInterface.export")} MarkDown File`,
|
`${Zotero.getString("fileInterface.export")} MarkDown File`,
|
||||||
"save",
|
"save",
|
||||||
[["MarkDown File(*.md)", "*.md"]],
|
[["MarkDown File(*.md)", "*.md"]],
|
||||||
`${noteItem.getNoteTitle()}.md`
|
`${noteItem.getNoteTitle()}.md`,
|
||||||
).open();
|
).open();
|
||||||
if (!raw) return;
|
if (!raw) return;
|
||||||
filename = formatPath(raw, ".md");
|
filename = formatPath(raw, ".md");
|
||||||
|
|
@ -155,7 +155,7 @@ async function toMD(
|
||||||
async function toSync(
|
async function toSync(
|
||||||
noteItem: Zotero.Item,
|
noteItem: Zotero.Item,
|
||||||
syncDir: string,
|
syncDir: string,
|
||||||
overwrite: boolean = false
|
overwrite: boolean = false,
|
||||||
) {
|
) {
|
||||||
if (!overwrite && addon.api.sync.isSyncNote(noteItem.id)) {
|
if (!overwrite && addon.api.sync.isSyncNote(noteItem.id)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -175,7 +175,7 @@ async function toDocx(noteItem: Zotero.Item) {
|
||||||
`${Zotero.getString("fileInterface.export")} MS Word Docx`,
|
`${Zotero.getString("fileInterface.export")} MS Word Docx`,
|
||||||
"save",
|
"save",
|
||||||
[["MS Word Docx File(*.docx)", "*.docx"]],
|
[["MS Word Docx File(*.docx)", "*.docx"]],
|
||||||
`${noteItem.getNoteTitle()}.docx`
|
`${noteItem.getNoteTitle()}.docx`,
|
||||||
).open();
|
).open();
|
||||||
if (!raw) return;
|
if (!raw) return;
|
||||||
const filename = formatPath(raw, ".docx");
|
const filename = formatPath(raw, ".docx");
|
||||||
|
|
@ -187,7 +187,7 @@ async function toFreeMind(noteItem: Zotero.Item) {
|
||||||
`${Zotero.getString("fileInterface.export")} FreeMind XML`,
|
`${Zotero.getString("fileInterface.export")} FreeMind XML`,
|
||||||
"save",
|
"save",
|
||||||
[["FreeMind XML File(*.mm)", "*.mm"]],
|
[["FreeMind XML File(*.mm)", "*.mm"]],
|
||||||
`${noteItem.getNoteTitle()}.mm`
|
`${noteItem.getNoteTitle()}.mm`,
|
||||||
).open();
|
).open();
|
||||||
if (!raw) return;
|
if (!raw) return;
|
||||||
const filename = formatPath(raw, ".mm");
|
const filename = formatPath(raw, ".mm");
|
||||||
|
|
@ -197,9 +197,9 @@ async function toFreeMind(noteItem: Zotero.Item) {
|
||||||
async function embedLinkedNotes(noteItem: Zotero.Item): Promise<string> {
|
async function embedLinkedNotes(noteItem: Zotero.Item): Promise<string> {
|
||||||
const parser = ztoolkit.getDOMParser();
|
const parser = ztoolkit.getDOMParser();
|
||||||
|
|
||||||
let newLines: string[] = [];
|
const newLines: string[] = [];
|
||||||
const noteLines = getLinesInNote(noteItem);
|
const noteLines = getLinesInNote(noteItem);
|
||||||
for (let i in noteLines) {
|
for (const i in noteLines) {
|
||||||
newLines.push(noteLines[i]);
|
newLines.push(noteLines[i]);
|
||||||
const doc = parser.parseFromString(noteLines[i], "text/html");
|
const doc = parser.parseFromString(noteLines[i], "text/html");
|
||||||
const linkParams = Array.from(doc.querySelectorAll("a"))
|
const linkParams = Array.from(doc.querySelectorAll("a"))
|
||||||
|
|
@ -210,7 +210,7 @@ async function embedLinkedNotes(noteItem: Zotero.Item): Promise<string> {
|
||||||
const html = await addon.api.template.runTemplate(
|
const html = await addon.api.template.runTemplate(
|
||||||
"[QuickImportV2]",
|
"[QuickImportV2]",
|
||||||
"link, noteItem",
|
"link, noteItem",
|
||||||
[linkParam.link, noteItem]
|
[linkParam.link, noteItem],
|
||||||
);
|
);
|
||||||
newLines.push(html);
|
newLines.push(html);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ async function note2docx(noteItem: Zotero.Item) {
|
||||||
jobId,
|
jobId,
|
||||||
message: htmlDoc,
|
message: htmlDoc,
|
||||||
},
|
},
|
||||||
"*"
|
"*",
|
||||||
);
|
);
|
||||||
await lock.promise;
|
await lock.promise;
|
||||||
worker.contentWindow?.removeEventListener("message", listener);
|
worker.contentWindow?.removeEventListener("message", listener);
|
||||||
|
|
@ -47,7 +47,7 @@ async function getWorker() {
|
||||||
return addon.data.export.docx.worker;
|
return addon.data.export.docx.worker;
|
||||||
}
|
}
|
||||||
const worker = Zotero.Browser.createHiddenBrowser(
|
const worker = Zotero.Browser.createHiddenBrowser(
|
||||||
window
|
window,
|
||||||
) as HTMLIFrameElement;
|
) as HTMLIFrameElement;
|
||||||
await waitUtilAsync(() => worker.contentDocument?.readyState === "complete");
|
await waitUtilAsync(() => worker.contentDocument?.readyState === "complete");
|
||||||
|
|
||||||
|
|
@ -57,11 +57,11 @@ async function getWorker() {
|
||||||
tag: "script",
|
tag: "script",
|
||||||
properties: {
|
properties: {
|
||||||
innerHTML: await getFileContent(
|
innerHTML: await getFileContent(
|
||||||
rootURI + "chrome/content/scripts/docxWorker.js"
|
rootURI + "chrome/content/scripts/docxWorker.js",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
doc?.head!
|
doc!.head,
|
||||||
);
|
);
|
||||||
addon.data.export.docx.worker = worker;
|
addon.data.export.docx.worker = worker;
|
||||||
return worker;
|
return worker;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ enum OPTIONS {
|
||||||
|
|
||||||
export async function showExportNoteOptions(
|
export async function showExportNoteOptions(
|
||||||
noteIds: number[],
|
noteIds: number[],
|
||||||
overwriteOptions: Record<string, any> = {}
|
overwriteOptions: Record<string, any> = {},
|
||||||
) {
|
) {
|
||||||
const items = Zotero.Items.get(noteIds);
|
const items = Zotero.Items.get(noteIds);
|
||||||
const noteItems: Zotero.Item[] = [];
|
const noteItems: Zotero.Item[] = [];
|
||||||
|
|
@ -33,17 +33,20 @@ export async function showExportNoteOptions(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dataKeys = Object.keys(OPTIONS).filter(
|
const dataKeys = Object.keys(OPTIONS).filter(
|
||||||
(value) => typeof value === "string"
|
(value) => typeof value === "string",
|
||||||
|
);
|
||||||
|
const data = dataKeys.reduce(
|
||||||
|
(acc, key) => {
|
||||||
|
acc[key] = getPref(`export.${key}`) as boolean;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, any>,
|
||||||
);
|
);
|
||||||
const data = dataKeys.reduce((acc, key) => {
|
|
||||||
acc[key] = getPref(`export.${key}`) as boolean;
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<string, any>);
|
|
||||||
|
|
||||||
data.loadCallback = () => {
|
data.loadCallback = () => {
|
||||||
const doc = dialog.window.document;
|
const doc = dialog.window.document;
|
||||||
const standaloneLinkRadio = doc.querySelector(
|
const standaloneLinkRadio = doc.querySelector(
|
||||||
"#standaloneLink"
|
"#standaloneLink",
|
||||||
) as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
const autoSyncRadio = doc.querySelector("#setAutoSync") as HTMLInputElement;
|
const autoSyncRadio = doc.querySelector("#setAutoSync") as HTMLInputElement;
|
||||||
function updateSyncCheckbox() {
|
function updateSyncCheckbox() {
|
||||||
|
|
@ -56,7 +59,7 @@ export async function showExportNoteOptions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Array.from(doc.querySelectorAll('input[name="linkMode"]')).forEach((elem) =>
|
Array.from(doc.querySelectorAll('input[name="linkMode"]')).forEach((elem) =>
|
||||||
elem.addEventListener("change", updateSyncCheckbox)
|
elem.addEventListener("change", updateSyncCheckbox),
|
||||||
);
|
);
|
||||||
updateSyncCheckbox();
|
updateSyncCheckbox();
|
||||||
};
|
};
|
||||||
|
|
@ -77,7 +80,7 @@ export async function showExportNoteOptions(
|
||||||
properties: {
|
properties: {
|
||||||
innerHTML: `${getString("export.target")}: ${fill(
|
innerHTML: `${getString("export.target")}: ${fill(
|
||||||
slice(noteItems[0].getNoteTitle(), 40),
|
slice(noteItems[0].getNoteTitle(), 40),
|
||||||
40
|
40,
|
||||||
)}${
|
)}${
|
||||||
noteItems.length > 1 ? ` and ${noteItems.length - 1} more` : ""
|
noteItems.length > 1 ? ` and ${noteItems.length - 1} more` : ""
|
||||||
}`,
|
}`,
|
||||||
|
|
@ -114,7 +117,7 @@ export async function showExportNoteOptions(
|
||||||
if (data._lastButtonId === "confirm") {
|
if (data._lastButtonId === "confirm") {
|
||||||
await addon.api.$export.exportNotes(
|
await addon.api.$export.exportNotes(
|
||||||
noteItems,
|
noteItems,
|
||||||
Object.assign(data as Record<string, boolean>, overwriteOptions)
|
Object.assign(data as Record<string, boolean>, overwriteOptions),
|
||||||
);
|
);
|
||||||
dataKeys.forEach((key) => {
|
dataKeys.forEach((key) => {
|
||||||
setPref(`export.${key}`, Boolean(data[key]));
|
setPref(`export.${key}`, Boolean(data[key]));
|
||||||
|
|
@ -187,7 +190,7 @@ function makeCheckboxLine(dataKey: string, callback?: (ev: Event) => void) {
|
||||||
function makeRadioLine(
|
function makeRadioLine(
|
||||||
dataKey: string,
|
dataKey: string,
|
||||||
radioName: string,
|
radioName: string,
|
||||||
callback?: (ev: Event) => void
|
callback?: (ev: Event) => void,
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
tag: "div",
|
tag: "div",
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ export async function saveFreeMind(filename: string, noteId: number) {
|
||||||
|
|
||||||
async function note2mm(
|
async function note2mm(
|
||||||
noteItem: Zotero.Item,
|
noteItem: Zotero.Item,
|
||||||
options: { withContent?: boolean } = { withContent: true }
|
options: { withContent?: boolean } = { withContent: true },
|
||||||
) {
|
) {
|
||||||
const root = getNoteTree(noteItem, false);
|
const root = getNoteTree(noteItem, false);
|
||||||
const textNodeForEach = (e: Node, callbackfn: Function) => {
|
const textNodeForEach = (e: Node, callbackfn: (e: any) => void) => {
|
||||||
if (e.nodeType === document.TEXT_NODE) {
|
if (e.nodeType === document.TEXT_NODE) {
|
||||||
callbackfn(e);
|
callbackfn(e);
|
||||||
return;
|
return;
|
||||||
|
|
@ -57,7 +57,7 @@ async function note2mm(
|
||||||
};
|
};
|
||||||
const convertNode = (node: TreeModel.Node<NoteNodeData>) => {
|
const convertNode = (node: TreeModel.Node<NoteNodeData>) => {
|
||||||
mmXML += `<node ID="${node.model.id}" TEXT="${html2Escape(
|
mmXML += `<node ID="${node.model.id}" TEXT="${html2Escape(
|
||||||
node.model.name || noteItem.getNoteTitle()
|
node.model.name || noteItem.getNoteTitle(),
|
||||||
)}"><hook NAME="AlwaysUnfoldedNode" />`;
|
)}"><hook NAME="AlwaysUnfoldedNode" />`;
|
||||||
if (
|
if (
|
||||||
options.withContent &&
|
options.withContent &&
|
||||||
|
|
@ -70,9 +70,9 @@ async function note2mm(
|
||||||
node.model.lineIndex,
|
node.model.lineIndex,
|
||||||
node.hasChildren()
|
node.hasChildren()
|
||||||
? node.children[0].model.lineIndex
|
? node.children[0].model.lineIndex
|
||||||
: node.model.endIndex + 1
|
: node.model.endIndex + 1,
|
||||||
)
|
)
|
||||||
.join("\n")
|
.join("\n"),
|
||||||
)}</body></html></richcontent>`;
|
)}</body></html></richcontent>`;
|
||||||
}
|
}
|
||||||
if (node.hasChildren()) {
|
if (node.hasChildren()) {
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@ export async function saveMD(
|
||||||
options: {
|
options: {
|
||||||
keepNoteLink?: boolean;
|
keepNoteLink?: boolean;
|
||||||
withYAMLHeader?: boolean;
|
withYAMLHeader?: boolean;
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
const noteItem = Zotero.Items.get(noteId);
|
const noteItem = Zotero.Items.get(noteId);
|
||||||
const dir = OS.Path.join(
|
const dir = OS.Path.join(
|
||||||
...OS.Path.split(formatPath(filename)).components.slice(0, -1)
|
...OS.Path.split(formatPath(filename)).components.slice(0, -1),
|
||||||
);
|
);
|
||||||
const hasImage = noteItem.getNote().includes("<img");
|
const hasImage = noteItem.getNote().includes("<img");
|
||||||
if (hasImage) {
|
if (hasImage) {
|
||||||
|
|
@ -19,7 +19,7 @@ export async function saveMD(
|
||||||
}
|
}
|
||||||
await Zotero.File.putContentsAsync(
|
await Zotero.File.putContentsAsync(
|
||||||
filename,
|
filename,
|
||||||
await addon.api.convert.note2md(noteItem, dir, options)
|
await addon.api.convert.note2md(noteItem, dir, options),
|
||||||
);
|
);
|
||||||
|
|
||||||
showHintWithLink(`Note Saved to ${filename}`, "Show in Folder", (ev) => {
|
showHintWithLink(`Note Saved to ${filename}`, "Show in Folder", (ev) => {
|
||||||
|
|
@ -32,7 +32,7 @@ export async function syncMDBatch(saveDir: string, noteIds: number[]) {
|
||||||
await Zotero.File.createDirectoryIfMissingAsync(saveDir);
|
await Zotero.File.createDirectoryIfMissingAsync(saveDir);
|
||||||
const attachmentsDir = formatPath(OS.Path.join(saveDir, "attachments"));
|
const attachmentsDir = formatPath(OS.Path.join(saveDir, "attachments"));
|
||||||
const hasImage = noteItems.some((noteItem) =>
|
const hasImage = noteItems.some((noteItem) =>
|
||||||
noteItem.getNote().includes("<img")
|
noteItem.getNote().includes("<img"),
|
||||||
);
|
);
|
||||||
if (hasImage) {
|
if (hasImage) {
|
||||||
await Zotero.File.createDirectoryIfMissingAsync(attachmentsDir);
|
await Zotero.File.createDirectoryIfMissingAsync(attachmentsDir);
|
||||||
|
|
@ -51,7 +51,7 @@ export async function syncMDBatch(saveDir: string, noteIds: number[]) {
|
||||||
itemID: noteItem.id,
|
itemID: noteItem.id,
|
||||||
md5: Zotero.Utilities.Internal.md5(
|
md5: Zotero.Utilities.Internal.md5(
|
||||||
addon.api.sync.getMDStatusFromContent(content).content,
|
addon.api.sync.getMDStatusFromContent(content).content,
|
||||||
false
|
false,
|
||||||
),
|
),
|
||||||
noteMd5: Zotero.Utilities.Internal.md5(noteItem.getNote(), false),
|
noteMd5: Zotero.Utilities.Internal.md5(noteItem.getNote(), false),
|
||||||
lastsync: new Date().getTime(),
|
lastsync: new Date().getTime(),
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export async function savePDF(noteId: number) {
|
||||||
const win = window.openDialog(
|
const win = window.openDialog(
|
||||||
`chrome://${config.addonRef}/content/pdfPrinter.html`,
|
`chrome://${config.addonRef}/content/pdfPrinter.html`,
|
||||||
`${config.addonRef}-imageViewer`,
|
`${config.addonRef}-imageViewer`,
|
||||||
`chrome,centerscreen,resizable,status,width=900,height=650,dialog=no`
|
`chrome,centerscreen,resizable,status,width=900,height=650,dialog=no`,
|
||||||
)!;
|
)!;
|
||||||
await waitUtilAsync(() => win.document.readyState === "complete");
|
await waitUtilAsync(() => win.document.readyState === "complete");
|
||||||
await Zotero.Promise.delay(3000);
|
await Zotero.Promise.delay(3000);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { waitUtilAsync } from "../utils/wait";
|
||||||
export async function showImageViewer(
|
export async function showImageViewer(
|
||||||
srcList: string[],
|
srcList: string[],
|
||||||
idx: number,
|
idx: number,
|
||||||
title: string
|
title: string,
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
!addon.data.imageViewer.window ||
|
!addon.data.imageViewer.window ||
|
||||||
|
|
@ -19,16 +19,16 @@ export async function showImageViewer(
|
||||||
`${config.addonRef}-imageViewer`,
|
`${config.addonRef}-imageViewer`,
|
||||||
`chrome,centerscreen,resizable,status,width=500,height=550,dialog=no${
|
`chrome,centerscreen,resizable,status,width=500,height=550,dialog=no${
|
||||||
addon.data.imageViewer.pined ? ",alwaysRaised=yes" : ""
|
addon.data.imageViewer.pined ? ",alwaysRaised=yes" : ""
|
||||||
}`
|
}`,
|
||||||
)!;
|
)!;
|
||||||
await waitUtilAsync(
|
await waitUtilAsync(
|
||||||
() => addon.data.imageViewer.window?.document.readyState === "complete"
|
() => addon.data.imageViewer.window?.document.readyState === "complete",
|
||||||
);
|
);
|
||||||
const container = addon.data.imageViewer.window.document.querySelector(
|
const container = addon.data.imageViewer.window.document.querySelector(
|
||||||
".container"
|
".container",
|
||||||
) as HTMLDivElement;
|
) as HTMLDivElement;
|
||||||
const img = addon.data.imageViewer.window.document.querySelector(
|
const img = addon.data.imageViewer.window.document.querySelector(
|
||||||
"#image"
|
"#image",
|
||||||
) as HTMLImageElement;
|
) as HTMLImageElement;
|
||||||
|
|
||||||
addon.data.imageViewer.window.document
|
addon.data.imageViewer.window.document
|
||||||
|
|
@ -75,26 +75,26 @@ export async function showImageViewer(
|
||||||
addon.data.imageViewer.window.document
|
addon.data.imageViewer.window.document
|
||||||
.querySelector("#save")
|
.querySelector("#save")
|
||||||
?.addEventListener("click", async (e) => {
|
?.addEventListener("click", async (e) => {
|
||||||
let parts =
|
const parts =
|
||||||
addon.data.imageViewer.srcList[addon.data.imageViewer.idx].split(",");
|
addon.data.imageViewer.srcList[addon.data.imageViewer.idx].split(",");
|
||||||
if (!parts[0].includes("base64")) {
|
if (!parts[0].includes("base64")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mime = parts[0].match(/:(.*?);/)![1];
|
const mime = parts[0].match(/:(.*?);/)![1];
|
||||||
let bstr = addon.data.imageViewer.window?.atob(parts[1])!;
|
const bstr = ztoolkit.getGlobal("atob")(parts[1]);
|
||||||
let n = bstr.length;
|
let n = bstr.length;
|
||||||
let u8arr = new Uint8Array(n);
|
const u8arr = new Uint8Array(n);
|
||||||
while (n--) {
|
while (n--) {
|
||||||
u8arr[n] = bstr.charCodeAt(n);
|
u8arr[n] = bstr.charCodeAt(n);
|
||||||
}
|
}
|
||||||
let ext = Zotero.MIME.getPrimaryExtension(mime, "");
|
const ext = Zotero.MIME.getPrimaryExtension(mime, "");
|
||||||
const filename = await new ztoolkit.FilePicker(
|
const filename = await new ztoolkit.FilePicker(
|
||||||
Zotero.getString("noteEditor.saveImageAs"),
|
Zotero.getString("noteEditor.saveImageAs"),
|
||||||
"save",
|
"save",
|
||||||
[[`Image(*.${ext})`, `*.${ext}`]],
|
[[`Image(*.${ext})`, `*.${ext}`]],
|
||||||
`${Zotero.getString("fileTypes.image").toLowerCase()}.${ext}`,
|
`${Zotero.getString("fileTypes.image").toLowerCase()}.${ext}`,
|
||||||
addon.data.imageViewer.window,
|
addon.data.imageViewer.window,
|
||||||
"images"
|
"images",
|
||||||
).open();
|
).open();
|
||||||
if (filename) {
|
if (filename) {
|
||||||
await OS.File.writeAtomic(formatPath(filename), u8arr);
|
await OS.File.writeAtomic(formatPath(filename), u8arr);
|
||||||
|
|
@ -103,7 +103,7 @@ export async function showImageViewer(
|
||||||
"Show in Folder",
|
"Show in Folder",
|
||||||
(ev) => {
|
(ev) => {
|
||||||
Zotero.File.reveal(filename);
|
Zotero.File.reveal(filename);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -112,7 +112,7 @@ export async function showImageViewer(
|
||||||
? ICONS.imageViewerPined
|
? ICONS.imageViewerPined
|
||||||
: ICONS.imageViewerPin;
|
: ICONS.imageViewerPin;
|
||||||
addon.data.imageViewer.window.document.querySelector(
|
addon.data.imageViewer.window.document.querySelector(
|
||||||
"#pin-tooltip"
|
"#pin-tooltip",
|
||||||
)!.innerHTML = addon.data.imageViewer.pined ? "Unpin" : "Pin";
|
)!.innerHTML = addon.data.imageViewer.pined ? "Unpin" : "Pin";
|
||||||
addon.data.imageViewer.window.document
|
addon.data.imageViewer.window.document
|
||||||
.querySelector("#pin")
|
.querySelector("#pin")
|
||||||
|
|
@ -160,7 +160,7 @@ export async function showImageViewer(
|
||||||
if (e.ctrlKey) {
|
if (e.ctrlKey) {
|
||||||
setScale(
|
setScale(
|
||||||
addon.data.imageViewer.scaling *
|
addon.data.imageViewer.scaling *
|
||||||
Math.pow(delta > 0 ? 1.1 : 1 / 1.1, Math.round(Math.abs(delta)))
|
Math.pow(delta > 0 ? 1.1 : 1 / 1.1, Math.round(Math.abs(delta))),
|
||||||
);
|
);
|
||||||
} else if (e.shiftKey) {
|
} else if (e.shiftKey) {
|
||||||
container.scrollLeft -= delta * 10;
|
container.scrollLeft -= delta * 10;
|
||||||
|
|
@ -201,18 +201,18 @@ export async function showImageViewer(
|
||||||
function setImage() {
|
function setImage() {
|
||||||
(
|
(
|
||||||
addon.data.imageViewer.window?.document.querySelector(
|
addon.data.imageViewer.window?.document.querySelector(
|
||||||
"#image"
|
"#image",
|
||||||
) as HTMLImageElement
|
) as HTMLImageElement
|
||||||
).src = addon.data.imageViewer.srcList[addon.data.imageViewer.idx];
|
).src = addon.data.imageViewer.srcList[addon.data.imageViewer.idx];
|
||||||
setTitle();
|
setTitle();
|
||||||
(
|
(
|
||||||
addon.data.imageViewer.window?.document.querySelector(
|
addon.data.imageViewer.window?.document.querySelector(
|
||||||
"#left-container"
|
"#left-container",
|
||||||
) as HTMLButtonElement
|
) as HTMLButtonElement
|
||||||
).style.opacity = addon.data.imageViewer.idx === 0 ? "0.5" : "1";
|
).style.opacity = addon.data.imageViewer.idx === 0 ? "0.5" : "1";
|
||||||
(
|
(
|
||||||
addon.data.imageViewer.window?.document.querySelector(
|
addon.data.imageViewer.window?.document.querySelector(
|
||||||
"#right-container"
|
"#right-container",
|
||||||
) as HTMLButtonElement
|
) as HTMLButtonElement
|
||||||
).style.opacity =
|
).style.opacity =
|
||||||
addon.data.imageViewer.idx === addon.data.imageViewer.srcList.length - 1
|
addon.data.imageViewer.idx === addon.data.imageViewer.srcList.length - 1
|
||||||
|
|
@ -244,29 +244,29 @@ function setScale(scaling: number) {
|
||||||
addon.data.imageViewer.scaling = 0.1;
|
addon.data.imageViewer.scaling = 0.1;
|
||||||
}
|
}
|
||||||
const container = addon.data.imageViewer.window?.document.querySelector(
|
const container = addon.data.imageViewer.window?.document.querySelector(
|
||||||
".container"
|
".container",
|
||||||
) as HTMLDivElement;
|
) as HTMLDivElement;
|
||||||
(
|
(
|
||||||
addon.data.imageViewer.window?.document.querySelector(
|
addon.data.imageViewer.window?.document.querySelector(
|
||||||
"#image"
|
"#image",
|
||||||
) as HTMLImageElement
|
) as HTMLImageElement
|
||||||
).style.width = `calc(100% * ${addon.data.imageViewer.scaling})`;
|
).style.width = `calc(100% * ${addon.data.imageViewer.scaling})`;
|
||||||
if (addon.data.imageViewer.scaling > 1) {
|
if (addon.data.imageViewer.scaling > 1) {
|
||||||
container.scrollLeft +=
|
container.scrollLeft +=
|
||||||
addon.data.imageViewer.anchorPosition?.left! *
|
addon.data.imageViewer.anchorPosition!.left *
|
||||||
(addon.data.imageViewer.scaling - oldScale);
|
(addon.data.imageViewer.scaling - oldScale);
|
||||||
container.scrollTop +=
|
container.scrollTop +=
|
||||||
addon.data.imageViewer.anchorPosition?.top! *
|
addon.data.imageViewer.anchorPosition!.top *
|
||||||
(addon.data.imageViewer.scaling - oldScale);
|
(addon.data.imageViewer.scaling - oldScale);
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
addon.data.imageViewer.window?.document.querySelector(
|
addon.data.imageViewer.window?.document.querySelector(
|
||||||
"#bigger-container"
|
"#bigger-container",
|
||||||
) as HTMLButtonElement
|
) as HTMLButtonElement
|
||||||
).style.opacity = addon.data.imageViewer.scaling === 10 ? "0.5" : "1";
|
).style.opacity = addon.data.imageViewer.scaling === 10 ? "0.5" : "1";
|
||||||
(
|
(
|
||||||
addon.data.imageViewer.window?.document.querySelector(
|
addon.data.imageViewer.window?.document.querySelector(
|
||||||
"#smaller-container"
|
"#smaller-container",
|
||||||
) as HTMLButtonElement
|
) as HTMLButtonElement
|
||||||
).style.opacity = addon.data.imageViewer.scaling === 0.1 ? "0.5" : "1";
|
).style.opacity = addon.data.imageViewer.scaling === 0.1 ? "0.5" : "1";
|
||||||
// (
|
// (
|
||||||
|
|
@ -276,7 +276,7 @@ function setScale(scaling: number) {
|
||||||
|
|
||||||
function setTitle() {
|
function setTitle() {
|
||||||
addon.data.imageViewer.window!.document.querySelector(
|
addon.data.imageViewer.window!.document.querySelector(
|
||||||
"title"
|
"title",
|
||||||
)!.innerText! = `${addon.data.imageViewer.idx + 1}/${
|
)!.innerText! = `${addon.data.imageViewer.idx + 1}/${
|
||||||
addon.data.imageViewer.srcList.length
|
addon.data.imageViewer.srcList.length
|
||||||
}:${addon.data.imageViewer.title}`;
|
}:${addon.data.imageViewer.title}`;
|
||||||
|
|
@ -288,6 +288,6 @@ function setPin() {
|
||||||
showImageViewer(
|
showImageViewer(
|
||||||
addon.data.imageViewer.srcList,
|
addon.data.imageViewer.srcList,
|
||||||
addon.data.imageViewer.idx,
|
addon.data.imageViewer.idx,
|
||||||
addon.data.imageViewer.title
|
addon.data.imageViewer.title,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export async function fromMD(
|
||||||
ignoreVersion?: boolean;
|
ignoreVersion?: boolean;
|
||||||
append?: boolean;
|
append?: boolean;
|
||||||
appendLineIndex?: number;
|
appendLineIndex?: number;
|
||||||
} = {}
|
} = {},
|
||||||
) {
|
) {
|
||||||
let mdStatus: MDStatus;
|
let mdStatus: MDStatus;
|
||||||
try {
|
try {
|
||||||
|
|
@ -26,7 +26,7 @@ export async function fromMD(
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
!window.confirm(
|
!window.confirm(
|
||||||
`The target note seems to be newer than the file ${filepath}. Are you sure you want to import it anyway?`
|
`The target note seems to be newer than the file ${filepath}. Are you sure you want to import it anyway?`,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export function registerMenus() {
|
||||||
icon: `chrome://${config.addonRef}/content/icons/favicon.png`,
|
icon: `chrome://${config.addonRef}/content/icons/favicon.png`,
|
||||||
commandListener: (ev) => {
|
commandListener: (ev) => {
|
||||||
addon.hooks.onShowExportNoteOptions(
|
addon.hooks.onShowExportNoteOptions(
|
||||||
ZoteroPane.getSelectedItems().map((item) => item.id)
|
ZoteroPane.getSelectedItems().map((item) => item.id),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -36,7 +36,7 @@ export function registerMenus() {
|
||||||
|
|
||||||
// menuEdit
|
// menuEdit
|
||||||
const menuEditAnchor = document.querySelector(
|
const menuEditAnchor = document.querySelector(
|
||||||
"#menu_EditPreferencesItem"
|
"#menu_EditPreferencesItem",
|
||||||
) as XUL.MenuItem;
|
) as XUL.MenuItem;
|
||||||
ztoolkit.Menu.register(
|
ztoolkit.Menu.register(
|
||||||
"menuEdit",
|
"menuEdit",
|
||||||
|
|
@ -49,7 +49,7 @@ export function registerMenus() {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"before",
|
"before",
|
||||||
menuEditAnchor
|
menuEditAnchor,
|
||||||
);
|
);
|
||||||
ztoolkit.Menu.register(
|
ztoolkit.Menu.register(
|
||||||
"menuEdit",
|
"menuEdit",
|
||||||
|
|
@ -62,7 +62,7 @@ export function registerMenus() {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"before",
|
"before",
|
||||||
menuEditAnchor
|
menuEditAnchor,
|
||||||
);
|
);
|
||||||
ztoolkit.Menu.register(
|
ztoolkit.Menu.register(
|
||||||
"menuEdit",
|
"menuEdit",
|
||||||
|
|
@ -75,7 +75,7 @@ export function registerMenus() {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"before",
|
"before",
|
||||||
menuEditAnchor
|
menuEditAnchor,
|
||||||
);
|
);
|
||||||
ztoolkit.Menu.register(
|
ztoolkit.Menu.register(
|
||||||
"menuEdit",
|
"menuEdit",
|
||||||
|
|
@ -88,13 +88,13 @@ export function registerMenus() {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"before",
|
"before",
|
||||||
menuEditAnchor
|
menuEditAnchor,
|
||||||
);
|
);
|
||||||
ztoolkit.Menu.register(
|
ztoolkit.Menu.register(
|
||||||
"menuEdit",
|
"menuEdit",
|
||||||
{ tag: "menuseparator" },
|
{ tag: "menuseparator" },
|
||||||
"before",
|
"before",
|
||||||
menuEditAnchor
|
menuEditAnchor,
|
||||||
);
|
);
|
||||||
|
|
||||||
// menuTools
|
// menuTools
|
||||||
|
|
@ -110,7 +110,7 @@ export function registerMenus() {
|
||||||
|
|
||||||
// menuFile
|
// menuFile
|
||||||
const menuFileAnchor = document.querySelector(
|
const menuFileAnchor = document.querySelector(
|
||||||
"#menu_newCollection"
|
"#menu_newCollection",
|
||||||
) as XUL.MenuItem;
|
) as XUL.MenuItem;
|
||||||
const recentMainNotesMenuId = "zotero-recent-main-notes-menu";
|
const recentMainNotesMenuId = "zotero-recent-main-notes-menu";
|
||||||
const recentMainNotesMenuPopupId = "zotero-recent-main-notes-popup";
|
const recentMainNotesMenuPopupId = "zotero-recent-main-notes-popup";
|
||||||
|
|
@ -130,7 +130,7 @@ export function registerMenus() {
|
||||||
popupId: recentMainNotesMenuPopupId,
|
popupId: recentMainNotesMenuPopupId,
|
||||||
},
|
},
|
||||||
"after",
|
"after",
|
||||||
menuFileAnchor
|
menuFileAnchor,
|
||||||
);
|
);
|
||||||
|
|
||||||
document
|
document
|
||||||
|
|
@ -142,7 +142,7 @@ export function registerMenus() {
|
||||||
((getPref("recentMainNoteIds") as string) || "")
|
((getPref("recentMainNoteIds") as string) || "")
|
||||||
.split(",")
|
.split(",")
|
||||||
.map((id) => parseInt(id))
|
.map((id) => parseInt(id))
|
||||||
.filter((id) => id !== addon.data.workspace.mainId)
|
.filter((id) => id !== addon.data.workspace.mainId),
|
||||||
)
|
)
|
||||||
.filter((item) => item.isNote())
|
.filter((item) => item.isNote())
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
|
|
@ -155,11 +155,11 @@ export function registerMenus() {
|
||||||
: "📁" +
|
: "📁" +
|
||||||
Zotero.Collections.get(item.getCollections())
|
Zotero.Collections.get(item.getCollections())
|
||||||
.map(
|
.map(
|
||||||
(collection) => (collection as Zotero.Collection).name
|
(collection) => (collection as Zotero.Collection).name,
|
||||||
)
|
)
|
||||||
.join(", ")
|
.join(", ")
|
||||||
}`,
|
}`,
|
||||||
200
|
200,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
listeners: [
|
listeners: [
|
||||||
|
|
@ -188,7 +188,7 @@ export function registerMenus() {
|
||||||
children: children.length === 0 ? defaultChildren : children,
|
children: children.length === 0 ? defaultChildren : children,
|
||||||
enableElementRecord: false,
|
enableElementRecord: false,
|
||||||
},
|
},
|
||||||
popup
|
popup,
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
@ -213,13 +213,13 @@ export function registerMenus() {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"after",
|
"after",
|
||||||
menuFileAnchor
|
menuFileAnchor,
|
||||||
);
|
);
|
||||||
ztoolkit.Menu.register(
|
ztoolkit.Menu.register(
|
||||||
"menuFile",
|
"menuFile",
|
||||||
{ tag: "menuseparator" },
|
{ tag: "menuseparator" },
|
||||||
"after",
|
"after",
|
||||||
menuFileAnchor
|
menuFileAnchor,
|
||||||
);
|
);
|
||||||
// a copy of create note menu in library
|
// a copy of create note menu in library
|
||||||
ztoolkit.Menu.register(
|
ztoolkit.Menu.register(
|
||||||
|
|
@ -231,7 +231,7 @@ export function registerMenus() {
|
||||||
commandListener: () => addon.hooks.onCreateNoteFromMD(),
|
commandListener: () => addon.hooks.onCreateNoteFromMD(),
|
||||||
},
|
},
|
||||||
"after",
|
"after",
|
||||||
menuFileAnchor
|
menuFileAnchor,
|
||||||
);
|
);
|
||||||
ztoolkit.Menu.register(
|
ztoolkit.Menu.register(
|
||||||
"menuFile",
|
"menuFile",
|
||||||
|
|
@ -243,7 +243,7 @@ export function registerMenus() {
|
||||||
addon.hooks.onCreateNoteFromTemplate("item", "library"),
|
addon.hooks.onCreateNoteFromTemplate("item", "library"),
|
||||||
},
|
},
|
||||||
"after",
|
"after",
|
||||||
menuFileAnchor
|
menuFileAnchor,
|
||||||
);
|
);
|
||||||
ztoolkit.Menu.register(
|
ztoolkit.Menu.register(
|
||||||
"menuFile",
|
"menuFile",
|
||||||
|
|
@ -254,7 +254,7 @@ export function registerMenus() {
|
||||||
commandListener: () => addon.hooks.onCreateNoteFromTemplate("standalone"),
|
commandListener: () => addon.hooks.onCreateNoteFromTemplate("standalone"),
|
||||||
},
|
},
|
||||||
"after",
|
"after",
|
||||||
menuFileAnchor
|
menuFileAnchor,
|
||||||
);
|
);
|
||||||
ztoolkit.Menu.register(
|
ztoolkit.Menu.register(
|
||||||
"menuFile",
|
"menuFile",
|
||||||
|
|
@ -265,7 +265,7 @@ export function registerMenus() {
|
||||||
commandListener: addon.hooks.onCreateWorkspaceNote,
|
commandListener: addon.hooks.onCreateWorkspaceNote,
|
||||||
},
|
},
|
||||||
"after",
|
"after",
|
||||||
menuFileAnchor
|
menuFileAnchor,
|
||||||
);
|
);
|
||||||
|
|
||||||
// create note menu in library
|
// create note menu in library
|
||||||
|
|
@ -301,7 +301,7 @@ export function registerMenus() {
|
||||||
// create note menu in reader side panel
|
// create note menu in reader side panel
|
||||||
ztoolkit.Menu.register(
|
ztoolkit.Menu.register(
|
||||||
document.querySelector(
|
document.querySelector(
|
||||||
"#context-pane-add-child-note-button-popup"
|
"#context-pane-add-child-note-button-popup",
|
||||||
) as XUL.MenuPopup,
|
) as XUL.MenuPopup,
|
||||||
{
|
{
|
||||||
tag: "menuitem",
|
tag: "menuitem",
|
||||||
|
|
@ -309,6 +309,6 @@ export function registerMenus() {
|
||||||
icon: `chrome://${config.addonRef}/content/icons/favicon.png`,
|
icon: `chrome://${config.addonRef}/content/icons/favicon.png`,
|
||||||
commandListener: () =>
|
commandListener: () =>
|
||||||
addon.hooks.onCreateNoteFromTemplate("item", "reader"),
|
addon.hooks.onCreateNoteFromTemplate("item", "reader"),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export function registerNotify(types: _ZoteroTypes.Notifier.Type[]) {
|
||||||
(e: Event) => {
|
(e: Event) => {
|
||||||
unregisterNotify(notifyID);
|
unregisterNotify(notifyID);
|
||||||
},
|
},
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,11 @@ async function updatePrefsUI() {
|
||||||
// You can initialize some UI elements on prefs window
|
// You can initialize some UI elements on prefs window
|
||||||
// with addon.data.prefs.window.document
|
// with addon.data.prefs.window.document
|
||||||
// Or bind some events to the elements
|
// Or bind some events to the elements
|
||||||
|
if (!addon.data.prefs?.window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const renderLock = ztoolkit.getGlobal("Zotero").Promise.defer();
|
const renderLock = ztoolkit.getGlobal("Zotero").Promise.defer();
|
||||||
const tableHelper = new ztoolkit.VirtualizedTable(addon.data.prefs?.window!)
|
const tableHelper = new ztoolkit.VirtualizedTable(addon.data.prefs.window)
|
||||||
.setContainerId(`${config.addonRef}-table-container`)
|
.setContainerId(`${config.addonRef}-table-container`)
|
||||||
.setProp({
|
.setProp({
|
||||||
id: `${config.addonRef}-prefs-table`,
|
id: `${config.addonRef}-prefs-table`,
|
||||||
|
|
@ -66,7 +69,7 @@ async function updatePrefsUI() {
|
||||||
columns: addon.data.prefs?.columns.map((column) =>
|
columns: addon.data.prefs?.columns.map((column) =>
|
||||||
Object.assign(column, {
|
Object.assign(column, {
|
||||||
label: getString(column.label) || column.label,
|
label: getString(column.label) || column.label,
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
multiSelect: true,
|
multiSelect: true,
|
||||||
|
|
@ -80,7 +83,7 @@ async function updatePrefsUI() {
|
||||||
addon.data.prefs?.rows[index] || {
|
addon.data.prefs?.rows[index] || {
|
||||||
title: "no data",
|
title: "no data",
|
||||||
detail: "no data",
|
detail: "no data",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
// Show a progress window when selection changes
|
// Show a progress window when selection changes
|
||||||
.setProp("onSelectionChange", (selection) => {
|
.setProp("onSelectionChange", (selection) => {
|
||||||
|
|
@ -100,7 +103,7 @@ async function updatePrefsUI() {
|
||||||
if (event.key == "Delete" || (Zotero.isMac && event.key == "Backspace")) {
|
if (event.key == "Delete" || (Zotero.isMac && event.key == "Backspace")) {
|
||||||
addon.data.prefs!.rows =
|
addon.data.prefs!.rows =
|
||||||
addon.data.prefs?.rows.filter(
|
addon.data.prefs?.rows.filter(
|
||||||
(v, i) => !tableHelper.treeInstance.selection.isSelected(i)
|
(v, i) => !tableHelper.treeInstance.selection.isSelected(i),
|
||||||
) || [];
|
) || [];
|
||||||
tableHelper.render();
|
tableHelper.render();
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -110,7 +113,7 @@ async function updatePrefsUI() {
|
||||||
// For find-as-you-type
|
// For find-as-you-type
|
||||||
.setProp(
|
.setProp(
|
||||||
"getRowString",
|
"getRowString",
|
||||||
(index) => addon.data.prefs?.rows[index].title || ""
|
(index) => addon.data.prefs?.rows[index].title || "",
|
||||||
)
|
)
|
||||||
// Render the table.
|
// Render the table.
|
||||||
.render(-1, () => {
|
.render(-1, () => {
|
||||||
|
|
@ -123,23 +126,23 @@ async function updatePrefsUI() {
|
||||||
function bindPrefEvents() {
|
function bindPrefEvents() {
|
||||||
addon.data
|
addon.data
|
||||||
.prefs!.window.document.querySelector(
|
.prefs!.window.document.querySelector(
|
||||||
`#zotero-prefpane-${config.addonRef}-enable`
|
`#zotero-prefpane-${config.addonRef}-enable`,
|
||||||
)
|
)
|
||||||
?.addEventListener("command", (e) => {
|
?.addEventListener("command", (e) => {
|
||||||
ztoolkit.log(e);
|
ztoolkit.log(e);
|
||||||
addon.data.prefs!.window.alert(
|
addon.data.prefs!.window.alert(
|
||||||
`Successfully changed to ${(e.target as XUL.Checkbox).checked}!`
|
`Successfully changed to ${(e.target as XUL.Checkbox).checked}!`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
addon.data
|
addon.data
|
||||||
.prefs!!.window.document.querySelector(
|
.prefs!.window.document.querySelector(
|
||||||
`#zotero-prefpane-${config.addonRef}-input`
|
`#zotero-prefpane-${config.addonRef}-input`,
|
||||||
)
|
)
|
||||||
?.addEventListener("change", (e) => {
|
?.addEventListener("change", (e) => {
|
||||||
ztoolkit.log(e);
|
ztoolkit.log(e);
|
||||||
addon.data.prefs!.window.alert(
|
addon.data.prefs!.window.alert(
|
||||||
`Successfully changed to ${(e.target as HTMLInputElement).value}!`
|
`Successfully changed to ${(e.target as HTMLInputElement).value}!`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export function registerReaderInitializer() {
|
||||||
ztoolkit.ReaderInstance.register(
|
ztoolkit.ReaderInstance.register(
|
||||||
"initialized",
|
"initialized",
|
||||||
`${config.addonRef}-annotationButtons`,
|
`${config.addonRef}-annotationButtons`,
|
||||||
initializeReaderAnnotationButton
|
initializeReaderAnnotationButton,
|
||||||
);
|
);
|
||||||
// Force re-initialize
|
// Force re-initialize
|
||||||
Zotero.Reader._readers.forEach((r) => {
|
Zotero.Reader._readers.forEach((r) => {
|
||||||
|
|
@ -38,7 +38,7 @@ export async function checkReaderAnnotationButton(items: Zotero.Item[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initializeReaderAnnotationButton(
|
async function initializeReaderAnnotationButton(
|
||||||
instance: _ZoteroTypes.ReaderInstance
|
instance: _ZoteroTypes.ReaderInstance,
|
||||||
): Promise<Zotero.Item[]> {
|
): Promise<Zotero.Item[]> {
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
return [];
|
return [];
|
||||||
|
|
@ -68,7 +68,7 @@ async function initializeReaderAnnotationButton(
|
||||||
const libraryID = Zotero.Items.get(instance.itemID).libraryID;
|
const libraryID = Zotero.Items.get(instance.itemID).libraryID;
|
||||||
const annotationItem = (await Zotero.Items.getByLibraryAndKeyAsync(
|
const annotationItem = (await Zotero.Items.getByLibraryAndKeyAsync(
|
||||||
libraryID,
|
libraryID,
|
||||||
itemKey
|
itemKey,
|
||||||
)) as Zotero.Item;
|
)) as Zotero.Item;
|
||||||
|
|
||||||
if (!annotationItem) {
|
if (!annotationItem) {
|
||||||
|
|
@ -90,7 +90,7 @@ async function initializeReaderAnnotationButton(
|
||||||
listener: (e) => {
|
listener: (e) => {
|
||||||
createNoteFromAnnotation(
|
createNoteFromAnnotation(
|
||||||
annotationItem,
|
annotationItem,
|
||||||
(e as MouseEvent).shiftKey ? "standalone" : "auto"
|
(e as MouseEvent).shiftKey ? "standalone" : "auto",
|
||||||
);
|
);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
},
|
},
|
||||||
|
|
@ -105,7 +105,7 @@ async function initializeReaderAnnotationButton(
|
||||||
type: "mouseout",
|
type: "mouseout",
|
||||||
listener: (e) => {
|
listener: (e) => {
|
||||||
(e.target as HTMLElement).style.removeProperty(
|
(e.target as HTMLElement).style.removeProperty(
|
||||||
"background-color"
|
"background-color",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -139,7 +139,7 @@ async function initializeReaderAnnotationButton(
|
||||||
type: "mouseout",
|
type: "mouseout",
|
||||||
listener: (e) => {
|
listener: (e) => {
|
||||||
(e.target as HTMLElement).style.removeProperty(
|
(e.target as HTMLElement).style.removeProperty(
|
||||||
"background-color"
|
"background-color",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -153,14 +153,14 @@ async function initializeReaderAnnotationButton(
|
||||||
tag: "fragment",
|
tag: "fragment",
|
||||||
children: annotationButtons,
|
children: annotationButtons,
|
||||||
},
|
},
|
||||||
moreButton
|
moreButton,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return hitItems;
|
return hitItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function unInitializeReaderAnnotationButton(
|
async function unInitializeReaderAnnotationButton(
|
||||||
instance: _ZoteroTypes.ReaderInstance
|
instance: _ZoteroTypes.ReaderInstance,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -180,7 +180,7 @@ async function unInitializeReaderAnnotationButton(
|
||||||
|
|
||||||
async function createNoteFromAnnotation(
|
async function createNoteFromAnnotation(
|
||||||
annotationItem: Zotero.Item,
|
annotationItem: Zotero.Item,
|
||||||
openMode: "standalone" | "auto" = "auto"
|
openMode: "standalone" | "auto" = "auto",
|
||||||
) {
|
) {
|
||||||
const annotationTags = annotationItem.getTags().map((_) => _.tag);
|
const annotationTags = annotationItem.getTags().map((_) => _.tag);
|
||||||
const linkRegex = new RegExp("^zotero://note/(.*)$");
|
const linkRegex = new RegExp("^zotero://note/(.*)$");
|
||||||
|
|
@ -209,7 +209,7 @@ async function createNoteFromAnnotation(
|
||||||
const renderredTemplate = await addon.api.template.runTemplate(
|
const renderredTemplate = await addon.api.template.runTemplate(
|
||||||
"[QuickNoteV5]",
|
"[QuickNoteV5]",
|
||||||
"annotationItem, topItem, noteItem",
|
"annotationItem, topItem, noteItem",
|
||||||
[annotationItem, annotationItem.parentItem!.parentItem, note]
|
[annotationItem, annotationItem.parentItem!.parentItem, note],
|
||||||
);
|
);
|
||||||
await addLineToNote(note, renderredTemplate);
|
await addLineToNote(note, renderredTemplate);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ async function getRelatedNoteIds(noteId: number): Promise<number[]> {
|
||||||
}
|
}
|
||||||
const subNoteIds = (
|
const subNoteIds = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
linkMatches.map(async (link) => getNoteLinkParams(link).noteItem)
|
linkMatches.map(async (link) => getNoteLinkParams(link).noteItem),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.filter((item) => item && item.isNote())
|
.filter((item) => item && item.isNote())
|
||||||
|
|
@ -49,7 +49,7 @@ async function getRelatedNoteIds(noteId: number): Promise<number[]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getRelatedNoteIdsFromNotes(
|
async function getRelatedNoteIdsFromNotes(
|
||||||
noteIds: number[]
|
noteIds: number[],
|
||||||
): Promise<number[]> {
|
): Promise<number[]> {
|
||||||
let allNoteIds: number[] = [];
|
let allNoteIds: number[] = [];
|
||||||
for (const noteId of noteIds) {
|
for (const noteId of noteIds) {
|
||||||
|
|
@ -101,7 +101,7 @@ function getNoteStatus(noteId: number) {
|
||||||
if (idx != -1) {
|
if (idx != -1) {
|
||||||
ret.content = fullContent.substring(
|
ret.content = fullContent.substring(
|
||||||
idx + match[0].length,
|
idx + match[0].length,
|
||||||
fullContent.length - ret.tail.length
|
fullContent.length - ret.tail.length,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -117,7 +117,7 @@ function getSyncStatus(noteId?: number): SyncStatus {
|
||||||
itemID: -1,
|
itemID: -1,
|
||||||
});
|
});
|
||||||
return JSON.parse(
|
return JSON.parse(
|
||||||
(getPref(`syncDetail-${noteId}`) as string) || defaultStatus
|
(getPref(`syncDetail-${noteId}`) as string) || defaultStatus,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +143,7 @@ function getMDStatusFromContent(contentRaw: string): MDStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMDStatus(
|
async function getMDStatus(
|
||||||
source: Zotero.Item | number | string
|
source: Zotero.Item | number | string,
|
||||||
): Promise<MDStatus> {
|
): Promise<MDStatus> {
|
||||||
let ret: MDStatus = {
|
let ret: MDStatus = {
|
||||||
meta: null,
|
meta: null,
|
||||||
|
|
@ -165,13 +165,13 @@ async function getMDStatus(
|
||||||
}
|
}
|
||||||
filepath = Zotero.File.normalizeToUnix(filepath);
|
filepath = Zotero.File.normalizeToUnix(filepath);
|
||||||
if (await OS.File.exists(filepath)) {
|
if (await OS.File.exists(filepath)) {
|
||||||
let contentRaw = (await OS.File.read(filepath, {
|
const contentRaw = (await OS.File.read(filepath, {
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
})) as string;
|
})) as string;
|
||||||
ret = getMDStatusFromContent(contentRaw);
|
ret = getMDStatusFromContent(contentRaw);
|
||||||
const pathSplit = filepath.split("/");
|
const pathSplit = filepath.split("/");
|
||||||
ret.filedir = Zotero.File.normalizeToUnix(
|
ret.filedir = Zotero.File.normalizeToUnix(
|
||||||
pathSplit.slice(0, -1).join("/")
|
pathSplit.slice(0, -1).join("/"),
|
||||||
);
|
);
|
||||||
ret.filename = filepath.split("/").pop() || "";
|
ret.filename = filepath.split("/").pop() || "";
|
||||||
const stat = await OS.File.stat(filepath);
|
const stat = await OS.File.stat(filepath);
|
||||||
|
|
@ -214,7 +214,7 @@ async function getMDFileName(noteId: number, searchDir?: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
if (matchedFileName) {
|
if (matchedFileName) {
|
||||||
return matchedFileName;
|
return matchedFileName;
|
||||||
|
|
@ -224,6 +224,6 @@ async function getMDFileName(noteId: number, searchDir?: string) {
|
||||||
return await addon.api.template.runTemplate(
|
return await addon.api.template.runTemplate(
|
||||||
"[ExportMDFileNameV2]",
|
"[ExportMDFileNameV2]",
|
||||||
"noteItem",
|
"noteItem",
|
||||||
[noteItem]
|
[noteItem],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ export async function showSyncDiff(noteId: number, mdPath: string) {
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, merge manually
|
// Otherwise, merge manually
|
||||||
const imageAttachemnts = Zotero.Items.get(noteItem.getAttachments()).filter(
|
const imageAttachemnts = Zotero.Items.get(noteItem.getAttachments()).filter(
|
||||||
(attch) => attch.isEmbeddedImageAttachment()
|
(attch) => attch.isEmbeddedImageAttachment(),
|
||||||
);
|
);
|
||||||
const imageData = {} as Record<string, string>;
|
const imageData = {} as Record<string, string>;
|
||||||
for (const image of imageAttachemnts) {
|
for (const image of imageAttachemnts) {
|
||||||
|
|
@ -66,10 +66,10 @@ export async function showSyncDiff(noteId: number, mdPath: string) {
|
||||||
addon.data.sync.diff.window = window.open(
|
addon.data.sync.diff.window = window.open(
|
||||||
`chrome://${config.addonRef}/content/syncDiff.html`,
|
`chrome://${config.addonRef}/content/syncDiff.html`,
|
||||||
`${config.addonRef}-syncDiff`,
|
`${config.addonRef}-syncDiff`,
|
||||||
`chrome,centerscreen,resizable,status,width=900,height=550`
|
`chrome,centerscreen,resizable,status,width=900,height=550`,
|
||||||
)!;
|
)!;
|
||||||
await waitUtilAsync(
|
await waitUtilAsync(
|
||||||
() => addon.data.sync.diff.window?.document.readyState === "complete"
|
() => addon.data.sync.diff.window?.document.readyState === "complete",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const win = addon.data.sync.diff.window as any;
|
const win = addon.data.sync.diff.window as any;
|
||||||
|
|
@ -86,7 +86,7 @@ export async function showSyncDiff(noteId: number, mdPath: string) {
|
||||||
Object.assign(change, {
|
Object.assign(change, {
|
||||||
id: id,
|
id: id,
|
||||||
text: change.value,
|
text: change.value,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
win.imageData = imageData;
|
win.imageData = imageData;
|
||||||
|
|
||||||
|
|
@ -115,7 +115,7 @@ export async function showSyncDiff(noteId: number, mdPath: string) {
|
||||||
switch (io.type) {
|
switch (io.type) {
|
||||||
case "skip":
|
case "skip":
|
||||||
alert(
|
alert(
|
||||||
`Syncing of "${noteItem.getNoteTitle()}" is skipped.\nTo sync manually, go to File->Better Notes Sync Manager.`
|
`Syncing of "${noteItem.getNoteTitle()}" is skipped.\nTo sync manually, go to File->Better Notes Sync Manager.`,
|
||||||
);
|
);
|
||||||
addon.data.sync.diff.window?.closed ||
|
addon.data.sync.diff.window?.closed ||
|
||||||
addon.data.sync.diff.window?.close();
|
addon.data.sync.diff.window?.close();
|
||||||
|
|
|
||||||
|
|
@ -8,20 +8,26 @@ function setSyncing() {
|
||||||
const syncPeriod = getPref("syncPeriodSeconds") as number;
|
const syncPeriod = getPref("syncPeriodSeconds") as number;
|
||||||
if (syncPeriod > 0) {
|
if (syncPeriod > 0) {
|
||||||
showHint(`${getString("sync.start.hint")} ${syncPeriod} s`);
|
showHint(`${getString("sync.start.hint")} ${syncPeriod} s`);
|
||||||
const timer = ztoolkit.getGlobal("setInterval")(() => {
|
const timer = ztoolkit.getGlobal("setInterval")(
|
||||||
if (!addon.data.alive) {
|
() => {
|
||||||
showHint(getString("sync.stop.hint"));
|
if (!addon.data.alive) {
|
||||||
ztoolkit.getGlobal("clearInterval")(timer);
|
showHint(getString("sync.stop.hint"));
|
||||||
}
|
ztoolkit.getGlobal("clearInterval")(timer);
|
||||||
// Only when Zotero is active and focused
|
}
|
||||||
if (document.hasFocus() && (getPref("syncPeriodSeconds") as number) > 0) {
|
// Only when Zotero is active and focused
|
||||||
callSyncing(undefined, {
|
if (
|
||||||
quiet: true,
|
document.hasFocus() &&
|
||||||
skipActive: true,
|
(getPref("syncPeriodSeconds") as number) > 0
|
||||||
reason: "auto",
|
) {
|
||||||
});
|
callSyncing(undefined, {
|
||||||
}
|
quiet: true,
|
||||||
}, Number(syncPeriod) * 1000);
|
skipActive: true,
|
||||||
|
reason: "auto",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Number(syncPeriod) * 1000,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,7 +37,7 @@ async function callSyncing(
|
||||||
quiet: true,
|
quiet: true,
|
||||||
skipActive: true,
|
skipActive: true,
|
||||||
reason: "unknown",
|
reason: "unknown",
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
// Always log in development mode
|
// Always log in development mode
|
||||||
if (addon.data.env === "development") {
|
if (addon.data.env === "development") {
|
||||||
|
|
@ -61,11 +67,11 @@ async function callSyncing(
|
||||||
.filter(
|
.filter(
|
||||||
(editor) =>
|
(editor) =>
|
||||||
!Components.utils.isDeadWrapper(editor._iframeWindow) &&
|
!Components.utils.isDeadWrapper(editor._iframeWindow) &&
|
||||||
editor._iframeWindow.document.hasFocus()
|
editor._iframeWindow.document.hasFocus(),
|
||||||
)
|
)
|
||||||
.map((editor) => editor._item.id);
|
.map((editor) => editor._item.id);
|
||||||
const filteredItems = items.filter(
|
const filteredItems = items.filter(
|
||||||
(item) => !activeNoteIds.includes(item.id)
|
(item) => !activeNoteIds.includes(item.id),
|
||||||
);
|
);
|
||||||
skippedCount = items.length - filteredItems.length;
|
skippedCount = items.length - filteredItems.length;
|
||||||
items = filteredItems;
|
items = filteredItems;
|
||||||
|
|
@ -76,7 +82,7 @@ async function callSyncing(
|
||||||
progress = new ztoolkit.ProgressWindow(
|
progress = new ztoolkit.ProgressWindow(
|
||||||
`[${getString("sync.running.hint.title")}] ${
|
`[${getString("sync.running.hint.title")}] ${
|
||||||
addon.data.env === "development" ? reason : "Better Notes"
|
addon.data.env === "development" ? reason : "Better Notes"
|
||||||
}`
|
}`,
|
||||||
)
|
)
|
||||||
.createLine({
|
.createLine({
|
||||||
text: `[${getString("sync.running.hint.check")}] 0/${
|
text: `[${getString("sync.running.hint.check")}] 0/${
|
||||||
|
|
@ -95,7 +101,7 @@ async function callSyncing(
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const syncStatus = addon.api.sync.getSyncStatus(item.id);
|
const syncStatus = addon.api.sync.getSyncStatus(item.id);
|
||||||
const filepath = syncStatus.path;
|
const filepath = syncStatus.path;
|
||||||
let compareResult = await doCompare(item);
|
const compareResult = await doCompare(item);
|
||||||
switch (compareResult) {
|
switch (compareResult) {
|
||||||
case SyncCode.NoteAhead:
|
case SyncCode.NoteAhead:
|
||||||
if (Object.keys(toExport).includes(filepath)) {
|
if (Object.keys(toExport).includes(filepath)) {
|
||||||
|
|
@ -139,7 +145,7 @@ async function callSyncing(
|
||||||
for (const syncStatus of toImport) {
|
for (const syncStatus of toImport) {
|
||||||
progress?.changeLine({
|
progress?.changeLine({
|
||||||
text: `[${getString(
|
text: `[${getString(
|
||||||
"sync.running.hint.updateNote"
|
"sync.running.hint.updateNote",
|
||||||
)}] ${i}/${totalCount}, ${toDiff.length} queuing...`,
|
)}] ${i}/${totalCount}, ${toDiff.length} queuing...`,
|
||||||
progress: ((i - 1) / totalCount) * 100,
|
progress: ((i - 1) / totalCount) * 100,
|
||||||
});
|
});
|
||||||
|
|
@ -160,7 +166,7 @@ async function callSyncing(
|
||||||
|
|
||||||
await addon.hooks.onShowSyncDiff(
|
await addon.hooks.onShowSyncDiff(
|
||||||
syncStatus.itemID,
|
syncStatus.itemID,
|
||||||
OS.Path.join(syncStatus.path, syncStatus.filename)
|
OS.Path.join(syncStatus.path, syncStatus.filename),
|
||||||
);
|
);
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
@ -170,10 +176,10 @@ async function callSyncing(
|
||||||
text:
|
text:
|
||||||
(syncCount
|
(syncCount
|
||||||
? `[${getString(
|
? `[${getString(
|
||||||
"sync.running.hint.finish"
|
"sync.running.hint.finish",
|
||||||
)}] ${syncCount} ${getString("sync.running.hint.synced")}`
|
)}] ${syncCount} ${getString("sync.running.hint.synced")}`
|
||||||
: `[${getString("sync.running.hint.finish")}] ${getString(
|
: `[${getString("sync.running.hint.finish")}] ${getString(
|
||||||
"sync.running.hint.upToDate"
|
"sync.running.hint.upToDate",
|
||||||
)}`) + (skippedCount ? `, ${skippedCount} skipped.` : ""),
|
)}`) + (skippedCount ? `, ${skippedCount} skipped.` : ""),
|
||||||
progress: 100,
|
progress: 100,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export async function showSyncInfo(noteId: number) {
|
||||||
tag: "label",
|
tag: "label",
|
||||||
properties: {
|
properties: {
|
||||||
innerHTML: formatPath(
|
innerHTML: formatPath(
|
||||||
OS.Path.join(slice(status.path, 30), status.filename)
|
OS.Path.join(slice(status.path, 30), status.filename),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
@ -57,7 +57,7 @@ export async function showSyncInfo(noteId: number) {
|
||||||
noClose: true,
|
noClose: true,
|
||||||
callback: (ev) => {
|
callback: (ev) => {
|
||||||
Zotero.File.reveal(
|
Zotero.File.reveal(
|
||||||
formatPath(OS.Path.join(status.path, status.filename))
|
formatPath(OS.Path.join(status.path, status.filename)),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export async function showSyncManager() {
|
||||||
`chrome://${config.addonRef}/content/syncManager.xhtml`,
|
`chrome://${config.addonRef}/content/syncManager.xhtml`,
|
||||||
`${config.addonRef}-syncManager`,
|
`${config.addonRef}-syncManager`,
|
||||||
`chrome,centerscreen,resizable,status,width=800,height=400,dialog=no`,
|
`chrome,centerscreen,resizable,status,width=800,height=400,dialog=no`,
|
||||||
windowArgs
|
windowArgs,
|
||||||
)!;
|
)!;
|
||||||
await windowArgs._initPromise.promise;
|
await windowArgs._initPromise.promise;
|
||||||
addon.data.sync.manager.window = win;
|
addon.data.sync.manager.window = win;
|
||||||
|
|
@ -46,7 +46,7 @@ export async function showSyncManager() {
|
||||||
].map((column) =>
|
].map((column) =>
|
||||||
Object.assign(column, {
|
Object.assign(column, {
|
||||||
label: getString(column.label),
|
label: getString(column.label),
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
multiSelect: true,
|
multiSelect: true,
|
||||||
|
|
@ -65,7 +65,7 @@ export async function showSyncManager() {
|
||||||
noteName: "no data",
|
noteName: "no data",
|
||||||
lastSync: "no data",
|
lastSync: "no data",
|
||||||
filePath: "no data",
|
filePath: "no data",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
.setProp("onSelectionChange", (selection) => {
|
.setProp("onSelectionChange", (selection) => {
|
||||||
updateButtons();
|
updateButtons();
|
||||||
|
|
@ -84,21 +84,21 @@ export async function showSyncManager() {
|
||||||
.setProp("onActivate", (ev) => {
|
.setProp("onActivate", (ev) => {
|
||||||
const noteIds = getSelectedNoteIds();
|
const noteIds = getSelectedNoteIds();
|
||||||
noteIds.forEach((noteId) =>
|
noteIds.forEach((noteId) =>
|
||||||
addon.hooks.onOpenNote(noteId, "standalone")
|
addon.hooks.onOpenNote(noteId, "standalone"),
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.setProp(
|
.setProp(
|
||||||
"getRowString",
|
"getRowString",
|
||||||
(index) => addon.data.prefs?.rows[index].title || ""
|
(index) => addon.data.prefs?.rows[index].title || "",
|
||||||
)
|
)
|
||||||
.render();
|
.render();
|
||||||
const refreshButton = win.document.querySelector(
|
const refreshButton = win.document.querySelector(
|
||||||
"#refresh"
|
"#refresh",
|
||||||
) as HTMLButtonElement;
|
) as HTMLButtonElement;
|
||||||
const syncButton = win.document.querySelector("#sync") as HTMLButtonElement;
|
const syncButton = win.document.querySelector("#sync") as HTMLButtonElement;
|
||||||
const unSyncButton = win.document.querySelector(
|
const unSyncButton = win.document.querySelector(
|
||||||
"#unSync"
|
"#unSync",
|
||||||
) as HTMLButtonElement;
|
) as HTMLButtonElement;
|
||||||
refreshButton.addEventListener("click", (ev) => {
|
refreshButton.addEventListener("click", (ev) => {
|
||||||
refresh();
|
refresh();
|
||||||
|
|
@ -148,7 +148,7 @@ function updateButtons() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const unSyncButton = win.document.querySelector(
|
const unSyncButton = win.document.querySelector(
|
||||||
"#unSync"
|
"#unSync",
|
||||||
) as HTMLButtonElement;
|
) as HTMLButtonElement;
|
||||||
if (
|
if (
|
||||||
addon.data.sync.manager.tableHelper?.treeInstance.selection.selected.size
|
addon.data.sync.manager.tableHelper?.treeInstance.selection.selected.size
|
||||||
|
|
@ -179,13 +179,13 @@ async function unSyncNotes(itemIds: number[]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const unSyncLinkedNotes = addon.data.sync.manager.window?.confirm(
|
const unSyncLinkedNotes = addon.data.sync.manager.window?.confirm(
|
||||||
`Un-sync their linked notes?`
|
`Un-sync their linked notes?`,
|
||||||
);
|
);
|
||||||
if (unSyncLinkedNotes) {
|
if (unSyncLinkedNotes) {
|
||||||
for (const item of Zotero.Items.get(itemIds)) {
|
for (const item of Zotero.Items.get(itemIds)) {
|
||||||
let linkedIds: number[] = getLinkedNotesRecursively(
|
const linkedIds: number[] = getLinkedNotesRecursively(
|
||||||
getNoteLink(item) || "",
|
getNoteLink(item) || "",
|
||||||
itemIds
|
itemIds,
|
||||||
);
|
);
|
||||||
itemIds.push(...linkedIds);
|
itemIds.push(...linkedIds);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ async function runTemplate(
|
||||||
useDefault: true,
|
useDefault: true,
|
||||||
dryRun: false,
|
dryRun: false,
|
||||||
stage: "default",
|
stage: "default",
|
||||||
}
|
},
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
ztoolkit.log(`runTemplate: ${key}`);
|
ztoolkit.log(`runTemplate: ${key}`);
|
||||||
if (argList.length > 0) {
|
if (argList.length > 0) {
|
||||||
|
|
@ -64,8 +64,8 @@ async function runTemplate(
|
||||||
// Check the markdown pragma
|
// Check the markdown pragma
|
||||||
templateLines = templateLines.slice(startIndex + 1, endIndex);
|
templateLines = templateLines.slice(startIndex + 1, endIndex);
|
||||||
let useMarkdown = false;
|
let useMarkdown = false;
|
||||||
let mdIndex = templateLines.findIndex((line) =>
|
const mdIndex = templateLines.findIndex((line) =>
|
||||||
line.startsWith("// @use-markdown")
|
line.startsWith("// @use-markdown"),
|
||||||
);
|
);
|
||||||
if (mdIndex >= 0) {
|
if (mdIndex >= 0) {
|
||||||
useMarkdown = true;
|
useMarkdown = true;
|
||||||
|
|
@ -87,7 +87,7 @@ async function runTemplate(
|
||||||
/\$\{\{([\s\S]*?)\}\}\$/g,
|
/\$\{\{([\s\S]*?)\}\}\$/g,
|
||||||
(match, content) => {
|
(match, content) => {
|
||||||
return constructFunction(content);
|
return constructFunction(content);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -110,9 +110,9 @@ async function runTextTemplate(
|
||||||
options: {
|
options: {
|
||||||
targetNoteId?: number;
|
targetNoteId?: number;
|
||||||
dryRun?: boolean;
|
dryRun?: boolean;
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
let { targetNoteId, dryRun } = options;
|
const { targetNoteId, dryRun } = options;
|
||||||
const targetNoteItem = Zotero.Items.get(targetNoteId || -1);
|
const targetNoteItem = Zotero.Items.get(targetNoteId || -1);
|
||||||
const sharedObj = {};
|
const sharedObj = {};
|
||||||
return await runTemplate(
|
return await runTemplate(
|
||||||
|
|
@ -121,7 +121,7 @@ async function runTextTemplate(
|
||||||
[targetNoteItem, sharedObj],
|
[targetNoteItem, sharedObj],
|
||||||
{
|
{
|
||||||
dryRun,
|
dryRun,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,7 +131,7 @@ async function runItemTemplate(
|
||||||
itemIds?: number[];
|
itemIds?: number[];
|
||||||
targetNoteId?: number;
|
targetNoteId?: number;
|
||||||
dryRun?: boolean;
|
dryRun?: boolean;
|
||||||
}
|
},
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
/**
|
/**
|
||||||
* args:
|
* args:
|
||||||
|
|
@ -139,7 +139,8 @@ async function runItemTemplate(
|
||||||
* default stage: topItem, itemNotes, copyNoteImage, sharedObj
|
* default stage: topItem, itemNotes, copyNoteImage, sharedObj
|
||||||
* afterloop stage: items, copyNoteImage, sharedObj
|
* afterloop stage: items, copyNoteImage, sharedObj
|
||||||
*/
|
*/
|
||||||
let { itemIds, targetNoteId, dryRun } = options;
|
let { itemIds } = options;
|
||||||
|
const { targetNoteId, dryRun } = options;
|
||||||
if (!itemIds) {
|
if (!itemIds) {
|
||||||
itemIds = await getItemTemplateData();
|
itemIds = await getItemTemplateData();
|
||||||
}
|
}
|
||||||
|
|
@ -169,8 +170,8 @@ async function runItemTemplate(
|
||||||
stage: "beforeloop",
|
stage: "beforeloop",
|
||||||
useDefault: false,
|
useDefault: false,
|
||||||
dryRun,
|
dryRun,
|
||||||
}
|
},
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const topItem of items) {
|
for (const topItem of items) {
|
||||||
|
|
@ -184,8 +185,8 @@ async function runItemTemplate(
|
||||||
[topItem, targetNoteItem, itemNotes, copyNoteImage, sharedObj],
|
[topItem, targetNoteItem, itemNotes, copyNoteImage, sharedObj],
|
||||||
{
|
{
|
||||||
dryRun,
|
dryRun,
|
||||||
}
|
},
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,8 +199,8 @@ async function runItemTemplate(
|
||||||
stage: "afterloop",
|
stage: "afterloop",
|
||||||
useDefault: false,
|
useDefault: false,
|
||||||
dryRun,
|
dryRun,
|
||||||
}
|
},
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let html = results.join("\n");
|
let html = results.join("\n");
|
||||||
|
|
@ -207,7 +208,7 @@ async function runItemTemplate(
|
||||||
html = await copyEmbeddedImagesInHTML(
|
html = await copyEmbeddedImagesInHTML(
|
||||||
html,
|
html,
|
||||||
targetNoteItem,
|
targetNoteItem,
|
||||||
copyImageRefNotes
|
copyImageRefNotes,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
html = await renderNoteHTML(html, copyImageRefNotes);
|
html = await renderNoteHTML(html, copyImageRefNotes);
|
||||||
|
|
@ -230,9 +231,9 @@ async function getItemTemplateData() {
|
||||||
slice(
|
slice(
|
||||||
(firstSelectedItem.getField("title") as string) ||
|
(firstSelectedItem.getField("title") as string) ||
|
||||||
firstSelectedItem.key,
|
firstSelectedItem.key,
|
||||||
40
|
40,
|
||||||
),
|
),
|
||||||
40
|
40,
|
||||||
)} ${
|
)} ${
|
||||||
librarySelectedIds.length > 1
|
librarySelectedIds.length > 1
|
||||||
? `and ${librarySelectedIds.length - 1} more`
|
? `and ${librarySelectedIds.length - 1} more`
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export {
|
||||||
|
|
||||||
// Controller
|
// Controller
|
||||||
function getTemplateKeys(): { name: string }[] {
|
function getTemplateKeys(): { name: string }[] {
|
||||||
let templateKeys = getPref("templateKeys") as string;
|
const templateKeys = getPref("templateKeys") as string;
|
||||||
return templateKeys ? JSON.parse(templateKeys) : [];
|
return templateKeys ? JSON.parse(templateKeys) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ function getTemplateText(keyName: string): string {
|
||||||
|
|
||||||
function setTemplate(
|
function setTemplate(
|
||||||
template: NoteTemplate,
|
template: NoteTemplate,
|
||||||
updatePrompt: boolean = true
|
updatePrompt: boolean = true,
|
||||||
): void {
|
): void {
|
||||||
template = JSON.parse(JSON.stringify(template));
|
template = JSON.parse(JSON.stringify(template));
|
||||||
addTemplateKey({ name: template.name });
|
addTemplateKey({ name: template.name });
|
||||||
|
|
@ -64,7 +64,7 @@ function setTemplate(
|
||||||
|
|
||||||
function removeTemplate(
|
function removeTemplate(
|
||||||
keyName: string | undefined,
|
keyName: string | undefined,
|
||||||
updatePrompt: boolean = true
|
updatePrompt: boolean = true,
|
||||||
): void {
|
): void {
|
||||||
if (typeof keyName === "undefined") {
|
if (typeof keyName === "undefined") {
|
||||||
return;
|
return;
|
||||||
|
|
@ -77,7 +77,7 @@ function removeTemplate(
|
||||||
}
|
}
|
||||||
|
|
||||||
function initTemplates() {
|
function initTemplates() {
|
||||||
let templateKeys = getTemplateKeys();
|
const templateKeys = getTemplateKeys();
|
||||||
const currentNames = templateKeys.map((t) => t.name);
|
const currentNames = templateKeys.map((t) => t.name);
|
||||||
for (const defaultTemplate of addon.api.template.DEFAULT_TEMPLATES) {
|
for (const defaultTemplate of addon.api.template.DEFAULT_TEMPLATES) {
|
||||||
if (!currentNames.includes(defaultTemplate.name)) {
|
if (!currentNames.includes(defaultTemplate.name)) {
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,13 @@ export async function showTemplateEditor() {
|
||||||
`chrome://${config.addonRef}/content/templateEditor.xhtml`,
|
`chrome://${config.addonRef}/content/templateEditor.xhtml`,
|
||||||
`${config.addonRef}-templateEditor`,
|
`${config.addonRef}-templateEditor`,
|
||||||
`chrome,centerscreen,resizable,status,width=600,height=400,dialog=no`,
|
`chrome,centerscreen,resizable,status,width=600,height=400,dialog=no`,
|
||||||
windowArgs
|
windowArgs,
|
||||||
)!;
|
)!;
|
||||||
addon.data.templateEditor.window = _window;
|
addon.data.templateEditor.window = _window;
|
||||||
await windowArgs._initPromise.promise;
|
await windowArgs._initPromise.promise;
|
||||||
updateData();
|
updateData();
|
||||||
addon.data.templateEditor.tableHelper = new ztoolkit.VirtualizedTable(
|
addon.data.templateEditor.tableHelper = new ztoolkit.VirtualizedTable(
|
||||||
_window!
|
_window!,
|
||||||
)
|
)
|
||||||
.setContainerId("table-container")
|
.setContainerId("table-container")
|
||||||
.setProp({
|
.setProp({
|
||||||
|
|
@ -40,7 +40,7 @@ export async function showTemplateEditor() {
|
||||||
].map((column) =>
|
].map((column) =>
|
||||||
Object.assign(column, {
|
Object.assign(column, {
|
||||||
label: getString(column.label),
|
label: getString(column.label),
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
multiSelect: false,
|
multiSelect: false,
|
||||||
|
|
@ -53,7 +53,7 @@ export async function showTemplateEditor() {
|
||||||
(index) =>
|
(index) =>
|
||||||
(addon.data.templateEditor.templates[index] as { name: string }) || {
|
(addon.data.templateEditor.templates[index] as { name: string }) || {
|
||||||
name: "no data",
|
name: "no data",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
.setProp("onSelectionChange", (selection) => {
|
.setProp("onSelectionChange", (selection) => {
|
||||||
updateEditor();
|
updateEditor();
|
||||||
|
|
@ -72,7 +72,7 @@ export async function showTemplateEditor() {
|
||||||
})
|
})
|
||||||
.setProp(
|
.setProp(
|
||||||
"getRowString",
|
"getRowString",
|
||||||
(index) => addon.data.prefs?.rows[index].title || ""
|
(index) => addon.data.prefs?.rows[index].title || "",
|
||||||
)
|
)
|
||||||
.render();
|
.render();
|
||||||
_window.document
|
_window.document
|
||||||
|
|
@ -87,12 +87,12 @@ export async function showTemplateEditor() {
|
||||||
});
|
});
|
||||||
_window.document.querySelector("#help")?.addEventListener("click", (ev) => {
|
_window.document.querySelector("#help")?.addEventListener("click", (ev) => {
|
||||||
Zotero.launchURL(
|
Zotero.launchURL(
|
||||||
"https://github.com/windingwind/zotero-better-notes/blob/master/docs/about-note-template.md"
|
"https://github.com/windingwind/zotero-better-notes/blob/master/docs/about-note-template.md",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
_window.document.querySelector("#more")?.addEventListener("click", (ev) => {
|
_window.document.querySelector("#more")?.addEventListener("click", (ev) => {
|
||||||
Zotero.launchURL(
|
Zotero.launchURL(
|
||||||
"https://github.com/windingwind/zotero-better-notes/discussions/categories/note-templates"
|
"https://github.com/windingwind/zotero-better-notes/discussions/categories/note-templates",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
_window.document.querySelector("#save")?.addEventListener("click", (ev) => {
|
_window.document.querySelector("#save")?.addEventListener("click", (ev) => {
|
||||||
|
|
@ -147,22 +147,22 @@ function updateEditor() {
|
||||||
const templateText = addon.api.template.getTemplateText(name);
|
const templateText = addon.api.template.getTemplateText(name);
|
||||||
|
|
||||||
const header = addon.data.templateEditor.window?.document.getElementById(
|
const header = addon.data.templateEditor.window?.document.getElementById(
|
||||||
"editor-name"
|
"editor-name",
|
||||||
) as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
const text = addon.data.templateEditor.window?.document.getElementById(
|
const text = addon.data.templateEditor.window?.document.getElementById(
|
||||||
"editor-textbox"
|
"editor-textbox",
|
||||||
) as HTMLTextAreaElement;
|
) as HTMLTextAreaElement;
|
||||||
const saveTemplate =
|
const saveTemplate =
|
||||||
addon.data.templateEditor.window?.document.getElementById(
|
addon.data.templateEditor.window?.document.getElementById(
|
||||||
"save"
|
"save",
|
||||||
) as XUL.Button;
|
) as XUL.Button;
|
||||||
const deleteTemplate =
|
const deleteTemplate =
|
||||||
addon.data.templateEditor.window?.document.getElementById(
|
addon.data.templateEditor.window?.document.getElementById(
|
||||||
"delete"
|
"delete",
|
||||||
) as XUL.Button;
|
) as XUL.Button;
|
||||||
const resetTemplate =
|
const resetTemplate =
|
||||||
addon.data.templateEditor.window?.document.getElementById(
|
addon.data.templateEditor.window?.document.getElementById(
|
||||||
"reset"
|
"reset",
|
||||||
) as XUL.Button;
|
) as XUL.Button;
|
||||||
if (!name) {
|
if (!name) {
|
||||||
header.value = "";
|
header.value = "";
|
||||||
|
|
@ -194,11 +194,11 @@ function updateEditor() {
|
||||||
|
|
||||||
async function updatePreview() {
|
async function updatePreview() {
|
||||||
const name = getSelectedTemplateName();
|
const name = getSelectedTemplateName();
|
||||||
let html = (await addon.api.template.renderTemplatePreview(name))
|
const html = (await addon.api.template.renderTemplatePreview(name))
|
||||||
.replace(/ /g, "#160;")
|
.replace(/ /g, "#160;")
|
||||||
.replace(/<br>/g, "<br/>")
|
.replace(/<br>/g, "<br/>")
|
||||||
.replace(/<hr>/g, "<hr/>")
|
.replace(/<hr>/g, "<hr/>")
|
||||||
.replace(/<img([^>]+)\>/g, "<img$1/>");
|
.replace(/<img([^>]+)>/g, "<img$1/>");
|
||||||
const win = addon.data.templateEditor.window;
|
const win = addon.data.templateEditor.window;
|
||||||
const container = win?.document.getElementById("preview-container");
|
const container = win?.document.getElementById("preview-container");
|
||||||
if (container) {
|
if (container) {
|
||||||
|
|
@ -207,15 +207,18 @@ async function updatePreview() {
|
||||||
} else {
|
} else {
|
||||||
container.innerHTML = "";
|
container.innerHTML = "";
|
||||||
container.appendChild(
|
container.appendChild(
|
||||||
ztoolkit.getDOMParser().parseFromString(html, "text/html").body
|
ztoolkit.getDOMParser().parseFromString(html, "text/html").body,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectedTemplateName() {
|
function getSelectedTemplateName() {
|
||||||
const selectedTemplate = addon.data.templateEditor.templates.find((v, i) =>
|
const selectedTemplate = addon.data.templateEditor.templates.find(
|
||||||
addon.data.templateEditor.tableHelper?.treeInstance.selection.isSelected(i)
|
(v, i) =>
|
||||||
|
addon.data.templateEditor.tableHelper?.treeInstance.selection.isSelected(
|
||||||
|
i,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
return selectedTemplate?.name || "";
|
return selectedTemplate?.name || "";
|
||||||
}
|
}
|
||||||
|
|
@ -232,7 +235,7 @@ function createTemplate() {
|
||||||
async function importNoteTemplate() {
|
async function importNoteTemplate() {
|
||||||
const ids = await itemPicker();
|
const ids = await itemPicker();
|
||||||
const note: Zotero.Item = Zotero.Items.get(ids).filter((item: Zotero.Item) =>
|
const note: Zotero.Item = Zotero.Items.get(ids).filter((item: Zotero.Item) =>
|
||||||
item.isNote()
|
item.isNote(),
|
||||||
)[0];
|
)[0];
|
||||||
if (!note) {
|
if (!note) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -248,10 +251,10 @@ async function importNoteTemplate() {
|
||||||
function saveSelectedTemplate() {
|
function saveSelectedTemplate() {
|
||||||
const name = getSelectedTemplateName();
|
const name = getSelectedTemplateName();
|
||||||
const header = addon.data.templateEditor.window?.document.getElementById(
|
const header = addon.data.templateEditor.window?.document.getElementById(
|
||||||
"editor-name"
|
"editor-name",
|
||||||
) as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
const text = addon.data.templateEditor.window?.document.getElementById(
|
const text = addon.data.templateEditor.window?.document.getElementById(
|
||||||
"editor-textbox"
|
"editor-textbox",
|
||||||
) as HTMLTextAreaElement;
|
) as HTMLTextAreaElement;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|
@ -259,7 +262,7 @@ function saveSelectedTemplate() {
|
||||||
header.value !== name
|
header.value !== name
|
||||||
) {
|
) {
|
||||||
showHint(
|
showHint(
|
||||||
`Template ${name} is a system template. Modifying template name is not allowed.`
|
`Template ${name} is a system template. Modifying template name is not allowed.`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -284,7 +287,7 @@ function deleteSelectedTemplate() {
|
||||||
const name = getSelectedTemplateName();
|
const name = getSelectedTemplateName();
|
||||||
if (addon.api.template.SYSTEM_TEMPLATE_NAMES.includes(name)) {
|
if (addon.api.template.SYSTEM_TEMPLATE_NAMES.includes(name)) {
|
||||||
showHint(
|
showHint(
|
||||||
`Template ${name} is a system template. Removing system template is note allowed.`
|
`Template ${name} is a system template. Removing system template is note allowed.`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +299,7 @@ function resetSelectedTemplate() {
|
||||||
const name = getSelectedTemplateName();
|
const name = getSelectedTemplateName();
|
||||||
if (addon.api.template.SYSTEM_TEMPLATE_NAMES.includes(name)) {
|
if (addon.api.template.SYSTEM_TEMPLATE_NAMES.includes(name)) {
|
||||||
const text = addon.data.templateEditor.window?.document.getElementById(
|
const text = addon.data.templateEditor.window?.document.getElementById(
|
||||||
"editor-textbox"
|
"editor-textbox",
|
||||||
) as HTMLTextAreaElement;
|
) as HTMLTextAreaElement;
|
||||||
text.value =
|
text.value =
|
||||||
addon.api.template.DEFAULT_TEMPLATES.find((t) => t.name === name)?.text ||
|
addon.api.template.DEFAULT_TEMPLATES.find((t) => t.name === name)?.text ||
|
||||||
|
|
@ -325,7 +328,7 @@ ${content
|
||||||
`;
|
`;
|
||||||
new ztoolkit.Clipboard().addText(yaml, "text/unicode").copy();
|
new ztoolkit.Clipboard().addText(yaml, "text/unicode").copy();
|
||||||
showHint(
|
showHint(
|
||||||
`Template ${name} is copied to clipboard. To import it, goto Zotero menu bar, click Edit->New Template from Clipboard. `
|
`Template ${name} is copied to clipboard. To import it, goto Zotero menu bar, click Edit->New Template from Clipboard. `,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,7 +338,7 @@ async function backupTemplates() {
|
||||||
"Save backup file",
|
"Save backup file",
|
||||||
"save",
|
"save",
|
||||||
[["yaml", "*.yaml"]],
|
[["yaml", "*.yaml"]],
|
||||||
`bn-template-backup-${time}.yaml`
|
`bn-template-backup-${time}.yaml`,
|
||||||
).open();
|
).open();
|
||||||
if (!filepath) {
|
if (!filepath) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -358,7 +361,7 @@ async function restoreTemplates(win: Window) {
|
||||||
[["yaml", "*.yaml"]],
|
[["yaml", "*.yaml"]],
|
||||||
undefined,
|
undefined,
|
||||||
win,
|
win,
|
||||||
"text"
|
"text",
|
||||||
).open();
|
).open();
|
||||||
if (!filepath) {
|
if (!filepath) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -370,7 +373,7 @@ async function restoreTemplates(win: Window) {
|
||||||
for (const t of templates) {
|
for (const t of templates) {
|
||||||
if (existingNames.includes(t.name)) {
|
if (existingNames.includes(t.name)) {
|
||||||
const overwrite = win.confirm(
|
const overwrite = win.confirm(
|
||||||
`Template ${t.name} already exists. Overwrite?`
|
`Template ${t.name} already exists. Overwrite?`,
|
||||||
);
|
);
|
||||||
if (!overwrite) {
|
if (!overwrite) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,17 @@ export { updateTemplatePicker, showTemplatePicker };
|
||||||
|
|
||||||
function showTemplatePicker(
|
function showTemplatePicker(
|
||||||
mode: "insert",
|
mode: "insert",
|
||||||
data?: { noteId?: number; lineIndex?: number }
|
data?: { noteId?: number; lineIndex?: number },
|
||||||
): void;
|
): void;
|
||||||
function showTemplatePicker(
|
function showTemplatePicker(
|
||||||
mode: "create",
|
mode: "create",
|
||||||
data?: { noteType?: "standalone" | "item"; parentItemId?: number }
|
data?: { noteType?: "standalone" | "item"; parentItemId?: number },
|
||||||
): void;
|
): void;
|
||||||
function showTemplatePicker(mode: "export", data?: {}): void;
|
function showTemplatePicker(mode: "export", data?: Record<string, never>): void;
|
||||||
function showTemplatePicker(): void;
|
function showTemplatePicker(): void;
|
||||||
function showTemplatePicker(
|
function showTemplatePicker(
|
||||||
mode: typeof addon.data.templatePicker.mode = "insert",
|
mode: typeof addon.data.templatePicker.mode = "insert",
|
||||||
data: Record<string, any> = {}
|
data: Record<string, any> = {},
|
||||||
) {
|
) {
|
||||||
if (addon.data.prompt) {
|
if (addon.data.prompt) {
|
||||||
addon.data.templatePicker.mode = mode;
|
addon.data.templatePicker.mode = mode;
|
||||||
|
|
@ -25,8 +25,8 @@ function showTemplatePicker(
|
||||||
addon.data.prompt.promptNode.style.display = "flex";
|
addon.data.prompt.promptNode.style.display = "flex";
|
||||||
addon.data.prompt.showCommands(
|
addon.data.prompt.showCommands(
|
||||||
addon.data.prompt.commands.filter(
|
addon.data.prompt.commands.filter(
|
||||||
(cmd) => cmd.label === "BNotes Template"
|
(cmd) => cmd.label === "BNotes Template",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +38,7 @@ function updateTemplatePicker() {
|
||||||
templates
|
templates
|
||||||
.filter(
|
.filter(
|
||||||
(template) =>
|
(template) =>
|
||||||
!addon.api.template.SYSTEM_TEMPLATE_NAMES.includes(template.name)
|
!addon.api.template.SYSTEM_TEMPLATE_NAMES.includes(template.name),
|
||||||
)
|
)
|
||||||
.map((template) => {
|
.map((template) => {
|
||||||
return {
|
return {
|
||||||
|
|
@ -46,7 +46,7 @@ function updateTemplatePicker() {
|
||||||
label: "BNotes Template",
|
label: "BNotes Template",
|
||||||
callback: getTemplatePromptHandler(template.name),
|
callback: getTemplatePromptHandler(template.name),
|
||||||
};
|
};
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
if (!addon.data.prompt) {
|
if (!addon.data.prompt) {
|
||||||
addon.data.prompt = ToolkitGlobal.getInstance().prompt.instance;
|
addon.data.prompt = ToolkitGlobal.getInstance().prompt.instance;
|
||||||
|
|
@ -77,7 +77,7 @@ function getTemplatePromptHandler(name: string) {
|
||||||
|
|
||||||
async function insertTemplateCallback(name: string) {
|
async function insertTemplateCallback(name: string) {
|
||||||
const targetNoteItem = Zotero.Items.get(
|
const targetNoteItem = Zotero.Items.get(
|
||||||
addon.data.templatePicker.data.noteId || addon.data.workspace.mainId
|
addon.data.templatePicker.data.noteId || addon.data.workspace.mainId,
|
||||||
);
|
);
|
||||||
let html = "";
|
let html = "";
|
||||||
if (name.toLowerCase().startsWith("[item]")) {
|
if (name.toLowerCase().startsWith("[item]")) {
|
||||||
|
|
@ -92,7 +92,7 @@ async function insertTemplateCallback(name: string) {
|
||||||
await addLineToNote(
|
await addLineToNote(
|
||||||
targetNoteItem,
|
targetNoteItem,
|
||||||
html,
|
html,
|
||||||
addon.data.templatePicker.data.lineIndex
|
addon.data.templatePicker.data.lineIndex,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export { renderTemplatePreview };
|
||||||
|
|
||||||
async function renderTemplatePreview(
|
async function renderTemplatePreview(
|
||||||
templateName: string,
|
templateName: string,
|
||||||
inputItems?: Zotero.Item[]
|
inputItems?: Zotero.Item[],
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
let html: string = "<p>Preview rendering failed</p>";
|
let html: string = "<p>Preview rendering failed</p>";
|
||||||
if (!inputItems) {
|
if (!inputItems) {
|
||||||
|
|
@ -33,7 +33,7 @@ async function renderTemplatePreview(
|
||||||
[data],
|
[data],
|
||||||
{
|
{
|
||||||
dryRun: true,
|
dryRun: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (templateName.includes("ExportMDFileHeader")) {
|
} else if (templateName.includes("ExportMDFileHeader")) {
|
||||||
|
|
@ -48,7 +48,7 @@ async function renderTemplatePreview(
|
||||||
[data],
|
[data],
|
||||||
{
|
{
|
||||||
dryRun: true,
|
dryRun: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
const header = Object.assign({}, JSON.parse(raw), {
|
const header = Object.assign({}, JSON.parse(raw), {
|
||||||
version: data.version,
|
version: data.version,
|
||||||
|
|
@ -73,7 +73,7 @@ async function renderTemplatePreview(
|
||||||
[link, linkText, subNoteItem, noteItem],
|
[link, linkText, subNoteItem, noteItem],
|
||||||
{
|
{
|
||||||
dryRun: true,
|
dryRun: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (templateName.includes("QuickBackLink")) {
|
} else if (templateName.includes("QuickBackLink")) {
|
||||||
|
|
@ -92,7 +92,7 @@ async function renderTemplatePreview(
|
||||||
[link, linkText, subNoteItem, noteItem],
|
[link, linkText, subNoteItem, noteItem],
|
||||||
{
|
{
|
||||||
dryRun: true,
|
dryRun: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (templateName.includes("QuickImport")) {
|
} else if (templateName.includes("QuickImport")) {
|
||||||
|
|
@ -109,7 +109,7 @@ async function renderTemplatePreview(
|
||||||
[link, noteItem],
|
[link, noteItem],
|
||||||
{
|
{
|
||||||
dryRun: true,
|
dryRun: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (templateName.includes("QuickNote")) {
|
} else if (templateName.includes("QuickNote")) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
|
||||||
id: string,
|
id: string,
|
||||||
content: string,
|
content: string,
|
||||||
title: string,
|
title: string,
|
||||||
callback: (ev: Event) => void
|
callback: (ev: Event) => void,
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
tag: "div",
|
tag: "div",
|
||||||
|
|
@ -119,7 +119,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
|
||||||
getString("workspace.switchOutline"),
|
getString("workspace.switchOutline"),
|
||||||
(ev) => {
|
(ev) => {
|
||||||
setOutline(container);
|
setOutline(container);
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
makeTooltipProp(
|
makeTooltipProp(
|
||||||
makeId("saveOutlineImage"),
|
makeId("saveOutlineImage"),
|
||||||
|
|
@ -127,7 +127,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
|
||||||
getString("workspace.saveOutlineImage"),
|
getString("workspace.saveOutlineImage"),
|
||||||
(ev) => {
|
(ev) => {
|
||||||
saveImage(container);
|
saveImage(container);
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
makeTooltipProp(
|
makeTooltipProp(
|
||||||
makeId("saveOutlineFreeMind"),
|
makeId("saveOutlineFreeMind"),
|
||||||
|
|
@ -135,7 +135,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
|
||||||
getString("workspace.saveOutlineFreeMind"),
|
getString("workspace.saveOutlineFreeMind"),
|
||||||
(ev) => {
|
(ev) => {
|
||||||
saveFreeMind();
|
saveFreeMind();
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
@ -219,7 +219,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
container
|
container,
|
||||||
);
|
);
|
||||||
// Manually add custom editor items in Zotero 7
|
// Manually add custom editor items in Zotero 7
|
||||||
if (ztoolkit.isZotero7()) {
|
if (ztoolkit.isZotero7()) {
|
||||||
|
|
@ -227,10 +227,10 @@ export function initWorkspace(container: XUL.Box | undefined) {
|
||||||
const customElements = container.ownerGlobal
|
const customElements = container.ownerGlobal
|
||||||
.customElements as CustomElementRegistry;
|
.customElements as CustomElementRegistry;
|
||||||
const mainEditorContainer = container.querySelector(
|
const mainEditorContainer = container.querySelector(
|
||||||
`#${makeId("editor-main-container")}`
|
`#${makeId("editor-main-container")}`,
|
||||||
);
|
);
|
||||||
const previewEditorContainer = container.querySelector(
|
const previewEditorContainer = container.querySelector(
|
||||||
`#${makeId("editor-preview-container")}`
|
`#${makeId("editor-preview-container")}`,
|
||||||
);
|
);
|
||||||
const mainEditor = new (customElements.get("note-editor")!)();
|
const mainEditor = new (customElements.get("note-editor")!)();
|
||||||
mainEditor.id = makeId("editor-main");
|
mainEditor.id = makeId("editor-main");
|
||||||
|
|
@ -243,7 +243,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const outlineContainer = container.querySelector(
|
const outlineContainer = container.querySelector(
|
||||||
`#${makeId("outline-container")}`
|
`#${makeId("outline-container")}`,
|
||||||
) as XUL.Box;
|
) as XUL.Box;
|
||||||
const outlineMut = new (ztoolkit.getGlobal("MutationObserver"))(
|
const outlineMut = new (ztoolkit.getGlobal("MutationObserver"))(
|
||||||
(mutations) => {
|
(mutations) => {
|
||||||
|
|
@ -252,7 +252,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
|
||||||
} else {
|
} else {
|
||||||
outlineContainer.style.display = "flex";
|
outlineContainer.style.display = "flex";
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
outlineMut.observe(outlineContainer, {
|
outlineMut.observe(outlineContainer, {
|
||||||
attributes: true,
|
attributes: true,
|
||||||
|
|
@ -269,7 +269,7 @@ export async function initWorkspaceEditor(
|
||||||
noteId: number,
|
noteId: number,
|
||||||
options: {
|
options: {
|
||||||
lineIndex?: number;
|
lineIndex?: number;
|
||||||
} = {}
|
} = {},
|
||||||
) {
|
) {
|
||||||
const noteItem = Zotero.Items.get(noteId);
|
const noteItem = Zotero.Items.get(noteId);
|
||||||
if (!noteItem || !noteItem.isNote()) {
|
if (!noteItem || !noteItem.isNote()) {
|
||||||
|
|
@ -279,7 +279,7 @@ export async function initWorkspaceEditor(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const editorElem = container?.querySelector(
|
const editorElem = container?.querySelector(
|
||||||
`#${makeId("editor-" + type)}`
|
`#${makeId("editor-" + type)}`,
|
||||||
) as EditorElement;
|
) as EditorElement;
|
||||||
await waitUtilAsync(() => Boolean(editorElem._initialized))
|
await waitUtilAsync(() => Boolean(editorElem._initialized))
|
||||||
.then(() => ztoolkit.log("ok"))
|
.then(() => ztoolkit.log("ok"))
|
||||||
|
|
@ -300,7 +300,7 @@ export async function initWorkspaceEditor(
|
||||||
}
|
}
|
||||||
|
|
||||||
function getContainerType(
|
function getContainerType(
|
||||||
container: XUL.Box | undefined
|
container: XUL.Box | undefined,
|
||||||
): "tab" | "window" | "unknown" {
|
): "tab" | "window" | "unknown" {
|
||||||
if (!container) {
|
if (!container) {
|
||||||
return "unknown";
|
return "unknown";
|
||||||
|
|
@ -337,7 +337,7 @@ export function toggleNotesPane(visibility?: boolean) {
|
||||||
|
|
||||||
export function getWorkspaceEditor(
|
export function getWorkspaceEditor(
|
||||||
workspaceType: "tab" | "window",
|
workspaceType: "tab" | "window",
|
||||||
editorType: "main" | "preview" = "main"
|
editorType: "main" | "preview" = "main",
|
||||||
) {
|
) {
|
||||||
const container =
|
const container =
|
||||||
workspaceType === "tab"
|
workspaceType === "tab"
|
||||||
|
|
@ -359,7 +359,7 @@ const SRC_LIST = [
|
||||||
|
|
||||||
function setOutline(
|
function setOutline(
|
||||||
container: XUL.Box,
|
container: XUL.Box,
|
||||||
newType: OutlineType = OutlineType.empty
|
newType: OutlineType = OutlineType.empty,
|
||||||
) {
|
) {
|
||||||
if (newType === OutlineType.empty) {
|
if (newType === OutlineType.empty) {
|
||||||
newType = addon.data.workspace.outline + 1;
|
newType = addon.data.workspace.outline + 1;
|
||||||
|
|
@ -373,11 +373,11 @@ function setOutline(
|
||||||
).hidden = newType === OutlineType.treeView;
|
).hidden = newType === OutlineType.treeView;
|
||||||
(
|
(
|
||||||
container.querySelector(
|
container.querySelector(
|
||||||
`#${makeId("saveOutlineFreeMind")}`
|
`#${makeId("saveOutlineFreeMind")}`,
|
||||||
) as HTMLDivElement
|
) as HTMLDivElement
|
||||||
).hidden = newType === OutlineType.treeView;
|
).hidden = newType === OutlineType.treeView;
|
||||||
const iframe = container.querySelector(
|
const iframe = container.querySelector(
|
||||||
`#${makeId("outline-iframe")}`
|
`#${makeId("outline-iframe")}`,
|
||||||
) as HTMLIFrameElement;
|
) as HTMLIFrameElement;
|
||||||
iframe.setAttribute("src", SRC_LIST[addon.data.workspace.outline]);
|
iframe.setAttribute("src", SRC_LIST[addon.data.workspace.outline]);
|
||||||
updateOutline(container);
|
updateOutline(container);
|
||||||
|
|
@ -386,22 +386,22 @@ function setOutline(
|
||||||
|
|
||||||
export async function updateOutline(container: XUL.Box) {
|
export async function updateOutline(container: XUL.Box) {
|
||||||
const iframe = container.querySelector(
|
const iframe = container.querySelector(
|
||||||
`#${makeId("outline-iframe")}`
|
`#${makeId("outline-iframe")}`,
|
||||||
) as HTMLIFrameElement;
|
) as HTMLIFrameElement;
|
||||||
await waitUtilAsync(
|
await waitUtilAsync(
|
||||||
() => iframe.contentWindow?.document.readyState === "complete"
|
() => iframe.contentWindow?.document.readyState === "complete",
|
||||||
);
|
);
|
||||||
iframe.contentWindow?.postMessage(
|
iframe.contentWindow?.postMessage(
|
||||||
{
|
{
|
||||||
type: "setMindMapData",
|
type: "setMindMapData",
|
||||||
nodes: getNoteTreeFlattened(
|
nodes: getNoteTreeFlattened(
|
||||||
Zotero.Items.get(addon.data.workspace.mainId),
|
Zotero.Items.get(addon.data.workspace.mainId),
|
||||||
{ keepLink: true }
|
{ keepLink: true },
|
||||||
),
|
),
|
||||||
workspaceType: getContainerType(container),
|
workspaceType: getContainerType(container),
|
||||||
expandLevel: getPref("workspace.outline.expandLevel"),
|
expandLevel: getPref("workspace.outline.expandLevel"),
|
||||||
},
|
},
|
||||||
"*"
|
"*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,20 +413,20 @@ function updateOutlineButtons(container: XUL.Box) {
|
||||||
).style.visibility = isTreeView ? "hidden" : "visible";
|
).style.visibility = isTreeView ? "hidden" : "visible";
|
||||||
(
|
(
|
||||||
container.querySelector(
|
container.querySelector(
|
||||||
`#${makeId("saveOutlineFreeMind")}`
|
`#${makeId("saveOutlineFreeMind")}`,
|
||||||
) as HTMLDivElement
|
) as HTMLDivElement
|
||||||
).style.visibility = isTreeView ? "hidden" : "visible";
|
).style.visibility = isTreeView ? "hidden" : "visible";
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveImage(container: XUL.Box) {
|
function saveImage(container: XUL.Box) {
|
||||||
const iframe = container.querySelector(
|
const iframe = container.querySelector(
|
||||||
`#${makeId("outline-iframe")}`
|
`#${makeId("outline-iframe")}`,
|
||||||
) as HTMLIFrameElement;
|
) as HTMLIFrameElement;
|
||||||
iframe.contentWindow?.postMessage(
|
iframe.contentWindow?.postMessage(
|
||||||
{
|
{
|
||||||
type: "saveSVG",
|
type: "saveSVG",
|
||||||
},
|
},
|
||||||
"*"
|
"*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -436,7 +436,7 @@ async function saveFreeMind() {
|
||||||
`${Zotero.getString("fileInterface.export")} FreeMind XML`,
|
`${Zotero.getString("fileInterface.export")} FreeMind XML`,
|
||||||
"save",
|
"save",
|
||||||
[["FreeMind XML File(*.mm)", "*.mm"]],
|
[["FreeMind XML File(*.mm)", "*.mm"]],
|
||||||
`${Zotero.Items.get(addon.data.workspace.mainId).getNoteTitle()}.mm`
|
`${Zotero.Items.get(addon.data.workspace.mainId).getNoteTitle()}.mm`,
|
||||||
).open();
|
).open();
|
||||||
if (filename) {
|
if (filename) {
|
||||||
await _saveFreeMind(filename, addon.data.workspace.mainId);
|
await _saveFreeMind(filename, addon.data.workspace.mainId);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export async function messageHandler(ev: MessageEvent) {
|
||||||
case "jumpNode": {
|
case "jumpNode": {
|
||||||
const editor = addon.api.workspace.getWorkspaceEditor(
|
const editor = addon.api.workspace.getWorkspaceEditor(
|
||||||
ev.data.workspaceType,
|
ev.data.workspaceType,
|
||||||
"main"
|
"main",
|
||||||
);
|
);
|
||||||
if (!editor) {
|
if (!editor) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -34,21 +34,21 @@ export async function messageHandler(ev: MessageEvent) {
|
||||||
}
|
}
|
||||||
case "moveNode": {
|
case "moveNode": {
|
||||||
const noteItem = Zotero.Items.get(addon.data.workspace.mainId);
|
const noteItem = Zotero.Items.get(addon.data.workspace.mainId);
|
||||||
let tree = getNoteTree(noteItem);
|
const tree = getNoteTree(noteItem);
|
||||||
let fromNode = getNoteTreeNodeById(noteItem, ev.data.fromID, tree);
|
const fromNode = getNoteTreeNodeById(noteItem, ev.data.fromID, tree);
|
||||||
let toNode = getNoteTreeNodeById(noteItem, ev.data.toID, tree);
|
const toNode = getNoteTreeNodeById(noteItem, ev.data.toID, tree);
|
||||||
moveHeading(
|
moveHeading(
|
||||||
getEditorInstance(noteItem.id),
|
getEditorInstance(noteItem.id),
|
||||||
fromNode!,
|
fromNode!,
|
||||||
toNode!,
|
toNode!,
|
||||||
ev.data.moveType
|
ev.data.moveType,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case "editNode": {
|
case "editNode": {
|
||||||
const editor = addon.api.workspace.getWorkspaceEditor(
|
const editor = addon.api.workspace.getWorkspaceEditor(
|
||||||
ev.data.workspaceType,
|
ev.data.workspaceType,
|
||||||
"main"
|
"main",
|
||||||
);
|
);
|
||||||
if (!editor) {
|
if (!editor) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -56,7 +56,7 @@ export async function messageHandler(ev: MessageEvent) {
|
||||||
updateHeadingTextAtLine(
|
updateHeadingTextAtLine(
|
||||||
editor,
|
editor,
|
||||||
ev.data.lineIndex,
|
ev.data.lineIndex,
|
||||||
ev.data.text.replace(/[\r\n]/g, "")
|
ev.data.text.replace(/[\r\n]/g, ""),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +65,7 @@ export async function messageHandler(ev: MessageEvent) {
|
||||||
`${Zotero.getString("fileInterface.export")} SVG Image`,
|
`${Zotero.getString("fileInterface.export")} SVG Image`,
|
||||||
"save",
|
"save",
|
||||||
[["SVG File(*.svg)", "*.svg"]],
|
[["SVG File(*.svg)", "*.svg"]],
|
||||||
`${Zotero.Items.get(addon.data.workspace.mainId).getNoteTitle()}.svg`
|
`${Zotero.Items.get(addon.data.workspace.mainId).getNoteTitle()}.svg`,
|
||||||
).open();
|
).open();
|
||||||
if (filename) {
|
if (filename) {
|
||||||
await Zotero.File.putContentsAsync(formatPath(filename), ev.data.image);
|
await Zotero.File.putContentsAsync(formatPath(filename), ev.data.image);
|
||||||
|
|
@ -74,7 +74,7 @@ export async function messageHandler(ev: MessageEvent) {
|
||||||
"Show in Folder",
|
"Show in Folder",
|
||||||
(ev) => {
|
(ev) => {
|
||||||
Zotero.File.reveal(filename);
|
Zotero.File.reveal(filename);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,9 @@ export function registerWorkspaceTab() {
|
||||||
attributeFilter: ["hidden"],
|
attributeFilter: ["hidden"],
|
||||||
});
|
});
|
||||||
waitUtilAsync(() =>
|
waitUtilAsync(() =>
|
||||||
Boolean(ztoolkit.getGlobal("ZoteroContextPane")._notifierID)
|
Boolean(ztoolkit.getGlobal("ZoteroContextPane")._notifierID),
|
||||||
).then(() => {
|
).then(() => {
|
||||||
addWorkspaceTab().then(() => {
|
addWorkspaceTab();
|
||||||
restoreWorkspaceTab();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
window.addEventListener("message", (e) => messageHandler(e), false);
|
window.addEventListener("message", (e) => messageHandler(e), false);
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +36,7 @@ export function unregisterWorkspaceTab() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addWorkspaceTab() {
|
async function addWorkspaceTab() {
|
||||||
let { id, container } = Zotero_Tabs.add({
|
const { id, container } = Zotero_Tabs.add({
|
||||||
type: TAB_TYPE,
|
type: TAB_TYPE,
|
||||||
title: getString("tab.name"),
|
title: getString("tab.name"),
|
||||||
index: 1,
|
index: 1,
|
||||||
|
|
@ -54,10 +52,10 @@ async function addWorkspaceTab() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await waitUtilAsync(() =>
|
await waitUtilAsync(() =>
|
||||||
Boolean(document.querySelector(`.tabs-wrapper .tab[data-id=${id}]`))
|
Boolean(document.querySelector(`.tabs-wrapper .tab[data-id=${id}]`)),
|
||||||
);
|
);
|
||||||
const tabElem = document.querySelector(
|
const tabElem = document.querySelector(
|
||||||
`.tabs-wrapper .tab[data-id=${id}]`
|
`.tabs-wrapper .tab[data-id=${id}]`,
|
||||||
) as HTMLDivElement;
|
) as HTMLDivElement;
|
||||||
tabElem.style.width = "30px";
|
tabElem.style.width = "30px";
|
||||||
tabElem.style.minWidth = "30px";
|
tabElem.style.minWidth = "30px";
|
||||||
|
|
@ -78,7 +76,7 @@ async function addWorkspaceTab() {
|
||||||
backgroundImage: `url("chrome://${config.addonRef}/content/icons/favicon.png")`,
|
backgroundImage: `url("chrome://${config.addonRef}/content/icons/favicon.png")`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
content
|
content,
|
||||||
);
|
);
|
||||||
close.style.visibility = "hidden";
|
close.style.visibility = "hidden";
|
||||||
addon.data.workspace.tab.id = id;
|
addon.data.workspace.tab.id = id;
|
||||||
|
|
@ -91,7 +89,7 @@ function hoverWorkspaceTab(hovered: boolean) {
|
||||||
(elem as HTMLDivElement).style.visibility = hovered ? "visible" : "hidden";
|
(elem as HTMLDivElement).style.visibility = hovered ? "visible" : "hidden";
|
||||||
});
|
});
|
||||||
const tabElem = document.querySelector(
|
const tabElem = document.querySelector(
|
||||||
`.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`
|
`.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`,
|
||||||
) as HTMLDivElement;
|
) as HTMLDivElement;
|
||||||
const content = tabElem.querySelector(".tab-name") as HTMLDivElement;
|
const content = tabElem.querySelector(".tab-name") as HTMLDivElement;
|
||||||
content.removeAttribute("style");
|
content.removeAttribute("style");
|
||||||
|
|
@ -107,10 +105,10 @@ function hoverWorkspaceTab(hovered: boolean) {
|
||||||
|
|
||||||
function updateWorkspaceTabToggleButton(
|
function updateWorkspaceTabToggleButton(
|
||||||
type: "outline" | "preview" | "notes",
|
type: "outline" | "preview" | "notes",
|
||||||
state: "open" | "collapsed"
|
state: "open" | "collapsed",
|
||||||
) {
|
) {
|
||||||
const elem = document.querySelector(
|
const elem = document.querySelector(
|
||||||
`#betternotes-tab-toggle-${type}`
|
`#betternotes-tab-toggle-${type}`,
|
||||||
) as HTMLDivElement;
|
) as HTMLDivElement;
|
||||||
if (!elem) {
|
if (!elem) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -123,12 +121,12 @@ function updateWorkspaceTabToggleButton(
|
||||||
|
|
||||||
function registerWorkspaceTabPaneObserver() {
|
function registerWorkspaceTabPaneObserver() {
|
||||||
const outlineSplitter = document.querySelector(
|
const outlineSplitter = document.querySelector(
|
||||||
"#betternotes-workspace-outline-splitter"
|
"#betternotes-workspace-outline-splitter",
|
||||||
);
|
);
|
||||||
const outlineMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
|
const outlineMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
|
||||||
updateWorkspaceTabToggleButton(
|
updateWorkspaceTabToggleButton(
|
||||||
"outline",
|
"outline",
|
||||||
outlineSplitter!.getAttribute("state")! as "open" | "collapsed"
|
outlineSplitter!.getAttribute("state")! as "open" | "collapsed",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
outlineMut.observe(outlineSplitter!, {
|
outlineMut.observe(outlineSplitter!, {
|
||||||
|
|
@ -136,12 +134,12 @@ function registerWorkspaceTabPaneObserver() {
|
||||||
attributeFilter: ["state"],
|
attributeFilter: ["state"],
|
||||||
});
|
});
|
||||||
const previewSplitter = document.querySelector(
|
const previewSplitter = document.querySelector(
|
||||||
"#betternotes-workspace-preview-splitter"
|
"#betternotes-workspace-preview-splitter",
|
||||||
);
|
);
|
||||||
const previeweMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
|
const previeweMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
|
||||||
updateWorkspaceTabToggleButton(
|
updateWorkspaceTabToggleButton(
|
||||||
"preview",
|
"preview",
|
||||||
previewSplitter!.getAttribute("state")! as "open" | "collapsed"
|
previewSplitter!.getAttribute("state")! as "open" | "collapsed",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
previeweMut.observe(previewSplitter!, {
|
previeweMut.observe(previewSplitter!, {
|
||||||
|
|
@ -152,7 +150,7 @@ function registerWorkspaceTabPaneObserver() {
|
||||||
const notesMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
|
const notesMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
|
||||||
updateWorkspaceTabToggleButton(
|
updateWorkspaceTabToggleButton(
|
||||||
"notes",
|
"notes",
|
||||||
notesSplitter!.getAttribute("state")! as "open" | "collapsed"
|
notesSplitter!.getAttribute("state")! as "open" | "collapsed",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
notesMut.observe(notesSplitter!, {
|
notesMut.observe(notesSplitter!, {
|
||||||
|
|
@ -173,7 +171,7 @@ export async function activateWorkspaceTab() {
|
||||||
document.querySelector("#zotero-tab-toolbar") as XUL.Box
|
document.querySelector("#zotero-tab-toolbar") as XUL.Box
|
||||||
).style.visibility = "collapse";
|
).style.visibility = "collapse";
|
||||||
const toolbar = document.querySelector(
|
const toolbar = document.querySelector(
|
||||||
"#zotero-context-toolbar-extension"
|
"#zotero-context-toolbar-extension",
|
||||||
) as XUL.Box;
|
) as XUL.Box;
|
||||||
toolbar.style.visibility = "collapse";
|
toolbar.style.visibility = "collapse";
|
||||||
toolbar.nextElementSibling?.setAttribute("selectedIndex", "1");
|
toolbar.nextElementSibling?.setAttribute("selectedIndex", "1");
|
||||||
|
|
@ -188,12 +186,12 @@ export async function activateWorkspaceTab() {
|
||||||
await waitUtilAsync(() =>
|
await waitUtilAsync(() =>
|
||||||
Boolean(
|
Boolean(
|
||||||
document.querySelector(
|
document.querySelector(
|
||||||
`.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`
|
`.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
const tabElem = document.querySelector(
|
const tabElem = document.querySelector(
|
||||||
`.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`
|
`.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`,
|
||||||
) as HTMLDivElement;
|
) as HTMLDivElement;
|
||||||
tabElem.removeAttribute("style");
|
tabElem.removeAttribute("style");
|
||||||
const content = tabElem.querySelector(".tab-name") as HTMLDivElement;
|
const content = tabElem.querySelector(".tab-name") as HTMLDivElement;
|
||||||
|
|
@ -222,7 +220,7 @@ export async function activateWorkspaceTab() {
|
||||||
addon.hooks.onToggleWorkspacePane(
|
addon.hooks.onToggleWorkspacePane(
|
||||||
"outline",
|
"outline",
|
||||||
undefined,
|
undefined,
|
||||||
addon.data.workspace.tab.container
|
addon.data.workspace.tab.container,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -245,7 +243,7 @@ export async function activateWorkspaceTab() {
|
||||||
addon.hooks.onToggleWorkspacePane(
|
addon.hooks.onToggleWorkspacePane(
|
||||||
"preview",
|
"preview",
|
||||||
undefined,
|
undefined,
|
||||||
addon.data.workspace.tab.container
|
addon.data.workspace.tab.container,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -281,7 +279,7 @@ export async function activateWorkspaceTab() {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
close
|
close,
|
||||||
);
|
);
|
||||||
hoverWorkspaceTab(false);
|
hoverWorkspaceTab(false);
|
||||||
tabElem.addEventListener("mouseenter", () => {
|
tabElem.addEventListener("mouseenter", () => {
|
||||||
|
|
@ -320,19 +318,11 @@ export function deActivateWorkspaceTab() {
|
||||||
document.querySelector("#zotero-tab-toolbar") as XUL.Box
|
document.querySelector("#zotero-tab-toolbar") as XUL.Box
|
||||||
).style.removeProperty("visibility");
|
).style.removeProperty("visibility");
|
||||||
const toolbar = document.querySelector(
|
const toolbar = document.querySelector(
|
||||||
"#zotero-context-toolbar-extension"
|
"#zotero-context-toolbar-extension",
|
||||||
) as XUL.Box;
|
) as XUL.Box;
|
||||||
toolbar.style.removeProperty("visibility");
|
toolbar.style.removeProperty("visibility");
|
||||||
}
|
}
|
||||||
|
|
||||||
function restoreWorkspaceTab() {
|
|
||||||
return;
|
|
||||||
if (1 || getPref("workspace.tab.active")) {
|
|
||||||
ztoolkit.log("restore workspace tab");
|
|
||||||
activateWorkspaceTab();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWorkspaceTabStatus(status: boolean) {
|
function setWorkspaceTabStatus(status: boolean) {
|
||||||
addon.data.workspace.tab.active = status;
|
addon.data.workspace.tab.active = status;
|
||||||
setPref("workspace.tab.active", status);
|
setPref("workspace.tab.active", status);
|
||||||
|
|
@ -340,7 +330,7 @@ function setWorkspaceTabStatus(status: boolean) {
|
||||||
|
|
||||||
function initWorkspaceTabDragDrop(
|
function initWorkspaceTabDragDrop(
|
||||||
container?: XUL.Box,
|
container?: XUL.Box,
|
||||||
tabElem?: HTMLDivElement
|
tabElem?: HTMLDivElement,
|
||||||
) {
|
) {
|
||||||
if (!container) {
|
if (!container) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -401,10 +391,10 @@ function initWorkspaceTabDragDrop(
|
||||||
],
|
],
|
||||||
enableElementRecord: false,
|
enableElementRecord: false,
|
||||||
},
|
},
|
||||||
container
|
container,
|
||||||
);
|
);
|
||||||
const dropElem = container.querySelector(
|
const dropElem = container.querySelector(
|
||||||
"#bn-workspace-tab-drop"
|
"#bn-workspace-tab-drop",
|
||||||
) as HTMLDivElement;
|
) as HTMLDivElement;
|
||||||
tabElem?.addEventListener("dragstart", (ev) => {
|
tabElem?.addEventListener("dragstart", (ev) => {
|
||||||
dropElem.style.visibility = "visible";
|
dropElem.style.visibility = "visible";
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@ export async function showWorkspaceWindow() {
|
||||||
`chrome://${config.addonRef}/content/workspaceWindow.xhtml`,
|
`chrome://${config.addonRef}/content/workspaceWindow.xhtml`,
|
||||||
`${config.addonRef}-workspaceWindow`,
|
`${config.addonRef}-workspaceWindow`,
|
||||||
`chrome,centerscreen,resizable,status,width=800,height=400,dialog=no`,
|
`chrome,centerscreen,resizable,status,width=800,height=400,dialog=no`,
|
||||||
windowArgs
|
windowArgs,
|
||||||
)!;
|
)!;
|
||||||
await windowArgs._initPromise.promise;
|
await windowArgs._initPromise.promise;
|
||||||
localeWindow(win);
|
localeWindow(win);
|
||||||
addon.data.workspace.window.active = true;
|
addon.data.workspace.window.active = true;
|
||||||
addon.data.workspace.window.window = win;
|
addon.data.workspace.window.window = win;
|
||||||
addon.data.workspace.window.container = win.document.querySelector(
|
addon.data.workspace.window.container = win.document.querySelector(
|
||||||
"#workspace-container"
|
"#workspace-container",
|
||||||
) as XUL.Box;
|
) as XUL.Box;
|
||||||
addon.hooks.onInitWorkspace(addon.data.workspace.window.container);
|
addon.hooks.onInitWorkspace(addon.data.workspace.window.container);
|
||||||
win.addEventListener("message", messageHandler, false);
|
win.addEventListener("message", messageHandler, false);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ async function parseAnnotationJSON(annotationItem: Zotero.Item) {
|
||||||
const annotationJSON = await Zotero.Annotations.toJSON(annotationItem);
|
const annotationJSON = await Zotero.Annotations.toJSON(annotationItem);
|
||||||
const annotationObj = Object.assign(
|
const annotationObj = Object.assign(
|
||||||
{},
|
{},
|
||||||
annotationJSON
|
annotationJSON,
|
||||||
) as CustomAnnotationJSON;
|
) as CustomAnnotationJSON;
|
||||||
annotationObj.id = annotationItem.key;
|
annotationObj.id = annotationItem.key;
|
||||||
annotationObj.attachmentItemID = annotationItem.parentItem?.id;
|
annotationObj.attachmentItemID = annotationItem.parentItem?.id;
|
||||||
|
|
@ -38,12 +38,12 @@ async function parseAnnotationJSON(annotationItem: Zotero.Item) {
|
||||||
function serializeAnnotations(
|
function serializeAnnotations(
|
||||||
annotations: Required<CustomAnnotationJSON>[],
|
annotations: Required<CustomAnnotationJSON>[],
|
||||||
skipEmbeddingItemData: boolean = false,
|
skipEmbeddingItemData: boolean = false,
|
||||||
skipCitation: boolean = false
|
skipCitation: boolean = false,
|
||||||
) {
|
) {
|
||||||
let storedCitationItems = [];
|
const storedCitationItems = [];
|
||||||
let html = "";
|
let html = "";
|
||||||
for (let annotation of annotations) {
|
for (const annotation of annotations) {
|
||||||
let attachmentItem = Zotero.Items.get(annotation.attachmentItemID);
|
const attachmentItem = Zotero.Items.get(annotation.attachmentItemID);
|
||||||
if (!attachmentItem) {
|
if (!attachmentItem) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ function serializeAnnotations(
|
||||||
let quotedHighlightHTML = "";
|
let quotedHighlightHTML = "";
|
||||||
let commentHTML = "";
|
let commentHTML = "";
|
||||||
|
|
||||||
let storedAnnotation: any = {
|
const storedAnnotation: any = {
|
||||||
attachmentURI: Zotero.URI.getItemURI(attachmentItem),
|
attachmentURI: Zotero.URI.getItemURI(attachmentItem),
|
||||||
annotationKey: annotation.id,
|
annotationKey: annotation.id,
|
||||||
color: annotation.color,
|
color: annotation.color,
|
||||||
|
|
@ -73,12 +73,12 @@ function serializeAnnotations(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Citation
|
// Citation
|
||||||
let parentItem = skipCitation
|
const parentItem = skipCitation
|
||||||
? undefined
|
? undefined
|
||||||
: attachmentItem.parentID && Zotero.Items.get(attachmentItem.parentID);
|
: attachmentItem.parentID && Zotero.Items.get(attachmentItem.parentID);
|
||||||
if (parentItem) {
|
if (parentItem) {
|
||||||
let uris = [Zotero.URI.getItemURI(parentItem)];
|
const uris = [Zotero.URI.getItemURI(parentItem)];
|
||||||
let citationItem: any = {
|
const citationItem: any = {
|
||||||
uris,
|
uris,
|
||||||
locator: annotation.pageLabel,
|
locator: annotation.pageLabel,
|
||||||
};
|
};
|
||||||
|
|
@ -86,48 +86,48 @@ function serializeAnnotations(
|
||||||
// Note: integration.js` uses `Zotero.Cite.System.prototype.retrieveItem`,
|
// Note: integration.js` uses `Zotero.Cite.System.prototype.retrieveItem`,
|
||||||
// which produces a little bit different CSL JSON
|
// which produces a little bit different CSL JSON
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let itemData = Zotero.Utilities.Item.itemToCSLJSON(parentItem);
|
const itemData = Zotero.Utilities.Item.itemToCSLJSON(parentItem);
|
||||||
if (!skipEmbeddingItemData) {
|
if (!skipEmbeddingItemData) {
|
||||||
citationItem.itemData = itemData;
|
citationItem.itemData = itemData;
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = storedCitationItems.find((item) =>
|
const item = storedCitationItems.find((item) =>
|
||||||
item.uris.some((uri) => uris.includes(uri))
|
item.uris.some((uri) => uris.includes(uri)),
|
||||||
);
|
);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
storedCitationItems.push({ uris, itemData });
|
storedCitationItems.push({ uris, itemData });
|
||||||
}
|
}
|
||||||
|
|
||||||
storedAnnotation.citationItem = citationItem;
|
storedAnnotation.citationItem = citationItem;
|
||||||
let citation = {
|
const citation = {
|
||||||
citationItems: [citationItem],
|
citationItems: [citationItem],
|
||||||
properties: {},
|
properties: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let citationWithData = JSON.parse(JSON.stringify(citation));
|
const citationWithData = JSON.parse(JSON.stringify(citation));
|
||||||
citationWithData.citationItems[0].itemData = itemData;
|
citationWithData.citationItems[0].itemData = itemData;
|
||||||
let formatted =
|
const formatted =
|
||||||
Zotero.EditorInstanceUtilities.formatCitation(citationWithData);
|
Zotero.EditorInstanceUtilities.formatCitation(citationWithData);
|
||||||
citationHTML = `<span class="citation" data-citation="${encodeURIComponent(
|
citationHTML = `<span class="citation" data-citation="${encodeURIComponent(
|
||||||
JSON.stringify(citation)
|
JSON.stringify(citation),
|
||||||
)}">${formatted}</span>`;
|
)}">${formatted}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image
|
// Image
|
||||||
if (annotation.imageAttachmentKey) {
|
if (annotation.imageAttachmentKey) {
|
||||||
// Normalize image dimensions to 1.25 of the print size
|
// Normalize image dimensions to 1.25 of the print size
|
||||||
let rect = annotation.position.rects[0];
|
const rect = annotation.position.rects[0];
|
||||||
let rectWidth = rect[2] - rect[0];
|
const rectWidth = rect[2] - rect[0];
|
||||||
let rectHeight = rect[3] - rect[1];
|
const rectHeight = rect[3] - rect[1];
|
||||||
// Constants from pdf.js
|
// Constants from pdf.js
|
||||||
const CSS_UNITS = 96.0 / 72.0;
|
const CSS_UNITS = 96.0 / 72.0;
|
||||||
const PDFJS_DEFAULT_SCALE = 1.25;
|
const PDFJS_DEFAULT_SCALE = 1.25;
|
||||||
let width = Math.round(rectWidth * CSS_UNITS * PDFJS_DEFAULT_SCALE);
|
const width = Math.round(rectWidth * CSS_UNITS * PDFJS_DEFAULT_SCALE);
|
||||||
let height = Math.round((rectHeight * width) / rectWidth);
|
const height = Math.round((rectHeight * width) / rectWidth);
|
||||||
imageHTML = `<img data-attachment-key="${
|
imageHTML = `<img data-attachment-key="${
|
||||||
annotation.imageAttachmentKey
|
annotation.imageAttachmentKey
|
||||||
}" width="${width}" height="${height}" data-annotation="${encodeURIComponent(
|
}" width="${width}" height="${height}" data-annotation="${encodeURIComponent(
|
||||||
JSON.stringify(storedAnnotation)
|
JSON.stringify(storedAnnotation),
|
||||||
)}"/>`;
|
)}"/>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,17 +138,17 @@ function serializeAnnotations(
|
||||||
|
|
||||||
// Text
|
// Text
|
||||||
if (annotation.text) {
|
if (annotation.text) {
|
||||||
let text = Zotero.EditorInstanceUtilities._transformTextToHTML.call(
|
const text = Zotero.EditorInstanceUtilities._transformTextToHTML.call(
|
||||||
Zotero.EditorInstanceUtilities,
|
Zotero.EditorInstanceUtilities,
|
||||||
annotation.text.trim()
|
annotation.text.trim(),
|
||||||
);
|
);
|
||||||
highlightHTML = `<span class="highlight" data-annotation="${encodeURIComponent(
|
highlightHTML = `<span class="highlight" data-annotation="${encodeURIComponent(
|
||||||
JSON.stringify(storedAnnotation)
|
JSON.stringify(storedAnnotation),
|
||||||
)}">${text}</span>`;
|
)}">${text}</span>`;
|
||||||
quotedHighlightHTML = `<span class="highlight" data-annotation="${encodeURIComponent(
|
quotedHighlightHTML = `<span class="highlight" data-annotation="${encodeURIComponent(
|
||||||
JSON.stringify(storedAnnotation)
|
JSON.stringify(storedAnnotation),
|
||||||
)}">${Zotero.getString(
|
)}">${Zotero.getString(
|
||||||
"punctuation.openingQMark"
|
"punctuation.openingQMark",
|
||||||
)}${text}${Zotero.getString("punctuation.closingQMark")}</span>`;
|
)}${text}${Zotero.getString("punctuation.closingQMark")}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,14 +156,14 @@ function serializeAnnotations(
|
||||||
if (annotation.comment) {
|
if (annotation.comment) {
|
||||||
commentHTML = Zotero.EditorInstanceUtilities._transformTextToHTML.call(
|
commentHTML = Zotero.EditorInstanceUtilities._transformTextToHTML.call(
|
||||||
Zotero.EditorInstanceUtilities,
|
Zotero.EditorInstanceUtilities,
|
||||||
annotation.comment.trim()
|
annotation.comment.trim(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let template: string = "";
|
let template: string = "";
|
||||||
if (annotation.type === "highlight") {
|
if (annotation.type === "highlight") {
|
||||||
template = Zotero.Prefs.get(
|
template = Zotero.Prefs.get(
|
||||||
"annotations.noteTemplates.highlight"
|
"annotations.noteTemplates.highlight",
|
||||||
) as string;
|
) as string;
|
||||||
} else if (annotation.type === "note") {
|
} else if (annotation.type === "note") {
|
||||||
template = Zotero.Prefs.get("annotations.noteTemplates.note") as string;
|
template = Zotero.Prefs.get("annotations.noteTemplates.note") as string;
|
||||||
|
|
@ -176,10 +176,10 @@ function serializeAnnotations(
|
||||||
|
|
||||||
template = template.replace(
|
template = template.replace(
|
||||||
/(<blockquote>[^<>]*?)({{highlight}})([\s\S]*?<\/blockquote>)/g,
|
/(<blockquote>[^<>]*?)({{highlight}})([\s\S]*?<\/blockquote>)/g,
|
||||||
(match, p1, p2, p3) => p1 + "{{highlight quotes='false'}}" + p3
|
(match, p1, p2, p3) => p1 + "{{highlight quotes='false'}}" + p3,
|
||||||
);
|
);
|
||||||
|
|
||||||
let vars = {
|
const vars = {
|
||||||
color: annotation.color || "",
|
color: annotation.color || "",
|
||||||
// Include quotation marks by default, but allow to disable with `quotes='false'`
|
// Include quotation marks by default, but allow to disable with `quotes='false'`
|
||||||
highlight: (attrs: any) =>
|
highlight: (attrs: any) =>
|
||||||
|
|
@ -196,7 +196,7 @@ function serializeAnnotations(
|
||||||
|
|
||||||
let templateHTML = Zotero.Utilities.Internal.generateHTMLFromTemplate(
|
let templateHTML = Zotero.Utilities.Internal.generateHTMLFromTemplate(
|
||||||
template,
|
template,
|
||||||
vars
|
vars,
|
||||||
);
|
);
|
||||||
// Remove some spaces at the end of paragraph
|
// Remove some spaces at the end of paragraph
|
||||||
templateHTML = templateHTML.replace(/([\s]*)(<\/p)/g, "$2");
|
templateHTML = templateHTML.replace(/([\s]*)(<\/p)/g, "$2");
|
||||||
|
|
@ -209,9 +209,9 @@ function serializeAnnotations(
|
||||||
|
|
||||||
export async function importAnnotationImagesToNote(
|
export async function importAnnotationImagesToNote(
|
||||||
note: Zotero.Item | undefined,
|
note: Zotero.Item | undefined,
|
||||||
annotations: CustomAnnotationJSON[]
|
annotations: CustomAnnotationJSON[],
|
||||||
) {
|
) {
|
||||||
for (let annotation of annotations) {
|
for (const annotation of annotations) {
|
||||||
if (annotation.image && note) {
|
if (annotation.image && note) {
|
||||||
annotation.imageAttachmentKey =
|
annotation.imageAttachmentKey =
|
||||||
(await importImageToNote(note, annotation.image)) || "";
|
(await importImageToNote(note, annotation.image)) || "";
|
||||||
|
|
@ -226,9 +226,9 @@ export async function parseAnnotationHTML(
|
||||||
noteItem?: Zotero.Item; // If you are sure there are no image annotations, note is not required.
|
noteItem?: Zotero.Item; // If you are sure there are no image annotations, note is not required.
|
||||||
ignoreComment?: boolean;
|
ignoreComment?: boolean;
|
||||||
skipCitation?: boolean;
|
skipCitation?: boolean;
|
||||||
} = {}
|
} = {},
|
||||||
) {
|
) {
|
||||||
let annotationJSONList: CustomAnnotationJSON[] = [];
|
const annotationJSONList: CustomAnnotationJSON[] = [];
|
||||||
for (const annot of annotations) {
|
for (const annot of annotations) {
|
||||||
const annotJson = await parseAnnotationJSON(annot);
|
const annotJson = await parseAnnotationJSON(annot);
|
||||||
if (options.ignoreComment && annotJson?.comment) {
|
if (options.ignoreComment && annotJson?.comment) {
|
||||||
|
|
@ -241,7 +241,7 @@ export async function parseAnnotationHTML(
|
||||||
const html = serializeAnnotations(
|
const html = serializeAnnotations(
|
||||||
annotationJSONList as Required<CustomAnnotationJSON>[],
|
annotationJSONList as Required<CustomAnnotationJSON>[],
|
||||||
false,
|
false,
|
||||||
options.skipCitation
|
options.skipCitation,
|
||||||
).html;
|
).html;
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export async function parseCitationHTML(citationIds: number[]) {
|
export async function parseCitationHTML(citationIds: number[]) {
|
||||||
let html = "";
|
let html = "";
|
||||||
let items = await Zotero.Items.getAsync(citationIds);
|
const items = await Zotero.Items.getAsync(citationIds);
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
if (
|
if (
|
||||||
item.isNote() &&
|
item.isNote() &&
|
||||||
!(await Zotero.Notes.ensureEmbeddedImagesAreAvailable(item)) &&
|
!(await Zotero.Notes.ensureEmbeddedImagesAreAvailable(item)) &&
|
||||||
|
|
@ -11,11 +11,11 @@ export async function parseCitationHTML(citationIds: number[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
if (item.isRegularItem()) {
|
if (item.isRegularItem()) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let itemData = Zotero.Utilities.Item.itemToCSLJSON(item);
|
const itemData = Zotero.Utilities.Item.itemToCSLJSON(item);
|
||||||
let citation = {
|
const citation = {
|
||||||
citationItems: [
|
citationItems: [
|
||||||
{
|
{
|
||||||
uris: [Zotero.URI.getItemURI(item)],
|
uris: [Zotero.URI.getItemURI(item)],
|
||||||
|
|
@ -24,9 +24,9 @@ export async function parseCitationHTML(citationIds: number[]) {
|
||||||
],
|
],
|
||||||
properties: {},
|
properties: {},
|
||||||
};
|
};
|
||||||
let formatted = Zotero.EditorInstanceUtilities.formatCitation(citation);
|
const formatted = Zotero.EditorInstanceUtilities.formatCitation(citation);
|
||||||
html += `<p><span class="citation" data-citation="${encodeURIComponent(
|
html += `<p><span class="citation" data-citation="${encodeURIComponent(
|
||||||
JSON.stringify(citation)
|
JSON.stringify(citation),
|
||||||
)}">${formatted}</span></p>`;
|
)}">${formatted}</span></p>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ function insert(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
content: string = "",
|
content: string = "",
|
||||||
position: number | "end" | "start" | "cursor" = "cursor",
|
position: number | "end" | "start" | "cursor" = "cursor",
|
||||||
select?: boolean
|
select?: boolean,
|
||||||
) {
|
) {
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
const EditorAPI = getEditorAPI(editor);
|
const EditorAPI = getEditorAPI(editor);
|
||||||
|
|
@ -45,7 +45,7 @@ function insert(
|
||||||
EditorAPI.refocusEditor(() => {
|
EditorAPI.refocusEditor(() => {
|
||||||
EditorAPI.setSelection(
|
EditorAPI.setSelection(
|
||||||
(position as number) + slice.content.size,
|
(position as number) + slice.content.size,
|
||||||
position as number
|
position as number,
|
||||||
)(core.view.state, core.view.dispatch);
|
)(core.view.state, core.view.dispatch);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +61,7 @@ function move(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
from: number,
|
from: number,
|
||||||
to: number,
|
to: number,
|
||||||
delta: number
|
delta: number,
|
||||||
) {
|
) {
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
const EditorAPI = getEditorAPI(editor);
|
const EditorAPI = getEditorAPI(editor);
|
||||||
|
|
@ -106,7 +106,7 @@ function replace(
|
||||||
| "link"
|
| "link"
|
||||||
| "code",
|
| "code",
|
||||||
markAttrs: Record<string, any>,
|
markAttrs: Record<string, any>,
|
||||||
select?: boolean
|
select?: boolean,
|
||||||
) {
|
) {
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
const EditorAPI = getEditorAPI(editor);
|
const EditorAPI = getEditorAPI(editor);
|
||||||
|
|
@ -119,7 +119,7 @@ function replace(
|
||||||
JSON.stringify(nodeAttrs),
|
JSON.stringify(nodeAttrs),
|
||||||
schema.marks[markTypeName],
|
schema.marks[markTypeName],
|
||||||
JSON.stringify(markAttrs),
|
JSON.stringify(markAttrs),
|
||||||
select
|
select,
|
||||||
)(core.view.state, core.view.dispatch);
|
)(core.view.state, core.view.dispatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ function scroll(editor: Zotero.EditorInstance, lineIndex: number) {
|
||||||
function getEditorInstance(noteId: number) {
|
function getEditorInstance(noteId: number) {
|
||||||
const editor = Zotero.Notes._editorInstances.find(
|
const editor = Zotero.Notes._editorInstances.find(
|
||||||
(e) =>
|
(e) =>
|
||||||
e._item.id === noteId && !Components.utils.isDeadWrapper(e._iframeWindow)
|
e._item.id === noteId && !Components.utils.isDeadWrapper(e._iframeWindow),
|
||||||
);
|
);
|
||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +179,7 @@ function getLineAtCursor(editor: Zotero.EditorInstance) {
|
||||||
|
|
||||||
function getDOMAtLine(
|
function getDOMAtLine(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
lineIndex: number
|
lineIndex: number,
|
||||||
): HTMLElement {
|
): HTMLElement {
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
const lineNodeDesc =
|
const lineNodeDesc =
|
||||||
|
|
@ -192,7 +192,7 @@ function getDOMAtLine(
|
||||||
function getPositionAtLine(
|
function getPositionAtLine(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
lineIndex: number,
|
lineIndex: number,
|
||||||
type: "start" | "end" = "end"
|
type: "start" | "end" = "end",
|
||||||
): number {
|
): number {
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
const lineNodeDesc =
|
const lineNodeDesc =
|
||||||
|
|
@ -204,8 +204,8 @@ function getPositionAtLine(
|
||||||
0,
|
0,
|
||||||
Math.min(
|
Math.min(
|
||||||
type === "end" ? linePos + lineNodeDesc.size - 1 : linePos - 1,
|
type === "end" ? linePos + lineNodeDesc.size - 1 : linePos - 1,
|
||||||
core.view.state.tr.doc.content.size
|
core.view.state.tr.doc.content.size,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,12 +217,12 @@ function getURLAtCursor(editor: Zotero.EditorInstance) {
|
||||||
function updateURLAtCursor(
|
function updateURLAtCursor(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
text: string | undefined,
|
text: string | undefined,
|
||||||
url: string
|
url: string,
|
||||||
) {
|
) {
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
const EditorAPI = getEditorAPI(editor);
|
const EditorAPI = getEditorAPI(editor);
|
||||||
let from = core.view.state.selection.from;
|
const from = core.view.state.selection.from;
|
||||||
let to = core.view.state.selection.to;
|
const to = core.view.state.selection.to;
|
||||||
const schema = core.view.state.schema;
|
const schema = core.view.state.schema;
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -232,13 +232,13 @@ function updateURLAtCursor(
|
||||||
text,
|
text,
|
||||||
schema.marks.link,
|
schema.marks.link,
|
||||||
JSON.stringify({ href: url }),
|
JSON.stringify({ href: url }),
|
||||||
schema.marks.link
|
schema.marks.link,
|
||||||
)(core.view.state, core.view.dispatch);
|
)(core.view.state, core.view.dispatch);
|
||||||
EditorAPI.refocusEditor(() => {
|
EditorAPI.refocusEditor(() => {
|
||||||
core.view.dispatch(
|
core.view.dispatch(
|
||||||
core.view.state.tr.setSelection(
|
core.view.state.tr.setSelection(
|
||||||
TextSelection.create(core.view.state.tr.doc, from, to)
|
TextSelection.create(core.view.state.tr.doc, from, to),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +246,7 @@ function updateURLAtCursor(
|
||||||
function updateHeadingTextAtLine(
|
function updateHeadingTextAtLine(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
lineIndex: number,
|
lineIndex: number,
|
||||||
text: string
|
text: string,
|
||||||
) {
|
) {
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
const schema = core.view.state.schema;
|
const schema = core.view.state.schema;
|
||||||
|
|
@ -260,13 +260,13 @@ function updateHeadingTextAtLine(
|
||||||
to,
|
to,
|
||||||
text,
|
text,
|
||||||
schema.nodes.heading,
|
schema.nodes.heading,
|
||||||
JSON.stringify({ level })
|
JSON.stringify({ level }),
|
||||||
)(core.view.state, core.view.dispatch);
|
)(core.view.state, core.view.dispatch);
|
||||||
EditorAPI.refocusEditor(() => {
|
EditorAPI.refocusEditor(() => {
|
||||||
core.view.dispatch(
|
core.view.dispatch(
|
||||||
core.view.state.tr.setSelection(
|
core.view.state.tr.setSelection(
|
||||||
TextSelection.create(core.view.state.tr.doc, from, from + text.length)
|
TextSelection.create(core.view.state.tr.doc, from, from + text.length),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +280,7 @@ function isImageAtCursor(editor: Zotero.EditorInstance) {
|
||||||
|
|
||||||
function updateImageDimensionsAtCursor(
|
function updateImageDimensionsAtCursor(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
width: number
|
width: number,
|
||||||
) {
|
) {
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
const EditorAPI = getEditorAPI(editor);
|
const EditorAPI = getEditorAPI(editor);
|
||||||
|
|
@ -290,7 +290,7 @@ function updateImageDimensionsAtCursor(
|
||||||
width,
|
width,
|
||||||
undefined,
|
undefined,
|
||||||
core.view.state,
|
core.view.state,
|
||||||
core.view.dispatch
|
core.view.dispatch,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,7 +298,7 @@ function moveLines(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
fromIndex: number,
|
fromIndex: number,
|
||||||
toIndex: number,
|
toIndex: number,
|
||||||
targetIndex: number
|
targetIndex: number,
|
||||||
) {
|
) {
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
const EditorAPI = getEditorAPI(editor);
|
const EditorAPI = getEditorAPI(editor);
|
||||||
|
|
@ -317,8 +317,12 @@ function moveLines(
|
||||||
EditorAPI.refocusEditor(() => {
|
EditorAPI.refocusEditor(() => {
|
||||||
core.view.dispatch(
|
core.view.dispatch(
|
||||||
core.view.state.tr.setSelection(
|
core.view.state.tr.setSelection(
|
||||||
TextSelection.create(core.view.state.tr.doc, target, target + to - from)
|
TextSelection.create(
|
||||||
)
|
core.view.state.tr.doc,
|
||||||
|
target,
|
||||||
|
target + to - from,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -327,7 +331,7 @@ function moveHeading(
|
||||||
editor: Zotero.EditorInstance | undefined,
|
editor: Zotero.EditorInstance | undefined,
|
||||||
currentNode: TreeModel.Node<NoteNodeData>,
|
currentNode: TreeModel.Node<NoteNodeData>,
|
||||||
targetNode: TreeModel.Node<NoteNodeData>,
|
targetNode: TreeModel.Node<NoteNodeData>,
|
||||||
as: "child" | "before" | "after"
|
as: "child" | "before" | "after",
|
||||||
) {
|
) {
|
||||||
if (!editor || targetNode.getPath().indexOf(currentNode) >= 0) {
|
if (!editor || targetNode.getPath().indexOf(currentNode) >= 0) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -359,13 +363,13 @@ function moveHeading(
|
||||||
|
|
||||||
const fromIndex = currentNode.model.lineIndex;
|
const fromIndex = currentNode.model.lineIndex;
|
||||||
const toIndex = currentNode.model.endIndex;
|
const toIndex = currentNode.model.endIndex;
|
||||||
let levelChange = targetLevel - currentNode.model.level;
|
const levelChange = targetLevel - currentNode.model.level;
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
const EditorAPI = getEditorAPI(editor);
|
const EditorAPI = getEditorAPI(editor);
|
||||||
EditorAPI.updateHeadingsInRange(
|
EditorAPI.updateHeadingsInRange(
|
||||||
getPositionAtLine(editor, fromIndex, "start"),
|
getPositionAtLine(editor, fromIndex, "start"),
|
||||||
getPositionAtLine(editor, toIndex, "end"),
|
getPositionAtLine(editor, toIndex, "end"),
|
||||||
levelChange
|
levelChange,
|
||||||
)(core.view.state, core.view.dispatch);
|
)(core.view.state, core.view.dispatch);
|
||||||
moveLines(editor, fromIndex, toIndex, targetIndex);
|
moveLines(editor, fromIndex, toIndex, targetIndex);
|
||||||
}
|
}
|
||||||
|
|
@ -373,7 +377,7 @@ function moveHeading(
|
||||||
function getTextBetween(
|
function getTextBetween(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
from: number,
|
from: number,
|
||||||
to: number
|
to: number,
|
||||||
) {
|
) {
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
return core.view.state.doc.textBetween(from, to);
|
return core.view.state.doc.textBetween(from, to);
|
||||||
|
|
@ -382,7 +386,7 @@ function getTextBetween(
|
||||||
function getTextBetweenLines(
|
function getTextBetweenLines(
|
||||||
editor: Zotero.EditorInstance,
|
editor: Zotero.EditorInstance,
|
||||||
fromIndex: number,
|
fromIndex: number,
|
||||||
toIndex: number
|
toIndex: number,
|
||||||
) {
|
) {
|
||||||
const core = getEditorCore(editor);
|
const core = getEditorCore(editor);
|
||||||
const from = getPositionAtLine(editor, fromIndex, "start");
|
const from = getPositionAtLine(editor, fromIndex, "start");
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ function showHint(text: string) {
|
||||||
async function showHintWithLink(
|
async function showHintWithLink(
|
||||||
text: string,
|
text: string,
|
||||||
linkText: string,
|
linkText: string,
|
||||||
linkCallback: (ev: MouseEvent) => any
|
linkCallback: (ev: MouseEvent) => any,
|
||||||
) {
|
) {
|
||||||
const progress = new ztoolkit.ProgressWindow(PROGRESS_TITLE)
|
const progress = new ztoolkit.ProgressWindow(PROGRESS_TITLE)
|
||||||
.createLine({ text, progress: 100, type: "default" })
|
.createLine({ text, progress: 100, type: "default" })
|
||||||
|
|
@ -20,7 +20,7 @@ async function showHintWithLink(
|
||||||
|
|
||||||
await waitUtilAsync(() =>
|
await waitUtilAsync(() =>
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
Boolean(progress.lines && progress.lines[0]._itemText)
|
Boolean(progress.lines && progress.lines[0]._itemText),
|
||||||
);
|
);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
progress.lines[0]._hbox.ownerDocument
|
progress.lines[0]._hbox.ownerDocument
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export async function itemPicker() {
|
||||||
: "chrome://zotero/content/selectItemsDialog.xul",
|
: "chrome://zotero/content/selectItemsDialog.xul",
|
||||||
"",
|
"",
|
||||||
"chrome,dialog=no,centerscreen,resizable=yes",
|
"chrome,dialog=no,centerscreen,resizable=yes",
|
||||||
io
|
io,
|
||||||
);
|
);
|
||||||
await io.deferred.promise;
|
await io.deferred.promise;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export function getNoteLinkParams(link: string) {
|
||||||
link,
|
link,
|
||||||
libraryID: -1,
|
libraryID: -1,
|
||||||
noteKey: undefined,
|
noteKey: undefined,
|
||||||
noteItem: false as false,
|
noteItem: false as const,
|
||||||
ignore: null,
|
ignore: null,
|
||||||
lineIndex: null,
|
lineIndex: null,
|
||||||
};
|
};
|
||||||
|
|
@ -38,7 +38,7 @@ export function getNoteLink(
|
||||||
options: {
|
options: {
|
||||||
ignore?: boolean | null;
|
ignore?: boolean | null;
|
||||||
lineIndex?: number | null;
|
lineIndex?: number | null;
|
||||||
} = {}
|
} = {},
|
||||||
) {
|
) {
|
||||||
const libraryID = noteItem.libraryID;
|
const libraryID = noteItem.libraryID;
|
||||||
const library = Zotero.Libraries.get(libraryID);
|
const library = Zotero.Libraries.get(libraryID);
|
||||||
|
|
@ -77,7 +77,7 @@ export function getNoteLink(
|
||||||
|
|
||||||
export function getLinkedNotesRecursively(
|
export function getLinkedNotesRecursively(
|
||||||
link: string,
|
link: string,
|
||||||
ignoreIds: number[] = []
|
ignoreIds: number[] = [],
|
||||||
) {
|
) {
|
||||||
const linkParams = getNoteLinkParams(link);
|
const linkParams = getNoteLinkParams(link);
|
||||||
if (!linkParams.noteItem) return [];
|
if (!linkParams.noteItem) return [];
|
||||||
|
|
@ -98,6 +98,6 @@ export function getLinkedNotesRecursively(
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
[linkParams.noteItem.id] as number[]
|
[linkParams.noteItem.id] as number[],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,22 +39,20 @@ function initLocale() {
|
||||||
* getString("addon-dynamic-example", { args: { count: 2 } }); // I have 2 apples
|
* getString("addon-dynamic-example", { args: { count: 2 } }); // I have 2 apples
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
function getString(localeString: string): string;
|
function getString(localString: string): string;
|
||||||
function getString(localeString: string, branch: string): string;
|
function getString(localString: string, branch: string): string;
|
||||||
function getString(
|
function getString(
|
||||||
localeString: string,
|
localeString: string,
|
||||||
options: { branch?: string | undefined; args?: Record<string, unknown> }
|
options: { branch?: string | undefined; args?: Record<string, unknown> },
|
||||||
): string;
|
): string;
|
||||||
function getString(...inputs: any[]) {
|
function getString(...inputs: any[]) {
|
||||||
// Old .properties uses . while .ftl uses -
|
|
||||||
const localeString = (inputs[0] as string).replace(/\./g, "-");
|
|
||||||
if (inputs.length === 1) {
|
if (inputs.length === 1) {
|
||||||
return _getString(localeString);
|
return _getString(inputs[0]);
|
||||||
} else if (inputs.length === 2) {
|
} else if (inputs.length === 2) {
|
||||||
if (typeof inputs[1] === "string") {
|
if (typeof inputs[1] === "string") {
|
||||||
return _getString(localeString, { branch: inputs[1] });
|
return _getString(inputs[0], { branch: inputs[1] });
|
||||||
} else {
|
} else {
|
||||||
return _getString(localeString, inputs[1]);
|
return _getString(inputs[0], inputs[1]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid arguments");
|
throw new Error("Invalid arguments");
|
||||||
|
|
@ -63,18 +61,30 @@ function getString(...inputs: any[]) {
|
||||||
|
|
||||||
function _getString(
|
function _getString(
|
||||||
localeString: string,
|
localeString: string,
|
||||||
options: { branch?: string | undefined; args?: Record<string, unknown> } = {}
|
options: { branch?: string | undefined; args?: Record<string, unknown> } = {},
|
||||||
): string {
|
): string {
|
||||||
|
switch (localeString) {
|
||||||
|
case "alt":
|
||||||
|
return Zotero.isMac ? "⌥" : "Alt";
|
||||||
|
case "ctrl":
|
||||||
|
return Zotero.isMac ? "⌘" : "Ctrl";
|
||||||
|
case "shift":
|
||||||
|
return Zotero.isMac ? "⇧" : "Shift";
|
||||||
|
}
|
||||||
|
const localStringWithPrefix = `${config.addonRef}-${localeString.replace(
|
||||||
|
/\./g,
|
||||||
|
"-",
|
||||||
|
)}`;
|
||||||
const { branch, args } = options;
|
const { branch, args } = options;
|
||||||
const pattern = addon.data.locale?.current.formatMessagesSync([
|
const pattern = addon.data.locale?.current.formatMessagesSync([
|
||||||
{ id: localeString, args },
|
{ id: localStringWithPrefix, args },
|
||||||
])[0];
|
])[0];
|
||||||
if (!pattern) {
|
if (!pattern) {
|
||||||
return localeString;
|
return localStringWithPrefix;
|
||||||
}
|
}
|
||||||
if (branch && pattern.attributes) {
|
if (branch && pattern.attributes) {
|
||||||
return pattern.attributes[branch] || localeString;
|
return pattern.attributes[branch] || localStringWithPrefix;
|
||||||
} else {
|
} else {
|
||||||
return pattern.value || localeString;
|
return pattern.value || localStringWithPrefix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ function parseHTMLLines(html: string): string[] {
|
||||||
html = html.replace(/<div[^>]*data-schema-version[^>]*>/, "");
|
html = html.replace(/<div[^>]*data-schema-version[^>]*>/, "");
|
||||||
html = html.replace(/<\/div>/, "");
|
html = html.replace(/<\/div>/, "");
|
||||||
}
|
}
|
||||||
let noteLines = html.split("\n").filter((e) => e);
|
const noteLines = html.split("\n").filter((e) => e);
|
||||||
|
|
||||||
// A cache for temporarily stored lines
|
// A cache for temporarily stored lines
|
||||||
let previousLineCache = [];
|
let previousLineCache = [];
|
||||||
|
|
@ -33,12 +33,12 @@ function parseHTMLLines(html: string): string[] {
|
||||||
|
|
||||||
const forceInline = ["table", "blockquote", "pre", "ol", "ul"];
|
const forceInline = ["table", "blockquote", "pre", "ol", "ul"];
|
||||||
const selfInline: string[] = [];
|
const selfInline: string[] = [];
|
||||||
let forceInlineStack = [];
|
const forceInlineStack = [];
|
||||||
let forceInlineFlag = false;
|
let forceInlineFlag = false;
|
||||||
let selfInlineFlag = false;
|
let selfInlineFlag = false;
|
||||||
|
|
||||||
const parsedLines = [];
|
const parsedLines = [];
|
||||||
for (let line of noteLines) {
|
for (const line of noteLines) {
|
||||||
// restore self inline flag
|
// restore self inline flag
|
||||||
selfInlineFlag = false;
|
selfInlineFlag = false;
|
||||||
|
|
||||||
|
|
@ -88,7 +88,7 @@ function parseHTMLLines(html: string): string[] {
|
||||||
// Append cache to previous line
|
// Append cache to previous line
|
||||||
if (previousLineCache.length) {
|
if (previousLineCache.length) {
|
||||||
parsedLines[parsedLines.length - 1] += `\n${previousLineCache.join(
|
parsedLines[parsedLines.length - 1] += `\n${previousLineCache.join(
|
||||||
"\n"
|
"\n",
|
||||||
)}`;
|
)}`;
|
||||||
previousLineCache = [];
|
previousLineCache = [];
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +113,7 @@ function getLinesInNote(note: Zotero.Item): string[] {
|
||||||
if (!note) {
|
if (!note) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
let noteText: string = note.getNote();
|
const noteText: string = note.getNote();
|
||||||
return parseHTMLLines(noteText);
|
return parseHTMLLines(noteText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,20 +121,20 @@ async function setLinesToNote(note: Zotero.Item, lines: string[]) {
|
||||||
if (!note) {
|
if (!note) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
let noteText: string = note.getNote();
|
const noteText: string = note.getNote();
|
||||||
let containerIndex = noteText.search(/data-schema-version="[0-9]*/g);
|
const containerIndex = noteText.search(/data-schema-version="[0-9]*/g);
|
||||||
if (containerIndex === -1) {
|
if (containerIndex === -1) {
|
||||||
note.setNote(
|
note.setNote(
|
||||||
`<div data-schema-version="${config.dataSchemaVersion}">${lines.join(
|
`<div data-schema-version="${config.dataSchemaVersion}">${lines.join(
|
||||||
"\n"
|
"\n",
|
||||||
)}</div>`
|
)}</div>`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let noteHead = noteText.substring(0, containerIndex);
|
const noteHead = noteText.substring(0, containerIndex);
|
||||||
note.setNote(
|
note.setNote(
|
||||||
`${noteHead}data-schema-version="${
|
`${noteHead}data-schema-version="${
|
||||||
config.dataSchemaVersion
|
config.dataSchemaVersion
|
||||||
}">${lines.join("\n")}</div>`
|
}">${lines.join("\n")}</div>`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,12 +145,12 @@ async function addLineToNote(
|
||||||
note: Zotero.Item,
|
note: Zotero.Item,
|
||||||
html: string,
|
html: string,
|
||||||
lineIndex: number = -1,
|
lineIndex: number = -1,
|
||||||
forceMetadata: boolean = false
|
forceMetadata: boolean = false,
|
||||||
) {
|
) {
|
||||||
if (!note || !html) {
|
if (!note || !html) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let noteLines = getLinesInNote(note);
|
const noteLines = getLinesInNote(note);
|
||||||
if (lineIndex < 0 || lineIndex >= noteLines.length) {
|
if (lineIndex < 0 || lineIndex >= noteLines.length) {
|
||||||
lineIndex = noteLines.length;
|
lineIndex = noteLines.length;
|
||||||
}
|
}
|
||||||
|
|
@ -174,12 +174,12 @@ async function addLineToNote(
|
||||||
|
|
||||||
async function renderNoteHTML(
|
async function renderNoteHTML(
|
||||||
html: string,
|
html: string,
|
||||||
refNotes: Zotero.Item[]
|
refNotes: Zotero.Item[],
|
||||||
): Promise<string>;
|
): Promise<string>;
|
||||||
async function renderNoteHTML(noteItem: Zotero.Item): Promise<string>;
|
async function renderNoteHTML(noteItem: Zotero.Item): Promise<string>;
|
||||||
async function renderNoteHTML(
|
async function renderNoteHTML(
|
||||||
htmlOrNote: string | Zotero.Item,
|
htmlOrNote: string | Zotero.Item,
|
||||||
refNotes?: Zotero.Item[]
|
refNotes?: Zotero.Item[],
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
let html: string;
|
let html: string;
|
||||||
if (typeof htmlOrNote === "string") {
|
if (typeof htmlOrNote === "string") {
|
||||||
|
|
@ -195,30 +195,32 @@ async function renderNoteHTML(
|
||||||
}
|
}
|
||||||
|
|
||||||
const parser = ztoolkit.getDOMParser();
|
const parser = ztoolkit.getDOMParser();
|
||||||
let doc = parser.parseFromString(html, "text/html");
|
const doc = parser.parseFromString(html, "text/html");
|
||||||
const imageAttachments = refNotes.reduce((acc, note) => {
|
const imageAttachments = refNotes.reduce((acc, note) => {
|
||||||
acc.push(...Zotero.Items.get(note.getAttachments()));
|
acc.push(...Zotero.Items.get(note.getAttachments()));
|
||||||
return acc;
|
return acc;
|
||||||
}, [] as Zotero.Item[]);
|
}, [] as Zotero.Item[]);
|
||||||
|
|
||||||
for (let attachment of imageAttachments) {
|
for (const attachment of imageAttachments) {
|
||||||
if (await attachment.fileExists()) {
|
if (await attachment.fileExists()) {
|
||||||
let imageNodes = Array.from(
|
const imageNodes = Array.from(
|
||||||
doc.querySelectorAll(`img[data-attachment-key="${attachment.key}"]`)
|
doc.querySelectorAll(`img[data-attachment-key="${attachment.key}"]`),
|
||||||
);
|
);
|
||||||
if (imageNodes.length) {
|
if (imageNodes.length) {
|
||||||
try {
|
try {
|
||||||
const b64 = await getItemDataURL(attachment);
|
const b64 = await getItemDataURL(attachment);
|
||||||
imageNodes.forEach((node) => node.setAttribute("src", b64));
|
imageNodes.forEach((node) => node.setAttribute("src", b64));
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
ztoolkit.log(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bgNodes = doc.querySelectorAll(
|
const bgNodes = doc.querySelectorAll(
|
||||||
"span[style]"
|
"span[style]",
|
||||||
) as NodeListOf<HTMLElement>;
|
) as NodeListOf<HTMLElement>;
|
||||||
for (let node of bgNodes) {
|
for (const node of bgNodes) {
|
||||||
// Browser converts #RRGGBBAA hex color to rgba function, and we convert it to rgb function,
|
// Browser converts #RRGGBBAA hex color to rgba function, and we convert it to rgb function,
|
||||||
// because word processors don't understand colors with alpha channel
|
// because word processors don't understand colors with alpha channel
|
||||||
if (
|
if (
|
||||||
|
|
@ -240,7 +242,7 @@ async function renderNoteHTML(
|
||||||
node.innerHTML.replace(mathDelimiterRegex, ""),
|
node.innerHTML.replace(mathDelimiterRegex, ""),
|
||||||
{
|
{
|
||||||
throwOnError: false,
|
throwOnError: false,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return doc.body.innerHTML;
|
return doc.body.innerHTML;
|
||||||
|
|
@ -258,12 +260,12 @@ function getNoteType(id: number) {
|
||||||
|
|
||||||
function getNoteTree(
|
function getNoteTree(
|
||||||
note: Zotero.Item,
|
note: Zotero.Item,
|
||||||
parseLink: boolean = true
|
parseLink: boolean = true,
|
||||||
): TreeModel.Node<NoteNodeData> {
|
): TreeModel.Node<NoteNodeData> {
|
||||||
const noteLines = getLinesInNote(note);
|
const noteLines = getLinesInNote(note);
|
||||||
const parser = ztoolkit.getDOMParser();
|
const parser = ztoolkit.getDOMParser();
|
||||||
let tree = new TreeModel();
|
const tree = new TreeModel();
|
||||||
let root = tree.parse({
|
const root = tree.parse({
|
||||||
id: -1,
|
id: -1,
|
||||||
level: 0,
|
level: 0,
|
||||||
lineIndex: -1,
|
lineIndex: -1,
|
||||||
|
|
@ -273,9 +275,9 @@ function getNoteTree(
|
||||||
let lastNode = root;
|
let lastNode = root;
|
||||||
const headingRegex = new RegExp("^<h([1-6])(.*?)</h[1-6]>");
|
const headingRegex = new RegExp("^<h([1-6])(.*?)</h[1-6]>");
|
||||||
const linkRegex = new RegExp('href="(zotero://note/[^"]*)"');
|
const linkRegex = new RegExp('href="(zotero://note/[^"]*)"');
|
||||||
for (let i in noteLines) {
|
for (const i in noteLines) {
|
||||||
let currentLevel = 7;
|
let currentLevel = 7;
|
||||||
let lineElement = noteLines[i];
|
const lineElement = noteLines[i];
|
||||||
const matchHeadingResult = lineElement.match(headingRegex);
|
const matchHeadingResult = lineElement.match(headingRegex);
|
||||||
const matchLinkResult = parseLink ? lineElement.match(linkRegex) : null;
|
const matchLinkResult = parseLink ? lineElement.match(linkRegex) : null;
|
||||||
const isHeading = Boolean(matchHeadingResult);
|
const isHeading = Boolean(matchHeadingResult);
|
||||||
|
|
@ -333,7 +335,7 @@ function getNoteTreeFlattened(
|
||||||
keepRoot?: boolean;
|
keepRoot?: boolean;
|
||||||
keepLink?: boolean;
|
keepLink?: boolean;
|
||||||
customFilter?: (node: TreeModel.Node<NoteNodeData>) => boolean;
|
customFilter?: (node: TreeModel.Node<NoteNodeData>) => boolean;
|
||||||
} = { keepRoot: false, keepLink: false }
|
} = { keepRoot: false, keepLink: false },
|
||||||
): TreeModel.Node<NoteNodeData>[] {
|
): TreeModel.Node<NoteNodeData>[] {
|
||||||
if (!note) {
|
if (!note) {
|
||||||
return [];
|
return [];
|
||||||
|
|
@ -342,14 +344,14 @@ function getNoteTreeFlattened(
|
||||||
(node) =>
|
(node) =>
|
||||||
(options.keepRoot || node.model.lineIndex >= 0) &&
|
(options.keepRoot || node.model.lineIndex >= 0) &&
|
||||||
(options.keepLink || node.model.level <= 6) &&
|
(options.keepLink || node.model.level <= 6) &&
|
||||||
(options.customFilter ? options.customFilter(node) : true)
|
(options.customFilter ? options.customFilter(node) : true),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNoteTreeNodeById(
|
function getNoteTreeNodeById(
|
||||||
note: Zotero.Item,
|
note: Zotero.Item,
|
||||||
id: number,
|
id: number,
|
||||||
root: TreeModel.Node<NoteNodeData> | undefined = undefined
|
root: TreeModel.Node<NoteNodeData> | undefined = undefined,
|
||||||
) {
|
) {
|
||||||
root = root || getNoteTree(note);
|
root = root || getNoteTree(note);
|
||||||
return root.first(function (node) {
|
return root.first(function (node) {
|
||||||
|
|
@ -360,7 +362,7 @@ function getNoteTreeNodeById(
|
||||||
function getNoteTreeNodesByLevel(
|
function getNoteTreeNodesByLevel(
|
||||||
note: Zotero.Item,
|
note: Zotero.Item,
|
||||||
level: number,
|
level: number,
|
||||||
root: TreeModel.Node<NoteNodeData> | undefined = undefined
|
root: TreeModel.Node<NoteNodeData> | undefined = undefined,
|
||||||
) {
|
) {
|
||||||
root = root || getNoteTree(note);
|
root = root || getNoteTree(note);
|
||||||
return root.all(function (node) {
|
return root.all(function (node) {
|
||||||
|
|
@ -370,7 +372,7 @@ function getNoteTreeNodesByLevel(
|
||||||
|
|
||||||
async function copyEmbeddedImagesFromNote(
|
async function copyEmbeddedImagesFromNote(
|
||||||
targetNote: Zotero.Item,
|
targetNote: Zotero.Item,
|
||||||
sourceNotes: Zotero.Item[]
|
sourceNotes: Zotero.Item[],
|
||||||
) {
|
) {
|
||||||
await Zotero.DB.executeTransaction(async () => {
|
await Zotero.DB.executeTransaction(async () => {
|
||||||
for (const fromNote of sourceNotes) {
|
for (const fromNote of sourceNotes) {
|
||||||
|
|
@ -382,9 +384,9 @@ async function copyEmbeddedImagesFromNote(
|
||||||
async function copyEmbeddedImagesInHTML(
|
async function copyEmbeddedImagesInHTML(
|
||||||
html: string,
|
html: string,
|
||||||
targetNote?: Zotero.Item,
|
targetNote?: Zotero.Item,
|
||||||
refNotes: Zotero.Item[] = []
|
refNotes: Zotero.Item[] = [],
|
||||||
) {
|
) {
|
||||||
ztoolkit.log("parseEmbeddedImagesInHTML", arguments);
|
ztoolkit.log("parseEmbeddedImagesInHTML", html, targetNote, refNotes);
|
||||||
if (!targetNote) {
|
if (!targetNote) {
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
@ -399,13 +401,13 @@ async function copyEmbeddedImagesInHTML(
|
||||||
|
|
||||||
ztoolkit.log(attachments);
|
ztoolkit.log(attachments);
|
||||||
|
|
||||||
let doc = ztoolkit.getDOMParser().parseFromString(html, "text/html");
|
const doc = ztoolkit.getDOMParser().parseFromString(html, "text/html");
|
||||||
|
|
||||||
// Copy note image attachments and replace keys in the new note
|
// Copy note image attachments and replace keys in the new note
|
||||||
for (let attachment of attachments) {
|
for (const attachment of attachments) {
|
||||||
if (await attachment.fileExists()) {
|
if (await attachment.fileExists()) {
|
||||||
let nodes = Array.from(
|
const nodes = Array.from(
|
||||||
doc.querySelectorAll(`img[data-attachment-key="${attachment.key}"]`)
|
doc.querySelectorAll(`img[data-attachment-key="${attachment.key}"]`),
|
||||||
);
|
);
|
||||||
if (nodes.length) {
|
if (nodes.length) {
|
||||||
let copiedAttachment: Zotero.Item;
|
let copiedAttachment: Zotero.Item;
|
||||||
|
|
@ -417,7 +419,7 @@ async function copyEmbeddedImagesInHTML(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
nodes.forEach((node) =>
|
nodes.forEach((node) =>
|
||||||
node.setAttribute("data-attachment-key", copiedAttachment.key)
|
node.setAttribute("data-attachment-key", copiedAttachment.key),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -427,16 +429,16 @@ async function copyEmbeddedImagesInHTML(
|
||||||
}
|
}
|
||||||
|
|
||||||
function dataURLtoBlob(dataurl: string) {
|
function dataURLtoBlob(dataurl: string) {
|
||||||
let parts = dataurl.split(",");
|
const parts = dataurl.split(",");
|
||||||
let matches = parts[0]?.match(/:(.*?);/);
|
const matches = parts[0]?.match(/:(.*?);/);
|
||||||
if (!matches || !matches[1]) {
|
if (!matches || !matches[1]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mime = matches[1];
|
const mime = matches[1];
|
||||||
if (parts[0].indexOf("base64") !== -1) {
|
if (parts[0].indexOf("base64") !== -1) {
|
||||||
let bstr = ztoolkit.getGlobal("atob")(parts[1]);
|
const bstr = ztoolkit.getGlobal("atob")(parts[1]);
|
||||||
let n = bstr.length;
|
let n = bstr.length;
|
||||||
let u8arr = new Uint8Array(n);
|
const u8arr = new Uint8Array(n);
|
||||||
while (n--) {
|
while (n--) {
|
||||||
u8arr[n] = bstr.charCodeAt(n);
|
u8arr[n] = bstr.charCodeAt(n);
|
||||||
}
|
}
|
||||||
|
|
@ -451,7 +453,7 @@ function dataURLtoBlob(dataurl: string) {
|
||||||
async function importImageToNote(
|
async function importImageToNote(
|
||||||
note: Zotero.Item,
|
note: Zotero.Item,
|
||||||
src: string,
|
src: string,
|
||||||
type: "b64" | "url" | "file" = "b64"
|
type: "b64" | "url" | "file" = "b64",
|
||||||
): Promise<string | void> {
|
): Promise<string | void> {
|
||||||
if (!note || !note.isNote()) {
|
if (!note || !note.isNote()) {
|
||||||
return "";
|
return "";
|
||||||
|
|
@ -474,7 +476,7 @@ async function importImageToNote(
|
||||||
} else if (type === "file") {
|
} else if (type === "file") {
|
||||||
src = Zotero.File.normalizeToUnix(src);
|
src = Zotero.File.normalizeToUnix(src);
|
||||||
const noteAttachmentKeys = Zotero.Items.get(note.getAttachments()).map(
|
const noteAttachmentKeys = Zotero.Items.get(note.getAttachments()).map(
|
||||||
(_i) => _i.key
|
(_i) => _i.key,
|
||||||
);
|
);
|
||||||
const filename = src.split("/").pop()?.split(".").shift();
|
const filename = src.split("/").pop()?.split(".").shift();
|
||||||
// The exported image is KEY.png by default.
|
// The exported image is KEY.png by default.
|
||||||
|
|
@ -497,7 +499,7 @@ async function importImageToNote(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let attachment = await Zotero.Attachments.importEmbeddedImage({
|
const attachment = await Zotero.Attachments.importEmbeddedImage({
|
||||||
blob,
|
blob,
|
||||||
parentItemID: note.id,
|
parentItemID: note.id,
|
||||||
saveOptions: {},
|
saveOptions: {},
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@ export function fill(
|
||||||
options: { char: string; position: "start" | "end" } = {
|
options: { char: string; position: "start" | "end" } = {
|
||||||
char: " ",
|
char: " ",
|
||||||
position: "end",
|
position: "end",
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
if (str.length >= len) {
|
if (str.length >= len) {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
return str[options.position === "start" ? "padStart" : "padEnd"](
|
return str[options.position === "start" ? "padStart" : "padEnd"](
|
||||||
len - str.length,
|
len - str.length,
|
||||||
options.char
|
options.char,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ export function randomString(len: number, seed?: string, chars?: string) {
|
||||||
len = 8;
|
len = 8;
|
||||||
}
|
}
|
||||||
let str = "";
|
let str = "";
|
||||||
const random: Function = seedrandom(seed);
|
const random = seedrandom(seed);
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
const rnum = Math.floor(random() * chars.length);
|
const rnum = Math.floor(random() * chars.length);
|
||||||
str += chars.substring(rnum, rnum + 1);
|
str += chars.substring(rnum, rnum + 1);
|
||||||
|
|
@ -62,18 +62,19 @@ export function randomString(len: number, seed?: string, chars?: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function arrayBufferToBase64(buffer: ArrayBufferLike) {
|
function arrayBufferToBase64(buffer: ArrayBufferLike) {
|
||||||
var binary = "";
|
let binary = "";
|
||||||
var bytes = new Uint8Array(buffer);
|
const bytes = new Uint8Array(buffer);
|
||||||
var len = bytes.byteLength;
|
const len = bytes.byteLength;
|
||||||
for (var i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
binary += String.fromCharCode(bytes[i]);
|
binary += String.fromCharCode(bytes[i]);
|
||||||
}
|
}
|
||||||
return ztoolkit.getGlobal("btoa")(binary);
|
return ztoolkit.getGlobal("btoa")(binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getItemDataURL(item: Zotero.Item) {
|
export async function getItemDataURL(item: Zotero.Item) {
|
||||||
let path = (await item.getFilePathAsync()) as string;
|
const path = (await item.getFilePathAsync()) as string;
|
||||||
let buf = new Uint8Array((await OS.File.read(path, {})) as Uint8Array).buffer;
|
const buf = new Uint8Array((await OS.File.read(path, {})) as Uint8Array)
|
||||||
|
.buffer;
|
||||||
return (
|
return (
|
||||||
"data:" + item.attachmentContentType + ";base64," + arrayBufferToBase64(buf)
|
"data:" + item.attachmentContentType + ";base64," + arrayBufferToBase64(buf)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ export function waitUntil(
|
||||||
condition: () => boolean,
|
condition: () => boolean,
|
||||||
callback: () => void,
|
callback: () => void,
|
||||||
interval = 100,
|
interval = 100,
|
||||||
timeout = 10000
|
timeout = 10000,
|
||||||
) {
|
) {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
const intervalId = ztoolkit.getGlobal("setInterval")(() => {
|
const intervalId = ztoolkit.getGlobal("setInterval")(() => {
|
||||||
|
|
@ -18,7 +18,7 @@ export function waitUntil(
|
||||||
export function waitUtilAsync(
|
export function waitUtilAsync(
|
||||||
condition: () => boolean,
|
condition: () => boolean,
|
||||||
interval = 100,
|
interval = 100,
|
||||||
timeout = 10000
|
timeout = 10000,
|
||||||
) {
|
) {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ function localeWindow(win: Window) {
|
||||||
const isProp = key in elem;
|
const isProp = key in elem;
|
||||||
try {
|
try {
|
||||||
const localeString = getString(
|
const localeString = getString(
|
||||||
(isProp ? (elem as any)[key] : elem.getAttribute(key)).trim() || ""
|
(isProp ? (elem as any)[key] : elem.getAttribute(key)).trim() || "",
|
||||||
);
|
);
|
||||||
isProp
|
isProp
|
||||||
? ((elem as any)[key] = localeString)
|
? ((elem as any)[key] = localeString)
|
||||||
|
|
@ -26,6 +26,6 @@ function localeWindow(win: Window) {
|
||||||
: elem.setAttribute(key, errorInfo);
|
: elem.setAttribute(key, errorInfo);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import ZoteroToolkit from "zotero-plugin-toolkit";
|
||||||
|
import { config } from "../../package.json";
|
||||||
|
|
||||||
|
export { createZToolkit };
|
||||||
|
|
||||||
|
function createZToolkit() {
|
||||||
|
const _ztoolkit = new ZoteroToolkit();
|
||||||
|
/**
|
||||||
|
* Alternatively, import toolkit modules you use to minify the plugin size.
|
||||||
|
* You can add the modules under the `MyToolkit` class below and uncomment the following line.
|
||||||
|
*/
|
||||||
|
// const _ztoolkit = new MyToolkit();
|
||||||
|
initZToolkit(_ztoolkit);
|
||||||
|
return _ztoolkit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initZToolkit(_ztoolkit: ReturnType<typeof createZToolkit>) {
|
||||||
|
const env = __env__;
|
||||||
|
_ztoolkit.basicOptions.log.prefix = `[${config.addonName}]`;
|
||||||
|
_ztoolkit.basicOptions.log.disableConsole = env === "production";
|
||||||
|
_ztoolkit.UI.basicOptions.ui.enableElementJSONLog = __env__ === "development";
|
||||||
|
_ztoolkit.UI.basicOptions.ui.enableElementDOMLog = __env__ === "development";
|
||||||
|
_ztoolkit.basicOptions.debug.disableDebugBridgePassword =
|
||||||
|
__env__ === "development";
|
||||||
|
_ztoolkit.ProgressWindow.setIconURI(
|
||||||
|
"default",
|
||||||
|
`chrome://${config.addonRef}/content/icons/favicon.png`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
import { BasicTool, unregister } from "zotero-plugin-toolkit/dist/basic";
|
||||||
|
import { UITool } from "zotero-plugin-toolkit/dist/tools/ui";
|
||||||
|
import { PreferencePaneManager } from "zotero-plugin-toolkit/dist/managers/preferencePane";
|
||||||
|
|
||||||
|
class MyToolkit extends BasicTool {
|
||||||
|
UI: UITool;
|
||||||
|
PreferencePane: PreferencePaneManager;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.UI = new UITool(this);
|
||||||
|
this.PreferencePane = new PreferencePaneManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
unregisterAll() {
|
||||||
|
unregister(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,13 +7,6 @@
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true
|
"strict": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src", "typings", "node_modules/zotero-types"],
|
||||||
"src",
|
"exclude": ["build", "addon"]
|
||||||
"typing",
|
}
|
||||||
"node_modules/zotero-types"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"builds",
|
|
||||||
"addon"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/ban-types */
|
||||||
declare interface EditorCore {
|
declare interface EditorCore {
|
||||||
debouncedUpdate: Function;
|
debouncedUpdate: Function;
|
||||||
disableDrag: boolean;
|
disableDrag: boolean;
|
||||||
|
|
@ -7,7 +7,7 @@ declare const _globalThis: {
|
||||||
document: Document;
|
document: Document;
|
||||||
OS: typeof OS;
|
OS: typeof OS;
|
||||||
Blob: typeof Blob;
|
Blob: typeof Blob;
|
||||||
ztoolkit: typeof ztoolkit;
|
ztoolkit: ZToolkit;
|
||||||
addon: typeof addon;
|
addon: typeof addon;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -20,8 +20,11 @@ declare interface Window {
|
||||||
): Window;
|
): Window;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const ztoolkit: import("../src/addon").MyToolkit;
|
declare type ZToolkit = ReturnType<
|
||||||
// declare const ztoolkit: import("zotero-plugin-toolkit").ZoteroToolkit;
|
typeof import("../src/utils/ztoolkit").createZToolkit
|
||||||
|
>;
|
||||||
|
|
||||||
|
declare const ztoolkit: ZToolkit;
|
||||||
|
|
||||||
declare const rootURI: string;
|
declare const rootURI: string;
|
||||||
|
|
||||||
Loading…
Reference in New Issue