Merge branch 'bootstrap-zo7' into bootstrap
This commit is contained in:
		
						commit
						2cd31e414e
					
				
							
								
								
									
										141
									
								
								addon/bootstrap.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										141
									
								
								addon/bootstrap.js
									
									
									
									
										vendored
									
									
								
							@ -3,69 +3,118 @@
 | 
				
			|||||||
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
					 * 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/. */
 | 
					 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"use strict";
 | 
					if (typeof Zotero == "undefined") {
 | 
				
			||||||
/* global Components, Services */
 | 
					  var Zotero;
 | 
				
			||||||
/* global addon, APP_SHUTDOWN */
 | 
					}
 | 
				
			||||||
const { classes: Cc, utils: Cu } = Components;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Cu.import("resource://gre/modules/Services.jsm");
 | 
					var chromeHandle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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 install(data, reason) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function startup(data, reason) {
 | 
					async function startup({ id, version, resourceURI, rootURI }, reason) {
 | 
				
			||||||
  // Load the addon to Zotero if window is ready
 | 
					  await waitForZotero();
 | 
				
			||||||
  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
 | 
					  // String 'rootURI' introduced in Zotero 7
 | 
				
			||||||
  var WindowListener = {
 | 
					  if (!rootURI) {
 | 
				
			||||||
    onOpenWindow: function (xulWindow) {
 | 
					    rootURI = resourceURI.spec;
 | 
				
			||||||
      loadAddon(
 | 
					  }
 | 
				
			||||||
        xulWindow
 | 
					 | 
				
			||||||
          .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
 | 
					 | 
				
			||||||
          .getInterface(Components.interfaces.nsIDOMWindow)
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  Services.wm.addListener(WindowListener);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Scan current windows
 | 
					  const ctx = { Zotero, rootURI };
 | 
				
			||||||
  const windows = Services.wm.getEnumerator("navigator:browser");
 | 
					
 | 
				
			||||||
  while (windows.hasMoreElements()) {
 | 
					  Services.scriptloader.loadSubScript(
 | 
				
			||||||
    loadAddon(
 | 
					    `${rootURI}/chrome/content/scripts/index.js`,
 | 
				
			||||||
      windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow)
 | 
					    ctx
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (Zotero.platformMajorVersion >= 102) {
 | 
				
			||||||
 | 
					    var aomStartup = Components.classes[
 | 
				
			||||||
 | 
					      "@mozilla.org/addons/addon-manager-startup;1"
 | 
				
			||||||
 | 
					    ].getService(Components.interfaces.amIAddonManagerStartup);
 | 
				
			||||||
 | 
					    var manifestURI = Services.io.newURI(rootURI + "manifest.json");
 | 
				
			||||||
 | 
					    chromeHandle = aomStartup.registerChrome(manifestURI, [
 | 
				
			||||||
 | 
					      ["content", "__addonRef__", rootURI + "chrome/content/"],
 | 
				
			||||||
 | 
					      ["locale", "__addonRef__", "en-US", rootURI + "chrome/locale/en-US/"],
 | 
				
			||||||
 | 
					      ["locale", "__addonRef__", "zh-CN", rootURI + "chrome/locale/zh-CN/"],
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Zotero.PreferencePanes.register({
 | 
				
			||||||
 | 
					    //   pluginID: "__addonID__",
 | 
				
			||||||
 | 
					    //   src: rootURI + "chrome/content/preferences.xhtml",
 | 
				
			||||||
 | 
					    //   extraDTD: ["chrome://__addonRef__/locale/overlay.dtd"],
 | 
				
			||||||
 | 
					    //   defaultXUL: true,
 | 
				
			||||||
 | 
					    // });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function shutdown(data, reason) {
 | 
					function shutdown({ id, version, resourceURI, rootURI }, reason) {
 | 
				
			||||||
  if (reason === APP_SHUTDOWN) {
 | 
					  if (reason === APP_SHUTDOWN) {
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  var _Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
 | 
					  if (typeof Zotero === "undefined") {
 | 
				
			||||||
    Components.interfaces.nsISupports
 | 
					    Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
 | 
				
			||||||
  ).wrappedJSObject;
 | 
					      Components.interfaces.nsISupports
 | 
				
			||||||
  _Zotero.AddonTemplate.events.onUnInit(_Zotero);
 | 
					    ).wrappedJSObject;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  Zotero.AddonTemplate.events.onUnInit(Zotero);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Cc["@mozilla.org/intl/stringbundle;1"]
 | 
					  Cc["@mozilla.org/intl/stringbundle;1"]
 | 
				
			||||||
    .getService(Components.interfaces.nsIStringBundleService)
 | 
					    .getService(Components.interfaces.nsIStringBundleService)
 | 
				
			||||||
    .flushBundles();
 | 
					    .flushBundles();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Cu.unload("chrome://_addonRef__/scripts/index.js");
 | 
					  Cu.unload(`${rootURI}/chrome/content/scripts/index.js`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  chromeHandle.destruct();
 | 
				
			||||||
 | 
					  chromeHandle = null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function uninstall(data, reason) {}
 | 
					function uninstall(data, reason) {}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,3 @@
 | 
				
			|||||||
content __addonRef__ chrome/content/
 | 
					content __addonRef__ chrome/content/
 | 
				
			||||||
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/
 | 
				
			||||||
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 677 B After Width: | Height: | Size: 677 B  | 
| 
		 Before Width: | Height: | Size: 836 B After Width: | Height: | Size: 836 B  | 
							
								
								
									
										21
									
								
								addon/chrome/content/preferences.xhtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								addon/chrome/content/preferences.xhtml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					<vbox
 | 
				
			||||||
 | 
					  id="zotero-prefpane-__addonRef__"
 | 
				
			||||||
 | 
					  onload="Zotero.AddonTemplate.prefs.initPreferences(window)"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					  <groupbox>
 | 
				
			||||||
 | 
					    <label><html:h2>Addon Template Example</html:h2></label>
 | 
				
			||||||
 | 
					    <checkbox
 | 
				
			||||||
 | 
					      id="zotero-prefpane-__addonRef__-enable"
 | 
				
			||||||
 | 
					      label="&zotero.__addonRef__.pref.enable.label;"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <hbox>
 | 
				
			||||||
 | 
					      <html:input
 | 
				
			||||||
 | 
					        type="text"
 | 
				
			||||||
 | 
					        id="zotero-prefpane-__addonRef__-input"
 | 
				
			||||||
 | 
					      ></html:input>
 | 
				
			||||||
 | 
					      <html:label for="zotero-prefpane-__addonRef__-input"
 | 
				
			||||||
 | 
					        >&zotero.__addonRef__.pref.input.label;</html:label
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					    </hbox>
 | 
				
			||||||
 | 
					  </groupbox>
 | 
				
			||||||
 | 
					</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>
 | 
					 | 
				
			||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
<!ENTITY zotero.__addonRef__.itemmenu.test.label "addon template">
 | 
					<!ENTITY zotero.__addonRef__.itemmenu.test.label "addon template">
 | 
				
			||||||
<!ENTITY zotero.__addonRef__.pref.enable.label "Enable">
 | 
					<!ENTITY zotero.__addonRef__.pref.enable.label "Enable">
 | 
				
			||||||
 | 
					<!ENTITY zotero.__addonRef__.pref.input.label "Input">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!ENTITY zotero.__addonRef__.help.version.label "__addonName__ VERSION __buildVersion__">
 | 
					<!ENTITY zotero.__addonRef__.help.version.label "__addonName__ VERSION __buildVersion__">
 | 
				
			||||||
<!ENTITY zotero.__addonRef__.help.releasetime.label "Build __buildTime__">
 | 
					<!ENTITY zotero.__addonRef__.help.releasetime.label "Build __buildTime__">
 | 
				
			||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
<!ENTITY zotero.__addonRef__.itemmenu.test.label "插件模板">
 | 
					<!ENTITY zotero.__addonRef__.itemmenu.test.label "插件模板">
 | 
				
			||||||
<!ENTITY zotero.__addonRef__.pref.enable.label "开启">
 | 
					<!ENTITY zotero.__addonRef__.pref.enable.label "开启">
 | 
				
			||||||
 | 
					<!ENTITY zotero.__addonRef__.pref.input.label "输入">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!ENTITY zotero.__addonRef__.help.version.label "__addonName__ 版本 __buildVersion__">
 | 
					<!ENTITY zotero.__addonRef__.help.version.label "__addonName__ 版本 __buildVersion__">
 | 
				
			||||||
<!ENTITY zotero.__addonRef__.help.releasetime.label "Build __buildTime__">
 | 
					<!ENTITY zotero.__addonRef__.help.releasetime.label "Build __buildTime__">
 | 
				
			||||||
@ -12,7 +12,7 @@
 | 
				
			|||||||
        em:creator="__author__"
 | 
					        em:creator="__author__"
 | 
				
			||||||
        em:description="__description__"
 | 
					        em:description="__description__"
 | 
				
			||||||
        em:homepageURL="__homepage__"
 | 
					        em:homepageURL="__homepage__"
 | 
				
			||||||
        em:iconURL="chrome://__addonRef__/skin/favicon.png"
 | 
					        em:iconURL="chrome://__addonRef__/content/icons/favicon.png"
 | 
				
			||||||
        em:optionsURL="chrome://__addonRef__/content/preferences.xul"
 | 
					        em:optionsURL="chrome://__addonRef__/content/preferences.xul"
 | 
				
			||||||
        em:updateURL="__updaterdf__"
 | 
					        em:updateURL="__updaterdf__"
 | 
				
			||||||
        em:multiprocessCompatible="true"
 | 
					        em:multiprocessCompatible="true"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										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/content/icons/favicon@0.5x.png",
 | 
				
			||||||
 | 
					    "96": "chrome/content/icons/favicon.png"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "applications": {
 | 
				
			||||||
 | 
					    "zotero": {
 | 
				
			||||||
 | 
					      "id": "__addonID__",
 | 
				
			||||||
 | 
					      "update_url": "__updaterdf__",
 | 
				
			||||||
 | 
					      "strict_min_version": "6.999",
 | 
				
			||||||
 | 
					      "strict_max_version": "7.0.*"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								build.js
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								build.js
									
									
									
									
									
								
							@ -96,6 +96,9 @@ async function main() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  copyFolderRecursiveSync("addon", buildDir);
 | 
					  copyFolderRecursiveSync("addon", buildDir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  copyFileSync("update-template.json", "update.json");
 | 
				
			||||||
 | 
					  copyFileSync("update-template.rdf", "update.rdf");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  await esbuild
 | 
					  await esbuild
 | 
				
			||||||
    .build({
 | 
					    .build({
 | 
				
			||||||
      entryPoints: ["src/index.ts"],
 | 
					      entryPoints: ["src/index.ts"],
 | 
				
			||||||
@ -113,9 +116,13 @@ async function main() {
 | 
				
			|||||||
      path.join(buildDir, "**/*.rdf"),
 | 
					      path.join(buildDir, "**/*.rdf"),
 | 
				
			||||||
      path.join(buildDir, "**/*.dtd"),
 | 
					      path.join(buildDir, "**/*.dtd"),
 | 
				
			||||||
      path.join(buildDir, "**/*.xul"),
 | 
					      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/defaults", "**/*.js"),
 | 
				
			||||||
 | 
					      path.join(buildDir, "addon/chrome.manifest"),
 | 
				
			||||||
 | 
					      path.join(buildDir, "addon/manifest.json"),
 | 
				
			||||||
      path.join(buildDir, "addon/bootstrap.js"),
 | 
					      path.join(buildDir, "addon/bootstrap.js"),
 | 
				
			||||||
 | 
					      "update.json",
 | 
				
			||||||
      "update.rdf",
 | 
					      "update.rdf",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    from: [
 | 
					    from: [
 | 
				
			||||||
@ -129,7 +136,6 @@ async function main() {
 | 
				
			|||||||
      /__addonRef__/g,
 | 
					      /__addonRef__/g,
 | 
				
			||||||
      /__buildVersion__/g,
 | 
					      /__buildVersion__/g,
 | 
				
			||||||
      /__buildTime__/g,
 | 
					      /__buildTime__/g,
 | 
				
			||||||
      /<em:version>\S*<\/em:version>/g,
 | 
					 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    to: [
 | 
					    to: [
 | 
				
			||||||
      author,
 | 
					      author,
 | 
				
			||||||
@ -142,7 +148,6 @@ async function main() {
 | 
				
			|||||||
      addonRef,
 | 
					      addonRef,
 | 
				
			||||||
      version,
 | 
					      version,
 | 
				
			||||||
      buildTime,
 | 
					      buildTime,
 | 
				
			||||||
      `<em:version>${version}</em:version>`,
 | 
					 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    countMatches: true,
 | 
					    countMatches: true,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
				
			|||||||
@ -26,14 +26,15 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "homepage": "https://github.com/windingwind/zotero-addon-template#readme",
 | 
					  "homepage": "https://github.com/windingwind/zotero-addon-template#readme",
 | 
				
			||||||
  "releasepage": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi",
 | 
					  "releasepage": "https://github.com/windingwind/zotero-addon-template/releases/latest/download/zotero-addon-template.xpi",
 | 
				
			||||||
  "updaterdf": "https://raw.githubusercontent.com/windingwind/zotero-addon-template/master/update.rdf",
 | 
					  "updaterdf": "https://raw.githubusercontent.com/windingwind/zotero-addon-template/master/update.json",
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "compressing": "^1.5.1",
 | 
					    "compressing": "^1.5.1",
 | 
				
			||||||
    "esbuild": "^0.15.16",
 | 
					    "esbuild": "^0.15.16",
 | 
				
			||||||
    "replace-in-file": "^6.3.2"
 | 
					    "replace-in-file": "^6.3.2"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "release-it": "^15.5.0",
 | 
					    "@types/node": "^18.7.20",
 | 
				
			||||||
    "zotero-types": "^0.0.6"
 | 
					    "release-it": "^14.14.0",
 | 
				
			||||||
 | 
					    "zotero-types": "^0.0.8"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										10
									
								
								src/addon.ts
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/addon.ts
									
									
									
									
									
								
							@ -1,19 +1,23 @@
 | 
				
			|||||||
import AddonEvents from "./events";
 | 
					import AddonEvents from "./events";
 | 
				
			||||||
import AddonPrefs from "./prefs";
 | 
					import AddonPrefs from "./prefs";
 | 
				
			||||||
 | 
					import AddonUtils from "./utils";
 | 
				
			||||||
import AddonViews from "./views";
 | 
					import AddonViews from "./views";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { addonName } = require("../package.json");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Addon {
 | 
					class Addon {
 | 
				
			||||||
 | 
					  public Zotero: _ZoteroConstructable;
 | 
				
			||||||
  public events: AddonEvents;
 | 
					  public events: AddonEvents;
 | 
				
			||||||
  public views: AddonViews;
 | 
					  public views: AddonViews;
 | 
				
			||||||
  public prefs: AddonPrefs;
 | 
					  public prefs: AddonPrefs;
 | 
				
			||||||
 | 
					  public Utils: AddonUtils;
 | 
				
			||||||
 | 
					  // root path to access the resources
 | 
				
			||||||
 | 
					  public rootURI: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    this.events = new AddonEvents(this);
 | 
					    this.events = new AddonEvents(this);
 | 
				
			||||||
    this.views = new AddonViews(this);
 | 
					    this.views = new AddonViews(this);
 | 
				
			||||||
    this.prefs = new AddonPrefs(this);
 | 
					    this.prefs = new AddonPrefs(this);
 | 
				
			||||||
 | 
					    this.Utils = new AddonUtils(this);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { addonName, Addon };
 | 
					export default Addon;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import { Addon, addonName } from "./addon";
 | 
					import Addon from "./addon";
 | 
				
			||||||
import AddonModule from "./module";
 | 
					import AddonModule from "./module";
 | 
				
			||||||
 | 
					import { addonName } from "../package.json";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AddonEvents extends AddonModule {
 | 
					class AddonEvents extends AddonModule {
 | 
				
			||||||
  private notifierCallback: any;
 | 
					  private notifierCallback: any;
 | 
				
			||||||
@ -27,32 +28,34 @@ class AddonEvents extends AddonModule {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async onInit(_Zotero) {
 | 
					  public async onInit(_Zotero: _ZoteroConstructable, rootURI) {
 | 
				
			||||||
 | 
					    this._Addon.Zotero = _Zotero;
 | 
				
			||||||
 | 
					    this._Addon.rootURI = rootURI;
 | 
				
			||||||
    // This function is the setup code of the addon
 | 
					    // This function is the setup code of the addon
 | 
				
			||||||
    console.log(`${addonName}: init called`);
 | 
					    this._Addon.Utils.Tool.log(`${addonName}: init called`);
 | 
				
			||||||
    _Zotero.debug(`${addonName}: init called`);
 | 
					 | 
				
			||||||
    // alert(112233);
 | 
					    // 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)
 | 
				
			||||||
    _Zotero.getMainWindow().addEventListener(
 | 
					    Zotero.getMainWindow().addEventListener(
 | 
				
			||||||
      "unload",
 | 
					      "unload",
 | 
				
			||||||
      function (e) {
 | 
					      function (e) {
 | 
				
			||||||
        _Zotero.Notifier.unregisterObserver(notifierID);
 | 
					        Zotero.Notifier.unregisterObserver(notifierID);
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      false
 | 
					      false
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this._Addon.views.initViews(_Zotero);
 | 
					    this._Addon.views.initViews();
 | 
				
			||||||
 | 
					    this._Addon.views.initPrefs();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private resetState(): void {
 | 
					  private resetState(): void {
 | 
				
			||||||
@ -68,13 +71,13 @@ class AddonEvents extends AddonModule {
 | 
				
			|||||||
    // }
 | 
					    // }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public onUnInit(_Zotero): void {
 | 
					  public onUnInit(): void {
 | 
				
			||||||
    console.log(`${addonName}: uninit called`);
 | 
					    const Zotero = this._Addon.Zotero;
 | 
				
			||||||
    _Zotero.debug(`${addonName}: uninit called`);
 | 
					    this._Addon.Utils.Tool.log(`${addonName}: uninit called`);
 | 
				
			||||||
    //  Remove elements and do clean up
 | 
					    //  Remove elements and do clean up
 | 
				
			||||||
    this._Addon.views.unInitViews(_Zotero);
 | 
					    this._Addon.views.unInitViews();
 | 
				
			||||||
    // Remove addon object
 | 
					    // Remove addon object
 | 
				
			||||||
    _Zotero.AddonTemplate = undefined;
 | 
					    Zotero.AddonTemplate = undefined;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										12
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/index.ts
									
									
									
									
									
								
							@ -1,9 +1,7 @@
 | 
				
			|||||||
import { Addon } from "./addon";
 | 
					import Addon from "./addon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
 | 
					if (!Zotero.AddonTemplate) {
 | 
				
			||||||
  Components.interfaces.nsISupports
 | 
					  Zotero.AddonTemplate = new Addon();
 | 
				
			||||||
).wrappedJSObject;
 | 
					  // @ts-ignore
 | 
				
			||||||
if (!_Zotero.AddonTemplate) {
 | 
					  Zotero.AddonTemplate.events.onInit(Zotero, rootURI);
 | 
				
			||||||
  _Zotero.AddonTemplate = new Addon();
 | 
					 | 
				
			||||||
  _Zotero.AddonTemplate.events.onInit(_Zotero);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,8 @@
 | 
				
			|||||||
 | 
					import Addon from "./addon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AddonModule {
 | 
					class AddonModule {
 | 
				
			||||||
  protected _Addon: any;
 | 
					  protected _Addon: Addon;
 | 
				
			||||||
  constructor(parent: any) {
 | 
					  constructor(parent: Addon) {
 | 
				
			||||||
    this._Addon = parent;
 | 
					    this._Addon = parent;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										28
									
								
								src/prefs.ts
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								src/prefs.ts
									
									
									
									
									
								
							@ -1,5 +1,6 @@
 | 
				
			|||||||
import { Addon, addonName } from "./addon";
 | 
					import Addon from "./addon";
 | 
				
			||||||
import AddonModule from "./module";
 | 
					import AddonModule from "./module";
 | 
				
			||||||
 | 
					import { addonName, addonRef } from "../package.json";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AddonPrefs extends AddonModule {
 | 
					class AddonPrefs extends AddonModule {
 | 
				
			||||||
  private _window: Window;
 | 
					  private _window: Window;
 | 
				
			||||||
@ -10,15 +11,36 @@ class AddonPrefs extends AddonModule {
 | 
				
			|||||||
    // This function is called when the prefs window is opened
 | 
					    // This function is called when the prefs window is opened
 | 
				
			||||||
    // See addon/chrome/content/preferences.xul onpaneload
 | 
					    // See addon/chrome/content/preferences.xul onpaneload
 | 
				
			||||||
    this._window = _window;
 | 
					    this._window = _window;
 | 
				
			||||||
    Zotero.debug(`${addonName}: init preferences`);
 | 
					    this._Addon.Utils.Tool.log(`${addonName}: init preferences`);
 | 
				
			||||||
    this.updatePrefsUI();
 | 
					    this.updatePrefsUI();
 | 
				
			||||||
 | 
					    this.bindPrefEvents();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private updatePrefsUI() {
 | 
					  private updatePrefsUI() {
 | 
				
			||||||
    // You can initialize some UI elements on prefs window
 | 
					    // You can initialize some UI elements on prefs window
 | 
				
			||||||
    // with this._window.document
 | 
					    // with this._window.document
 | 
				
			||||||
    // Or bind some events to the elements
 | 
					    // Or bind some events to the elements
 | 
				
			||||||
    Zotero.debug(`${addonName}: init preferences UI`);
 | 
					    this._Addon.Utils.Tool.log(`${addonName}: init preferences UI`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private bindPrefEvents() {
 | 
				
			||||||
 | 
					    this._window.document
 | 
				
			||||||
 | 
					      .querySelector(`#zotero-prefpane-${addonRef}-enable`)
 | 
				
			||||||
 | 
					      ?.addEventListener("command", (e) => {
 | 
				
			||||||
 | 
					        this._Addon.Utils.Tool.log(e);
 | 
				
			||||||
 | 
					        this._window.alert(
 | 
				
			||||||
 | 
					          `Successfully changed to ${(e.target as XUL.Checkbox).checked}!`
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this._window.document
 | 
				
			||||||
 | 
					      .querySelector(`#zotero-prefpane-${addonRef}-input`)
 | 
				
			||||||
 | 
					      ?.addEventListener("change", (e) => {
 | 
				
			||||||
 | 
					        this._Addon.Utils.Tool.log(e);
 | 
				
			||||||
 | 
					        this._window.alert(
 | 
				
			||||||
 | 
					          `Successfully changed to ${(e.target as HTMLInputElement).value}!`
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										461
									
								
								src/utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										461
									
								
								src/utils.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,461 @@
 | 
				
			|||||||
 | 
					import Addon from "./addon";
 | 
				
			||||||
 | 
					import AddonModule from "./module";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddonUtils extends AddonModule {
 | 
				
			||||||
 | 
					  public Compat: ZoteroCompat;
 | 
				
			||||||
 | 
					  public Tool: ZoteroTool;
 | 
				
			||||||
 | 
					  public UI: ZoteroUI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(parent: Addon) {
 | 
				
			||||||
 | 
					    super(parent);
 | 
				
			||||||
 | 
					    this.Compat = {
 | 
				
			||||||
 | 
					      // Get Zotero instance
 | 
				
			||||||
 | 
					      getZotero: () => {
 | 
				
			||||||
 | 
					        if (typeof Zotero === "undefined") {
 | 
				
			||||||
 | 
					          return Components.classes["@zotero.org/Zotero;1"].getService(
 | 
				
			||||||
 | 
					            Components.interfaces.nsISupports
 | 
				
			||||||
 | 
					          ).wrappedJSObject;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return Zotero;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      // Check if it's running on Zotero 7 (Firefox 102)
 | 
				
			||||||
 | 
					      isZotero7: () => Zotero.platformMajorVersion >= 102,
 | 
				
			||||||
 | 
					      // Firefox 102 support DOMParser natively
 | 
				
			||||||
 | 
					      getDOMParser: () => {
 | 
				
			||||||
 | 
					        if (this.Compat.isZotero7()) {
 | 
				
			||||||
 | 
					          return new DOMParser();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          return new (this.Compat.getZotero().getMainWindow().DOMParser)();
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					          return Components.classes[
 | 
				
			||||||
 | 
					            "@mozilla.org/xmlextras/domparser;1"
 | 
				
			||||||
 | 
					          ].createInstance(Components.interfaces.nsIDOMParser);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // create XUL element
 | 
				
			||||||
 | 
					      createXULElement: (doc: Document, type: string) => {
 | 
				
			||||||
 | 
					        if (this.Compat.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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      parseXHTMLToFragment: (
 | 
				
			||||||
 | 
					        str: string,
 | 
				
			||||||
 | 
					        entities: string[] = [],
 | 
				
			||||||
 | 
					        defaultXUL = true
 | 
				
			||||||
 | 
					      ) => {
 | 
				
			||||||
 | 
					        // Adapted from MozXULElement.parseXULToFragment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* eslint-disable indent */
 | 
				
			||||||
 | 
					        let parser = this.Compat.getDOMParser();
 | 
				
			||||||
 | 
					        // parser.forceEnableXULXBL();
 | 
				
			||||||
 | 
					        const xulns =
 | 
				
			||||||
 | 
					          "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 | 
				
			||||||
 | 
					        const htmlns = "http://www.w3.org/1999/xhtml";
 | 
				
			||||||
 | 
					        const wrappedStr = `${
 | 
				
			||||||
 | 
					          entities.length
 | 
				
			||||||
 | 
					            ? `<!DOCTYPE bindings [ ${entities.reduce(
 | 
				
			||||||
 | 
					                (preamble, url, index) => {
 | 
				
			||||||
 | 
					                  return (
 | 
				
			||||||
 | 
					                    preamble +
 | 
				
			||||||
 | 
					                    `<!ENTITY % _dtd-${index} SYSTEM "${url}"> %_dtd-${index}; `
 | 
				
			||||||
 | 
					                  );
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                ""
 | 
				
			||||||
 | 
					              )}]>`
 | 
				
			||||||
 | 
					            : ""
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        <html:div xmlns="${defaultXUL ? xulns : htmlns}"
 | 
				
			||||||
 | 
					            xmlns:xul="${xulns}" xmlns:html="${htmlns}">
 | 
				
			||||||
 | 
					        ${str}
 | 
				
			||||||
 | 
					        </html:div>`;
 | 
				
			||||||
 | 
					        this.Tool.log(wrappedStr, parser);
 | 
				
			||||||
 | 
					        let doc = parser.parseFromString(wrappedStr, "text/xml");
 | 
				
			||||||
 | 
					        /* eslint-enable indent */
 | 
				
			||||||
 | 
					        console.log(doc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (doc.documentElement.localName === "parsererror") {
 | 
				
			||||||
 | 
					          throw new Error("not well-formed XHTML");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // We use a range here so that we don't access the inner DOM elements from
 | 
				
			||||||
 | 
					        // JavaScript before they are imported and inserted into a document.
 | 
				
			||||||
 | 
					        let range = doc.createRange();
 | 
				
			||||||
 | 
					        range.selectNodeContents(doc.querySelector("div"));
 | 
				
			||||||
 | 
					        return range.extractContents();
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      prefPaneCache: { win: undefined, listeners: [], ids: [] },
 | 
				
			||||||
 | 
					      registerPrefPane: (options: PrefPaneOptions) => {
 | 
				
			||||||
 | 
					        const windowListener = {
 | 
				
			||||||
 | 
					          onOpenWindow: (xulWindow) => {
 | 
				
			||||||
 | 
					            const win: Window = xulWindow
 | 
				
			||||||
 | 
					              .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
 | 
				
			||||||
 | 
					              .getInterface(Components.interfaces.nsIDOMWindow);
 | 
				
			||||||
 | 
					            win.addEventListener(
 | 
				
			||||||
 | 
					              "load",
 | 
				
			||||||
 | 
					              async () => {
 | 
				
			||||||
 | 
					                if (
 | 
				
			||||||
 | 
					                  win.location.href ===
 | 
				
			||||||
 | 
					                  "chrome://zotero/content/preferences/preferences.xul"
 | 
				
			||||||
 | 
					                ) {
 | 
				
			||||||
 | 
					                  this.Tool.log("registerPrefPane:detected", options);
 | 
				
			||||||
 | 
					                  const Zotero = this.Compat.getZotero();
 | 
				
			||||||
 | 
					                  options.id || (options.id = `plugin-${new Date().getTime()}`);
 | 
				
			||||||
 | 
					                  const src = `<prefpane id="${
 | 
				
			||||||
 | 
					                    options.id
 | 
				
			||||||
 | 
					                  }" insertafter="zotero-prefpane-advanced" label="${
 | 
				
			||||||
 | 
					                    options.label || options.pluginID
 | 
				
			||||||
 | 
					                  }" image="${options.image || ""}">
 | 
				
			||||||
 | 
					                  ${(await Zotero.File.getContentsAsync(options.src)) as string}
 | 
				
			||||||
 | 
					                  </prefpane>`;
 | 
				
			||||||
 | 
					                  const frag = this.Compat.parseXHTMLToFragment(
 | 
				
			||||||
 | 
					                    src,
 | 
				
			||||||
 | 
					                    options.extraDTD,
 | 
				
			||||||
 | 
					                    options.defaultXUL
 | 
				
			||||||
 | 
					                  );
 | 
				
			||||||
 | 
					                  this.Tool.log(frag);
 | 
				
			||||||
 | 
					                  const prefWindow = win.document.querySelector("prefwindow");
 | 
				
			||||||
 | 
					                  prefWindow.appendChild(frag);
 | 
				
			||||||
 | 
					                  const prefPane = win.document.querySelector(`#${options.id}`);
 | 
				
			||||||
 | 
					                  // @ts-ignore
 | 
				
			||||||
 | 
					                  prefWindow.addPane(prefPane);
 | 
				
			||||||
 | 
					                  this.Compat.prefPaneCache.win = win;
 | 
				
			||||||
 | 
					                  this.Compat.prefPaneCache.listeners.push(windowListener);
 | 
				
			||||||
 | 
					                  this.Compat.prefPaneCache.ids.push(options.id);
 | 
				
			||||||
 | 
					                  if (options.onload) {
 | 
				
			||||||
 | 
					                    options.onload(win);
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              false
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Services.wm.addListener(windowListener);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      unregisterPrefPane: () => {
 | 
				
			||||||
 | 
					        this.Compat.prefPaneCache.listeners.forEach((l) =>
 | 
				
			||||||
 | 
					          Services.wm.removeListener(l)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        const win = this.Compat.prefPaneCache.win;
 | 
				
			||||||
 | 
					        if (win && !win.closed) {
 | 
				
			||||||
 | 
					          this.Compat.prefPaneCache.ids.forEach((id) =>
 | 
				
			||||||
 | 
					            win.document.querySelector(id)?.remove()
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    this.Tool = {
 | 
				
			||||||
 | 
					      getCopyHelper: () => new CopyHelper(),
 | 
				
			||||||
 | 
					      openFilePicker: (
 | 
				
			||||||
 | 
					        title: string,
 | 
				
			||||||
 | 
					        mode: "open" | "save" | "folder",
 | 
				
			||||||
 | 
					        filters?: [string, string][],
 | 
				
			||||||
 | 
					        suggestion?: string
 | 
				
			||||||
 | 
					      ) => {
 | 
				
			||||||
 | 
					        const fp = Components.classes[
 | 
				
			||||||
 | 
					          "@mozilla.org/filepicker;1"
 | 
				
			||||||
 | 
					        ].createInstance(Components.interfaces.nsIFilePicker);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (suggestion) fp.defaultString = suggestion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mode = {
 | 
				
			||||||
 | 
					          open: Components.interfaces.nsIFilePicker.modeOpen,
 | 
				
			||||||
 | 
					          save: Components.interfaces.nsIFilePicker.modeSave,
 | 
				
			||||||
 | 
					          folder: Components.interfaces.nsIFilePicker.modeGetFolder,
 | 
				
			||||||
 | 
					        }[mode];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fp.init(window, title, mode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (const [label, ext] of filters || []) {
 | 
				
			||||||
 | 
					          fp.appendFilter(label, ext);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // eslint-disable-next-line @typescript-eslint/no-unsafe-return
 | 
				
			||||||
 | 
					        return new Promise((resolve) => {
 | 
				
			||||||
 | 
					          fp.open((userChoice) => {
 | 
				
			||||||
 | 
					            switch (userChoice) {
 | 
				
			||||||
 | 
					              case Components.interfaces.nsIFilePicker.returnOK:
 | 
				
			||||||
 | 
					              case Components.interfaces.nsIFilePicker.returnReplace:
 | 
				
			||||||
 | 
					                resolve(fp.file.path);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              default: // aka returnCancel
 | 
				
			||||||
 | 
					                resolve("");
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      log: (...data: any[]) => {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          this._Addon.Zotero.getMainWindow().console.log(...data);
 | 
				
			||||||
 | 
					          for (const d of data) {
 | 
				
			||||||
 | 
					            this._Addon.Zotero.debug(d);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					          this._Addon.Zotero.debug(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    this.UI = {
 | 
				
			||||||
 | 
					      addonElements: [],
 | 
				
			||||||
 | 
					      createElement: (
 | 
				
			||||||
 | 
					        doc: Document,
 | 
				
			||||||
 | 
					        tagName: string,
 | 
				
			||||||
 | 
					        namespace: "html" | "svg" | "xul" = "html"
 | 
				
			||||||
 | 
					      ) => {
 | 
				
			||||||
 | 
					        namespace = namespace || "html";
 | 
				
			||||||
 | 
					        const namespaces = {
 | 
				
			||||||
 | 
					          html: "http://www.w3.org/1999/xhtml",
 | 
				
			||||||
 | 
					          svg: "http://www.w3.org/2000/svg",
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        if (tagName === "fragment") {
 | 
				
			||||||
 | 
					          return doc.createDocumentFragment();
 | 
				
			||||||
 | 
					        } else if (namespace === "xul") {
 | 
				
			||||||
 | 
					          const e = this.Compat.createXULElement(doc, tagName);
 | 
				
			||||||
 | 
					          this.UI.addonElements.push(e);
 | 
				
			||||||
 | 
					          return e;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          const e = doc.createElementNS(namespaces[namespace], tagName) as
 | 
				
			||||||
 | 
					            | HTMLElement
 | 
				
			||||||
 | 
					            | SVGAElement;
 | 
				
			||||||
 | 
					          this.UI.addonElements.push(e);
 | 
				
			||||||
 | 
					          return e;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      removeAddonElements: () => {
 | 
				
			||||||
 | 
					        this.UI.addonElements.forEach((e) => {
 | 
				
			||||||
 | 
					          try {
 | 
				
			||||||
 | 
					            e?.remove();
 | 
				
			||||||
 | 
					          } catch (e) {
 | 
				
			||||||
 | 
					            this._Addon.Utils.Tool.log(e);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      creatElementsFromJSON: (doc: Document, options: ElementOptions) => {
 | 
				
			||||||
 | 
					        this.Tool.log(options);
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          options.id &&
 | 
				
			||||||
 | 
					          (options.checkExistanceParent
 | 
				
			||||||
 | 
					            ? options.checkExistanceParent
 | 
				
			||||||
 | 
					            : doc
 | 
				
			||||||
 | 
					          ).querySelector(`#${options.id}`)
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          if (options.ignoreIfExists) {
 | 
				
			||||||
 | 
					            return undefined;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (options.removeIfExists) {
 | 
				
			||||||
 | 
					            doc.querySelector(`#${options.id}`).remove();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (options.customCheck && !options.customCheck()) {
 | 
				
			||||||
 | 
					          return undefined;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const element = this.UI.createElement(
 | 
				
			||||||
 | 
					          doc,
 | 
				
			||||||
 | 
					          options.tag,
 | 
				
			||||||
 | 
					          options.namespace
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let _DocumentFragment: typeof DocumentFragment;
 | 
				
			||||||
 | 
					        if (typeof DocumentFragment === "undefined") {
 | 
				
			||||||
 | 
					          _DocumentFragment = (doc as any).ownerGlobal.DocumentFragment;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          _DocumentFragment = DocumentFragment;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!(element instanceof _DocumentFragment)) {
 | 
				
			||||||
 | 
					          if (options.id) {
 | 
				
			||||||
 | 
					            element.id = options.id;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (options.styles && Object.keys(options.styles).length) {
 | 
				
			||||||
 | 
					            Object.keys(options.styles).forEach((k) => {
 | 
				
			||||||
 | 
					              const v = options.styles[k];
 | 
				
			||||||
 | 
					              typeof v !== "undefined" && (element.style[k] = v);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (
 | 
				
			||||||
 | 
					            options.directAttributes &&
 | 
				
			||||||
 | 
					            Object.keys(options.directAttributes).length
 | 
				
			||||||
 | 
					          ) {
 | 
				
			||||||
 | 
					            Object.keys(options.directAttributes).forEach((k) => {
 | 
				
			||||||
 | 
					              const v = options.directAttributes[k];
 | 
				
			||||||
 | 
					              typeof v !== "undefined" && (element[k] = v);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (options.attributes && Object.keys(options.attributes).length) {
 | 
				
			||||||
 | 
					            Object.keys(options.attributes).forEach((k) => {
 | 
				
			||||||
 | 
					              const v = options.attributes[k];
 | 
				
			||||||
 | 
					              typeof v !== "undefined" && element.setAttribute(k, String(v));
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (options.listeners?.length) {
 | 
				
			||||||
 | 
					            options.listeners.forEach(([type, cbk, option]) => {
 | 
				
			||||||
 | 
					              typeof cbk !== "undefined" &&
 | 
				
			||||||
 | 
					                element.addEventListener(type, cbk, option);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (options.subElementOptions?.length) {
 | 
				
			||||||
 | 
					          const subElements = options.subElementOptions
 | 
				
			||||||
 | 
					            .map((_options) => this.UI.creatElementsFromJSON(doc, _options))
 | 
				
			||||||
 | 
					            .filter((e) => e);
 | 
				
			||||||
 | 
					          element.append(...subElements);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return element;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      defaultMenuPopupSelectors: {
 | 
				
			||||||
 | 
					        menuFile: "#menu_FilePopup",
 | 
				
			||||||
 | 
					        menuEdit: "#menu_EditPopup",
 | 
				
			||||||
 | 
					        menuView: "#menu_viewPopup",
 | 
				
			||||||
 | 
					        menuGo: "#menu_goPopup",
 | 
				
			||||||
 | 
					        menuTools: "#menu_ToolsPopup",
 | 
				
			||||||
 | 
					        menuHelp: "#menu_HelpPopup",
 | 
				
			||||||
 | 
					        collection: "#zotero-collectionmenu",
 | 
				
			||||||
 | 
					        item: "#zotero-itemmenu",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      insertMenuItem: (
 | 
				
			||||||
 | 
					        menuPopup: XUL.Menupopup | string,
 | 
				
			||||||
 | 
					        options: MenuitemOptions,
 | 
				
			||||||
 | 
					        insertPosition: "before" | "after" = "after",
 | 
				
			||||||
 | 
					        anchorElement: XUL.Element = undefined
 | 
				
			||||||
 | 
					      ) => {
 | 
				
			||||||
 | 
					        const Zotero = this.Compat.getZotero();
 | 
				
			||||||
 | 
					        let popup: XUL.Menupopup;
 | 
				
			||||||
 | 
					        if (typeof menuPopup === "string") {
 | 
				
			||||||
 | 
					          if (
 | 
				
			||||||
 | 
					            !Object.keys(this.UI.defaultMenuPopupSelectors).includes(menuPopup)
 | 
				
			||||||
 | 
					          ) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            popup = (Zotero.getMainWindow() as Window).document.querySelector(
 | 
				
			||||||
 | 
					              this.UI.defaultMenuPopupSelectors[menuPopup]
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          popup = menuPopup;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!popup) {
 | 
				
			||||||
 | 
					          return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const doc: Document = popup.ownerDocument;
 | 
				
			||||||
 | 
					        const generateElementOptions = (
 | 
				
			||||||
 | 
					          menuitemOption: MenuitemOptions
 | 
				
			||||||
 | 
					        ): ElementOptions => {
 | 
				
			||||||
 | 
					          let elementOption: ElementOptions = {
 | 
				
			||||||
 | 
					            tag: menuitemOption.tag,
 | 
				
			||||||
 | 
					            id: menuitemOption.id,
 | 
				
			||||||
 | 
					            namespace: "xul",
 | 
				
			||||||
 | 
					            attributes: {
 | 
				
			||||||
 | 
					              label: menuitemOption.label,
 | 
				
			||||||
 | 
					              hidden: Boolean(menuitemOption.hidden),
 | 
				
			||||||
 | 
					              disaled: Boolean(menuitemOption.disabled),
 | 
				
			||||||
 | 
					              class: menuitemOption.class || "",
 | 
				
			||||||
 | 
					              oncommand: menuitemOption.oncommand,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            styles: menuitemOption.styles || {},
 | 
				
			||||||
 | 
					            listeners: [["command", menuitemOption.commandListener]],
 | 
				
			||||||
 | 
					            subElementOptions: [],
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					          if (menuitemOption.icon) {
 | 
				
			||||||
 | 
					            elementOption.attributes["class"] += " menuitem-iconic";
 | 
				
			||||||
 | 
					            elementOption.styles[
 | 
				
			||||||
 | 
					              "list-style-image"
 | 
				
			||||||
 | 
					            ] = `url(${menuitemOption.icon})`;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (menuitemOption.tag === "menu") {
 | 
				
			||||||
 | 
					            elementOption.subElementOptions.push({
 | 
				
			||||||
 | 
					              tag: "menupopup",
 | 
				
			||||||
 | 
					              id: menuitemOption.popupId,
 | 
				
			||||||
 | 
					              namespace: "xul",
 | 
				
			||||||
 | 
					              attributes: { onpopupshowing: menuitemOption.onpopupshowing },
 | 
				
			||||||
 | 
					              subElementOptions: menuitemOption.subElementOptions.map(
 | 
				
			||||||
 | 
					                generateElementOptions
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return elementOption;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        const menuItem = this.UI.creatElementsFromJSON(
 | 
				
			||||||
 | 
					          doc,
 | 
				
			||||||
 | 
					          generateElementOptions(options)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        if (!anchorElement) {
 | 
				
			||||||
 | 
					          anchorElement = (
 | 
				
			||||||
 | 
					            insertPosition === "after"
 | 
				
			||||||
 | 
					              ? popup.lastElementChild
 | 
				
			||||||
 | 
					              : popup.firstElementChild
 | 
				
			||||||
 | 
					          ) as XUL.Element;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        anchorElement[insertPosition](menuItem);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CopyHelper {
 | 
				
			||||||
 | 
					  private transferable: any;
 | 
				
			||||||
 | 
					  private clipboardService: any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    this.transferable = Components.classes[
 | 
				
			||||||
 | 
					      "@mozilla.org/widget/transferable;1"
 | 
				
			||||||
 | 
					    ].createInstance(Components.interfaces.nsITransferable);
 | 
				
			||||||
 | 
					    this.clipboardService = Components.classes[
 | 
				
			||||||
 | 
					      "@mozilla.org/widget/clipboard;1"
 | 
				
			||||||
 | 
					    ].getService(Components.interfaces.nsIClipboard);
 | 
				
			||||||
 | 
					    this.transferable.init(null);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public addText(source: string, type: "text/html" | "text/unicode") {
 | 
				
			||||||
 | 
					    const str = Components.classes[
 | 
				
			||||||
 | 
					      "@mozilla.org/supports-string;1"
 | 
				
			||||||
 | 
					    ].createInstance(Components.interfaces.nsISupportsString);
 | 
				
			||||||
 | 
					    str.data = source;
 | 
				
			||||||
 | 
					    this.transferable.addDataFlavor(type);
 | 
				
			||||||
 | 
					    this.transferable.setTransferData(type, str, source.length * 2);
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public addImage(source: string) {
 | 
				
			||||||
 | 
					    let parts = source.split(",");
 | 
				
			||||||
 | 
					    if (!parts[0].includes("base64")) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let mime = parts[0].match(/:(.*?);/)[1];
 | 
				
			||||||
 | 
					    let bstr = atob(parts[1]);
 | 
				
			||||||
 | 
					    let n = bstr.length;
 | 
				
			||||||
 | 
					    let u8arr = new Uint8Array(n);
 | 
				
			||||||
 | 
					    while (n--) {
 | 
				
			||||||
 | 
					      u8arr[n] = bstr.charCodeAt(n);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let imgTools = Components.classes["@mozilla.org/image/tools;1"].getService(
 | 
				
			||||||
 | 
					      Components.interfaces.imgITools
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    let imgPtr = Components.classes[
 | 
				
			||||||
 | 
					      "@mozilla.org/supports-interface-pointer;1"
 | 
				
			||||||
 | 
					    ].createInstance(Components.interfaces.nsISupportsInterfacePointer);
 | 
				
			||||||
 | 
					    imgPtr.data = imgTools.decodeImageFromArrayBuffer(u8arr.buffer, mime);
 | 
				
			||||||
 | 
					    this.transferable.addDataFlavor(mime);
 | 
				
			||||||
 | 
					    this.transferable.setTransferData(mime, imgPtr, 0);
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public copy() {
 | 
				
			||||||
 | 
					    this.clipboardService.setData(
 | 
				
			||||||
 | 
					      this.transferable,
 | 
				
			||||||
 | 
					      null,
 | 
				
			||||||
 | 
					      Components.interfaces.nsIClipboard.kGlobalClipboard
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default AddonUtils;
 | 
				
			||||||
							
								
								
									
										93
									
								
								src/views.ts
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								src/views.ts
									
									
									
									
									
								
							@ -1,10 +1,9 @@
 | 
				
			|||||||
import { Addon } from "./addon";
 | 
					import Addon from "./addon";
 | 
				
			||||||
import AddonModule from "./module";
 | 
					import AddonModule from "./module";
 | 
				
			||||||
const { addonRef } = require("../package.json");
 | 
					const { addonRef, addonID } = require("../package.json");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AddonViews extends AddonModule {
 | 
					class AddonViews extends AddonModule {
 | 
				
			||||||
  // You can store some element in the object attributes
 | 
					  // You can store some element in the object attributes
 | 
				
			||||||
  private testButton: XUL.Button;
 | 
					 | 
				
			||||||
  private progressWindowIcon: object;
 | 
					  private progressWindowIcon: object;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(parent: Addon) {
 | 
					  constructor(parent: Addon) {
 | 
				
			||||||
@ -12,31 +11,83 @@ class AddonViews extends AddonModule {
 | 
				
			|||||||
    this.progressWindowIcon = {
 | 
					    this.progressWindowIcon = {
 | 
				
			||||||
      success: "chrome://zotero/skin/tick.png",
 | 
					      success: "chrome://zotero/skin/tick.png",
 | 
				
			||||||
      fail: "chrome://zotero/skin/cross.png",
 | 
					      fail: "chrome://zotero/skin/cross.png",
 | 
				
			||||||
      default: `chrome://${addonRef}/skin/favicon.png`,
 | 
					      default: `chrome://${addonRef}/content/icons/favicon.png`,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public initViews(_Zotero) {
 | 
					  public initViews() {
 | 
				
			||||||
 | 
					    const Zotero = this._Addon.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");
 | 
					    this._Addon.Utils.Tool.log("Initializing UI");
 | 
				
			||||||
    const _window: Window = _Zotero.getMainWindow();
 | 
					    const menuIcon =
 | 
				
			||||||
    const menuitem = _window.document.createElement("menuitem");
 | 
					      'url("chrome://addontemplate/content/icons/favicon@0.5x.png")';
 | 
				
			||||||
    menuitem.id = "zotero-itemmenu-addontemplate-test";
 | 
					    // item menuitem with icon
 | 
				
			||||||
    menuitem.setAttribute("label", "Addon Template");
 | 
					    this._Addon.Utils.UI.insertMenuItem("item", {
 | 
				
			||||||
    menuitem.setAttribute("oncommand", "alert('Hello World!')");
 | 
					      tag: "menuitem",
 | 
				
			||||||
    menuitem.className = "menuitem-iconic";
 | 
					      id: "zotero-itemmenu-addontemplate-test",
 | 
				
			||||||
    menuitem.style["list-style-image"] =
 | 
					      label: "Addon Template: Menuitem",
 | 
				
			||||||
      "url('chrome://addontemplate/skin/favicon@0.5x.png')";
 | 
					      oncommand: "alert('Hello World! Default Menuitem.')",
 | 
				
			||||||
    _window.document.querySelector("#zotero-itemmenu").appendChild(menuitem);
 | 
					      icon: menuIcon,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    // item menupopup with sub-menuitems
 | 
				
			||||||
 | 
					    this._Addon.Utils.UI.insertMenuItem(
 | 
				
			||||||
 | 
					      "item",
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        tag: "menu",
 | 
				
			||||||
 | 
					        label: "Addon Template: Menupopup",
 | 
				
			||||||
 | 
					        subElementOptions: [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            tag: "menuitem",
 | 
				
			||||||
 | 
					            label: "Addon Template",
 | 
				
			||||||
 | 
					            oncommand: "alert('Hello World! Sub Menuitem.')",
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "before",
 | 
				
			||||||
 | 
					      this._Addon.Zotero.getMainWindow().document.querySelector(
 | 
				
			||||||
 | 
					        "#zotero-itemmenu-addontemplate-test"
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    this._Addon.Utils.UI.insertMenuItem("menuFile", {
 | 
				
			||||||
 | 
					      tag: "menuseparator",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    // menu->File menuitem
 | 
				
			||||||
 | 
					    this._Addon.Utils.UI.insertMenuItem("menuFile", {
 | 
				
			||||||
 | 
					      tag: "menuitem",
 | 
				
			||||||
 | 
					      label: "Addon Template: File Menuitem",
 | 
				
			||||||
 | 
					      oncommand: "alert('Hello World! File Menuitem.')",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public unInitViews(_Zotero) {
 | 
					  public initPrefs() {
 | 
				
			||||||
    console.log("Uninitializing UI");
 | 
					    const Zotero = this._Addon.Zotero;
 | 
				
			||||||
    const _window: Window = _Zotero.getMainWindow();
 | 
					    this._Addon.Utils.Tool.log(this._Addon.rootURI);
 | 
				
			||||||
    _window.document
 | 
					    const prefOptions = {
 | 
				
			||||||
      .querySelector("#zotero-itemmenu-addontemplate-test")
 | 
					      pluginID: addonID,
 | 
				
			||||||
      ?.remove();
 | 
					      src: this._Addon.rootURI + "chrome/content/preferences.xhtml",
 | 
				
			||||||
 | 
					      label: "Template",
 | 
				
			||||||
 | 
					      image: `chrome://${addonRef}/content/icons/favicon.png`,
 | 
				
			||||||
 | 
					      extraDTD: [`chrome://${addonRef}/locale/overlay.dtd`],
 | 
				
			||||||
 | 
					      defaultXUL: true,
 | 
				
			||||||
 | 
					      onload: (win: Window) => {
 | 
				
			||||||
 | 
					        this._Addon.prefs.initPreferences(win);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    if (this._Addon.Utils.Compat.isZotero7()) {
 | 
				
			||||||
 | 
					      Zotero.PreferencePanes.register(prefOptions);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this._Addon.Utils.Compat.registerPrefPane(prefOptions);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public unInitViews() {
 | 
				
			||||||
 | 
					    const Zotero = this._Addon.Zotero;
 | 
				
			||||||
 | 
					    this._Addon.Utils.Tool.log("Uninitializing UI");
 | 
				
			||||||
 | 
					    this._Addon.Utils.UI.removeAddonElements();
 | 
				
			||||||
 | 
					    if (!this._Addon.Utils.Compat.isZotero7()) {
 | 
				
			||||||
 | 
					      this._Addon.Utils.Compat.unregisterPrefPane();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public showProgressWindow(
 | 
					  public showProgressWindow(
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@
 | 
				
			|||||||
  "compilerOptions": {
 | 
					  "compilerOptions": {
 | 
				
			||||||
    "module": "commonjs",
 | 
					    "module": "commonjs",
 | 
				
			||||||
    "target": "es6",
 | 
					    "target": "es6",
 | 
				
			||||||
 | 
					    "resolveJsonModule": true
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "include": [
 | 
					  "include": [
 | 
				
			||||||
    "src",
 | 
					    "src",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										108
									
								
								typing/global.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								typing/global.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					declare interface ZoteroCompat {
 | 
				
			||||||
 | 
					  getZotero: () => _ZoteroConstructable;
 | 
				
			||||||
 | 
					  isZotero7: () => boolean;
 | 
				
			||||||
 | 
					  getDOMParser: () => DOMParser;
 | 
				
			||||||
 | 
					  createXULElement: (doc: Document, type: string) => XUL.Element;
 | 
				
			||||||
 | 
					  parseXHTMLToFragment: (
 | 
				
			||||||
 | 
					    str: string,
 | 
				
			||||||
 | 
					    entities: string[],
 | 
				
			||||||
 | 
					    defaultXUL?: boolean
 | 
				
			||||||
 | 
					  ) => DocumentFragment;
 | 
				
			||||||
 | 
					  prefPaneCache: { win: Window; listeners: any[]; ids: string[] };
 | 
				
			||||||
 | 
					  registerPrefPane: (options: PrefPaneOptions) => void;
 | 
				
			||||||
 | 
					  unregisterPrefPane: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare interface ZoteroTool {
 | 
				
			||||||
 | 
					  getCopyHelper: () => CopyHelper;
 | 
				
			||||||
 | 
					  openFilePicker: (
 | 
				
			||||||
 | 
					    title: string,
 | 
				
			||||||
 | 
					    mode: "open" | "save" | "folder",
 | 
				
			||||||
 | 
					    filters?: [string, string][],
 | 
				
			||||||
 | 
					    suggestion?: string
 | 
				
			||||||
 | 
					  ) => Promise<string>;
 | 
				
			||||||
 | 
					  log: (...data: any[]) => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare interface ZoteroUI {
 | 
				
			||||||
 | 
					  addonElements: Element[];
 | 
				
			||||||
 | 
					  createElement: (
 | 
				
			||||||
 | 
					    doc: Document,
 | 
				
			||||||
 | 
					    tagName: string,
 | 
				
			||||||
 | 
					    namespace: "html" | "svg" | "xul"
 | 
				
			||||||
 | 
					  ) => XUL.Element | DocumentFragment | HTMLElement | SVGAElement;
 | 
				
			||||||
 | 
					  removeAddonElements: () => void;
 | 
				
			||||||
 | 
					  creatElementsFromJSON: (
 | 
				
			||||||
 | 
					    doc: Document,
 | 
				
			||||||
 | 
					    options: ElementOptions
 | 
				
			||||||
 | 
					  ) => XUL.Element | DocumentFragment | HTMLElement | SVGAElement;
 | 
				
			||||||
 | 
					  defaultMenuPopupSelectors: {
 | 
				
			||||||
 | 
					    [key: string]: string;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  insertMenuItem: (
 | 
				
			||||||
 | 
					    menuPopup: XUL.Menupopup | string,
 | 
				
			||||||
 | 
					    options: MenuitemOptions,
 | 
				
			||||||
 | 
					    insertPosition?: "before" | "after",
 | 
				
			||||||
 | 
					    anchorElement?: XUL.Element
 | 
				
			||||||
 | 
					  ) => boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare interface ElementOptions {
 | 
				
			||||||
 | 
					  tag: string;
 | 
				
			||||||
 | 
					  id?: string;
 | 
				
			||||||
 | 
					  namespace?: "html" | "svg" | "xul";
 | 
				
			||||||
 | 
					  styles?: { [key: string]: string };
 | 
				
			||||||
 | 
					  directAttributes?: { [key: string]: string | boolean | number };
 | 
				
			||||||
 | 
					  attributes?: { [key: string]: string | boolean | number };
 | 
				
			||||||
 | 
					  listeners?: Array<
 | 
				
			||||||
 | 
					    | [
 | 
				
			||||||
 | 
					        string,
 | 
				
			||||||
 | 
					        EventListenerOrEventListenerObject,
 | 
				
			||||||
 | 
					        boolean | AddEventListenerOptions
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    | [string, EventListenerOrEventListenerObject]
 | 
				
			||||||
 | 
					  >;
 | 
				
			||||||
 | 
					  checkExistanceParent?: HTMLElement;
 | 
				
			||||||
 | 
					  ignoreIfExists?: boolean;
 | 
				
			||||||
 | 
					  removeIfExists?: boolean;
 | 
				
			||||||
 | 
					  customCheck?: () => boolean;
 | 
				
			||||||
 | 
					  subElementOptions?: Array<ElementOptions>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare interface MenuitemOptions {
 | 
				
			||||||
 | 
					  tag: "menuitem" | "menu" | "menuseparator";
 | 
				
			||||||
 | 
					  id?: string;
 | 
				
			||||||
 | 
					  label?: string;
 | 
				
			||||||
 | 
					  // data url (chrome://xxx.png) or base64 url (data:image/png;base64,xxx)
 | 
				
			||||||
 | 
					  icon?: string;
 | 
				
			||||||
 | 
					  class?: string;
 | 
				
			||||||
 | 
					  styles?: { [key: string]: string };
 | 
				
			||||||
 | 
					  hidden?: boolean;
 | 
				
			||||||
 | 
					  disabled?: boolean;
 | 
				
			||||||
 | 
					  oncommand?: string;
 | 
				
			||||||
 | 
					  commandListener?: EventListenerOrEventListenerObject;
 | 
				
			||||||
 | 
					  // Attributes below are used when type === "menu"
 | 
				
			||||||
 | 
					  popupId?: string;
 | 
				
			||||||
 | 
					  onpopupshowing?: string;
 | 
				
			||||||
 | 
					  subElementOptions?: Array<MenuitemOptions>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare interface PrefPaneOptions {
 | 
				
			||||||
 | 
					  pluginID: string;
 | 
				
			||||||
 | 
					  src: string;
 | 
				
			||||||
 | 
					  id?: string;
 | 
				
			||||||
 | 
					  parent?: string;
 | 
				
			||||||
 | 
					  label?: string;
 | 
				
			||||||
 | 
					  image?: string;
 | 
				
			||||||
 | 
					  extraDTD?: string[];
 | 
				
			||||||
 | 
					  scripts?: string[];
 | 
				
			||||||
 | 
					  defaultXUL?: boolean;
 | 
				
			||||||
 | 
					  // Only for Zotero 6
 | 
				
			||||||
 | 
					  onload?: (win: Window) => any;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare class CopyHelper {
 | 
				
			||||||
 | 
					  addText: (source: string, type: "text/html" | "text/unicode") => CopyHelper;
 | 
				
			||||||
 | 
					  addImage: (source: string) => CopyHelper;
 | 
				
			||||||
 | 
					  copy: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										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"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								update-template.rdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								update-template.rdf
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 | 
				
			||||||
 | 
					  <rdf:Description rdf:about="urn:mozilla:extension:__addonID__">
 | 
				
			||||||
 | 
					    <em:updates>
 | 
				
			||||||
 | 
					      <rdf:Seq>
 | 
				
			||||||
 | 
					        <rdf:li>
 | 
				
			||||||
 | 
					          <rdf:Description>
 | 
				
			||||||
 | 
					            <em:version>__buildVersion__</em:version>
 | 
				
			||||||
 | 
					            <em:targetApplication>
 | 
				
			||||||
 | 
					              <rdf:Description>
 | 
				
			||||||
 | 
					                <em:id>zotero@chnm.gmu.edu</em:id>
 | 
				
			||||||
 | 
					                <em:minVersion>6.999</em:minVersion>
 | 
				
			||||||
 | 
					                <em:maxVersion>*</em:maxVersion>
 | 
				
			||||||
 | 
					                <em:updateLink>__releasepage__</em:updateLink>
 | 
				
			||||||
 | 
					              </rdf:Description>
 | 
				
			||||||
 | 
					            </em:targetApplication>
 | 
				
			||||||
 | 
					            <em:targetApplication>
 | 
				
			||||||
 | 
					              <rdf:Description>
 | 
				
			||||||
 | 
					                <em:id>juris-m@juris-m.github.io</em:id>
 | 
				
			||||||
 | 
					                <em:minVersion>6.999</em:minVersion>
 | 
				
			||||||
 | 
					                <em:maxVersion>*</em:maxVersion>
 | 
				
			||||||
 | 
					                <em:updateLink>__releasepage__</em:updateLink>
 | 
				
			||||||
 | 
					              </rdf:Description>
 | 
				
			||||||
 | 
					            </em:targetApplication>
 | 
				
			||||||
 | 
					          </rdf:Description>
 | 
				
			||||||
 | 
					        </rdf:li>
 | 
				
			||||||
 | 
					      </rdf:Seq>
 | 
				
			||||||
 | 
					    </em:updates>
 | 
				
			||||||
 | 
					  </rdf:Description>
 | 
				
			||||||
 | 
					</rdf:RDF>
 | 
				
			||||||
							
								
								
									
										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"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										10
									
								
								update.rdf
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								update.rdf
									
									
									
									
									
								
							@ -1,6 +1,6 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 | 
					<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 | 
				
			||||||
  <rdf:Description rdf:about="urn:mozilla:extension:plugintemplate@euclpts.com">
 | 
					  <rdf:Description rdf:about="urn:mozilla:extension:addontemplate@euclpts.com">
 | 
				
			||||||
    <em:updates>
 | 
					    <em:updates>
 | 
				
			||||||
      <rdf:Seq>
 | 
					      <rdf:Seq>
 | 
				
			||||||
        <rdf:li>
 | 
					        <rdf:li>
 | 
				
			||||||
@ -9,17 +9,17 @@
 | 
				
			|||||||
            <em:targetApplication>
 | 
					            <em:targetApplication>
 | 
				
			||||||
              <rdf:Description>
 | 
					              <rdf:Description>
 | 
				
			||||||
                <em:id>zotero@chnm.gmu.edu</em:id>
 | 
					                <em:id>zotero@chnm.gmu.edu</em:id>
 | 
				
			||||||
                <em:minVersion>5.0</em:minVersion>
 | 
					                <em:minVersion>6.999</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>
 | 
				
			||||||
              <rdf:Description>
 | 
					              <rdf:Description>
 | 
				
			||||||
                <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>6.999</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>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user