Some checks failed
		
		
	
	Types tests / Test (lts/*) (push) Has been cancelled
				
			Lint / Lint (lts/*) (push) Has been cancelled
				
			CodeQL / Analyze (javascript) (push) Has been cancelled
				
			CI / Test (20) (push) Has been cancelled
				
			CI / Test (22) (push) Has been cancelled
				
			CI / Test (24) (push) Has been cancelled
				
			
		
			
				
	
	
		
			1264 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1264 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* Copyright 2025 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.
 | 
						|
 */
 | 
						|
 | 
						|
import {
 | 
						|
  AnnotationEditorType,
 | 
						|
  applyOpacity,
 | 
						|
  CSSConstants,
 | 
						|
  findContrastColor,
 | 
						|
  MathClamp,
 | 
						|
  noContextMenu,
 | 
						|
  PDFDateString,
 | 
						|
  renderRichText,
 | 
						|
  shadow,
 | 
						|
  stopEvent,
 | 
						|
  Util,
 | 
						|
} from "pdfjs-lib";
 | 
						|
import { binarySearchFirstItem } from "./ui_utils.js";
 | 
						|
 | 
						|
class CommentManager {
 | 
						|
  #dialog;
 | 
						|
 | 
						|
  #popup;
 | 
						|
 | 
						|
  #sidebar;
 | 
						|
 | 
						|
  static #hasForcedColors = null;
 | 
						|
 | 
						|
  constructor(
 | 
						|
    commentDialog,
 | 
						|
    sidebar,
 | 
						|
    eventBus,
 | 
						|
    linkService,
 | 
						|
    overlayManager,
 | 
						|
    ltr,
 | 
						|
    hasForcedColors
 | 
						|
  ) {
 | 
						|
    const dateFormat = new Intl.DateTimeFormat(undefined, {
 | 
						|
      dateStyle: "long",
 | 
						|
    });
 | 
						|
    this.dialogElement = commentDialog.dialog;
 | 
						|
    this.#dialog = new CommentDialog(
 | 
						|
      commentDialog,
 | 
						|
      overlayManager,
 | 
						|
      eventBus,
 | 
						|
      ltr
 | 
						|
    );
 | 
						|
    this.#popup = new CommentPopup(
 | 
						|
      eventBus,
 | 
						|
      dateFormat,
 | 
						|
      ltr,
 | 
						|
      this.dialogElement
 | 
						|
    );
 | 
						|
    this.#sidebar = new CommentSidebar(
 | 
						|
      sidebar,
 | 
						|
      eventBus,
 | 
						|
      linkService,
 | 
						|
      this.#popup,
 | 
						|
      dateFormat,
 | 
						|
      ltr
 | 
						|
    );
 | 
						|
    this.#popup.sidebar = this.#sidebar;
 | 
						|
    CommentManager.#hasForcedColors = hasForcedColors;
 | 
						|
  }
 | 
						|
 | 
						|
  setSidebarUiManager(uiManager) {
 | 
						|
    this.#sidebar.setUIManager(uiManager);
 | 
						|
  }
 | 
						|
 | 
						|
  showSidebar(annotations) {
 | 
						|
    this.#sidebar.show(annotations);
 | 
						|
  }
 | 
						|
 | 
						|
  hideSidebar() {
 | 
						|
    this.#sidebar.hide();
 | 
						|
  }
 | 
						|
 | 
						|
  removeComments(ids) {
 | 
						|
    this.#sidebar.removeComments(ids);
 | 
						|
  }
 | 
						|
 | 
						|
  selectComment(id) {
 | 
						|
    this.#sidebar.selectComment(null, id);
 | 
						|
  }
 | 
						|
 | 
						|
  addComment(annotation) {
 | 
						|
    this.#sidebar.addComment(annotation);
 | 
						|
  }
 | 
						|
 | 
						|
  updateComment(annotation) {
 | 
						|
    this.#sidebar.updateComment(annotation);
 | 
						|
  }
 | 
						|
 | 
						|
  toggleCommentPopup(editor, isSelected, visibility, isEditable) {
 | 
						|
    if (isSelected) {
 | 
						|
      this.selectComment(editor.uid);
 | 
						|
    }
 | 
						|
    this.#popup.toggle(editor, isSelected, visibility, isEditable);
 | 
						|
  }
 | 
						|
 | 
						|
  destroyPopup() {
 | 
						|
    this.#popup.destroy();
 | 
						|
  }
 | 
						|
 | 
						|
  updatePopupColor(editor) {
 | 
						|
    this.#popup.updateColor(editor);
 | 
						|
  }
 | 
						|
 | 
						|
  showDialog(uiManager, editor, posX, posY, options) {
 | 
						|
    return this.#dialog.open(uiManager, editor, posX, posY, options);
 | 
						|
  }
 | 
						|
 | 
						|
  makeCommentColor(color, opacity) {
 | 
						|
    return CommentManager._makeCommentColor(color, opacity);
 | 
						|
  }
 | 
						|
 | 
						|
  static _makeCommentColor(color, opacity) {
 | 
						|
    return this.#hasForcedColors
 | 
						|
      ? null
 | 
						|
      : findContrastColor(
 | 
						|
          applyOpacity(...color, opacity ?? 1),
 | 
						|
          CSSConstants.commentForegroundColor
 | 
						|
        );
 | 
						|
  }
 | 
						|
 | 
						|
  destroy() {
 | 
						|
    this.#dialog.destroy();
 | 
						|
    this.#sidebar.hide();
 | 
						|
    this.#popup.destroy();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class CommentSidebar {
 | 
						|
  #annotations = null;
 | 
						|
 | 
						|
  #eventBus;
 | 
						|
 | 
						|
  #boundCommentClick = this.#commentClick.bind(this);
 | 
						|
 | 
						|
  #boundCommentKeydown = this.#commentKeydown.bind(this);
 | 
						|
 | 
						|
  #sidebar;
 | 
						|
 | 
						|
  #closeButton;
 | 
						|
 | 
						|
  #commentsList;
 | 
						|
 | 
						|
  #commentCount;
 | 
						|
 | 
						|
  #dateFormat;
 | 
						|
 | 
						|
  #sidebarTitle;
 | 
						|
 | 
						|
  #learnMoreUrl;
 | 
						|
 | 
						|
  #linkService;
 | 
						|
 | 
						|
  #popup;
 | 
						|
 | 
						|
  #elementsToAnnotations = null;
 | 
						|
 | 
						|
  #idsToElements = null;
 | 
						|
 | 
						|
  #uiManager = null;
 | 
						|
 | 
						|
  #minWidth = 0;
 | 
						|
 | 
						|
  #maxWidth = 0;
 | 
						|
 | 
						|
  #initialWidth = 0;
 | 
						|
 | 
						|
  #width = 0;
 | 
						|
 | 
						|
  #ltr;
 | 
						|
 | 
						|
  constructor(
 | 
						|
    {
 | 
						|
      learnMoreUrl,
 | 
						|
      sidebar,
 | 
						|
      sidebarResizer,
 | 
						|
      commentsList,
 | 
						|
      commentCount,
 | 
						|
      sidebarTitle,
 | 
						|
      closeButton,
 | 
						|
      commentToolbarButton,
 | 
						|
    },
 | 
						|
    eventBus,
 | 
						|
    linkService,
 | 
						|
    popup,
 | 
						|
    dateFormat,
 | 
						|
    ltr
 | 
						|
  ) {
 | 
						|
    this.#sidebar = sidebar;
 | 
						|
    this.#sidebarTitle = sidebarTitle;
 | 
						|
    this.#commentsList = commentsList;
 | 
						|
    this.#commentCount = commentCount;
 | 
						|
    this.#learnMoreUrl = learnMoreUrl;
 | 
						|
    this.#linkService = linkService;
 | 
						|
    this.#closeButton = closeButton;
 | 
						|
    this.#popup = popup;
 | 
						|
    this.#dateFormat = dateFormat;
 | 
						|
    this.#ltr = ltr;
 | 
						|
    this.#eventBus = eventBus;
 | 
						|
 | 
						|
    const style = window.getComputedStyle(sidebar);
 | 
						|
    this.#minWidth = parseFloat(style.getPropertyValue("--sidebar-min-width"));
 | 
						|
    this.#maxWidth = parseFloat(style.getPropertyValue("--sidebar-max-width"));
 | 
						|
    this.#initialWidth = this.#width = parseFloat(
 | 
						|
      style.getPropertyValue("--sidebar-width")
 | 
						|
    );
 | 
						|
 | 
						|
    this.#makeSidebarResizable(sidebarResizer);
 | 
						|
    closeButton.addEventListener("click", () => {
 | 
						|
      eventBus.dispatch("switchannotationeditormode", {
 | 
						|
        source: this,
 | 
						|
        mode: AnnotationEditorType.NONE,
 | 
						|
      });
 | 
						|
    });
 | 
						|
    const keyDownCallback = e => {
 | 
						|
      if (e.key === "ArrowDown" || e.key === "Home" || e.key === "F6") {
 | 
						|
        this.#commentsList.firstElementChild.focus();
 | 
						|
        stopEvent(e);
 | 
						|
      } else if (e.key === "ArrowUp" || e.key === "End") {
 | 
						|
        this.#commentsList.lastElementChild.focus();
 | 
						|
        stopEvent(e);
 | 
						|
      }
 | 
						|
    };
 | 
						|
    commentToolbarButton.addEventListener("keydown", keyDownCallback);
 | 
						|
    sidebar.addEventListener("keydown", keyDownCallback);
 | 
						|
    this.#sidebar.hidden = true;
 | 
						|
  }
 | 
						|
 | 
						|
  #makeSidebarResizable(resizer) {
 | 
						|
    let pointerMoveAC;
 | 
						|
    const cancelResize = () => {
 | 
						|
      this.#width = MathClamp(this.#width, this.#minWidth, this.#maxWidth);
 | 
						|
      this.#sidebar.classList.remove("resizing");
 | 
						|
      pointerMoveAC?.abort();
 | 
						|
      pointerMoveAC = null;
 | 
						|
    };
 | 
						|
    resizer.addEventListener("pointerdown", e => {
 | 
						|
      if (pointerMoveAC) {
 | 
						|
        cancelResize();
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      const { clientX } = e;
 | 
						|
      stopEvent(e);
 | 
						|
      let prevX = clientX;
 | 
						|
      pointerMoveAC = new AbortController();
 | 
						|
      const { signal } = pointerMoveAC;
 | 
						|
      const sign = this.#ltr ? -1 : 1;
 | 
						|
      const sidebar = this.#sidebar;
 | 
						|
      const sidebarStyle = sidebar.style;
 | 
						|
      sidebar.classList.add("resizing");
 | 
						|
      const parentStyle = sidebar.parentElement.style;
 | 
						|
      parentStyle.minWidth = 0;
 | 
						|
      window.addEventListener("contextmenu", noContextMenu, { signal });
 | 
						|
      window.addEventListener(
 | 
						|
        "pointermove",
 | 
						|
        ev => {
 | 
						|
          if (!pointerMoveAC) {
 | 
						|
            return;
 | 
						|
          }
 | 
						|
          stopEvent(ev);
 | 
						|
          const { clientX: x } = ev;
 | 
						|
          const newWidth = (this.#width += sign * (x - prevX));
 | 
						|
          prevX = x;
 | 
						|
          if (newWidth > this.#maxWidth || newWidth < this.#minWidth) {
 | 
						|
            return;
 | 
						|
          }
 | 
						|
          sidebarStyle.width = `${newWidth.toFixed(3)}px`;
 | 
						|
          parentStyle.insetInlineStart = `${(this.#initialWidth - newWidth).toFixed(3)}px`;
 | 
						|
        },
 | 
						|
        { signal, capture: true }
 | 
						|
      );
 | 
						|
      window.addEventListener("blur", cancelResize, { signal });
 | 
						|
      window.addEventListener(
 | 
						|
        "pointerup",
 | 
						|
        ev => {
 | 
						|
          if (pointerMoveAC) {
 | 
						|
            cancelResize();
 | 
						|
            stopEvent(ev);
 | 
						|
          }
 | 
						|
        },
 | 
						|
        { signal }
 | 
						|
      );
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  setUIManager(uiManager) {
 | 
						|
    this.#uiManager = uiManager;
 | 
						|
  }
 | 
						|
 | 
						|
  show(annotations) {
 | 
						|
    this.#elementsToAnnotations = new WeakMap();
 | 
						|
    this.#idsToElements = new Map();
 | 
						|
    this.#annotations = annotations;
 | 
						|
    annotations.sort(this.#sortComments.bind(this));
 | 
						|
    if (annotations.length !== 0) {
 | 
						|
      const fragment = document.createDocumentFragment();
 | 
						|
      for (const annotation of annotations) {
 | 
						|
        fragment.append(this.#createCommentElement(annotation));
 | 
						|
      }
 | 
						|
 | 
						|
      this.#setCommentsCount(fragment);
 | 
						|
      this.#commentsList.append(fragment);
 | 
						|
    } else {
 | 
						|
      this.#setCommentsCount();
 | 
						|
    }
 | 
						|
    this.#sidebar.hidden = false;
 | 
						|
    this.#eventBus.dispatch("reporttelemetry", {
 | 
						|
      source: this,
 | 
						|
      details: {
 | 
						|
        type: "commentSidebar",
 | 
						|
        data: { numberOfAnnotations: annotations.length },
 | 
						|
      },
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  hide() {
 | 
						|
    this.#sidebar.hidden = true;
 | 
						|
    this.#commentsList.replaceChildren();
 | 
						|
    this.#elementsToAnnotations = null;
 | 
						|
    this.#idsToElements = null;
 | 
						|
    this.#annotations = null;
 | 
						|
  }
 | 
						|
 | 
						|
  removeComments(ids) {
 | 
						|
    if (ids.length === 0 || !this.#idsToElements) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (
 | 
						|
      new Set(this.#idsToElements.keys()).difference(new Set(ids)).size === 0
 | 
						|
    ) {
 | 
						|
      this.#removeAll();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    for (const id of ids) {
 | 
						|
      this.#removeComment(id);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  focusComment(id) {
 | 
						|
    const element = this.#idsToElements.get(id);
 | 
						|
    if (!element) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    this.#sidebar.scrollTop = element.offsetTop - this.#sidebar.offsetTop;
 | 
						|
    for (const el of this.#commentsList.children) {
 | 
						|
      el.classList.toggle("selected", el === element);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  updateComment(annotation) {
 | 
						|
    if (!this.#idsToElements) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const {
 | 
						|
      id,
 | 
						|
      creationDate,
 | 
						|
      modificationDate,
 | 
						|
      richText,
 | 
						|
      contentsObj,
 | 
						|
      popupRef,
 | 
						|
    } = annotation;
 | 
						|
 | 
						|
    if (!popupRef || (!richText && !contentsObj?.str)) {
 | 
						|
      this.#removeComment(id);
 | 
						|
    }
 | 
						|
 | 
						|
    const element = this.#idsToElements.get(id);
 | 
						|
    if (!element) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const prevAnnotation = this.#elementsToAnnotations.get(element);
 | 
						|
    let index = binarySearchFirstItem(
 | 
						|
      this.#annotations,
 | 
						|
      a => this.#sortComments(a, prevAnnotation) >= 0
 | 
						|
    );
 | 
						|
    if (index >= this.#annotations.length) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    this.#setDate(element.firstChild, modificationDate || creationDate);
 | 
						|
    this.#setText(element.lastChild, richText, contentsObj);
 | 
						|
 | 
						|
    this.#annotations.splice(index, 1);
 | 
						|
    index = binarySearchFirstItem(
 | 
						|
      this.#annotations,
 | 
						|
      a => this.#sortComments(a, annotation) >= 0
 | 
						|
    );
 | 
						|
    this.#annotations.splice(index, 0, annotation);
 | 
						|
    if (index >= this.#commentsList.children.length) {
 | 
						|
      this.#commentsList.append(element);
 | 
						|
    } else {
 | 
						|
      this.#commentsList.insertBefore(
 | 
						|
        element,
 | 
						|
        this.#commentsList.children[index]
 | 
						|
      );
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  #removeComment(id) {
 | 
						|
    const element = this.#idsToElements?.get(id);
 | 
						|
    if (!element) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const annotation = this.#elementsToAnnotations.get(element);
 | 
						|
    const index = binarySearchFirstItem(
 | 
						|
      this.#annotations,
 | 
						|
      a => this.#sortComments(a, annotation) >= 0
 | 
						|
    );
 | 
						|
    if (index >= this.#annotations.length) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    this.#annotations.splice(index, 1);
 | 
						|
    element.remove();
 | 
						|
    this.#idsToElements.delete(id);
 | 
						|
    this.#setCommentsCount();
 | 
						|
  }
 | 
						|
 | 
						|
  #removeAll() {
 | 
						|
    this.#commentsList.replaceChildren();
 | 
						|
    this.#elementsToAnnotations = new WeakMap();
 | 
						|
    this.#idsToElements.clear();
 | 
						|
    this.#annotations.length = 0;
 | 
						|
    this.#setCommentsCount();
 | 
						|
  }
 | 
						|
 | 
						|
  selectComment(element, id = null) {
 | 
						|
    if (!this.#idsToElements) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const hasNoElement = !element;
 | 
						|
    element ||= this.#idsToElements.get(id);
 | 
						|
    for (const el of this.#commentsList.children) {
 | 
						|
      el.classList.toggle("selected", el === element);
 | 
						|
    }
 | 
						|
    if (hasNoElement) {
 | 
						|
      element?.scrollIntoView({ behavior: "instant", block: "center" });
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  addComment(annotation) {
 | 
						|
    if (this.#idsToElements?.has(annotation.id)) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const { popupRef, contentsObj } = annotation;
 | 
						|
    if (!popupRef || !contentsObj?.str) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const commentItem = this.#createCommentElement(annotation);
 | 
						|
    if (this.#annotations.length === 0) {
 | 
						|
      this.#commentsList.replaceChildren(commentItem);
 | 
						|
      this.#annotations.push(annotation);
 | 
						|
      this.#setCommentsCount();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const index = binarySearchFirstItem(
 | 
						|
      this.#annotations,
 | 
						|
      a => this.#sortComments(a, annotation) >= 0
 | 
						|
    );
 | 
						|
    this.#annotations.splice(index, 0, annotation);
 | 
						|
    if (index >= this.#commentsList.children.length) {
 | 
						|
      this.#commentsList.append(commentItem);
 | 
						|
    } else {
 | 
						|
      this.#commentsList.insertBefore(
 | 
						|
        commentItem,
 | 
						|
        this.#commentsList.children[index]
 | 
						|
      );
 | 
						|
    }
 | 
						|
    this.#setCommentsCount();
 | 
						|
  }
 | 
						|
 | 
						|
  #setCommentsCount(container = this.#commentsList) {
 | 
						|
    const count = this.#idsToElements.size;
 | 
						|
    this.#sidebarTitle.setAttribute(
 | 
						|
      "data-l10n-args",
 | 
						|
      JSON.stringify({ count })
 | 
						|
    );
 | 
						|
    this.#commentCount.textContent = count;
 | 
						|
    if (count === 0) {
 | 
						|
      container.append(this.#createZeroCommentElement());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  #createZeroCommentElement() {
 | 
						|
    const commentItem = document.createElement("li");
 | 
						|
    commentItem.classList.add("sidebarComment", "noComments");
 | 
						|
    const textDiv = document.createElement("div");
 | 
						|
    textDiv.className = "sidebarCommentText";
 | 
						|
    textDiv.setAttribute(
 | 
						|
      "data-l10n-id",
 | 
						|
      "pdfjs-editor-comments-sidebar-no-comments1"
 | 
						|
    );
 | 
						|
    commentItem.append(textDiv);
 | 
						|
    if (this.#learnMoreUrl) {
 | 
						|
      const a = document.createElement("a");
 | 
						|
      a.setAttribute(
 | 
						|
        "data-l10n-id",
 | 
						|
        "pdfjs-editor-comments-sidebar-no-comments-link"
 | 
						|
      );
 | 
						|
      a.href = this.#learnMoreUrl;
 | 
						|
      a.target = "_blank";
 | 
						|
      a.rel = "noopener noreferrer";
 | 
						|
      commentItem.append(a);
 | 
						|
    }
 | 
						|
    return commentItem;
 | 
						|
  }
 | 
						|
 | 
						|
  #setDate(element, date) {
 | 
						|
    date = PDFDateString.toDateObject(date);
 | 
						|
    element.dateTime = date.toISOString();
 | 
						|
    element.textContent = this.#dateFormat.format(date);
 | 
						|
  }
 | 
						|
 | 
						|
  #setText(element, richText, contentsObj) {
 | 
						|
    element.replaceChildren();
 | 
						|
    const html =
 | 
						|
      richText?.str && (!contentsObj?.str || richText.str === contentsObj.str)
 | 
						|
        ? richText.html
 | 
						|
        : contentsObj?.str;
 | 
						|
    renderRichText(
 | 
						|
      {
 | 
						|
        html,
 | 
						|
        dir: contentsObj?.dir || "auto",
 | 
						|
        className: "richText",
 | 
						|
      },
 | 
						|
      element
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  #createCommentElement(annotation) {
 | 
						|
    const {
 | 
						|
      id,
 | 
						|
      creationDate,
 | 
						|
      modificationDate,
 | 
						|
      richText,
 | 
						|
      contentsObj,
 | 
						|
      color,
 | 
						|
      opacity,
 | 
						|
    } = annotation;
 | 
						|
    const commentItem = document.createElement("li");
 | 
						|
    commentItem.role = "button";
 | 
						|
    commentItem.className = "sidebarComment";
 | 
						|
    commentItem.tabIndex = -1;
 | 
						|
    commentItem.style.backgroundColor =
 | 
						|
      (color && CommentManager._makeCommentColor(color, opacity)) || "";
 | 
						|
    const dateDiv = document.createElement("time");
 | 
						|
    this.#setDate(dateDiv, modificationDate || creationDate);
 | 
						|
 | 
						|
    const textDiv = document.createElement("div");
 | 
						|
    textDiv.className = "sidebarCommentText";
 | 
						|
    this.#setText(textDiv, richText, contentsObj);
 | 
						|
 | 
						|
    commentItem.append(dateDiv, textDiv);
 | 
						|
    commentItem.addEventListener("click", this.#boundCommentClick);
 | 
						|
    commentItem.addEventListener("keydown", this.#boundCommentKeydown);
 | 
						|
 | 
						|
    this.#elementsToAnnotations.set(commentItem, annotation);
 | 
						|
    this.#idsToElements.set(id, commentItem);
 | 
						|
    return commentItem;
 | 
						|
  }
 | 
						|
 | 
						|
  async #commentClick({ currentTarget }) {
 | 
						|
    if (currentTarget.classList.contains("selected")) {
 | 
						|
      currentTarget.classList.remove("selected");
 | 
						|
      this.#popup._hide();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const annotation = this.#elementsToAnnotations.get(currentTarget);
 | 
						|
    if (!annotation) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    this.#popup._hide();
 | 
						|
    const { id, pageIndex, rect } = annotation;
 | 
						|
    const pageNumber = pageIndex + 1;
 | 
						|
    const pageVisiblePromise =
 | 
						|
      this.#uiManager?.waitForEditorsRendered(pageNumber);
 | 
						|
    this.#linkService?.goToXY(pageNumber, rect[0], rect[3], {
 | 
						|
      center: "both",
 | 
						|
    });
 | 
						|
    this.selectComment(currentTarget);
 | 
						|
    await pageVisiblePromise;
 | 
						|
    this.#uiManager?.selectComment(pageIndex, id);
 | 
						|
  }
 | 
						|
 | 
						|
  #commentKeydown(e) {
 | 
						|
    const { key, currentTarget } = e;
 | 
						|
    switch (key) {
 | 
						|
      case "ArrowDown":
 | 
						|
        (
 | 
						|
          currentTarget.nextElementSibling ||
 | 
						|
          this.#commentsList.firstElementChild
 | 
						|
        ).focus();
 | 
						|
        stopEvent(e);
 | 
						|
        break;
 | 
						|
      case "ArrowUp":
 | 
						|
        (
 | 
						|
          currentTarget.previousElementSibling ||
 | 
						|
          this.#commentsList.lastElementChild
 | 
						|
        ).focus();
 | 
						|
        stopEvent(e);
 | 
						|
        break;
 | 
						|
      case "Home":
 | 
						|
        this.#commentsList.firstElementChild.focus();
 | 
						|
        stopEvent(e);
 | 
						|
        break;
 | 
						|
      case "End":
 | 
						|
        this.#commentsList.lastElementChild.focus();
 | 
						|
        stopEvent(e);
 | 
						|
        break;
 | 
						|
      case "Enter":
 | 
						|
      case " ":
 | 
						|
        this.#commentClick(e);
 | 
						|
        stopEvent(e);
 | 
						|
        break;
 | 
						|
      case "ShiftTab":
 | 
						|
        this.#closeButton.focus();
 | 
						|
        stopEvent(e);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  #sortComments(a, b) {
 | 
						|
    const dateA = PDFDateString.toDateObject(
 | 
						|
      a.modificationDate || a.creationDate
 | 
						|
    );
 | 
						|
    const dateB = PDFDateString.toDateObject(
 | 
						|
      b.modificationDate || b.creationDate
 | 
						|
    );
 | 
						|
    if (dateA !== dateB) {
 | 
						|
      if (dateA !== null && dateB !== null) {
 | 
						|
        return dateB - dateA;
 | 
						|
      }
 | 
						|
      return dateA !== null ? -1 : 1;
 | 
						|
    }
 | 
						|
    if (a.pageIndex !== b.pageIndex) {
 | 
						|
      return a.pageIndex - b.pageIndex;
 | 
						|
    }
 | 
						|
    if (a.rect[3] !== b.rect[3]) {
 | 
						|
      return b.rect[3] - a.rect[3];
 | 
						|
    }
 | 
						|
    if (a.rect[0] !== b.rect[0]) {
 | 
						|
      return a.rect[0] - b.rect[0];
 | 
						|
    }
 | 
						|
    if (a.rect[1] !== b.rect[1]) {
 | 
						|
      return b.rect[1] - a.rect[1];
 | 
						|
    }
 | 
						|
    if (a.rect[2] !== b.rect[2]) {
 | 
						|
      return a.rect[2] - b.rect[2];
 | 
						|
    }
 | 
						|
    return a.id.localeCompare(b.id);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class CommentDialog {
 | 
						|
  #dialog;
 | 
						|
 | 
						|
  #editor;
 | 
						|
 | 
						|
  #overlayManager;
 | 
						|
 | 
						|
  #previousText = "";
 | 
						|
 | 
						|
  #commentText = "";
 | 
						|
 | 
						|
  #textInput;
 | 
						|
 | 
						|
  #title;
 | 
						|
 | 
						|
  #saveButton;
 | 
						|
 | 
						|
  #uiManager;
 | 
						|
 | 
						|
  #prevDragX = 0;
 | 
						|
 | 
						|
  #prevDragY = 0;
 | 
						|
 | 
						|
  #dialogX = 0;
 | 
						|
 | 
						|
  #dialogY = 0;
 | 
						|
 | 
						|
  #isLTR;
 | 
						|
 | 
						|
  #eventBus;
 | 
						|
 | 
						|
  constructor(
 | 
						|
    { dialog, toolbar, title, textInput, cancelButton, saveButton },
 | 
						|
    overlayManager,
 | 
						|
    eventBus,
 | 
						|
    ltr
 | 
						|
  ) {
 | 
						|
    this.#dialog = dialog;
 | 
						|
    this.#textInput = textInput;
 | 
						|
    this.#overlayManager = overlayManager;
 | 
						|
    this.#eventBus = eventBus;
 | 
						|
    this.#saveButton = saveButton;
 | 
						|
    this.#title = title;
 | 
						|
    this.#isLTR = ltr;
 | 
						|
 | 
						|
    const finishBound = this.#finish.bind(this);
 | 
						|
    dialog.addEventListener("close", finishBound);
 | 
						|
    dialog.addEventListener("contextmenu", e => {
 | 
						|
      if (e.target !== this.#textInput) {
 | 
						|
        e.preventDefault();
 | 
						|
      }
 | 
						|
    });
 | 
						|
    cancelButton.addEventListener("click", finishBound);
 | 
						|
    saveButton.addEventListener("click", this.#save.bind(this));
 | 
						|
 | 
						|
    textInput.addEventListener("input", () => {
 | 
						|
      saveButton.disabled = textInput.value === this.#previousText;
 | 
						|
    });
 | 
						|
 | 
						|
    // Make the dialog draggable.
 | 
						|
    let pointerMoveAC;
 | 
						|
    const cancelDrag = () => {
 | 
						|
      dialog.classList.remove("dragging");
 | 
						|
      pointerMoveAC?.abort();
 | 
						|
      pointerMoveAC = null;
 | 
						|
    };
 | 
						|
    toolbar.addEventListener("pointerdown", e => {
 | 
						|
      if (pointerMoveAC) {
 | 
						|
        cancelDrag();
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      const { clientX, clientY } = e;
 | 
						|
      stopEvent(e);
 | 
						|
      this.#prevDragX = clientX;
 | 
						|
      this.#prevDragY = clientY;
 | 
						|
      pointerMoveAC = new AbortController();
 | 
						|
      const { signal } = pointerMoveAC;
 | 
						|
      const { innerHeight, innerWidth } = window;
 | 
						|
      dialog.classList.add("dragging");
 | 
						|
      window.addEventListener(
 | 
						|
        "pointermove",
 | 
						|
        ev => {
 | 
						|
          if (!pointerMoveAC) {
 | 
						|
            return;
 | 
						|
          }
 | 
						|
          const { clientX: x, clientY: y } = ev;
 | 
						|
          this.#setPosition(
 | 
						|
            this.#dialogX + (x - this.#prevDragX) / innerWidth,
 | 
						|
            this.#dialogY + (y - this.#prevDragY) / innerHeight
 | 
						|
          );
 | 
						|
          this.#prevDragX = x;
 | 
						|
          this.#prevDragY = y;
 | 
						|
          stopEvent(ev);
 | 
						|
        },
 | 
						|
        { signal }
 | 
						|
      );
 | 
						|
      window.addEventListener("blur", cancelDrag, { signal });
 | 
						|
      window.addEventListener(
 | 
						|
        "pointerup",
 | 
						|
        ev => {
 | 
						|
          if (pointerMoveAC) {
 | 
						|
            cancelDrag();
 | 
						|
            stopEvent(ev);
 | 
						|
          }
 | 
						|
        },
 | 
						|
        { signal }
 | 
						|
      );
 | 
						|
    });
 | 
						|
 | 
						|
    overlayManager.register(dialog);
 | 
						|
  }
 | 
						|
 | 
						|
  async open(uiManager, editor, posX, posY, options) {
 | 
						|
    if (editor) {
 | 
						|
      this.#uiManager = uiManager;
 | 
						|
      this.#editor = editor;
 | 
						|
    }
 | 
						|
    const {
 | 
						|
      contentsObj: { str },
 | 
						|
      color,
 | 
						|
      opacity,
 | 
						|
    } = editor.getData();
 | 
						|
    const { style: dialogStyle } = this.#dialog;
 | 
						|
    if (color) {
 | 
						|
      dialogStyle.backgroundColor = CommentManager._makeCommentColor(
 | 
						|
        color,
 | 
						|
        opacity
 | 
						|
      );
 | 
						|
      dialogStyle.borderColor = Util.makeHexColor(...color);
 | 
						|
    } else {
 | 
						|
      dialogStyle.backgroundColor = dialogStyle.borderColor = "";
 | 
						|
    }
 | 
						|
    this.#commentText = str || "";
 | 
						|
    const textInput = this.#textInput;
 | 
						|
    textInput.value = this.#previousText = this.#commentText;
 | 
						|
    if (str) {
 | 
						|
      this.#title.setAttribute(
 | 
						|
        "data-l10n-id",
 | 
						|
        "pdfjs-editor-edit-comment-dialog-title-when-editing"
 | 
						|
      );
 | 
						|
      this.#saveButton.setAttribute(
 | 
						|
        "data-l10n-id",
 | 
						|
        "pdfjs-editor-edit-comment-dialog-save-button-when-editing"
 | 
						|
      );
 | 
						|
    } else {
 | 
						|
      this.#title.setAttribute(
 | 
						|
        "data-l10n-id",
 | 
						|
        "pdfjs-editor-edit-comment-dialog-title-when-adding"
 | 
						|
      );
 | 
						|
      this.#saveButton.setAttribute(
 | 
						|
        "data-l10n-id",
 | 
						|
        "pdfjs-editor-edit-comment-dialog-save-button-when-adding"
 | 
						|
      );
 | 
						|
    }
 | 
						|
    if (options?.height) {
 | 
						|
      textInput.style.height = `${options.height}px`;
 | 
						|
    }
 | 
						|
    this.#uiManager?.removeEditListeners();
 | 
						|
    this.#saveButton.disabled = true;
 | 
						|
    const parentDimensions = options?.parentDimensions;
 | 
						|
    const { innerHeight, innerWidth } = window;
 | 
						|
    if (editor.hasDefaultPopupPosition()) {
 | 
						|
      const { dialogWidth, dialogHeight } = this._dialogDimensions;
 | 
						|
      if (parentDimensions) {
 | 
						|
        if (
 | 
						|
          this.#isLTR &&
 | 
						|
          posX + dialogWidth >
 | 
						|
            Math.min(parentDimensions.x + parentDimensions.width, innerWidth)
 | 
						|
        ) {
 | 
						|
          const buttonWidth = this.#editor.commentButtonWidth;
 | 
						|
          posX -= dialogWidth - buttonWidth * parentDimensions.width;
 | 
						|
        } else if (!this.#isLTR) {
 | 
						|
          const buttonWidth =
 | 
						|
            this.#editor.commentButtonWidth * parentDimensions.width;
 | 
						|
          if (posX - dialogWidth < Math.max(0, parentDimensions.x)) {
 | 
						|
            posX = Math.max(0, posX);
 | 
						|
          } else {
 | 
						|
            posX -= dialogWidth - buttonWidth;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      const height = Math.max(dialogHeight, options?.height || 0);
 | 
						|
      if (posY + height > innerHeight) {
 | 
						|
        posY = innerHeight - height;
 | 
						|
      }
 | 
						|
      if (posY < 0) {
 | 
						|
        posY = 0;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    posX = MathClamp(posX / innerWidth, 0, 1);
 | 
						|
    posY = MathClamp(posY / innerHeight, 0, 1);
 | 
						|
    this.#setPosition(posX, posY);
 | 
						|
 | 
						|
    await this.#overlayManager.open(this.#dialog);
 | 
						|
    textInput.focus();
 | 
						|
  }
 | 
						|
 | 
						|
  async #save() {
 | 
						|
    this.#editor.comment = this.#textInput.value;
 | 
						|
    this.#finish();
 | 
						|
  }
 | 
						|
 | 
						|
  get _dialogDimensions() {
 | 
						|
    const dialog = this.#dialog;
 | 
						|
    const { style } = dialog;
 | 
						|
    style.opacity = "0";
 | 
						|
    style.display = "block";
 | 
						|
    const { width, height } = dialog.getBoundingClientRect();
 | 
						|
    style.opacity = style.display = "";
 | 
						|
    return shadow(this, "_dialogDimensions", {
 | 
						|
      dialogWidth: width,
 | 
						|
      dialogHeight: height,
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  #setPosition(x, y) {
 | 
						|
    this.#dialogX = x;
 | 
						|
    this.#dialogY = y;
 | 
						|
    const { style } = this.#dialog;
 | 
						|
    style.left = `${100 * x}%`;
 | 
						|
    style.top = `${100 * y}%`;
 | 
						|
  }
 | 
						|
 | 
						|
  #finish() {
 | 
						|
    if (!this.#editor) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const edited = this.#textInput.value !== this.#commentText;
 | 
						|
    this.#eventBus.dispatch("reporttelemetry", {
 | 
						|
      source: this,
 | 
						|
      details: {
 | 
						|
        type: "comment",
 | 
						|
        data: {
 | 
						|
          edited,
 | 
						|
        },
 | 
						|
      },
 | 
						|
    });
 | 
						|
 | 
						|
    this.#editor?.focusCommentButton();
 | 
						|
    this.#editor = null;
 | 
						|
    this.#textInput.value = this.#previousText = this.#commentText = "";
 | 
						|
    this.#overlayManager.closeIfActive(this.#dialog);
 | 
						|
    this.#textInput.style.height = "";
 | 
						|
    this.#uiManager?.addEditListeners();
 | 
						|
    this.#uiManager = null;
 | 
						|
  }
 | 
						|
 | 
						|
  destroy() {
 | 
						|
    this.#uiManager = null;
 | 
						|
    this.#editor = null;
 | 
						|
    this.#finish();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class CommentPopup {
 | 
						|
  #buttonsContainer = null;
 | 
						|
 | 
						|
  #eventBus;
 | 
						|
 | 
						|
  #commentDialog;
 | 
						|
 | 
						|
  #dateFormat;
 | 
						|
 | 
						|
  #editor = null;
 | 
						|
 | 
						|
  #isLTR;
 | 
						|
 | 
						|
  #container = null;
 | 
						|
 | 
						|
  #text = null;
 | 
						|
 | 
						|
  #time = null;
 | 
						|
 | 
						|
  #prevDragX = 0;
 | 
						|
 | 
						|
  #prevDragY = 0;
 | 
						|
 | 
						|
  #posX = 0;
 | 
						|
 | 
						|
  #posY = 0;
 | 
						|
 | 
						|
  #previousFocusedElement = null;
 | 
						|
 | 
						|
  #selected = false;
 | 
						|
 | 
						|
  #visible = false;
 | 
						|
 | 
						|
  constructor(eventBus, dateFormat, ltr, commentDialog) {
 | 
						|
    this.#eventBus = eventBus;
 | 
						|
    this.#dateFormat = dateFormat;
 | 
						|
    this.#isLTR = ltr;
 | 
						|
    this.#commentDialog = commentDialog;
 | 
						|
    this.sidebar = null;
 | 
						|
  }
 | 
						|
 | 
						|
  get _popupWidth() {
 | 
						|
    const container = this.#createPopup();
 | 
						|
    const { style } = container;
 | 
						|
    style.opacity = "0";
 | 
						|
    style.display = "block";
 | 
						|
    document.body.append(container);
 | 
						|
    const width = container.getBoundingClientRect().width;
 | 
						|
    container.remove();
 | 
						|
    style.opacity = style.display = "";
 | 
						|
    return shadow(this, "_popupWidth", width);
 | 
						|
  }
 | 
						|
 | 
						|
  #createPopup() {
 | 
						|
    if (this.#container) {
 | 
						|
      return this.#container;
 | 
						|
    }
 | 
						|
    const container = (this.#container = document.createElement("div"));
 | 
						|
    container.className = "commentPopup";
 | 
						|
    container.id = "commentPopup";
 | 
						|
    container.tabIndex = -1;
 | 
						|
    container.role = "dialog";
 | 
						|
    container.ariaModal = "false";
 | 
						|
    container.addEventListener("contextmenu", noContextMenu);
 | 
						|
    container.addEventListener("keydown", e => {
 | 
						|
      if (e.key === "Escape") {
 | 
						|
        this.toggle(this.#editor, true, false);
 | 
						|
        this.#previousFocusedElement?.focus();
 | 
						|
        stopEvent(e);
 | 
						|
      }
 | 
						|
    });
 | 
						|
    container.addEventListener("click", () => {
 | 
						|
      container.focus();
 | 
						|
    });
 | 
						|
 | 
						|
    const top = document.createElement("div");
 | 
						|
    top.className = "commentPopupTop";
 | 
						|
    const time = (this.#time = document.createElement("time"));
 | 
						|
    time.className = "commentPopupTime";
 | 
						|
 | 
						|
    const buttons = (this.#buttonsContainer = document.createElement("div"));
 | 
						|
    buttons.className = "commentPopupButtons";
 | 
						|
    const edit = document.createElement("button");
 | 
						|
    edit.classList.add("commentPopupEdit", "toolbarButton");
 | 
						|
    edit.tabIndex = 0;
 | 
						|
    edit.setAttribute("data-l10n-id", "pdfjs-editor-edit-comment-popup-button");
 | 
						|
    edit.ariaHasPopup = "dialog";
 | 
						|
    edit.ariaControlsElements = [this.#commentDialog];
 | 
						|
    const editLabel = document.createElement("span");
 | 
						|
    editLabel.setAttribute(
 | 
						|
      "data-l10n-id",
 | 
						|
      "pdfjs-editor-edit-comment-popup-button-label"
 | 
						|
    );
 | 
						|
    edit.append(editLabel);
 | 
						|
    edit.addEventListener("click", () => {
 | 
						|
      const editor = this.#editor;
 | 
						|
      const height = parseFloat(getComputedStyle(this.#text).height);
 | 
						|
      this.toggle(editor, /* isSelected */ true, /* visibility */ false);
 | 
						|
      editor.editComment({
 | 
						|
        height,
 | 
						|
      });
 | 
						|
    });
 | 
						|
    edit.addEventListener("contextmenu", noContextMenu);
 | 
						|
 | 
						|
    const del = document.createElement("button");
 | 
						|
    del.classList.add("commentPopupDelete", "toolbarButton");
 | 
						|
    del.tabIndex = 0;
 | 
						|
    del.setAttribute(
 | 
						|
      "data-l10n-id",
 | 
						|
      "pdfjs-editor-delete-comment-popup-button"
 | 
						|
    );
 | 
						|
    const delLabel = document.createElement("span");
 | 
						|
    delLabel.setAttribute(
 | 
						|
      "data-l10n-id",
 | 
						|
      "pdfjs-editor-delete-comment-popup-button-label"
 | 
						|
    );
 | 
						|
    del.append(delLabel);
 | 
						|
    del.addEventListener("click", () => {
 | 
						|
      this.#eventBus.dispatch("reporttelemetry", {
 | 
						|
        source: this,
 | 
						|
        details: {
 | 
						|
          type: "comment",
 | 
						|
          data: {
 | 
						|
            deleted: true,
 | 
						|
          },
 | 
						|
        },
 | 
						|
      });
 | 
						|
      this.#editor.comment = null;
 | 
						|
      this.destroy();
 | 
						|
    });
 | 
						|
    del.addEventListener("contextmenu", noContextMenu);
 | 
						|
    buttons.append(edit, del);
 | 
						|
 | 
						|
    top.append(time, buttons);
 | 
						|
 | 
						|
    const separator = document.createElement("hr");
 | 
						|
 | 
						|
    const text = (this.#text = document.createElement("div"));
 | 
						|
    text.className = "commentPopupText";
 | 
						|
    container.append(top, separator, text);
 | 
						|
 | 
						|
    // Make the dialog draggable.
 | 
						|
    let pointerMoveAC;
 | 
						|
    const cancelDrag = () => {
 | 
						|
      container.classList.remove("dragging");
 | 
						|
      pointerMoveAC?.abort();
 | 
						|
      pointerMoveAC = null;
 | 
						|
    };
 | 
						|
    top.addEventListener("pointerdown", e => {
 | 
						|
      if (pointerMoveAC) {
 | 
						|
        cancelDrag();
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      const { target, clientX, clientY } = e;
 | 
						|
      if (buttons.contains(target)) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      stopEvent(e);
 | 
						|
      const { width: parentWidth, height: parentHeight } =
 | 
						|
        this.#editor.parentBoundingClientRect;
 | 
						|
      this.#prevDragX = clientX;
 | 
						|
      this.#prevDragY = clientY;
 | 
						|
      pointerMoveAC = new AbortController();
 | 
						|
      const { signal } = pointerMoveAC;
 | 
						|
      container.classList.add("dragging");
 | 
						|
      window.addEventListener(
 | 
						|
        "pointermove",
 | 
						|
        ev => {
 | 
						|
          if (!pointerMoveAC) {
 | 
						|
            return; // Not dragging.
 | 
						|
          }
 | 
						|
          const { clientX: x, clientY: y } = ev;
 | 
						|
          this.#setPosition(
 | 
						|
            this.#posX + (x - this.#prevDragX) / parentWidth,
 | 
						|
            this.#posY + (y - this.#prevDragY) / parentHeight,
 | 
						|
            /* correctPosition = */ false
 | 
						|
          );
 | 
						|
          this.#prevDragX = x;
 | 
						|
          this.#prevDragY = y;
 | 
						|
          stopEvent(ev);
 | 
						|
        },
 | 
						|
        { signal }
 | 
						|
      );
 | 
						|
      window.addEventListener("blur", cancelDrag, { signal });
 | 
						|
      window.addEventListener(
 | 
						|
        "pointerup",
 | 
						|
        ev => {
 | 
						|
          if (pointerMoveAC) {
 | 
						|
            cancelDrag();
 | 
						|
            stopEvent(ev);
 | 
						|
          }
 | 
						|
        },
 | 
						|
        { signal }
 | 
						|
      );
 | 
						|
    });
 | 
						|
 | 
						|
    return container;
 | 
						|
  }
 | 
						|
 | 
						|
  updateColor(editor) {
 | 
						|
    if (this.#editor !== editor || !this.#visible) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const { color, opacity } = editor.getData();
 | 
						|
    this.#container.style.backgroundColor =
 | 
						|
      (color && CommentManager._makeCommentColor(color, opacity)) || "";
 | 
						|
  }
 | 
						|
 | 
						|
  _hide(editor) {
 | 
						|
    const container = this.#createPopup();
 | 
						|
 | 
						|
    container.classList.toggle("hidden", true);
 | 
						|
    container.classList.toggle("selected", false);
 | 
						|
    (editor || this.#editor)?.setCommentButtonStates({
 | 
						|
      selected: false,
 | 
						|
      hasPopup: false,
 | 
						|
    });
 | 
						|
    this.#editor = null;
 | 
						|
    this.#selected = false;
 | 
						|
    this.#visible = false;
 | 
						|
    this.#text.replaceChildren();
 | 
						|
    this.sidebar.selectComment(null);
 | 
						|
  }
 | 
						|
 | 
						|
  toggle(editor, isSelected, visibility = undefined, isEditable = true) {
 | 
						|
    if (!editor) {
 | 
						|
      this.destroy();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (isSelected) {
 | 
						|
      visibility ??=
 | 
						|
        this.#editor === editor ? !this.#selected || !this.#visible : true;
 | 
						|
    } else {
 | 
						|
      if (this.#selected) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      visibility ??= !this.#visible;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!visibility) {
 | 
						|
      this._hide(editor);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    this.#visible = true;
 | 
						|
    if (this.#editor !== editor) {
 | 
						|
      this.#editor?.setCommentButtonStates({
 | 
						|
        selected: false,
 | 
						|
        hasPopup: false,
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    const container = this.#createPopup();
 | 
						|
    this.#buttonsContainer.classList.toggle("hidden", !isEditable);
 | 
						|
    container.classList.toggle("hidden", false);
 | 
						|
    container.classList.toggle("selected", isSelected);
 | 
						|
    this.#selected = isSelected;
 | 
						|
    this.#editor = editor;
 | 
						|
    editor.setCommentButtonStates({
 | 
						|
      selected: isSelected,
 | 
						|
      hasPopup: true,
 | 
						|
    });
 | 
						|
 | 
						|
    const {
 | 
						|
      contentsObj,
 | 
						|
      richText,
 | 
						|
      creationDate,
 | 
						|
      modificationDate,
 | 
						|
      color,
 | 
						|
      opacity,
 | 
						|
    } = editor.getData();
 | 
						|
    container.style.backgroundColor =
 | 
						|
      (color && CommentManager._makeCommentColor(color, opacity)) || "";
 | 
						|
    this.#text.replaceChildren();
 | 
						|
    const html =
 | 
						|
      richText?.str && (!contentsObj?.str || richText.str === contentsObj.str)
 | 
						|
        ? richText.html
 | 
						|
        : contentsObj?.str;
 | 
						|
    if (html) {
 | 
						|
      renderRichText(
 | 
						|
        {
 | 
						|
          html,
 | 
						|
          dir: contentsObj?.dir || "auto",
 | 
						|
          className: "richText",
 | 
						|
        },
 | 
						|
        this.#text
 | 
						|
      );
 | 
						|
    }
 | 
						|
    this.#time.textContent = this.#dateFormat.format(
 | 
						|
      PDFDateString.toDateObject(modificationDate || creationDate)
 | 
						|
    );
 | 
						|
    this.#setPosition(
 | 
						|
      ...editor.commentPopupPosition,
 | 
						|
      /* correctPosition = */ editor.hasDefaultPopupPosition()
 | 
						|
    );
 | 
						|
    editor.elementBeforePopup.after(container);
 | 
						|
    container.addEventListener(
 | 
						|
      "focus",
 | 
						|
      ({ relatedTarget }) => {
 | 
						|
        this.#previousFocusedElement = relatedTarget;
 | 
						|
      },
 | 
						|
      { once: true }
 | 
						|
    );
 | 
						|
    if (isSelected) {
 | 
						|
      setTimeout(() => container.focus(), 0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  #setPosition(x, y, correctPosition) {
 | 
						|
    if (!correctPosition) {
 | 
						|
      this.#editor.commentPopupPosition = [x, y];
 | 
						|
    } else {
 | 
						|
      const widthRatio =
 | 
						|
        this._popupWidth / this.#editor.parentBoundingClientRect.width;
 | 
						|
      if (
 | 
						|
        (this.#isLTR && x + widthRatio > 1) ||
 | 
						|
        (!this.#isLTR && x - widthRatio >= 0)
 | 
						|
      ) {
 | 
						|
        const buttonWidth = this.#editor.commentButtonWidth;
 | 
						|
        x -= widthRatio - buttonWidth;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    this.#posX = x;
 | 
						|
    this.#posY = y;
 | 
						|
    const { style } = this.#container;
 | 
						|
    style.left = `${100 * x}%`;
 | 
						|
    style.top = `${100 * y}%`;
 | 
						|
  }
 | 
						|
 | 
						|
  destroy() {
 | 
						|
    this._hide();
 | 
						|
    this.#container?.remove();
 | 
						|
    this.#container = this.#text = this.#time = null;
 | 
						|
    this.#prevDragX = this.#prevDragY = Infinity;
 | 
						|
    this.#posX = this.#posY = 0;
 | 
						|
    this.#previousFocusedElement = null;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
export { CommentManager };
 |