diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..bd779ad --- /dev/null +++ b/.eslintrc.json @@ -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" + ] +} diff --git a/.gitattributes b/.gitattributes index 9fbfa96..25471fa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ addon/chrome/content/lib/** linguist-vendored +* text=auto eol=lf \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index c06fda3..4ea3d26 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -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"] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 197be52..6647b98 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,8 +3,7 @@ name: Bug report about: Create a report to help us improve title: "[Bug]" labels: bug -assignees: '' - +assignees: "" --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 6269982..fbabde4 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -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.** diff --git a/.github/ISSUE_TEMPLATE/note-template-issue.md b/.github/ISSUE_TEMPLATE/note-template-issue.md index 5a649c2..d250b5d 100644 --- a/.github/ISSUE_TEMPLATE/note-template-issue.md +++ b/.github/ISSUE_TEMPLATE/note-template-issue.md @@ -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 diff --git a/.gitignore b/.gitignore index d2d96bf..d277962 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -**/builds +**/build node_modules package-lock.json zotero-cmd.json \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..75fa134 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "tabWidth": 2 +} diff --git a/.release-it.json b/.release-it.json index 2b66daf..083b14b 100644 --- a/.release-it.json +++ b/.release-it.json @@ -4,7 +4,7 @@ }, "github": { "release": true, - "assets": ["builds/*.xpi"] + "assets": ["build/*.xpi"] }, "hooks": { "after:bump": "npm run build", diff --git a/.vscode/toolkit.code-snippets b/.vscode/toolkit.code-snippets index ab0b6da..425b7dd 100644 --- a/.vscode/toolkit.code-snippets +++ b/.vscode/toolkit.code-snippets @@ -1,45 +1,45 @@ { - "appendElement - full": { - "scope": "javascript,typescript", - "prefix": "appendElement", - "body": [ - "appendElement({", - "\ttag: '${1:div}',", - "\tid: '${2:id}',", - "\tnamespace: '${3:html}',", - "\tclassList: ['${4:class}'],", - "\tstyles: {${5:style}: '$6'},", - "\tproperties: {},", - "\tattributes: {},", - "\t[{ '${7:onload}', (e: Event) => $8, ${9:false} }],", - "\tcheckExistanceParent: ${10:HTMLElement},", - "\tignoreIfExists: ${11:true},", - "\tskipIfExists: ${12:true},", - "\tremoveIfExists: ${13:true},", - "\tcustomCheck: (doc: Document, options: ElementOptions) => ${14:true},", - "\tchildren: [$15]", - "}, ${16:container});" - ] - }, - "appendElement - minimum": { - "scope": "javascript,typescript", - "prefix": "appendElement", - "body": "appendElement({ tag: '$1' }, $2);" - }, - "register Notifier": { - "scope": "javascript,typescript", - "prefix": "registerObserver", - "body": [ - "registerObserver({", - "\t notify: (", - "\t\tevent: _ZoteroTypes.Notifier.Event,", - "\t\ttype: _ZoteroTypes.Notifier.Type,", - "\t\tids: string[],", - "\t\textraData: _ZoteroTypes.anyObj", - "\t) => {", - "\t\t$0", - "\t}", - "});" - ] - } + "appendElement - full": { + "scope": "javascript,typescript", + "prefix": "appendElement", + "body": [ + "appendElement({", + "\ttag: '${1:div}',", + "\tid: '${2:id}',", + "\tnamespace: '${3:html}',", + "\tclassList: ['${4:class}'],", + "\tstyles: {${5:style}: '$6'},", + "\tproperties: {},", + "\tattributes: {},", + "\t[{ '${7:onload}', (e: Event) => $8, ${9:false} }],", + "\tcheckExistanceParent: ${10:HTMLElement},", + "\tignoreIfExists: ${11:true},", + "\tskipIfExists: ${12:true},", + "\tremoveIfExists: ${13:true},", + "\tcustomCheck: (doc: Document, options: ElementOptions) => ${14:true},", + "\tchildren: [$15]", + "}, ${16:container});" + ] + }, + "appendElement - minimum": { + "scope": "javascript,typescript", + "prefix": "appendElement", + "body": "appendElement({ tag: '$1' }, $2);" + }, + "register Notifier": { + "scope": "javascript,typescript", + "prefix": "registerObserver", + "body": [ + "registerObserver({", + "\t notify: (", + "\t\tevent: _ZoteroTypes.Notifier.Event,", + "\t\ttype: _ZoteroTypes.Notifier.Type,", + "\t\tids: string[],", + "\t\textraData: _ZoteroTypes.anyObj", + "\t) => {", + "\t\t$0", + "\t}", + "});" + ] + } } diff --git a/README.md b/README.md index 0067d37..cd61fea 100644 --- a/README.md +++ b/README.md @@ -86,10 +86,10 @@ and: ## 👋 Install - Download the latest release (.xpi file) from the [Releases Page](https://github.com/windingwind/zotero-better-notes/releases) - +
More Versions - + - [Latest Stable](https://github.com/windingwind/zotero-better-notes/releases/latest) - [v1.0.4](https://github.com/windingwind/zotero-better-notes/releases/tag/1.0.4) (last for Zotero 6) - [v0.8.9](https://github.com/windingwind/zotero-better-notes/releases/tag/0.8.9) (last with auto-insert, tag-insert, math-ocr, for Zotero 6) @@ -98,6 +98,7 @@ and:
_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`. diff --git a/addon/bootstrap.js b/addon/bootstrap.js index e11e17a..21ef77f 100644 --- a/addon/bootstrap.js +++ b/addon/bootstrap.js @@ -11,6 +11,8 @@ if (typeof Zotero == "undefined") { var chromeHandle; +var windowListener; + // In Zotero 6, bootstrap methods are called before Zotero is initialized, and using include.js // to get the Zotero XPCOM service would risk breaking Zotero startup. Instead, wait for the main // Zotero window to open and get the Zotero object from there. @@ -18,73 +20,85 @@ var chromeHandle; // In Zotero 7, bootstrap methods are not called until Zotero is initialized, and the 'Zotero' is // automatically made available. async function waitForZotero() { - if (typeof Zotero != "undefined") { - await Zotero.initializationPromise; - } - - var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); - var windows = Services.wm.getEnumerator("navigator:browser"); - var found = false; - while (windows.hasMoreElements()) { - let win = windows.getNext(); - if (win.Zotero) { - Zotero = win.Zotero; - found = true; - break; + await new Promise(async (resolve) => { + if (typeof Zotero != "undefined") { + resolve(); } - } - if (!found) { - await new Promise((resolve) => { - var listener = { - onOpenWindow: function (aWindow) { - // Wait for the window to finish loading - let domWindow = aWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow); - domWindow.addEventListener( - "load", - function () { - domWindow.removeEventListener("load", arguments.callee, false); - if (domWindow.Zotero) { - Services.wm.removeListener(listener); - Zotero = domWindow.Zotero; - resolve(); - } - }, - false - ); - }, - }; - Services.wm.addListener(listener); - }); - } - await Zotero.initializationPromise; + + const { Services } = ChromeUtils.import( + "resource://gre/modules/Services.jsm", + ); + const windows = Services.wm.getEnumerator("navigator:browser"); + let found = false; + while (windows.hasMoreElements()) { + let win = windows.getNext(); + if (win.Zotero) { + Zotero = win.Zotero; + found = true; + resolve(); + break; + } + } + windowListener = { + onOpenWindow: function (aWindow) { + // Wait for the window to finish loading + const domWindow = aWindow + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow); + domWindow.addEventListener( + "load", + async function () { + domWindow.removeEventListener("load", arguments.callee, false); + if (!found && domWindow.Zotero) { + Zotero = domWindow.Zotero; + resolve(); + } else if ( + domWindow.location.href === + "chrome://zotero/content/zoteroPane.xhtml" + ) { + // Call the hook for the main window load event + // Note that this is not called the first time the window is opened + // (when Zotero is initialized), but only when the window is re-opened + // after being closed + await Zotero.__addonInstance__?.hooks.onMainWindowLoad(domWindow); + } + }, + false, + ); + }, + onCloseWindow: function (aWindow) { + const domWindow = aWindow + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow); + if ( + domWindow.location.href === "chrome://zotero/content/zoteroPane.xhtml" + ) { + Zotero.__addonInstance__?.hooks.onMainWindowUnload(domWindow); + } + }, + }; + Services.wm.addListener(windowListener); + }); } function install(data, reason) {} async function startup({ id, version, resourceURI, rootURI }, reason) { await waitForZotero(); + await Zotero.initializationPromise; // String 'rootURI' introduced in Zotero 7 if (!rootURI) { rootURI = resourceURI.spec; } - if (Zotero.platformMajorVersion >= 102) { - var aomStartup = Components.classes[ - "@mozilla.org/addons/addon-manager-startup;1" - ].getService(Components.interfaces.amIAddonManagerStartup); - var manifestURI = Services.io.newURI(rootURI + "manifest.json"); - chromeHandle = aomStartup.registerChrome(manifestURI, [ - ["content", "__addonRef__", rootURI + "chrome/content/"], - ["locale", "__addonRef__", "en-US", rootURI + "chrome/locale/en-US/"], - ["locale", "__addonRef__", "zh-CN", rootURI + "chrome/locale/zh-CN/"], - ["locale", "__addonRef__", "ru-RU", rootURI + "chrome/locale/ru-RU/"], - ]); - } else { - setDefaultPrefs(rootURI); - } + var aomStartup = Components.classes[ + "@mozilla.org/addons/addon-manager-startup;1" + ].getService(Components.interfaces.amIAddonManagerStartup); + var manifestURI = Services.io.newURI(rootURI + "manifest.json"); + chromeHandle = aomStartup.registerChrome(manifestURI, [ + ["content", "__addonRef__", rootURI + "chrome/content/"], + ]); /** * Global variables for plugin code. @@ -94,13 +108,14 @@ async function startup({ id, version, resourceURI, rootURI }, reason) { */ const ctx = { rootURI, + // Define main window's document to create a fake browser environment document: Zotero.getMainWindow().document, }; ctx._globalThis = ctx; Services.scriptloader.loadSubScript( - `${rootURI}/chrome/content/scripts/index.js`, - ctx + `${rootURI}/chrome/content/scripts/__addonRef__.js`, + ctx, ); } @@ -108,9 +123,11 @@ function shutdown({ id, version, resourceURI, rootURI }, reason) { if (reason === APP_SHUTDOWN) { return; } + Services.wm.removeListener(windowListener); + if (typeof Zotero === "undefined") { Zotero = Components.classes["@zotero.org/Zotero;1"].getService( - Components.interfaces.nsISupports + Components.interfaces.nsISupports, ).wrappedJSObject; } Zotero.__addonInstance__.hooks.onShutdown(); @@ -119,7 +136,7 @@ function shutdown({ id, version, resourceURI, rootURI }, reason) { .getService(Components.interfaces.nsIStringBundleService) .flushBundles(); - Cu.unload(`${rootURI}/chrome/content/scripts/index.js`); + Cu.unload(`${rootURI}/chrome/content/scripts/__addonRef__.js`); if (chromeHandle) { chromeHandle.destruct(); @@ -128,27 +145,3 @@ function shutdown({ id, version, resourceURI, rootURI }, reason) { } function uninstall(data, reason) {} - -// Loads default preferences from defaults/preferences/prefs.js in Zotero 6 -function setDefaultPrefs(rootURI) { - var branch = Services.prefs.getDefaultBranch(""); - var obj = { - pref(pref, value) { - switch (typeof value) { - case "boolean": - branch.setBoolPref(pref, value); - break; - case "string": - branch.setStringPref(pref, value); - break; - case "number": - branch.setIntPref(pref, value); - break; - default: - Zotero.logError(`Invalid type '${typeof value}' for pref '${pref}'`); - } - }, - }; - Zotero.getMainWindow().console.log(rootURI + "prefs.js"); - Services.scriptloader.loadSubScript(rootURI + "prefs.js", obj); -} diff --git a/package.json b/package.json index ac63687..ab92acd 100644 --- a/package.json +++ b/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" } diff --git a/scripts/build.mjs b/scripts/build.mjs index 8a69acc..552f25a 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -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( + /^(?[a-zA-Z]\S*)([ ]*=[ ]*)(?.*)$/m, + ); + if (match) { + localeMessage.add(match.groups.message); + return `${config.addonRef}-${line}`; + } else { + return line; + } + }); + return prefixedLines.join("\n"); + }, + }); + + const replaceResultXhtml = replaceInFileSync({ + files: [`${buildDir}/addon/**/*.xhtml`], + processor: (input) => { + const matchs = [...input.matchAll(/(data-l10n-id)="(\S*)"/g)]; + matchs.map((match) => { + if (localeMessage.has(match[2])) { + input = input.replace( + match[0], + `${match[1]}="${config.addonRef}-${match[2]}"`, + ); + } else { + localeMessageMiss.add(match[2]); + } + }); + return input; + }, + }); + + console.log( + "[Build] Run replace in ", + replaceResult + .filter((f) => f.hasChanged) + .map((f) => `${f.file} : ${f.numReplacements} / ${f.numMatches}`), + replaceResultFlt.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`), + replaceResultXhtml.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`), + ); + + if (localeMessageMiss.size !== 0) { + console.warn( + `[Build] [Warn] Fluent message [${new Array( + ...localeMessageMiss, + )}] do not exsit in addon's locale files.`, + ); + } +} + +async function esbuild() { + await build({ + entryPoints: ["src/index.ts"], + define: { + __env__: `"${env.NODE_ENV}"`, + }, + bundle: true, + outfile: path.join( + buildDir, + `addon/chrome/content/scripts/${config.addonRef}.js`, + ), + // Don't turn minify on + // minify: true, + target: ["firefox102"], + }).catch(() => exit(1)); + + await build({ + entryPoints: ["src/extras/editorScript.ts"], + bundle: true, + outfile: path.join( + buildDir, + "addon/chrome/content/scripts/editorScript.js", + ), + target: ["firefox102"], + }).catch(() => exit(1)); + + await build({ + entryPoints: ["src/extras/docxWorker.ts"], + bundle: true, + outfile: path.join(buildDir, "addon/chrome/content/scripts/docxWorker.js"), + target: ["firefox102"], + }).catch(() => exit(1)); +} + +async function main() { + console.log( + `[Build] BUILD_DIR=${buildDir}, VERSION=${version}, BUILD_TIME=${buildTime}, ENV=${[ + env.NODE_ENV, + ]}`, + ); + + clearFolder(buildDir); + + copyFolderRecursiveSync("addon", buildDir); + + copyFileSync("update-template.json", "update.json"); + + await esbuild(); + + console.log("[Build] Run esbuild OK"); + + replaceString(); + + console.log("[Build] Replace OK"); + + // Walk the builds/addon/locale folder's sub folders and rename *.ftl to addonRef-*.ftl + renameLocaleFiles(); console.log("[Build] Addon prepare OK"); - zip.compressDir(join(buildDir, "addon"), join(buildDir, `${name}.xpi`), { - ignoreBase: true, - }); + await zip.compressDir( + path.join(buildDir, "addon"), + path.join(buildDir, `${name}.xpi`), + { + ignoreBase: true, + }, + ); console.log("[Build] Addon pack OK"); console.log( - `[Build] Finished in ${(new Date().getTime() - t.getTime()) / 1000} s.` + `[Build] Finished in ${(new Date().getTime() - t.getTime()) / 1000} s.`, ); } diff --git a/scripts/reload.mjs b/scripts/reload.mjs index f6ea774..5cf84c7 100644 --- a/scripts/reload.mjs +++ b/scripts/reload.mjs @@ -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 () => { diff --git a/scripts/start.mjs b/scripts/start.mjs index 5cec962..118b5a0 100644 --- a/scripts/start.mjs +++ b/scripts/start.mjs @@ -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 /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); diff --git a/scripts/zotero-cmd-default.json b/scripts/zotero-cmd-default.json index 9bb8011..c02a99f 100644 --- a/scripts/zotero-cmd-default.json +++ b/scripts/zotero-cmd-default.json @@ -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": "" } -} \ No newline at end of file +} diff --git a/src/addon.ts b/src/addon.ts index 717c277..9e0081c 100644 --- a/src/addon.ts +++ b/src/addon.ts @@ -133,7 +133,7 @@ class Addon { recentMainNoteIdsArr .slice(0, 10) .filter((id) => Zotero.Items.get(id).isNote()) - .join(",") + .join(","), ); }, previewId: -1, diff --git a/src/extras/editorScript.ts b/src/extras/editorScript.ts index 6fbdec3..e326e3b 100644 --- a/src/extras/editorScript.ts +++ b/src/extras/editorScript.ts @@ -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; diff --git a/src/hooks.ts b/src/hooks.ts index 39d72a9..c32e143 100644 --- a/src/hooks.ts +++ b/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 { + // 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 { + 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, diff --git a/src/index.ts b/src/index.ts index c89e404..c4790a9 100644 --- a/src/index.ts +++ b/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[0]) { + Object.defineProperty(_globalThis, name, { + get() { + return basicTool.getGlobal(name); + }, + }); +} diff --git a/src/modules/annotationTagAction.ts b/src/modules/annotationTagAction.ts index f50f6b7..0293ba3 100644 --- a/src/modules/annotationTagAction.ts +++ b/src/modules/annotationTagAction.ts @@ -4,7 +4,7 @@ export { annotationTagAction }; async function annotationTagAction( ids: Array, - extraData: Record + extraData: Record, ) { 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, ); } } diff --git a/src/modules/convert/api.ts b/src/modules/convert/api.ts index 91ca025..3761beb 100644 --- a/src/modules/convert/api.ts +++ b/src/modules/convert/api.ts @@ -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[1] + options: Parameters[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[1] = {} + options: Parameters[1] = {}, ) { return parseAnnotationHTML(annotations, options); } @@ -227,7 +229,7 @@ function note2rehype(str: string) { removeBlank(_n, parentNode, -1); removeBlank(_n, parentNode, 1); } - } + }, ); // Make sure and wrapped by

@@ -245,7 +247,7 @@ function note2rehype(str: string) { replace(_n, p); } } - } + }, ); // Make sure empty

