123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604 |
- /**
- * @licstart The following is the entire license notice for the
- * JavaScript code in this page
- *
- * Copyright 2022 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.
- *
- * @licend The above is the entire license notice for the
- * JavaScript code in this page
- */
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports.AnnotationEditorLayer = void 0;
- var _util = require("../../shared/util.js");
- var _tools = require("./tools.js");
- var _display_utils = require("../display_utils.js");
- var _freetext = require("./freetext.js");
- var _ink = require("./ink.js");
- class AnnotationEditorLayer {
- #allowClick = false;
- #boundPointerup = this.pointerup.bind(this);
- #boundPointerdown = this.pointerdown.bind(this);
- #editors = new Map();
- #isCleaningUp = false;
- #textLayerMap = new WeakMap();
- #textNodes = new Map();
- #uiManager;
- #waitingEditors = new Set();
- static _initialized = false;
- constructor(options) {
- if (!AnnotationEditorLayer._initialized) {
- AnnotationEditorLayer._initialized = true;
- _freetext.FreeTextEditor.initialize(options.l10n);
- _ink.InkEditor.initialize(options.l10n);
- options.uiManager.registerEditorTypes([_freetext.FreeTextEditor, _ink.InkEditor]);
- }
- this.#uiManager = options.uiManager;
- this.annotationStorage = options.annotationStorage;
- this.pageIndex = options.pageIndex;
- this.div = options.div;
- this.#uiManager.addLayer(this);
- }
- get textLayerElements() {
- const textLayer = this.div.parentNode.getElementsByClassName("textLayer").item(0);
- if (!textLayer) {
- return (0, _util.shadow)(this, "textLayerElements", null);
- }
- let textChildren = this.#textLayerMap.get(textLayer);
- if (textChildren) {
- return textChildren;
- }
- textChildren = textLayer.querySelectorAll(`span[role="presentation"]`);
- if (textChildren.length === 0) {
- return (0, _util.shadow)(this, "textLayerElements", null);
- }
- textChildren = Array.from(textChildren);
- textChildren.sort(AnnotationEditorLayer.#compareElementPositions);
- this.#textLayerMap.set(textLayer, textChildren);
- return textChildren;
- }
- get #hasTextLayer() {
- return !!this.div.parentNode.querySelector(".textLayer .endOfContent");
- }
- updateToolbar(mode) {
- this.#uiManager.updateToolbar(mode);
- }
- updateMode(mode = this.#uiManager.getMode()) {
- this.#cleanup();
- if (mode === _util.AnnotationEditorType.INK) {
- this.addInkEditorIfNeeded(false);
- this.disableClick();
- } else {
- this.enableClick();
- }
- this.#uiManager.unselectAll();
- }
- addInkEditorIfNeeded(isCommitting) {
- if (!isCommitting && this.#uiManager.getMode() !== _util.AnnotationEditorType.INK) {
- return;
- }
- if (!isCommitting) {
- for (const editor of this.#editors.values()) {
- if (editor.isEmpty()) {
- editor.setInBackground();
- return;
- }
- }
- }
- const editor = this.#createAndAddNewEditor({
- offsetX: 0,
- offsetY: 0
- });
- editor.setInBackground();
- }
- setEditingState(isEditing) {
- this.#uiManager.setEditingState(isEditing);
- }
- addCommands(params) {
- this.#uiManager.addCommands(params);
- }
- enable() {
- this.div.style.pointerEvents = "auto";
- for (const editor of this.#editors.values()) {
- editor.enableEditing();
- }
- }
- disable() {
- this.div.style.pointerEvents = "none";
- for (const editor of this.#editors.values()) {
- editor.disableEditing();
- }
- }
- setActiveEditor(editor) {
- const currentActive = this.#uiManager.getActive();
- if (currentActive === editor) {
- return;
- }
- this.#uiManager.setActiveEditor(editor);
- }
- enableClick() {
- this.div.addEventListener("pointerdown", this.#boundPointerdown);
- this.div.addEventListener("pointerup", this.#boundPointerup);
- }
- disableClick() {
- this.div.removeEventListener("pointerdown", this.#boundPointerdown);
- this.div.removeEventListener("pointerup", this.#boundPointerup);
- }
- attach(editor) {
- this.#editors.set(editor.id, editor);
- }
- detach(editor) {
- this.#editors.delete(editor.id);
- this.removePointerInTextLayer(editor);
- }
- remove(editor) {
- this.#uiManager.removeEditor(editor);
- this.detach(editor);
- this.annotationStorage.removeKey(editor.id);
- editor.div.style.display = "none";
- setTimeout(() => {
- editor.div.style.display = "";
- editor.div.remove();
- editor.isAttachedToDOM = false;
- if (document.activeElement === document.body) {
- this.#uiManager.focusMainContainer();
- }
- }, 0);
- if (!this.#isCleaningUp) {
- this.addInkEditorIfNeeded(false);
- }
- }
- #changeParent(editor) {
- if (editor.parent === this) {
- return;
- }
- this.attach(editor);
- editor.pageIndex = this.pageIndex;
- editor.parent?.detach(editor);
- editor.parent = this;
- if (editor.div && editor.isAttachedToDOM) {
- editor.div.remove();
- this.div.append(editor.div);
- }
- }
- static #compareElementPositions(e1, e2) {
- const rect1 = e1.getBoundingClientRect();
- const rect2 = e2.getBoundingClientRect();
- if (rect1.y + rect1.height <= rect2.y) {
- return -1;
- }
- if (rect2.y + rect2.height <= rect1.y) {
- return +1;
- }
- const centerX1 = rect1.x + rect1.width / 2;
- const centerX2 = rect2.x + rect2.width / 2;
- return centerX1 - centerX2;
- }
- onTextLayerRendered() {
- this.#textNodes.clear();
- for (const editor of this.#waitingEditors) {
- if (editor.isAttachedToDOM) {
- this.addPointerInTextLayer(editor);
- }
- }
- this.#waitingEditors.clear();
- }
- removePointerInTextLayer(editor) {
- if (!this.#hasTextLayer) {
- this.#waitingEditors.delete(editor);
- return;
- }
- const {
- id
- } = editor;
- const node = this.#textNodes.get(id);
- if (!node) {
- return;
- }
- this.#textNodes.delete(id);
- let owns = node.getAttribute("aria-owns");
- if (owns?.includes(id)) {
- owns = owns.split(" ").filter(x => x !== id).join(" ");
- if (owns) {
- node.setAttribute("aria-owns", owns);
- } else {
- node.removeAttribute("aria-owns");
- node.setAttribute("role", "presentation");
- }
- }
- }
- addPointerInTextLayer(editor) {
- if (!this.#hasTextLayer) {
- this.#waitingEditors.add(editor);
- return;
- }
- this.removePointerInTextLayer(editor);
- const children = this.textLayerElements;
- if (!children) {
- return;
- }
- const {
- contentDiv
- } = editor;
- const id = editor.getIdForTextLayer();
- const index = (0, _display_utils.binarySearchFirstItem)(children, node => AnnotationEditorLayer.#compareElementPositions(contentDiv, node) < 0);
- const node = children[Math.max(0, index - 1)];
- const owns = node.getAttribute("aria-owns");
- if (!owns?.includes(id)) {
- node.setAttribute("aria-owns", owns ? `${owns} ${id}` : id);
- }
- node.removeAttribute("role");
- this.#textNodes.set(id, node);
- }
- moveDivInDOM(editor) {
- this.addPointerInTextLayer(editor);
- const {
- div,
- contentDiv
- } = editor;
- if (!this.div.hasChildNodes()) {
- this.div.append(div);
- return;
- }
- const children = Array.from(this.div.childNodes).filter(node => node !== div);
- if (children.length === 0) {
- return;
- }
- const index = (0, _display_utils.binarySearchFirstItem)(children, node => AnnotationEditorLayer.#compareElementPositions(contentDiv, node) < 0);
- if (index === 0) {
- children[0].before(div);
- } else {
- children[index - 1].after(div);
- }
- }
- add(editor) {
- this.#changeParent(editor);
- this.addToAnnotationStorage(editor);
- this.#uiManager.addEditor(editor);
- this.attach(editor);
- if (!editor.isAttachedToDOM) {
- const div = editor.render();
- this.div.append(div);
- editor.isAttachedToDOM = true;
- }
- this.moveDivInDOM(editor);
- editor.onceAdded();
- }
- addToAnnotationStorage(editor) {
- if (!editor.isEmpty() && !this.annotationStorage.has(editor.id)) {
- this.annotationStorage.setValue(editor.id, editor);
- }
- }
- addOrRebuild(editor) {
- if (editor.needsToBeRebuilt()) {
- editor.rebuild();
- } else {
- this.add(editor);
- }
- }
- addANewEditor(editor) {
- const cmd = () => {
- this.addOrRebuild(editor);
- };
- const undo = () => {
- editor.remove();
- };
- this.addCommands({
- cmd,
- undo,
- mustExec: true
- });
- }
- addUndoableEditor(editor) {
- const cmd = () => {
- this.addOrRebuild(editor);
- };
- const undo = () => {
- editor.remove();
- };
- this.addCommands({
- cmd,
- undo,
- mustExec: false
- });
- }
- getNextId() {
- return this.#uiManager.getId();
- }
- #createNewEditor(params) {
- switch (this.#uiManager.getMode()) {
- case _util.AnnotationEditorType.FREETEXT:
- return new _freetext.FreeTextEditor(params);
- case _util.AnnotationEditorType.INK:
- return new _ink.InkEditor(params);
- }
- return null;
- }
- deserialize(data) {
- switch (data.annotationType) {
- case _util.AnnotationEditorType.FREETEXT:
- return _freetext.FreeTextEditor.deserialize(data, this);
- case _util.AnnotationEditorType.INK:
- return _ink.InkEditor.deserialize(data, this);
- }
- return null;
- }
- #createAndAddNewEditor(event) {
- const id = this.getNextId();
- const editor = this.#createNewEditor({
- parent: this,
- id,
- x: event.offsetX,
- y: event.offsetY
- });
- if (editor) {
- this.add(editor);
- }
- return editor;
- }
- setSelected(editor) {
- this.#uiManager.setSelected(editor);
- }
- toggleSelected(editor) {
- this.#uiManager.toggleSelected(editor);
- }
- isSelected(editor) {
- return this.#uiManager.isSelected(editor);
- }
- unselect(editor) {
- this.#uiManager.unselect(editor);
- }
- pointerup(event) {
- const isMac = _tools.KeyboardManager.platform.isMac;
- if (event.button !== 0 || event.ctrlKey && isMac) {
- return;
- }
- if (event.target !== this.div) {
- return;
- }
- if (!this.#allowClick) {
- this.#allowClick = true;
- return;
- }
- this.#createAndAddNewEditor(event);
- }
- pointerdown(event) {
- const isMac = _tools.KeyboardManager.platform.isMac;
- if (event.button !== 0 || event.ctrlKey && isMac) {
- return;
- }
- if (event.target !== this.div) {
- return;
- }
- const editor = this.#uiManager.getActive();
- this.#allowClick = !editor || editor.isEmpty();
- }
- drop(event) {
- const id = event.dataTransfer.getData("text/plain");
- const editor = this.#uiManager.getEditor(id);
- if (!editor) {
- return;
- }
- event.preventDefault();
- event.dataTransfer.dropEffect = "move";
- this.#changeParent(editor);
- const rect = this.div.getBoundingClientRect();
- const endX = event.clientX - rect.x;
- const endY = event.clientY - rect.y;
- editor.translate(endX - editor.startX, endY - editor.startY);
- this.moveDivInDOM(editor);
- editor.div.focus();
- }
- dragover(event) {
- event.preventDefault();
- }
- destroy() {
- if (this.#uiManager.getActive()?.parent === this) {
- this.#uiManager.setActiveEditor(null);
- }
- for (const editor of this.#editors.values()) {
- this.removePointerInTextLayer(editor);
- editor.isAttachedToDOM = false;
- editor.div.remove();
- editor.parent = null;
- }
- this.#textNodes.clear();
- this.div = null;
- this.#editors.clear();
- this.#waitingEditors.clear();
- this.#uiManager.removeLayer(this);
- }
- #cleanup() {
- this.#isCleaningUp = true;
- for (const editor of this.#editors.values()) {
- if (editor.isEmpty()) {
- editor.remove();
- }
- }
- this.#isCleaningUp = false;
- }
- render(parameters) {
- this.viewport = parameters.viewport;
- (0, _tools.bindEvents)(this, this.div, ["dragover", "drop"]);
- this.setDimensions();
- for (const editor of this.#uiManager.getEditors(this.pageIndex)) {
- this.add(editor);
- }
- this.updateMode();
- }
- update(parameters) {
- this.viewport = parameters.viewport;
- this.setDimensions();
- this.updateMode();
- }
- get scaleFactor() {
- return this.viewport.scale;
- }
- get pageDimensions() {
- const [pageLLx, pageLLy, pageURx, pageURy] = this.viewport.viewBox;
- const width = pageURx - pageLLx;
- const height = pageURy - pageLLy;
- return [width, height];
- }
- get viewportBaseDimensions() {
- const {
- width,
- height,
- rotation
- } = this.viewport;
- return rotation % 180 === 0 ? [width, height] : [height, width];
- }
- setDimensions() {
- const {
- width,
- height,
- rotation
- } = this.viewport;
- const flipOrientation = rotation % 180 !== 0,
- widthStr = Math.floor(width) + "px",
- heightStr = Math.floor(height) + "px";
- this.div.style.width = flipOrientation ? heightStr : widthStr;
- this.div.style.height = flipOrientation ? widthStr : heightStr;
- this.div.setAttribute("data-main-rotation", rotation);
- }
- }
- exports.AnnotationEditorLayer = AnnotationEditorLayer;
|