update: plugin template

fix: reopen window bug
add: eslint
This commit is contained in:
windingwind 2023-07-26 23:18:30 +08:00
parent 5a4bdc72bf
commit 15d8cce685
73 changed files with 1178 additions and 924 deletions

41
.eslintrc.json Normal file
View File

@ -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
.gitattributes vendored
View File

@ -1 +1,2 @@
addon/chrome/content/lib/** linguist-vendored
* text=auto eol=lf

2
.github/FUNDING.yml vendored
View File

@ -10,4 +10,4 @@ liberapay: windingwind
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
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"]

View File

@ -3,8 +3,7 @@ name: Bug report
about: Create a report to help us improve
title: "[Bug]"
labels: bug
assignees: ''
assignees: ""
---
**Describe the bug**
@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
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.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -3,8 +3,7 @@ name: Feature request
about: Suggest an idea for this project
title: "[Feature]"
labels: enhancement
assignees: ''
assignees: ""
---
**Is your feature request related to a problem? Please describe.**

View File

@ -3,8 +3,7 @@ name: Note Template Issue
about: Need help with your custom note template
title: "[Note Template]"
labels: help wanted
assignees: ''
assignees: ""
---
## Template Type

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
**/builds
**/build
node_modules
package-lock.json
zotero-cmd.json

3
.prettierrc Normal file
View File

@ -0,0 +1,3 @@
{
"tabWidth": 2
}

View File

