bookmarks.js

/* eslint-disable max-len */

import { TW_DATA_TAG, tw_focus, tw_hide, tw_toggleCssClass } from "./util.js";

/**
 * Bookmarks manager
 */
class BookmarkLayer {
  constructor(
    app,
    showBookmarksOnOpen = false,
    preserveBookmarksState = true,
    bookmarksFullyExpanded = false
  ) {
    this._app = app;
    this._isBookmarksOpened = false;
    this._isResizeEventAdded = false;
    this._bindEvents();

    /**
     * All bookmarks items.
     */
    this.allBookmarks = [];

    /**
     * If *true*, on first open bookmarks it will show bookmarks fully expaned.
     * In next openings, if it will be expanded or collapsed depends on the property value
     * of *preserveBookmarksState*.
     *
     * Default false.
     * */
    this.bookmarksFullyExpanded = bookmarksFullyExpanded || false;

    /** If *false*, on every open bookmarks it will show bookmarks
     * fully expaned or collapsed based on the property value
     * of *bookmarksFullyExpanded*.
     *
     * Default: *true*
     * */
    this.preserveBookmarksState = preserveBookmarksState === false || true;

    /** If *true* it will automatically open bookmarks when the document
     * is loaded. If the document doesn't have bookmarks nothing will happen.
     *
     * Default: *false*
     * */
    this.showBookmarksOnOpen = showBookmarksOnOpen || false;
  }

  // #region private functions
  get _bookmarksDiv() {
    return this._app.isMobile
      ? this._app.elements.tw2018elem_bookmarksDivMobile
      : this._app.elements.tw2018elem_bookmarksDivDesktop;
  }

  _bindEvents() {
    const me = this;
    this._app.elements.tw2018elem_bookmarkBtn.addEventListener(
      "click",
      function (event) {
        if (!event.defaultPrevented) {
          me._toggle();
        }
      }
    );

    this._app.elements.tw2018elem_bookmarkPopupClose.addEventListener(
      "click",
      function (event) {
        me._toggle();
        event.preventDefault();
      }
    );
  }

  _showMobile(val) {
    this._app.elements.tw2018elem_bookmarkPopupOverlay.style.display = val
      ? "flex"
      : "none";
  }

  _showDesktop(val) {
    this._app.elements.tw2018elem_bookmarksDivDesktop.style.display = val
      ? "inline-block"
      : "none";
    if (!val) {
      this._app.elements.tw2018elem_viewerContainer.style.left = "0";
    } else {
      const bookmarkWidth =
        this._app.elements.tw2018elem_bookmarksDivDesktop.getBoundingClientRect()
          .width;
      this._app.elements.tw2018elem_viewerContainer.style.left =
        bookmarkWidth + "px";
    }

    this._app.setElementsSize();
  }

  _addBookmark(bookmark, parentDiv) {
    const outlineItem = document.createElement("div");
    outlineItem.classList.add("tw2018css_outlineItem");
    outlineItem.setAttribute(TW_DATA_TAG, "");
    const a = document.createElement("a");
    a.setAttribute("href", "#");
    a.innerText = bookmark.title;
    a.setAttribute("bookmarkId", this.allBookmarks.length);
    this.allBookmarks.push(bookmark.dest);
    const me = this;
    a.onclick = function (event) {
      const id = parseInt(this.getAttribute("bookmarkId"));
      me._app.linkService.navigateTo(me.allBookmarks[id]);
      event.preventDefault();
      if (me._app.isMobile) {
        me._toggle();
      }
      return false;
    };

    if (bookmark.items.length > 0) {
      const toggleDiv = document.createElement("div");
      toggleDiv.classList.add("tw2018css_outlineItemToggler");
      toggleDiv.setAttribute(TW_DATA_TAG, "");
      if (!this.bookmarksFullyExpanded) {
        toggleDiv.classList.add("tw2018css_outlineItemsHidden");
      }

      toggleDiv.onclick = function () {
        me._toggleBookmark(this);
      };
      outlineItem.appendChild(toggleDiv);
      outlineItem.appendChild(a);
      const outlineItems = document.createElement("div");
      outlineItems.classList.add("tw2018css_outlineItems");
      outlineItems.setAttribute(TW_DATA_TAG, "");
      outlineItem.appendChild(outlineItems);
      for (let i = 0; i < bookmark.items.length; i++) {
        me._addBookmark(bookmark.items[i], outlineItems);
      }
    } else {
      outlineItem.classList.add("tw2018css_simple");
      outlineItem.setAttribute(TW_DATA_TAG, "");
      outlineItem.appendChild(a);
    }
    parentDiv.appendChild(outlineItem);
  }

  _toggleBookmarks() {
    if (!this._bookmarksDiv) {
      return;
    }
    const itemTogglers = this._bookmarksDiv.getElementsByClassName(
      "tw2018css_outlineItemToggler"
    );
    for (let i = 0; i < itemTogglers.length; i++) {
      this._toggleBookmark(itemTogglers[i]);
    }
  }

