From 6426ae519876b06fc5ac3a409f84bb830a4a9447 Mon Sep 17 00:00:00 2001 From: xiangyu <3170102889@zju.edu.cn> Date: Thu, 5 Jan 2023 15:04:09 +0800 Subject: [PATCH] add: _globalThis --- README.md | 17 +++- addon/bootstrap.js | 6 +- src/addon.ts | 9 -- src/events.ts | 32 ++++--- src/index.ts | 10 --- src/prefs.ts | 8 +- src/views.ts | 214 +++++++++++++++++++++------------------------ typing/global.d.ts | 13 +++ 8 files changed, 156 insertions(+), 153 deletions(-) diff --git a/README.md b/README.md index 3f0fa0b..7d283c5 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,19 @@ This is an addon/plugin template for [Zotero](https://www.zotero.org/). - `events.ts` > `onUninit` is called. Remove UI elements(`unInitViews`), preference panes(`uninitPrefs`), or anything created by the plugin. - Remove scripts and release resources. +### About Global Variables + +The bootstrapped plugin runs in a sandbox, which does not have default global variables like `Zotero` or `window`, which we used to have in the overlay plugins' window environment. + +This template registers the following variables to the global scope: + +```ts +Zotero, ZoteroPane, Zotero_Tabs, window, document, rootURI, ZToolkit +``` + +See `src/events.ts` > `initGlobalVariables` for more details. + + ### Examples See https://github.com/windingwind/zotero-plugin-toolkit for more detailed API documentations. @@ -134,7 +147,7 @@ There are more advanced APIs for creating elements in batch: `creatElementsFromJ Using [Zotero Plugin Toolkit:ItemTreeTool](https://github.com/windingwind/zotero-plugin-toolkit/blob/HEAD/docs/zotero-plugin-toolkit.itemtreetool.md) to register an extra column in `src/views.ts`. ```ts -this._Addon.toolkit.ItemTree.registerExample(); +ZToolkit.ItemTree.registerExample(); ``` This will register a column with dataKey `test`. Looks like: @@ -143,7 +156,7 @@ This will register a column with dataKey `test`. Looks like: Remember to unregister it when exiting. ```ts -this._Addon.toolkit.ItemTree.unregister("test"); +ZToolkit.ItemTree.unregister("test"); ``` ### Directory Structure diff --git a/addon/bootstrap.js b/addon/bootstrap.js index 6efc004..03f9488 100644 --- a/addon/bootstrap.js +++ b/addon/bootstrap.js @@ -81,15 +81,11 @@ async function startup({ id, version, resourceURI, rootURI }, reason) { rootURI = resourceURI.spec; } - const window = Zotero.getMainWindow(); // Global variables for plugin code const ctx = { - Zotero, rootURI, - window, - document: window.document, - ZoteroPane: Zotero.getActiveZoteroPane(), }; + ctx._globalThis = ctx; Services.scriptloader.loadSubScript( `${rootURI}/chrome/content/scripts/index.js`, diff --git a/src/addon.ts b/src/addon.ts index 23ece8b..fc4f719 100644 --- a/src/addon.ts +++ b/src/addon.ts @@ -3,13 +3,7 @@ import AddonPrefs from "./prefs"; import AddonViews from "./views"; import AddonLocale from "./locale"; -import ZoteroToolkit from "zotero-plugin-toolkit"; - class Addon { - // A global Zotero instance - public Zotero!: _ZoteroConstructable; - // Root path to access the resources - public rootURI!: string; // Env type, see build.js public env!: "development" | "production"; // Lifecycle events @@ -20,15 +14,12 @@ class Addon { public prefs: AddonPrefs; // Runtime locale with .properties public locale: AddonLocale; - // A toolkit instance. See zotero-plugin-toolkit - public toolkit: ZoteroToolkit; constructor() { this.events = new AddonEvents(this); this.views = new AddonViews(this); this.prefs = new AddonPrefs(this); this.locale = new AddonLocale(this); - this.toolkit = new ZoteroToolkit(); } } diff --git a/src/events.ts b/src/events.ts index ff5345b..245da07 100644 --- a/src/events.ts +++ b/src/events.ts @@ -1,6 +1,7 @@ import Addon from "./addon"; import AddonModule from "./module"; import { config } from "../package.json"; +import ZoteroToolkit from "zotero-plugin-toolkit"; class AddonEvents extends AddonModule { constructor(parent: Addon) { @@ -9,18 +10,16 @@ class AddonEvents extends AddonModule { // This function is the setup code of the addon public async onInit() { - this._Addon.Zotero = Zotero; + this.initGlobalVariables(); // @ts-ignore - this._Addon.rootURI = rootURI; const development = "development"; const production = "production"; // The env will be replaced after esbuild // @ts-ignore this._Addon.env = __env__; - this._Addon.toolkit.Tool.logOptionsGlobal.prefix = `[${config.addonName}]`; - this._Addon.toolkit.Tool.logOptionsGlobal.disableConsole = + ZToolkit.Tool.logOptionsGlobal.disableConsole = this._Addon.env === "production"; - this._Addon.toolkit.Tool.log("init called"); + ZToolkit.Tool.log("init called"); // Initialize locale provider this._Addon.locale.initLocale(); @@ -33,7 +32,7 @@ class AddonEvents extends AddonModule { } public onUnInit(): void { - this._Addon.toolkit.Tool.log("uninit called"); + ZToolkit.Tool.log("uninit called"); this.unInitPrefs(); // Remove elements and do clean up this._Addon.views.unInitViews(); @@ -41,6 +40,17 @@ class AddonEvents extends AddonModule { Zotero.AddonTemplate = undefined; } + private initGlobalVariables() { + _globalThis.ZToolkit = new ZoteroToolkit(); + ZToolkit.Tool.logOptionsGlobal.prefix = `[${config.addonName}]`; + _globalThis.Zotero = ZToolkit.Compat.getGlobal("Zotero"); + _globalThis.ZoteroPane = ZToolkit.Compat.getGlobal("ZoteroPane"); + _globalThis.Zotero_Tabs = ZToolkit.Compat.getGlobal("Zotero_Tabs"); + _globalThis.window = ZToolkit.Compat.getGlobal("window"); + _globalThis.document = ZToolkit.Compat.getGlobal("document"); + ZToolkit.Tool.log("initializeing global variables"); + } + private initNotifier() { const callback = { notify: async ( @@ -83,7 +93,7 @@ class AddonEvents extends AddonModule { private initPrefs() { const prefOptions = { pluginID: config.addonID, - src: this._Addon.rootURI + "chrome/content/preferences.xhtml", + src: rootURI + "chrome/content/preferences.xhtml", label: this._Addon.locale.getString("prefs.title"), image: `chrome://${config.addonRef}/content/icons/favicon.png`, extraDTD: [`chrome://${config.addonRef}/locale/overlay.dtd`], @@ -92,16 +102,16 @@ class AddonEvents extends AddonModule { this._Addon.prefs.initPreferences(win); }, }; - if (this._Addon.toolkit.Compat.isZotero7()) { + if (ZToolkit.Compat.isZotero7()) { Zotero.PreferencePanes.register(prefOptions); } else { - this._Addon.toolkit.Compat.registerPrefPane(prefOptions); + ZToolkit.Compat.registerPrefPane(prefOptions); } } private unInitPrefs() { - if (!this._Addon.toolkit.Compat.isZotero7()) { - this._Addon.toolkit.Compat.unregisterPrefPane(); + if (!ZToolkit.Compat.isZotero7()) { + ZToolkit.Compat.unregisterPrefPane(); } } } diff --git a/src/index.ts b/src/index.ts index 6c92462..9267ac4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,5 @@ import Addon from "./addon"; -/** - * Globals: bootstrap.js > ctx - * const ctx = { - Zotero, - rootURI, - window, - document: window.document, - ZoteroPane: Zotero.getActiveZoteroPane(), - }; - */ if (!Zotero.AddonTemplate) { Zotero.AddonTemplate = new Addon(); // @ts-ignore diff --git a/src/prefs.ts b/src/prefs.ts index b8be02e..9093792 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -11,7 +11,7 @@ class AddonPrefs extends AddonModule { // This function is called when the prefs window is opened // See addon/chrome/content/preferences.xul onpaneload this._window = _window; - this._Addon.toolkit.Tool.log("init preferences"); + ZToolkit.Tool.log("init preferences"); this.updatePrefsUI(); this.bindPrefEvents(); } @@ -20,14 +20,14 @@ class AddonPrefs extends AddonModule { // You can initialize some UI elements on prefs window // with this._window.document // Or bind some events to the elements - this._Addon.toolkit.Tool.log("init preferences UI"); + ZToolkit.Tool.log("init preferences UI"); } private bindPrefEvents() { this._window.document .querySelector(`#zotero-prefpane-${config.addonRef}-enable`) ?.addEventListener("command", (e) => { - this._Addon.toolkit.Tool.log(e); + ZToolkit.Tool.log(e); this._window.alert( `Successfully changed to ${(e.target as XUL.Checkbox).checked}!` ); @@ -36,7 +36,7 @@ class AddonPrefs extends AddonModule { this._window.document .querySelector(`#zotero-prefpane-${config.addonRef}-input`) ?.addEventListener("change", (e) => { - this._Addon.toolkit.Tool.log(e); + ZToolkit.Tool.log(e); this._window.alert( `Successfully changed to ${(e.target as HTMLInputElement).value}!` ); diff --git a/src/views.ts b/src/views.ts index 552dd57..f659611 100644 --- a/src/views.ts +++ b/src/views.ts @@ -18,10 +18,10 @@ class AddonViews extends AddonModule { public initViews() { // You can init the UI elements that // cannot be initialized with overlay.xul - this._Addon.toolkit.Tool.log("Initializing UI"); + ZToolkit.Tool.log("Initializing UI"); // register style sheet - const styles = this._Addon.toolkit.UI.creatElementsFromJSON(document, { + const styles = ZToolkit.UI.creatElementsFromJSON(document, { tag: "link", directAttributes: { type: "text/css", @@ -36,7 +36,7 @@ class AddonViews extends AddonModule { const menuIcon = `chrome://${config.addonRef}/content/icons/favicon@0.5x.png`; // item menuitem with icon - this._Addon.toolkit.UI.insertMenuItem("item", { + ZToolkit.UI.insertMenuItem("item", { tag: "menuitem", id: "zotero-itemmenu-addontemplate-test", label: this._Addon.locale.getString("menuitem.label"), @@ -44,7 +44,7 @@ class AddonViews extends AddonModule { icon: menuIcon, }); // item menupopup with sub-menuitems - this._Addon.toolkit.UI.insertMenuItem( + ZToolkit.UI.insertMenuItem( "item", { tag: "menu", @@ -58,15 +58,15 @@ class AddonViews extends AddonModule { ], }, "before", - this._Addon.Zotero.getMainWindow().document.querySelector( + document.querySelector( "#zotero-itemmenu-addontemplate-test" - ) + ) as XUL.MenuItem ); - this._Addon.toolkit.UI.insertMenuItem("menuFile", { + ZToolkit.UI.insertMenuItem("menuFile", { tag: "menuseparator", }); // menu->File menuitem - this._Addon.toolkit.UI.insertMenuItem("menuFile", { + ZToolkit.UI.insertMenuItem("menuFile", { tag: "menuitem", label: this._Addon.locale.getString("menuitem.filemenulabel"), oncommand: "alert('Hello World! File Menuitem.')", @@ -79,7 +79,7 @@ class AddonViews extends AddonModule { * Example: extra column starts */ // Initialize extra columns - this._Addon.toolkit.ItemTree.register( + ZToolkit.ItemTree.register( "test1", "text column", ( @@ -94,7 +94,7 @@ class AddonViews extends AddonModule { iconPath: "chrome://zotero/skin/cross.png", } ); - this._Addon.toolkit.ItemTree.register( + ZToolkit.ItemTree.register( "test2", "custom column", ( @@ -125,7 +125,7 @@ class AddonViews extends AddonModule { * Example: custom cell starts */ // Customize cells - this._Addon.toolkit.ItemTree.addRenderCellHook( + ZToolkit.ItemTree.addRenderCellHook( "title", (index: number, data: string, column: any, original: Function) => { const span = original(index, data, column) as HTMLSpanElement; @@ -141,49 +141,44 @@ class AddonViews extends AddonModule { /** * Example: extra library tab starts */ - const libTabId = this._Addon.toolkit.UI.registerLibraryTabPanel( + const libTabId = ZToolkit.UI.registerLibraryTabPanel( this._Addon.locale.getString("tabpanel.lib.tab.label"), (panel: XUL.Element, win: Window) => { - const elem = this._Addon.toolkit.UI.creatElementsFromJSON( - win.document, - { - tag: "vbox", - namespace: "xul", - subElementOptions: [ - { - tag: "h2", - namespace: "html", - directAttributes: { - innerText: "Hello World!", - }, + const elem = ZToolkit.UI.creatElementsFromJSON(win.document, { + tag: "vbox", + namespace: "xul", + subElementOptions: [ + { + tag: "h2", + namespace: "html", + directAttributes: { + innerText: "Hello World!", }, - { - tag: "div", - namespace: "html", - directAttributes: { - innerText: "This is a library tab.", - }, + }, + { + tag: "div", + namespace: "html", + directAttributes: { + innerText: "This is a library tab.", }, - { - tag: "button", - namespace: "html", - directAttributes: { - innerText: "Unregister", - }, - listeners: [ - { - type: "click", - listener: () => { - this._Addon.toolkit.UI.unregisterLibraryTabPanel( - libTabId - ); - }, + }, + { + tag: "button", + namespace: "html", + directAttributes: { + innerText: "Unregister", + }, + listeners: [ + { + type: "click", + listener: () => { + ZToolkit.UI.unregisterLibraryTabPanel(libTabId); }, - ], - }, - ], - } - ); + }, + ], + }, + ], + }); panel.append(elem); }, { @@ -198,7 +193,7 @@ class AddonViews extends AddonModule { * Example: extra reader tab starts */ const readerTabId = `${config.addonRef}-extra-reader-tab`; - this._Addon.toolkit.UI.registerReaderTabPanel( + ZToolkit.UI.registerReaderTabPanel( this._Addon.locale.getString("tabpanel.reader.tab.label"), ( panel: XUL.Element, @@ -207,70 +202,65 @@ class AddonViews extends AddonModule { reader: _ZoteroReaderInstance ) => { if (!panel) { - this._Addon.toolkit.Tool.log( + ZToolkit.Tool.log( "This reader do not have right-side bar. Adding reader tab skipped." ); return; } - this._Addon.toolkit.Tool.log(reader); - const elem = this._Addon.toolkit.UI.creatElementsFromJSON( - win.document, - { - tag: "vbox", - id: `${config.addonRef}-${reader._instanceID}-extra-reader-tab-div`, - namespace: "xul", - // This is important! Don't create content for multiple times - // ignoreIfExists: true, - removeIfExists: true, - subElementOptions: [ - { - tag: "h2", - namespace: "html", - directAttributes: { - innerText: "Hello World!", - }, + ZToolkit.Tool.log(reader); + const elem = ZToolkit.UI.creatElementsFromJSON(win.document, { + tag: "vbox", + id: `${config.addonRef}-${reader._instanceID}-extra-reader-tab-div`, + namespace: "xul", + // This is important! Don't create content for multiple times + // ignoreIfExists: true, + removeIfExists: true, + subElementOptions: [ + { + tag: "h2", + namespace: "html", + directAttributes: { + innerText: "Hello World!", }, - { - tag: "div", - namespace: "html", - directAttributes: { - innerText: "This is a reader tab.", - }, + }, + { + tag: "div", + namespace: "html", + directAttributes: { + innerText: "This is a reader tab.", }, - { - tag: "div", - namespace: "html", - directAttributes: { - innerText: `Reader: ${reader._title.slice(0, 20)}`, - }, + }, + { + tag: "div", + namespace: "html", + directAttributes: { + innerText: `Reader: ${reader._title.slice(0, 20)}`, }, - { - tag: "div", - namespace: "html", - directAttributes: { - innerText: `itemID: ${reader.itemID}.`, - }, + }, + { + tag: "div", + namespace: "html", + directAttributes: { + innerText: `itemID: ${reader.itemID}.`, }, - { - tag: "button", - namespace: "html", - directAttributes: { - innerText: "Unregister", - }, - listeners: [ - { - type: "click", - listener: () => { - this._Addon.toolkit.UI.unregisterReaderTabPanel( - readerTabId - ); - }, + }, + { + tag: "button", + namespace: "html", + directAttributes: { + innerText: "Unregister", + }, + listeners: [ + { + type: "click", + listener: () => { + ZToolkit.UI.unregisterReaderTabPanel(readerTabId); }, - ], - }, - ], - } - ); + }, + ], + }, + ], + }); panel.append(elem); }, { @@ -284,17 +274,17 @@ class AddonViews extends AddonModule { } public unInitViews() { - this._Addon.toolkit.Tool.log("Uninitializing UI"); - this._Addon.toolkit.unregisterAll(); - // this._Addon.toolkit.UI.removeAddonElements(); + ZToolkit.Tool.log("Uninitializing UI"); + ZToolkit.unregisterAll(); + // toolkit.UI.removeAddonElements(); // // Remove extra columns - // this._Addon.toolkit.ItemTree.unregister("test1"); - // this._Addon.toolkit.ItemTree.unregister("test2"); + // toolkit.ItemTree.unregister("test1"); + // toolkit.ItemTree.unregister("test2"); // // Remove title cell patch - // this._Addon.toolkit.ItemTree.removeRenderCellHook("title"); + // toolkit.ItemTree.removeRenderCellHook("title"); - // this._Addon.toolkit.UI.unregisterReaderTabPanel( + // toolkit.UI.unregisterReaderTabPanel( // `${config.addonRef}-extra-reader-tab` // ); } diff --git a/typing/global.d.ts b/typing/global.d.ts index e69de29..7453f61 100644 --- a/typing/global.d.ts +++ b/typing/global.d.ts @@ -0,0 +1,13 @@ +declare const _globalThis: { + [key: string]: any; + Zotero: _ZoteroConstructable; + ZoteroPane: _ZoteroPaneConstructable; + Zotero_Tabs: typeof Zotero_Tabs; + window: Window; + document: Document; + ZToolkit: typeof ZToolkit; +}; + +declare const ZToolkit: import("zotero-plugin-toolkit").ZoteroToolkit; + +declare const rootURI: string;