mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-05-31 07:11:00 +02:00
Re-factor how "internal" EventBus listeners are handled in the viewer
Currently the viewer uses semi-private `EventBus.prototype.{_on, _off}` methods, to try and ensure that all internal viewer state is updated *before* any "external" listeners are invoked.
For all use-cases outside of the viewer, e.g in the integration-tests, the `EventBus.prototype.{on, off}` methods are supposed to be used instead.
Unfortunately this isn't currently enforced in any way, except (hopefully) during review, and generally speaking it's not really possible to prevent the semi-private methods being used (e.g. by third-party users).
Hence this patch adds a new `INTERNAL_EVT` property which is *not* exposed anywhere (neither in the API nor globally), and whose value is generated at build-time, that the viewer uses to mark its `EventBus` listeners are internal.
This allows us to remove the semi-private `EventBus` methods, which helps to simplify that class a little bit.
This commit is contained in:
parent
19d95c8fee
commit
74db085794
@ -116,6 +116,7 @@ const DEFINES = Object.freeze({
|
||||
WORKER_THREAD: false,
|
||||
TESTING: undefined,
|
||||
COVERAGE: undefined,
|
||||
INTERNAL_EVT: crypto.randomUUID(),
|
||||
// The main build targets:
|
||||
GENERIC: false,
|
||||
MOZCENTRAL: false,
|
||||
|
||||
@ -36,6 +36,7 @@ import {
|
||||
stopEvent,
|
||||
} from "../display_utils.js";
|
||||
import { FloatingToolbar } from "./toolbar.js";
|
||||
import { internalOpt } from "../../shared/internal_evt.js";
|
||||
|
||||
function bindEvents(obj, element, names) {
|
||||
for (const name of names) {
|
||||
@ -939,17 +940,21 @@ class AnnotationEditorUIManager {
|
||||
this.#signatureManager = signatureManager;
|
||||
this.#pdfDocument = pdfDocument;
|
||||
this._eventBus = eventBus;
|
||||
eventBus._on("editingaction", this.onEditingAction.bind(this), { signal });
|
||||
eventBus._on("pagechanging", this.onPageChanging.bind(this), { signal });
|
||||
eventBus._on("scalechanging", this.onScaleChanging.bind(this), { signal });
|
||||
eventBus._on("rotationchanging", this.onRotationChanging.bind(this), {
|
||||
signal,
|
||||
});
|
||||
eventBus._on("setpreference", this.onSetPreference.bind(this), { signal });
|
||||
eventBus._on(
|
||||
|
||||
const evtOpts = { signal, ...internalOpt };
|
||||
eventBus.on("editingaction", this.onEditingAction.bind(this), evtOpts);
|
||||
eventBus.on("pagechanging", this.onPageChanging.bind(this), evtOpts);
|
||||
eventBus.on("scalechanging", this.onScaleChanging.bind(this), evtOpts);
|
||||
eventBus.on(
|
||||
"rotationchanging",
|
||||
this.onRotationChanging.bind(this),
|
||||
evtOpts
|
||||
);
|
||||
eventBus.on("setpreference", this.onSetPreference.bind(this), evtOpts);
|
||||
eventBus.on(
|
||||
"switchannotationeditorparams",
|
||||
evt => this.updateParams(evt.type, evt.value),
|
||||
{ signal }
|
||||
evtOpts
|
||||
);
|
||||
window.addEventListener(
|
||||
"pointerdown",
|
||||
@ -1222,7 +1227,7 @@ class AnnotationEditorUIManager {
|
||||
const { resolve, promise } = Promise.withResolvers();
|
||||
const onEditorsRendered = evt => {
|
||||
if (evt.pageNumber === pageNumber) {
|
||||
this._eventBus._off("editorsrendered", onEditorsRendered);
|
||||
this._eventBus.off("editorsrendered", onEditorsRendered);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
|
||||
27
src/shared/internal_evt.js
Normal file
27
src/shared/internal_evt.js
Normal file
@ -0,0 +1,27 @@
|
||||
/* Copyright 2026 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Keep this file in sync with `web/internal_evt.js`.
|
||||
*/
|
||||
|
||||
const INTERNAL_EVT =
|
||||
typeof PDFJSDev !== "undefined" && !PDFJSDev.test("TESTING")
|
||||
? PDFJSDev.eval("INTERNAL_EVT")
|
||||
: "internalEvent";
|
||||
|
||||
const internalOpt = Object.freeze({ internal: INTERNAL_EVT });
|
||||
|
||||
export { INTERNAL_EVT, internalOpt };
|
||||
@ -3556,7 +3556,7 @@ describe("Reorganize Pages View", () => {
|
||||
await waitAndClick(page, getThumbnailSelector(2));
|
||||
|
||||
const handleMerged = await createPromise(page, resolve => {
|
||||
window.PDFViewerApplication.eventBus._on(
|
||||
window.PDFViewerApplication.eventBus.on(
|
||||
"thumbnailsloaded",
|
||||
resolve,
|
||||
{ once: true }
|
||||
@ -3628,7 +3628,7 @@ describe("Reorganize Pages View", () => {
|
||||
await waitForThumbnailVisible(page, 1);
|
||||
|
||||
const handleMerged = await createPromise(page, resolve => {
|
||||
window.PDFViewerApplication.eventBus._on(
|
||||
window.PDFViewerApplication.eventBus.on(
|
||||
"thumbnailsloaded",
|
||||
resolve,
|
||||
{ once: true }
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
import { DOMSVGFactory } from "pdfjs-lib";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
|
||||
class AltTextManager {
|
||||
#clickAC = null;
|
||||
@ -170,8 +171,9 @@ class AltTextManager {
|
||||
this.#uiManager.removeEditListeners();
|
||||
|
||||
this.#resizeAC = new AbortController();
|
||||
this.#eventBus._on("resize", this.#setPosition.bind(this), {
|
||||
this.#eventBus.on("resize", this.#setPosition.bind(this), {
|
||||
signal: this.#resizeAC.signal,
|
||||
...internalOpt,
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
@ -21,6 +21,7 @@ import {
|
||||
getRGBA,
|
||||
Util,
|
||||
} from "pdfjs-lib";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
|
||||
/**
|
||||
* @typedef {Object} AnnotationEditorParamsOptions
|
||||
@ -155,42 +156,46 @@ class AnnotationEditorParams {
|
||||
dispatchEvent("CREATE");
|
||||
});
|
||||
|
||||
eventBus._on("annotationeditorparamschanged", evt => {
|
||||
for (const [type, value] of evt.details) {
|
||||
switch (type) {
|
||||
case AnnotationEditorParamsType.FREETEXT_SIZE:
|
||||
editorFreeTextFontSize.value = value;
|
||||
break;
|
||||
case AnnotationEditorParamsType.FREETEXT_COLOR:
|
||||
editorFreeTextColor.value = value;
|
||||
break;
|
||||
case AnnotationEditorParamsType.INK_COLOR:
|
||||
updateInkColor(value);
|
||||
break;
|
||||
case AnnotationEditorParamsType.INK_THICKNESS:
|
||||
editorInkThickness.value = value;
|
||||
break;
|
||||
case AnnotationEditorParamsType.INK_OPACITY:
|
||||
updateInkOpacity(value);
|
||||
break;
|
||||
case AnnotationEditorParamsType.HIGHLIGHT_COLOR:
|
||||
eventBus.dispatch("mainhighlightcolorpickerupdatecolor", {
|
||||
source: this,
|
||||
value,
|
||||
});
|
||||
break;
|
||||
case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS:
|
||||
editorFreeHighlightThickness.value = value;
|
||||
break;
|
||||
case AnnotationEditorParamsType.HIGHLIGHT_FREE:
|
||||
editorFreeHighlightThickness.disabled = !value;
|
||||
break;
|
||||
case AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL:
|
||||
editorHighlightShowAll.setAttribute("aria-pressed", value);
|
||||
break;
|
||||
eventBus.on(
|
||||
"annotationeditorparamschanged",
|
||||
evt => {
|
||||
for (const [type, value] of evt.details) {
|
||||
switch (type) {
|
||||
case AnnotationEditorParamsType.FREETEXT_SIZE:
|
||||
editorFreeTextFontSize.value = value;
|
||||
break;
|
||||
case AnnotationEditorParamsType.FREETEXT_COLOR:
|
||||
editorFreeTextColor.value = value;
|
||||
break;
|
||||
case AnnotationEditorParamsType.INK_COLOR:
|
||||
updateInkColor(value);
|
||||
break;
|
||||
case AnnotationEditorParamsType.INK_THICKNESS:
|
||||
editorInkThickness.value = value;
|
||||
break;
|
||||
case AnnotationEditorParamsType.INK_OPACITY:
|
||||
updateInkOpacity(value);
|
||||
break;
|
||||
case AnnotationEditorParamsType.HIGHLIGHT_COLOR:
|
||||
eventBus.dispatch("mainhighlightcolorpickerupdatecolor", {
|
||||
source: this,
|
||||
value,
|
||||
});
|
||||
break;
|
||||
case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS:
|
||||
editorFreeHighlightThickness.value = value;
|
||||
break;
|
||||
case AnnotationEditorParamsType.HIGHLIGHT_FREE:
|
||||
editorFreeHighlightThickness.disabled = !value;
|
||||
break;
|
||||
case AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL:
|
||||
editorHighlightShowAll.setAttribute("aria-pressed", value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ import {
|
||||
setLayerDimensions,
|
||||
Util,
|
||||
} from "pdfjs-lib";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { PresentationModeState } from "./ui_utils.js";
|
||||
|
||||
/**
|
||||
@ -75,7 +76,7 @@ class AnnotationLayerBuilder {
|
||||
|
||||
#onAppend = null;
|
||||
|
||||
#eventAbortController = null;
|
||||
#eventAC = null;
|
||||
|
||||
#linksInjected = false;
|
||||
|
||||
@ -190,15 +191,15 @@ class AnnotationLayerBuilder {
|
||||
if (this.linkService.isInPresentationMode) {
|
||||
this.#updatePresentationModeState(PresentationModeState.FULLSCREEN);
|
||||
}
|
||||
if (!this.#eventAbortController) {
|
||||
this.#eventAbortController = new AbortController();
|
||||
if (!this.#eventAC) {
|
||||
this.#eventAC = new AbortController();
|
||||
|
||||
this._eventBus?._on(
|
||||
this._eventBus?.on(
|
||||
"presentationmodechanged",
|
||||
evt => {
|
||||
this.#updatePresentationModeState(evt.state);
|
||||
},
|
||||
{ signal: this.#eventAbortController.signal }
|
||||
{ signal: this.#eventAC.signal, ...internalOpt }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -221,8 +222,8 @@ class AnnotationLayerBuilder {
|
||||
cancel() {
|
||||
this._cancelled = true;
|
||||
|
||||
this.#eventAbortController?.abort();
|
||||
this.#eventAbortController = null;
|
||||
this.#eventAC?.abort();
|
||||
this.#eventAC = null;
|
||||
}
|
||||
|
||||
hide(internal = false) {
|
||||
|
||||
129
web/app.js
129
web/app.js
@ -72,6 +72,7 @@ import { CaretBrowsingMode } from "./caret_browsing.js";
|
||||
import { CommentManager } from "./comment_manager.js";
|
||||
import { DownloadManager } from "web-download_manager";
|
||||
import { EditorUndoBar } from "./editor_undo_bar.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { OverlayManager } from "./overlay_manager.js";
|
||||
import { PasswordPrompt } from "./password_prompt.js";
|
||||
import { PDFAttachmentViewer } from "web-pdf_attachment_viewer";
|
||||
@ -171,8 +172,8 @@ const PDFViewerApplication = {
|
||||
baseUrl: "",
|
||||
mlManager: null,
|
||||
_downloadUrl: "",
|
||||
_eventBusAbortController: null,
|
||||
_windowAbortController: null,
|
||||
_eventBusAC: null,
|
||||
_windowAC: null,
|
||||
_globalAbortController: new AbortController(),
|
||||
documentInfo: null,
|
||||
metadata: null,
|
||||
@ -1681,7 +1682,10 @@ const PDFViewerApplication = {
|
||||
// It should be *extremely* rare for metadata to not have been resolved
|
||||
// when this code runs, but ensure that we handle that case here.
|
||||
await new Promise(resolve => {
|
||||
this.eventBus._on("metadataloaded", resolve, { once: true });
|
||||
this.eventBus.on("metadataloaded", resolve, {
|
||||
once: true,
|
||||
...internalOpt,
|
||||
});
|
||||
});
|
||||
if (pdfDocument !== this.pdfDocument) {
|
||||
return null; // The document was closed while the metadata resolved.
|
||||
@ -1694,7 +1698,10 @@ const PDFViewerApplication = {
|
||||
// Hence we'll simply have to trust that the `contentLength` (as provided
|
||||
// by the server), when it exists, is accurate enough here.
|
||||
await new Promise(resolve => {
|
||||
this.eventBus._on("documentloaded", resolve, { once: true });
|
||||
this.eventBus.on("documentloaded", resolve, {
|
||||
once: true,
|
||||
...internalOpt,
|
||||
});
|
||||
});
|
||||
if (pdfDocument !== this.pdfDocument) {
|
||||
return null; // The document was closed while the downloadInfo resolved.
|
||||
@ -2095,11 +2102,11 @@ const PDFViewerApplication = {
|
||||
},
|
||||
|
||||
bindEvents() {
|
||||
if (this._eventBusAbortController) {
|
||||
if (this._eventBusAC) {
|
||||
return;
|
||||
}
|
||||
const ac = (this._eventBusAbortController = new AbortController());
|
||||
const opts = { signal: ac.signal };
|
||||
const ac = (this._eventBusAC = new AbortController());
|
||||
const opts = { signal: ac.signal, ...internalOpt };
|
||||
|
||||
const {
|
||||
eventBus,
|
||||
@ -2109,109 +2116,109 @@ const PDFViewerApplication = {
|
||||
preferences,
|
||||
} = this;
|
||||
|
||||
eventBus._on("resize", onResize.bind(this), opts);
|
||||
eventBus._on("hashchange", onHashchange.bind(this), opts);
|
||||
eventBus._on("beforeprint", this.beforePrint.bind(this), opts);
|
||||
eventBus._on("afterprint", this.afterPrint.bind(this), opts);
|
||||
eventBus._on("pagerender", onPageRender.bind(this), opts);
|
||||
eventBus._on("pagerendered", onPageRendered.bind(this), opts);
|
||||
eventBus._on("updateviewarea", onUpdateViewarea.bind(this), opts);
|
||||
eventBus._on("pagechanging", onPageChanging.bind(this), opts);
|
||||
eventBus._on("scalechanging", onScaleChanging.bind(this), opts);
|
||||
eventBus._on("rotationchanging", onRotationChanging.bind(this), opts);
|
||||
eventBus._on("sidebarviewchanged", onSidebarViewChanged.bind(this), opts);
|
||||
eventBus._on("pagemode", onPageMode.bind(this), opts);
|
||||
eventBus._on("namedaction", onNamedAction.bind(this), opts);
|
||||
eventBus._on(
|
||||
eventBus.on("resize", onResize.bind(this), opts);
|
||||
eventBus.on("hashchange", onHashchange.bind(this), opts);
|
||||
eventBus.on("beforeprint", this.beforePrint.bind(this), opts);
|
||||
eventBus.on("afterprint", this.afterPrint.bind(this), opts);
|
||||
eventBus.on("pagerender", onPageRender.bind(this), opts);
|
||||
eventBus.on("pagerendered", onPageRendered.bind(this), opts);
|
||||
eventBus.on("updateviewarea", onUpdateViewarea.bind(this), opts);
|
||||
eventBus.on("pagechanging", onPageChanging.bind(this), opts);
|
||||
eventBus.on("scalechanging", onScaleChanging.bind(this), opts);
|
||||
eventBus.on("rotationchanging", onRotationChanging.bind(this), opts);
|
||||
eventBus.on("sidebarviewchanged", onSidebarViewChanged.bind(this), opts);
|
||||
eventBus.on("pagemode", onPageMode.bind(this), opts);
|
||||
eventBus.on("namedaction", onNamedAction.bind(this), opts);
|
||||
eventBus.on(
|
||||
"presentationmodechanged",
|
||||
evt => (pdfViewer.presentationModeState = evt.state),
|
||||
opts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"presentationmode",
|
||||
this.requestPresentationMode.bind(this),
|
||||
opts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"switchannotationeditormode",
|
||||
evt => (pdfViewer.annotationEditorMode = evt),
|
||||
opts
|
||||
);
|
||||
eventBus._on("print", this.triggerPrinting.bind(this), opts);
|
||||
eventBus._on("download", this.downloadOrSave.bind(this), opts);
|
||||
eventBus._on("firstpage", () => (this.page = 1), opts);
|
||||
eventBus._on("lastpage", () => (this.page = this.pagesCount), opts);
|
||||
eventBus._on("nextpage", () => pdfViewer.nextPage(), opts);
|
||||
eventBus._on("previouspage", () => pdfViewer.previousPage(), opts);
|
||||
eventBus._on("zoomin", this.zoomIn.bind(this), opts);
|
||||
eventBus._on("zoomout", this.zoomOut.bind(this), opts);
|
||||
eventBus._on("zoomreset", this.zoomReset.bind(this), opts);
|
||||
eventBus._on("pagenumberchanged", onPageNumberChanged.bind(this), opts);
|
||||
eventBus._on(
|
||||
eventBus.on("print", this.triggerPrinting.bind(this), opts);
|
||||
eventBus.on("download", this.downloadOrSave.bind(this), opts);
|
||||
eventBus.on("firstpage", () => (this.page = 1), opts);
|
||||
eventBus.on("lastpage", () => (this.page = this.pagesCount), opts);
|
||||
eventBus.on("nextpage", () => pdfViewer.nextPage(), opts);
|
||||
eventBus.on("previouspage", () => pdfViewer.previousPage(), opts);
|
||||
eventBus.on("zoomin", this.zoomIn.bind(this), opts);
|
||||
eventBus.on("zoomout", this.zoomOut.bind(this), opts);
|
||||
eventBus.on("zoomreset", this.zoomReset.bind(this), opts);
|
||||
eventBus.on("pagenumberchanged", onPageNumberChanged.bind(this), opts);
|
||||
eventBus.on(
|
||||
"scalechanged",
|
||||
evt => (pdfViewer.currentScaleValue = evt.value),
|
||||
opts
|
||||
);
|
||||
eventBus._on("rotatecw", this.rotatePages.bind(this, 90), opts);
|
||||
eventBus._on("rotateccw", this.rotatePages.bind(this, -90), opts);
|
||||
eventBus._on(
|
||||
eventBus.on("rotatecw", this.rotatePages.bind(this, 90), opts);
|
||||
eventBus.on("rotateccw", this.rotatePages.bind(this, -90), opts);
|
||||
eventBus.on(
|
||||
"optionalcontentconfig",
|
||||
evt => (pdfViewer.optionalContentConfigPromise = evt.promise),
|
||||
opts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"switchscrollmode",
|
||||
evt => (pdfViewer.scrollMode = evt.mode),
|
||||
opts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"scrollmodechanged",
|
||||
onViewerModesChanged.bind(this, "scrollMode"),
|
||||
opts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"switchspreadmode",
|
||||
evt => (pdfViewer.spreadMode = evt.mode),
|
||||
opts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"spreadmodechanged",
|
||||
onViewerModesChanged.bind(this, "spreadMode"),
|
||||
opts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"imagealttextsettings",
|
||||
onImageAltTextSettings.bind(this),
|
||||
opts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"documentproperties",
|
||||
() => pdfDocumentProperties?.open(),
|
||||
opts
|
||||
);
|
||||
eventBus._on("findfromurlhash", onFindFromUrlHash.bind(this), opts);
|
||||
eventBus._on(
|
||||
eventBus.on("findfromurlhash", onFindFromUrlHash.bind(this), opts);
|
||||
eventBus.on(
|
||||
"updatefindmatchescount",
|
||||
onUpdateFindMatchesCount.bind(this),
|
||||
opts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"updatefindcontrolstate",
|
||||
onUpdateFindControlState.bind(this),
|
||||
opts
|
||||
);
|
||||
|
||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
||||
eventBus._on("fileinputchange", onFileInputChange.bind(this), opts);
|
||||
eventBus._on("openfile", onOpenFile.bind(this), opts);
|
||||
eventBus.on("fileinputchange", onFileInputChange.bind(this), opts);
|
||||
eventBus.on("openfile", onOpenFile.bind(this), opts);
|
||||
}
|
||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"editingstateschanged",
|
||||
evt => externalServices.updateEditorStates(evt),
|
||||
opts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"reporttelemetry",
|
||||
evt => externalServices.reportTelemetry(evt.details),
|
||||
opts
|
||||
@ -2221,28 +2228,28 @@ const PDFViewerApplication = {
|
||||
typeof PDFJSDev === "undefined" ||
|
||||
PDFJSDev.test("TESTING || MOZCENTRAL")
|
||||
) {
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"setpreference",
|
||||
evt => preferences.set(evt.name, evt.value),
|
||||
opts
|
||||
);
|
||||
}
|
||||
eventBus._on("pagesedited", this.onPagesEdited.bind(this), opts);
|
||||
eventBus._on("saveextractedpages", this.onSavePages.bind(this), opts);
|
||||
eventBus._on("saveandload", this.onSaveAndLoad.bind(this), opts);
|
||||
eventBus.on("pagesedited", this.onPagesEdited.bind(this), opts);
|
||||
eventBus.on("saveextractedpages", this.onSavePages.bind(this), opts);
|
||||
eventBus.on("saveandload", this.onSaveAndLoad.bind(this), opts);
|
||||
},
|
||||
|
||||
bindWindowEvents() {
|
||||
if (this._windowAbortController) {
|
||||
if (this._windowAC) {
|
||||
return;
|
||||
}
|
||||
this._windowAbortController = new AbortController();
|
||||
this._windowAC = new AbortController();
|
||||
|
||||
const {
|
||||
eventBus,
|
||||
appConfig: { mainContainer },
|
||||
pdfViewer,
|
||||
_windowAbortController: { signal },
|
||||
_windowAC: { signal },
|
||||
} = this;
|
||||
|
||||
this._touchManager = new TouchManager({
|
||||
@ -2382,13 +2389,13 @@ const PDFViewerApplication = {
|
||||
},
|
||||
|
||||
unbindEvents() {
|
||||
this._eventBusAbortController?.abort();
|
||||
this._eventBusAbortController = null;
|
||||
this._eventBusAC?.abort();
|
||||
this._eventBusAC = null;
|
||||
},
|
||||
|
||||
unbindWindowEvents() {
|
||||
this._windowAbortController?.abort();
|
||||
this._windowAbortController = null;
|
||||
this._windowAC?.abort();
|
||||
this._windowAC = null;
|
||||
this._touchManager = null;
|
||||
},
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { noContextMenu } from "pdfjs-lib";
|
||||
|
||||
class EditorUndoBar {
|
||||
@ -62,13 +63,14 @@ class EditorUndoBar {
|
||||
show(undoAction, messageData) {
|
||||
if (!this.#initController) {
|
||||
this.#initController = new AbortController();
|
||||
const opts = { signal: this.#initController.signal };
|
||||
const domOpts = { signal: this.#initController.signal };
|
||||
const evtOpts = { signal: this.#initController.signal, ...internalOpt };
|
||||
const boundHide = this.hide.bind(this);
|
||||
|
||||
this.#container.addEventListener("contextmenu", noContextMenu, opts);
|
||||
this.#closeButton.addEventListener("click", boundHide, opts);
|
||||
this.#eventBus._on("beforeprint", boundHide, opts);
|
||||
this.#eventBus._on("download", boundHide, opts);
|
||||
this.#container.addEventListener("contextmenu", noContextMenu, domOpts);
|
||||
this.#closeButton.addEventListener("click", boundHide, domOpts);
|
||||
this.#eventBus.on("beforeprint", boundHide, evtOpts);
|
||||
this.#eventBus.on("download", boundHide, evtOpts);
|
||||
}
|
||||
|
||||
this.hide();
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { INTERNAL_EVT, internalOpt } from "./internal_evt.js";
|
||||
|
||||
const WaitOnType = {
|
||||
EVENT: "event",
|
||||
TIMEOUT: "timeout",
|
||||
@ -53,10 +55,12 @@ async function waitOnEventOrTimeout({ target, name, delay = 0 }) {
|
||||
resolve(type);
|
||||
}
|
||||
|
||||
const evtMethod = target instanceof EventBus ? "_on" : "addEventListener";
|
||||
target[evtMethod](name, handler.bind(null, WaitOnType.EVENT), {
|
||||
signal: ac.signal,
|
||||
});
|
||||
const evtMethod = target instanceof EventBus ? "on" : "addEventListener";
|
||||
const evtOpts =
|
||||
target instanceof EventBus
|
||||
? { signal: ac.signal, ...internalOpt }
|
||||
: { signal: ac.signal };
|
||||
target[evtMethod](name, handler.bind(null, WaitOnType.EVENT), evtOpts);
|
||||
|
||||
const timeout = setTimeout(handler.bind(null, WaitOnType.TIMEOUT), delay);
|
||||
|
||||
@ -70,16 +74,39 @@ async function waitOnEventOrTimeout({ target, name, delay = 0 }) {
|
||||
class EventBus {
|
||||
#listeners = Object.create(null);
|
||||
|
||||
constructor() {
|
||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
||||
// Prevent the class methods from being overridden by third-party users,
|
||||
// to ensure that `INTERNAL_EVT` cannot be accessed from the outside.
|
||||
Object.seal(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {function} listener
|
||||
* @param {Object} [options]
|
||||
*/
|
||||
on(eventName, listener, options = null) {
|
||||
this._on(eventName, listener, {
|
||||
external: true,
|
||||
once: options?.once,
|
||||
signal: options?.signal,
|
||||
let rmAbort = null;
|
||||
if (options?.signal instanceof AbortSignal) {
|
||||
const { signal } = options;
|
||||
if (signal.aborted) {
|
||||
console.error("Cannot use an `aborted` signal.");
|
||||
return;
|
||||
}
|
||||
const onAbort = () => this.off(eventName, listener);
|
||||
rmAbort = () => signal.removeEventListener("abort", onAbort);
|
||||
|
||||
signal.addEventListener("abort", onAbort);
|
||||
}
|
||||
|
||||
const eventListeners = (this.#listeners[eventName] ??= []);
|
||||
eventListeners.push({
|
||||
listener,
|
||||
internal: options?.internal === INTERNAL_EVT,
|
||||
once: options?.once === true,
|
||||
rmAbort,
|
||||
});
|
||||
}
|
||||
|
||||
@ -89,71 +116,6 @@ class EventBus {
|
||||
* @param {Object} [options]
|
||||
*/
|
||||
off(eventName, listener, options = null) {
|
||||
this._off(eventName, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {Object} data
|
||||
*/
|
||||
dispatch(eventName, data) {
|
||||
const eventListeners = this.#listeners[eventName];
|
||||
if (!eventListeners || eventListeners.length === 0) {
|
||||
return;
|
||||
}
|
||||
let externalListeners;
|
||||
// Making copy of the listeners array in case if it will be modified
|
||||
// during dispatch.
|
||||
for (const { listener, external, once } of eventListeners.slice(0)) {
|
||||
if (once) {
|
||||
this._off(eventName, listener);
|
||||
}
|
||||
if (external) {
|
||||
(externalListeners ||= []).push(listener);
|
||||
continue;
|
||||
}
|
||||
listener(data);
|
||||
}
|
||||
// Dispatch any "external" listeners *after* the internal ones, to give the
|
||||
// viewer components time to handle events and update their state first.
|
||||
if (externalListeners) {
|
||||
for (const listener of externalListeners) {
|
||||
listener(data);
|
||||
}
|
||||
externalListeners = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
_on(eventName, listener, options = null) {
|
||||
let rmAbort = null;
|
||||
if (options?.signal instanceof AbortSignal) {
|
||||
const { signal } = options;
|
||||
if (signal.aborted) {
|
||||
console.error("Cannot use an `aborted` signal.");
|
||||
return;
|
||||
}
|
||||
const onAbort = () => this._off(eventName, listener);
|
||||
rmAbort = () => signal.removeEventListener("abort", onAbort);
|
||||
|
||||
signal.addEventListener("abort", onAbort);
|
||||
}
|
||||
|
||||
const eventListeners = (this.#listeners[eventName] ||= []);
|
||||
eventListeners.push({
|
||||
listener,
|
||||
external: options?.external === true,
|
||||
once: options?.once === true,
|
||||
rmAbort,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
_off(eventName, listener, options = null) {
|
||||
const eventListeners = this.#listeners[eventName];
|
||||
if (!eventListeners) {
|
||||
return;
|
||||
@ -167,6 +129,37 @@ class EventBus {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {Object} data
|
||||
*/
|
||||
dispatch(eventName, data) {
|
||||
const eventListeners = this.#listeners[eventName];
|
||||
if (!eventListeners?.length) {
|
||||
return;
|
||||
}
|
||||
let extListeners;
|
||||
// Making copy of the listeners array in case if it will be modified
|
||||
// during dispatch.
|
||||
for (const { listener, internal, once } of eventListeners.slice(0)) {
|
||||
if (once) {
|
||||
this.off(eventName, listener);
|
||||
}
|
||||
if (!internal) {
|
||||
(extListeners ??= []).push(listener);
|
||||
continue;
|
||||
}
|
||||
listener(data);
|
||||
}
|
||||
// Dispatch any "external" listeners *after* the internal ones, to give the
|
||||
// viewer components time to handle events and update their state first.
|
||||
if (extListeners) {
|
||||
for (const listener of extListeners) {
|
||||
listener(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -19,6 +19,7 @@ import { BaseDownloadManager } from "./base_download_manager.js";
|
||||
import { BaseExternalServices } from "./external_services.js";
|
||||
import { BasePreferences } from "./preferences.js";
|
||||
import { DEFAULT_SCALE_VALUE } from "./ui_utils.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { L10n } from "./l10n.js";
|
||||
|
||||
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) {
|
||||
@ -294,7 +295,9 @@ class MLManager {
|
||||
setEventBus(eventBus, abortSignal) {
|
||||
this.#eventBus = eventBus;
|
||||
this.#abortSignal = abortSignal;
|
||||
eventBus._on(
|
||||
|
||||
const evtOpts = { signal: abortSignal, ...internalOpt };
|
||||
eventBus.on(
|
||||
"enablealttextmodeldownload",
|
||||
({ value }) => {
|
||||
if (this.enableAltTextModelDownload === value) {
|
||||
@ -306,14 +309,14 @@ class MLManager {
|
||||
this.deleteModel("altText");
|
||||
}
|
||||
},
|
||||
{ signal: abortSignal }
|
||||
evtOpts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"enableguessalttext",
|
||||
({ value }) => {
|
||||
this.toggleService("altText", value);
|
||||
},
|
||||
{ signal: abortSignal }
|
||||
evtOpts
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
27
web/internal_evt.js
Normal file
27
web/internal_evt.js
Normal file
@ -0,0 +1,27 @@
|
||||
/* Copyright 2026 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Keep this file in sync with `src/shared/internal_evt.js`.
|
||||
*/
|
||||
|
||||
const INTERNAL_EVT =
|
||||
typeof PDFJSDev !== "undefined" && !PDFJSDev.test("TESTING")
|
||||
? PDFJSDev.eval("INTERNAL_EVT")
|
||||
: "internalEvent";
|
||||
|
||||
const internalOpt = Object.freeze({ internal: INTERNAL_EVT });
|
||||
|
||||
export { INTERNAL_EVT, internalOpt };
|
||||
@ -13,6 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { noContextMenu } from "pdfjs-lib";
|
||||
|
||||
class NewAltTextManager {
|
||||
@ -165,9 +166,13 @@ class NewAltTextManager {
|
||||
}
|
||||
});
|
||||
|
||||
eventBus._on("enableguessalttext", ({ value }) => {
|
||||
this.#toggleGuessAltText(value, /* isInitial = */ false);
|
||||
});
|
||||
eventBus.on(
|
||||
"enableguessalttext",
|
||||
({ value }) => {
|
||||
this.#toggleGuessAltText(value, /* isInitial = */ false);
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
|
||||
this.#overlayManager.register(dialog);
|
||||
|
||||
@ -345,7 +350,7 @@ class NewAltTextManager {
|
||||
}
|
||||
|
||||
// We're done, remove the listener and hide the download model progress.
|
||||
this.#eventBus._off("loadaiengineprogress", callback);
|
||||
this.#eventBus.off("loadaiengineprogress", callback);
|
||||
this.#downloadModel.classList.toggle("hidden", true);
|
||||
|
||||
this.#toggleAI(true);
|
||||
@ -361,7 +366,7 @@ class NewAltTextManager {
|
||||
/* isInitial = */ true
|
||||
);
|
||||
};
|
||||
this.#eventBus._on("loadaiengineprogress", callback);
|
||||
this.#eventBus.on("loadaiengineprogress", callback, internalOpt);
|
||||
}
|
||||
|
||||
async editAltText(uiManager, editor, firstTime) {
|
||||
@ -611,13 +616,17 @@ class ImageAltTextSettings {
|
||||
});
|
||||
});
|
||||
|
||||
eventBus._on("enablealttextmodeldownload", ({ value }) => {
|
||||
if (value) {
|
||||
this.#download(false);
|
||||
} else {
|
||||
this.#delete(false);
|
||||
}
|
||||
});
|
||||
eventBus.on(
|
||||
"enablealttextmodeldownload",
|
||||
({ value }) => {
|
||||
if (value) {
|
||||
this.#download(false);
|
||||
} else {
|
||||
this.#delete(false);
|
||||
}
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
|
||||
this.#overlayManager.register(dialog);
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
/** @typedef {import("./download_manager.js").DownloadManager} DownloadManager */
|
||||
|
||||
import { BaseTreeViewer } from "./base_tree_viewer.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { waitOnEventOrTimeout } from "./event_utils.js";
|
||||
|
||||
/**
|
||||
@ -41,9 +42,10 @@ class PDFAttachmentViewer extends BaseTreeViewer {
|
||||
super(options);
|
||||
this.downloadManager = options.downloadManager;
|
||||
|
||||
this.eventBus._on(
|
||||
this.eventBus.on(
|
||||
"fileattachmentannotation",
|
||||
this.#appendAttachment.bind(this)
|
||||
this.#appendAttachment.bind(this),
|
||||
internalOpt
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
import { AnnotationEditorType, shadow } from "pdfjs-lib";
|
||||
import { CursorTool, PresentationModeState } from "./ui_utils.js";
|
||||
import { GrabToPan } from "./grab_to_pan.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
|
||||
/**
|
||||
* @typedef {Object} PDFCursorToolsOptions
|
||||
@ -120,16 +121,22 @@ class PDFCursorTools {
|
||||
}
|
||||
|
||||
#addEventListeners() {
|
||||
this.eventBus._on("switchcursortool", evt => {
|
||||
if (!evt.reset) {
|
||||
this.switchTool(evt.tool);
|
||||
} else if (this.#prevActive !== null) {
|
||||
annotationEditorMode = AnnotationEditorType.NONE;
|
||||
presentationModeState = PresentationModeState.NORMAL;
|
||||
const { eventBus } = this;
|
||||
|
||||
enableActive();
|
||||
}
|
||||
});
|
||||
eventBus.on(
|
||||
"switchcursortool",
|
||||
evt => {
|
||||
if (!evt.reset) {
|
||||
this.switchTool(evt.tool);
|
||||
} else if (this.#prevActive !== null) {
|
||||
annotationEditorMode = AnnotationEditorType.NONE;
|
||||
presentationModeState = PresentationModeState.NORMAL;
|
||||
|
||||
enableActive();
|
||||
}
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
|
||||
let annotationEditorMode = AnnotationEditorType.NONE,
|
||||
presentationModeState = PresentationModeState.NORMAL;
|
||||
@ -149,25 +156,33 @@ class PDFCursorTools {
|
||||
}
|
||||
};
|
||||
|
||||
this.eventBus._on("annotationeditormodechanged", ({ mode }) => {
|
||||
annotationEditorMode = mode;
|
||||
eventBus.on(
|
||||
"annotationeditormodechanged",
|
||||
({ mode }) => {
|
||||
annotationEditorMode = mode;
|
||||
|
||||
if (mode === AnnotationEditorType.NONE) {
|
||||
enableActive();
|
||||
} else {
|
||||
disableActive();
|
||||
}
|
||||
});
|
||||
if (mode === AnnotationEditorType.NONE) {
|
||||
enableActive();
|
||||
} else {
|
||||
disableActive();
|
||||
}
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
|
||||
this.eventBus._on("presentationmodechanged", ({ state }) => {
|
||||
presentationModeState = state;
|
||||
eventBus.on(
|
||||
"presentationmodechanged",
|
||||
({ state }) => {
|
||||
presentationModeState = state;
|
||||
|
||||
if (state === PresentationModeState.NORMAL) {
|
||||
enableActive();
|
||||
} else if (state === PresentationModeState.FULLSCREEN) {
|
||||
disableActive();
|
||||
}
|
||||
});
|
||||
if (state === PresentationModeState.NORMAL) {
|
||||
enableActive();
|
||||
} else if (state === PresentationModeState.FULLSCREEN) {
|
||||
disableActive();
|
||||
}
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
/** @typedef {import("../src/display/api.js").PDFDocumentProxy} PDFDocumentProxy */
|
||||
|
||||
import { getPageSizeInches, isPortraitOrientation } from "./ui_utils.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { PDFDateString } from "pdfjs-lib";
|
||||
|
||||
// See https://en.wikibooks.org/wiki/Lentis/Conversion_to_the_Metric_Standard_in_the_United_States
|
||||
@ -82,12 +83,20 @@ class PDFDocumentProperties {
|
||||
|
||||
this.overlayManager.register(this.dialog);
|
||||
|
||||
eventBus._on("pagechanging", evt => {
|
||||
this._currentPageNumber = evt.pageNumber;
|
||||
});
|
||||
eventBus._on("rotationchanging", evt => {
|
||||
this._pagesRotation = evt.pagesRotation;
|
||||
});
|
||||
eventBus.on(
|
||||
"pagechanging",
|
||||
evt => {
|
||||
this._currentPageNumber = evt.pageNumber;
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
eventBus.on(
|
||||
"rotationchanging",
|
||||
evt => {
|
||||
this._pagesRotation = evt.pagesRotation;
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
import { getCharacterType, getNormalizeWithNFKC } from "./pdf_find_utils.js";
|
||||
import { binarySearchFirstItem } from "./ui_utils.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
|
||||
const FindState = {
|
||||
FOUND: 0,
|
||||
@ -450,9 +451,9 @@ class PDFFindController {
|
||||
this.onIsPageVisible = null;
|
||||
|
||||
this.#reset();
|
||||
eventBus._on("find", this.#onFind.bind(this));
|
||||
eventBus._on("findbarclose", this.#onFindBarClose.bind(this));
|
||||
eventBus._on("pagesedited", this.#onPagesEdited.bind(this));
|
||||
eventBus.on("find", this.#onFind.bind(this), internalOpt);
|
||||
eventBus.on("findbarclose", this.#onFindBarClose.bind(this), internalOpt);
|
||||
eventBus.on("pagesedited", this.#onPagesEdited.bind(this), internalOpt);
|
||||
}
|
||||
|
||||
get highlightMatches() {
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
/** @typedef {import("./pdf_link_service.js").PDFLinkService} PDFLinkService */
|
||||
|
||||
import { isValidRotation, parseQueryString } from "./ui_utils.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { updateUrlHash } from "pdfjs-lib";
|
||||
import { waitOnEventOrTimeout } from "./event_utils.js";
|
||||
|
||||
@ -54,7 +55,7 @@ function getCurrentHash() {
|
||||
}
|
||||
|
||||
class PDFHistory {
|
||||
#eventAbortController = null;
|
||||
#eventAC = null;
|
||||
|
||||
/**
|
||||
* @param {PDFHistoryOptions} options
|
||||
@ -69,17 +70,21 @@ class PDFHistory {
|
||||
|
||||
// Ensure that we don't miss a "pagesinit" event,
|
||||
// by registering the listener immediately.
|
||||
this.eventBus._on("pagesinit", () => {
|
||||
this._isPagesLoaded = false;
|
||||
this.eventBus.on(
|
||||
"pagesinit",
|
||||
() => {
|
||||
this._isPagesLoaded = false;
|
||||
|
||||
this.eventBus._on(
|
||||
"pagesloaded",
|
||||
evt => {
|
||||
this._isPagesLoaded = !!evt.pagesCount;
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
this.eventBus.on(
|
||||
"pagesloaded",
|
||||
evt => {
|
||||
this._isPagesLoaded = !!evt.pagesCount;
|
||||
},
|
||||
{ once: true, ...internalOpt }
|
||||
);
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -671,22 +676,23 @@ class PDFHistory {
|
||||
}
|
||||
|
||||
#bindEvents() {
|
||||
if (this.#eventAbortController) {
|
||||
if (this.#eventAC) {
|
||||
return; // The event listeners were already added.
|
||||
}
|
||||
this.#eventAbortController = new AbortController();
|
||||
const { signal } = this.#eventAbortController;
|
||||
this.#eventAC = new AbortController();
|
||||
const { signal } = this.#eventAC;
|
||||
|
||||
this.eventBus._on("updateviewarea", this.#updateViewarea.bind(this), {
|
||||
this.eventBus.on("updateviewarea", this.#updateViewarea.bind(this), {
|
||||
signal,
|
||||
...internalOpt,
|
||||
});
|
||||
window.addEventListener("popstate", this.#popState.bind(this), { signal });
|
||||
window.addEventListener("pagehide", this.#pageHide.bind(this), { signal });
|
||||
}
|
||||
|
||||
#unbindEvents() {
|
||||
this.#eventAbortController?.abort();
|
||||
this.#eventAbortController = null;
|
||||
this.#eventAC?.abort();
|
||||
this.#eventAC = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
/** @typedef {import("../src/display/api.js").PDFDocumentProxy} PDFDocumentProxy */
|
||||
|
||||
import { BaseTreeViewer } from "./base_tree_viewer.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
|
||||
/**
|
||||
* @typedef {Object} PDFLayerViewerOptions
|
||||
@ -38,13 +39,26 @@ class PDFLayerViewer extends BaseTreeViewer {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.eventBus._on("optionalcontentconfigchanged", evt => {
|
||||
this.#updateLayers(evt.promise);
|
||||
});
|
||||
this.eventBus._on("resetlayers", () => {
|
||||
this.#updateLayers();
|
||||
});
|
||||
this.eventBus._on("togglelayerstree", this._toggleAllTreeItems.bind(this));
|
||||
const { eventBus } = this;
|
||||
eventBus.on(
|
||||
"optionalcontentconfigchanged",
|
||||
evt => {
|
||||
this.#updateLayers(evt.promise);
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
eventBus.on(
|
||||
"resetlayers",
|
||||
() => {
|
||||
this.#updateLayers();
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
eventBus.on(
|
||||
"togglelayerstree",
|
||||
this._toggleAllTreeItems.bind(this),
|
||||
internalOpt
|
||||
);
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
|
||||
/** @typedef {import("./event_utils").EventBus} EventBus */
|
||||
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { isValidExplicitDest } from "pdfjs-lib";
|
||||
import { parseQueryString } from "./ui_utils.js";
|
||||
|
||||
@ -192,7 +193,7 @@ class PDFLinkService {
|
||||
});
|
||||
|
||||
const ac = new AbortController();
|
||||
this.eventBus._on(
|
||||
this.eventBus.on(
|
||||
"textlayerrendered",
|
||||
evt => {
|
||||
if (evt.pageNumber === pageNumber) {
|
||||
@ -200,7 +201,7 @@ class PDFLinkService {
|
||||
ac.abort();
|
||||
}
|
||||
},
|
||||
{ signal: ac.signal }
|
||||
{ signal: ac.signal, ...internalOpt }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
/** @typedef {import("../src/display/api.js").PDFDocumentProxy} PDFDocumentProxy */
|
||||
|
||||
import { BaseTreeViewer } from "./base_tree_viewer.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { SidebarView } from "./ui_utils.js";
|
||||
|
||||
/**
|
||||
@ -45,27 +46,45 @@ class PDFOutlineViewer extends BaseTreeViewer {
|
||||
this.linkService = options.linkService;
|
||||
this.downloadManager = options.downloadManager;
|
||||
|
||||
this.eventBus._on("toggleoutlinetree", this._toggleAllTreeItems.bind(this));
|
||||
this.eventBus._on(
|
||||
const { eventBus } = this;
|
||||
eventBus.on(
|
||||
"toggleoutlinetree",
|
||||
this._toggleAllTreeItems.bind(this),
|
||||
internalOpt
|
||||
);
|
||||
eventBus.on(
|
||||
"currentoutlineitem",
|
||||
this._currentOutlineItem.bind(this)
|
||||
this._currentOutlineItem.bind(this),
|
||||
internalOpt
|
||||
);
|
||||
|
||||
this.eventBus._on("pagechanging", evt => {
|
||||
this._currentPageNumber = evt.pageNumber;
|
||||
});
|
||||
this.eventBus._on("pagesloaded", evt => {
|
||||
this._isPagesLoaded = !!evt.pagesCount;
|
||||
eventBus.on(
|
||||
"pagechanging",
|
||||
evt => {
|
||||
this._currentPageNumber = evt.pageNumber;
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
eventBus.on(
|
||||
"pagesloaded",
|
||||
evt => {
|
||||
this._isPagesLoaded = !!evt.pagesCount;
|
||||
|
||||
// If the capability is still pending, see the `_dispatchEvent`-method,
|
||||
// we know that the `currentOutlineItem`-button can be enabled here.
|
||||
this._currentOutlineItemCapability?.resolve(
|
||||
/* enabled = */ this._isPagesLoaded
|
||||
);
|
||||
});
|
||||
this.eventBus._on("sidebarviewchanged", evt => {
|
||||
this._sidebarView = evt.view;
|
||||
});
|
||||
// If the capability is still pending, see the `_dispatchEvent`-method,
|
||||
// we know that the `currentOutlineItem`-button can be enabled here.
|
||||
this._currentOutlineItemCapability?.resolve(
|
||||
/* enabled = */ this._isPagesLoaded
|
||||
);
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
eventBus.on(
|
||||
"sidebarviewchanged",
|
||||
evt => {
|
||||
this._sidebarView = evt.view;
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
@ -49,9 +49,9 @@ class PDFPresentationMode {
|
||||
|
||||
#args = null;
|
||||
|
||||
#fullscreenChangeAbortController = null;
|
||||
#fullscreenChangeAC = null;
|
||||
|
||||
#windowAbortController = null;
|
||||
#windowAC = null;
|
||||
|
||||
/**
|
||||
* @param {PDFPresentationModeOptions} options
|
||||
@ -350,11 +350,11 @@ class PDFPresentationMode {
|
||||
}
|
||||
|
||||
#addWindowListeners() {
|
||||
if (this.#windowAbortController) {
|
||||
if (this.#windowAC) {
|
||||
return;
|
||||
}
|
||||
this.#windowAbortController = new AbortController();
|
||||
const { signal } = this.#windowAbortController;
|
||||
this.#windowAC = new AbortController();
|
||||
const { signal } = this.#windowAC;
|
||||
|
||||
const touchSwipeBind = this.#touchSwipe.bind(this);
|
||||
|
||||
@ -380,15 +380,15 @@ class PDFPresentationMode {
|
||||
}
|
||||
|
||||
#removeWindowListeners() {
|
||||
this.#windowAbortController?.abort();
|
||||
this.#windowAbortController = null;
|
||||
this.#windowAC?.abort();
|
||||
this.#windowAC = null;
|
||||
}
|
||||
|
||||
#addFullscreenChangeListeners() {
|
||||
if (this.#fullscreenChangeAbortController) {
|
||||
if (this.#fullscreenChangeAC) {
|
||||
return;
|
||||
}
|
||||
this.#fullscreenChangeAbortController = new AbortController();
|
||||
this.#fullscreenChangeAC = new AbortController();
|
||||
|
||||
window.addEventListener(
|
||||
"fullscreenchange",
|
||||
@ -399,13 +399,13 @@ class PDFPresentationMode {
|
||||
this.#exit();
|
||||
}
|
||||
},
|
||||
{ signal: this.#fullscreenChangeAbortController.signal }
|
||||
{ signal: this.#fullscreenChangeAC.signal }
|
||||
);
|
||||
}
|
||||
|
||||
#removeFullscreenChangeListeners() {
|
||||
this.#fullscreenChangeAbortController?.abort();
|
||||
this.#fullscreenChangeAbortController = null;
|
||||
this.#fullscreenChangeAC?.abort();
|
||||
this.#fullscreenChangeAC = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
/** @typedef {import("./event_utils").EventBus} EventBus */
|
||||
|
||||
import { apiPageLayoutToViewerModes } from "./ui_utils.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { RenderingStates } from "./renderable_view.js";
|
||||
import { shadow } from "pdfjs-lib";
|
||||
|
||||
@ -38,7 +39,7 @@ class PDFScriptingManager {
|
||||
|
||||
#docProperties = null;
|
||||
|
||||
#eventAbortController = null;
|
||||
#eventAC = null;
|
||||
|
||||
#eventBus = null;
|
||||
|
||||
@ -113,27 +114,27 @@ class PDFScriptingManager {
|
||||
}
|
||||
const eventBus = this.#eventBus;
|
||||
|
||||
this.#eventAbortController = new AbortController();
|
||||
const { signal } = this.#eventAbortController;
|
||||
this.#eventAC = new AbortController();
|
||||
const evtOpts = { signal: this.#eventAC.signal, ...internalOpt };
|
||||
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"updatefromsandbox",
|
||||
event => {
|
||||
if (event?.source === window) {
|
||||
this.#updateFromSandbox(event.detail);
|
||||
}
|
||||
},
|
||||
{ signal }
|
||||
evtOpts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"dispatcheventinsandbox",
|
||||
event => {
|
||||
this.#scripting?.dispatchEventInSandbox(event.detail);
|
||||
},
|
||||
{ signal }
|
||||
evtOpts
|
||||
);
|
||||
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"pagechanging",
|
||||
({ pageNumber, previous }) => {
|
||||
if (pageNumber === previous) {
|
||||
@ -142,9 +143,9 @@ class PDFScriptingManager {
|
||||
this.#dispatchPageClose(previous);
|
||||
this.#dispatchPageOpen(pageNumber);
|
||||
},
|
||||
{ signal }
|
||||
evtOpts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"pagerendered",
|
||||
({ pageNumber }) => {
|
||||
if (!this._pageOpenPending.has(pageNumber)) {
|
||||
@ -155,9 +156,9 @@ class PDFScriptingManager {
|
||||
}
|
||||
this.#dispatchPageOpen(pageNumber);
|
||||
},
|
||||
{ signal }
|
||||
evtOpts
|
||||
);
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"pagesdestroy",
|
||||
async () => {
|
||||
await this.#dispatchPageClose(this.#pdfViewer.currentPageNumber);
|
||||
@ -169,7 +170,7 @@ class PDFScriptingManager {
|
||||
|
||||
this.#closeCapability?.resolve();
|
||||
},
|
||||
{ signal }
|
||||
evtOpts
|
||||
);
|
||||
|
||||
try {
|
||||
@ -482,8 +483,8 @@ class PDFScriptingManager {
|
||||
this.#willPrintCapability?.reject(new Error("Scripting destroyed."));
|
||||
this.#willPrintCapability = null;
|
||||
|
||||
this.#eventAbortController?.abort();
|
||||
this.#eventAbortController = null;
|
||||
this.#eventAC?.abort();
|
||||
this.#eventAC = null;
|
||||
|
||||
this._pageOpenPending.clear();
|
||||
this._visitedPages.clear();
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
|
||||
/**
|
||||
* This class manages the interaction of extracting the text content of the page
|
||||
* and passing it back to the external service.
|
||||
@ -29,15 +31,23 @@ class PdfTextExtractor {
|
||||
constructor(externalServices, pdfViewer, eventBus) {
|
||||
this.#externalServices = externalServices;
|
||||
|
||||
eventBus._on("pagesinit", () => {
|
||||
this.#capability.resolve(pdfViewer);
|
||||
});
|
||||
eventBus._on("pagesdestroy", () => {
|
||||
this.#capability.reject(new Error("pagesdestroy"));
|
||||
this.#textPromise = null;
|
||||
eventBus.on(
|
||||
"pagesinit",
|
||||
() => {
|
||||
this.#capability.resolve(pdfViewer);
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
eventBus.on(
|
||||
"pagesdestroy",
|
||||
() => {
|
||||
this.#capability.reject(new Error("pagesdestroy"));
|
||||
this.#textPromise = null;
|
||||
|
||||
this.#capability = Promise.withResolvers();
|
||||
});
|
||||
this.#capability = Promise.withResolvers();
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
|
||||
window.addEventListener("requestTextContent", ({ detail }) => {
|
||||
this.extractTextContent(detail.requestId);
|
||||
|
||||
@ -27,6 +27,7 @@ import {
|
||||
watchScroll,
|
||||
} from "./ui_utils.js";
|
||||
import { MathClamp, noContextMenu, stopEvent } from "pdfjs-lib";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { Menu } from "./menu.js";
|
||||
import { PDFThumbnailView } from "./pdf_thumbnail_view.js";
|
||||
import { RenderingStates } from "./renderable_view.js";
|
||||
@ -383,7 +384,7 @@ class PDFThumbnailViewer {
|
||||
? this.getStructuralChanges()
|
||||
: [{ document: null }];
|
||||
data.push(...entries);
|
||||
this.eventBus._on(
|
||||
this.eventBus.on(
|
||||
"pagesloaded",
|
||||
() => {
|
||||
// Clear any pre-merge selection: thumbnails are rebuilt fresh
|
||||
@ -406,7 +407,7 @@ class PDFThumbnailViewer {
|
||||
this.#updateCurrentPage(insertAfter + 2, /* force = */ true);
|
||||
}
|
||||
},
|
||||
{ once: true }
|
||||
{ once: true, ...internalOpt }
|
||||
);
|
||||
this.#reportTelemetry({ action: "merge" });
|
||||
this.eventBus.dispatch("saveandload", {
|
||||
|
||||
@ -66,6 +66,7 @@ import {
|
||||
watchScroll,
|
||||
} from "./ui_utils.js";
|
||||
import { GenericL10n } from "web-null_l10n";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { PDFPageView } from "./pdf_page_view.js";
|
||||
import { PDFRenderingQueue } from "./pdf_rendering_queue.js";
|
||||
import { RenderingStates } from "./renderable_view.js";
|
||||
@ -258,7 +259,7 @@ class PDFViewer {
|
||||
|
||||
#abortSignal = null;
|
||||
|
||||
#eventAbortController = null;
|
||||
#eventAC = null;
|
||||
|
||||
#minDurationToUpdateCanvas = 0;
|
||||
|
||||
@ -420,12 +421,16 @@ class PDFViewer {
|
||||
|
||||
// Trigger API-cleanup, once thumbnail rendering has finished,
|
||||
// if the relevant pageView is *not* cached in the buffer.
|
||||
this.eventBus._on("thumbnailrendered", ({ pageNumber, pdfPage }) => {
|
||||
const pageView = this._pages[pageNumber - 1];
|
||||
if (!this.#buffer.has(pageView)) {
|
||||
pdfPage?.cleanup();
|
||||
}
|
||||
});
|
||||
this.eventBus.on(
|
||||
"thumbnailrendered",
|
||||
({ pageNumber, pdfPage }) => {
|
||||
const pageView = this._pages[pageNumber - 1];
|
||||
if (!this.#buffer.has(pageView)) {
|
||||
pdfPage?.cleanup();
|
||||
}
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
|
||||
if (
|
||||
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
|
||||
@ -937,8 +942,9 @@ class PDFViewer {
|
||||
|
||||
const { eventBus, pageColors, viewer } = this;
|
||||
|
||||
this.#eventAbortController = new AbortController();
|
||||
const { signal } = this.#eventAbortController;
|
||||
this.#eventAC = new AbortController();
|
||||
const { signal } = this.#eventAC;
|
||||
const evtOpts = { signal, ...internalOpt };
|
||||
|
||||
// Given that browsers don't handle huge amounts of DOM-elements very well,
|
||||
// enforce usage of PAGE-scrolling when loading *very* long/large documents.
|
||||
@ -968,7 +974,7 @@ class PDFViewer {
|
||||
// evicted from the buffer and destroyed even if we pause its rendering.
|
||||
this.#buffer.push(pageView);
|
||||
};
|
||||
eventBus._on("pagerender", onBeforeDraw, { signal });
|
||||
eventBus.on("pagerender", onBeforeDraw, evtOpts);
|
||||
|
||||
const onAfterDraw = evt => {
|
||||
if (evt.cssTransform || evt.isDetailView) {
|
||||
@ -976,9 +982,9 @@ class PDFViewer {
|
||||
}
|
||||
this._onePageRenderedCapability.resolve({ timestamp: evt.timestamp });
|
||||
|
||||
eventBus._off("pagerendered", onAfterDraw); // Remove immediately.
|
||||
eventBus.off("pagerendered", onAfterDraw); // Remove immediately.
|
||||
};
|
||||
eventBus._on("pagerendered", onAfterDraw, { signal });
|
||||
eventBus.on("pagerendered", onAfterDraw, evtOpts);
|
||||
|
||||
// Fetch a single page so we can get a viewport that will be the default
|
||||
// viewport for all pages
|
||||
@ -1120,7 +1126,7 @@ class PDFViewer {
|
||||
this._updateSpreadMode();
|
||||
}
|
||||
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"annotationeditorlayerrendered",
|
||||
evt => {
|
||||
if (this.#annotationEditorUIManager) {
|
||||
@ -1131,7 +1137,7 @@ class PDFViewer {
|
||||
});
|
||||
}
|
||||
},
|
||||
{ once: true, signal }
|
||||
{ once: true, signal, ...internalOpt }
|
||||
);
|
||||
|
||||
// Fetch all the pages since the viewport is needed before printing
|
||||
@ -1368,8 +1374,8 @@ class PDFViewer {
|
||||
pages: [],
|
||||
};
|
||||
|
||||
this.#eventAbortController?.abort();
|
||||
this.#eventAbortController = null;
|
||||
this.#eventAC?.abort();
|
||||
this.#eventAC = null;
|
||||
|
||||
// Remove the pages from the DOM...
|
||||
this.viewer.textContent = "";
|
||||
@ -2688,11 +2694,11 @@ class PDFViewer {
|
||||
this.#cleanupSwitchAnnotationEditorMode();
|
||||
this.#switchAnnotationEditorModeAC = new AbortController();
|
||||
const signal = AbortSignal.any([
|
||||
this.#eventAbortController.signal,
|
||||
this.#eventAC.signal,
|
||||
this.#switchAnnotationEditorModeAC.signal,
|
||||
]);
|
||||
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"pagerendered",
|
||||
({ pageNumber }) => {
|
||||
idsToRefresh.delete(pageNumber);
|
||||
@ -2703,7 +2709,7 @@ class PDFViewer {
|
||||
);
|
||||
}
|
||||
},
|
||||
{ signal }
|
||||
{ signal, ...internalOpt }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import {
|
||||
toggleCheckedBtn,
|
||||
toggleExpandedBtn,
|
||||
} from "./ui_utils.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { PagesCountLimit } from "./pdf_viewer.js";
|
||||
|
||||
/**
|
||||
@ -235,9 +236,21 @@ class SecondaryToolbar {
|
||||
});
|
||||
}
|
||||
|
||||
eventBus._on("cursortoolchanged", this.#cursorToolChanged.bind(this));
|
||||
eventBus._on("scrollmodechanged", this.#scrollModeChanged.bind(this));
|
||||
eventBus._on("spreadmodechanged", this.#spreadModeChanged.bind(this));
|
||||
eventBus.on(
|
||||
"cursortoolchanged",
|
||||
this.#cursorToolChanged.bind(this),
|
||||
internalOpt
|
||||
);
|
||||
eventBus.on(
|
||||
"scrollmodechanged",
|
||||
this.#scrollModeChanged.bind(this),
|
||||
internalOpt
|
||||
);
|
||||
eventBus.on(
|
||||
"spreadmodechanged",
|
||||
this.#spreadModeChanged.bind(this),
|
||||
internalOpt
|
||||
);
|
||||
}
|
||||
|
||||
#cursorToolChanged({ tool, disabled }) {
|
||||
|
||||
@ -21,6 +21,7 @@ import {
|
||||
stopEvent,
|
||||
SupportedImageMimeTypes,
|
||||
} from "pdfjs-lib";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
|
||||
// Default height of the added signature in page coordinates.
|
||||
const DEFAULT_HEIGHT_IN_PAGE = 40;
|
||||
@ -229,7 +230,11 @@ class SignatureManager {
|
||||
this.#initTabButtons(typeButton, drawButton, imageButton, panels);
|
||||
imagePicker.accept = SupportedImageMimeTypes.join(",");
|
||||
|
||||
eventBus._on("storedsignatureschanged", this.#signaturesChanged.bind(this));
|
||||
eventBus.on(
|
||||
"storedsignatureschanged",
|
||||
this.#signaturesChanged.bind(this),
|
||||
internalOpt
|
||||
);
|
||||
|
||||
overlayManager.register(dialog);
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
|
||||
/** @typedef {import("./event_utils").EventBus} EventBus */
|
||||
// eslint-disable-next-line max-len
|
||||
/** @typedef {import("./pdf_find_controller").PDFFindController} PDFFindController */
|
||||
@ -29,7 +31,7 @@
|
||||
* either the text layer or XFA layer depending on the type of document.
|
||||
*/
|
||||
class TextHighlighter {
|
||||
#eventAbortController = null;
|
||||
#eventAC = null;
|
||||
|
||||
/**
|
||||
* @param {TextHighlighterOptions} options
|
||||
@ -71,17 +73,17 @@ class TextHighlighter {
|
||||
}
|
||||
this.enabled = true;
|
||||
|
||||
if (!this.#eventAbortController) {
|
||||
this.#eventAbortController = new AbortController();
|
||||
if (!this.#eventAC) {
|
||||
this.#eventAC = new AbortController();
|
||||
|
||||
this.eventBus._on(
|
||||
this.eventBus.on(
|
||||
"updatetextlayermatches",
|
||||
evt => {
|
||||
if (evt.pageIndex === this.pageIdx || evt.pageIndex === -1) {
|
||||
this._updateMatches();
|
||||
}
|
||||
},
|
||||
{ signal: this.#eventAbortController.signal }
|
||||
{ signal: this.#eventAC.signal, ...internalOpt }
|
||||
);
|
||||
}
|
||||
this._updateMatches();
|
||||
@ -93,8 +95,8 @@ class TextHighlighter {
|
||||
}
|
||||
this.enabled = false;
|
||||
|
||||
this.#eventAbortController?.abort();
|
||||
this.#eventAbortController = null;
|
||||
this.#eventAC?.abort();
|
||||
this.#eventAC = null;
|
||||
|
||||
this._updateMatches(/* reset = */ true);
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ class TextLayerBuilder {
|
||||
|
||||
static #textLayers = new Map();
|
||||
|
||||
static #selectionChangeAbortController = null;
|
||||
static #selectionChangeAC = null;
|
||||
|
||||
/**
|
||||
* @param {TextLayerBuilderOptions} options
|
||||
@ -204,23 +204,20 @@ class TextLayerBuilder {
|
||||
this.#textLayers.delete(textLayerDiv);
|
||||
|
||||
if (this.#textLayers.size === 0) {
|
||||
this.#selectionChangeAbortController?.abort();
|
||||
this.#selectionChangeAbortController = null;
|
||||
this.#selectionChangeAC?.abort();
|
||||
this.#selectionChangeAC = null;
|
||||
}
|
||||
}
|
||||
|
||||
static #enableGlobalSelectionListener(globalAbortSignal) {
|
||||
if (this.#selectionChangeAbortController) {
|
||||
if (this.#selectionChangeAC) {
|
||||
// document-level event listeners already installed
|
||||
return;
|
||||
}
|
||||
this.#selectionChangeAbortController = new AbortController();
|
||||
this.#selectionChangeAC = new AbortController();
|
||||
const signal = globalAbortSignal
|
||||
? AbortSignal.any([
|
||||
this.#selectionChangeAbortController.signal,
|
||||
globalAbortSignal,
|
||||
])
|
||||
: this.#selectionChangeAbortController.signal;
|
||||
? AbortSignal.any([this.#selectionChangeAC.signal, globalAbortSignal])
|
||||
: this.#selectionChangeAC.signal;
|
||||
|
||||
const reset = (end, textLayer) => {
|
||||
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) {
|
||||
|
||||
@ -23,6 +23,7 @@ import {
|
||||
MIN_SCALE,
|
||||
toggleExpandedBtn,
|
||||
} from "./ui_utils.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
|
||||
/**
|
||||
* @typedef {Object} ToolbarOptions
|
||||
@ -237,12 +238,16 @@ class Toolbar {
|
||||
value: this.value,
|
||||
});
|
||||
});
|
||||
eventBus._on("pagesedited", ({ pagesMapper }) => {
|
||||
const pagesCount = pagesMapper.pagesNumber;
|
||||
if (pagesCount !== this.pagesCount) {
|
||||
this.setPagesCount(pagesCount, this.hasPageLabels);
|
||||
}
|
||||
});
|
||||
eventBus.on(
|
||||
"pagesedited",
|
||||
({ pagesMapper }) => {
|
||||
const pagesCount = pagesMapper.pagesNumber;
|
||||
if (pagesCount !== this.pagesCount) {
|
||||
this.setPagesCount(pagesCount, this.hasPageLabels);
|
||||
}
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
|
||||
scaleSelect.addEventListener("change", function () {
|
||||
if (this.value === "custom") {
|
||||
@ -268,29 +273,46 @@ class Toolbar {
|
||||
// Suppress context menus for some controls.
|
||||
scaleSelect.oncontextmenu = noContextMenu;
|
||||
|
||||
eventBus._on(
|
||||
eventBus.on(
|
||||
"annotationeditormodechanged",
|
||||
this.#editorModeChanged.bind(this)
|
||||
this.#editorModeChanged.bind(this),
|
||||
internalOpt
|
||||
);
|
||||
eventBus.on(
|
||||
"showannotationeditorui",
|
||||
({ mode }) => {
|
||||
switch (mode) {
|
||||
case AnnotationEditorType.HIGHLIGHT:
|
||||
editorHighlightButton.click();
|
||||
break;
|
||||
}
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
eventBus.on(
|
||||
"toolbardensity",
|
||||
this.#updateToolbarDensity.bind(this),
|
||||
internalOpt
|
||||
);
|
||||
eventBus._on("showannotationeditorui", ({ mode }) => {
|
||||
switch (mode) {
|
||||
case AnnotationEditorType.HIGHLIGHT:
|
||||
editorHighlightButton.click();
|
||||
break;
|
||||
}
|
||||
});
|
||||
eventBus._on("toolbardensity", this.#updateToolbarDensity.bind(this));
|
||||
|
||||
if (editorHighlightColorPicker) {
|
||||
eventBus._on("annotationeditoruimanager", ({ uiManager }) => {
|
||||
const cp = (this.#colorPicker = new ColorPicker({ uiManager }));
|
||||
uiManager.setMainHighlightColorPicker(cp);
|
||||
editorHighlightColorPicker.append(cp.renderMainDropdown());
|
||||
});
|
||||
eventBus.on(
|
||||
"annotationeditoruimanager",
|
||||
({ uiManager }) => {
|
||||
const cp = (this.#colorPicker = new ColorPicker({ uiManager }));
|
||||
uiManager.setMainHighlightColorPicker(cp);
|
||||
editorHighlightColorPicker.append(cp.renderMainDropdown());
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
|
||||
eventBus._on("mainhighlightcolorpickerupdatecolor", ({ value }) => {
|
||||
this.#colorPicker?.updateColor(value);
|
||||
});
|
||||
eventBus.on(
|
||||
"mainhighlightcolorpickerupdatecolor",
|
||||
({ value }) => {
|
||||
this.#colorPicker?.updateColor(value);
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ import {
|
||||
toggleExpandedBtn,
|
||||
toggleSelectedBtn,
|
||||
} from "./ui_utils.js";
|
||||
import { internalOpt } from "./internal_evt.js";
|
||||
import { Menu } from "./menu.js";
|
||||
import { Sidebar } from "./sidebar.js";
|
||||
|
||||
@ -474,38 +475,54 @@ class ViewsManager extends Sidebar {
|
||||
}
|
||||
};
|
||||
|
||||
eventBus._on("outlineloaded", evt => {
|
||||
onTreeLoaded(evt.outlineCount, this.outlineButton, SidebarView.OUTLINE);
|
||||
eventBus.on(
|
||||
"outlineloaded",
|
||||
evt => {
|
||||
onTreeLoaded(evt.outlineCount, this.outlineButton, SidebarView.OUTLINE);
|
||||
|
||||
evt.currentOutlineItemPromise.then(enabled => {
|
||||
if (!this.isInitialViewSet) {
|
||||
return;
|
||||
}
|
||||
this.viewsManagerCurrentOutlineButton.disabled = !enabled;
|
||||
});
|
||||
});
|
||||
evt.currentOutlineItemPromise.then(enabled => {
|
||||
if (!this.isInitialViewSet) {
|
||||
return;
|
||||
}
|
||||
this.viewsManagerCurrentOutlineButton.disabled = !enabled;
|
||||
});
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
|
||||
eventBus._on("attachmentsloaded", evt => {
|
||||
onTreeLoaded(
|
||||
evt.attachmentsCount,
|
||||
this.attachmentsButton,
|
||||
SidebarView.ATTACHMENTS
|
||||
);
|
||||
});
|
||||
eventBus.on(
|
||||
"attachmentsloaded",
|
||||
evt => {
|
||||
onTreeLoaded(
|
||||
evt.attachmentsCount,
|
||||
this.attachmentsButton,
|
||||
SidebarView.ATTACHMENTS
|
||||
);
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
|
||||
eventBus._on("layersloaded", evt => {
|
||||
onTreeLoaded(evt.layersCount, this.layersButton, SidebarView.LAYERS);
|
||||
});
|
||||
eventBus.on(
|
||||
"layersloaded",
|
||||
evt => {
|
||||
onTreeLoaded(evt.layersCount, this.layersButton, SidebarView.LAYERS);
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
|
||||
// Update the thumbnailViewer, if visible, when exiting presentation mode.
|
||||
eventBus._on("presentationmodechanged", evt => {
|
||||
if (
|
||||
evt.state === PresentationModeState.NORMAL &&
|
||||
this.visibleView === SidebarView.THUMBS
|
||||
) {
|
||||
this.onUpdateThumbnails();
|
||||
}
|
||||
});
|
||||
eventBus.on(
|
||||
"presentationmodechanged",
|
||||
evt => {
|
||||
if (
|
||||
evt.state === PresentationModeState.NORMAL &&
|
||||
this.visibleView === SidebarView.THUMBS
|
||||
) {
|
||||
this.onUpdateThumbnails();
|
||||
}
|
||||
},
|
||||
internalOpt
|
||||
);
|
||||
}
|
||||
|
||||
onStartResizing() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user