add: useful utils

This commit is contained in:
windingwind 2023-05-07 11:27:15 +08:00
parent 98a1c7770b
commit 1c43ef671a
9 changed files with 170 additions and 16 deletions

View File

@ -1,2 +1,3 @@
pref("extensions.zotero.__addonRef__.enable", true); /* eslint-disable no-undef */
pref("extensions.zotero.__addonRef__.input", "This is input"); pref("__prefsPrefix__.enable", true);
pref("__prefsPrefix__.input", "This is input");

View File

@ -7,6 +7,7 @@
"addonID": "addontemplate@euclpts.com", "addonID": "addontemplate@euclpts.com",
"addonRef": "addontemplate", "addonRef": "addontemplate",
"addonInstance": "AddonTemplate", "addonInstance": "AddonTemplate",
"prefsPrefix": "extensions.zotero.addontemplate",
"releasepage": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi", "releasepage": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi",
"updaterdf": "https://raw.githubusercontent.com/windingwind/zotero-addon-template/bootstrap/update.json" "updaterdf": "https://raw.githubusercontent.com/windingwind/zotero-addon-template/bootstrap/update.json"
}, },
@ -39,26 +40,26 @@
}, },
"homepage": "https://github.com/windingwind/zotero-addon-template#readme", "homepage": "https://github.com/windingwind/zotero-addon-template#readme",
"dependencies": { "dependencies": {
"zotero-plugin-toolkit": "^2.0.1" "zotero-plugin-toolkit": "^2.1.3"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^18.11.17", "@types/node": "^18.16.5",
"@typescript-eslint/eslint-plugin": "^5.59.1", "@typescript-eslint/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.1", "@typescript-eslint/parser": "^5.59.2",
"compressing": "^1.6.3", "compressing": "^1.9.0",
"concurrently": "^8.0.1", "concurrently": "^8.0.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"esbuild": "^0.17.4", "esbuild": "^0.17.18",
"eslint": "^8.39.0", "eslint": "^8.40.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
"minimist": "^1.2.7", "minimist": "^1.2.8",
"prettier": "2.8.8", "prettier": "2.8.8",
"release-it": "^15.6.0", "release-it": "^15.10.3",
"replace-in-file": "^6.3.5", "replace-in-file": "^6.3.5",
"typescript": "^5.0.4", "typescript": "^5.0.4",
"zotero-types": "^1.0.12" "zotero-types": "^1.0.14"
}, },
"prettier": { "prettier": {
"tabWidth": 2 "tabWidth": 2
} }
} }

View File

