Source: src/js/views/maps/LayerDetailView.js

"use strict";

define([
  "jquery",
  "underscore",
  "backbone",
  "models/maps/assets/MapAsset",
  "text!templates/maps/layer-detail.html",
], function ($, _, Backbone, MapAsset, Template) {
  /**
   * @class LayerDetailView
   * @classdesc A LayerDetailView creates a section to be inserted into a
   * LayerDetailsView. It renders a label and a toggle to collapse and expand the
   * section's contents. The contents of the Detail section can be rendered by any other
   * view, but the view should be one that shows details about a MapAsset or allows
   * editing elements of the MapAsset.
   * @classcategory Views/Maps
   * @name LayerDetailView
   * @extends Backbone.View
   * @screenshot views/maps/LayerDetailView.png
   * @since 2.18.0
   * @constructs
   */
  var LayerDetailView = Backbone.View.extend(
    /** @lends LayerDetailView.prototype */ {
      /**
       * The type of View this is
       * @type {string}
       */
      type: "LayerDetailView",

      /**
       * The HTML classes to use for this view's element
       * @type {string}
       */
      className: "layer-detail",

      /**
       * The model that this view uses
       * @type {MapAsset}
       */
      model: undefined,

      /**
       * The primary HTML template for this view
       * @type {Underscore.template}
       */
      template: _.template(Template),

      /**
       * CSS classes for HTML elements within this view.
       * @property {string} toggle The element in the template that acts as a toggle to
       * expand or collapse this Layer Detail section.
       * @property {string} open The class to add to the view when the contents are
       * visible (i.e. the section is expanded)
       * @property {string} noHeader The class to add to the view when there is no
       * title/label and the view is not collapsible.
       * @property {string} label The element that holds the view's label text
       * @property {string} contentContainer The container into which the contentView's
       * rendered content will be placed
       */
      classes: {
        open: "layer-detail--open",
        noHeader: "layer-detail--no-header",
        label: "layer-detail__label",
        toggle: "layer-detail__toggle",
        contentContainer: "layer-detail__content",
      },

      /**
       * Indicates whether this section is collapsed or expanded
       * @type {Boolean}
       */
      isOpen: true,

      /**
       * The name to display for the Layer Detail section
       * @type {string}
       */
      label: null,

      /**
       * The sub-view that will show details about, or allow editing of, the given Layer
       * model. The contentView will be passed the Layer model.
       * @type {Backbone.View}
       */
      contentView: null,

      /**
       * Creates an object that gives the events this view will listen to and the
       * associated function to call. Each entry in the object has the format 'event
       * selector': 'function'.
       * @returns {Object}
       */
      events: function () {
        var events = {};
        // Collapse or expand this Detail section when the toggle button is clicked. Get
        // the class of the toggle button from the classes property set in this view.
        events["click ." + this.classes.toggle] = "toggle";
        return events;
      },

      /**
       * Executed when a new LayerDetailView is created
       * @param {Object} [options] - A literal object with options to pass to the view
       */
      initialize: function (options) {
        try {
          // Get all the options and apply them to this view
          if (typeof options == "object") {
            for (const [key, value] of Object.entries(options)) {
              this[key] = value;
            }
          }
        } catch (e) {
          console.log(
            "A LayerDetailView failed to initialize. Error message: " + e,
          );
        }
      },

      /**
       * Renders this view
       * @return {LayerDetailView} Returns the rendered view element
       */
      render: function () {
        try {
          // Save a reference to this view
          var view = this;

          // Ensure the view's main element has the given class name
          this.el.classList.add(this.className);

          // Display the section's contents depending on the view's initial setting
          if (this.isOpen) {
            this.el.classList.add(this.classes.open);
          }

          // Insert the template into the view
          this.$el.html(
            this.template({
              label: this.label,
              collapsible: this.collapsible,
              showTitle: this.showTitle,
            }),
          );

          // Render the content for this Layer Detail section
          if (this.contentView) {
            var contentContainer = this.el.querySelector(
              "." + this.classes.contentContainer,
            );
            this.renderedContentView = new this.contentView({
              model: this.model,
            });
            contentContainer.append(this.renderedContentView.el);
            this.renderedContentView.render();
          }

          if (!this.collapsible && !this.showTitle) {
            this.el.classList.add(this.classes.noHeader);
          }

          return this;
        } catch (error) {
          console.log(
            "There was an error rendering a LayerDetailView" +
              ". Error details: " +
              error,
          );
        }
      },

      /**
       * Show or hide this section's contents by adding or removing the open class.
       */
      toggle: function () {
        try {
          this.el.classList.toggle(this.classes.open);
          if (this.isOpen) {
            this.isOpen = false;
          } else {
            this.isOpen = true;
          }
        } catch (error) {
          console.log(
            "There was an error toggling a LayerDetailView" +
              ". Error details: " +
              error,
          );
        }
      },

      /**
       * Perform clean-up functions when this view is about to be removed from the page
       * or navigated away from.
       */
      onClose: function () {
        try {
          if (
            this.renderedContentView &&
            typeof this.renderedContentView.onClose === "function"
          ) {
            this.renderedContentView.onClose();
          }
        } catch (error) {
          console.log(
            "There was an error performing clean up functions in a LayerDetailView" +
              ". Error details: " +
              error,
          );
        }
      },
    },
  );

  return LayerDetailView;
});