  _resizeBookmarksDivFrame() {
    const bookmarkWidth = this._bookmarksDiv.getBoundingClientRect().width;
    this._app.elements.tw2018elem_viewerContainer.style.left =
      bookmarkWidth + "px";
    this._app.elements.tw2018elem_viewerContainer.style.width =
      this._app.elements.tw2018elem_mainContainer.getBoundingClientRect()
        .width -
      2 -
      bookmarkWidth +
      "px";
    this._app.setElementsSize();
  }

  _addIFrameResizeHandlerIfNeeded() {
    if (!this._app.isMobile) {
      if (!this._isResizeEventAdded) {
        const me = this;
        if (this._app.elements.tw2018elem_bookmarksDivFrame.contentWindow) {
          this._app.elements.tw2018elem_bookmarksDivFrame.contentWindow.addEventListener(
            "resize",
            me._resizeBookmarksDivFrame.bind(me)
          );
        } else {
          // TODO check for IE11 -> multiple instances of app
          window.frames.tw2018elem_bookmarksDivFrame.addEventListener(
            "resize",
            me._resizeBookmarksDivFrame.bind(me)
          );
        }
        me._isResizeEventAdded = true;
      }
    }
  }

  _expandAll() {
    if (!this._bookmarksDiv) {
      return;
    }
    const itemTogglers = this._bookmarksDiv.getElementsByClassName(
      "tw2018css_outlineItemToggler"
    );
    for (let i = 0; i < itemTogglers.length; i++) {
      this._expandBookmarkItem(itemTogglers[i]);
    }
  }

  _expandBookmarkItem(div) {
    div.classList.remove("tw2018css_outlineItemsHidden");
  }

  _closeBookmarkItem(div) {
    div.classList.add("tw2018css_outlineItemsHidden");
    div.setAttribute(TW_DATA_TAG, "");
  }

  _toggleBookmark(div) {
    if (div.classList.contains("tw2018css_outlineItemsHidden")) {
      this._expandBookmarkItem(div);
    } else {
      this._closeBookmarkItem(div);
    }
  }

  _toggle() {
    this.show(!this._isBookmarksOpened);
    if (this._app.isMobile) {
      tw_toggleCssClass(
        this._app.elements.tw2018elem_bookmarkPopupOverlay,
        "tw2018elem_bookmarkPopupOverlayVisible"
      );
    }
  }
  // #endregion

  // #region  public functions

  /** Based on *val* parameter shows or hides bookmarks. */
  show(val) {
    if (!this._app.pdfDocument) {
      val = false;
    }

    if (this.allBookmarks.length === 0) {
      val = false;
    }

    /*
    if (this._isBookmarksOpened === val) {
      return;
    }
    */

    this._isBookmarksOpened = val;

    if (!val) {
      this._showMobile(val);
      this._showDesktop(val);
    } else {
      if (!this.preserveBookmarksState) {
        if (this.bookmarksFullyExpanded) {
          this._expandAll();
        }
      }

      if (this._app.isMobile) {
        this._showMobile(val);
        this._showDesktop(false);
      } else {
        this._showDesktop(val);
        this._showMobile(false);
      }
    }
    tw_focus(this._app.elements.tw2018elem_bookmarkBtn, val);
  }

  /**
   * Resets bookmarks and its GUI elements.
   */
  reset() {
    this.allBookmarks = [];
    this._isBookmarksCompletelyOpen = false;
    this._isBookmarksOpened = false;

    if (!this._app.isMobile) {
      this._bookmarksDiv.innerHTML =
        "<iframe class='tw2018elem_bookmarksDivFrame' data-tw2018-pdf-viewer='' allowtransparency='true'></iframe>";
      this._app.elements.tw2018elem_bookmarksDivFrame =
        this._bookmarksDiv.children[0];
    }
  }

  /**
   * Read all bookmarks (if they exist) from the PDF document and fill its value in related GUI elements.
   */
  loadBookmarks() {
    this.allBookmarks = [];
    this._addIFrameResizeHandlerIfNeeded();

    const me = this;
    me._app.viewer.pdfDoc.getOutline().then(function (destinations) {
      if (
        !destinations ||
        destinations.length === 0 ||
        destinations.length === 1
      ) {
        me._app.elements.tw2018elem_bookmarkBtn.disabled = true;
        tw_focus(me._app.elements.tw2018elem_bookmarkBtn, false);

        if (!me._app.isMobile) {
          tw_hide(me._bookmarksDiv);
        } else {
          tw_hide(me._app.elements.tw2018elem_bookmarkPopupOverlay);
        }
        me._app.elements.tw2018elem_viewerContainer.style.left = "0";
      } else {
        me._app.elements.tw2018elem_bookmarkBtn.disabled = false;

        for (let i = 0; i < destinations.length; i++) {
          me._addBookmark(
            destinations[i],
            me._app.elements.tw2018elem_bookmarksDivMobile
          );
        }

        for (let i = 0; i < destinations.length; i++) {
          me._addBookmark(
            destinations[i],
            me._app.elements.tw2018elem_bookmarksDivDesktop
          );
        }

        // me.isBookmarksCompletelyOpen = me.showBookmarksCompletelyOnOpen;
        me._isBookmarksOpened = me.showBookmarksOnOpen;
        me.show(me._isBookmarksOpened);
      }
    });
  }
}
// #endregion

export { BookmarkLayer };