Source: src/js/views/VersionNavigationView.js

define(["jquery", "backbone", "models/sysmeta/VersionTracker", "semantic"], (
  $,
  Backbone,
  VersionTracker,
  Semantic,
) => {
  "use strict";

  const CLASS_NAMES = {
    BASE: "version-navigation-view",
    PREV_VERSION_CONTAINER: "prev-version-container",
    NEXT_VERSION_CONTAINER: "next-version-container",
    ALL_VERSIONS_LINK_CONTAINER: "all-versions-link-container",

    // Bootstrap
    BUTTON: "btn",
    BUTTON_LINK: "btn-link",
    BUTTON_LINK_NEUTRAL: "btn-link--neutral",
    BUTTON_MINI: "btn-mini",
  };

  const BUTTON_CLASSES = `${CLASS_NAMES.BUTTON} ${CLASS_NAMES.BUTTON_LINK} ${CLASS_NAMES.BUTTON_LINK_NEUTRAL}`;

  const DEFAULT_DOC_TYPE = "document";

  const GET_DEFAULT_DESCRIPTION = (type) => {
    const docType = type || DEFAULT_DOC_TYPE;
    return {
      prev: `View the previous version of this ${docType}`,
      next: `View the next version of this ${docType}`,
      all: `Browse all versions of this ${docType}`,
    };
  };

  /**
   * @class VersionNavigationView
   * @classdesc A view showing buttons that lead to the previous and next
   * versions of a metadata document, as well as a link to show all versions if
   * configured.
   * @since 2.36.0
   * @classcategory Views
   * @augments Backbone.View
   * @class
   * @screenshot views/VersionNavigationView.png
   */
  const VersionNavigationView = Backbone.View.extend(
    /** @lends MetadataView.prototype */ {
      /**
       * The type of the view
       * @type {string}
       */
      type: "VersionNavigationView",

      /** @inheritdoc */
      tagName: "span",

      /** @inheritdoc */
      className: CLASS_NAMES.BASE,

      /**
       * Settings for the tooltips
       * @type {object}
       * @see https://fomantic-ui.com/modules/popup.html#/settings
       */
      tooltipSettings: {
        position: "top center",
        variation: `${Semantic.CLASS_NAMES.variations.inverted} ${Semantic.CLASS_NAMES.variations.mini}`,
        delay: {
          show: 400,
          hide: 20,
        },
        exclusive: true,
      },

      /**
       * Descriptions for the tooltips
       * @type {object}
       */
      descriptions: GET_DEFAULT_DESCRIPTION(),

      /**
       * Template for the view
       * @returns {string} The HTML string for the view
       */
      template() {
        const CN = CLASS_NAMES;

        return `
            <span class="${CN.PREV_VERSION_CONTAINER}"></span>
            <span class="${CN.ALL_VERSIONS_LINK_CONTAINER}"></span>
            <span class="${CN.NEXT_VERSION_CONTAINER}"></span>
        `;
      },

      /**
       * Template for the Previous and Next version buttons
       * @param {string} url The URL for the button
       * @param {"next"|"prev"} direction The direction of the button
       * @returns {string} The HTML string for the button
       */
      prevNextBtnTemplate(url, direction) {
        if (!url) return "";
        const text = direction === "next" ? "Next" : "Previous";
        const icon = direction === "next" ? "right" : "left";
        const iconHTML = `<span class="icon-arrow-${icon}" aria-hidden="true"></span>`;

        return `
          <a href=${url} class="${BUTTON_CLASSES}" title="${text} Version">
            ${iconHTML}
          </a>
        `;
      },

      /**
       * Template for the All Versions link
       * @param {string} url The URL for the link
       * @returns {string} The HTML string for the link
       */
      allVersionsTemplate(url) {
        const extraStyles = url ? "" : " pointer-events: none; opacity: 0.8;";
        return `
          <a href="${url}" class="${BUTTON_CLASSES}" title="View All Versions" style="${extraStyles}">
            Version History
          </a>
        `;
      },

      /**
       * Initialize the VersionNavigationView
       * @param {object} options Object containing the view's options
       * @param {pid} options.pid The PID of the metadata document
       * @param {boolean} [options.showAllVersionsLink] Whether to show a link
       * to view all versions of the document
       * @param {object} [options.descriptions] An object containing custom
       * descriptions for the tooltips
       * @param {string} [options.documentType] The type of document (e.g.
       * dataset, article, etc.) to use in the default tooltip descriptions
       */
      initialize(options = {}) {
        this.pid = options.pid;
        this.showAllVersionsLink = options.showAllVersionsLink !== false;
        this.versionTracker = VersionTracker.get();
        if (options.descriptions) {
          this.descriptions = { ...this.descriptions, ...options.descriptions };
        } else if (options.documentType) {
          this.descriptions = GET_DEFAULT_DESCRIPTION(options.documentType);
        }
      },

      /**
       * Convenience method to select important containers in the view
       * @returns {object} An object containing references to the containers for
       * the previous & next button containers and the all versions link
       * container.
       */
      selectContainers() {
        this.$prevVersionContainer = this.el.querySelector(
          `.${CLASS_NAMES.PREV_VERSION_CONTAINER}`,
        );
        this.$nextVersionContainer = this.el.querySelector(
          `.${CLASS_NAMES.NEXT_VERSION_CONTAINER}`,
        );
        this.$allVersionsLinkContainer = this.el.querySelector(
          `.${CLASS_NAMES.ALL_VERSIONS_LINK_CONTAINER}`,
        );
        return {
          prev: this.$prevVersionContainer,
          next: this.$nextVersionContainer,
          allVersionsLink: this.$allVersionsLinkContainer,
        };
      },

      /** @inheritdoc */
      async render() {
        if (!this.pid) return this;

        this.el.innerHTML = this.template();
        const { prev, next, allVersionsLink } = this.selectContainers();
        const versions = await this.versionTracker.getAdjacent(this.pid, true);

        if (versions.next) {
          const nextUrl = this.getViewUrl(versions.next);
          next.innerHTML = this.prevNextBtnTemplate(nextUrl, "next");
          this.addTooltip(next, this.descriptions.next);
        }
        if (versions.prev) {
          const prevUrl = this.getViewUrl(versions.prev);
          prev.innerHTML = this.prevNextBtnTemplate(prevUrl, "prev");
          this.addTooltip(prev, this.descriptions.prev);
        }
        if (this.showAllVersionsLink && (versions.prev || versions.next)) {
          const allUrl = this.getAllVersionsUrl(this.pid);
          allVersionsLink.innerHTML = this.allVersionsTemplate(allUrl);
          this.addTooltip(allVersionsLink, this.descriptions.allVersions);
        }

        return this;
      },

      /**
       * Add a tooltip to an element
       * @param {HTMLElement} element The element to add the tooltip to
       * @param {string} content The content of the tooltip
       */
      addTooltip(element, content) {
        if (!element || !content) return;
        $(element)
          .find("a")
          .popup({
            content,
            ...this.tooltipSettings,
          });
      },

      /**
       * Get the URL to view a metadata document by its PID
       * @param {string} pid The PID of the metadata document
       * @returns {string} The URL to view the metadata document
       */
      getViewUrl(pid) {
        if (!pid) return "";
        return `${MetacatUI.root}/view/${encodeURIComponent(pid)}`;
      },

      // TODO: Implement version history page
      getAllVersionsUrl(_pid) {
        return "";
        // if (!pid) return "";
        // return `${MetacatUI.root}/versionHistory/${encodeURIComponent(pid)}`;
      },

      /** Clean up tasks before the view is closed */
      onClose() {
        this.$el.find(".popup").popup("destroy");
      },

      /** @inheritdoc */
      remove() {
        this.onClose();
        return Backbone.View.prototype.remove.call(this);
      },
    },
  );

  return VersionNavigationView;
});