add: _globalThis

This commit is contained in:
xiangyu 2023-01-05 15:04:09 +08:00
parent 4f2e81e661
commit 6426ae5198
8 changed files with 156 additions and 153 deletions

View File

@ -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

6
addon/bootstrap.js vendored
View File

@ -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`,

View File

@ -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();
}
}

View File

@ -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();
}
}
}

View File

@ -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

View File

@ -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}!`
);

View File

@ -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`
// );
}

13
typing/global.d.ts vendored
View File

@ -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;