update: bootstrap for zotero 7
This commit is contained in:
parent
51bbc58590
commit
56ac4d5320
137
addon/bootstrap.js
vendored
137
addon/bootstrap.js
vendored
@ -3,69 +3,114 @@
|
||||
* 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;
|
||||
if (typeof Zotero == "undefined") {
|
||||
var Zotero;
|
||||
}
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
// In Zotero 6, bootstrap methods are called before Zotero is initialized, and using include.js
|
||||
// to get the Zotero XPCOM service would risk breaking Zotero startup. Instead, wait for the main
|
||||
// Zotero window to open and get the Zotero object from there.
|
||||
//
|
||||
// In Zotero 7, bootstrap methods are not called until Zotero is initialized, and the 'Zotero' is
|
||||
// automatically made available.
|
||||
async function waitForZotero() {
|
||||
if (typeof Zotero != "undefined") {
|
||||
await Zotero.initializationPromise;
|
||||
}
|
||||
|
||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
var windows = Services.wm.getEnumerator("navigator:browser");
|
||||
var found = false;
|
||||
while (windows.hasMoreElements()) {
|
||||
let win = windows.getNext();
|
||||
if (win.Zotero) {
|
||||
Zotero = win.Zotero;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
await new Promise((resolve) => {
|
||||
var listener = {
|
||||
onOpenWindow: function (aWindow) {
|
||||
// Wait for the window to finish loading
|
||||
let domWindow = aWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
|
||||
domWindow.addEventListener(
|
||||
"load",
|
||||
function () {
|
||||
domWindow.removeEventListener("load", arguments.callee, false);
|
||||
if (domWindow.Zotero) {
|
||||
Services.wm.removeListener(listener);
|
||||
Zotero = domWindow.Zotero;
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
},
|
||||
};
|
||||
Services.wm.addListener(listener);
|
||||
});
|
||||
}
|
||||
await Zotero.initializationPromise;
|
||||
}
|
||||
|
||||
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"
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
async function startup({ id, version, resourceURI, rootURI }, reason) {
|
||||
await waitForZotero();
|
||||
|
||||
// Listen to windows
|
||||
var WindowListener = {
|
||||
onOpenWindow: function (xulWindow) {
|
||||
loadAddon(
|
||||
xulWindow
|
||||
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindow)
|
||||
);
|
||||
},
|
||||
};
|
||||
Services.wm.addListener(WindowListener);
|
||||
// String 'rootURI' introduced in Zotero 7
|
||||
if (!rootURI) {
|
||||
rootURI = resourceURI.spec;
|
||||
}
|
||||
|
||||
// Scan current windows
|
||||
const windows = Services.wm.getEnumerator("navigator:browser");
|
||||
while (windows.hasMoreElements()) {
|
||||
loadAddon(
|
||||
windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow)
|
||||
);
|
||||
Services.scriptloader.loadSubScript(
|
||||
`${rootURI}/chrome/content/scripts/index.js`
|
||||
);
|
||||
|
||||
Zotero.AddonTemplate.rootURI = rootURI;
|
||||
|
||||
if (Zotero.platformMajorVersion >= 102) {
|
||||
var aomStartup = Components.Cc[
|
||||
"@mozilla.org/addons/addon-manager-startup;1"
|
||||
].getService(Components.Ci.amIAddonManagerStartup);
|
||||
var manifestURI = Services.io.newURI(rootURI + "manifest.json");
|
||||
var chromeHandle = aomStartup.registerChrome(manifestURI, [
|
||||
["content", "__addonRef__", "chrome/content/"],
|
||||
["locale", "__addonRef__", "en-US", "chrome/locale/en-US/"],
|
||||
["locale", "__addonRef__", "zh-CN", "chrome/locale/zh-CN/"],
|
||||
]);
|
||||
|
||||
Zotero.PreferencePanes.register({
|
||||
pluginID: "__addonID__",
|
||||
src: `${rootURI}/chrome/content/preferences.xhtml`,
|
||||
extraDTD: [`chrome://__addonRef__/locale/overlay.dtd`],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
function shutdown({ id, version, resourceURI, rootURI }, reason) {
|
||||
if (reason === APP_SHUTDOWN) {
|
||||
return;
|
||||
}
|
||||
var _Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
|
||||
Components.interfaces.nsISupports
|
||||
).wrappedJSObject;
|
||||
_Zotero.AddonTemplate.events.onUnInit(_Zotero);
|
||||
if (typeof Zotero === "undefined") {
|
||||
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");
|
||||
Cu.unload(`${rootURI}/chrome/content/scripts/index.js`);
|
||||
|
||||
chromeHandle.destruct();
|
||||
chromeHandle = null;
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {}
|
||||
|
13
addon/chrome/content/preferences.xhtml
Normal file
13
addon/chrome/content/preferences.xhtml
Normal file
@ -0,0 +1,13 @@
|
||||
<xul:vbox
|
||||
id="zotero-prefpane-__addonRef__"
|
||||
onload="Zotero.AddonTemplate.prefs.initPreferences(window)"
|
||||
>
|
||||
<xul:groupbox>
|
||||
<label><h2>Addon Template Example</h2></label>
|
||||
<xul:checkbox
|
||||
id="zotero-prefpane-__addonRef__-enable"
|
||||
preference="pref-__addonRef__-enable"
|
||||
label="&zotero.__addonRef__.pref.enable.label;"
|
||||
/>
|
||||
</xul:groupbox>
|
||||
</xul:vbox>
|
@ -1,15 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE prefwindow SYSTEM "chrome://__addonRef__/locale/overlay.dtd">
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/preferences.css"?>
|
||||
|
||||
<prefwindow id="__addonRef__-prefs" title="&zotero.__addonRef__.pref.title;" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script src="chrome://zotero/content/include.js" />
|
||||
<prefpane id=" zotero-prefpane-__addonRef__" insertafter=" zotero-prefpane-advanced" label=" __addonName__" image=" chrome: __addonRef__ skin favicon.png" onpaneload=" Zotero.AddonTemplate.prefs.initPreferences(window)">
|
||||
<preferences id=" zotero-preferences-__addonRef__">
|
||||
<preference id=" pref-__addonRef__-enable" name=" extensions.zotero.__addonRef__.enable" type=" bool" />
|
||||
</preferences>
|
||||
<checkbox id=" zotero-prefpane-__addonRef__-enable" preference=" pref-__addonRef__-enable" label=" &zotero.__addonRef__.pref.enable.label;" />
|
||||
</prefpane>
|
||||
</prefwindow>
|
19
addon/manifest.json
Normal file
19
addon/manifest.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "__addonName__",
|
||||
"version": "__buildVersion__",
|
||||
"description": "__description__",
|
||||
"author": "__author__",
|
||||
"icons": {
|
||||
"48": "chrome/skin/default/__addonRef__/favicon@0.5x.png",
|
||||
"96": "chrome/skin/default/__addonRef__/favicon.png"
|
||||
},
|
||||
"applications": {
|
||||
"zotero": {
|
||||
"id": "__addonID__",
|
||||
"update_url": "__updaterdf__",
|
||||
"strict_min_version": "6.999",
|
||||
"strict_max_version": "7.0.*"
|
||||
}
|
||||
}
|
||||
}
|
10
build.js
10
build.js
@ -96,6 +96,8 @@ async function main() {
|
||||
|
||||
copyFolderRecursiveSync("addon", buildDir);
|
||||
|
||||
copyFileSync("update-template.json", "update.json");
|
||||
|
||||
await esbuild
|
||||
.build({
|
||||
entryPoints: ["src/index.ts"],
|
||||
@ -113,10 +115,12 @@ async function main() {
|
||||
path.join(buildDir, "**/*.rdf"),
|
||||
path.join(buildDir, "**/*.dtd"),
|
||||
path.join(buildDir, "**/*.xul"),
|
||||
path.join(buildDir, "**/*.manifest"),
|
||||
path.join(buildDir, "**/*.xhtml"),
|
||||
path.join(buildDir, "**/*.json"),
|
||||
path.join(buildDir, "addon/defaults", "**/*.js"),
|
||||
path.join(buildDir, "addon/chrome.manifest"),
|
||||
path.join(buildDir, "addon/bootstrap.js"),
|
||||
"update.rdf",
|
||||
"update.json",
|
||||
],
|
||||
from: [
|
||||
/__author__/g,
|
||||
@ -129,7 +133,6 @@ async function main() {
|
||||
/__addonRef__/g,
|
||||
/__buildVersion__/g,
|
||||
/__buildTime__/g,
|
||||
/<em:version>\S*<\/em:version>/g,
|
||||
],
|
||||
to: [
|
||||
author,
|
||||
@ -142,7 +145,6 @@ async function main() {
|
||||
addonRef,
|
||||
version,
|
||||
buildTime,
|
||||
`<em:version>${version}</em:version>`,
|
||||
],
|
||||
countMatches: true,
|
||||
};
|
||||
|
@ -33,7 +33,8 @@
|
||||
"replace-in-file": "^6.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.7.20",
|
||||
"release-it": "^14.14.0",
|
||||
"zotero-types": "^0.0.4"
|
||||
"zotero-types": "^0.0.7"
|
||||
}
|
||||
}
|
||||
|
29
src/addon.ts
29
src/addon.ts
@ -8,6 +8,8 @@ class Addon {
|
||||
public events: AddonEvents;
|
||||
public views: AddonViews;
|
||||
public prefs: AddonPrefs;
|
||||
// root path to access the resources
|
||||
public rootURI: string;
|
||||
|
||||
constructor() {
|
||||
this.events = new AddonEvents(this);
|
||||
@ -16,4 +18,29 @@ class Addon {
|
||||
}
|
||||
}
|
||||
|
||||
export { addonName, Addon };
|
||||
function getZotero(): _ZoteroConstructable {
|
||||
if (typeof Zotero === "undefined") {
|
||||
return Components.classes["@zotero.org/Zotero;1"].getService(
|
||||
Components.interfaces.nsISupports
|
||||
).wrappedJSObject;
|
||||
}
|
||||
return Zotero;
|
||||
}
|
||||
|
||||
function isZotero7(): boolean {
|
||||
return Zotero.platformMajorVersion >= 102;
|
||||
}
|
||||
|
||||
function createXULElement(doc: Document, type: string): XUL.Element {
|
||||
if (isZotero7()) {
|
||||
// @ts-ignore
|
||||
return doc.createXULElement(type);
|
||||
} else {
|
||||
return doc.createElementNS(
|
||||
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
||||
type
|
||||
) as XUL.Element;
|
||||
}
|
||||
}
|
||||
|
||||
export { addonName, Addon, getZotero, isZotero7, createXULElement };
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Addon, addonName } from "./addon";
|
||||
import { Addon, addonName, getZotero } from "./addon";
|
||||
import AddonModule from "./module";
|
||||
|
||||
class AddonEvents extends AddonModule {
|
||||
@ -27,32 +27,33 @@ class AddonEvents extends AddonModule {
|
||||
};
|
||||
}
|
||||
|
||||
public async onInit(_Zotero) {
|
||||
public async onInit() {
|
||||
const Zotero = getZotero();
|
||||
// This function is the setup code of the addon
|
||||
console.log(`${addonName}: init called`);
|
||||
_Zotero.debug(`${addonName}: init called`);
|
||||
Zotero.debug(`${addonName}: init called`);
|
||||
// alert(112233);
|
||||
|
||||
// Reset prefs
|
||||
this.resetState();
|
||||
|
||||
// Register the callback in Zotero as an item observer
|
||||
let notifierID = _Zotero.Notifier.registerObserver(this.notifierCallback, [
|
||||
let notifierID = Zotero.Notifier.registerObserver(this.notifierCallback, [
|
||||
"tab",
|
||||
"item",
|
||||
"file",
|
||||
]);
|
||||
|
||||
// Unregister callback when the window closes (important to avoid a memory leak)
|
||||
_Zotero.getMainWindow().addEventListener(
|
||||
Zotero.getMainWindow().addEventListener(
|
||||
"unload",
|
||||
function (e) {
|
||||
_Zotero.Notifier.unregisterObserver(notifierID);
|
||||
Zotero.Notifier.unregisterObserver(notifierID);
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
this._Addon.views.initViews(_Zotero);
|
||||
this._Addon.views.initViews();
|
||||
this._Addon.views.initPrefs();
|
||||
}
|
||||
|
||||
private resetState(): void {
|
||||
@ -68,13 +69,13 @@ class AddonEvents extends AddonModule {
|
||||
// }
|
||||
}
|
||||
|
||||
public onUnInit(_Zotero): void {
|
||||
console.log(`${addonName}: uninit called`);
|
||||
_Zotero.debug(`${addonName}: uninit called`);
|
||||
public onUnInit(): void {
|
||||
const Zotero = getZotero();
|
||||
Zotero.debug(`${addonName}: uninit called`);
|
||||
// Remove elements and do clean up
|
||||
this._Addon.views.unInitViews(_Zotero);
|
||||
this._Addon.views.unInitViews();
|
||||
// Remove addon object
|
||||
_Zotero.AddonTemplate = undefined;
|
||||
Zotero.AddonTemplate = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
13
src/index.ts
13
src/index.ts
@ -1,9 +1,8 @@
|
||||
import { Addon } from "./addon";
|
||||
import { Addon, getZotero } from "./addon";
|
||||
|
||||
var _Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
|
||||
Components.interfaces.nsISupports
|
||||
).wrappedJSObject;
|
||||
if (!_Zotero.AddonTemplate) {
|
||||
_Zotero.AddonTemplate = new Addon();
|
||||
_Zotero.AddonTemplate.events.onInit(_Zotero);
|
||||
const Zotero = getZotero();
|
||||
|
||||
if (!Zotero.AddonTemplate) {
|
||||
Zotero.AddonTemplate = new Addon();
|
||||
Zotero.AddonTemplate.events.onInit();
|
||||
}
|
||||
|
35
src/views.ts
35
src/views.ts
@ -1,6 +1,6 @@
|
||||
import { Addon } from "./addon";
|
||||
import { Addon, getZotero, createXULElement } from "./addon";
|
||||
import AddonModule from "./module";
|
||||
const { addonRef } = require("../package.json");
|
||||
const { addonRef, addonID } = require("../package.json");
|
||||
|
||||
class AddonViews extends AddonModule {
|
||||
// You can store some element in the object attributes
|
||||
@ -16,24 +16,35 @@ class AddonViews extends AddonModule {
|
||||
};
|
||||
}
|
||||
|
||||
public initViews(_Zotero) {
|
||||
public initViews() {
|
||||
const Zotero = getZotero();
|
||||
// You can init the UI elements that
|
||||
// cannot be initialized with overlay.xul
|
||||
console.log("Initializing UI");
|
||||
const _window: Window = _Zotero.getMainWindow();
|
||||
const menuitem = _window.document.createElement("menuitem");
|
||||
Zotero.debug("Initializing UI");
|
||||
const _window: Window = Zotero.getMainWindow();
|
||||
const menuitem = createXULElement(_window.document, "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')";
|
||||
// menuitem.className = "menuitem-iconic";
|
||||
// menuitem.style["list-style-image"] =
|
||||
// "url('chrome/skin/default/addontemplate/favicon@0.5x.png')";
|
||||
_window.document.querySelector("#zotero-itemmenu").appendChild(menuitem);
|
||||
}
|
||||
|
||||
public unInitViews(_Zotero) {
|
||||
console.log("Uninitializing UI");
|
||||
const _window: Window = _Zotero.getMainWindow();
|
||||
public initPrefs() {
|
||||
const Zotero = getZotero();
|
||||
Zotero.PreferencePanes.register({
|
||||
pluginID: addonID,
|
||||
src: `${this._Addon.rootURI}/chrome/content/preferences.xhtml`,
|
||||
extraDTD: [`chrome://${addonRef}/locale/overlay.dtd`],
|
||||
});
|
||||
}
|
||||
|
||||
public unInitViews() {
|
||||
const Zotero = getZotero();
|
||||
Zotero.debug("Uninitializing UI");
|
||||
const _window: Window = Zotero.getMainWindow();
|
||||
_window.document
|
||||
.querySelector("#zotero-itemmenu-addontemplate-test")
|
||||
?.remove();
|
||||
|
26
update-template.json
Normal file
26
update-template.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"addons": {
|
||||
"__addonID__": {
|
||||
"updates": [
|
||||
{
|
||||
"version": "__buildVersion__",
|
||||
"update_link": "__releasepage__",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"strict_min_version": "60.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "__buildVersion__",
|
||||
"update_link": "__releasepage__",
|
||||
"applications": {
|
||||
"zotero": {
|
||||
"strict_min_version": "6.999"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
26
update.json
Normal file
26
update.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"addons": {
|
||||
"addontemplate@euclpts.com": {
|
||||
"updates": [
|
||||
{
|
||||
"version": "0.0.0",
|
||||
"update_link": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"strict_min_version": "60.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "0.0.0",
|
||||
"update_link": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi",
|
||||
"applications": {
|
||||
"zotero": {
|
||||
"strict_min_version": "6.999"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user