From 27f171e30f788271e4709e002260100f3c641a64 Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Sun, 27 Aug 2023 11:43:58 +0800 Subject: [PATCH] fix: preview bug with z7 beta 28+ fix: #120 fix: #117 fix: #115 fix: #113 fix: #110 --- addon/bootstrap.js | 85 +++------------------------- addon/chrome/content/previewPDF.html | 75 ++++++++++++------------ src/addon.ts | 46 ++------------- src/hooks.ts | 25 +++++--- src/index.ts | 21 ++++--- src/modules/split.ts | 9 ++- src/utils/ztoolkit.ts | 50 ++++++++++++++++ typings/global.d.ts | 12 +++- 8 files changed, 143 insertions(+), 180 deletions(-) create mode 100644 src/utils/ztoolkit.ts diff --git a/addon/bootstrap.js b/addon/bootstrap.js index 00e7758..0bbfc06 100644 --- a/addon/bootstrap.js +++ b/addon/bootstrap.js @@ -5,80 +5,11 @@ * [2] https://www.zotero.org/support/dev/zotero_7_for_developers */ -if (typeof Zotero == "undefined") { - var Zotero; -} - var chromeHandle; -var windowListener; - -// 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() { - await new Promise(async (resolve) => { - if (typeof Zotero != "undefined") { - resolve(); - } - - const { Services } = ChromeUtils.import( - "resource://gre/modules/Services.jsm", - ); - const windows = Services.wm.getEnumerator("navigator:browser"); - let found = false; - while (windows.hasMoreElements()) { - let win = windows.getNext(); - if (win.Zotero) { - Zotero = win.Zotero; - found = true; - resolve(); - break; - } - } - windowListener = { - 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 (!found && domWindow.Zotero) { - Zotero = domWindow.Zotero; - resolve(); - } else if ( - domWindow.location.href === - "chrome://zotero/content/zoteroPane.xhtml" - ) { - Zotero.__addonInstance__?.hooks.onMainWindowLoad(domWindow); - domWindow.addEventListener("unload", function () { - domWindow.removeEventListener( - "unload", - arguments.callee, - false, - ); - Zotero.__addonInstance__?.hooks.onMainWindowUnload(domWindow); - }); - } - }, - false, - ); - }, - }; - Services.wm.addListener(windowListener); - }); -} - function install(data, reason) {} async function startup({ id, version, resourceURI, rootURI }, reason) { - await waitForZotero(); await Zotero.initializationPromise; // String 'rootURI' introduced in Zotero 7 @@ -111,18 +42,20 @@ async function startup({ id, version, resourceURI, rootURI }, reason) { ); } +async function onMainWindowLoad({ window }, reason) { + Zotero.__addonInstance__?.hooks.onMainWindowLoad(window); +} + +async function onMainWindowUnload({ window }, reason) { + Zotero.__addonInstance__?.hooks.onMainWindowUnload(window); +} + function shutdown({ id, version, resourceURI, rootURI }, reason) { if (reason === APP_SHUTDOWN) { return; } - Services.wm.removeListener(windowListener); - if (typeof Zotero === "undefined") { - Zotero = Components.classes["@zotero.org/Zotero;1"].getService( - Components.interfaces.nsISupports, - ).wrappedJSObject; - } - Zotero.__addonInstance__.hooks.onShutdown(); + Zotero.__addonInstance__?.hooks.onShutdown(); Cc["@mozilla.org/intl/stringbundle;1"] .getService(Components.interfaces.nsIStringBundleService) diff --git a/addon/chrome/content/previewPDF.html b/addon/chrome/content/previewPDF.html index 013cbcd..8f14bdf 100644 --- a/addon/chrome/content/previewPDF.html +++ b/addon/chrome/content/previewPDF.html @@ -9,6 +9,10 @@ - + + - +
-
-
-
-
-
- - - -
-
-
+
+
+
+ + +
+
@@ -498,7 +495,7 @@ cachedData.pdfDocument && cachedData.pdfDocument.destroy(); cachedData.pdfDocument = await pdfjsLib.getDocument({ data: await getBuffer(e.data.itemID), - cMapUrl: "resource://zotero/pdf-reader/cmaps/", + cMapUrl: "resource://zotero/reader/pdf/build/cmaps/", cMapPacked: true, }).promise; cachedData.itemID = e.data.itemID; @@ -526,7 +523,7 @@ var MAX_IMAGE_SIZE = 1024 * 1024; pdfjsLib.GlobalWorkerOptions.workerSrc = - "resource://zotero/pdf-reader/pdf.worker.js"; + "resource://zotero/reader/pdf/build/pdf.worker.js"; document .querySelector("#zoomOut") diff --git a/src/addon.ts b/src/addon.ts index 6426934..4a5c451 100644 --- a/src/addon.ts +++ b/src/addon.ts @@ -1,6 +1,9 @@ -import ZoteroToolkit from "zotero-plugin-toolkit/dist/index"; import { ColumnOptions } from "zotero-plugin-toolkit/dist/helpers/virtualizedTable"; import hooks from "./hooks"; +import { getPref, setPref } from "./utils/prefs"; +import { createZToolkit } from "./utils/ztoolkit"; +import { DialogHelper } from "zotero-plugin-toolkit/dist/helpers/dialog"; +import { InfoPaneMode, PreviewType } from "./utils/type"; class Addon { public data: { @@ -8,7 +11,7 @@ class Addon { // Env type, see build.js env: "development" | "production"; // ztoolkit: MyToolkit; - ztoolkit: ZoteroToolkit; + ztoolkit: ZToolkit; locale?: { current: any; }; @@ -41,7 +44,7 @@ class Addon { alive: true, env: __env__, // ztoolkit: new MyToolkit(), - ztoolkit: new ZoteroToolkit(), + ztoolkit: createZToolkit(), state: { splitCollapsed: false, get splitPosition() { @@ -68,43 +71,6 @@ class Addon { } } -/** - * Alternatively, import toolkit modules you use to minify the plugin size. - * - * Steps to replace the default `ztoolkit: ZoteroToolkit` with your `ztoolkit: MyToolkit`: - * - * 1. Uncomment this file's line 30: `ztoolkit: new MyToolkit(),` - * and comment line 31: `ztoolkit: new ZoteroToolkit(),`. - * 2. Uncomment this file's line 10: `ztoolkit: MyToolkit;` in this file - * and comment line 11: `ztoolkit: ZoteroToolkit;`. - * 3. Uncomment `./typing/global.d.ts` line 12: `declare const ztoolkit: import("../src/addon").MyToolkit;` - * and comment line 13: `declare const ztoolkit: import("zotero-plugin-toolkit").ZoteroToolkit;`. - * - * You can now add the modules under the `MyToolkit` class. - */ - -import { BasicTool, unregister } from "zotero-plugin-toolkit/dist/basic"; -import { UITool } from "zotero-plugin-toolkit/dist/tools/ui"; -import { PreferencePaneManager } from "zotero-plugin-toolkit/dist/managers/preferencePane"; -import { DialogHelper } from "zotero-plugin-toolkit/dist/helpers/dialog"; -import { getPref, setPref } from "./utils/prefs"; -import { InfoPaneMode, PreviewType } from "./utils/type"; - -export class MyToolkit extends BasicTool { - UI: UITool; - PreferencePane: PreferencePaneManager; - - constructor() { - super(); - this.UI = new UITool(this); - this.PreferencePane = new PreferencePaneManager(this); - } - - unregisterAll() { - unregister(this); - } -} - export default Addon; const getResolvedPromise = () => { diff --git a/src/hooks.ts b/src/hooks.ts index e18270d..0a2fc94 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -21,19 +21,28 @@ async function onStartup() { Zotero.uiReadyPromise, ]); initLocale(); - ztoolkit.ProgressWindow.setIconURI( - "default", - `chrome://${config.addonRef}/content/icons/favicon.png`, - ); registerPrefPane(); - await waitUtilAsync(() => { - const win = ztoolkit.getGlobal("window"); - return win && win.document.readyState === "complete"; - }); await onMainWindowLoad(ztoolkit.getGlobal("window")); } async function onMainWindowLoad(win: Window): Promise { + await new Promise((resolve) => { + if (win.document.readyState !== "complete") { + win.document.addEventListener("readystatechange", () => { + if (win.document.readyState === "complete") { + resolve(void 0); + } + }); + } + resolve(void 0); + }); + + await Promise.all([ + Zotero.initializationPromise, + Zotero.unlockPromise, + Zotero.uiReadyPromise, + ]); + try { const doc = win.document; registerPreviewTab(); diff --git a/src/index.ts b/src/index.ts index a11f5b8..629df49 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,25 +11,24 @@ if (!basicTool.getGlobal("Zotero")[config.addonInstance]) { defineGlobal("document"); defineGlobal("ZoteroPane"); defineGlobal("Zotero_Tabs"); + defineGlobal("ZoteroContextPane"); + defineGlobal("crypto"); + defineGlobal("TextEncoder"); _globalThis.addon = new Addon(); - _globalThis.ztoolkit = addon.data.ztoolkit; - ztoolkit.basicOptions.log.prefix = `[${config.addonName}]`; - ztoolkit.basicOptions.log.disableConsole = addon.data.env === "production"; - ztoolkit.UI.basicOptions.ui.enableElementJSONLog = - addon.data.env === "development"; - ztoolkit.UI.basicOptions.ui.enableElementDOMLog = - addon.data.env === "development"; - ztoolkit.basicOptions.debug.disableDebugBridgePassword = - addon.data.env === "development"; + defineGlobal("ztoolkit", () => { + return _globalThis.addon.data.ztoolkit; + }); Zotero[config.addonInstance] = addon; // Trigger addon hook for initialization addon.hooks.onStartup(); } -function defineGlobal(name: Parameters[0]) { +function defineGlobal(name: Parameters[0]): void; +function defineGlobal(name: string, getter: () => any): void; +function defineGlobal(name: string, getter?: () => any) { Object.defineProperty(_globalThis, name, { get() { - return basicTool.getGlobal(name); + return getter ? getter() : basicTool.getGlobal(name); }, }); } diff --git a/src/modules/split.ts b/src/modules/split.ts index 2dc499d..5f3a7e0 100644 --- a/src/modules/split.ts +++ b/src/modules/split.ts @@ -4,6 +4,8 @@ import { waitUtilAsync } from "../utils/wait"; export { registerSplit, updateSplit, setSplitCollapsed }; +const splitterHeight = 3; + function getSplitterId(type: PreviewType, position: "before" | "after") { return `${getContainerId(type, position)}-splitter`; } @@ -54,7 +56,7 @@ async function registerSplit(type: PreviewType) { collapse: "before", }, styles: { - height: "3px", + height: `${splitterHeight}px`, }, listeners: [ { @@ -89,7 +91,7 @@ async function registerSplit(type: PreviewType) { collapse: "after", }, styles: { - height: "3px", + height: `${splitterHeight}px`, }, listeners: [ { @@ -203,7 +205,8 @@ function updateSplit(type: PreviewType) { Array.from(splitContainer.parentElement?.children || []).reduce( (acc, cur) => acc + cur.clientHeight, 0, - ); + ) + + splitterHeight; // Make sure the height is not too small if (maxHeight < 1) { maxHeight = 300; diff --git a/src/utils/ztoolkit.ts b/src/utils/ztoolkit.ts new file mode 100644 index 0000000..50a2566 --- /dev/null +++ b/src/utils/ztoolkit.ts @@ -0,0 +1,50 @@ +import { config } from "../../package.json"; + +export { createZToolkit }; + +function createZToolkit() { + // const _ztoolkit = new ZoteroToolkit(); + /** + * Alternatively, import toolkit modules you use to minify the plugin size. + * You can add the modules under the `MyToolkit` class below and uncomment the following line. + */ + const _ztoolkit = new MyToolkit(); + initZToolkit(_ztoolkit); + return _ztoolkit; +} + +function initZToolkit(_ztoolkit: ReturnType) { + const env = __env__; + _ztoolkit.basicOptions.log.prefix = `[${config.addonName}]`; + _ztoolkit.basicOptions.log.disableConsole = env === "production"; + _ztoolkit.UI.basicOptions.ui.enableElementJSONLog = __env__ === "development"; + _ztoolkit.UI.basicOptions.ui.enableElementDOMLog = __env__ === "development"; + _ztoolkit.basicOptions.debug.disableDebugBridgePassword = + __env__ === "development"; + _ztoolkit.basicOptions.api.pluginID = config.addonID; +} + +import { BasicTool, unregister } from "zotero-plugin-toolkit/dist/basic"; +import { ToolkitGlobal } from "zotero-plugin-toolkit/dist/managers/toolkitGlobal"; +import { UITool } from "zotero-plugin-toolkit/dist/tools/ui"; +import { PreferencePaneManager } from "zotero-plugin-toolkit/dist/managers/preferencePane"; +import { LibraryTabPanelManager } from "zotero-plugin-toolkit/dist/managers/libraryTabPanel"; + +export class MyToolkit extends BasicTool { + Global: typeof ToolkitGlobal; + UI: UITool; + PreferencePane: PreferencePaneManager; + LibraryTabPanel: LibraryTabPanelManager; + + constructor() { + super(); + this.Global = ToolkitGlobal; + this.UI = new UITool(this); + this.PreferencePane = new PreferencePaneManager(this); + this.LibraryTabPanel = new LibraryTabPanelManager(this); + } + + unregisterAll() { + unregister(this); + } +} diff --git a/typings/global.d.ts b/typings/global.d.ts index 8906d0b..51132dc 100644 --- a/typings/global.d.ts +++ b/typings/global.d.ts @@ -3,14 +3,20 @@ declare const _globalThis: { Zotero: _ZoteroTypes.Zotero; ZoteroPane: _ZoteroTypes.ZoteroPane; Zotero_Tabs: typeof Zotero_Tabs; + ZoteroContextPane: typeof ZoteroContextPane; window: Window; document: Document; - ztoolkit: typeof ztoolkit; + crypto: Crypto; + TextEncoder: typeof TextEncoder; + ztoolkit: ZToolkit; addon: typeof addon; }; -// declare const ztoolkit: import("../src/addon").MyToolkit; -declare const ztoolkit: import("zotero-plugin-toolkit").ZoteroToolkit; +declare type ZToolkit = ReturnType< + typeof import("../src/utils/ztoolkit").createZToolkit +>; + +declare const ztoolkit: ZToolkit; declare const rootURI: string;