diff --git a/README.md b/README.md
index 276acdc..ca1c422 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,6 @@ Search `@example` in `src/examples.ts`. The examples are called in `src/hooks.ts
1. When install/enable/startup triggered from Zotero, `bootstrap.js` > `startup` is called
- Wait for Zotero ready
- - Prepare global variables `ctx`. They are available globally in the plugin scope
- Load `index.js` (the main entrance of plugin code, built from `index.ts`)
- Register resources if Zotero 7+
2. In the main entrance `index.js`, the plugin object is injected under `Zotero` and `hooks.ts` > `onStartup` is called.
@@ -194,7 +193,7 @@ This section shows the directory structure of a template.
│ │
│ └─zh-CN
│ | overlay.dtd
-│ │ addon.properties
+│ └─ addon.properties
│
├─builds # build dir
│ └─.xpi
@@ -203,10 +202,12 @@ This section shows the directory structure of a template.
│ index.ts # main entry
│ addon.ts # base class
│ hooks.ts # lifecycle hooks
- │ examples.ts # examples factory
- │ locale.ts # Locale class for properties files
- └─ prefs.ts # preferences class
-
+ |
+ └─modules # sub modules
+ │ examples.ts # examples factory
+ │ locale.ts # locale .properties
+ │ preferenceScript.ts # script runs in preferences.xhtml
+ │ progressWindow.ts # progressWindow tool
```
### Build
diff --git a/addon/chrome/content/preferences.xhtml b/addon/chrome/content/preferences.xhtml
index 86ad3f4..c928e36 100644
--- a/addon/chrome/content/preferences.xhtml
+++ b/addon/chrome/content/preferences.xhtml
@@ -1,6 +1,6 @@
@@ -22,5 +22,7 @@
-
+
diff --git a/src/addon.ts b/src/addon.ts
index 76445ed..f1b6fda 100644
--- a/src/addon.ts
+++ b/src/addon.ts
@@ -1,24 +1,26 @@
-import AddonHooks from "./hooks";
-import AddonPrefs from "./prefs";
-import AddonLocale from "./locale";
+import hooks from "./hooks";
class Addon {
- // Env type, see build.js
- public env!: "development" | "production";
- // If addon is disabled/removed, set it false
- public alive: boolean;
- // Lifecycle events
- public hooks: AddonHooks;
- // Scripts for prefpane window
- public prefs: AddonPrefs;
- // Runtime locale with .properties
- public locale: AddonLocale;
+ public data: {
+ alive: boolean;
+ // Env type, see build.js
+ env: "development" | "production";
+ locale?: {
+ stringBundle: any;
+ };
+ prefs?: {
+ window: Window;
+ };
+ };
+ // Lifecycle hooks
+ public hooks: typeof hooks;
constructor() {
- this.alive = true;
- this.hooks = new AddonHooks();
- this.prefs = new AddonPrefs();
- this.locale = new AddonLocale();
+ this.data = {
+ alive: true,
+ env: __env__,
+ };
+ this.hooks = hooks;
}
}
diff --git a/src/hooks.ts b/src/hooks.ts
index 580d92d..57c4f1a 100644
--- a/src/hooks.ts
+++ b/src/hooks.ts
@@ -1,62 +1,85 @@
-import { BasicExampleFactory, UIExampleFactory } from "./examples";
-import { changeProgressWindowLine, showProgressWindow } from "./tools/progress";
+import { BasicExampleFactory, UIExampleFactory } from "./modules/examples";
+import {
+ changeProgressWindowLine,
+ showProgressWindow,
+} from "./modules/progressWindow";
import { config } from "../package.json";
+import { getString, initLocale } from "./modules/locale";
+import { registerPrefsScripts } from "./modules/preferenceScript";
-class AddonHooks {
- public async onStartup() {
- addon.locale.initLocale();
+async function onStartup() {
+ initLocale();
- const w = showProgressWindow(
- config.addonName,
- addon.locale.getString("startup.begin"),
- "default",
- -1
- );
- changeProgressWindowLine(w, { newProgress: 0 });
+ const w = showProgressWindow(
+ config.addonName,
+ getString("startup.begin"),
+ "default",
+ -1
+ );
+ changeProgressWindowLine(w, { newProgress: 0 });
- BasicExampleFactory.registerPrefs();
+ BasicExampleFactory.registerPrefs();
- BasicExampleFactory.registerNotifier();
+ BasicExampleFactory.registerNotifier();
- await Zotero.Promise.delay(1000);
- changeProgressWindowLine(w, {
- newProgress: 30,
- newText: `[30%] ${addon.locale.getString("startup.begin")}`,
- });
+ await Zotero.Promise.delay(1000);
+ changeProgressWindowLine(w, {
+ newProgress: 30,
+ newText: `[30%] ${getString("startup.begin")}`,
+ });
- UIExampleFactory.registerStyleSheet();
+ UIExampleFactory.registerStyleSheet();
- UIExampleFactory.registerRightClickMenuItem();
+ UIExampleFactory.registerRightClickMenuItem();
- UIExampleFactory.registerRightClickMenuPopup();
+ UIExampleFactory.registerRightClickMenuPopup();
- UIExampleFactory.registerWindowMenuWithSeprator();
+ UIExampleFactory.registerWindowMenuWithSeprator();
- await UIExampleFactory.registerExtraColumn();
+ await UIExampleFactory.registerExtraColumn();
- await UIExampleFactory.registerExtraColumnWithCustomCell();
+ await UIExampleFactory.registerExtraColumnWithCustomCell();
- await UIExampleFactory.registerCustomCellRenderer();
+ await UIExampleFactory.registerCustomCellRenderer();
- UIExampleFactory.registerLibraryTabPanel();
+ UIExampleFactory.registerLibraryTabPanel();
- await UIExampleFactory.registerReaderTabPanel();
+ await UIExampleFactory.registerReaderTabPanel();
- await Zotero.Promise.delay(1000);
- changeProgressWindowLine(w, {
- newProgress: 100,
- newText: `[100%] ${addon.locale.getString("startup.finish")}`,
- });
- w.startCloseTimer(5000);
- }
+ await Zotero.Promise.delay(1000);
+ changeProgressWindowLine(w, {
+ newProgress: 100,
+ newText: `[100%] ${getString("startup.finish")}`,
+ });
+ w.startCloseTimer(5000);
+}
- public onShutdown(): void {
- BasicExampleFactory.unregisterPrefs();
- UIExampleFactory.unregisterUIExamples();
- // Remove addon object
- addon.alive = false;
- delete Zotero.AddonTemplate;
+function onShutdown(): void {
+ BasicExampleFactory.unregisterPrefs();
+ UIExampleFactory.unregisterUIExamples();
+ // Remove addon object
+ addon.data.alive = false;
+ delete Zotero.AddonTemplate;
+}
+
+/**
+ * This function is just a dispatcher for UI events.
+ * Any operations should be placed in a function to keep this funcion clear
+ * @param type event type
+ * @param data event data
+ */
+function onCustomEvent(type: string, data: { [key: string]: any }) {
+ switch (type) {
+ case "prefLoad":
+ registerPrefsScripts(data.window);
+ break;
+ default:
+ return;
}
}
-export default AddonHooks;
+export default {
+ onStartup,
+ onShutdown,
+ onCustomEvent,
+};
diff --git a/src/index.ts b/src/index.ts
index 16ed824..8a8dc19 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -12,10 +12,9 @@ if (!getGlobal("Zotero").AddonTemplate) {
_globalThis.document = getGlobal("document");
_globalThis.ztoolkit = new ZoteroToolkit();
_globalThis.addon = new Addon();
- // The env will be replaced after esbuild
- addon.env = __env__;
ztoolkit.Tool.logOptionsGlobal.prefix = `[${config.addonName}]`;
- ztoolkit.Tool.logOptionsGlobal.disableConsole = addon.env === "production";
+ ztoolkit.Tool.logOptionsGlobal.disableConsole =
+ addon.data.env === "production";
Zotero.AddonTemplate = addon;
// Trigger addon hook for initialization
addon.hooks.onStartup();
diff --git a/src/locale.ts b/src/locale.ts
deleted file mode 100644
index bc2ee83..0000000
--- a/src/locale.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { config } from "../package.json";
-
-class AddonLocale {
- private stringBundle: any;
-
- public initLocale() {
- this.stringBundle = Components.classes["@mozilla.org/intl/stringbundle;1"]
- .getService(Components.interfaces.nsIStringBundleService)
- .createBundle(`chrome://${config.addonRef}/locale/addon.properties`);
- }
-
- public getString(localString: string): string {
- return this.stringBundle.GetStringFromName(localString);
- }
-}
-
-export default AddonLocale;
diff --git a/src/examples.ts b/src/modules/examples.ts
similarity index 87%
rename from src/examples.ts
rename to src/modules/examples.ts
index 434fb9f..0dd8a2b 100644
--- a/src/examples.ts
+++ b/src/modules/examples.ts
@@ -1,19 +1,31 @@
-import { log } from "zotero-plugin-toolkit/dist/utils";
-import { config } from "../package.json";
+import { config } from "../../package.json";
+import { getString } from "./locale";
-export function example(type?: string): MethodDecorator {
- return function (
- target: Object,
- propertyKey: string | symbol,
- descriptor: PropertyDescriptor
- ) {
- log("Calling example", target, type, propertyKey, descriptor);
- return descriptor;
+function example(
+ target: any,
+ propertyKey: string | symbol,
+ descriptor: PropertyDescriptor
+) {
+ const original = descriptor.value;
+ descriptor.value = function (...args: any) {
+ try {
+ ztoolkit.Tool.log(
+ `Calling example ${target.name}.${String(propertyKey)}`
+ );
+ return original.apply(this, args);
+ } catch (e) {
+ ztoolkit.Tool.log(
+ `Error in example ${target.name}.${String(propertyKey)}`,
+ e
+ );
+ throw e;
+ }
};
+ return descriptor;
}
export class BasicExampleFactory {
- @example()
+ @example
static registerNotifier() {
const callback = {
notify: async (
@@ -22,7 +34,7 @@ export class BasicExampleFactory {
ids: Array,
extraData: { [key: string]: any }
) => {
- if (!addon.alive) {
+ if (!addon.data.alive) {
this.unregisterNotifier(notifierID);
return;
}
@@ -58,23 +70,20 @@ export class BasicExampleFactory {
);
}
- @example()
+ @example
private static unregisterNotifier(notifierID: string) {
Zotero.Notifier.unregisterObserver(notifierID);
}
- @example()
+ @example
static registerPrefs() {
const prefOptions = {
pluginID: config.addonID,
src: rootURI + "chrome/content/preferences.xhtml",
- label: addon.locale.getString("prefs.title"),
+ label: getString("prefs.title"),
image: `chrome://${config.addonRef}/content/icons/favicon.png`,
extraDTD: [`chrome://${config.addonRef}/locale/overlay.dtd`],
defaultXUL: true,
- onload: (win: Window) => {
- addon.prefs.initPreferences(win);
- },
};
if (ztoolkit.Compat.isZotero7()) {
Zotero.PreferencePanes.register(prefOptions);
@@ -83,7 +92,7 @@ export class BasicExampleFactory {
}
}
- @example()
+ @example
static unregisterPrefs() {
if (!ztoolkit.Compat.isZotero7()) {
ztoolkit.Compat.unregisterPrefPane();
@@ -92,7 +101,7 @@ export class BasicExampleFactory {
}
export class UIExampleFactory {
- @example()
+ @example
static registerStyleSheet() {
const styles = ztoolkit.UI.creatElementsFromJSON(document, {
tag: "link",
@@ -108,30 +117,30 @@ export class UIExampleFactory {
?.classList.add("makeItRed");
}
- @example()
+ @example
static registerRightClickMenuItem() {
const menuIcon = `chrome://${config.addonRef}/content/icons/favicon@0.5x.png`;
// item menuitem with icon
ztoolkit.UI.insertMenuItem("item", {
tag: "menuitem",
id: "zotero-itemmenu-addontemplate-test",
- label: addon.locale.getString("menuitem.label"),
+ label: getString("menuitem.label"),
oncommand: "alert('Hello World! Default Menuitem.')",
icon: menuIcon,
});
}
- @example()
+ @example
static registerRightClickMenuPopup() {
ztoolkit.UI.insertMenuItem(
"item",
{
tag: "menu",
- label: addon.locale.getString("menupopup.label"),
+ label: getString("menupopup.label"),
subElementOptions: [
{
tag: "menuitem",
- label: addon.locale.getString("menuitem.submenulabel"),
+ label: getString("menuitem.submenulabel"),
oncommand: "alert('Hello World! Sub Menuitem.')",
},
],
@@ -143,7 +152,7 @@ export class UIExampleFactory {
);
}
- @example()
+ @example
static registerWindowMenuWithSeprator() {
ztoolkit.UI.insertMenuItem("menuFile", {
tag: "menuseparator",
@@ -151,12 +160,12 @@ export class UIExampleFactory {
// menu->File menuitem
ztoolkit.UI.insertMenuItem("menuFile", {
tag: "menuitem",
- label: addon.locale.getString("menuitem.filemenulabel"),
+ label: getString("menuitem.filemenulabel"),
oncommand: "alert('Hello World! File Menuitem.')",
});
}
- @example()
+ @example
static async registerExtraColumn() {
await ztoolkit.ItemTree.register(
"test1",
@@ -175,7 +184,7 @@ export class UIExampleFactory {
);
}
- @example()
+ @example
static async registerExtraColumnWithCustomCell() {
await ztoolkit.ItemTree.register(
"test2",
@@ -202,7 +211,7 @@ export class UIExampleFactory {
);
}
- @example()
+ @example
static async registerCustomCellRenderer() {
await ztoolkit.ItemTree.addRenderCellHook(
"title",
@@ -218,10 +227,10 @@ export class UIExampleFactory {
await ztoolkit.ItemTree.refresh();
}
- @example()
+ @example
static registerLibraryTabPanel() {
const tabId = ztoolkit.UI.registerLibraryTabPanel(
- addon.locale.getString("tabpanel.lib.tab.label"),
+ getString("tabpanel.lib.tab.label"),
(panel: XUL.Element, win: Window) => {
const elem = ztoolkit.UI.creatElementsFromJSON(win.document, {
tag: "vbox",
@@ -266,10 +275,10 @@ export class UIExampleFactory {
);
}
- @example()
+ @example
static async registerReaderTabPanel() {
const tabId = await ztoolkit.UI.registerReaderTabPanel(
- addon.locale.getString("tabpanel.reader.tab.label"),
+ getString("tabpanel.reader.tab.label"),
(
panel: XUL.TabPanel | undefined,
deck: XUL.Deck,
@@ -344,8 +353,11 @@ export class UIExampleFactory {
);
}
- @example()
+ @example
static unregisterUIExamples() {
ztoolkit.unregisterAll();
}
}
+function initPreferences(win: Window) {
+ throw new Error("Function not implemented.");
+}
diff --git a/src/modules/locale.ts b/src/modules/locale.ts
new file mode 100644
index 0000000..88fd966
--- /dev/null
+++ b/src/modules/locale.ts
@@ -0,0 +1,13 @@
+import { config } from "../../package.json";
+
+export function initLocale() {
+ addon.data.locale = {
+ stringBundle: Components.classes["@mozilla.org/intl/stringbundle;1"]
+ .getService(Components.interfaces.nsIStringBundleService)
+ .createBundle(`chrome://${config.addonRef}/locale/addon.properties`),
+ };
+}
+
+export function getString(localString: string): string {
+ return addon.data.locale?.stringBundle.GetStringFromName(localString);
+}
diff --git a/src/modules/preferenceScript.ts b/src/modules/preferenceScript.ts
new file mode 100644
index 0000000..98526a4
--- /dev/null
+++ b/src/modules/preferenceScript.ts
@@ -0,0 +1,41 @@
+import { config } from "../../package.json";
+
+export function registerPrefsScripts(_window: Window) {
+ // This function is called when the prefs window is opened
+ // See addon/chrome/content/preferences.xul onpaneload
+ addon.data.prefs = {
+ window: _window,
+ };
+ updatePrefsUI();
+ bindPrefEvents();
+}
+
+function updatePrefsUI() {
+ // You can initialize some UI elements on prefs window
+ // with addon.data.prefs.window.document
+ // Or bind some events to the elements
+}
+
+function bindPrefEvents() {
+ addon.data
+ .prefs!.window.document.querySelector(
+ `#zotero-prefpane-${config.addonRef}-enable`
+ )
+ ?.addEventListener("command", (e) => {
+ ztoolkit.Tool.log(e);
+ addon.data.prefs!.window.alert(
+ `Successfully changed to ${(e.target as XUL.Checkbox).checked}!`
+ );
+ });
+
+ addon.data
+ .prefs!!.window.document.querySelector(
+ `#zotero-prefpane-${config.addonRef}-input`
+ )
+ ?.addEventListener("change", (e) => {
+ ztoolkit.Tool.log(e);
+ addon.data.prefs!.window.alert(
+ `Successfully changed to ${(e.target as HTMLInputElement).value}!`
+ );
+ });
+}
diff --git a/src/tools/progress.ts b/src/modules/progressWindow.ts
similarity index 100%
rename from src/tools/progress.ts
rename to src/modules/progressWindow.ts
diff --git a/src/prefs.ts b/src/prefs.ts
deleted file mode 100644
index e954a03..0000000
--- a/src/prefs.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { config } from "../package.json";
-
-class AddonPrefs {
- private _window!: Window;
-
- public initPreferences(_window: Window) {
- // This function is called when the prefs window is opened
- // See addon/chrome/content/preferences.xul onpaneload
- this._window = _window;
- this.updatePrefsUI();
- this.bindPrefEvents();
- }
-
- private updatePrefsUI() {
- // You can initialize some UI elements on prefs window
- // with this._window.document
- // Or bind some events to the elements
- }
-
- private bindPrefEvents() {
- this._window.document
- .querySelector(`#zotero-prefpane-${config.addonRef}-enable`)
- ?.addEventListener("command", (e) => {
- ztoolkit.Tool.log(e);
- this._window.alert(
- `Successfully changed to ${(e.target as XUL.Checkbox).checked}!`
- );
- });
-
- this._window.document
- .querySelector(`#zotero-prefpane-${config.addonRef}-input`)
- ?.addEventListener("change", (e) => {
- ztoolkit.Tool.log(e);
- this._window.alert(
- `Successfully changed to ${(e.target as HTMLInputElement).value}!`
- );
- });
- }
-}
-
-export default AddonPrefs;