refactor: make prefs & locale to modules
This commit is contained in:
parent
4469cbc82d
commit
626cff548b
13
README.md
13
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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<vbox
|
||||
id="zotero-prefpane-__addonRef__"
|
||||
onload="Zotero.AddonTemplate.prefs.initPreferences(window)"
|
||||
onload="Zotero.AddonTemplate.hooks.onCustomEvent('prefLoad', {window})"
|
||||
>
|
||||
<groupbox>
|
||||
<label><html:h2>&zotero.__addonRef__.pref.title;</html:h2></label>
|
||||
|
|
@ -22,5 +22,7 @@
|
|||
</groupbox>
|
||||
</vbox>
|
||||
<vbox>
|
||||
<label value="&zotero.__addonRef__.help.version.label; &zotero.__addonRef__.help.releasetime.label;"></label>
|
||||
<label
|
||||
value="&zotero.__addonRef__.help.version.label; &zotero.__addonRef__.help.releasetime.label;"
|
||||
></label>
|
||||
</vbox>
|
||||
|
|
|
|||
36
src/addon.ts
36
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
107
src/hooks.ts
107
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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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<string>,
|
||||
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.");
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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}!`
|
||||
);
|
||||
});
|
||||
}
|
||||
41
src/prefs.ts
41
src/prefs.ts
|
|
@ -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;
|
||||
Loading…
Reference in New Issue