under root node is removed @@ -259,7 +261,7 @@ function note2rehype(str: string) { parentNode.children.splice(parentNode.children.indexOf(_n), 1); } } - } + }, ); return rehype; } @@ -310,13 +312,13 @@ async function rehype2remark(rehype: HRoot) { if (node.properties.style) { hasStyle = true; } - } + }, ); - if (0 && hasStyle) { - return h(node, "styleTable", toHtml(node)); - } else { - return defaultHandlers.table(h, node); - } + // if (0 && hasStyle) { + // return h(node, "styleTable", toHtml(node)); + // } else { + return defaultHandlers.table(h, node); + // } }, wrapper: (h, node) => { return h(node, "wrapper", toText(node)); @@ -389,7 +391,7 @@ function remark2md(remark: MRoot) { }, }, } as any) - .stringify(remark) + .stringify(remark), ); } @@ -401,7 +403,7 @@ function md2remark(str: string) { .replace( /!\[.*\]\((.*)\)/g, (s: string) => - `![](${encodeURIComponent(s.match(/\(.*\)/g)![0].slice(1, -1))})` + `![](${encodeURIComponent(s.match(/\(.*\)/g)![0].slice(1, -1))})`, ); const remark = unified() .use(remarkGfm) @@ -439,7 +441,7 @@ function rehype2note(rehype: HRoot) { (node: any) => { node.tagName = "span"; node.properties.style = "text-decoration: line-through"; - } + }, ); // Code node @@ -454,7 +456,7 @@ function rehype2note(rehype: HRoot) { node.value = toText(node); node.type = "text"; } - } + }, ); // Table node with style @@ -472,14 +474,14 @@ function rehype2note(rehype: HRoot) { if (node.properties.style) { hasStyle = true; } - } + }, ); if (hasStyle) { node.value = toHtml(node).replace(/[\r\n]/g, ""); node.children = []; node.type = "raw"; } - } + }, ); // Convert thead to tbody @@ -490,7 +492,7 @@ function rehype2note(rehype: HRoot) { node.value = toHtml(node).slice(7, -8); node.children = []; node.type = "raw"; - } + }, ); // Wrap lines in list with