@ -4,7 +4,7 @@
},
"github": {
"release": true,
"assets": ["builds/*.xpi"]
"assets": ["build/*.xpi"]
},
"hooks": {
"after:bump": "npm run build",

View File

@ -1,45 +1,45 @@
{
"appendElement - full": {
"scope": "javascript,typescript",
"prefix": "appendElement",
"body": [
"appendElement({",
"\ttag: '${1:div}',",
"\tid: '${2:id}',",
"\tnamespace: '${3:html}',",
"\tclassList: ['${4:class}'],",
"\tstyles: {${5:style}: '$6'},",
"\tproperties: {},",
"\tattributes: {},",
"\t[{ '${7:onload}', (e: Event) => $8, ${9:false} }],",
"\tcheckExistanceParent: ${10:HTMLElement},",
"\tignoreIfExists: ${11:true},",
"\tskipIfExists: ${12:true},",
"\tremoveIfExists: ${13:true},",
"\tcustomCheck: (doc: Document, options: ElementOptions) => ${14:true},",
"\tchildren: [$15]",
"}, ${16:container});"
]
},
"appendElement - minimum": {
"scope": "javascript,typescript",
"prefix": "appendElement",
"body": "appendElement({ tag: '$1' }, $2);"
},
"register Notifier": {
"scope": "javascript,typescript",
"prefix": "registerObserver",
"body": [
"registerObserver({",
"\t notify: (",
"\t\tevent: _ZoteroTypes.Notifier.Event,",
"\t\ttype: _ZoteroTypes.Notifier.Type,",
"\t\tids: string[],",
"\t\textraData: _ZoteroTypes.anyObj",
"\t) => {",
"\t\t$0",
"\t}",
"});"
]
}
"appendElement - full": {
"scope": "javascript,typescript",
"prefix": "appendElement",
"body": [
"appendElement({",
"\ttag: '${1:div}',",
"\tid: '${2:id}',",
"\tnamespace: '${3:html}',",
"\tclassList: ['${4:class}'],",
"\tstyles: {${5:style}: '$6'},",
"\tproperties: {},",
"\tattributes: {},",
"\t[{ '${7:onload}', (e: Event) => $8, ${9:false} }],",
"\tcheckExistanceParent: ${10:HTMLElement},",
"\tignoreIfExists: ${11:true},",
"\tskipIfExists: ${12:true},",
"\tremoveIfExists: ${13:true},",
"\tcustomCheck: (doc: Document, options: ElementOptions) => ${14:true},",
"\tchildren: [$15]",
"}, ${16:container});"
]
},
"appendElement - minimum": {
"scope": "javascript,typescript",
"prefix": "appendElement",
"body": "appendElement({ tag: '$1' }, $2);"
},
"register Notifier": {
"scope": "javascript,typescript",
"prefix": "registerObserver",
"body": [
"registerObserver({",
"\t notify: (",
"\t\tevent: _ZoteroTypes.Notifier.Event,",
"\t\ttype: _ZoteroTypes.Notifier.Type,",
"\t\tids: string[],",
"\t\textraData: _ZoteroTypes.anyObj",
"\t) => {",
"\t\t$0",
"\t}",
"});"
]
}
}

View File

@ -86,10 +86,10 @@ and:
## 👋 Install
- Download the latest release (.xpi file) from the [Releases Page](https://github.com/windingwind/zotero-better-notes/releases)
<details style="text-indent: 2em">
<summary>More Versions</summary>
- [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)
- [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>
_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`
- Go to the Extensions page and then click the gear icon in the top right.
- Select `Install Add-on from file`.

157
addon/bootstrap.js vendored
View File

@ -11,6 +11,8 @@ if (typeof Zotero == "undefined") {
var chromeHandle;
var windowListener;
// 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
// 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
// automatically made available.
async function waitForZotero() {
if (typeof Zotero != "undefined") {
await Zotero.initializationPromise;
}
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;
await new Promise(async (resolve) => {
if (typeof Zotero != "undefined") {
resolve();
}
}
if (!found) {
await new Promise((resolve) => {
var listener = {
onOpenWindow: function (aWindow) {
// Wait for the window to finish loading
let domWindow = aWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
domWindow.addEventListener(
"load",
function () {
domWindow.removeEventListener("load", arguments.callee, false);
if (domWindow.Zotero) {
Services.wm.removeListener(listener);
Zotero = domWindow.Zotero;
resolve();
}
},
false
);
},
};
Services.wm.addListener(listener);
});
}
await Zotero.initializationPromise;
const { Services } = ChromeUtils.import(
"resource://gre/modules/Services.jsm",
);
const windows = Services.wm.getEnumerator("navigator:browser");
let found = false;
while (windows.hasMoreElements()) {
let win = windows.getNext();
if (win.Zotero) {
Zotero = win.Zotero;
found = true;
resolve();
break;
}
}
windowListener = {
onOpenWindow: function (aWindow) {
// Wait for the window to finish loading
const domWindow = aWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
domWindow.addEventListener(
"load",
async function () {
domWindow.removeEventListener("load", arguments.callee, false);
if (!found && domWindow.Zotero) {
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) {}
async function startup({ id, version, resourceURI, rootURI }, reason) {
await waitForZotero();
await Zotero.initializationPromise;
// String 'rootURI' introduced in Zotero 7
if (!rootURI) {
rootURI = resourceURI.spec;
}
if (Zotero.platformMajorVersion >= 102) {
var aomStartup = Components.classes[
"@mozilla.org/addons/addon-manager-startup;1"
].getService(Components.interfaces.amIAddonManagerStartup);
var manifestURI = Services.io.newURI(rootURI + "manifest.json");
chromeHandle = aomStartup.registerChrome(manifestURI, [
["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);
}
var aomStartup = Components.classes[
"@mozilla.org/addons/addon-manager-startup;1"
].getService(Components.interfaces.amIAddonManagerStartup);
var manifestURI = Services.io.newURI(rootURI + "manifest.json");
chromeHandle = aomStartup.registerChrome(manifestURI, [
["content", "__addonRef__", rootURI + "chrome/content/"],
]);
/**
* Global variables for plugin code.
@ -94,13 +108,14 @@ async function startup({ id, version, resourceURI, rootURI }, reason) {
*/
const ctx = {
rootURI,
// Define main window's document to create a fake browser environment
document: Zotero.getMainWindow().document,
};
ctx._globalThis = ctx;
Services.scriptloader.loadSubScript(
`${rootURI}/chrome/content/scripts/index.js`,
ctx
`${rootURI}/chrome/content/scripts/__addonRef__.js`,
ctx,
);
}
@ -108,9 +123,11 @@ function shutdown({ id, version, resourceURI, rootURI }, reason) {
if (reason === APP_SHUTDOWN) {
return;
}
Services.wm.removeListener(windowListener);
if (typeof Zotero === "undefined") {
Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
Components.interfaces.nsISupports
Components.interfaces.nsISupports,
).wrappedJSObject;
}
Zotero.__addonInstance__.hooks.onShutdown();
@ -119,7 +136,7 @@ function shutdown({ id, version, resourceURI, rootURI }, reason) {
.getService(Components.interfaces.nsIStringBundleService)
.flushBundles();
Cu.unload(`${rootURI}/chrome/content/scripts/index.js`);
Cu.unload(`${rootURI}/chrome/content/scripts/__addonRef__.js`);
if (chromeHandle) {
chromeHandle.destruct();
@ -128,27 +145,3 @@ function shutdown({ id, version, resourceURI, rootURI }, 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);
}

View File

@ -31,6 +31,7 @@
"reload": "npm run build-dev && node scripts/reload.mjs --z 7",
"watch": "chokidar \"src/**\" \"addon/**\" -c \"npm run reload\"",
"release": "release-it",
"lint": "prettier --write src/** && eslint . --ext .ts --fix",
"test": "echo \"Error: no test specified\" && exit 1",
"update-deps": "npm update --save"
},
@ -63,7 +64,6 @@
"remark-parse": "^10.0.2",
"remark-rehype": "^10.1.0",
"remark-stringify": "^10.0.3",
"replace-in-file": "^6.3.5",
"seedrandom": "^3.0.5",
"tree-model": "^1.0.7",
"unified": "^10.1.2",
@ -75,21 +75,25 @@
"devDependencies": {
"@types/browser-or-node": "^1.3.0",
"@types/diff": "^5.0.3",
"@types/node": "^20.3.0",
"@types/node": "^20.4.2",
"@types/seedrandom": "^3.0.5",
"@types/yamljs": "^0.2.31",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"chokidar-cli": "^3.0.0",
"compressing": "^1.9.0",
"concurrently": "^8.2.0",
"cross-env": "^7.0.3",
"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-state": "^1.4.3",
"prosemirror-transform": "^1.7.3",
"prosemirror-view": "^1.31.4",
"release-it": "^15.11.0",
"replace-in-file": "^6.3.5",
"replace-in-file": "^7.0.1",
"typescript": "^5.1.3",
"zotero-types": "^1.0.15"
}

View File

@ -1,6 +1,6 @@
import { build } from "esbuild";
import { zip } from "compressing";
import { join, basename } from "path";
import path from "path";
import {
existsSync,
lstatSync,
@ -13,18 +13,22 @@ import {
} from "fs";
import { env, exit } from "process";
import replaceInFile from "replace-in-file";
const { sync: replaceSync } = replaceInFile;
const { replaceInFileSync } = replaceInFile;
import details from "../package.json" assert { type: "json" };
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) {
var targetFile = target;
// If target is a directory, a new file with the same name will be created
if (existsSync(target)) {
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 = [];
// 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)) {
mkdirSync(targetFolder);
}
@ -44,7 +48,7 @@ function copyFolderRecursiveSync(source, target) {
if (lstatSync(source).isDirectory()) {
files = readdirSync(source);
files.forEach(function (file) {
var curSource = join(source, file);
var curSource = path.join(source, file);
if (lstatSync(curSource).isDirectory()) {
copyFolderRecursiveSync(curSource, targetFolder);
} else {
@ -77,107 +81,21 @@ function dateFormat(fmt, date) {
if (ret) {
fmt = fmt.replace(
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;
}
async function main() {
const t = new Date();
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");
function renameLocaleFiles() {
const localeDir = path.join(buildDir, "addon/locale");
const localeFolders = readdirSync(localeDir, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);
for (const localeSubFolder of localeFolders) {
const localeSubDir = join(localeDir, localeSubFolder);
const localeSubDir = path.join(localeDir, localeSubFolder);
const localeSubFiles = readdirSync(localeSubDir, {
withFileTypes: true,
})
@ -187,22 +105,176 @@ async function main() {
for (const localeSubFile of localeSubFiles) {
if (localeSubFile.endsWith(".ftl")) {
renameSync(
join(localeSubDir, localeSubFile),
join(localeSubDir, `${config.addonRef}-${localeSubFile}`)
path.join(localeSubDir, 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");
zip.compressDir(join(buildDir, "addon"), join(buildDir, `${name}.xpi`), {
ignoreBase: true,
});
await zip.compressDir(
path.join(buildDir, "addon"),
path.join(buildDir, `${name}.xpi`),
{
ignoreBase: true,
},
);
console.log("[Build] Addon pack OK");
console.log(
`[Build] Finished in ${(new Date().getTime() - t.getTime()) / 1000} s.`
`[Build] Finished in ${(new Date().getTime() - t.getTime()) / 1000} s.`,
);
}

View File

@ -1,20 +1,13 @@
import { exit, argv } from "process";
import minimist from "minimist";
import { exit } from "process";
import { execSync } from "child_process";
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" };
const { exec } = cmd;
// Run node reload.js -h for help
const args = minimist(argv.slice(2));
const { addonID, addonName } = details.config;
const { version } = details;
const { zoteroBinPath, profilePath } = cmd.exec;
const zoteroPath = exec[args.zotero || args.z || Object.keys(exec)[0]];
const profile = args.profile || args.p;
const startZotero = `${zoteroPath} --debugger --purgecaches ${
profile ? `-p ${profile}` : ""
}`;
const startZotero = `"${zoteroBinPath}" --debugger --purgecaches -profile "${profilePath}"`;
const script = `
(async () => {

View File

@ -1,28 +1,74 @@
import process from "process";
import { execSync } from "child_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" };
const { exec } = cmd;
// Run node start.js -h for help
const args = minimist(process.argv.slice(2));
const { addonID } = details.config;
const { zoteroBinPath, profilePath, dataDir } = cmd.exec;
if (args.help || args.h) {
console.log("Start Zotero Args:");
console.log(
"--zotero(-z): Zotero exec key in zotero-cmd.json. Default the first one."
);
console.log("--profile(-p): Zotero profile name.");
exit(0);
if (!existsSync(zoteroBinPath)) {
throw new Error("Zotero binary does not exist.");
}
const zoteroPath = exec[args.zotero || args.z || Object.keys(exec)[0]];
const profile = args.profile || args.p;
if (existsSync(profilePath)) {
const addonProxyFilePath = path.join(profilePath, `extensions/${addonID}`);
const buildPath = path.resolve("build/addon");
const startZotero = `${zoteroPath} --debugger --purgecaches ${
profile ? `-p ${profile}` : ""
}`;
if (!existsSync(path.join(buildPath, "./manifest.json"))) {
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);
exit(0);

View File

@ -3,7 +3,18 @@
"killZoteroWindows": "taskkill /f /im zotero.exe",
"killZoteroUnix": "kill -9 $(ps -x | grep zotero)",
"exec": {
"6": "/path/to/zotero6.exe",
"7": "/path/to/zotero7.exe"
"@comment-zoteroBinPath": "Please input the path of the Zotero binary file in `zoteroBinPath`.",
"@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": ""
}
}
}

View File

@ -133,7 +133,7 @@ class Addon {
recentMainNoteIdsArr
.slice(0, 10)
.filter((id) => Zotero.Items.get(id).isNote())
.join(",")
.join(","),
);
},
previewId: -1,

View File

@ -17,9 +17,9 @@ declare const _currentEditorInstance: {
};
function fromHTML(schema: Schema, html: string, slice?: boolean) {
let domNode = document.createElement("div");
const domNode = document.createElement("div");
domNode.innerHTML = html;
let fragment = document.createDocumentFragment();
const fragment = document.createDocumentFragment();
while (domNode.firstChild) {
fragment.appendChild(domNode.firstChild);
}
@ -63,7 +63,7 @@ function objectIncludes(object1: any, object2: any) {
function findMarkInSet(
marks: readonly Mark[],
type: MarkType,
attributes = {}
attributes = {},
) {
return marks.find((item) => {
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);
if (start.node) {
const mark = start.node.marks.find(
(mark) => mark.type.name === type.name
(mark) => mark.type.name === type.name,
);
if (mark) {
return getMarkRange($from, type, mark.attrs);
@ -164,7 +164,7 @@ function replaceRange(
to: number,
text: string | undefined,
type: MarkType,
attrs: Attrs | string
attrs: Attrs | string,
) {
return (state: EditorState, dispatch: EditorView["dispatch"]) => {
const { tr } = state;
@ -189,7 +189,7 @@ function replaceRangeNode(
nodeAttrs: Attrs | string,
markType?: MarkType,
markAttrs?: Attrs | string,
select?: boolean
select?: boolean,
) {
return (state: EditorState, dispatch: EditorView["dispatch"]) => {
const { tr } = state;
@ -203,7 +203,7 @@ function replaceRangeNode(
const node = nodeType.create(
nodeAttrs,
state.schema.text(text || state.doc.textBetween(from, to)),
markType ? [markType.create(markAttrs)] : []
markType ? [markType.create(markAttrs)] : [],
);
console.log("Replace Node", from, to, node);
tr.replaceWith(from, to, node);
@ -218,7 +218,7 @@ function replaceRangeAtCursor(
text: string | undefined,
type: MarkType,
attrs: Attrs | string,
searchType: MarkType
searchType: MarkType,
) {
return (state: EditorState, dispatch: EditorView["dispatch"]) => {
const range = getMarkRangeAtCursor(state, searchType);
@ -323,9 +323,9 @@ function updateHeadingsInRange(from: number, to: number, levelOffset: number) {
};
}
function refocusEditor(callback: Function) {
let scrollTop = document.querySelector(".editor-core")!.scrollTop;
let input = document.createElement("input");
function refocusEditor(callback: (args: void) => void) {
const scrollTop = document.querySelector(".editor-core")!.scrollTop;
const input = document.createElement("input");
input.style.position = "absolute";
input.style.opacity = "0";
document.body.append(input);
@ -344,9 +344,9 @@ function updateImageDimensions(
width: number,
height: number | undefined,
state: EditorState,
dispatch: EditorView["dispatch"]
dispatch: EditorView["dispatch"],
) {
let { tr } = state;
const { tr } = state;
state.doc.descendants((node: Node, pos: number) => {
if (node.type.name === "image" && node.attrs.nodeID === nodeID) {
// tr.step(new SetAttrsStep(pos, { ...node.attrs, width, height }));
@ -356,7 +356,7 @@ function updateImageDimensions(
pos,
node.type,
{ ...node.attrs, width, height },
node.marks
node.marks,
);
dispatch(tr);
return false;

View File

@ -47,6 +47,7 @@ import {
createNoteFromMD,
} from "./modules/createNote";
import { annotationTagAction } from "./modules/annotationTagAction";
import { createZToolkit } from "./utils/ztoolkit";
async function onStartup() {
await Promise.all([
@ -57,26 +58,38 @@ async function onStartup() {
initLocale();
ztoolkit.ProgressWindow.setIconURI(
"default",
`chrome://${config.addonRef}/content/icons/favicon.png`
`chrome://${config.addonRef}/content/icons/favicon.png`,
);
registerNoteLinkProxyHandler();
registerNotify(["tab", "item", "item-tag"]);
registerEditorInstanceHook();
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();
registerWorkspaceTab();
registerReaderInitializer();
}
registerPrefsWindow();
setSyncing();
async function onMainWindowUnload(win: Window): Promise<void> {
ztoolkit.unregisterAll();
unregisterReaderInitializer();
}
function onShutdown(): void {
@ -96,7 +109,7 @@ function onNotify(
event: string,
type: string,
ids: number[] | string[],
extraData: { [key: string]: any }
extraData: { [key: string]: any },
) {
// Workspace tab select/unselect callback
if (event === "select" && type === "tab") {
@ -128,7 +141,7 @@ function onNotify(
// Reader annotation buttons update
if (event === "add" && type === "item") {
const annotationItems = Zotero.Items.get(ids as number[]).filter((item) =>
item.isAnnotation()
item.isAnnotation(),
);
if (annotationItems.length !== 0) {
checkReaderAnnotationButton(annotationItems);
@ -163,7 +176,7 @@ function onOpenNote(
mode: "auto" | "preview" | "workspace" | "standalone" = "auto",
options: {
lineIndex?: number;
} = {}
} = {},
) {
const noteItem = Zotero.Items.get(noteId);
if (!noteItem?.isNote()) {
@ -188,6 +201,7 @@ function onOpenNote(
break;
case "workspace":
addon.hooks.onSetWorkspaceNote(noteId, "main", options);
break;
case "standalone":
ZoteroPane.openNoteWindow(noteId);
break;
@ -201,7 +215,7 @@ function onSetWorkspaceNote(
type: "main" | "preview" = "main",
options: {
lineIndex?: number;
} = {}
} = {},
) {
if (type === "main") {
addon.data.workspace.mainId = noteId;
@ -215,13 +229,13 @@ function onSetWorkspaceNote(
addon.data.workspace.window.container,
type,
noteId,
options
options,
);
type === "preview" &&
addon.hooks.onToggleWorkspacePane(
"preview",
true,
addon.data.workspace.window.container
addon.data.workspace.window.container,
);
addon.data.workspace.window.window?.focus();
}
@ -230,13 +244,13 @@ function onSetWorkspaceNote(
addon.data.workspace.tab.container,
type,
noteId,
options
options,
);
type === "preview" &&
addon.hooks.onToggleWorkspacePane(
"preview",
true,
addon.data.workspace.tab.container
addon.data.workspace.tab.container,
);
Zotero_Tabs.select(addon.data.workspace.tab.id!);
}
@ -263,7 +277,7 @@ const onInitWorkspace = initWorkspace;
function onToggleWorkspacePane(
type: "outline" | "preview" | "notes",
visibility?: boolean,
container?: XUL.Box
container?: XUL.Box,
) {
switch (type) {
case "outline":
@ -274,6 +288,7 @@ function onToggleWorkspacePane(
break;
case "notes":
toggleNotesPane(visibility);
break;
default:
break;
}
@ -311,6 +326,8 @@ const onCreateNoteFromMD = createNoteFromMD;
export default {
onStartup,
onMainWindowLoad,
onMainWindowUnload,
onShutdown,
onNotify,
onPrefsEvent,

View File

@ -7,22 +7,26 @@ const basicTool = new BasicTool();
if (!basicTool.getGlobal("Zotero")[config.addonInstance]) {
// Set global variables
_globalThis.Zotero = basicTool.getGlobal("Zotero");
_globalThis.ZoteroPane = basicTool.getGlobal("ZoteroPane");
_globalThis.Zotero_Tabs = basicTool.getGlobal("Zotero_Tabs");
_globalThis.window = basicTool.getGlobal("window");
_globalThis.document = basicTool.getGlobal("document");
_globalThis.OS = basicTool.getGlobal("OS") as typeof OS;
defineGlobal("window");
defineGlobal("document");
defineGlobal("ZoteroPane");
defineGlobal("Zotero_Tabs");
defineGlobal("OS");
_globalThis.addon = new Addon();
_globalThis.ztoolkit = addon.data.ztoolkit;
ztoolkit.basicOptions.log.prefix = `[${config.addonName}]`;
ztoolkit.basicOptions.log.disableConsole = addon.data.env === "production";
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";
Object.defineProperty(_globalThis, "ztoolkit", {
get() {
return _globalThis.addon.data.ztoolkit;
},
});
Zotero[config.addonInstance] = addon;
// Trigger addon hook for initialization
addon.hooks.onStartup();
}
function defineGlobal(name: Parameters<BasicTool["getGlobal"]>[0]) {
Object.defineProperty(_globalThis, name, {
get() {
return basicTool.getGlobal(name);
},
});
}

View File

@ -4,7 +4,7 @@ export { annotationTagAction };
async function annotationTagAction(
ids: Array<number | string>,
extraData: Record<string, any>
extraData: Record<string, any>,
) {
const workspaceNote = Zotero.Items.get(addon.data.workspace.mainId);
if (!workspaceNote || !workspaceNote.isNote()) {
@ -14,7 +14,7 @@ async function annotationTagAction(
const headings: string[] = nodes.map((node) => node.model.name);
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();
if (headings.includes(tagName) || tagName === "@") {
@ -35,7 +35,7 @@ async function annotationTagAction(
await addon.api.convert.annotations2html([annotationItem], {
noteItem: workspaceNote,
}),
lineIndex
lineIndex,
);
}
}

View File

@ -54,22 +54,22 @@ async function note2md(
keepNoteLink?: boolean;
withYAMLHeader?: boolean;
skipSavingImages?: boolean;
} = {}
} = {},
) {
const noteStatus = addon.api.sync.getNoteStatus(noteItem.id)!;
const rehype = note2rehype(noteStatus.content);
processN2MRehypeHighlightNodes(
getN2MRehypeHighlightNodes(rehype),
NodeMode.direct
NodeMode.direct,
);
processN2MRehypeCitationNodes(
getN2MRehypeCitationNodes(rehype),
NodeMode.direct
NodeMode.direct,
);
await processN2MRehypeNoteLinkNodes(
getN2MRehypeNoteLinkNodes(rehype),
dir,
options.keepNoteLink ? NodeMode.default : NodeMode.direct
options.keepNoteLink ? NodeMode.default : NodeMode.direct,
);
await processN2MRehypeImageNodes(
getN2MRehypeImageNodes(rehype),
@ -77,7 +77,7 @@ async function note2md(
formatPath(OS.Path.join(dir, "attachments")),
options.skipSavingImages,
false,
NodeMode.direct
NodeMode.direct,
);
const remark = await rehype2remark(rehype);
let md = remark2md(remark);
@ -89,16 +89,18 @@ async function note2md(
await addon.api.template.runTemplate(
"[ExportMDFileHeaderV2]",
"noteItem",
[noteItem]
)
[noteItem],
),
);
} catch (e) {}
} catch (e) {
ztoolkit.log(e);
}
Object.assign(header, {
version: noteItem.version,
libraryID: noteItem.libraryID,
itemKey: noteItem.key,
});
let yamlFrontMatter = `---\n${YAML.stringify(header, 10)}\n---`;
const yamlFrontMatter = `---\n${YAML.stringify(header, 10)}\n---`;
md = `${yamlFrontMatter}\n${md}`;
}
return md;
@ -107,7 +109,7 @@ async function note2md(
async function md2note(
mdStatus: MDStatus,
noteItem: Zotero.Item,
options: { isImport?: boolean } = {}
options: { isImport?: boolean } = {},
) {
const remark = md2remark(mdStatus.content);
const _rehype = await remark2rehype(remark);
@ -120,14 +122,14 @@ async function md2note(
processM2NRehypeHighlightNodes(getM2NRehypeHighlightNodes(rehype));
await processM2NRehypeCitationNodes(
getM2NRehypeCitationNodes(rehype),
options.isImport
options.isImport,
);
processM2NRehypeNoteLinkNodes(getM2NRehypeNoteLinkNodes(rehype));
await processM2NRehypeImageNodes(
getM2NRehypeImageNodes(rehype),
noteItem,
mdStatus.filedir,
options.isImport
options.isImport,
);
const noteContent = rehype2note(rehype);
return noteContent;
@ -143,7 +145,7 @@ async function note2noteDiff(noteItem: Zotero.Item) {
function note2link(
noteItem: Zotero.Item,
options: Parameters<typeof getNoteLink>[1]
options: Parameters<typeof getNoteLink>[1],
) {
return getNoteLink(noteItem, options);
}
@ -154,9 +156,9 @@ function link2note(link: string) {
async function link2html(
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);
if (!linkParams.noteItem) {
return "";
@ -173,7 +175,7 @@ async function link2html(
// Only embed the note content
html,
options.noteItem,
refNotes
refNotes,
);
}
}
@ -194,7 +196,7 @@ async function html2md(html: string) {
function annotations2html(
annotations: Zotero.Item[],
options: Parameters<typeof parseAnnotationHTML>[1] = {}
options: Parameters<typeof parseAnnotationHTML>[1] = {},
) {
return parseAnnotationHTML(annotations, options);
}
@ -227,7 +229,7 @@ function note2rehype(str: string) {
removeBlank(_n, parentNode, -1);
removeBlank(_n, parentNode, 1);
}
}
},
);
// Make sure <span> and <img> wrapped by <p>
@ -245,7 +247,7 @@ function note2rehype(str: string) {
replace(_n, p);
}
}
}
},
);
// 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);
}
}
}
},
);
return rehype;
}
@ -310,13 +312,13 @@ async function rehype2remark(rehype: HRoot) {
if (node.properties.style) {
hasStyle = true;
}
}
},
);
if (0 && hasStyle) {
return h(node, "styleTable", toHtml(node));
} else {
return defaultHandlers.table(h, node);
}
// if (0 && hasStyle) {
// return h(node, "styleTable", toHtml(node));
// } else {
return defaultHandlers.table(h, node);
// }
},
wrapper: (h, node) => {
return h(node, "wrapper", toText(node));
@ -389,7 +391,7 @@ function remark2md(remark: MRoot) {
},
},
} as any)
.stringify(remark)
.stringify(remark),
);
}
@ -401,7 +403,7 @@ function md2remark(str: string) {
.replace(
/!\[.*\]\((.*)\)/g,
(s: string) =>
`![](${encodeURIComponent(s.match(/\(.*\)/g)![0].slice(1, -1))})`
`![](${encodeURIComponent(s.match(/\(.*\)/g)![0].slice(1, -1))})`,
);
const remark = unified()
.use(remarkGfm)
@ -439,7 +441,7 @@ function rehype2note(rehype: HRoot) {
(node: any) => {
node.tagName = "span";
node.properties.style = "text-decoration: line-through";
}
},
);
// Code node
@ -454,7 +456,7 @@ function rehype2note(rehype: HRoot) {
node.value = toText(node);
node.type = "text";
}
}
},
);
// Table node with style
@ -472,14 +474,14 @@ function rehype2note(rehype: HRoot) {
if (node.properties.style) {
hasStyle = true;
}
}
},
);
if (hasStyle) {
node.value = toHtml(node).replace(/[\r\n]/g, "");
node.children = [];
node.type = "raw";
}
}
},
);
// Convert thead to tbody
@ -490,7 +492,7 @@ function rehype2note(rehype: HRoot) {
node.value = toHtml(node).slice(7, -8);
node.children = [];
node.type = "raw";
}
},
);
// Wrap lines in list with <p> (for diff)
@ -522,9 +524,9 @@ function rehype2note(rehype: HRoot) {
node.children = node.children.filter(
(_n: { type: string; value: string }) =>
_n.type === "element" ||
(_n.type === "text" && _n.value.replace(/[\r\n]/g, ""))
(_n.type === "text" && _n.value.replace(/[\r\n]/g, "")),
);
}
},
);
// Math node
@ -550,7 +552,7 @@ function rehype2note(rehype: HRoot) {
node.tagName = "pre";
}
node.properties.className = "math";
}
},
);
// 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.properties.rel = undefined;
}
},
);
// Ignore empty lines, as they are not parsed to md
@ -610,7 +612,7 @@ function getN2MRehypeHighlightNodes(rehype: HRoot) {
(node: any) =>
node.type === "element" &&
node.properties?.className?.includes("highlight"),
(node) => nodes.push(node)
(node) => nodes.push(node),
);
return new Array(...new Set(nodes));
}
@ -622,7 +624,7 @@ function getN2MRehypeCitationNodes(rehype: HRoot) {
(node: any) =>
node.type === "element" &&
node.properties?.className?.includes("citation"),
(node) => nodes.push(node)
(node) => nodes.push(node),
);
return new Array(...new Set(nodes));
}
@ -636,7 +638,7 @@ function getN2MRehypeNoteLinkNodes(rehype: any) {
node.tagName === "a" &&
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));
}
@ -649,14 +651,14 @@ function getN2MRehypeImageNodes(rehype: any) {
node.type === "element" &&
node.tagName === "img" &&
node.properties?.dataAttachmentKey,
(node) => nodes.push(node)
(node) => nodes.push(node),
);
return new Array(...new Set(nodes));
}
function processN2MRehypeHighlightNodes(
nodes: string | any[],
mode: NodeMode = NodeMode.default
mode: NodeMode = NodeMode.default,
) {
if (!nodes.length) {
return;
@ -665,7 +667,7 @@ function processN2MRehypeHighlightNodes(
let annotation;
try {
annotation = JSON.parse(
decodeURIComponent(node.properties.dataAnnotation)
decodeURIComponent(node.properties.dataAnnotation),
);
} catch (e) {
continue;
@ -674,20 +676,20 @@ function processN2MRehypeHighlightNodes(
continue;
}
// annotation.uri was used before note-editor v4
let uri = annotation.attachmentURI || annotation.uri;
let position = annotation.position;
const uri = annotation.attachmentURI || annotation.uri;
const position = annotation.position;
if (typeof uri === "string" && typeof position === "object") {
let openURI;
let uriParts = uri.split("/");
let libraryType = uriParts[3];
let key = uriParts[uriParts.length - 1];
const uriParts = uri.split("/");
const libraryType = uriParts[3];
const key = uriParts[uriParts.length - 1];
if (libraryType === "users") {
openURI = "zotero://open-pdf/library/items/" + key;
}
// groups
else {
let groupID = uriParts[4];
const groupID = uriParts[4];
openURI = "zotero://open-pdf/groups/" + groupID + "/items/" + key;
}
@ -709,7 +711,7 @@ function processN2MRehypeHighlightNodes(
randomString(
8,
Zotero.Utilities.Internal.md5(node.properties.dataAnnotation),
Zotero.Utilities.allowedKeyChars
Zotero.Utilities.allowedKeyChars,
);
if (mode === NodeMode.wrap) {
@ -731,7 +733,7 @@ function processN2MRehypeHighlightNodes(
function processN2MRehypeCitationNodes(
nodes: string | any[],
mode: NodeMode = NodeMode.default
mode: NodeMode = NodeMode.default,
) {
if (!nodes.length) {
return;
@ -747,19 +749,19 @@ function processN2MRehypeCitationNodes(
continue;
}
let uris: any[] = [];
for (let citationItem of citation.citationItems) {
let uri = citationItem.uris[0];
const uris: any[] = [];
for (const citationItem of citation.citationItems) {
const uri = citationItem.uris[0];
if (typeof uri === "string") {
let uriParts = uri.split("/");
let libraryType = uriParts[3];
let key = uriParts[uriParts.length - 1];
const uriParts = uri.split("/");
const libraryType = uriParts[3];
const key = uriParts[uriParts.length - 1];
if (libraryType === "users") {
uris.push("zotero://select/library/items/" + key);
}
// groups
else {
let groupID = uriParts[4];
const groupID = uriParts[4];
uris.push("zotero://select/groups/" + groupID + "/items/" + key);
}
}
@ -772,7 +774,7 @@ function processN2MRehypeCitationNodes(
(_n: any) => _n.properties?.className.includes("citation-item"),
(_n: any) => {
return childNodes?.push(_n);
}
},
);
// For unknown reasons, the element will be duplicated. Remove them.
@ -797,7 +799,7 @@ function processN2MRehypeCitationNodes(
const citationKey = randomString(
8,
Zotero.Utilities.Internal.md5(node.properties.dataCitation),
Zotero.Utilities.allowedKeyChars
Zotero.Utilities.allowedKeyChars,
);
if (mode === NodeMode.wrap) {
newNode.children.splice(0, 0, h("wrapperleft", `cite:${citationKey}`));
@ -817,7 +819,7 @@ function processN2MRehypeCitationNodes(
async function processN2MRehypeNoteLinkNodes(
nodes: string | any[],
dir: string,
mode: NodeMode = NodeMode.default
mode: NodeMode = NodeMode.default,
) {
if (!nodes.length) {
return;
@ -835,7 +837,7 @@ async function processN2MRehypeNoteLinkNodes(
const linkKey = randomString(
8,
Zotero.Utilities.Internal.md5(node.properties.href),
Zotero.Utilities.allowedKeyChars
Zotero.Utilities.allowedKeyChars,
);
if (mode === NodeMode.wrap) {
const newNode = h("span", [
@ -843,7 +845,7 @@ async function processN2MRehypeNoteLinkNodes(
h(
node.tagName,
Object.assign(node.properties, { href: link }),
node.children
node.children,
),
h("wrapperright", `note:${linkKey}`),
]);
@ -869,25 +871,25 @@ async function processN2MRehypeImageNodes(
dir: string,
skipSavingImages: boolean = false,
absolutePath: boolean = false,
mode: NodeMode = NodeMode.default
mode: NodeMode = NodeMode.default,
) {
if (!nodes.length) {
return;
}
for (const node of nodes) {
let imgKey = node.properties.dataAttachmentKey;
const imgKey = node.properties.dataAttachmentKey;
const attachmentItem = (await Zotero.Items.getByLibraryAndKeyAsync(
libraryID,
imgKey
imgKey,
)) as Zotero.Item;
if (!attachmentItem) {
continue;
}
let oldFile = String(await attachmentItem.getFilePathAsync());
let ext = oldFile.split(".").pop();
let newAbsPath = formatPath(`${dir}/${imgKey}.${ext}`);
const oldFile = String(await attachmentItem.getFilePathAsync());
const ext = oldFile.split(".").pop();
const newAbsPath = formatPath(`${dir}/${imgKey}.${ext}`);
let newFile = oldFile;
try {
// Don't overwrite
@ -898,7 +900,7 @@ async function processN2MRehypeImageNodes(
newFile = newFile.replace(/\\/g, "/");
}
newFile = Zotero.File.normalizeToUnix(
absolutePath ? newFile : `attachments/${newFile.split(/\//).pop()}`
absolutePath ? newFile : `attachments/${newFile.split(/\//).pop()}`,
);
} catch (e) {
ztoolkit.log(e);
@ -922,7 +924,7 @@ function getM2NRehypeAnnotationNodes(rehype: any) {
visit(
rehype,
(node: any) => node.type === "element" && node.properties?.dataAnnotation,
(node: any) => nodes.push(node)
(node: any) => nodes.push(node),
);
return new Array(...new Set(nodes));
}
@ -933,7 +935,7 @@ function getM2NRehypeHighlightNodes(rehype: any) {
rehype,
(node: any) =>
node.type === "element" && node.properties?.ztype === "zhighlight",
(node) => nodes.push(node)
(node) => nodes.push(node),
);
return new Array(...new Set(nodes));
}
@ -945,7 +947,7 @@ function getM2NRehypeCitationNodes(rehype: any) {
(node: any) =>
node.type === "element" &&
(node.properties?.ztype === "zcitation" || node.properties?.dataCitation),
(node) => nodes.push(node)
(node) => nodes.push(node),
);
return new Array(...new Set(nodes));
}
@ -956,7 +958,7 @@ function getM2NRehypeNoteLinkNodes(rehype: any) {
rehype,
(node: any) =>
node.type === "element" && node.properties?.ztype === "znotelink",
(node) => nodes.push(node)
(node) => nodes.push(node),
);
return new Array(...new Set(nodes));
}
@ -966,7 +968,7 @@ function getM2NRehypeImageNodes(rehype: any) {
visit(
rehype,
(node: any) => node.type === "element" && node.tagName === "img",
(node) => nodes.push(node)
(node) => nodes.push(node),
);
return new Array(...new Set(nodes));
}
@ -1002,7 +1004,7 @@ function processM2NRehypeHighlightNodes(nodes: string | any[]) {
async function processM2NRehypeCitationNodes(
nodes: string | any[],
isImport: boolean = false
isImport: boolean = false,
) {
if (!nodes.length) {
return;
@ -1021,10 +1023,10 @@ async function processM2NRehypeCitationNodes(
// "properties": {}
// }
const dataCitation = JSON.parse(
decodeURIComponent(node.properties.dataCitation)
decodeURIComponent(node.properties.dataCitation),
);
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 newNode = note2rehype(html);
@ -1040,7 +1042,7 @@ async function processM2NRehypeCitationNodes(
(_n: any) => _n.properties?.className.includes("citation-item"),
(_n) => {
_n.children = [{ type: "text", value: toText(_n) }];
}
},
);
delete node.properties?.ztype;
}
@ -1064,7 +1066,7 @@ async function processM2NRehypeImageNodes(
nodes: any[],
noteItem: Zotero.Item,
fileDir: string,
isImport: boolean = false
isImport: boolean = false,
) {
if (!nodes.length || (isImport && !noteItem)) {
return;
@ -1074,7 +1076,7 @@ async function processM2NRehypeImageNodes(
if (isImport) {
// We encode the src in md2remark and decode it here.
let src = Zotero.File.normalizeToUnix(
decodeURIComponent(node.properties.src)
decodeURIComponent(node.properties.src),
);
const srcType = (src as string).startsWith("data:")
? "b64"

View File

@ -11,23 +11,23 @@ async function createWorkspaceNote() {
}
const confirmOperation = window.confirm(
`${getString(
"menuAddNote.newMainNote.confirmHead"
"menuAddNote.newMainNote.confirmHead",
// @ts-ignore
)} '${currentCollection.getName()}' ${getString(
"menuAddNote.newMainNote.confirmTail"
)}`
"menuAddNote.newMainNote.confirmTail",
)}`,
);
if (!confirmOperation) {
return;
}
const header = window.prompt(
getString("menuAddNote.newMainNote.enterNoteTitle"),
`New Note ${new Date().toLocaleString()}`
`New Note ${new Date().toLocaleString()}`,
);
const noteID = await ZoteroPane.newNote();
const noteItem = Zotero.Items.get(noteID);
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();
addon.hooks.onSetWorkspaceNote(noteID, "main");
@ -47,10 +47,8 @@ function getLibraryParentId() {
function getReaderParentId() {
const currentReader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (currentReader) {
}
const parentItemId = Zotero.Items.get(
currentReader?.itemID || -1
currentReader?.itemID || -1,
).parentItemID;
return parentItemId;
}
@ -58,11 +56,11 @@ function getReaderParentId() {
async function createNoteFromTemplate(noteType: "standalone"): Promise<void>;
async function createNoteFromTemplate(
noteType: "item",
parentType: "reader" | "library"
parentType: "reader" | "library",
): Promise<void>;
async function createNoteFromTemplate(
noteType: "standalone" | "item",
parentType?: "reader" | "library"
parentType?: "reader" | "library",
) {
if (noteType === "item") {
const parentItemId =
@ -94,7 +92,7 @@ async function createNoteFromMD() {
const filepaths = await new ztoolkit.FilePicker(
"Import MarkDown",
"multiple",
[[`MarkDown(*.md)`, `*.md`]]
[[`MarkDown(*.md)`, `*.md`]],
).open();
if (!filepaths) {

View File

@ -12,7 +12,7 @@ export function initEditorImagePreviewer(editor: Zotero.EditorInstance) {
addon.hooks.onShowImageViewer(
imageList.map((elem) => elem.src),
imageList.indexOf(e.target as HTMLImageElement),
editor._item.getNoteTitle()
editor._item.getNoteTitle(),
);
};
editor._iframeWindow.document.addEventListener("dblclick", (e) => {

View File

@ -11,12 +11,12 @@ export function registerEditorInstanceHook() {
apply: (
target,
thisArg,
argumentsList: [instance: Zotero.EditorInstance]
argumentsList: [instance: Zotero.EditorInstance],
) => {
target.apply(thisArg, argumentsList);
argumentsList.forEach(onEditorInstanceCreated);
},
}
},
);
}

View File

@ -7,12 +7,12 @@ export async function injectEditorScripts(win: Window) {
id: "betternotes-script",
properties: {
innerHTML: await getFileContent(
rootURI + "chrome/content/scripts/editorScript.js"
rootURI + "chrome/content/scripts/editorScript.js",
),
},
ignoreIfExists: true,
},
win.document.head
win.document.head,
);
}
@ -60,6 +60,6 @@ export function injectEditorCSS(win: Window) {
},
ignoreIfExists: true,
},
win.document.head
win.document.head,
);
}

View File

@ -31,8 +31,8 @@ export function initEditorMenu(editor: Zotero.EditorInstance) {
editor._iframeWindow.prompt(
getString("editor.resizeImage.prompt"),
// @ts-ignore
getEditorCore(editor).view.state.selection.node?.attrs?.width
) || ""
getEditorCore(editor).view.state.selection.node?.attrs?.width,
) || "",
);
if (newWidth && newWidth > 10) {
updateImageDimensionsAtCursor(editor, newWidth);

View File

@ -38,7 +38,7 @@ export function initEditorPopup(editor: Zotero.EditorInstance) {
childList: true,
attributes: true,
attributeFilter: ["href"],
}
},
);
}
@ -68,7 +68,7 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
const templateText = await addon.api.template.runTemplate(
"[QuickImportV2]",
"link, noteItem",
[link, editorNote]
[link, editorNote],
);
// auto insert to anchor position
updateURLAtCursor(
@ -76,8 +76,8 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
undefined,
getNoteLink(
linkNote,
Object.assign({}, linkParams, { ignore: true })
)!
Object.assign({}, linkParams, { ignore: true }),
)!,
);
insert(editor, templateText);
} else {
@ -86,14 +86,14 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
undefined,
getNoteLink(
linkNote,
Object.assign({}, linkParams, { ignore: null })
)!
Object.assign({}, linkParams, { ignore: null }),
)!,
);
const lineIndex = getLineAtCursor(editor);
del(
editor,
getPositionAtLine(editor, lineIndex),
getPositionAtLine(editor, lineIndex + 1)
getPositionAtLine(editor, lineIndex + 1),
);
}
},
@ -117,7 +117,7 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
updateURLAtCursor(
editor,
linkNote.getNoteTitle(),
getURLAtCursor(editor)
getURLAtCursor(editor),
);
},
},
@ -184,7 +184,7 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
// }
} else {
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
.querySelector(".primary-editor")
?.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"),
// @ts-ignore
getEditorCore(editor).view.state.selection.node?.attrs
?.width
) || ""
?.width,
) || "",
);
if (newWidth && newWidth > 10) {
updateImageDimensionsAtCursor(editor, newWidth);
@ -257,6 +257,6 @@ function updateEditorImagePopup(editor: Zotero.EditorInstance) {
},
],
},
editor._iframeWindow.document.querySelector(".image-popup")!
editor._iframeWindow.document.querySelector(".image-popup")!,
);
}

View File

@ -25,14 +25,14 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
ICONS.settings,
getString("editor.toolbar.settings.title"),
"end",
(e) => {}
(e) => {},
);
settingsButton.addEventListener("click", async (ev) => {
ev.stopPropagation();
function removePopup() {
const popup = editor._iframeWindow.document.querySelector(
`#${makeId("settings-popup")}`
`#${makeId("settings-popup")}`,
);
if (popup) {
popup.remove();
@ -116,7 +116,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
`<a href="${link}">${
e.editor._item.getNoteTitle().trim() || link
}</a>`,
"text/html"
"text/html",
)
.copy();
showHint(`Link ${link} copied`);
@ -158,7 +158,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
settingsButton,
`${config.addonRef}-settings-popup`,
"right",
settingsMenuData
settingsMenuData,
).then((popup) => {
settingsButton.querySelector(".toolbar-button")?.classList.add("active");
editor._iframeWindow.document.addEventListener("click", removePopup);
@ -173,7 +173,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
"middle",
ztoolkit.UI.createElement(editor._iframeWindow.document, "div", {
properties: { innerHTML: getString("editor.toolbar.main") },
})
}),
);
} else {
const onTriggerMenu = (ev: MouseEvent) => {
@ -188,7 +188,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
linkButton,
`${config.addonRef}-link-popup`,
"middle",
linkMenu
linkMenu,
);
};
@ -207,7 +207,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
return;
}
const lineIndex = parseInt(
(ev.target as HTMLDivElement).id.split("-").pop() || "-1"
(ev.target as HTMLDivElement).id.split("-").pop() || "-1",
);
const forwardLink = getNoteLink(noteItem);
const backLink = getNoteLink(mainNote, { ignore: true, lineIndex });
@ -221,9 +221,9 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
noteItem.getNoteTitle().trim() || forwardLink,
noteItem,
mainNote,
]
],
),
lineIndex
lineIndex,
);
addLineToNote(
noteItem,
@ -236,8 +236,8 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
noteItem,
mainNote,
"",
]
)
],
),
);
onExitMenu(ev);
ev.stopPropagation();
@ -250,7 +250,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
ICONS.addon,
getString("editor.toolbar.link.title"),
"middle",
onClickMenu
onClickMenu,
);
linkButton.addEventListener("mouseenter", onTriggerMenu);
@ -312,7 +312,7 @@ function getLinkMenuData(editor: Zotero.EditorInstance): PopupData[] {
getPref("editor.link.insertPosition")
? node.model.lineIndex - 1
: node.model.endIndex
}`
}`,
),
text: node.model.name,
prefix: "·".repeat(node.model.level - 1),
@ -323,7 +323,7 @@ function getLinkMenuData(editor: Zotero.EditorInstance): PopupData[] {
async function registerEditorToolbar(
editor: Zotero.EditorInstance,
id: string
id: string,
) {
await editor._initPromise;
const _document = editor._iframeWindow.document;
@ -359,7 +359,7 @@ async function registerEditorToolbarDropdown(
icon: string,
title: string,
position: "start" | "middle" | "end",
callback: (e: MouseEvent & { editor: Zotero.EditorInstance }) => any
callback: (e: MouseEvent & { editor: Zotero.EditorInstance }) => any,
) {
await editor._initPromise;
const _document = editor._iframeWindow.document;
@ -386,7 +386,7 @@ async function registerEditorToolbarDropdown(
Object.assign(e, { editor });
if (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,
id: string,
align: "middle" | "left" | "right",
popupLines: PopupData[]
popupLines: PopupData[],
) {
await editor._initPromise;
const popup = ztoolkit.UI.appendElement(
@ -449,7 +449,7 @@ async function registerEditorToolbarPopup(
props.callback(
e as any as MouseEvent & {
editor: Zotero.EditorInstance;
}
},
);
},
},
@ -458,7 +458,7 @@ async function registerEditorToolbarPopup(
}),
removeIfExists: true,
},
dropdown
dropdown,
) as HTMLDivElement;
let style: string = "";
if (align === "middle") {
@ -476,7 +476,7 @@ async function registerEditorToolbarElement(
editor: Zotero.EditorInstance,
toolbar: HTMLDivElement,
position: "start" | "middle" | "end",
elem: HTMLElement
elem: HTMLElement,
) {
await editor._initPromise;
toolbar.querySelector(`.${position}`)?.append(elem);

View File

@ -22,7 +22,7 @@ async function exportNotes(
exportDocx?: boolean;
exportPDF?: boolean;
exportFreeMind?: boolean;
}
},
) {
let inputNoteItems = noteItems;
// If embedLink or exportNote, create a new note item
@ -51,26 +51,26 @@ async function exportNotes(
if (options.standaloneLink) {
const linkedNoteIds = [] as number[];
for (const noteItem of inputNoteItems) {
let linkedIds: number[] = getLinkedNotesRecursively(
const linkedIds: number[] = getLinkedNotesRecursively(
getNoteLink(noteItem) || "",
linkedNoteIds
linkedNoteIds,
);
linkedNoteIds.push(...linkedIds);
}
const targetNoteItemIds = inputNoteItems.map((item) => item.id);
linkedNoteItems = Zotero.Items.get(
linkedNoteIds.filter((id) => !targetNoteItemIds.includes(id))
linkedNoteIds.filter((id) => !targetNoteItemIds.includes(id)),
);
}
const allNoteItems = Array.from(
new Set(inputNoteItems.concat(linkedNoteItems))
new Set(inputNoteItems.concat(linkedNoteItems)),
);
if (options.exportMD) {
if (options.setAutoSync) {
const raw = await new ztoolkit.FilePicker(
`${getString("fileInterface.sync")} MarkDown File`,
"folder"
"folder",
).open();
if (raw) {
const syncDir = formatPath(raw);
@ -136,7 +136,7 @@ async function toMD(
filename?: string;
keepNoteLink?: boolean;
withYAMLHeader?: boolean;
} = {}
} = {},
) {
let filename = options.filename;
if (!filename) {
@ -144,7 +144,7 @@ async function toMD(
`${Zotero.getString("fileInterface.export")} MarkDown File`,
"save",
[["MarkDown File(*.md)", "*.md"]],
`${noteItem.getNoteTitle()}.md`
`${noteItem.getNoteTitle()}.md`,
).open();
if (!raw) return;
filename = formatPath(raw, ".md");
@ -155,7 +155,7 @@ async function toMD(
async function toSync(
noteItem: Zotero.Item,
syncDir: string,
overwrite: boolean = false
overwrite: boolean = false,
) {
if (!overwrite && addon.api.sync.isSyncNote(noteItem.id)) {
return;
@ -175,7 +175,7 @@ async function toDocx(noteItem: Zotero.Item) {
`${Zotero.getString("fileInterface.export")} MS Word Docx`,
"save",
[["MS Word Docx File(*.docx)", "*.docx"]],
`${noteItem.getNoteTitle()}.docx`
`${noteItem.getNoteTitle()}.docx`,
).open();
if (!raw) return;
const filename = formatPath(raw, ".docx");
@ -187,7 +187,7 @@ async function toFreeMind(noteItem: Zotero.Item) {
`${Zotero.getString("fileInterface.export")} FreeMind XML`,
"save",
[["FreeMind XML File(*.mm)", "*.mm"]],
`${noteItem.getNoteTitle()}.mm`
`${noteItem.getNoteTitle()}.mm`,
).open();
if (!raw) return;
const filename = formatPath(raw, ".mm");
@ -197,9 +197,9 @@ async function toFreeMind(noteItem: Zotero.Item) {
async function embedLinkedNotes(noteItem: Zotero.Item): Promise<string> {
const parser = ztoolkit.getDOMParser();
let newLines: string[] = [];
const newLines: string[] = [];
const noteLines = getLinesInNote(noteItem);
for (let i in noteLines) {
for (const i in noteLines) {
newLines.push(noteLines[i]);
const doc = parser.parseFromString(noteLines[i], "text/html");
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(
"[QuickImportV2]",
"link, noteItem",
[linkParam.link, noteItem]
[linkParam.link, noteItem],
);
newLines.push(html);
}

View File

@ -35,7 +35,7 @@ async function note2docx(noteItem: Zotero.Item) {
jobId,
message: htmlDoc,
},
"*"
"*",
);
await lock.promise;
worker.contentWindow?.removeEventListener("message", listener);
@ -47,7 +47,7 @@ async function getWorker() {
return addon.data.export.docx.worker;
}
const worker = Zotero.Browser.createHiddenBrowser(
window
window,
) as HTMLIFrameElement;
await waitUtilAsync(() => worker.contentDocument?.readyState === "complete");
@ -57,11 +57,11 @@ async function getWorker() {
tag: "script",
properties: {
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;
return worker;

View File

@ -17,7 +17,7 @@ enum OPTIONS {
export async function showExportNoteOptions(
noteIds: number[],
overwriteOptions: Record<string, any> = {}
overwriteOptions: Record<string, any> = {},
) {
const items = Zotero.Items.get(noteIds);
const noteItems: Zotero.Item[] = [];
@ -33,17 +33,20 @@ export async function showExportNoteOptions(
return;
}
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 = () => {
const doc = dialog.window.document;
const standaloneLinkRadio = doc.querySelector(
"#standaloneLink"
"#standaloneLink",
) as HTMLInputElement;
const autoSyncRadio = doc.querySelector("#setAutoSync") as HTMLInputElement;
function updateSyncCheckbox() {
@ -56,7 +59,7 @@ export async function showExportNoteOptions(
}
}
Array.from(doc.querySelectorAll('input[name="linkMode"]')).forEach((elem) =>
elem.addEventListener("change", updateSyncCheckbox)
elem.addEventListener("change", updateSyncCheckbox),
);
updateSyncCheckbox();
};
@ -77,7 +80,7 @@ export async function showExportNoteOptions(
properties: {
innerHTML: `${getString("export.target")}: ${fill(
slice(noteItems[0].getNoteTitle(), 40),
40
40,
)}${
noteItems.length > 1 ? ` and ${noteItems.length - 1} more` : ""
}`,
@ -114,7 +117,7 @@ export async function showExportNoteOptions(
if (data._lastButtonId === "confirm") {
await addon.api.$export.exportNotes(
noteItems,
Object.assign(data as Record<string, boolean>, overwriteOptions)
Object.assign(data as Record<string, boolean>, overwriteOptions),
);
dataKeys.forEach((key) => {
setPref(`export.${key}`, Boolean(data[key]));
@ -187,7 +190,7 @@ function makeCheckboxLine(dataKey: string, callback?: (ev: Event) => void) {
function makeRadioLine(
dataKey: string,
radioName: string,
callback?: (ev: Event) => void
callback?: (ev: Event) => void,
) {
return {
tag: "div",

View File

@ -12,10 +12,10 @@ export async function saveFreeMind(filename: string, noteId: number) {
async function note2mm(
noteItem: Zotero.Item,
options: { withContent?: boolean } = { withContent: true }
options: { withContent?: boolean } = { withContent: true },
) {
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) {
callbackfn(e);
return;
@ -57,7 +57,7 @@ async function note2mm(
};
const convertNode = (node: TreeModel.Node<NoteNodeData>) => {
mmXML += `<node ID="${node.model.id}" TEXT="${html2Escape(
node.model.name || noteItem.getNoteTitle()
node.model.name || noteItem.getNoteTitle(),
)}"><hook NAME="AlwaysUnfoldedNode" />`;
if (
options.withContent &&
@ -70,9 +70,9 @@ async function note2mm(
node.model.lineIndex,
node.hasChildren()
? node.children[0].model.lineIndex
: node.model.endIndex + 1
: node.model.endIndex + 1,
)
.join("\n")
.join("\n"),
)}</body></html></richcontent>`;
}
if (node.hasChildren()) {

View File

@ -7,11 +7,11 @@ export async function saveMD(
options: {
keepNoteLink?: boolean;
withYAMLHeader?: boolean;
}
},
) {
const noteItem = Zotero.Items.get(noteId);
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");
if (hasImage) {
@ -19,7 +19,7 @@ export async function saveMD(
}
await Zotero.File.putContentsAsync(
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) => {
@ -32,7 +32,7 @@ export async function syncMDBatch(saveDir: string, noteIds: number[]) {
await Zotero.File.createDirectoryIfMissingAsync(saveDir);
const attachmentsDir = formatPath(OS.Path.join(saveDir, "attachments"));
const hasImage = noteItems.some((noteItem) =>
noteItem.getNote().includes("<img")
noteItem.getNote().includes("<img"),
);
if (hasImage) {
await Zotero.File.createDirectoryIfMissingAsync(attachmentsDir);
@ -51,7 +51,7 @@ export async function syncMDBatch(saveDir: string, noteIds: number[]) {
itemID: noteItem.id,
md5: Zotero.Utilities.Internal.md5(
addon.api.sync.getMDStatusFromContent(content).content,
false
false,
),
noteMd5: Zotero.Utilities.Internal.md5(noteItem.getNote(), false),
lastsync: new Date().getTime(),

View File

@ -9,7 +9,7 @@ export async function savePDF(noteId: number) {
const win = window.openDialog(
`chrome://${config.addonRef}/content/pdfPrinter.html`,
`${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 Zotero.Promise.delay(3000);

View File

@ -7,7 +7,7 @@ import { waitUtilAsync } from "../utils/wait";
export async function showImageViewer(
srcList: string[],
idx: number,
title: string
title: string,
) {
if (
!addon.data.imageViewer.window ||
@ -19,16 +19,16 @@ export async function showImageViewer(
`${config.addonRef}-imageViewer`,
`chrome,centerscreen,resizable,status,width=500,height=550,dialog=no${
addon.data.imageViewer.pined ? ",alwaysRaised=yes" : ""
}`
}`,
)!;
await waitUtilAsync(
() => addon.data.imageViewer.window?.document.readyState === "complete"
() => addon.data.imageViewer.window?.document.readyState === "complete",
);
const container = addon.data.imageViewer.window.document.querySelector(
".container"
".container",
) as HTMLDivElement;
const img = addon.data.imageViewer.window.document.querySelector(
"#image"
"#image",
) as HTMLImageElement;
addon.data.imageViewer.window.document
@ -75,26 +75,26 @@ export async function showImageViewer(
addon.data.imageViewer.window.document
.querySelector("#save")
?.addEventListener("click", async (e) => {
let parts =
const parts =
addon.data.imageViewer.srcList[addon.data.imageViewer.idx].split(",");
if (!parts[0].includes("base64")) {
return;
}
let mime = parts[0].match(/:(.*?);/)![1];
let bstr = addon.data.imageViewer.window?.atob(parts[1])!;
const mime = parts[0].match(/:(.*?);/)![1];
const bstr = ztoolkit.getGlobal("atob")(parts[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
let ext = Zotero.MIME.getPrimaryExtension(mime, "");
const ext = Zotero.MIME.getPrimaryExtension(mime, "");
const filename = await new ztoolkit.FilePicker(
Zotero.getString("noteEditor.saveImageAs"),
"save",
[[`Image(*.${ext})`, `*.${ext}`]],
`${Zotero.getString("fileTypes.image").toLowerCase()}.${ext}`,
addon.data.imageViewer.window,
"images"
"images",
).open();
if (filename) {
await OS.File.writeAtomic(formatPath(filename), u8arr);
@ -103,7 +103,7 @@ export async function showImageViewer(
"Show in Folder",
(ev) => {
Zotero.File.reveal(filename);
}
},
);
}
});
@ -112,7 +112,7 @@ export async function showImageViewer(
? ICONS.imageViewerPined
: ICONS.imageViewerPin;
addon.data.imageViewer.window.document.querySelector(
"#pin-tooltip"
"#pin-tooltip",
)!.innerHTML = addon.data.imageViewer.pined ? "Unpin" : "Pin";
addon.data.imageViewer.window.document
.querySelector("#pin")
@ -160,7 +160,7 @@ export async function showImageViewer(
if (e.ctrlKey) {
setScale(
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) {
container.scrollLeft -= delta * 10;
@ -201,18 +201,18 @@ export async function showImageViewer(
function setImage() {
(
addon.data.imageViewer.window?.document.querySelector(
"#image"
"#image",
) as HTMLImageElement
).src = addon.data.imageViewer.srcList[addon.data.imageViewer.idx];
setTitle();
(
addon.data.imageViewer.window?.document.querySelector(
"#left-container"
"#left-container",
) as HTMLButtonElement
).style.opacity = addon.data.imageViewer.idx === 0 ? "0.5" : "1";
(
addon.data.imageViewer.window?.document.querySelector(
"#right-container"
"#right-container",
) as HTMLButtonElement
).style.opacity =
addon.data.imageViewer.idx === addon.data.imageViewer.srcList.length - 1
@ -244,29 +244,29 @@ function setScale(scaling: number) {
addon.data.imageViewer.scaling = 0.1;
}
const container = addon.data.imageViewer.window?.document.querySelector(
".container"
".container",
) as HTMLDivElement;
(
addon.data.imageViewer.window?.document.querySelector(
"#image"
"#image",
) as HTMLImageElement
).style.width = `calc(100% * ${addon.data.imageViewer.scaling})`;
if (addon.data.imageViewer.scaling > 1) {
container.scrollLeft +=
addon.data.imageViewer.anchorPosition?.left! *
addon.data.imageViewer.anchorPosition!.left *
(addon.data.imageViewer.scaling - oldScale);
container.scrollTop +=
addon.data.imageViewer.anchorPosition?.top! *
addon.data.imageViewer.anchorPosition!.top *
(addon.data.imageViewer.scaling - oldScale);
}
(
addon.data.imageViewer.window?.document.querySelector(
"#bigger-container"
"#bigger-container",
) as HTMLButtonElement
).style.opacity = addon.data.imageViewer.scaling === 10 ? "0.5" : "1";
(
addon.data.imageViewer.window?.document.querySelector(
"#smaller-container"
"#smaller-container",
) as HTMLButtonElement
).style.opacity = addon.data.imageViewer.scaling === 0.1 ? "0.5" : "1";
// (
@ -276,7 +276,7 @@ function setScale(scaling: number) {
function setTitle() {
addon.data.imageViewer.window!.document.querySelector(
"title"
"title",
)!.innerText! = `${addon.data.imageViewer.idx + 1}/${
addon.data.imageViewer.srcList.length
}:${addon.data.imageViewer.title}`;
@ -288,6 +288,6 @@ function setPin() {
showImageViewer(
addon.data.imageViewer.srcList,
addon.data.imageViewer.idx,
addon.data.imageViewer.title
addon.data.imageViewer.title,
);
}

View File

@ -8,7 +8,7 @@ export async function fromMD(
ignoreVersion?: boolean;
append?: boolean;
appendLineIndex?: number;
} = {}
} = {},
) {
let mdStatus: MDStatus;
try {
@ -26,7 +26,7 @@ export async function fromMD(
) {
if (
!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;

View File

@ -13,7 +13,7 @@ export function registerMenus() {
icon: `chrome://${config.addonRef}/content/icons/favicon.png`,
commandListener: (ev) => {
addon.hooks.onShowExportNoteOptions(
ZoteroPane.getSelectedItems().map((item) => item.id)
ZoteroPane.getSelectedItems().map((item) => item.id),
);
},
});
@ -36,7 +36,7 @@ export function registerMenus() {
// menuEdit
const menuEditAnchor = document.querySelector(
"#menu_EditPreferencesItem"
"#menu_EditPreferencesItem",
) as XUL.MenuItem;
ztoolkit.Menu.register(
"menuEdit",
@ -49,7 +49,7 @@ export function registerMenus() {
},
},
"before",
menuEditAnchor
menuEditAnchor,
);
ztoolkit.Menu.register(
"menuEdit",
@ -62,7 +62,7 @@ export function registerMenus() {
},
},
"before",
menuEditAnchor
menuEditAnchor,
);
ztoolkit.Menu.register(
"menuEdit",
@ -75,7 +75,7 @@ export function registerMenus() {
},
},
"before",
menuEditAnchor
menuEditAnchor,
);
ztoolkit.Menu.register(
"menuEdit",
@ -88,13 +88,13 @@ export function registerMenus() {
},
},
"before",
menuEditAnchor
menuEditAnchor,
);
ztoolkit.Menu.register(
"menuEdit",
{ tag: "menuseparator" },
"before",
menuEditAnchor
menuEditAnchor,
);
// menuTools
@ -110,7 +110,7 @@ export function registerMenus() {
// menuFile
const menuFileAnchor = document.querySelector(
"#menu_newCollection"
"#menu_newCollection",
) as XUL.MenuItem;
const recentMainNotesMenuId = "zotero-recent-main-notes-menu";
const recentMainNotesMenuPopupId = "zotero-recent-main-notes-popup";
@ -130,7 +130,7 @@ export function registerMenus() {
popupId: recentMainNotesMenuPopupId,
},
"after",
menuFileAnchor
menuFileAnchor,
);
document
@ -142,7 +142,7 @@ export function registerMenus() {
((getPref("recentMainNoteIds") as string) || "")
.split(",")
.map((id) => parseInt(id))
.filter((id) => id !== addon.data.workspace.mainId)
.filter((id) => id !== addon.data.workspace.mainId),
)
.filter((item) => item.isNote())
.map((item) => ({
@ -155,11 +155,11 @@ export function registerMenus() {
: "📁" +
Zotero.Collections.get(item.getCollections())
.map(
(collection) => (collection as Zotero.Collection).name
(collection) => (collection as Zotero.Collection).name,
)
.join(", ")
}`,
200
200,
),
},
listeners: [
@ -188,7 +188,7 @@ export function registerMenus() {
children: children.length === 0 ? defaultChildren : children,
enableElementRecord: false,
},
popup
popup,
);
return true;
});
@ -213,13 +213,13 @@ export function registerMenus() {
},
},
"after",
menuFileAnchor
menuFileAnchor,
);
ztoolkit.Menu.register(
"menuFile",
{ tag: "menuseparator" },
"after",
menuFileAnchor
menuFileAnchor,
);
// a copy of create note menu in library
ztoolkit.Menu.register(
@ -231,7 +231,7 @@ export function registerMenus() {
commandListener: () => addon.hooks.onCreateNoteFromMD(),
},
"after",
menuFileAnchor
menuFileAnchor,
);
ztoolkit.Menu.register(
"menuFile",
@ -243,7 +243,7 @@ export function registerMenus() {
addon.hooks.onCreateNoteFromTemplate("item", "library"),
},
"after",
menuFileAnchor
menuFileAnchor,
);
ztoolkit.Menu.register(
"menuFile",
@ -254,7 +254,7 @@ export function registerMenus() {
commandListener: () => addon.hooks.onCreateNoteFromTemplate("standalone"),
},
"after",
menuFileAnchor
menuFileAnchor,
);
ztoolkit.Menu.register(
"menuFile",
@ -265,7 +265,7 @@ export function registerMenus() {
commandListener: addon.hooks.onCreateWorkspaceNote,
},
"after",
menuFileAnchor
menuFileAnchor,
);
// create note menu in library
@ -301,7 +301,7 @@ export function registerMenus() {
// create note menu in reader side panel
ztoolkit.Menu.register(
document.querySelector(
"#context-pane-add-child-note-button-popup"
"#context-pane-add-child-note-button-popup",
) as XUL.MenuPopup,
{
tag: "menuitem",
@ -309,6 +309,6 @@ export function registerMenus() {
icon: `chrome://${config.addonRef}/content/icons/favicon.png`,
commandListener: () =>
addon.hooks.onCreateNoteFromTemplate("item", "reader"),
}
},
);
}

View File

@ -18,7 +18,7 @@ export function registerNotify(types: _ZoteroTypes.Notifier.Type[]) {
(e: Event) => {
unregisterNotify(notifyID);
},
false
false,
);
}

View File

@ -56,8 +56,11 @@ async function updatePrefsUI() {
// You can initialize some UI elements on prefs window
// with addon.data.prefs.window.document
// Or bind some events to the elements
if (!addon.data.prefs?.window) {
return;
}
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`)
.setProp({
id: `${config.addonRef}-prefs-table`,
@ -66,7 +69,7 @@ async function updatePrefsUI() {
columns: addon.data.prefs?.columns.map((column) =>
Object.assign(column, {
label: getString(column.label) || column.label,
})
}),
),
showHeader: true,
multiSelect: true,
@ -80,7 +83,7 @@ async function updatePrefsUI() {
addon.data.prefs?.rows[index] || {
title: "no data",
detail: "no data",
}
},
)
// Show a progress window when selection changes
.setProp("onSelectionChange", (selection) => {
@ -100,7 +103,7 @@ async function updatePrefsUI() {
if (event.key == "Delete" || (Zotero.isMac && event.key == "Backspace")) {
addon.data.prefs!.rows =
addon.data.prefs?.rows.filter(
(v, i) => !tableHelper.treeInstance.selection.isSelected(i)
(v, i) => !tableHelper.treeInstance.selection.isSelected(i),
) || [];
tableHelper.render();
return false;
@ -110,7 +113,7 @@ async function updatePrefsUI() {
// For find-as-you-type
.setProp(
"getRowString",
(index) => addon.data.prefs?.rows[index].title || ""
(index) => addon.data.prefs?.rows[index].title || "",
)
// Render the table.
.render(-1, () => {
@ -123,23 +126,23 @@ async function updatePrefsUI() {
function bindPrefEvents() {
addon.data
.prefs!.window.document.querySelector(
`#zotero-prefpane-${config.addonRef}-enable`
`#zotero-prefpane-${config.addonRef}-enable`,
)
?.addEventListener("command", (e) => {
ztoolkit.log(e);
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
.prefs!!.window.document.querySelector(
`#zotero-prefpane-${config.addonRef}-input`
.prefs!.window.document.querySelector(
`#zotero-prefpane-${config.addonRef}-input`,
)
?.addEventListener("change", (e) => {
ztoolkit.log(e);
addon.data.prefs!.window.alert(
`Successfully changed to ${(e.target as HTMLInputElement).value}!`
`Successfully changed to ${(e.target as HTMLInputElement).value}!`,
);
});
}

View File

@ -8,7 +8,7 @@ export function registerReaderInitializer() {
ztoolkit.ReaderInstance.register(
"initialized",
`${config.addonRef}-annotationButtons`,
initializeReaderAnnotationButton
initializeReaderAnnotationButton,
);
// Force re-initialize
Zotero.Reader._readers.forEach((r) => {
@ -38,7 +38,7 @@ export async function checkReaderAnnotationButton(items: Zotero.Item[]) {
}
async function initializeReaderAnnotationButton(
instance: _ZoteroTypes.ReaderInstance
instance: _ZoteroTypes.ReaderInstance,
): Promise<Zotero.Item[]> {
if (!instance) {
return [];
@ -68,7 +68,7 @@ async function initializeReaderAnnotationButton(
const libraryID = Zotero.Items.get(instance.itemID).libraryID;
const annotationItem = (await Zotero.Items.getByLibraryAndKeyAsync(
libraryID,
itemKey
itemKey,
)) as Zotero.Item;
if (!annotationItem) {
@ -90,7 +90,7 @@ async function initializeReaderAnnotationButton(
listener: (e) => {
createNoteFromAnnotation(
annotationItem,
(e as MouseEvent).shiftKey ? "standalone" : "auto"
(e as MouseEvent).shiftKey ? "standalone" : "auto",
);
e.preventDefault();
},
@ -105,7 +105,7 @@ async function initializeReaderAnnotationButton(
type: "mouseout",
listener: (e) => {
(e.target as HTMLElement).style.removeProperty(
"background-color"
"background-color",
);
},
},
@ -139,7 +139,7 @@ async function initializeReaderAnnotationButton(
type: "mouseout",
listener: (e) => {
(e.target as HTMLElement).style.removeProperty(
"background-color"
"background-color",
);
},
},
@ -153,14 +153,14 @@ async function initializeReaderAnnotationButton(
tag: "fragment",
children: annotationButtons,
},
moreButton
moreButton,
);
}
return hitItems;
}
async function unInitializeReaderAnnotationButton(
instance: _ZoteroTypes.ReaderInstance
instance: _ZoteroTypes.ReaderInstance,
): Promise<void> {
if (!instance) {
return;
@ -180,7 +180,7 @@ async function unInitializeReaderAnnotationButton(
async function createNoteFromAnnotation(
annotationItem: Zotero.Item,
openMode: "standalone" | "auto" = "auto"
openMode: "standalone" | "auto" = "auto",
) {
const annotationTags = annotationItem.getTags().map((_) => _.tag);
const linkRegex = new RegExp("^zotero://note/(.*)$");
@ -209,7 +209,7 @@ async function createNoteFromAnnotation(
const renderredTemplate = await addon.api.template.runTemplate(
"[QuickNoteV5]",
"annotationItem, topItem, noteItem",
[annotationItem, annotationItem.parentItem!.parentItem, note]
[annotationItem, annotationItem.parentItem!.parentItem, note],
);
await addLineToNote(note, renderredTemplate);

View File

@ -38,7 +38,7 @@ async function getRelatedNoteIds(noteId: number): Promise<number[]> {
}
const subNoteIds = (
await Promise.all(
linkMatches.map(async (link) => getNoteLinkParams(link).noteItem)
linkMatches.map(async (link) => getNoteLinkParams(link).noteItem),
)
)
.filter((item) => item && item.isNote())
@ -49,7 +49,7 @@ async function getRelatedNoteIds(noteId: number): Promise<number[]> {
}
async function getRelatedNoteIdsFromNotes(
noteIds: number[]
noteIds: number[],
): Promise<number[]> {
let allNoteIds: number[] = [];
for (const noteId of noteIds) {
@ -101,7 +101,7 @@ function getNoteStatus(noteId: number) {
if (idx != -1) {
ret.content = fullContent.substring(
idx + match[0].length,
fullContent.length - ret.tail.length
fullContent.length - ret.tail.length,
);
}
return ret;
@ -117,7 +117,7 @@ function getSyncStatus(noteId?: number): SyncStatus {
itemID: -1,
});
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(
source: Zotero.Item | number | string
source: Zotero.Item | number | string,
): Promise<MDStatus> {
let ret: MDStatus = {
meta: null,
@ -165,13 +165,13 @@ async function getMDStatus(
}
filepath = Zotero.File.normalizeToUnix(filepath);
if (await OS.File.exists(filepath)) {
let contentRaw = (await OS.File.read(filepath, {
const contentRaw = (await OS.File.read(filepath, {
encoding: "utf-8",
})) as string;
ret = getMDStatusFromContent(contentRaw);
const pathSplit = filepath.split("/");
ret.filedir = Zotero.File.normalizeToUnix(
pathSplit.slice(0, -1).join("/")
pathSplit.slice(0, -1).join("/"),
);
ret.filename = filepath.split("/").pop() || "";
const stat = await OS.File.stat(filepath);
@ -214,7 +214,7 @@ async function getMDFileName(noteId: number, searchDir?: string) {
}
}
}
}
},
);
if (matchedFileName) {
return matchedFileName;
@ -224,6 +224,6 @@ async function getMDFileName(noteId: number, searchDir?: string) {
return await addon.api.template.runTemplate(
"[ExportMDFileNameV2]",
"noteItem",
[noteItem]
[noteItem],
);
}

View File

@ -50,7 +50,7 @@ export async function showSyncDiff(noteId: number, mdPath: string) {
} else {
// Otherwise, merge manually
const imageAttachemnts = Zotero.Items.get(noteItem.getAttachments()).filter(
(attch) => attch.isEmbeddedImageAttachment()
(attch) => attch.isEmbeddedImageAttachment(),
);
const imageData = {} as Record<string, string>;
for (const image of imageAttachemnts) {
@ -66,10 +66,10 @@ export async function showSyncDiff(noteId: number, mdPath: string) {
addon.data.sync.diff.window = window.open(
`chrome://${config.addonRef}/content/syncDiff.html`,
`${config.addonRef}-syncDiff`,
`chrome,centerscreen,resizable,status,width=900,height=550`
`chrome,centerscreen,resizable,status,width=900,height=550`,
)!;
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;
@ -86,7 +86,7 @@ export async function showSyncDiff(noteId: number, mdPath: string) {
Object.assign(change, {
id: id,
text: change.value,
})
}),
);
win.imageData = imageData;
@ -115,7 +115,7 @@ export async function showSyncDiff(noteId: number, mdPath: string) {
switch (io.type) {
case "skip":
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?.close();

View File

@ -8,20 +8,26 @@ function setSyncing() {
const syncPeriod = getPref("syncPeriodSeconds") as number;
if (syncPeriod > 0) {
showHint(`${getString("sync.start.hint")} ${syncPeriod} s`);
const timer = ztoolkit.getGlobal("setInterval")(() => {
if (!addon.data.alive) {
showHint(getString("sync.stop.hint"));
ztoolkit.getGlobal("clearInterval")(timer);
}
// Only when Zotero is active and focused
if (document.hasFocus() && (getPref("syncPeriodSeconds") as number) > 0) {
callSyncing(undefined, {
quiet: true,
skipActive: true,
reason: "auto",
});
}
}, Number(syncPeriod) * 1000);
const timer = ztoolkit.getGlobal("setInterval")(
() => {
if (!addon.data.alive) {
showHint(getString("sync.stop.hint"));
ztoolkit.getGlobal("clearInterval")(timer);
}
// Only when Zotero is active and focused
if (
document.hasFocus() &&
(getPref("syncPeriodSeconds") as number) > 0
) {
callSyncing(undefined, {
quiet: true,
skipActive: true,
reason: "auto",
});
}
},
Number(syncPeriod) * 1000,
);
}
}
@ -31,7 +37,7 @@ async function callSyncing(
quiet: true,
skipActive: true,
reason: "unknown",
}
},
) {
// Always log in development mode
if (addon.data.env === "development") {
@ -61,11 +67,11 @@ async function callSyncing(
.filter(
(editor) =>
!Components.utils.isDeadWrapper(editor._iframeWindow) &&
editor._iframeWindow.document.hasFocus()
editor._iframeWindow.document.hasFocus(),
)
.map((editor) => editor._item.id);
const filteredItems = items.filter(
(item) => !activeNoteIds.includes(item.id)
(item) => !activeNoteIds.includes(item.id),
);
skippedCount = items.length - filteredItems.length;
items = filteredItems;
@ -76,7 +82,7 @@ async function callSyncing(
progress = new ztoolkit.ProgressWindow(
`[${getString("sync.running.hint.title")}] ${
addon.data.env === "development" ? reason : "Better Notes"
}`
}`,
)
.createLine({
text: `[${getString("sync.running.hint.check")}] 0/${
@ -95,7 +101,7 @@ async function callSyncing(
for (const item of items) {
const syncStatus = addon.api.sync.getSyncStatus(item.id);
const filepath = syncStatus.path;
let compareResult = await doCompare(item);
const compareResult = await doCompare(item);
switch (compareResult) {
case SyncCode.NoteAhead:
if (Object.keys(toExport).includes(filepath)) {
@ -139,7 +145,7 @@ async function callSyncing(
for (const syncStatus of toImport) {
progress?.changeLine({
text: `[${getString(
"sync.running.hint.updateNote"
"sync.running.hint.updateNote",
)}] ${i}/${totalCount}, ${toDiff.length} queuing...`,
progress: ((i - 1) / totalCount) * 100,
});
@ -160,7 +166,7 @@ async function callSyncing(
await addon.hooks.onShowSyncDiff(
syncStatus.itemID,
OS.Path.join(syncStatus.path, syncStatus.filename)
OS.Path.join(syncStatus.path, syncStatus.filename),
);
i += 1;
}
@ -170,10 +176,10 @@ async function callSyncing(
text:
(syncCount
? `[${getString(
"sync.running.hint.finish"
"sync.running.hint.finish",
)}] ${syncCount} ${getString("sync.running.hint.synced")}`
: `[${getString("sync.running.hint.finish")}] ${getString(
"sync.running.hint.upToDate"
"sync.running.hint.upToDate",
)}`) + (skippedCount ? `, ${skippedCount} skipped.` : ""),
progress: 100,
});

View File

@ -18,7 +18,7 @@ export async function showSyncInfo(noteId: number) {
tag: "label",
properties: {
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,
callback: (ev) => {
Zotero.File.reveal(
formatPath(OS.Path.join(status.path, status.filename))
formatPath(OS.Path.join(status.path, status.filename)),
);
},
})

View File

@ -15,7 +15,7 @@ export async function showSyncManager() {
`chrome://${config.addonRef}/content/syncManager.xhtml`,
`${config.addonRef}-syncManager`,
`chrome,centerscreen,resizable,status,width=800,height=400,dialog=no`,
windowArgs
windowArgs,
)!;
await windowArgs._initPromise.promise;
addon.data.sync.manager.window = win;
@ -46,7 +46,7 @@ export async function showSyncManager() {
].map((column) =>
Object.assign(column, {
label: getString(column.label),
})
}),
),
showHeader: true,
multiSelect: true,
@ -65,7 +65,7 @@ export async function showSyncManager() {
noteName: "no data",
lastSync: "no data",
filePath: "no data",
}
},
)
.setProp("onSelectionChange", (selection) => {
updateButtons();
@ -84,21 +84,21 @@ export async function showSyncManager() {
.setProp("onActivate", (ev) => {
const noteIds = getSelectedNoteIds();
noteIds.forEach((noteId) =>
addon.hooks.onOpenNote(noteId, "standalone")
addon.hooks.onOpenNote(noteId, "standalone"),
);
return true;
})
.setProp(
"getRowString",
(index) => addon.data.prefs?.rows[index].title || ""
(index) => addon.data.prefs?.rows[index].title || "",
)
.render();
const refreshButton = win.document.querySelector(
"#refresh"
"#refresh",
) as HTMLButtonElement;
const syncButton = win.document.querySelector("#sync") as HTMLButtonElement;
const unSyncButton = win.document.querySelector(
"#unSync"
"#unSync",
) as HTMLButtonElement;
refreshButton.addEventListener("click", (ev) => {
refresh();
@ -148,7 +148,7 @@ function updateButtons() {
return;
}
const unSyncButton = win.document.querySelector(
"#unSync"
"#unSync",
) as HTMLButtonElement;
if (
addon.data.sync.manager.tableHelper?.treeInstance.selection.selected.size
@ -179,13 +179,13 @@ async function unSyncNotes(itemIds: number[]) {
return;
}
const unSyncLinkedNotes = addon.data.sync.manager.window?.confirm(
`Un-sync their linked notes?`
`Un-sync their linked notes?`,
);
if (unSyncLinkedNotes) {
for (const item of Zotero.Items.get(itemIds)) {
let linkedIds: number[] = getLinkedNotesRecursively(
const linkedIds: number[] = getLinkedNotesRecursively(
getNoteLink(item) || "",
itemIds
itemIds,
);
itemIds.push(...linkedIds);
}

View File

@ -19,7 +19,7 @@ async function runTemplate(
useDefault: true,
dryRun: false,
stage: "default",
}
},
): Promise<string> {
ztoolkit.log(`runTemplate: ${key}`);
if (argList.length > 0) {
@ -64,8 +64,8 @@ async function runTemplate(
// Check the markdown pragma
templateLines = templateLines.slice(startIndex + 1, endIndex);
let useMarkdown = false;
let mdIndex = templateLines.findIndex((line) =>
line.startsWith("// @use-markdown")
const mdIndex = templateLines.findIndex((line) =>
line.startsWith("// @use-markdown"),
);
if (mdIndex >= 0) {
useMarkdown = true;
@ -87,7 +87,7 @@ async function runTemplate(
/\$\{\{([\s\S]*?)\}\}\$/g,
(match, content) => {
return constructFunction(content);
}
},
);
try {
@ -110,9 +110,9 @@ async function runTextTemplate(
options: {
targetNoteId?: number;
dryRun?: boolean;
}
},
) {
let { targetNoteId, dryRun } = options;
const { targetNoteId, dryRun } = options;
const targetNoteItem = Zotero.Items.get(targetNoteId || -1);
const sharedObj = {};
return await runTemplate(
@ -121,7 +121,7 @@ async function runTextTemplate(
[targetNoteItem, sharedObj],
{
dryRun,
}
},
);
}
@ -131,7 +131,7 @@ async function runItemTemplate(
itemIds?: number[];
targetNoteId?: number;
dryRun?: boolean;
}
},
): Promise<string> {
/**
* args:
@ -139,7 +139,8 @@ async function runItemTemplate(
* default stage: topItem, itemNotes, copyNoteImage, sharedObj
* afterloop stage: items, copyNoteImage, sharedObj
*/
let { itemIds, targetNoteId, dryRun } = options;
let { itemIds } = options;
const { targetNoteId, dryRun } = options;
if (!itemIds) {
itemIds = await getItemTemplateData();
}
@ -169,8 +170,8 @@ async function runItemTemplate(
stage: "beforeloop",
useDefault: false,
dryRun,
}
)
},
),
);
for (const topItem of items) {
@ -184,8 +185,8 @@ async function runItemTemplate(
[topItem, targetNoteItem, itemNotes, copyNoteImage, sharedObj],
{
dryRun,
}
)
},
),
);
}
@ -198,8 +199,8 @@ async function runItemTemplate(
stage: "afterloop",
useDefault: false,
dryRun,
}
)
},
),
);
let html = results.join("\n");
@ -207,7 +208,7 @@ async function runItemTemplate(
html = await copyEmbeddedImagesInHTML(
html,
targetNoteItem,
copyImageRefNotes
copyImageRefNotes,
);
} else {
html = await renderNoteHTML(html, copyImageRefNotes);
@ -230,9 +231,9 @@ async function getItemTemplateData() {
slice(
(firstSelectedItem.getField("title") as string) ||
firstSelectedItem.key,
40
40,
),
40
40,
)} ${
librarySelectedIds.length > 1
? `and ${librarySelectedIds.length - 1} more`

View File

@ -13,7 +13,7 @@ export {
// Controller
function getTemplateKeys(): { name: string }[] {
let templateKeys = getPref("templateKeys") as string;
const templateKeys = getPref("templateKeys") as string;
return templateKeys ? JSON.parse(templateKeys) : [];
}
@ -52,7 +52,7 @@ function getTemplateText(keyName: string): string {
function setTemplate(
template: NoteTemplate,
updatePrompt: boolean = true
updatePrompt: boolean = true,
): void {
template = JSON.parse(JSON.stringify(template));
addTemplateKey({ name: template.name });
@ -64,7 +64,7 @@ function setTemplate(
function removeTemplate(
keyName: string | undefined,
updatePrompt: boolean = true
updatePrompt: boolean = true,
): void {
if (typeof keyName === "undefined") {
return;
@ -77,7 +77,7 @@ function removeTemplate(
}
function initTemplates() {
let templateKeys = getTemplateKeys();
const templateKeys = getTemplateKeys();
const currentNames = templateKeys.map((t) => t.name);
for (const defaultTemplate of addon.api.template.DEFAULT_TEMPLATES) {
if (!currentNames.includes(defaultTemplate.name)) {

View File

@ -18,13 +18,13 @@ export async function showTemplateEditor() {
`chrome://${config.addonRef}/content/templateEditor.xhtml`,
`${config.addonRef}-templateEditor`,
`chrome,centerscreen,resizable,status,width=600,height=400,dialog=no`,
windowArgs
windowArgs,
)!;
addon.data.templateEditor.window = _window;
await windowArgs._initPromise.promise;
updateData();
addon.data.templateEditor.tableHelper = new ztoolkit.VirtualizedTable(
_window!
_window!,
)
.setContainerId("table-container")
.setProp({
@ -40,7 +40,7 @@ export async function showTemplateEditor() {
].map((column) =>
Object.assign(column, {
label: getString(column.label),
})
}),
),
showHeader: true,
multiSelect: false,
@ -53,7 +53,7 @@ export async function showTemplateEditor() {
(index) =>
(addon.data.templateEditor.templates[index] as { name: string }) || {
name: "no data",
}
},
)
.setProp("onSelectionChange", (selection) => {
updateEditor();
@ -72,7 +72,7 @@ export async function showTemplateEditor() {
})
.setProp(
"getRowString",
(index) => addon.data.prefs?.rows[index].title || ""
(index) => addon.data.prefs?.rows[index].title || "",
)
.render();
_window.document
@ -87,12 +87,12 @@ export async function showTemplateEditor() {
});
_window.document.querySelector("#help")?.addEventListener("click", (ev) => {
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) => {
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) => {
@ -147,22 +147,22 @@ function updateEditor() {
const templateText = addon.api.template.getTemplateText(name);
const header = addon.data.templateEditor.window?.document.getElementById(
"editor-name"
"editor-name",
) as HTMLInputElement;
const text = addon.data.templateEditor.window?.document.getElementById(
"editor-textbox"
"editor-textbox",
) as HTMLTextAreaElement;
const saveTemplate =
addon.data.templateEditor.window?.document.getElementById(
"save"
"save",
) as XUL.Button;
const deleteTemplate =
addon.data.templateEditor.window?.document.getElementById(
"delete"
"delete",
) as XUL.Button;
const resetTemplate =
addon.data.templateEditor.window?.document.getElementById(
"reset"
"reset",
) as XUL.Button;
if (!name) {
header.value = "";
@ -194,11 +194,11 @@ function updateEditor() {
async function updatePreview() {
const name = getSelectedTemplateName();
let html = (await addon.api.template.renderTemplatePreview(name))
const html = (await addon.api.template.renderTemplatePreview(name))
.replace(/&nbsp;/g, "#160;")
.replace(/<br>/g, "<br/>")
.replace(/<hr>/g, "<hr/>")
.replace(/<img([^>]+)\>/g, "<img$1/>");
.replace(/<img([^>]+)>/g, "<img$1/>");
const win = addon.data.templateEditor.window;
const container = win?.document.getElementById("preview-container");
if (container) {
@ -207,15 +207,18 @@ async function updatePreview() {
} else {
container.innerHTML = "";
container.appendChild(
ztoolkit.getDOMParser().parseFromString(html, "text/html").body
ztoolkit.getDOMParser().parseFromString(html, "text/html").body,
);
}
}
}
function getSelectedTemplateName() {
const selectedTemplate = addon.data.templateEditor.templates.find((v, i) =>
addon.data.templateEditor.tableHelper?.treeInstance.selection.isSelected(i)
const selectedTemplate = addon.data.templateEditor.templates.find(
(v, i) =>
addon.data.templateEditor.tableHelper?.treeInstance.selection.isSelected(
i,
),
);
return selectedTemplate?.name || "";
}
@ -232,7 +235,7 @@ function createTemplate() {
async function importNoteTemplate() {
const ids = await itemPicker();
const note: Zotero.Item = Zotero.Items.get(ids).filter((item: Zotero.Item) =>
item.isNote()
item.isNote(),
)[0];
if (!note) {
return;
@ -248,10 +251,10 @@ async function importNoteTemplate() {
function saveSelectedTemplate() {
const name = getSelectedTemplateName();
const header = addon.data.templateEditor.window?.document.getElementById(
"editor-name"
"editor-name",
) as HTMLInputElement;
const text = addon.data.templateEditor.window?.document.getElementById(
"editor-textbox"
"editor-textbox",
) as HTMLTextAreaElement;
if (
@ -259,7 +262,7 @@ function saveSelectedTemplate() {
header.value !== name
) {
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;
}
@ -284,7 +287,7 @@ function deleteSelectedTemplate() {
const name = getSelectedTemplateName();
if (addon.api.template.SYSTEM_TEMPLATE_NAMES.includes(name)) {
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;
}
@ -296,7 +299,7 @@ function resetSelectedTemplate() {
const name = getSelectedTemplateName();
if (addon.api.template.SYSTEM_TEMPLATE_NAMES.includes(name)) {
const text = addon.data.templateEditor.window?.document.getElementById(
"editor-textbox"
"editor-textbox",
) as HTMLTextAreaElement;
text.value =
addon.api.template.DEFAULT_TEMPLATES.find((t) => t.name === name)?.text ||
@ -325,7 +328,7 @@ ${content
`;
new ztoolkit.Clipboard().addText(yaml, "text/unicode").copy();
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",
[["yaml", "*.yaml"]],
`bn-template-backup-${time}.yaml`
`bn-template-backup-${time}.yaml`,
).open();
if (!filepath) {
return;
@ -358,7 +361,7 @@ async function restoreTemplates(win: Window) {
[["yaml", "*.yaml"]],
undefined,
win,
"text"
"text",
).open();
if (!filepath) {
return;
@ -370,7 +373,7 @@ async function restoreTemplates(win: Window) {
for (const t of templates) {
if (existingNames.includes(t.name)) {
const overwrite = win.confirm(
`Template ${t.name} already exists. Overwrite?`
`Template ${t.name} already exists. Overwrite?`,
);
if (!overwrite) {
continue;

View File

@ -7,17 +7,17 @@ export { updateTemplatePicker, showTemplatePicker };
function showTemplatePicker(
mode: "insert",
data?: { noteId?: number; lineIndex?: number }
data?: { noteId?: number; lineIndex?: number },
): void;
function showTemplatePicker(
mode: "create",
data?: { noteType?: "standalone" | "item"; parentItemId?: number }
data?: { noteType?: "standalone" | "item"; parentItemId?: number },
): void;
function showTemplatePicker(mode: "export", data?: {}): void;
function showTemplatePicker(mode: "export", data?: Record<string, never>): void;
function showTemplatePicker(): void;
function showTemplatePicker(
mode: typeof addon.data.templatePicker.mode = "insert",
data: Record<string, any> = {}
data: Record<string, any> = {},
) {
if (addon.data.prompt) {
addon.data.templatePicker.mode = mode;
@ -25,8 +25,8 @@ function showTemplatePicker(
addon.data.prompt.promptNode.style.display = "flex";
addon.data.prompt.showCommands(
addon.data.prompt.commands.filter(
(cmd) => cmd.label === "BNotes Template"
)
(cmd) => cmd.label === "BNotes Template",
),
);
}
}
@ -38,7 +38,7 @@ function updateTemplatePicker() {
templates
.filter(
(template) =>
!addon.api.template.SYSTEM_TEMPLATE_NAMES.includes(template.name)
!addon.api.template.SYSTEM_TEMPLATE_NAMES.includes(template.name),
)
.map((template) => {
return {
@ -46,7 +46,7 @@ function updateTemplatePicker() {
label: "BNotes Template",
callback: getTemplatePromptHandler(template.name),
};
})
}),
);
if (!addon.data.prompt) {
addon.data.prompt = ToolkitGlobal.getInstance().prompt.instance;
@ -77,7 +77,7 @@ function getTemplatePromptHandler(name: string) {
async function insertTemplateCallback(name: string) {
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 = "";
if (name.toLowerCase().startsWith("[item]")) {
@ -92,7 +92,7 @@ async function insertTemplateCallback(name: string) {
await addLineToNote(
targetNoteItem,
html,
addon.data.templatePicker.data.lineIndex
addon.data.templatePicker.data.lineIndex,
);
}

View File

@ -5,7 +5,7 @@ export { renderTemplatePreview };
async function renderTemplatePreview(
templateName: string,
inputItems?: Zotero.Item[]
inputItems?: Zotero.Item[],
): Promise<string> {
let html: string = "<p>Preview rendering failed</p>";
if (!inputItems) {
@ -33,7 +33,7 @@ async function renderTemplatePreview(
[data],
{
dryRun: true,
}
},
);
}
} else if (templateName.includes("ExportMDFileHeader")) {
@ -48,7 +48,7 @@ async function renderTemplatePreview(
[data],
{
dryRun: true,
}
},
);
const header = Object.assign({}, JSON.parse(raw), {
version: data.version,
@ -73,7 +73,7 @@ async function renderTemplatePreview(
[link, linkText, subNoteItem, noteItem],
{
dryRun: true,
}
},
);
}
} else if (templateName.includes("QuickBackLink")) {
@ -92,7 +92,7 @@ async function renderTemplatePreview(
[link, linkText, subNoteItem, noteItem],
{
dryRun: true,
}
},
);
}
} else if (templateName.includes("QuickImport")) {
@ -109,7 +109,7 @@ async function renderTemplatePreview(
[link, noteItem],
{
dryRun: true,
}
},
);
}
} else if (templateName.includes("QuickNote")) {

View File

@ -20,7 +20,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
id: string,
content: string,
title: string,
callback: (ev: Event) => void
callback: (ev: Event) => void,
) {
return {
tag: "div",
@ -119,7 +119,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
getString("workspace.switchOutline"),
(ev) => {
setOutline(container);
}
},
),
makeTooltipProp(
makeId("saveOutlineImage"),
@ -127,7 +127,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
getString("workspace.saveOutlineImage"),
(ev) => {
saveImage(container);
}
},
),
makeTooltipProp(
makeId("saveOutlineFreeMind"),
@ -135,7 +135,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
getString("workspace.saveOutlineFreeMind"),
(ev) => {
saveFreeMind();
}
},
),
],
},
@ -219,7 +219,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
},
],
},
container
container,
);
// Manually add custom editor items in Zotero 7
if (ztoolkit.isZotero7()) {
@ -227,10 +227,10 @@ export function initWorkspace(container: XUL.Box | undefined) {
const customElements = container.ownerGlobal
.customElements as CustomElementRegistry;
const mainEditorContainer = container.querySelector(
`#${makeId("editor-main-container")}`
`#${makeId("editor-main-container")}`,
);
const previewEditorContainer = container.querySelector(
`#${makeId("editor-preview-container")}`
`#${makeId("editor-preview-container")}`,
);
const mainEditor = new (customElements.get("note-editor")!)();
mainEditor.id = makeId("editor-main");
@ -243,7 +243,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
}
const outlineContainer = container.querySelector(
`#${makeId("outline-container")}`
`#${makeId("outline-container")}`,
) as XUL.Box;
const outlineMut = new (ztoolkit.getGlobal("MutationObserver"))(
(mutations) => {
@ -252,7 +252,7 @@ export function initWorkspace(container: XUL.Box | undefined) {
} else {
outlineContainer.style.display = "flex";
}
}
},
);
outlineMut.observe(outlineContainer, {
attributes: true,
@ -269,7 +269,7 @@ export async function initWorkspaceEditor(
noteId: number,
options: {
lineIndex?: number;
} = {}
} = {},
) {
const noteItem = Zotero.Items.get(noteId);
if (!noteItem || !noteItem.isNote()) {
@ -279,7 +279,7 @@ export async function initWorkspaceEditor(
return;
}
const editorElem = container?.querySelector(
`#${makeId("editor-" + type)}`
`#${makeId("editor-" + type)}`,
) as EditorElement;
await waitUtilAsync(() => Boolean(editorElem._initialized))
.then(() => ztoolkit.log("ok"))
@ -300,7 +300,7 @@ export async function initWorkspaceEditor(
}
function getContainerType(
container: XUL.Box | undefined
container: XUL.Box | undefined,
): "tab" | "window" | "unknown" {
if (!container) {
return "unknown";
@ -337,7 +337,7 @@ export function toggleNotesPane(visibility?: boolean) {
export function getWorkspaceEditor(
workspaceType: "tab" | "window",
editorType: "main" | "preview" = "main"
editorType: "main" | "preview" = "main",
) {
const container =
workspaceType === "tab"
@ -359,7 +359,7 @@ const SRC_LIST = [
function setOutline(
container: XUL.Box,
newType: OutlineType = OutlineType.empty
newType: OutlineType = OutlineType.empty,
) {
if (newType === OutlineType.empty) {
newType = addon.data.workspace.outline + 1;
@ -373,11 +373,11 @@ function setOutline(
).hidden = newType === OutlineType.treeView;
(
container.querySelector(
`#${makeId("saveOutlineFreeMind")}`
`#${makeId("saveOutlineFreeMind")}`,
) as HTMLDivElement
).hidden = newType === OutlineType.treeView;
const iframe = container.querySelector(
`#${makeId("outline-iframe")}`
`#${makeId("outline-iframe")}`,
) as HTMLIFrameElement;
iframe.setAttribute("src", SRC_LIST[addon.data.workspace.outline]);
updateOutline(container);
@ -386,22 +386,22 @@ function setOutline(
export async function updateOutline(container: XUL.Box) {
const iframe = container.querySelector(
`#${makeId("outline-iframe")}`
`#${makeId("outline-iframe")}`,
) as HTMLIFrameElement;
await waitUtilAsync(
() => iframe.contentWindow?.document.readyState === "complete"
() => iframe.contentWindow?.document.readyState === "complete",
);
iframe.contentWindow?.postMessage(
{
type: "setMindMapData",
nodes: getNoteTreeFlattened(
Zotero.Items.get(addon.data.workspace.mainId),
{ keepLink: true }
{ keepLink: true },
),
workspaceType: getContainerType(container),
expandLevel: getPref("workspace.outline.expandLevel"),
},
"*"
"*",
);
}
@ -413,20 +413,20 @@ function updateOutlineButtons(container: XUL.Box) {
).style.visibility = isTreeView ? "hidden" : "visible";
(
container.querySelector(
`#${makeId("saveOutlineFreeMind")}`
`#${makeId("saveOutlineFreeMind")}`,
) as HTMLDivElement
).style.visibility = isTreeView ? "hidden" : "visible";
}
function saveImage(container: XUL.Box) {
const iframe = container.querySelector(
`#${makeId("outline-iframe")}`
`#${makeId("outline-iframe")}`,
) as HTMLIFrameElement;
iframe.contentWindow?.postMessage(
{
type: "saveSVG",
},
"*"
"*",
);
}
@ -436,7 +436,7 @@ async function saveFreeMind() {
`${Zotero.getString("fileInterface.export")} FreeMind XML`,
"save",
[["FreeMind XML File(*.mm)", "*.mm"]],
`${Zotero.Items.get(addon.data.workspace.mainId).getNoteTitle()}.mm`
`${Zotero.Items.get(addon.data.workspace.mainId).getNoteTitle()}.mm`,
).open();
if (filename) {
await _saveFreeMind(filename, addon.data.workspace.mainId);

View File

@ -14,7 +14,7 @@ export async function messageHandler(ev: MessageEvent) {
case "jumpNode": {
const editor = addon.api.workspace.getWorkspaceEditor(
ev.data.workspaceType,
"main"
"main",
);
if (!editor) {
return;
@ -34,21 +34,21 @@ export async function messageHandler(ev: MessageEvent) {
}
case "moveNode": {
const noteItem = Zotero.Items.get(addon.data.workspace.mainId);
let tree = getNoteTree(noteItem);
let fromNode = getNoteTreeNodeById(noteItem, ev.data.fromID, tree);
let toNode = getNoteTreeNodeById(noteItem, ev.data.toID, tree);
const tree = getNoteTree(noteItem);
const fromNode = getNoteTreeNodeById(noteItem, ev.data.fromID, tree);
const toNode = getNoteTreeNodeById(noteItem, ev.data.toID, tree);
moveHeading(
getEditorInstance(noteItem.id),
fromNode!,
toNode!,
ev.data.moveType
ev.data.moveType,
);
return;
}
case "editNode": {
const editor = addon.api.workspace.getWorkspaceEditor(
ev.data.workspaceType,
"main"
"main",
);
if (!editor) {
return;
@ -56,7 +56,7 @@ export async function messageHandler(ev: MessageEvent) {
updateHeadingTextAtLine(
editor,
ev.data.lineIndex,
ev.data.text.replace(/[\r\n]/g, "")
ev.data.text.replace(/[\r\n]/g, ""),
);
return;
}
@ -65,7 +65,7 @@ export async function messageHandler(ev: MessageEvent) {
`${Zotero.getString("fileInterface.export")} SVG Image`,
"save",
[["SVG File(*.svg)", "*.svg"]],
`${Zotero.Items.get(addon.data.workspace.mainId).getNoteTitle()}.svg`
`${Zotero.Items.get(addon.data.workspace.mainId).getNoteTitle()}.svg`,
).open();
if (filename) {
await Zotero.File.putContentsAsync(formatPath(filename), ev.data.image);
@ -74,7 +74,7 @@ export async function messageHandler(ev: MessageEvent) {
"Show in Folder",
(ev) => {
Zotero.File.reveal(filename);
}
},
);
}
return;

View File

@ -24,11 +24,9 @@ export function registerWorkspaceTab() {
attributeFilter: ["hidden"],
});
waitUtilAsync(() =>
Boolean(ztoolkit.getGlobal("ZoteroContextPane")._notifierID)
Boolean(ztoolkit.getGlobal("ZoteroContextPane")._notifierID),
).then(() => {
addWorkspaceTab().then(() => {
restoreWorkspaceTab();
});
addWorkspaceTab();
});
window.addEventListener("message", (e) => messageHandler(e), false);
}
@ -38,7 +36,7 @@ export function unregisterWorkspaceTab() {
}
async function addWorkspaceTab() {
let { id, container } = Zotero_Tabs.add({
const { id, container } = Zotero_Tabs.add({
type: TAB_TYPE,
title: getString("tab.name"),
index: 1,
@ -54,10 +52,10 @@ async function addWorkspaceTab() {
},
});
await waitUtilAsync(() =>
Boolean(document.querySelector(`.tabs-wrapper .tab[data-id=${id}]`))
Boolean(document.querySelector(`.tabs-wrapper .tab[data-id=${id}]`)),
);
const tabElem = document.querySelector(
`.tabs-wrapper .tab[data-id=${id}]`
`.tabs-wrapper .tab[data-id=${id}]`,
) as HTMLDivElement;
tabElem.style.width = "30px";
tabElem.style.minWidth = "30px";
@ -78,7 +76,7 @@ async function addWorkspaceTab() {
backgroundImage: `url("chrome://${config.addonRef}/content/icons/favicon.png")`,
},
},
content
content,
);
close.style.visibility = "hidden";
addon.data.workspace.tab.id = id;
@ -91,7 +89,7 @@ function hoverWorkspaceTab(hovered: boolean) {
(elem as HTMLDivElement).style.visibility = hovered ? "visible" : "hidden";
});
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;
const content = tabElem.querySelector(".tab-name") as HTMLDivElement;
content.removeAttribute("style");
@ -107,10 +105,10 @@ function hoverWorkspaceTab(hovered: boolean) {
function updateWorkspaceTabToggleButton(
type: "outline" | "preview" | "notes",
state: "open" | "collapsed"
state: "open" | "collapsed",
) {
const elem = document.querySelector(
`#betternotes-tab-toggle-${type}`
`#betternotes-tab-toggle-${type}`,
) as HTMLDivElement;
if (!elem) {
return;
@ -123,12 +121,12 @@ function updateWorkspaceTabToggleButton(
function registerWorkspaceTabPaneObserver() {
const outlineSplitter = document.querySelector(
"#betternotes-workspace-outline-splitter"
"#betternotes-workspace-outline-splitter",
);
const outlineMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
updateWorkspaceTabToggleButton(
"outline",
outlineSplitter!.getAttribute("state")! as "open" | "collapsed"
outlineSplitter!.getAttribute("state")! as "open" | "collapsed",
);
});
outlineMut.observe(outlineSplitter!, {
@ -136,12 +134,12 @@ function registerWorkspaceTabPaneObserver() {
attributeFilter: ["state"],
});
const previewSplitter = document.querySelector(
"#betternotes-workspace-preview-splitter"
"#betternotes-workspace-preview-splitter",
);
const previeweMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
updateWorkspaceTabToggleButton(
"preview",
previewSplitter!.getAttribute("state")! as "open" | "collapsed"
previewSplitter!.getAttribute("state")! as "open" | "collapsed",
);
});
previeweMut.observe(previewSplitter!, {
@ -152,7 +150,7 @@ function registerWorkspaceTabPaneObserver() {
const notesMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
updateWorkspaceTabToggleButton(
"notes",
notesSplitter!.getAttribute("state")! as "open" | "collapsed"
notesSplitter!.getAttribute("state")! as "open" | "collapsed",
);
});
notesMut.observe(notesSplitter!, {
@ -173,7 +171,7 @@ export async function activateWorkspaceTab() {
document.querySelector("#zotero-tab-toolbar") as XUL.Box
).style.visibility = "collapse";
const toolbar = document.querySelector(
"#zotero-context-toolbar-extension"
"#zotero-context-toolbar-extension",
) as XUL.Box;
toolbar.style.visibility = "collapse";
toolbar.nextElementSibling?.setAttribute("selectedIndex", "1");
@ -188,12 +186,12 @@ export async function activateWorkspaceTab() {
await waitUtilAsync(() =>
Boolean(
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(
`.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`
`.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`,
) as HTMLDivElement;
tabElem.removeAttribute("style");
const content = tabElem.querySelector(".tab-name") as HTMLDivElement;
@ -222,7 +220,7 @@ export async function activateWorkspaceTab() {
addon.hooks.onToggleWorkspacePane(
"outline",
undefined,
addon.data.workspace.tab.container
addon.data.workspace.tab.container,
);
},
},
@ -245,7 +243,7 @@ export async function activateWorkspaceTab() {
addon.hooks.onToggleWorkspacePane(
"preview",
undefined,
addon.data.workspace.tab.container
addon.data.workspace.tab.container,
);
},
},
@ -281,7 +279,7 @@ export async function activateWorkspaceTab() {
},
],
},
close
close,
);
hoverWorkspaceTab(false);
tabElem.addEventListener("mouseenter", () => {
@ -320,19 +318,11 @@ export function deActivateWorkspaceTab() {
document.querySelector("#zotero-tab-toolbar") as XUL.Box
).style.removeProperty("visibility");
const toolbar = document.querySelector(
"#zotero-context-toolbar-extension"
"#zotero-context-toolbar-extension",
) as XUL.Box;
toolbar.style.removeProperty("visibility");
}
function restoreWorkspaceTab() {
return;
if (1 || getPref("workspace.tab.active")) {
ztoolkit.log("restore workspace tab");
activateWorkspaceTab();
}
}
function setWorkspaceTabStatus(status: boolean) {
addon.data.workspace.tab.active = status;
setPref("workspace.tab.active", status);
@ -340,7 +330,7 @@ function setWorkspaceTabStatus(status: boolean) {
function initWorkspaceTabDragDrop(
container?: XUL.Box,
tabElem?: HTMLDivElement
tabElem?: HTMLDivElement,
) {
if (!container) {
return;
@ -401,10 +391,10 @@ function initWorkspaceTabDragDrop(
],
enableElementRecord: false,
},
container
container,
);
const dropElem = container.querySelector(
"#bn-workspace-tab-drop"
"#bn-workspace-tab-drop",
) as HTMLDivElement;
tabElem?.addEventListener("dragstart", (ev) => {
dropElem.style.visibility = "visible";

View File

@ -14,14 +14,14 @@ export async function showWorkspaceWindow() {
`chrome://${config.addonRef}/content/workspaceWindow.xhtml`,
`${config.addonRef}-workspaceWindow`,
`chrome,centerscreen,resizable,status,width=800,height=400,dialog=no`,
windowArgs
windowArgs,
)!;
await windowArgs._initPromise.promise;
localeWindow(win);
addon.data.workspace.window.active = true;
addon.data.workspace.window.window = win;
addon.data.workspace.window.container = win.document.querySelector(
"#workspace-container"
"#workspace-container",
) as XUL.Box;
addon.hooks.onInitWorkspace(addon.data.workspace.window.container);
win.addEventListener("message", messageHandler, false);

View File

@ -17,7 +17,7 @@ async function parseAnnotationJSON(annotationItem: Zotero.Item) {
const annotationJSON = await Zotero.Annotations.toJSON(annotationItem);
const annotationObj = Object.assign(
{},
annotationJSON
annotationJSON,
) as CustomAnnotationJSON;
annotationObj.id = annotationItem.key;
annotationObj.attachmentItemID = annotationItem.parentItem?.id;
@ -38,12 +38,12 @@ async function parseAnnotationJSON(annotationItem: Zotero.Item) {
function serializeAnnotations(
annotations: Required<CustomAnnotationJSON>[],
skipEmbeddingItemData: boolean = false,
skipCitation: boolean = false
skipCitation: boolean = false,
) {
let storedCitationItems = [];
const storedCitationItems = [];
let html = "";
for (let annotation of annotations) {
let attachmentItem = Zotero.Items.get(annotation.attachmentItemID);
for (const annotation of annotations) {
const attachmentItem = Zotero.Items.get(annotation.attachmentItemID);
if (!attachmentItem) {
continue;
}
@ -64,7 +64,7 @@ function serializeAnnotations(
let quotedHighlightHTML = "";
let commentHTML = "";
let storedAnnotation: any = {
const storedAnnotation: any = {
attachmentURI: Zotero.URI.getItemURI(attachmentItem),
annotationKey: annotation.id,
color: annotation.color,
@ -73,12 +73,12 @@ function serializeAnnotations(
};
// Citation
let parentItem = skipCitation
const parentItem = skipCitation
? undefined
: attachmentItem.parentID && Zotero.Items.get(attachmentItem.parentID);
if (parentItem) {
let uris = [Zotero.URI.getItemURI(parentItem)];
let citationItem: any = {
const uris = [Zotero.URI.getItemURI(parentItem)];
const citationItem: any = {
uris,
locator: annotation.pageLabel,
};
@ -86,48 +86,48 @@ function serializeAnnotations(
// Note: integration.js` uses `Zotero.Cite.System.prototype.retrieveItem`,
// which produces a little bit different CSL JSON
// @ts-ignore
let itemData = Zotero.Utilities.Item.itemToCSLJSON(parentItem);
const itemData = Zotero.Utilities.Item.itemToCSLJSON(parentItem);
if (!skipEmbeddingItemData) {
citationItem.itemData = itemData;
}
let item = storedCitationItems.find((item) =>
item.uris.some((uri) => uris.includes(uri))
const item = storedCitationItems.find((item) =>
item.uris.some((uri) => uris.includes(uri)),
);
if (!item) {
storedCitationItems.push({ uris, itemData });
}
storedAnnotation.citationItem = citationItem;
let citation = {
const citation = {
citationItems: [citationItem],
properties: {},
};
let citationWithData = JSON.parse(JSON.stringify(citation));
const citationWithData = JSON.parse(JSON.stringify(citation));
citationWithData.citationItems[0].itemData = itemData;
let formatted =
const formatted =
Zotero.EditorInstanceUtilities.formatCitation(citationWithData);
citationHTML = `<span class="citation" data-citation="${encodeURIComponent(
JSON.stringify(citation)
JSON.stringify(citation),
)}">${formatted}</span>`;
}
// Image
if (annotation.imageAttachmentKey) {
// Normalize image dimensions to 1.25 of the print size
let rect = annotation.position.rects[0];
let rectWidth = rect[2] - rect[0];
let rectHeight = rect[3] - rect[1];
const rect = annotation.position.rects[0];
const rectWidth = rect[2] - rect[0];
const rectHeight = rect[3] - rect[1];
// Constants from pdf.js
const CSS_UNITS = 96.0 / 72.0;
const PDFJS_DEFAULT_SCALE = 1.25;
let width = Math.round(rectWidth * CSS_UNITS * PDFJS_DEFAULT_SCALE);
let height = Math.round((rectHeight * width) / rectWidth);
const width = Math.round(rectWidth * CSS_UNITS * PDFJS_DEFAULT_SCALE);
const height = Math.round((rectHeight * width) / rectWidth);
imageHTML = `<img data-attachment-key="${
annotation.imageAttachmentKey
}" width="${width}" height="${height}" data-annotation="${encodeURIComponent(
JSON.stringify(storedAnnotation)
JSON.stringify(storedAnnotation),
)}"/>`;
}
@ -138,17 +138,17 @@ function serializeAnnotations(
// Text
if (annotation.text) {
let text = Zotero.EditorInstanceUtilities._transformTextToHTML.call(
const text = Zotero.EditorInstanceUtilities._transformTextToHTML.call(
Zotero.EditorInstanceUtilities,
annotation.text.trim()
annotation.text.trim(),
);
highlightHTML = `<span class="highlight" data-annotation="${encodeURIComponent(
JSON.stringify(storedAnnotation)
JSON.stringify(storedAnnotation),
)}">${text}</span>`;
quotedHighlightHTML = `<span class="highlight" data-annotation="${encodeURIComponent(
JSON.stringify(storedAnnotation)
JSON.stringify(storedAnnotation),
)}">${Zotero.getString(
"punctuation.openingQMark"
"punctuation.openingQMark",
)}${text}${Zotero.getString("punctuation.closingQMark")}</span>`;
}
@ -156,14 +156,14 @@ function serializeAnnotations(
if (annotation.comment) {
commentHTML = Zotero.EditorInstanceUtilities._transformTextToHTML.call(
Zotero.EditorInstanceUtilities,
annotation.comment.trim()
annotation.comment.trim(),
);
}
let template: string = "";
if (annotation.type === "highlight") {
template = Zotero.Prefs.get(
"annotations.noteTemplates.highlight"
"annotations.noteTemplates.highlight",
) as string;
} else if (annotation.type === "note") {
template = Zotero.Prefs.get("annotations.noteTemplates.note") as string;
@ -176,10 +176,10 @@ function serializeAnnotations(
template = template.replace(
/(<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 || "",
// Include quotation marks by default, but allow to disable with `quotes='false'`
highlight: (attrs: any) =>
@ -196,7 +196,7 @@ function serializeAnnotations(
let templateHTML = Zotero.Utilities.Internal.generateHTMLFromTemplate(
template,
vars
vars,
);
// Remove some spaces at the end of paragraph
templateHTML = templateHTML.replace(/([\s]*)(<\/p)/g, "$2");
@ -209,9 +209,9 @@ function serializeAnnotations(
export async function importAnnotationImagesToNote(
note: Zotero.Item | undefined,
annotations: CustomAnnotationJSON[]
annotations: CustomAnnotationJSON[],
) {
for (let annotation of annotations) {
for (const annotation of annotations) {
if (annotation.image && note) {
annotation.imageAttachmentKey =
(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.
ignoreComment?: boolean;
skipCitation?: boolean;
} = {}
} = {},
) {
let annotationJSONList: CustomAnnotationJSON[] = [];
const annotationJSONList: CustomAnnotationJSON[] = [];
for (const annot of annotations) {
const annotJson = await parseAnnotationJSON(annot);
if (options.ignoreComment && annotJson?.comment) {
@ -241,7 +241,7 @@ export async function parseAnnotationHTML(
const html = serializeAnnotations(
annotationJSONList as Required<CustomAnnotationJSON>[],
false,
options.skipCitation
options.skipCitation,
).html;
return html;
}

View File

@ -1,7 +1,7 @@
export async function parseCitationHTML(citationIds: number[]) {
let html = "";
let items = await Zotero.Items.getAsync(citationIds);
for (let item of items) {
const items = await Zotero.Items.getAsync(citationIds);
for (const item of items) {
if (
item.isNote() &&
!(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()) {
// @ts-ignore
let itemData = Zotero.Utilities.Item.itemToCSLJSON(item);
let citation = {
const itemData = Zotero.Utilities.Item.itemToCSLJSON(item);
const citation = {
citationItems: [
{
uris: [Zotero.URI.getItemURI(item)],
@ -24,9 +24,9 @@ export async function parseCitationHTML(citationIds: number[]) {
],
properties: {},
};
let formatted = Zotero.EditorInstanceUtilities.formatCitation(citation);
const formatted = Zotero.EditorInstanceUtilities.formatCitation(citation);
html += `<p><span class="citation" data-citation="${encodeURIComponent(
JSON.stringify(citation)
JSON.stringify(citation),
)}">${formatted}</span></p>`;
}
}

View File

@ -27,7 +27,7 @@ function insert(
editor: Zotero.EditorInstance,
content: string = "",
position: number | "end" | "start" | "cursor" = "cursor",
select?: boolean
select?: boolean,
) {
const core = getEditorCore(editor);
const EditorAPI = getEditorAPI(editor);
@ -45,7 +45,7 @@ function insert(
EditorAPI.refocusEditor(() => {
EditorAPI.setSelection(
(position as number) + slice.content.size,
position as number
position as number,
)(core.view.state, core.view.dispatch);
});
}
@ -61,7 +61,7 @@ function move(
editor: Zotero.EditorInstance,
from: number,
to: number,
delta: number
delta: number,
) {
const core = getEditorCore(editor);
const EditorAPI = getEditorAPI(editor);
@ -106,7 +106,7 @@ function replace(
| "link"
| "code",
markAttrs: Record<string, any>,
select?: boolean
select?: boolean,
) {
const core = getEditorCore(editor);
const EditorAPI = getEditorAPI(editor);
@ -119,7 +119,7 @@ function replace(
JSON.stringify(nodeAttrs),
schema.marks[markTypeName],
JSON.stringify(markAttrs),
select
select,
)(core.view.state, core.view.dispatch);
}
@ -133,7 +133,7 @@ function scroll(editor: Zotero.EditorInstance, lineIndex: number) {
function getEditorInstance(noteId: number) {
const editor = Zotero.Notes._editorInstances.find(
(e) =>
e._item.id === noteId && !Components.utils.isDeadWrapper(e._iframeWindow)
e._item.id === noteId && !Components.utils.isDeadWrapper(e._iframeWindow),
);
return editor;
}
@ -179,7 +179,7 @@ function getLineAtCursor(editor: Zotero.EditorInstance) {
function getDOMAtLine(
editor: Zotero.EditorInstance,
lineIndex: number
lineIndex: number,
): HTMLElement {
const core = getEditorCore(editor);
const lineNodeDesc =
@ -192,7 +192,7 @@ function getDOMAtLine(
function getPositionAtLine(
editor: Zotero.EditorInstance,
lineIndex: number,
type: "start" | "end" = "end"
type: "start" | "end" = "end",
): number {
const core = getEditorCore(editor);
const lineNodeDesc =
@ -204,8 +204,8 @@ function getPositionAtLine(
0,
Math.min(
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(
editor: Zotero.EditorInstance,
text: string | undefined,
url: string
url: string,
) {
const core = getEditorCore(editor);
const EditorAPI = getEditorAPI(editor);
let from = core.view.state.selection.from;
let to = core.view.state.selection.to;
const from = core.view.state.selection.from;
const to = core.view.state.selection.to;
const schema = core.view.state.schema;
if (!url) {
return;
@ -232,13 +232,13 @@ function updateURLAtCursor(
text,
schema.marks.link,
JSON.stringify({ href: url }),
schema.marks.link
schema.marks.link,
)(core.view.state, core.view.dispatch);
EditorAPI.refocusEditor(() => {
core.view.dispatch(
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(
editor: Zotero.EditorInstance,
lineIndex: number,
text: string
text: string,
) {
const core = getEditorCore(editor);
const schema = core.view.state.schema;
@ -260,13 +260,13 @@ function updateHeadingTextAtLine(
to,
text,
schema.nodes.heading,
JSON.stringify({ level })
JSON.stringify({ level }),
)(core.view.state, core.view.dispatch);
EditorAPI.refocusEditor(() => {
core.view.dispatch(
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(
editor: Zotero.EditorInstance,
width: number
width: number,
) {
const core = getEditorCore(editor);
const EditorAPI = getEditorAPI(editor);
@ -290,7 +290,7 @@ function updateImageDimensionsAtCursor(
width,
undefined,
core.view.state,
core.view.dispatch
core.view.dispatch,
);
}
@ -298,7 +298,7 @@ function moveLines(
editor: Zotero.EditorInstance,
fromIndex: number,
toIndex: number,
targetIndex: number
targetIndex: number,
) {
const core = getEditorCore(editor);
const EditorAPI = getEditorAPI(editor);
@ -317,8 +317,12 @@ function moveLines(
EditorAPI.refocusEditor(() => {
core.view.dispatch(
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,
currentNode: TreeModel.Node<NoteNodeData>,
targetNode: TreeModel.Node<NoteNodeData>,
as: "child" | "before" | "after"
as: "child" | "before" | "after",
) {
if (!editor || targetNode.getPath().indexOf(currentNode) >= 0) {
return;
@ -359,13 +363,13 @@ function moveHeading(
const fromIndex = currentNode.model.lineIndex;
const toIndex = currentNode.model.endIndex;
let levelChange = targetLevel - currentNode.model.level;
const levelChange = targetLevel - currentNode.model.level;
const core = getEditorCore(editor);
const EditorAPI = getEditorAPI(editor);
EditorAPI.updateHeadingsInRange(
getPositionAtLine(editor, fromIndex, "start"),
getPositionAtLine(editor, toIndex, "end"),
levelChange
levelChange,
)(core.view.state, core.view.dispatch);
moveLines(editor, fromIndex, toIndex, targetIndex);
}
@ -373,7 +377,7 @@ function moveHeading(
function getTextBetween(
editor: Zotero.EditorInstance,
from: number,
to: number
to: number,
) {
const core = getEditorCore(editor);
return core.view.state.doc.textBetween(from, to);
@ -382,7 +386,7 @@ function getTextBetween(
function getTextBetweenLines(
editor: Zotero.EditorInstance,
fromIndex: number,
toIndex: number
toIndex: number,
) {
const core = getEditorCore(editor);
const from = getPositionAtLine(editor, fromIndex, "start");

View File

@ -10,7 +10,7 @@ function showHint(text: string) {
async function showHintWithLink(
text: string,
linkText: string,
linkCallback: (ev: MouseEvent) => any
linkCallback: (ev: MouseEvent) => any,
) {
const progress = new ztoolkit.ProgressWindow(PROGRESS_TITLE)
.createLine({ text, progress: 100, type: "default" })
@ -20,7 +20,7 @@ async function showHintWithLink(
await waitUtilAsync(() =>
// @ts-ignore
Boolean(progress.lines && progress.lines[0]._itemText)
Boolean(progress.lines && progress.lines[0]._itemText),
);
// @ts-ignore
progress.lines[0]._hbox.ownerDocument

View File

@ -12,7 +12,7 @@ export async function itemPicker() {
: "chrome://zotero/content/selectItemsDialog.xul",
"",
"chrome,dialog=no,centerscreen,resizable=yes",
io
io,
);
await io.deferred.promise;

View File

@ -26,7 +26,7 @@ export function getNoteLinkParams(link: string) {
link,
libraryID: -1,
noteKey: undefined,
noteItem: false as false,
noteItem: false as const,
ignore: null,
lineIndex: null,
};
@ -38,7 +38,7 @@ export function getNoteLink(
options: {
ignore?: boolean | null;
lineIndex?: number | null;
} = {}
} = {},
) {
const libraryID = noteItem.libraryID;
const library = Zotero.Libraries.get(libraryID);
@ -77,7 +77,7 @@ export function getNoteLink(
export function getLinkedNotesRecursively(
link: string,
ignoreIds: number[] = []
ignoreIds: number[] = [],
) {
const linkParams = getNoteLinkParams(link);
if (!linkParams.noteItem) return [];
@ -98,6 +98,6 @@ export function getLinkedNotesRecursively(
}
return acc;
},
[linkParams.noteItem.id] as number[]
[linkParams.noteItem.id] as number[],
);
}

View File

@ -39,22 +39,20 @@ function initLocale() {
* getString("addon-dynamic-example", { args: { count: 2 } }); // I have 2 apples
* ```
*/
function getString(localeString: string): string;
function getString(localeString: string, branch: string): string;
function getString(localString: string): string;
function getString(localString: string, branch: string): string;
function getString(
localeString: string,
options: { branch?: string | undefined; args?: Record<string, unknown> }
options: { branch?: string | undefined; args?: Record<string, unknown> },
): string;
function getString(...inputs: any[]) {
// Old .properties uses . while .ftl uses -
const localeString = (inputs[0] as string).replace(/\./g, "-");
if (inputs.length === 1) {
return _getString(localeString);
return _getString(inputs[0]);
} else if (inputs.length === 2) {
if (typeof inputs[1] === "string") {
return _getString(localeString, { branch: inputs[1] });
return _getString(inputs[0], { branch: inputs[1] });
} else {
return _getString(localeString, inputs[1]);
return _getString(inputs[0], inputs[1]);
}
} else {
throw new Error("Invalid arguments");
@ -63,18 +61,30 @@ function getString(...inputs: any[]) {
function _getString(
localeString: string,
options: { branch?: string | undefined; args?: Record<string, unknown> } = {}
options: { branch?: string | undefined; args?: Record<string, unknown> } = {},
): 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 pattern = addon.data.locale?.current.formatMessagesSync([
{ id: localeString, args },
{ id: localStringWithPrefix, args },
])[0];
if (!pattern) {
return localeString;
return localStringWithPrefix;
}
if (branch && pattern.attributes) {
return pattern.attributes[branch] || localeString;
return pattern.attributes[branch] || localStringWithPrefix;
} else {
return pattern.value || localeString;
return pattern.value || localStringWithPrefix;
}
}

View File

@ -25,7 +25,7 @@ function parseHTMLLines(html: string): string[] {
html = html.replace(/<div[^>]*data-schema-version[^>]*>/, "");
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
let previousLineCache = [];
@ -33,12 +33,12 @@ function parseHTMLLines(html: string): string[] {
const forceInline = ["table", "blockquote", "pre", "ol", "ul"];
const selfInline: string[] = [];
let forceInlineStack = [];
const forceInlineStack = [];
let forceInlineFlag = false;
let selfInlineFlag = false;
const parsedLines = [];
for (let line of noteLines) {
for (const line of noteLines) {
// restore self inline flag
selfInlineFlag = false;
@ -88,7 +88,7 @@ function parseHTMLLines(html: string): string[] {
// Append cache to previous line
if (previousLineCache.length) {
parsedLines[parsedLines.length - 1] += `\n${previousLineCache.join(
"\n"
"\n",
)}`;
previousLineCache = [];
}
@ -113,7 +113,7 @@ function getLinesInNote(note: Zotero.Item): string[] {
if (!note) {
return [];
}
let noteText: string = note.getNote();
const noteText: string = note.getNote();
return parseHTMLLines(noteText);
}
@ -121,20 +121,20 @@ async function setLinesToNote(note: Zotero.Item, lines: string[]) {
if (!note) {
return [];
}
let noteText: string = note.getNote();
let containerIndex = noteText.search(/data-schema-version="[0-9]*/g);
const noteText: string = note.getNote();
const containerIndex = noteText.search(/data-schema-version="[0-9]*/g);
if (containerIndex === -1) {
note.setNote(
`<div data-schema-version="${config.dataSchemaVersion}">${lines.join(
"\n"
)}</div>`
"\n",
)}</div>`,
);
} else {
let noteHead = noteText.substring(0, containerIndex);
const noteHead = noteText.substring(0, containerIndex);
note.setNote(
`${noteHead}data-schema-version="${
config.dataSchemaVersion
}">${lines.join("\n")}</div>`
}">${lines.join("\n")}</div>`,
);
}
@ -145,12 +145,12 @@ async function addLineToNote(
note: Zotero.Item,
html: string,
lineIndex: number = -1,
forceMetadata: boolean = false
forceMetadata: boolean = false,
) {
if (!note || !html) {
return;
}
let noteLines = getLinesInNote(note);
const noteLines = getLinesInNote(note);
if (lineIndex < 0 || lineIndex >= noteLines.length) {
lineIndex = noteLines.length;
}
@ -174,12 +174,12 @@ async function addLineToNote(
async function renderNoteHTML(
html: string,
refNotes: Zotero.Item[]
refNotes: Zotero.Item[],
): Promise<string>;
async function renderNoteHTML(noteItem: Zotero.Item): Promise<string>;
async function renderNoteHTML(
htmlOrNote: string | Zotero.Item,
refNotes?: Zotero.Item[]
refNotes?: Zotero.Item[],
): Promise<string> {
let html: string;
if (typeof htmlOrNote === "string") {
@ -195,30 +195,32 @@ async function renderNoteHTML(
}
const parser = ztoolkit.getDOMParser();
let doc = parser.parseFromString(html, "text/html");
const doc = parser.parseFromString(html, "text/html");
const imageAttachments = refNotes.reduce((acc, note) => {
acc.push(...Zotero.Items.get(note.getAttachments()));
return acc;
}, [] as Zotero.Item[]);
for (let attachment of imageAttachments) {
for (const attachment of imageAttachments) {
if (await attachment.fileExists()) {
let imageNodes = Array.from(
doc.querySelectorAll(`img[data-attachment-key="${attachment.key}"]`)
const imageNodes = Array.from(
doc.querySelectorAll(`img[data-attachment-key="${attachment.key}"]`),
);
if (imageNodes.length) {
try {
const b64 = await getItemDataURL(attachment);
imageNodes.forEach((node) => node.setAttribute("src", b64));
} catch (e) {}
} catch (e) {
ztoolkit.log(e);
}
}
}
}
const bgNodes = doc.querySelectorAll(
"span[style]"
"span[style]",
) 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,
// because word processors don't understand colors with alpha channel
if (
@ -240,7 +242,7 @@ async function renderNoteHTML(
node.innerHTML.replace(mathDelimiterRegex, ""),
{
throwOnError: false,
}
},
);
});
return doc.body.innerHTML;
@ -258,12 +260,12 @@ function getNoteType(id: number) {
function getNoteTree(
note: Zotero.Item,
parseLink: boolean = true
parseLink: boolean = true,
): TreeModel.Node<NoteNodeData> {
const noteLines = getLinesInNote(note);
const parser = ztoolkit.getDOMParser();
let tree = new TreeModel();
let root = tree.parse({
const tree = new TreeModel();
const root = tree.parse({
id: -1,
level: 0,
lineIndex: -1,
@ -273,9 +275,9 @@ function getNoteTree(
let lastNode = root;
const headingRegex = new RegExp("^<h([1-6])(.*?)</h[1-6]>");
const linkRegex = new RegExp('href="(zotero://note/[^"]*)"');
for (let i in noteLines) {
for (const i in noteLines) {
let currentLevel = 7;
let lineElement = noteLines[i];
const lineElement = noteLines[i];
const matchHeadingResult = lineElement.match(headingRegex);
const matchLinkResult = parseLink ? lineElement.match(linkRegex) : null;
const isHeading = Boolean(matchHeadingResult);
@ -333,7 +335,7 @@ function getNoteTreeFlattened(
keepRoot?: boolean;
keepLink?: boolean;
customFilter?: (node: TreeModel.Node<NoteNodeData>) => boolean;
} = { keepRoot: false, keepLink: false }
} = { keepRoot: false, keepLink: false },
): TreeModel.Node<NoteNodeData>[] {
if (!note) {
return [];
@ -342,14 +344,14 @@ function getNoteTreeFlattened(
(node) =>
(options.keepRoot || node.model.lineIndex >= 0) &&
(options.keepLink || node.model.level <= 6) &&
(options.customFilter ? options.customFilter(node) : true)
(options.customFilter ? options.customFilter(node) : true),
);
}
function getNoteTreeNodeById(
note: Zotero.Item,
id: number,
root: TreeModel.Node<NoteNodeData> | undefined = undefined
root: TreeModel.Node<NoteNodeData> | undefined = undefined,
) {
root = root || getNoteTree(note);
return root.first(function (node) {
@ -360,7 +362,7 @@ function getNoteTreeNodeById(
function getNoteTreeNodesByLevel(
note: Zotero.Item,
level: number,
root: TreeModel.Node<NoteNodeData> | undefined = undefined
root: TreeModel.Node<NoteNodeData> | undefined = undefined,
) {
root = root || getNoteTree(note);
return root.all(function (node) {
@ -370,7 +372,7 @@ function getNoteTreeNodesByLevel(
async function copyEmbeddedImagesFromNote(
targetNote: Zotero.Item,
sourceNotes: Zotero.Item[]
sourceNotes: Zotero.Item[],
) {
await Zotero.DB.executeTransaction(async () => {
for (const fromNote of sourceNotes) {
@ -382,9 +384,9 @@ async function copyEmbeddedImagesFromNote(
async function copyEmbeddedImagesInHTML(
html: string,
targetNote?: Zotero.Item,
refNotes: Zotero.Item[] = []
refNotes: Zotero.Item[] = [],
) {
ztoolkit.log("parseEmbeddedImagesInHTML", arguments);
ztoolkit.log("parseEmbeddedImagesInHTML", html, targetNote, refNotes);
if (!targetNote) {
return html;
}
@ -399,13 +401,13 @@ async function copyEmbeddedImagesInHTML(
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
for (let attachment of attachments) {
for (const attachment of attachments) {
if (await attachment.fileExists()) {
let nodes = Array.from(
doc.querySelectorAll(`img[data-attachment-key="${attachment.key}"]`)
const nodes = Array.from(
doc.querySelectorAll(`img[data-attachment-key="${attachment.key}"]`),
);
if (nodes.length) {
let copiedAttachment: Zotero.Item;
@ -417,7 +419,7 @@ async function copyEmbeddedImagesInHTML(
});
});
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) {
let parts = dataurl.split(",");
let matches = parts[0]?.match(/:(.*?);/);
const parts = dataurl.split(",");
const matches = parts[0]?.match(/:(.*?);/);
if (!matches || !matches[1]) {
return;
}
let mime = matches[1];
const mime = matches[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 u8arr = new Uint8Array(n);
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
@ -451,7 +453,7 @@ function dataURLtoBlob(dataurl: string) {
async function importImageToNote(
note: Zotero.Item,
src: string,
type: "b64" | "url" | "file" = "b64"
type: "b64" | "url" | "file" = "b64",
): Promise<string | void> {
if (!note || !note.isNote()) {
return "";
@ -474,7 +476,7 @@ async function importImageToNote(
} else if (type === "file") {
src = Zotero.File.normalizeToUnix(src);
const noteAttachmentKeys = Zotero.Items.get(note.getAttachments()).map(
(_i) => _i.key
(_i) => _i.key,
);
const filename = src.split("/").pop()?.split(".").shift();
// The exported image is KEY.png by default.
@ -497,7 +499,7 @@ async function importImageToNote(
return;
}
let attachment = await Zotero.Attachments.importEmbeddedImage({
const attachment = await Zotero.Attachments.importEmbeddedImage({
blob,
parentItemID: note.id,
saveOptions: {},

View File

@ -10,14 +10,14 @@ export function fill(
options: { char: string; position: "start" | "end" } = {
char: " ",
position: "end",
}
},
) {
if (str.length >= len) {
return str;
}
return str[options.position === "start" ? "padStart" : "padEnd"](
len - str.length,
options.char
options.char,
);
}
@ -53,7 +53,7 @@ export function randomString(len: number, seed?: string, chars?: string) {
len = 8;
}
let str = "";
const random: Function = seedrandom(seed);
const random = seedrandom(seed);
for (let i = 0; i < len; i++) {
const rnum = Math.floor(random() * chars.length);
str += chars.substring(rnum, rnum + 1);
@ -62,18 +62,19 @@ export function randomString(len: number, seed?: string, chars?: string) {
}
function arrayBufferToBase64(buffer: ArrayBufferLike) {
var binary = "";
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
let binary = "";
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return ztoolkit.getGlobal("btoa")(binary);
}
export async function getItemDataURL(item: Zotero.Item) {
let path = (await item.getFilePathAsync()) as string;
let buf = new Uint8Array((await OS.File.read(path, {})) as Uint8Array).buffer;
const path = (await item.getFilePathAsync()) as string;
const buf = new Uint8Array((await OS.File.read(path, {})) as Uint8Array)
.buffer;
return (
"data:" + item.attachmentContentType + ";base64," + arrayBufferToBase64(buf)
);

View File

@ -2,7 +2,7 @@ export function waitUntil(
condition: () => boolean,
callback: () => void,
interval = 100,
timeout = 10000
timeout = 10000,
) {
const start = Date.now();
const intervalId = ztoolkit.getGlobal("setInterval")(() => {
@ -18,7 +18,7 @@ export function waitUntil(
export function waitUtilAsync(
condition: () => boolean,
interval = 100,
timeout = 10000
timeout = 10000,
) {
return new Promise<void>((resolve, reject) => {
const start = Date.now();

View File

@ -15,7 +15,7 @@ function localeWindow(win: Window) {
const isProp = key in elem;
try {
const localeString = getString(
(isProp ? (elem as any)[key] : elem.getAttribute(key)).trim() || ""
(isProp ? (elem as any)[key] : elem.getAttribute(key)).trim() || "",
);
isProp
? ((elem as any)[key] = localeString)
@ -26,6 +26,6 @@ function localeWindow(win: Window) {
: elem.setAttribute(key, errorInfo);
}
});
}
},
);
}

48
src/utils/ztoolkit.ts Normal file
View File

@ -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);
}
}

View File

@ -7,13 +7,6 @@
"skipLibCheck": true,
"strict": true
},
"include": [
"src",
"typing",
"node_modules/zotero-types"
],
"exclude": [
"builds",
"addon"
]
}
"include": ["src", "typings", "node_modules/zotero-types"],
"exclude": ["build", "addon"]
}

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/ban-types */
declare interface EditorCore {
debouncedUpdate: Function;
disableDrag: boolean;

View File

@ -7,7 +7,7 @@ declare const _globalThis: {
document: Document;
OS: typeof OS;
Blob: typeof Blob;
ztoolkit: typeof ztoolkit;
ztoolkit: ZToolkit;
addon: typeof addon;
};
@ -20,8 +20,11 @@ declare interface Window {
): Window;
}
declare const ztoolkit: import("../src/addon").MyToolkit;
// declare const ztoolkit: import("zotero-plugin-toolkit").ZoteroToolkit;
declare type ZToolkit = ReturnType<
typeof import("../src/utils/ztoolkit").createZToolkit
>;
declare const ztoolkit: ZToolkit;
declare const rootURI: string;