@ -6,7 +6,7 @@ import {
UIExampleFactory, UIExampleFactory,
} from "./modules/examples"; } from "./modules/examples";
import { config } from "../package.json"; import { config } from "../package.json";
import { getString, initLocale } from "./modules/locale"; import { getString, initLocale } from "./utils/locale";
import { registerPrefsScripts } from "./modules/preferenceScript"; import { registerPrefsScripts } from "./modules/preferenceScript";
async function onStartup() { async function onStartup() {

View File

@ -1,5 +1,5 @@
import { config } from "../../package.json"; import { config } from "../../package.json";
import { getString } from "./locale"; import { getString } from "../utils/locale";
function example( function example(
target: any, target: any,

View File

@ -1,5 +1,5 @@
import { config } from "../../package.json"; import { config } from "../../package.json";
import { getString } from "./locale"; import { getString } from "../utils/locale";
export function registerPrefsScripts(_window: Window) { export function registerPrefsScripts(_window: Window) {
// This function is called when the prefs window is opened // This function is called when the prefs window is opened

View File

@ -1,5 +1,8 @@
import { config } from "../../package.json"; import { config } from "../../package.json";
/**
* Initialize locale data
*/
export function initLocale() { export function initLocale() {
addon.data.locale = { addon.data.locale = {
stringBundle: Components.classes["@mozilla.org/intl/stringbundle;1"] stringBundle: Components.classes["@mozilla.org/intl/stringbundle;1"]
@ -8,6 +11,11 @@ export function initLocale() {
}; };
} }
/**
* Get locale string
* @param localString
* @param noReload
*/
export function getString(localString: string, noReload = false): string { export function getString(localString: string, noReload = false): string {
try { try {
return addon.data.locale?.stringBundle.GetStringFromName(localString); return addon.data.locale?.stringBundle.GetStringFromName(localString);

29
src/utils/prefs.ts Normal file
View File

@ -0,0 +1,29 @@
import { config } from "../../package.json";
/**
* Get preference value.
* Wrapper of `Zotero.Prefs.get`.
* @param key
*/
export function getPref(key: string) {
return Zotero.Prefs.get(`${config.prefsPrefix}.${key}`, true);
}
/**
* Set preference value.
* Wrapper of `Zotero.Prefs.set`.
* @param key
* @param value
*/
export function setPref(key: string, value: string | number | boolean) {
return Zotero.Prefs.set(`${config.prefsPrefix}.${key}`, value, true);
}
/**
* Clear preference value.
* Wrapper of `Zotero.Prefs.clear`.
* @param key
*/
export function clearPref(key: string) {
return Zotero.Prefs.clear(`${config.prefsPrefix}.${key}`, true);
}

49
src/utils/wait.ts Normal file
View File

@ -0,0 +1,49 @@
/**
* Wait until the condition is `true` or timeout.
* The callback is triggered if condition returns `true`.
* @param condition
* @param callback
* @param interval
* @param timeout
*/
export function waitUntil(
condition: () => boolean,
callback: () => void,
interval = 100,
timeout = 10000
) {
const start = Date.now();
const intervalId = ztoolkit.getGlobal("setInterval")(() => {
if (condition()) {
ztoolkit.getGlobal("clearInterval")(intervalId);
callback();
} else if (Date.now() - start > timeout) {
ztoolkit.getGlobal("clearInterval")(intervalId);
}
}, interval);
}
/**
* Wait async until the condition is `true` or timeout.
* @param condition
* @param interval
* @param timeout
*/
export function waitUtilAsync(
condition: () => boolean,
interval = 100,
timeout = 10000
) {
return new Promise<void>((resolve, reject) => {
const start = Date.now();
const intervalId = ztoolkit.getGlobal("setInterval")(() => {
if (condition()) {
ztoolkit.getGlobal("clearInterval")(intervalId);
resolve();
} else if (Date.now() - start > timeout) {
ztoolkit.getGlobal("clearInterval")(intervalId);
reject();
}
}, interval);
});
}

66
src/utils/window.ts Normal file
View File

@ -0,0 +1,66 @@
import { getString } from "./locale";
export { isWindowAlive, localeWindow };
/**
* Check if the window is alive.
* Useful to prevent opening duplicate windows.
* @param win
*/
function isWindowAlive(win?: Window) {
return win && !Components.utils.isDeadWrapper(win) && !win.closed;
}
/**
* Locale the elements in window with the locale-target attribute.
* Useful when the window is created dynamically.
* @example
* In HTML:
* ```html
* <div locale-target="innerHTML,title" title="elem.title">elem.text</div>
* ```
* In `addon/chrome/locale/en-US/addon.properties`:
* ```properties
* elem.text=Hello World
* elem.title=Locale example
* ```
* In `addon/chrome/locale/zh-CN/addon.properties`:
* ```properties
* elem.text=
* elem.title=
* ```
* After locale:
*
* if locale is "en-US"
* ```html
* <div locale-target="innerHTML,title" title="Locale example">Hello World</div>
* ```
* else if locale is "zh-CN"
* ```html
* <div locale-target="innerHTML,title" title="多语言样例"></div>
* ```
* @param win
*/
function localeWindow(win: Window) {
Array.from(win.document.querySelectorAll("*[locale-target]")).forEach(
(elem) => {
const errorInfo = "Locale Error";
const locales = elem.getAttribute("locale-target")?.split(",");
locales?.forEach((key) => {
const isProp = key in elem;
try {
const localeString = getString(
(isProp ? (elem as any)[key] : elem.getAttribute(key)).trim() || ""
);
isProp
? ((elem as any)[key] = localeString)
: elem.setAttribute(key, localeString);
} catch (error) {
isProp
? ((elem as any)[key] = errorInfo)
: elem.setAttribute(key, errorInfo);
}
});
}
);
}