(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" diff --git a/src/modules/createNote.ts b/src/modules/createNote.ts index ba554c4..686925b 100644 --- a/src/modules/createNote.ts +++ b/src/modules/createNote.ts @@ -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( - `

${header}

\n
` + `

${header}

\n
`, ); 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; async function createNoteFromTemplate( noteType: "item", - parentType: "reader" | "library" + parentType: "reader" | "library", ): Promise; 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) { diff --git a/src/modules/editor/image.ts b/src/modules/editor/image.ts index bda6f9b..b669efb 100644 --- a/src/modules/editor/image.ts +++ b/src/modules/editor/image.ts @@ -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) => { diff --git a/src/modules/editor/initalize.ts b/src/modules/editor/initalize.ts index 2241479..03cffa2 100644 --- a/src/modules/editor/initalize.ts +++ b/src/modules/editor/initalize.ts @@ -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); }, - } + }, ); } diff --git a/src/modules/editor/inject.ts b/src/modules/editor/inject.ts index 36b30ae..eb4afea 100644 --- a/src/modules/editor/inject.ts +++ b/src/modules/editor/inject.ts @@ -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, ); } diff --git a/src/modules/editor/menu.ts b/src/modules/editor/menu.ts index d9eb69b..453d00d 100644 --- a/src/modules/editor/menu.ts +++ b/src/modules/editor/menu.ts @@ -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); diff --git a/src/modules/editor/popup.ts b/src/modules/editor/popup.ts index 26a7ac0..d868535 100644 --- a/src/modules/editor/popup.ts +++ b/src/modules/editor/popup.ts @@ -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")!, ); } diff --git a/src/modules/editor/toolbar.ts b/src/modules/editor/toolbar.ts index 16a37ce..984b409 100644 --- a/src/modules/editor/toolbar.ts +++ b/src/modules/editor/toolbar.ts @@ -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) { `${ e.editor._item.getNoteTitle().trim() || link }`, - "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); diff --git a/src/modules/export/api.ts b/src/modules/export/api.ts index ffdf401..34573a1 100644 --- a/src/modules/export/api.ts +++ b/src/modules/export/api.ts @@ -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 { 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 { const html = await addon.api.template.runTemplate( "[QuickImportV2]", "link, noteItem", - [linkParam.link, noteItem] + [linkParam.link, noteItem], ); newLines.push(html); } diff --git a/src/modules/export/docx.ts b/src/modules/export/docx.ts index 5cc0f0f..9ecd4c5 100644 --- a/src/modules/export/docx.ts +++ b/src/modules/export/docx.ts @@ -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; diff --git a/src/modules/export/exportWindow.ts b/src/modules/export/exportWindow.ts index 95cfd9f..e8cfbb6 100644 --- a/src/modules/export/exportWindow.ts +++ b/src/modules/export/exportWindow.ts @@ -17,7 +17,7 @@ enum OPTIONS { export async function showExportNoteOptions( noteIds: number[], - overwriteOptions: Record = {} + overwriteOptions: Record = {}, ) { const items = Zotero.Items.get(noteIds); const noteItems: Zotero.Item[] = []; @@ -33,17 +33,20 @@ export async function showExportNoteOptions( return; } const dataKeys = Object.keys(OPTIONS).filter( - (value) => typeof value === "string" + (value) => typeof value === "string", + ); + const data = dataKeys.reduce( + (acc, key) => { + acc[key] = getPref(`export.${key}`) as boolean; + return acc; + }, + {} as Record, ); - const data = dataKeys.reduce((acc, key) => { - acc[key] = getPref(`export.${key}`) as boolean; - return acc; - }, {} as Record); 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, overwriteOptions) + Object.assign(data as Record, 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", diff --git a/src/modules/export/freemind.ts b/src/modules/export/freemind.ts index fd1018a..08b26ea 100644 --- a/src/modules/export/freemind.ts +++ b/src/modules/export/freemind.ts @@ -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) => { mmXML += ``; 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"), )}`; } if (node.hasChildren()) { diff --git a/src/modules/export/markdown.ts b/src/modules/export/markdown.ts index ed8fb67..3760b19 100644 --- a/src/modules/export/markdown.ts +++ b/src/modules/export/markdown.ts @@ -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(" { @@ -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(" win.document.readyState === "complete"); await Zotero.Promise.delay(3000); diff --git a/src/modules/imageViewer.ts b/src/modules/imageViewer.ts index def5b37..15b0d6b 100644 --- a/src/modules/imageViewer.ts +++ b/src/modules/imageViewer.ts @@ -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, ); } diff --git a/src/modules/import/markdown.ts b/src/modules/import/markdown.ts index 7a0c3cf..98593a8 100644 --- a/src/modules/import/markdown.ts +++ b/src/modules/import/markdown.ts @@ -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; diff --git a/src/modules/menu.ts b/src/modules/menu.ts index c318cdb..aa8d78d 100644 --- a/src/modules/menu.ts +++ b/src/modules/menu.ts @@ -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"), - } + }, ); } diff --git a/src/modules/notify.ts b/src/modules/notify.ts index 6685ec4..edecf49 100644 --- a/src/modules/notify.ts +++ b/src/modules/notify.ts @@ -18,7 +18,7 @@ export function registerNotify(types: _ZoteroTypes.Notifier.Type[]) { (e: Event) => { unregisterNotify(notifyID); }, - false + false, ); } diff --git a/src/modules/preferenceWindow.ts b/src/modules/preferenceWindow.ts index 7b42f90..013bf50 100644 --- a/src/modules/preferenceWindow.ts +++ b/src/modules/preferenceWindow.ts @@ -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}!`, ); }); } diff --git a/src/modules/reader.ts b/src/modules/reader.ts index 5f3f17e..7729d51 100644 --- a/src/modules/reader.ts +++ b/src/modules/reader.ts @@ -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 { 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 { 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); diff --git a/src/modules/sync/api.ts b/src/modules/sync/api.ts index a71426e..f37637c 100644 --- a/src/modules/sync/api.ts +++ b/src/modules/sync/api.ts @@ -38,7 +38,7 @@ async function getRelatedNoteIds(noteId: number): Promise { } 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 { } async function getRelatedNoteIdsFromNotes( - noteIds: number[] + noteIds: number[], ): Promise { 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 { 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], ); } diff --git a/src/modules/sync/diffWindow.ts b/src/modules/sync/diffWindow.ts index d89b095..339b446 100644 --- a/src/modules/sync/diffWindow.ts +++ b/src/modules/sync/diffWindow.ts @@ -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; 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(); diff --git a/src/modules/sync/hooks.ts b/src/modules/sync/hooks.ts index 798b38e..9d2594d 100644 --- a/src/modules/sync/hooks.ts +++ b/src/modules/sync/hooks.ts @@ -8,20 +8,26 @@ function setSyncing() { const syncPeriod = getPref("syncPeriodSeconds") as number; if (syncPeriod > 0) { showHint(`${getString("sync.start.hint")} ${syncPeriod} s`); - const timer = ztoolkit.getGlobal("setInterval")(() => { - if (!addon.data.alive) { - showHint(getString("sync.stop.hint")); - ztoolkit.getGlobal("clearInterval")(timer); - } - // Only when Zotero is active and focused - if (document.hasFocus() && (getPref("syncPeriodSeconds") as number) > 0) { - callSyncing(undefined, { - quiet: true, - skipActive: true, - reason: "auto", - }); - } - }, Number(syncPeriod) * 1000); + const timer = ztoolkit.getGlobal("setInterval")( + () => { + if (!addon.data.alive) { + showHint(getString("sync.stop.hint")); + ztoolkit.getGlobal("clearInterval")(timer); + } + // Only when Zotero is active and focused + if ( + document.hasFocus() && + (getPref("syncPeriodSeconds") as number) > 0 + ) { + callSyncing(undefined, { + quiet: true, + skipActive: true, + reason: "auto", + }); + } + }, + Number(syncPeriod) * 1000, + ); } } @@ -31,7 +37,7 @@ async function callSyncing( quiet: true, skipActive: true, reason: "unknown", - } + }, ) { // Always log in development mode if (addon.data.env === "development") { @@ -61,11 +67,11 @@ async function callSyncing( .filter( (editor) => !Components.utils.isDeadWrapper(editor._iframeWindow) && - editor._iframeWindow.document.hasFocus() + editor._iframeWindow.document.hasFocus(), ) .map((editor) => editor._item.id); const filteredItems = items.filter( - (item) => !activeNoteIds.includes(item.id) + (item) => !activeNoteIds.includes(item.id), ); skippedCount = items.length - filteredItems.length; items = filteredItems; @@ -76,7 +82,7 @@ async function callSyncing( progress = new ztoolkit.ProgressWindow( `[${getString("sync.running.hint.title")}] ${ addon.data.env === "development" ? reason : "Better Notes" - }` + }`, ) .createLine({ text: `[${getString("sync.running.hint.check")}] 0/${ @@ -95,7 +101,7 @@ async function callSyncing( for (const item of items) { const syncStatus = addon.api.sync.getSyncStatus(item.id); const filepath = syncStatus.path; - let compareResult = await doCompare(item); + const compareResult = await doCompare(item); switch (compareResult) { case SyncCode.NoteAhead: if (Object.keys(toExport).includes(filepath)) { @@ -139,7 +145,7 @@ async function callSyncing( for (const syncStatus of toImport) { progress?.changeLine({ text: `[${getString( - "sync.running.hint.updateNote" + "sync.running.hint.updateNote", )}] ${i}/${totalCount}, ${toDiff.length} queuing...`, progress: ((i - 1) / totalCount) * 100, }); @@ -160,7 +166,7 @@ async function callSyncing( await addon.hooks.onShowSyncDiff( syncStatus.itemID, - OS.Path.join(syncStatus.path, syncStatus.filename) + OS.Path.join(syncStatus.path, syncStatus.filename), ); i += 1; } @@ -170,10 +176,10 @@ async function callSyncing( text: (syncCount ? `[${getString( - "sync.running.hint.finish" + "sync.running.hint.finish", )}] ${syncCount} ${getString("sync.running.hint.synced")}` : `[${getString("sync.running.hint.finish")}] ${getString( - "sync.running.hint.upToDate" + "sync.running.hint.upToDate", )}`) + (skippedCount ? `, ${skippedCount} skipped.` : ""), progress: 100, }); diff --git a/src/modules/sync/infoWindow.ts b/src/modules/sync/infoWindow.ts index 2ae1cab..7e8cc24 100644 --- a/src/modules/sync/infoWindow.ts +++ b/src/modules/sync/infoWindow.ts @@ -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)), ); }, }) diff --git a/src/modules/sync/managerWindow.ts b/src/modules/sync/managerWindow.ts index 4350df8..03a0e4e 100644 --- a/src/modules/sync/managerWindow.ts +++ b/src/modules/sync/managerWindow.ts @@ -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); } diff --git a/src/modules/template/api.ts b/src/modules/template/api.ts index 540453f..c5d93a0 100644 --- a/src/modules/template/api.ts +++ b/src/modules/template/api.ts @@ -19,7 +19,7 @@ async function runTemplate( useDefault: true, dryRun: false, stage: "default", - } + }, ): Promise { 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 { /** * 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` diff --git a/src/modules/template/controller.ts b/src/modules/template/controller.ts index 255fcd4..04eef92 100644 --- a/src/modules/template/controller.ts +++ b/src/modules/template/controller.ts @@ -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)) { diff --git a/src/modules/template/editorWindow.ts b/src/modules/template/editorWindow.ts index 21219e8..a8929bc 100644 --- a/src/modules/template/editorWindow.ts +++ b/src/modules/template/editorWindow.ts @@ -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(/
/g, "
") .replace(/
/g, "
") - .replace(/]+)\>/g, ""); + .replace(/]+)>/g, ""); 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; diff --git a/src/modules/template/picker.ts b/src/modules/template/picker.ts index bd58afc..52d7d8e 100644 --- a/src/modules/template/picker.ts +++ b/src/modules/template/picker.ts @@ -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): void; function showTemplatePicker(): void; function showTemplatePicker( mode: typeof addon.data.templatePicker.mode = "insert", - data: Record = {} + data: Record = {}, ) { 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, ); } diff --git a/src/modules/template/preview.ts b/src/modules/template/preview.ts index 0d752dc..e94261a 100644 --- a/src/modules/template/preview.ts +++ b/src/modules/template/preview.ts @@ -5,7 +5,7 @@ export { renderTemplatePreview }; async function renderTemplatePreview( templateName: string, - inputItems?: Zotero.Item[] + inputItems?: Zotero.Item[], ): Promise { let html: string = "

Preview rendering failed

"; 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")) { diff --git a/src/modules/workspace/content.ts b/src/modules/workspace/content.ts index 447cfcb..86de793 100644 --- a/src/modules/workspace/content.ts +++ b/src/modules/workspace/content.ts @@ -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); diff --git a/src/modules/workspace/message.ts b/src/modules/workspace/message.ts index b2d382f..f24c5ae 100644 --- a/src/modules/workspace/message.ts +++ b/src/modules/workspace/message.ts @@ -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; diff --git a/src/modules/workspace/tab.ts b/src/modules/workspace/tab.ts index df0c65d..aeb5547 100644 --- a/src/modules/workspace/tab.ts +++ b/src/modules/workspace/tab.ts @@ -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"; diff --git a/src/modules/workspace/window.ts b/src/modules/workspace/window.ts index da92fb5..f671f93 100644 --- a/src/modules/workspace/window.ts +++ b/src/modules/workspace/window.ts @@ -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); diff --git a/src/utils/annotation.ts b/src/utils/annotation.ts index 3ce32c9..30d1b87 100644 --- a/src/utils/annotation.ts +++ b/src/utils/annotation.ts @@ -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[], 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 = `${formatted}`; } // 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 = ``; } @@ -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 = `${text}`; quotedHighlightHTML = `${Zotero.getString( - "punctuation.openingQMark" + "punctuation.openingQMark", )}${text}${Zotero.getString("punctuation.closingQMark")}`; } @@ -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( /(
[^<>]*?)({{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[], false, - options.skipCitation + options.skipCitation, ).html; return html; } diff --git a/src/utils/citation.ts b/src/utils/citation.ts index b263f74..7c10417 100644 --- a/src/utils/citation.ts +++ b/src/utils/citation.ts @@ -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 += `

${formatted}

`; } } diff --git a/src/utils/editor.ts b/src/utils/editor.ts index 0663db1..c8d4124 100644 --- a/src/utils/editor.ts +++ b/src/utils/editor.ts @@ -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, - 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, targetNode: TreeModel.Node, - 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"); diff --git a/src/utils/hint.ts b/src/utils/hint.ts index 0c3afb9..a83fa1e 100644 --- a/src/utils/hint.ts +++ b/src/utils/hint.ts @@ -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 diff --git a/src/utils/itemPicker.ts b/src/utils/itemPicker.ts index 1638b70..e96b996 100644 --- a/src/utils/itemPicker.ts +++ b/src/utils/itemPicker.ts @@ -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; diff --git a/src/utils/link.ts b/src/utils/link.ts index c103fb0..71f9c71 100644 --- a/src/utils/link.ts +++ b/src/utils/link.ts @@ -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[], ); } diff --git a/src/utils/locale.ts b/src/utils/locale.ts index 569c64b..b3c7c92 100644 --- a/src/utils/locale.ts +++ b/src/utils/locale.ts @@ -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 } + options: { branch?: string | undefined; args?: Record }, ): 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 } = {} + options: { branch?: string | undefined; args?: Record } = {}, ): 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; } } diff --git a/src/utils/note.ts b/src/utils/note.ts index 7443623..f3227e5 100644 --- a/src/utils/note.ts +++ b/src/utils/note.ts @@ -25,7 +25,7 @@ function parseHTMLLines(html: string): string[] { html = html.replace(/]*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( `
${lines.join( - "\n" - )}
` + "\n", + )}`, ); } else { - let noteHead = noteText.substring(0, containerIndex); + const noteHead = noteText.substring(0, containerIndex); note.setNote( `${noteHead}data-schema-version="${ config.dataSchemaVersion - }">${lines.join("\n")}` + }">${lines.join("\n")}`, ); } @@ -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; async function renderNoteHTML(noteItem: Zotero.Item): Promise; async function renderNoteHTML( htmlOrNote: string | Zotero.Item, - refNotes?: Zotero.Item[] + refNotes?: Zotero.Item[], ): Promise { 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; - 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 { 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("^"); 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) => boolean; - } = { keepRoot: false, keepLink: false } + } = { keepRoot: false, keepLink: false }, ): TreeModel.Node[] { 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 | undefined = undefined + root: TreeModel.Node | 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 | undefined = undefined + root: TreeModel.Node | 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 { 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: {}, diff --git a/src/utils/str.ts b/src/utils/str.ts index f8d96da..f9d2727 100644 --- a/src/utils/str.ts +++ b/src/utils/str.ts @@ -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) ); diff --git a/src/utils/wait.ts b/src/utils/wait.ts index db654b6..4183d14 100644 --- a/src/utils/wait.ts +++ b/src/utils/wait.ts @@ -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((resolve, reject) => { const start = Date.now(); diff --git a/src/utils/window.ts b/src/utils/window.ts index 0170eb1..567dce5 100644 --- a/src/utils/window.ts +++ b/src/utils/window.ts @@ -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); } }); - } + }, ); } diff --git a/src/utils/ztoolkit.ts b/src/utils/ztoolkit.ts new file mode 100644 index 0000000..dd7035c --- /dev/null +++ b/src/utils/ztoolkit.ts @@ -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) { + 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); + } +} diff --git a/tsconfig.json b/tsconfig.json index 40c646b..a033072 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,13 +7,6 @@ "skipLibCheck": true, "strict": true }, - "include": [ - "src", - "typing", - "node_modules/zotero-types" - ], - "exclude": [ - "builds", - "addon" - ] -} \ No newline at end of file + "include": ["src", "typings", "node_modules/zotero-types"], + "exclude": ["build", "addon"] +} diff --git a/typing/editor.d.ts b/typings/editor.d.ts similarity index 96% rename from typing/editor.d.ts rename to typings/editor.d.ts index 592c2ef..dbaaacc 100644 --- a/typing/editor.d.ts +++ b/typings/editor.d.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-types */ declare interface EditorCore { debouncedUpdate: Function; disableDrag: boolean; diff --git a/typing/global.d.ts b/typings/global.d.ts similarity index 79% rename from typing/global.d.ts rename to typings/global.d.ts index 97559a3..e9e395c 100644 --- a/typing/global.d.ts +++ b/typings/global.d.ts @@ -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; diff --git a/typing/note.d.ts b/typings/note.d.ts similarity index 100% rename from typing/note.d.ts rename to typings/note.d.ts diff --git a/typing/template.d.ts b/typings/template.d.ts similarity index 100% rename from typing/template.d.ts rename to typings/template.d.ts