mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-08 09:20:06 +02:00
[Editor] Remove event listeners with AbortSignal.any()
There's a fair number of event listeners in the editor-code that we're currently removing "manually", by keeping references to their event handler functions. This was necessary since we have a "global" `AbortController` that applies to all event listeners used in the editor-code, however it's now possible to combine multiple `AbortSignal`s; please see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static Since this functionality is [fairly new](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static#browser_compatibility) the viewer will check that `AbortSignal.any()` is available before enabling the editing-functionality. (It should hopefully be fairly straightforward, famous last words, for users to implement a polyfill to allow editing in older browsers.) Finally, this patch also adds checks and test-only asserts to ensure that we don't add duplicate event listeners in various editor-code.
This commit is contained in:
parent
4569e88778
commit
c0bf3d3c94
9 changed files with 202 additions and 193 deletions
|
@ -61,11 +61,7 @@ class AnnotationEditorLayer {
|
||||||
|
|
||||||
#annotationLayer = null;
|
#annotationLayer = null;
|
||||||
|
|
||||||
#boundPointerup = null;
|
#clickAC = null;
|
||||||
|
|
||||||
#boundPointerdown = null;
|
|
||||||
|
|
||||||
#boundTextLayerPointerDown = null;
|
|
||||||
|
|
||||||
#editorFocusTimeoutId = null;
|
#editorFocusTimeoutId = null;
|
||||||
|
|
||||||
|
@ -79,6 +75,8 @@ class AnnotationEditorLayer {
|
||||||
|
|
||||||
#textLayer = null;
|
#textLayer = null;
|
||||||
|
|
||||||
|
#textSelectionAC = null;
|
||||||
|
|
||||||
#uiManager;
|
#uiManager;
|
||||||
|
|
||||||
static _initialized = false;
|
static _initialized = false;
|
||||||
|
@ -365,12 +363,14 @@ class AnnotationEditorLayer {
|
||||||
|
|
||||||
enableTextSelection() {
|
enableTextSelection() {
|
||||||
this.div.tabIndex = -1;
|
this.div.tabIndex = -1;
|
||||||
if (this.#textLayer?.div && !this.#boundTextLayerPointerDown) {
|
if (this.#textLayer?.div && !this.#textSelectionAC) {
|
||||||
this.#boundTextLayerPointerDown = this.#textLayerPointerDown.bind(this);
|
this.#textSelectionAC = new AbortController();
|
||||||
|
const signal = this.#uiManager.combinedSignal(this.#textSelectionAC);
|
||||||
|
|
||||||
this.#textLayer.div.addEventListener(
|
this.#textLayer.div.addEventListener(
|
||||||
"pointerdown",
|
"pointerdown",
|
||||||
this.#boundTextLayerPointerDown,
|
this.#textLayerPointerDown.bind(this),
|
||||||
{ signal: this.#uiManager._signal }
|
{ signal }
|
||||||
);
|
);
|
||||||
this.#textLayer.div.classList.add("highlighting");
|
this.#textLayer.div.classList.add("highlighting");
|
||||||
}
|
}
|
||||||
|
@ -378,12 +378,10 @@ class AnnotationEditorLayer {
|
||||||
|
|
||||||
disableTextSelection() {
|
disableTextSelection() {
|
||||||
this.div.tabIndex = 0;
|
this.div.tabIndex = 0;
|
||||||
if (this.#textLayer?.div && this.#boundTextLayerPointerDown) {
|
if (this.#textLayer?.div && this.#textSelectionAC) {
|
||||||
this.#textLayer.div.removeEventListener(
|
this.#textSelectionAC.abort();
|
||||||
"pointerdown",
|
this.#textSelectionAC = null;
|
||||||
this.#boundTextLayerPointerDown
|
|
||||||
);
|
|
||||||
this.#boundTextLayerPointerDown = null;
|
|
||||||
this.#textLayer.div.classList.remove("highlighting");
|
this.#textLayer.div.classList.remove("highlighting");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -428,26 +426,23 @@ class AnnotationEditorLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
enableClick() {
|
enableClick() {
|
||||||
if (this.#boundPointerdown) {
|
if (this.#clickAC) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const signal = this.#uiManager._signal;
|
this.#clickAC = new AbortController();
|
||||||
this.#boundPointerdown = this.pointerdown.bind(this);
|
const signal = this.#uiManager.combinedSignal(this.#clickAC);
|
||||||
this.#boundPointerup = this.pointerup.bind(this);
|
|
||||||
this.div.addEventListener("pointerdown", this.#boundPointerdown, {
|
this.div.addEventListener("pointerdown", this.pointerdown.bind(this), {
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
this.div.addEventListener("pointerup", this.pointerup.bind(this), {
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
this.div.addEventListener("pointerup", this.#boundPointerup, { signal });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disableClick() {
|
disableClick() {
|
||||||
if (!this.#boundPointerdown) {
|
this.#clickAC?.abort();
|
||||||
return;
|
this.#clickAC = null;
|
||||||
}
|
|
||||||
this.div.removeEventListener("pointerdown", this.#boundPointerdown);
|
|
||||||
this.div.removeEventListener("pointerup", this.#boundPointerup);
|
|
||||||
this.#boundPointerdown = null;
|
|
||||||
this.#boundPointerup = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attach(editor) {
|
attach(editor) {
|
||||||
|
@ -611,8 +606,8 @@ class AnnotationEditorLayer {
|
||||||
return AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode());
|
return AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
get _signal() {
|
combinedSignal(ac) {
|
||||||
return this.#uiManager._signal;
|
return this.#uiManager.combinedSignal(ac);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -54,9 +54,7 @@ class AnnotationEditor {
|
||||||
|
|
||||||
#savedDimensions = null;
|
#savedDimensions = null;
|
||||||
|
|
||||||
#boundFocusin = this.focusin.bind(this);
|
#focusAC = null;
|
||||||
|
|
||||||
#boundFocusout = this.focusout.bind(this);
|
|
||||||
|
|
||||||
#focusedResizerName = "";
|
#focusedResizerName = "";
|
||||||
|
|
||||||
|
@ -758,16 +756,17 @@ class AnnotationEditor {
|
||||||
|
|
||||||
this.#altText?.toggle(false);
|
this.#altText?.toggle(false);
|
||||||
|
|
||||||
const boundResizerPointermove = this.#resizerPointermove.bind(this, name);
|
|
||||||
const savedDraggable = this._isDraggable;
|
const savedDraggable = this._isDraggable;
|
||||||
this._isDraggable = false;
|
this._isDraggable = false;
|
||||||
const signal = this._uiManager._signal;
|
|
||||||
const pointerMoveOptions = { passive: true, capture: true, signal };
|
const ac = new AbortController();
|
||||||
|
const signal = this._uiManager.combinedSignal(ac);
|
||||||
|
|
||||||
this.parent.togglePointerEvents(false);
|
this.parent.togglePointerEvents(false);
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
"pointermove",
|
"pointermove",
|
||||||
boundResizerPointermove,
|
this.#resizerPointermove.bind(this, name),
|
||||||
pointerMoveOptions
|
{ passive: true, capture: true, signal }
|
||||||
);
|
);
|
||||||
window.addEventListener("contextmenu", noContextMenu, { signal });
|
window.addEventListener("contextmenu", noContextMenu, { signal });
|
||||||
const savedX = this.x;
|
const savedX = this.x;
|
||||||
|
@ -780,17 +779,10 @@ class AnnotationEditor {
|
||||||
window.getComputedStyle(event.target).cursor;
|
window.getComputedStyle(event.target).cursor;
|
||||||
|
|
||||||
const pointerUpCallback = () => {
|
const pointerUpCallback = () => {
|
||||||
|
ac.abort();
|
||||||
this.parent.togglePointerEvents(true);
|
this.parent.togglePointerEvents(true);
|
||||||
this.#altText?.toggle(true);
|
this.#altText?.toggle(true);
|
||||||
this._isDraggable = savedDraggable;
|
this._isDraggable = savedDraggable;
|
||||||
window.removeEventListener("pointerup", pointerUpCallback);
|
|
||||||
window.removeEventListener("blur", pointerUpCallback);
|
|
||||||
window.removeEventListener(
|
|
||||||
"pointermove",
|
|
||||||
boundResizerPointermove,
|
|
||||||
pointerMoveOptions
|
|
||||||
);
|
|
||||||
window.removeEventListener("contextmenu", noContextMenu);
|
|
||||||
this.parent.div.style.cursor = savedParentCursor;
|
this.parent.div.style.cursor = savedParentCursor;
|
||||||
this.div.style.cursor = savedCursor;
|
this.div.style.cursor = savedCursor;
|
||||||
|
|
||||||
|
@ -1069,10 +1061,7 @@ class AnnotationEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setInForeground();
|
this.setInForeground();
|
||||||
|
this.#addFocusListeners();
|
||||||
const signal = this._uiManager._signal;
|
|
||||||
this.div.addEventListener("focusin", this.#boundFocusin, { signal });
|
|
||||||
this.div.addEventListener("focusout", this.#boundFocusout, { signal });
|
|
||||||
|
|
||||||
const [parentWidth, parentHeight] = this.parentDimensions;
|
const [parentWidth, parentHeight] = this.parentDimensions;
|
||||||
if (this.parentRotation % 180 !== 0) {
|
if (this.parentRotation % 180 !== 0) {
|
||||||
|
@ -1132,14 +1121,14 @@ class AnnotationEditor {
|
||||||
const isSelected = this._uiManager.isSelected(this);
|
const isSelected = this._uiManager.isSelected(this);
|
||||||
this._uiManager.setUpDragSession();
|
this._uiManager.setUpDragSession();
|
||||||
|
|
||||||
let pointerMoveOptions, pointerMoveCallback;
|
const ac = new AbortController();
|
||||||
const signal = this._uiManager._signal;
|
const signal = this._uiManager.combinedSignal(ac);
|
||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
this.div.classList.add("moving");
|
this.div.classList.add("moving");
|
||||||
pointerMoveOptions = { passive: true, capture: true, signal };
|
|
||||||
this.#prevDragX = event.clientX;
|
this.#prevDragX = event.clientX;
|
||||||
this.#prevDragY = event.clientY;
|
this.#prevDragY = event.clientY;
|
||||||
pointerMoveCallback = e => {
|
const pointerMoveCallback = e => {
|
||||||
const { clientX: x, clientY: y } = e;
|
const { clientX: x, clientY: y } = e;
|
||||||
const [tx, ty] = this.screenToPageTranslation(
|
const [tx, ty] = this.screenToPageTranslation(
|
||||||
x - this.#prevDragX,
|
x - this.#prevDragX,
|
||||||
|
@ -1149,23 +1138,17 @@ class AnnotationEditor {
|
||||||
this.#prevDragY = y;
|
this.#prevDragY = y;
|
||||||
this._uiManager.dragSelectedEditors(tx, ty);
|
this._uiManager.dragSelectedEditors(tx, ty);
|
||||||
};
|
};
|
||||||
window.addEventListener(
|
window.addEventListener("pointermove", pointerMoveCallback, {
|
||||||
"pointermove",
|
passive: true,
|
||||||
pointerMoveCallback,
|
capture: true,
|
||||||
pointerMoveOptions
|
signal,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const pointerUpCallback = () => {
|
const pointerUpCallback = () => {
|
||||||
window.removeEventListener("pointerup", pointerUpCallback);
|
ac.abort();
|
||||||
window.removeEventListener("blur", pointerUpCallback);
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
this.div.classList.remove("moving");
|
this.div.classList.remove("moving");
|
||||||
window.removeEventListener(
|
|
||||||
"pointermove",
|
|
||||||
pointerMoveCallback,
|
|
||||||
pointerMoveOptions
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#hasBeenClicked = false;
|
this.#hasBeenClicked = false;
|
||||||
|
@ -1323,15 +1306,24 @@ class AnnotationEditor {
|
||||||
return this.div && !this.isAttachedToDOM;
|
return this.div && !this.isAttachedToDOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#addFocusListeners() {
|
||||||
|
if (this.#focusAC || !this.div) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#focusAC = new AbortController();
|
||||||
|
const signal = this._uiManager.combinedSignal(this.#focusAC);
|
||||||
|
|
||||||
|
this.div.addEventListener("focusin", this.focusin.bind(this), { signal });
|
||||||
|
this.div.addEventListener("focusout", this.focusout.bind(this), { signal });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rebuild the editor in case it has been removed on undo.
|
* Rebuild the editor in case it has been removed on undo.
|
||||||
*
|
*
|
||||||
* To implement in subclasses.
|
* To implement in subclasses.
|
||||||
*/
|
*/
|
||||||
rebuild() {
|
rebuild() {
|
||||||
const signal = this._uiManager._signal;
|
this.#addFocusListeners();
|
||||||
this.div?.addEventListener("focusin", this.#boundFocusin, { signal });
|
|
||||||
this.div?.addEventListener("focusout", this.#boundFocusout, { signal });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1401,8 +1393,8 @@ class AnnotationEditor {
|
||||||
* It's used on ctrl+backspace action.
|
* It's used on ctrl+backspace action.
|
||||||
*/
|
*/
|
||||||
remove() {
|
remove() {
|
||||||
this.div.removeEventListener("focusin", this.#boundFocusin);
|
this.#focusAC?.abort();
|
||||||
this.div.removeEventListener("focusout", this.#boundFocusout);
|
this.#focusAC = null;
|
||||||
|
|
||||||
if (!this.isEmpty()) {
|
if (!this.isEmpty()) {
|
||||||
// The editor is removed but it can be back at some point thanks to
|
// The editor is removed but it can be back at some point thanks to
|
||||||
|
|
|
@ -38,22 +38,14 @@ const EOL_PATTERN = /\r\n?|\n/g;
|
||||||
* Basic text editor in order to create a FreeTex annotation.
|
* Basic text editor in order to create a FreeTex annotation.
|
||||||
*/
|
*/
|
||||||
class FreeTextEditor extends AnnotationEditor {
|
class FreeTextEditor extends AnnotationEditor {
|
||||||
#boundEditorDivBlur = this.editorDivBlur.bind(this);
|
|
||||||
|
|
||||||
#boundEditorDivFocus = this.editorDivFocus.bind(this);
|
|
||||||
|
|
||||||
#boundEditorDivInput = this.editorDivInput.bind(this);
|
|
||||||
|
|
||||||
#boundEditorDivKeydown = this.editorDivKeydown.bind(this);
|
|
||||||
|
|
||||||
#boundEditorDivPaste = this.editorDivPaste.bind(this);
|
|
||||||
|
|
||||||
#color;
|
#color;
|
||||||
|
|
||||||
#content = "";
|
#content = "";
|
||||||
|
|
||||||
#editorDivId = `${this.id}-editor`;
|
#editorDivId = `${this.id}-editor`;
|
||||||
|
|
||||||
|
#editModeAC = null;
|
||||||
|
|
||||||
#fontSize;
|
#fontSize;
|
||||||
|
|
||||||
#initialData = null;
|
#initialData = null;
|
||||||
|
@ -307,20 +299,31 @@ class FreeTextEditor extends AnnotationEditor {
|
||||||
this.editorDiv.contentEditable = true;
|
this.editorDiv.contentEditable = true;
|
||||||
this._isDraggable = false;
|
this._isDraggable = false;
|
||||||
this.div.removeAttribute("aria-activedescendant");
|
this.div.removeAttribute("aria-activedescendant");
|
||||||
const signal = this._uiManager._signal;
|
|
||||||
this.editorDiv.addEventListener("keydown", this.#boundEditorDivKeydown, {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||||
|
assert(
|
||||||
|
!this.#editModeAC,
|
||||||
|
"No `this.#editModeAC` AbortController should exist."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.#editModeAC = new AbortController();
|
||||||
|
const signal = this._uiManager.combinedSignal(this.#editModeAC);
|
||||||
|
|
||||||
|
this.editorDiv.addEventListener(
|
||||||
|
"keydown",
|
||||||
|
this.editorDivKeydown.bind(this),
|
||||||
|
{ signal }
|
||||||
|
);
|
||||||
|
this.editorDiv.addEventListener("focus", this.editorDivFocus.bind(this), {
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
this.editorDiv.addEventListener("focus", this.#boundEditorDivFocus, {
|
this.editorDiv.addEventListener("blur", this.editorDivBlur.bind(this), {
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
this.editorDiv.addEventListener("blur", this.#boundEditorDivBlur, {
|
this.editorDiv.addEventListener("input", this.editorDivInput.bind(this), {
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
this.editorDiv.addEventListener("input", this.#boundEditorDivInput, {
|
this.editorDiv.addEventListener("paste", this.editorDivPaste.bind(this), {
|
||||||
signal,
|
|
||||||
});
|
|
||||||
this.editorDiv.addEventListener("paste", this.#boundEditorDivPaste, {
|
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -337,11 +340,9 @@ class FreeTextEditor extends AnnotationEditor {
|
||||||
this.editorDiv.contentEditable = false;
|
this.editorDiv.contentEditable = false;
|
||||||
this.div.setAttribute("aria-activedescendant", this.#editorDivId);
|
this.div.setAttribute("aria-activedescendant", this.#editorDivId);
|
||||||
this._isDraggable = true;
|
this._isDraggable = true;
|
||||||
this.editorDiv.removeEventListener("keydown", this.#boundEditorDivKeydown);
|
|
||||||
this.editorDiv.removeEventListener("focus", this.#boundEditorDivFocus);
|
this.#editModeAC?.abort();
|
||||||
this.editorDiv.removeEventListener("blur", this.#boundEditorDivBlur);
|
this.#editModeAC = null;
|
||||||
this.editorDiv.removeEventListener("input", this.#boundEditorDivInput);
|
|
||||||
this.editorDiv.removeEventListener("paste", this.#boundEditorDivPaste);
|
|
||||||
|
|
||||||
// On Chrome, the focus is given to <body> when contentEditable is set to
|
// On Chrome, the focus is given to <body> when contentEditable is set to
|
||||||
// false, hence we focus the div.
|
// false, hence we focus the div.
|
||||||
|
|
|
@ -700,34 +700,33 @@ class HighlightEditor extends AnnotationEditor {
|
||||||
width: parentWidth,
|
width: parentWidth,
|
||||||
height: parentHeight,
|
height: parentHeight,
|
||||||
} = textLayer.getBoundingClientRect();
|
} = textLayer.getBoundingClientRect();
|
||||||
const pointerMove = e => {
|
|
||||||
this.#highlightMove(parent, e);
|
const ac = new AbortController();
|
||||||
};
|
const signal = parent.combinedSignal(ac);
|
||||||
const signal = parent._signal;
|
|
||||||
const pointerDownOptions = { capture: true, passive: false, signal };
|
|
||||||
const pointerDown = e => {
|
const pointerDown = e => {
|
||||||
// Avoid to have undesired clicks during the drawing.
|
// Avoid to have undesired clicks during the drawing.
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
const pointerUpCallback = e => {
|
const pointerUpCallback = e => {
|
||||||
textLayer.removeEventListener("pointermove", pointerMove);
|
ac.abort();
|
||||||
window.removeEventListener("blur", pointerUpCallback);
|
|
||||||
window.removeEventListener("pointerup", pointerUpCallback);
|
|
||||||
window.removeEventListener(
|
|
||||||
"pointerdown",
|
|
||||||
pointerDown,
|
|
||||||
pointerDownOptions
|
|
||||||
);
|
|
||||||
window.removeEventListener("contextmenu", noContextMenu);
|
|
||||||
this.#endHighlight(parent, e);
|
this.#endHighlight(parent, e);
|
||||||
};
|
};
|
||||||
window.addEventListener("blur", pointerUpCallback, { signal });
|
window.addEventListener("blur", pointerUpCallback, { signal });
|
||||||
window.addEventListener("pointerup", pointerUpCallback, { signal });
|
window.addEventListener("pointerup", pointerUpCallback, { signal });
|
||||||
window.addEventListener("pointerdown", pointerDown, pointerDownOptions);
|
window.addEventListener("pointerdown", pointerDown, {
|
||||||
|
capture: true,
|
||||||
|
passive: false,
|
||||||
|
signal,
|
||||||
|
});
|
||||||
window.addEventListener("contextmenu", noContextMenu, { signal });
|
window.addEventListener("contextmenu", noContextMenu, { signal });
|
||||||
|
|
||||||
textLayer.addEventListener("pointermove", pointerMove, { signal });
|
textLayer.addEventListener(
|
||||||
|
"pointermove",
|
||||||
|
this.#highlightMove.bind(this, parent),
|
||||||
|
{ signal }
|
||||||
|
);
|
||||||
this._freeHighlight = new FreeOutliner(
|
this._freeHighlight = new FreeOutliner(
|
||||||
{ x, y },
|
{ x, y },
|
||||||
[layerX, layerY, parentWidth, parentHeight],
|
[layerX, layerY, parentWidth, parentHeight],
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import {
|
import {
|
||||||
AnnotationEditorParamsType,
|
AnnotationEditorParamsType,
|
||||||
AnnotationEditorType,
|
AnnotationEditorType,
|
||||||
|
assert,
|
||||||
Util,
|
Util,
|
||||||
} from "../../shared/util.js";
|
} from "../../shared/util.js";
|
||||||
import { AnnotationEditor } from "./editor.js";
|
import { AnnotationEditor } from "./editor.js";
|
||||||
|
@ -31,26 +32,22 @@ class InkEditor extends AnnotationEditor {
|
||||||
|
|
||||||
#baseWidth = 0;
|
#baseWidth = 0;
|
||||||
|
|
||||||
#boundCanvasPointermove = this.canvasPointermove.bind(this);
|
|
||||||
|
|
||||||
#boundCanvasPointerleave = this.canvasPointerleave.bind(this);
|
|
||||||
|
|
||||||
#boundCanvasPointerup = this.canvasPointerup.bind(this);
|
|
||||||
|
|
||||||
#boundCanvasPointerdown = this.canvasPointerdown.bind(this);
|
|
||||||
|
|
||||||
#canvasContextMenuTimeoutId = null;
|
#canvasContextMenuTimeoutId = null;
|
||||||
|
|
||||||
#currentPath2D = new Path2D();
|
#currentPath2D = new Path2D();
|
||||||
|
|
||||||
#disableEditing = false;
|
#disableEditing = false;
|
||||||
|
|
||||||
|
#drawingAC = null;
|
||||||
|
|
||||||
#hasSomethingToDraw = false;
|
#hasSomethingToDraw = false;
|
||||||
|
|
||||||
#isCanvasInitialized = false;
|
#isCanvasInitialized = false;
|
||||||
|
|
||||||
#observer = null;
|
#observer = null;
|
||||||
|
|
||||||
|
#pointerdownAC = null;
|
||||||
|
|
||||||
#realWidth = 0;
|
#realWidth = 0;
|
||||||
|
|
||||||
#realHeight = 0;
|
#realHeight = 0;
|
||||||
|
@ -296,9 +293,7 @@ class InkEditor extends AnnotationEditor {
|
||||||
|
|
||||||
super.enableEditMode();
|
super.enableEditMode();
|
||||||
this._isDraggable = false;
|
this._isDraggable = false;
|
||||||
this.canvas.addEventListener("pointerdown", this.#boundCanvasPointerdown, {
|
this.#addPointerdownListener();
|
||||||
signal: this._uiManager._signal,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
|
@ -310,11 +305,7 @@ class InkEditor extends AnnotationEditor {
|
||||||
super.disableEditMode();
|
super.disableEditMode();
|
||||||
this._isDraggable = !this.isEmpty();
|
this._isDraggable = !this.isEmpty();
|
||||||
this.div.classList.remove("editing");
|
this.div.classList.remove("editing");
|
||||||
|
this.#removePointerdownListener();
|
||||||
this.canvas.removeEventListener(
|
|
||||||
"pointerdown",
|
|
||||||
this.#boundCanvasPointerdown
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
|
@ -365,23 +356,33 @@ class InkEditor extends AnnotationEditor {
|
||||||
* @param {number} y
|
* @param {number} y
|
||||||
*/
|
*/
|
||||||
#startDrawing(x, y) {
|
#startDrawing(x, y) {
|
||||||
const signal = this._uiManager._signal;
|
this.canvas.addEventListener("contextmenu", noContextMenu, {
|
||||||
this.canvas.addEventListener("contextmenu", noContextMenu, { signal });
|
signal: this._uiManager._signal,
|
||||||
|
});
|
||||||
|
this.#removePointerdownListener();
|
||||||
|
|
||||||
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
|
||||||
|
assert(
|
||||||
|
!this.#drawingAC,
|
||||||
|
"No `this.#drawingAC` AbortController should exist."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.#drawingAC = new AbortController();
|
||||||
|
const signal = this._uiManager.combinedSignal(this.#drawingAC);
|
||||||
|
|
||||||
this.canvas.addEventListener(
|
this.canvas.addEventListener(
|
||||||
"pointerleave",
|
"pointerleave",
|
||||||
this.#boundCanvasPointerleave,
|
this.canvasPointerleave.bind(this),
|
||||||
{ signal }
|
{ signal }
|
||||||
);
|
);
|
||||||
this.canvas.addEventListener("pointermove", this.#boundCanvasPointermove, {
|
this.canvas.addEventListener(
|
||||||
signal,
|
"pointermove",
|
||||||
});
|
this.canvasPointermove.bind(this),
|
||||||
this.canvas.addEventListener("pointerup", this.#boundCanvasPointerup, {
|
{ signal }
|
||||||
signal,
|
|
||||||
});
|
|
||||||
this.canvas.removeEventListener(
|
|
||||||
"pointerdown",
|
|
||||||
this.#boundCanvasPointerdown
|
|
||||||
);
|
);
|
||||||
|
this.canvas.addEventListener("pointerup", this.canvasPointerup.bind(this), {
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
|
||||||
this.isEditing = true;
|
this.isEditing = true;
|
||||||
if (!this.#isCanvasInitialized) {
|
if (!this.#isCanvasInitialized) {
|
||||||
|
@ -653,6 +654,25 @@ class InkEditor extends AnnotationEditor {
|
||||||
this.enableEditMode();
|
this.enableEditMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#addPointerdownListener() {
|
||||||
|
if (this.#pointerdownAC) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#pointerdownAC = new AbortController();
|
||||||
|
const signal = this._uiManager.combinedSignal(this.#pointerdownAC);
|
||||||
|
|
||||||
|
this.canvas.addEventListener(
|
||||||
|
"pointerdown",
|
||||||
|
this.canvasPointerdown.bind(this),
|
||||||
|
{ signal }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#removePointerdownListener() {
|
||||||
|
this.pointerdownAC?.abort();
|
||||||
|
this.pointerdownAC = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* onpointerdown callback for the canvas we're drawing on.
|
* onpointerdown callback for the canvas we're drawing on.
|
||||||
* @param {PointerEvent} event
|
* @param {PointerEvent} event
|
||||||
|
@ -708,19 +728,10 @@ class InkEditor extends AnnotationEditor {
|
||||||
* @param {PointerEvent} event
|
* @param {PointerEvent} event
|
||||||
*/
|
*/
|
||||||
#endDrawing(event) {
|
#endDrawing(event) {
|
||||||
this.canvas.removeEventListener(
|
this.#drawingAC?.abort();
|
||||||
"pointerleave",
|
this.#drawingAC = null;
|
||||||
this.#boundCanvasPointerleave
|
|
||||||
);
|
|
||||||
this.canvas.removeEventListener(
|
|
||||||
"pointermove",
|
|
||||||
this.#boundCanvasPointermove
|
|
||||||
);
|
|
||||||
this.canvas.removeEventListener("pointerup", this.#boundCanvasPointerup);
|
|
||||||
this.canvas.addEventListener("pointerdown", this.#boundCanvasPointerdown, {
|
|
||||||
signal: this._uiManager._signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
this.#addPointerdownListener();
|
||||||
// Slight delay to avoid the context menu to appear (it can happen on a long
|
// Slight delay to avoid the context menu to appear (it can happen on a long
|
||||||
// tap with a pen).
|
// tap with a pen).
|
||||||
if (this.#canvasContextMenuTimeoutId) {
|
if (this.#canvasContextMenuTimeoutId) {
|
||||||
|
|
|
@ -550,6 +550,8 @@ class AnnotationEditorUIManager {
|
||||||
|
|
||||||
#commandManager = new CommandManager();
|
#commandManager = new CommandManager();
|
||||||
|
|
||||||
|
#copyPasteAC = null;
|
||||||
|
|
||||||
#currentPageIndex = 0;
|
#currentPageIndex = 0;
|
||||||
|
|
||||||
#deletedAnnotationsElementIds = new Set();
|
#deletedAnnotationsElementIds = new Set();
|
||||||
|
@ -570,6 +572,8 @@ class AnnotationEditorUIManager {
|
||||||
|
|
||||||
#focusMainContainerTimeoutId = null;
|
#focusMainContainerTimeoutId = null;
|
||||||
|
|
||||||
|
#focusManagerAC = null;
|
||||||
|
|
||||||
#highlightColors = null;
|
#highlightColors = null;
|
||||||
|
|
||||||
#highlightWhenShiftUp = false;
|
#highlightWhenShiftUp = false;
|
||||||
|
@ -582,6 +586,8 @@ class AnnotationEditorUIManager {
|
||||||
|
|
||||||
#isWaiting = false;
|
#isWaiting = false;
|
||||||
|
|
||||||
|
#keyboardManagerAC = null;
|
||||||
|
|
||||||
#lastActiveElement = null;
|
#lastActiveElement = null;
|
||||||
|
|
||||||
#mainHighlightColorPicker = null;
|
#mainHighlightColorPicker = null;
|
||||||
|
@ -598,20 +604,6 @@ class AnnotationEditorUIManager {
|
||||||
|
|
||||||
#showAllStates = null;
|
#showAllStates = null;
|
||||||
|
|
||||||
#boundBlur = this.blur.bind(this);
|
|
||||||
|
|
||||||
#boundFocus = this.focus.bind(this);
|
|
||||||
|
|
||||||
#boundCopy = this.copy.bind(this);
|
|
||||||
|
|
||||||
#boundCut = this.cut.bind(this);
|
|
||||||
|
|
||||||
#boundPaste = this.paste.bind(this);
|
|
||||||
|
|
||||||
#boundKeydown = this.keydown.bind(this);
|
|
||||||
|
|
||||||
#boundKeyup = this.keyup.bind(this);
|
|
||||||
|
|
||||||
#previousStates = {
|
#previousStates = {
|
||||||
isEditing: false,
|
isEditing: false,
|
||||||
isEmpty: true,
|
isEmpty: true,
|
||||||
|
@ -855,6 +847,10 @@ class AnnotationEditorUIManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
combinedSignal(ac) {
|
||||||
|
return AbortSignal.any([this._signal, ac.signal]);
|
||||||
|
}
|
||||||
|
|
||||||
get mlManager() {
|
get mlManager() {
|
||||||
return this.#mlManager;
|
return this.#mlManager;
|
||||||
}
|
}
|
||||||
|
@ -1142,15 +1138,16 @@ class AnnotationEditorUIManager {
|
||||||
: null;
|
: null;
|
||||||
activeLayer?.toggleDrawing();
|
activeLayer?.toggleDrawing();
|
||||||
|
|
||||||
const signal = this._signal;
|
const ac = new AbortController();
|
||||||
|
const signal = this.combinedSignal(ac);
|
||||||
|
|
||||||
const pointerup = e => {
|
const pointerup = e => {
|
||||||
if (e.type === "pointerup" && e.button !== 0) {
|
if (e.type === "pointerup" && e.button !== 0) {
|
||||||
// Do nothing on right click.
|
// Do nothing on right click.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
ac.abort();
|
||||||
activeLayer?.toggleDrawing(true);
|
activeLayer?.toggleDrawing(true);
|
||||||
window.removeEventListener("pointerup", pointerup);
|
|
||||||
window.removeEventListener("blur", pointerup);
|
|
||||||
if (e.type === "pointerup") {
|
if (e.type === "pointerup") {
|
||||||
this.#onSelectEnd("main_toolbar");
|
this.#onSelectEnd("main_toolbar");
|
||||||
}
|
}
|
||||||
|
@ -1172,21 +1169,24 @@ class AnnotationEditorUIManager {
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
"selectionchange",
|
"selectionchange",
|
||||||
this.#selectionChange.bind(this),
|
this.#selectionChange.bind(this),
|
||||||
{
|
{ signal: this._signal }
|
||||||
signal: this._signal,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#addFocusManager() {
|
#addFocusManager() {
|
||||||
const signal = this._signal;
|
if (this.#focusManagerAC) {
|
||||||
window.addEventListener("focus", this.#boundFocus, { signal });
|
return;
|
||||||
window.addEventListener("blur", this.#boundBlur, { signal });
|
}
|
||||||
|
this.#focusManagerAC = new AbortController();
|
||||||
|
const signal = this.combinedSignal(this.#focusManagerAC);
|
||||||
|
|
||||||
|
window.addEventListener("focus", this.focus.bind(this), { signal });
|
||||||
|
window.addEventListener("blur", this.blur.bind(this), { signal });
|
||||||
}
|
}
|
||||||
|
|
||||||
#removeFocusManager() {
|
#removeFocusManager() {
|
||||||
window.removeEventListener("focus", this.#boundFocus);
|
this.#focusManagerAC?.abort();
|
||||||
window.removeEventListener("blur", this.#boundBlur);
|
this.#focusManagerAC = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
blur() {
|
blur() {
|
||||||
|
@ -1229,29 +1229,38 @@ class AnnotationEditorUIManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
#addKeyboardManager() {
|
#addKeyboardManager() {
|
||||||
const signal = this._signal;
|
if (this.#keyboardManagerAC) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#keyboardManagerAC = new AbortController();
|
||||||
|
const signal = this.combinedSignal(this.#keyboardManagerAC);
|
||||||
|
|
||||||
// The keyboard events are caught at the container level in order to be able
|
// The keyboard events are caught at the container level in order to be able
|
||||||
// to execute some callbacks even if the current page doesn't have focus.
|
// to execute some callbacks even if the current page doesn't have focus.
|
||||||
window.addEventListener("keydown", this.#boundKeydown, { signal });
|
window.addEventListener("keydown", this.keydown.bind(this), { signal });
|
||||||
window.addEventListener("keyup", this.#boundKeyup, { signal });
|
window.addEventListener("keyup", this.keyup.bind(this), { signal });
|
||||||
}
|
}
|
||||||
|
|
||||||
#removeKeyboardManager() {
|
#removeKeyboardManager() {
|
||||||
window.removeEventListener("keydown", this.#boundKeydown);
|
this.#keyboardManagerAC?.abort();
|
||||||
window.removeEventListener("keyup", this.#boundKeyup);
|
this.#keyboardManagerAC = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#addCopyPasteListeners() {
|
#addCopyPasteListeners() {
|
||||||
const signal = this._signal;
|
if (this.#copyPasteAC) {
|
||||||
document.addEventListener("copy", this.#boundCopy, { signal });
|
return;
|
||||||
document.addEventListener("cut", this.#boundCut, { signal });
|
}
|
||||||
document.addEventListener("paste", this.#boundPaste, { signal });
|
this.#copyPasteAC = new AbortController();
|
||||||
|
const signal = this.combinedSignal(this.#copyPasteAC);
|
||||||
|
|
||||||
|
document.addEventListener("copy", this.copy.bind(this), { signal });
|
||||||
|
document.addEventListener("cut", this.cut.bind(this), { signal });
|
||||||
|
document.addEventListener("paste", this.paste.bind(this), { signal });
|
||||||
}
|
}
|
||||||
|
|
||||||
#removeCopyPasteListeners() {
|
#removeCopyPasteListeners() {
|
||||||
document.removeEventListener("copy", this.#boundCopy);
|
this.#copyPasteAC?.abort();
|
||||||
document.removeEventListener("cut", this.#boundCut);
|
this.#copyPasteAC = null;
|
||||||
document.removeEventListener("paste", this.#boundPaste);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#addDragAndDropListeners() {
|
#addDragAndDropListeners() {
|
||||||
|
|
|
@ -538,7 +538,11 @@ const PDFViewerApplication = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appConfig.annotationEditorParams) {
|
if (appConfig.annotationEditorParams) {
|
||||||
if (annotationEditorMode !== AnnotationEditorType.DISABLE) {
|
if (
|
||||||
|
((typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) ||
|
||||||
|
typeof AbortSignal.any === "function") &&
|
||||||
|
annotationEditorMode !== AnnotationEditorType.DISABLE
|
||||||
|
) {
|
||||||
const editorHighlightButton = appConfig.toolbar?.editorHighlightButton;
|
const editorHighlightButton = appConfig.toolbar?.editorHighlightButton;
|
||||||
if (editorHighlightButton && AppOptions.get("enableHighlightEditor")) {
|
if (editorHighlightButton && AppOptions.get("enableHighlightEditor")) {
|
||||||
editorHighlightButton.hidden = false;
|
editorHighlightButton.hidden = false;
|
||||||
|
|
|
@ -797,11 +797,9 @@ class PDFViewer {
|
||||||
this.findController?.setDocument(null);
|
this.findController?.setDocument(null);
|
||||||
this._scriptingManager?.setDocument(null);
|
this._scriptingManager?.setDocument(null);
|
||||||
|
|
||||||
if (this.#annotationEditorUIManager) {
|
this.#annotationEditorUIManager?.destroy();
|
||||||
this.#annotationEditorUIManager.destroy();
|
|
||||||
this.#annotationEditorUIManager = null;
|
this.#annotationEditorUIManager = null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.pdfDocument = pdfDocument;
|
this.pdfDocument = pdfDocument;
|
||||||
if (!pdfDocument) {
|
if (!pdfDocument) {
|
||||||
|
|
|
@ -623,7 +623,7 @@ See https://github.com/adobe-type-tools/cmap-resources
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button id="deleteModelButton" type="button" class="secondaryButton" tabindex="0"><span data-l10n-id="pdfjs-editor-alt-text-settings-delete-model-button">Delete</span></button>
|
<button id="deleteModelButton" type="button" class="secondaryButton" tabindex="0"><span data-l10n-id="pdfjs-editor-alt-text-settings-delete-model-button">Delete</span></button>
|
||||||
<button id="downloadModelButton"type="button" class="secondaryButton" tabindex="0"><span data-l10n-id="pdfjs-editor-alt-text-settings-download-model-button">Download</span></button>
|
<button id="downloadModelButton" type="button" class="secondaryButton" tabindex="0"><span data-l10n-id="pdfjs-editor-alt-text-settings-download-model-button">Download</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue