diff --git a/src/display/editor/annotation_editor_layer.js b/src/display/editor/annotation_editor_layer.js index d25e97d2b6f1e..2328940d34a27 100644 --- a/src/display/editor/annotation_editor_layer.js +++ b/src/display/editor/annotation_editor_layer.js @@ -244,7 +244,7 @@ class AnnotationEditorLayer { * Enable pointer events on the main div in order to enable * editor creation. */ - enable() { + async enable() { this.div.tabIndex = 0; this.togglePointerEvents(true); const annotationElementIds = new Set(); @@ -271,7 +271,7 @@ class AnnotationEditorLayer { if (annotationElementIds.has(editable.data.id)) { continue; } - const editor = this.deserialize(editable); + const editor = await this.deserialize(editable); if (!editor) { continue; } @@ -657,11 +657,11 @@ class AnnotationEditorLayer { * @param {Object} data * @returns {AnnotationEditor | null} */ - deserialize(data) { + async deserialize(data) { return ( - AnnotationEditorLayer.#editorTypes + (await AnnotationEditorLayer.#editorTypes .get(data.annotationType ?? data.annotationEditorType) - ?.deserialize(data, this, this.#uiManager) || null + ?.deserialize(data, this, this.#uiManager)) || null ); } diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index 54036b1d68e13..a76ba95daa75e 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -1371,9 +1371,9 @@ class AnnotationEditor { * @param {Object} data * @param {AnnotationEditorLayer} parent * @param {AnnotationEditorUIManager} uiManager - * @returns {AnnotationEditor | null} + * @returns {Promise} */ - static deserialize(data, parent, uiManager) { + static async deserialize(data, parent, uiManager) { const editor = new this.prototype.constructor({ parent, id: parent.getNextId(), diff --git a/src/display/editor/freetext.js b/src/display/editor/freetext.js index c3d4c958e7619..3977f9e62620d 100644 --- a/src/display/editor/freetext.js +++ b/src/display/editor/freetext.js @@ -770,7 +770,7 @@ class FreeTextEditor extends AnnotationEditor { } /** @inheritdoc */ - static deserialize(data, parent, uiManager) { + static async deserialize(data, parent, uiManager) { let initialData = null; if (data instanceof FreeTextAnnotationElement) { const { @@ -807,7 +807,7 @@ class FreeTextEditor extends AnnotationEditor { popupRef, }; } - const editor = super.deserialize(data, parent, uiManager); + const editor = await super.deserialize(data, parent, uiManager); editor.#fontSize = data.fontSize; editor.#color = Util.makeHexColor(...data.color); editor.#content = FreeTextEditor.#deserializeContent(data.value); diff --git a/src/display/editor/highlight.js b/src/display/editor/highlight.js index 2e2d9cbf7f423..f6f603873cdac 100644 --- a/src/display/editor/highlight.js +++ b/src/display/editor/highlight.js @@ -779,7 +779,7 @@ class HighlightEditor extends AnnotationEditor { } /** @inheritdoc */ - static deserialize(data, parent, uiManager) { + static async deserialize(data, parent, uiManager) { let initialData = null; if (data instanceof HighlightAnnotationElement) { const { @@ -832,7 +832,7 @@ class HighlightEditor extends AnnotationEditor { } const { color, quadPoints, inkLists, opacity } = data; - const editor = super.deserialize(data, parent, uiManager); + const editor = await super.deserialize(data, parent, uiManager); editor.color = Util.makeHexColor(...color); editor.#opacity = opacity || 1; diff --git a/src/display/editor/ink.js b/src/display/editor/ink.js index 314bd72c7d6a7..c5d0def6c51bb 100644 --- a/src/display/editor/ink.js +++ b/src/display/editor/ink.js @@ -1149,11 +1149,11 @@ class InkEditor extends AnnotationEditor { } /** @inheritdoc */ - static deserialize(data, parent, uiManager) { + static async deserialize(data, parent, uiManager) { if (data instanceof InkAnnotationElement) { return null; } - const editor = super.deserialize(data, parent, uiManager); + const editor = await super.deserialize(data, parent, uiManager); editor.thickness = data.thickness; editor.color = Util.makeHexColor(...data.color); diff --git a/src/display/editor/stamp.js b/src/display/editor/stamp.js index 6dd348d320545..623a7d77d986a 100644 --- a/src/display/editor/stamp.js +++ b/src/display/editor/stamp.js @@ -768,11 +768,11 @@ class StampEditor extends AnnotationEditor { } /** @inheritdoc */ - static deserialize(data, parent, uiManager) { + static async deserialize(data, parent, uiManager) { if (data instanceof StampAnnotationElement) { return null; } - const editor = super.deserialize(data, parent, uiManager); + const editor = await super.deserialize(data, parent, uiManager); const { rect, bitmapUrl, bitmapId, isSvg, accessibilityData } = data; if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) { editor.#bitmapId = bitmapId; diff --git a/src/display/editor/tools.js b/src/display/editor/tools.js index e266ab18972d8..2823cdd53b6e4 100644 --- a/src/display/editor/tools.js +++ b/src/display/editor/tools.js @@ -621,6 +621,8 @@ class AnnotationEditorUIManager { #viewer = null; + #updateModeCapability = null; + static TRANSLATE_SMALL = 1; // page units. static TRANSLATE_BIG = 10; // page units. @@ -817,6 +819,9 @@ class AnnotationEditorUIManager { } destroy() { + this.#updateModeCapability?.resolve(); + this.#updateModeCapability = null; + this.#abortController?.abort(); this.#abortController = null; this._signal = null; @@ -1344,7 +1349,7 @@ class AnnotationEditorUIManager { * Paste callback. * @param {ClipboardEvent} event */ - paste(event) { + async paste(event) { event.preventDefault(); const { clipboardData } = event; for (const item of clipboardData.items) { @@ -1378,7 +1383,7 @@ class AnnotationEditorUIManager { try { const newEditors = []; for (const editor of data) { - const deserializedEditor = layer.deserialize(editor); + const deserializedEditor = await layer.deserialize(editor); if (!deserializedEditor) { return; } @@ -1572,30 +1577,44 @@ class AnnotationEditorUIManager { * @param {boolean} [isFromKeyboard] - true if the mode change is due to a * keyboard action. */ - updateMode(mode, editId = null, isFromKeyboard = false) { + async updateMode(mode, editId = null, isFromKeyboard = false) { if (this.#mode === mode) { return; } + + if (this.#updateModeCapability) { + await this.#updateModeCapability.promise; + if (!this.#updateModeCapability) { + // This ui manager has been destroyed. + return; + } + } + + this.#updateModeCapability = Promise.withResolvers(); + this.#mode = mode; if (mode === AnnotationEditorType.NONE) { this.setEditingState(false); this.#disableAll(); + + this.#updateModeCapability.resolve(); return; } this.setEditingState(true); - this.#enableAll(); + await this.#enableAll(); this.unselectAll(); for (const layer of this.#allLayers.values()) { layer.updateMode(mode); } - if (!editId && isFromKeyboard) { - this.addNewEditorFromKeyboard(); - return; - } - if (!editId) { + if (isFromKeyboard) { + this.addNewEditorFromKeyboard(); + } + + this.#updateModeCapability.resolve(); return; } + for (const editor of this.#allEditors.values()) { if (editor.annotationElementId === editId) { this.setSelected(editor); @@ -1603,6 +1622,8 @@ class AnnotationEditorUIManager { break; } } + + this.#updateModeCapability.resolve(); } addNewEditorFromKeyboard() { @@ -1702,12 +1723,14 @@ class AnnotationEditorUIManager { /** * Enable all the layers. */ - #enableAll() { + async #enableAll() { if (!this.#isEnabled) { this.#isEnabled = true; + const promises = []; for (const layer of this.#allLayers.values()) { - layer.enable(); + promises.push(layer.enable()); } + await Promise.all(promises); for (const editor of this.#allEditors.values()) { editor.enable(); }