update: plugin template
fix: reopen window bug add: eslint
This commit is contained in:
parent
5a4bdc72bf
commit
15d8cce685
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"root": true,
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"overrides": [],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {
|
||||
"@typescript-eslint/ban-ts-comment": [
|
||||
"warn",
|
||||
{
|
||||
"ts-expect-error": "allow-with-description",
|
||||
"ts-ignore": "allow-with-description",
|
||||
"ts-nocheck": "allow-with-description",
|
||||
"ts-check": "allow-with-description"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/no-explicit-any": ["off", { "ignoreRestArgs": true }],
|
||||
"@typescript-eslint/no-non-null-assertion": "off"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"**/build/**",
|
||||
"**/dist/**",
|
||||
"**/node_modules/**",
|
||||
"**/scripts/**",
|
||||
"**/*.js",
|
||||
"**/*.bak"
|
||||
]
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
addon/chrome/content/lib/** linguist-vendored
|
||||
* text=auto eol=lf
|
||||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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,11 +24,13 @@ 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]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
|
|
|
|||
|
|
@ -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.**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
**/builds
|
||||
**/build
|
||||
node_modules
|
||||
package-lock.json
|
||||
zotero-cmd.json
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"tabWidth": 2
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
},
|
||||
"github": {
|
||||
"release": true,
|
||||
"assets": ["builds/*.xpi"]
|
||||
"assets": ["build/*.xpi"]
|
||||
},
|
||||
"hooks": {
|
||||
"after:bump": "npm run build",
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
await new Promise(async (resolve) => {
|
||||
if (typeof Zotero != "undefined") {
|
||||
await Zotero.initializationPromise;
|
||||
resolve();
|
||||
}
|
||||
|
||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
var windows = Services.wm.getEnumerator("navigator:browser");
|
||||
var found = false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
await new Promise((resolve) => {
|
||||
var listener = {
|
||||
windowListener = {
|
||||
onOpenWindow: function (aWindow) {
|
||||
// Wait for the window to finish loading
|
||||
let domWindow = aWindow
|
||||
const domWindow = aWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
|
||||
domWindow.addEventListener(
|
||||
"load",
|
||||
function () {
|
||||
async function () {
|
||||
domWindow.removeEventListener("load", arguments.callee, false);
|
||||
if (domWindow.Zotero) {
|
||||
Services.wm.removeListener(listener);
|
||||
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
|
||||
false,
|
||||
);
|
||||
},
|
||||
};
|
||||
Services.wm.addListener(listener);
|
||||
});
|
||||
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);
|
||||
}
|
||||
await Zotero.initializationPromise;
|
||||
},
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
|
|
|||
12
package.json
12
package.json
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`), {
|
||||
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.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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": ""
|
||||
}
|
||||
}
|
||||
|
|
@ -133,7 +133,7 @@ class Addon {
|
|||
recentMainNoteIdsArr
|
||||
.slice(0, 10)
|
||||
.filter((id) => Zotero.Items.get(id).isNote())
|
||||
.join(",")
|
||||
.join(","),
|
||||
);
|
||||
},
|
||||
previewId: -1,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
47
src/hooks.ts
47
src/hooks.ts
|
|
@ -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,
|
||||
|
|
|
|||
32
src/index.ts
32
src/index.ts
|
|
@ -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);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
// 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) =>
|
||||
`/g)![0].slice(1, -1))})`
|
||||
`/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"
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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")!,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
const data = dataKeys.reduce(
|
||||
(acc, key) => {
|
||||
acc[key] = getPref(`export.${key}`) as boolean;
|
||||
return acc;
|
||||
}, {} as Record<string, any>);
|
||||
},
|
||||
{} 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",
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export function registerNotify(types: _ZoteroTypes.Notifier.Type[]) {
|
|||
(e: Event) => {
|
||||
unregisterNotify(notifyID);
|
||||
},
|
||||
false
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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}!`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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")(() => {
|
||||
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) {
|
||||
if (
|
||||
document.hasFocus() &&
|
||||
(getPref("syncPeriodSeconds") as number) > 0
|
||||
) {
|
||||
callSyncing(undefined, {
|
||||
quiet: true,
|
||||
skipActive: true,
|
||||
reason: "auto",
|
||||
});
|
||||
}
|
||||
}, Number(syncPeriod) * 1000);
|
||||
},
|
||||
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,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
);
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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(/ /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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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[],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: {},
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
import ZoteroToolkit from "zotero-plugin-toolkit";
|
||||
import { config } from "../../package.json";
|
||||
|
||||
export { createZToolkit };
|
||||
|
||||
function createZToolkit() {
|
||||
const _ztoolkit = new ZoteroToolkit();
|
||||
/**
|
||||
* Alternatively, import toolkit modules you use to minify the plugin size.
|
||||
* You can add the modules under the `MyToolkit` class below and uncomment the following line.
|
||||
*/
|
||||
// const _ztoolkit = new MyToolkit();
|
||||
initZToolkit(_ztoolkit);
|
||||
return _ztoolkit;
|
||||
}
|
||||
|
||||
function initZToolkit(_ztoolkit: ReturnType<typeof createZToolkit>) {
|
||||
const env = __env__;
|
||||
_ztoolkit.basicOptions.log.prefix = `[${config.addonName}]`;
|
||||
_ztoolkit.basicOptions.log.disableConsole = env === "production";
|
||||
_ztoolkit.UI.basicOptions.ui.enableElementJSONLog = __env__ === "development";
|
||||
_ztoolkit.UI.basicOptions.ui.enableElementDOMLog = __env__ === "development";
|
||||
_ztoolkit.basicOptions.debug.disableDebugBridgePassword =
|
||||
__env__ === "development";
|
||||
_ztoolkit.ProgressWindow.setIconURI(
|
||||
"default",
|
||||
`chrome://${config.addonRef}/content/icons/favicon.png`,
|
||||
);
|
||||
}
|
||||
|
||||
import { BasicTool, unregister } from "zotero-plugin-toolkit/dist/basic";
|
||||
import { UITool } from "zotero-plugin-toolkit/dist/tools/ui";
|
||||
import { PreferencePaneManager } from "zotero-plugin-toolkit/dist/managers/preferencePane";
|
||||
|
||||
class MyToolkit extends BasicTool {
|
||||
UI: UITool;
|
||||
PreferencePane: PreferencePaneManager;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.UI = new UITool(this);
|
||||
this.PreferencePane = new PreferencePaneManager(this);
|
||||
}
|
||||
|
||||
unregisterAll() {
|
||||
unregister(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,13 +7,6 @@
|
|||
"skipLibCheck": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
"typing",
|
||||
"node_modules/zotero-types"
|
||||
],
|
||||
"exclude": [
|
||||
"builds",
|
||||
"addon"
|
||||
]
|
||||
"include": ["src", "typings", "node_modules/zotero-types"],
|
||||
"exclude": ["build", "addon"]
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
declare interface EditorCore {
|
||||
debouncedUpdate: Function;
|
||||
disableDrag: boolean;
|
||||
|
|
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue