init: bootstrap extension mode

This commit is contained in:
xiangyu 2022-09-13 12:37:09 +08:00
parent 9976511dc9
commit cfd3014559
14 changed files with 144 additions and 47 deletions

View File

@ -2,6 +2,12 @@
This is an addon/plugin template for [Zotero](https://www.zotero.org/). This is an addon/plugin template for [Zotero](https://www.zotero.org/).
[Documentation](https://zotero.yuque.com/books/share/8d230829-6004-4934-b4c6-685a7001bfa0/vec88d)(Chinese, provides English translation)
> 👍You are currently in `bootstrap` extension mode. To use `overlay` mode, plsase switch to `overlay` branch in git.
> ⚠️`overlay` mode will no longer be supported in the coming Zotero 7. Please use the `bootstrap` extension mode instead. See discussion here: https://groups.google.com/g/zotero-dev/c/TT_rcLVpQwg
## Features ## Features
- TypeScript support; - TypeScript support;
@ -49,7 +55,7 @@ This section shows the directory structure of a template.
```shell ```shell
│ .gitignore │ .gitignore
│ .release-it.json # release-it conf │ .release-it.json # release-it conf
| jsconfig.json # https://code.visualstudio.com/docs/languages/jsconfig# | tsconfig.json # https://code.visualstudio.com/docs/languages/jsconfig#
│ build.js # esbuild │ build.js # esbuild
│ LICENSE │ LICENSE
│ package.json # npm conf │ package.json # npm conf
@ -61,10 +67,10 @@ This section shows the directory structure of a template.
├─addon # addon dir ├─addon # addon dir
│ │ chrome.manifest #addon conf │ │ chrome.manifest #addon conf
│ │ install.rdf # addon install conf │ │ install.rdf # addon install conf
│ │ bootstrap.js # addon load/unload script, like a main.c
│ │ │ │
│ └─chrome │ └─chrome
│ ├─content # UI │ ├─content # UI
│ │ │ overlay.xul
│ │ │ preferences.xul │ │ │ preferences.xul
│ │ │ │ │ │
│ │ └─scripts │ │ └─scripts
@ -152,6 +158,8 @@ You can also debug code in these ways:
Zotero docs are outdated or incomplete. Searching the source code of Zotero is unavoidable. Zotero docs are outdated or incomplete. Searching the source code of Zotero is unavoidable.
Clone https://github.com/zotero/zotero and search the keyword globally. You can search the UI text in `.xul`/`.dtd` files, and then search the keys of the text value in `.js`/`.xul` files. Clone https://github.com/zotero/zotero and search the keyword globally. You can search the UI text in `.xul`/`.dtd` files, and then search the keys of the text value in `.js`/`.xul` files.
> ⭐The [zotero-types](https://github.com/windingwind/zotero-types) provides most frequently used Zotero APIs. It's included in this template by default.
## Disclaimer ## Disclaimer
Use this code under AGPL. No warranties are provided. Keep the laws of your locality in mind! Use this code under AGPL. No warranties are provided. Keep the laws of your locality in mind!

71
addon/bootstrap.js vendored Normal file
View File

@ -0,0 +1,71 @@
/* Copyright 2012 Will Shanks.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* global Components, Services */
/* global addon, APP_SHUTDOWN */
const { classes: Cc, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
function install(data, reason) {}
function startup(data, reason) {
// Load the addon to Zotero if window is ready
const loadAddon = (window) => {
console.log(window);
if (window.document.readyState === "complete" && window.Zotero) {
Services.scriptloader.loadSubScript(
"chrome://__addonRef__/content/scripts/index.js"
);
} else {
window.addEventListener("load", (e) => {
if (window.Zotero) {
Services.scriptloader.loadSubScript(
"chrome://__addonRef__/content/scripts/index.js"
);
}
});
}
};
// Listen to windows
var WindowListener = {
onOpenWindow: function (xulWindow) {
loadAddon(
xulWindow
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow)
);
},
};
Services.wm.addListener(WindowListener);
// Scan current windows
const windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
loadAddon(
windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow)
);
}
}
function shutdown(data, reason) {
if (reason === APP_SHUTDOWN) {
return;
}
var _Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
Components.interfaces.nsISupports
).wrappedJSObject;
_Zotero.AddonTemplate.events.onUnInit(_Zotero);
Cc["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService)
.flushBundles();
Cu.unload("chrome://_addonRef__/scripts/index.js");
}
function uninstall(data, reason) {}

View File

@ -2,6 +2,3 @@ content __addonRef__ chrome/content/
skin __addonRef__ default chrome/skin/default/__addonRef__/ skin __addonRef__ default chrome/skin/default/__addonRef__/
locale __addonRef__ en-US chrome/locale/en-US/ locale __addonRef__ en-US chrome/locale/en-US/
locale __addonRef__ zh-CN chrome/locale/zh-CN/ locale __addonRef__ zh-CN chrome/locale/zh-CN/
overlay chrome://zotero/content/zoteroPane.xul chrome://__addonRef__/content/overlay.xul
overlay chrome://zotero/content/preferences/preferences.xul chrome://__addonRef__/content/preferences.xul

View File

@ -1,11 +0,0 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://__addonRef__/skin/overlay.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://__addonRef__/locale/overlay.dtd">
<overlay id="__addonRef__" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="chrome://__addonRef__/content/scripts/index.js" />
<popup id="zotero-itemmenu">
<menuseparator />
<menuitem id="zotero-itemmenu-__addonRef__-test" label="&zotero.__addonRef__.itemmenu.test.label;" oncommand="alert('Hello World!')" class="menuitem-iconic" style="list-style-image: url('chrome://__addonRef__/skin/favicon@0.5x.png');" />
</popup>
</overlay>

View File

@ -1,14 +1,15 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!DOCTYPE window SYSTEM "chrome://__addonRef__/locale/overlay.dtd"> <!DOCTYPE prefwindow SYSTEM "chrome://__addonRef__/locale/overlay.dtd">
<overlay id="__addonRef__-preferences" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"> <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://zotero-platform/content/preferences.css"?>
<prefwindow id="zotero-prefs"> <prefwindow id="__addonRef__-prefs" title="&zotero.__addonRef__.pref.title;" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<prefpane id="zotero-prefpane-__addonRef__" insertafter="zotero-prefpane-advanced" label="__addonName__" image="chrome://__addonRef__/skin/favicon.png" onpaneload="Zotero.AddonTemplate.prefs.initPreferences(window)"> <script src="chrome://zotero/content/include.js" />
<preferences id="zotero-preferences-__addonRef__"> <prefpane id=" zotero-prefpane-__addonRef__" insertafter=" zotero-prefpane-advanced" label=" __addonName__" image=" chrome: __addonRef__ skin favicon.png" onpaneload=" Zotero.AddonTemplate.prefs.initPreferences(window)">
<preference id="pref-__addonRef__-enable" name="extensions.zotero.__addonRef__.enable" type="bool" /> <preferences id=" zotero-preferences-__addonRef__">
</preferences> <preference id=" pref-__addonRef__-enable" name=" extensions.zotero.__addonRef__.enable" type=" bool" />
<checkbox id="zotero-prefpane-__addonRef__-enable" preference="pref-__addonRef__-enable" label="&zotero.__addonRef__.pref.enable.label;" /> </preferences>
</prefpane> <checkbox id=" zotero-prefpane-__addonRef__-enable" preference=" pref-__addonRef__-enable" label=" &zotero.__addonRef__.pref.enable.label;" />
</prefwindow> </prefpane>
</overlay> </prefwindow>

View File

Before

Width:  |  Height:  |  Size: 677 B

After

Width:  |  Height:  |  Size: 677 B

View File

Before

Width:  |  Height:  |  Size: 836 B

After

Width:  |  Height:  |  Size: 836 B

View File

@ -13,7 +13,10 @@
em:description="__description__" em:description="__description__"
em:homepageURL="__homepage__" em:homepageURL="__homepage__"
em:iconURL="chrome://__addonRef__/skin/favicon.png" em:iconURL="chrome://__addonRef__/skin/favicon.png"
em:updateURL="__updaterdf__">> em:optionsURL="chrome://__addonRef__/content/preferences.xul"
em:updateURL="__updaterdf__"
em:multiprocessCompatible="true"
em:bootstrap="true">>
<em:type>2</em:type> <em:type>2</em:type>
<em:targetApplication RDF:resource="rdf:#$x61SL3"/> <em:targetApplication RDF:resource="rdf:#$x61SL3"/>
<em:targetApplication> <em:targetApplication>

View File

@ -115,6 +115,7 @@ async function main() {
path.join(buildDir, "**/*.xul"), path.join(buildDir, "**/*.xul"),
path.join(buildDir, "**/*.manifest"), path.join(buildDir, "**/*.manifest"),
path.join(buildDir, "addon/defaults", "**/*.js"), path.join(buildDir, "addon/defaults", "**/*.js"),
path.join(buildDir, "addon/bootstrap.js"),
"update.rdf", "update.rdf",
], ],
from: [ from: [

View File

@ -34,6 +34,6 @@
}, },
"devDependencies": { "devDependencies": {
"release-it": "^14.14.0", "release-it": "^14.14.0",
"zotero-types": "^0.0.3" "zotero-types": "^0.0.4"
} }
} }

View File

@ -27,30 +27,32 @@ class AddonEvents extends AddonModule {
}; };
} }
public async onInit() { public async onInit(_Zotero) {
// This function is the setup code of the addon // This function is the setup code of the addon
Zotero.debug(`${addonName}: init called`); console.log(`${addonName}: init called`);
_Zotero.debug(`${addonName}: init called`);
// alert(112233);
// Reset prefs // Reset prefs
this.resetState(); this.resetState();
// Register the callback in Zotero as an item observer // Register the callback in Zotero as an item observer
let notifierID = Zotero.Notifier.registerObserver(this.notifierCallback, [ let notifierID = _Zotero.Notifier.registerObserver(this.notifierCallback, [
"tab", "tab",
"item", "item",
"file", "file",
]); ]);
// Unregister callback when the window closes (important to avoid a memory leak) // Unregister callback when the window closes (important to avoid a memory leak)
window.addEventListener( _Zotero.getMainWindow().addEventListener(
"unload", "unload",
function (e) { function (e) {
Zotero.Notifier.unregisterObserver(notifierID); _Zotero.Notifier.unregisterObserver(notifierID);
}, },
false false
); );
this._Addon.views.initViews(); this._Addon.views.initViews(_Zotero);
} }
private resetState(): void { private resetState(): void {
@ -65,6 +67,15 @@ class AddonEvents extends AddonModule {
// Zotero.Prefs.set("addonTemplate.testPref", true); // Zotero.Prefs.set("addonTemplate.testPref", true);
// } // }
} }
public onUnInit(_Zotero): void {
console.log(`${addonName}: uninit called`);
_Zotero.debug(`${addonName}: uninit called`);
// Remove elements and do clean up
this._Addon.views.unInitViews(_Zotero);
// Remove addon object
_Zotero.AddonTemplate = undefined;
}
} }
export default AddonEvents; export default AddonEvents;

View File

@ -1,11 +1,9 @@
import { Addon } from "./addon"; import { Addon } from "./addon";
Zotero.AddonTemplate = new Addon(); var _Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
Components.interfaces.nsISupports
window.addEventListener( ).wrappedJSObject;
"load", if (!_Zotero.AddonTemplate) {
async function (e) { _Zotero.AddonTemplate = new Addon();
Zotero.AddonTemplate.events.onInit(); _Zotero.AddonTemplate.events.onInit(_Zotero);
}, }
false
);

View File

@ -16,9 +16,27 @@ class AddonViews extends AddonModule {
}; };
} }
public initViews() { public initViews(_Zotero) {
// You can init the UI elements that // You can init the UI elements that
// cannot be initialized with overlay.xul // cannot be initialized with overlay.xul
console.log("Initializing UI");
const _window: Window = _Zotero.getMainWindow();
const menuitem = _window.document.createElement("menuitem");
menuitem.id = "zotero-itemmenu-addontemplate-test";
menuitem.setAttribute("label", "Addon Template");
menuitem.setAttribute("oncommand", "alert('Hello World!')");
menuitem.className = "menuitem-iconic";
menuitem.style["list-style-image"] =
"url('chrome://addontemplate/skin/favicon@0.5x.png')";
_window.document.querySelector("#zotero-itemmenu").appendChild(menuitem);
}
public unInitViews(_Zotero) {
console.log("Uninitializing UI");
const _window: Window = _Zotero.getMainWindow();
_window.document
.querySelector("#zotero-itemmenu-addontemplate-test")
?.remove();
} }
public showProgressWindow( public showProgressWindow(

View File

@ -11,7 +11,7 @@
<em:id>zotero@chnm.gmu.edu</em:id> <em:id>zotero@chnm.gmu.edu</em:id>
<em:minVersion>5.0</em:minVersion> <em:minVersion>5.0</em:minVersion>
<em:maxVersion>*</em:maxVersion> <em:maxVersion>*</em:maxVersion>
<em:updateLink>__releasepage__</em:updateLink> <em:updateLink>https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi</em:updateLink>
</rdf:Description> </rdf:Description>
</em:targetApplication> </em:targetApplication>
<em:targetApplication> <em:targetApplication>
@ -19,7 +19,7 @@
<em:id>juris-m@juris-m.github.io</em:id> <em:id>juris-m@juris-m.github.io</em:id>
<em:minVersion>5.0</em:minVersion> <em:minVersion>5.0</em:minVersion>
<em:maxVersion>*</em:maxVersion> <em:maxVersion>*</em:maxVersion>
<em:updateLink>__releasepage__</em:updateLink> <em:updateLink>https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi</em:updateLink>
</rdf:Description> </rdf:Description>
</em:targetApplication> </em:targetApplication>
</rdf:Description> </rdf:Description>