Source: src/js/views/portals/editor/PortEditorSettingsView.js

define([
  "underscore",
  "jquery",
  "backbone",
  "models/portals/PortalSectionModel",
  "views/portals/editor/PortEditorSectionView",
  "views/portals/editor/PortEditorLogosView",
  "text!templates/portals/editor/portEditorSettings.html",
], function (
  _,
  $,
  Backbone,
  PortalSection,
  PortEditorSectionView,
  PortEditorLogosView,
  Template,
) {
  /**
   * @class PortEditorSettingsView
   * @classcategory Views/Portals/Editor
   * @extends PortEditorSectionView
   */
  var PortEditorSettingsView = PortEditorSectionView.extend(
    /** @lends PortEditorSettingsView.prototype */ {
      /**
       * The type of View this is
       * @type {string}
       */
      type: "PortEditorSettings",

      /**
       * The display name for this Section
       * @type {string}
       */
      uniqueSectionLabel: "Settings",

      /**
       * The type of section view this is
       * @type {string}
       */
      sectionType: "settings",

      /**
       * The HTML classes to use for this view's element
       * @type {string}
       */
      className:
        PortEditorSectionView.prototype.className + " port-editor-settings",

      /**
       * The id attribute of the view element
       * @param {string}
       */
      id: "Settings",

      /**
       * The PortalModel that is being edited
       * @type {Portal}
       */
      model: undefined,

      /**
       * A reference to the PortalEditorView
       * @type {PortalEditorView}
       */
      editorView: undefined,

      /**
       * References to templates for this view. HTML files are converted to Underscore.js templates
       */
      template: _.template(Template),

      /**
       * The events this view will listen to and the associated function to call.
       * @type {Object}
       */
      events: {
        "focusout .label-container input": "showLabelValidation",
        "click .change-label": "changeLabel",
        "click .cancel-change-label": "cancelChangeLabel",
        "click .ok-change-label": "okChangeLabel",
        "keyup .label-container input": "removeLabelValidation",
      },

      /**
       * Creates a new PortEditorSettingsView
       * @param {Object} options - A literal object with options to pass to the view
       */
      initialize: function (options) {
        try {
          //Call the superclass initialize() function
          PortEditorSectionView.prototype.initialize(options);
        } catch (e) {
          console.log(
            "Error initializing the portal editor settings view. Error message: " +
              e,
          );
        }
      },

      /**
       * Renders this view
       */
      render: function () {
        try {
          //Insert the template into the view
          var portalTermSingular = MetacatUI.appModel.get("portalTermSingular");
          this.$el.html(
            this.template({
              label: this.model.get("label"),
              description: this.model.get("description"),
              portalTermPlural: MetacatUI.appModel.get("portalTermPlural"),
              portalTermSingular: MetacatUI.appModel.get("portalTermSingular"),
            }),
          );

          //Render the PortEditorLogosView
          var logosView = new PortEditorLogosView({
            model: this.model,
            editorView: this.editorView,
          });
          logosView.render();
          this.$(".logos-container").html(logosView.el).data("view", logosView);

          //Save a reference to this view
          this.$el.data("view", this);

          // If it's a new model, it won't have a label (URL) yet. Display the label
          // input field so the user doesn't miss it.
          if (this.model.get("isNew")) {
            this.changeLabel();
          }
        } catch (e) {
          console.log(
            "Error rendering the portal editor settings view. Error message: " +
              e,
          );
        }
      },

      /**
       * Removes help text and css formatting that indicates error or success after label/URL validation.
       *
       *  @param {Event} e - The keyup or focusout event
       */
      removeLabelValidation: function (e) {
        try {
          var container = this.$(".label-container"),
            messageEl = $(container).find(".notification");

          // Remove input formating if there was any
          messageEl.html("");
          container.removeClass("error");
          container.removeClass("success");
          container.find(".error").removeClass("error");
          container.find(".success").removeClass("success");

          if (!this.model.get("isNew")) {
            // Ensure that the OK button is showing, may be hidden if a previous
            // attempt to change the label resulted in an error
            this.$(".ok-change-label").show();
          }
        } catch (error) {
          console.log(
            "Error removing label validation, error message: " + error,
          );
        }
      },

      /**
       * showLabelValidationError - add css formatting and hide OK button when there are errors in label validation.
       *
       * @param {Event} e - The keyup or focusout event
       */
      showLabelValidationError: function (e) {
        try {
          var container = this.$(".label-container"),
            input = container.find("input"),
            messageEl = container.find(".notification"),
            okButton = container.find(".ok-change-label");

          messageEl.addClass("error");
          input.addClass("error");
          okButton.hide();
        } catch (error) {
          console.log(
            "Error showing label validation error, message: " + error,
          );
        }
      },

      /**
       * Initiates validatation of the newly inputed label (a URL component).
       * Listens for a response from the model, then displays help text based on
       * whether the new label was valid or not.
       *
       *  @param {Event} e - The focusout event
       */
      showLabelValidation: function (e) {
        try {
          var container = this.$(".label-container"),
            input = container.find("input"),
            messageEl = container.find(".notification"),
            value = this.model.cleanXMLText(input.val()),
            model = this.model;

          //If the label is unchanged, remove the validation messaging and exit
          if (value == this.model.get("originalLabel")) {
            this.removeLabelValidation(e);
            return;
          }

          //If there is an error checking the validity, display a message
          this.listenToOnce(this.model, "errorValidatingLabel", function () {
            this.removeLabelValidation(e);
            var email = MetacatUI.appModel.get("emailContact");
            messageEl.html(
              "There was a problem checking the availablity of this URL. " +
                "Please try again or <a href='mailto:" +
                email +
                "'> contact us at " +
                email +
                "</a>.",
            );
            this.showLabelValidationError(e);
          });

          // Validate the label string
          var error = this.model.validateLabel(value);

          // If there is an error, display it and exit
          if (error) {
            this.removeLabelValidation(e);
            this.showLabelValidationError(e);
            messageEl.html(error);
            return;
          }

          // If there are no validation errors, check label availability

          // Success
          this.listenToOnce(this.model, "labelAvailable", function () {
            this.removeLabelValidation(e);
            messageEl
              .html("<i class='icon-check'></i> This URL is available")
              .addClass("success");
            // Make sure the OK button is enabled
            if (!this.model.isNew()) {
              this.$(".ok-change-label").show();
            }
          });

          // Error: label taken
          this.listenToOnce(this.model, "labelTaken", function () {
            this.removeLabelValidation(e);
            this.showLabelValidationError(e);
            messageEl.html(
              "This URL is already taken, please try something else",
            );

            //Manually add the validation error message since this check is done outside of the validate() function
            if (
              typeof this.model.validationError == "object" &&
              this.model.validationError !== null
            ) {
              this.model.validationError.label =
                "This URL is already taken, please try something else";
            } else {
              this.model.validationError = {
                label: "This URL is already taken, please try something else",
              };
            }
          });

          // Show 'checking URL' message
          messageEl.html(
            "<i class='icon-spinner icon-spin icon-large loading icon'></i> " +
              "Checking if URL is available",
          );

          // Check label availability
          this.model.checkLabelAvailability(value);
        } catch (error) {
          console.log("Error validating the label, error message: " + error);
        }
      },

      /**
       * Makes the portal label editable whenever the `change url` button is clicked
       */
      changeLabel: function () {
        try {
          //Get the label at this point in time
          this.model.set("latestLabel", this.model.get("label"));

          //Hide the label display and Change button
          this.$(".display-label, .change-label").hide();
          //Show the input and controls
          this.$(".label-container").show();

          // If the model is new, hide the Cancel and Ok buttons.
          if (this.model.get("isNew")) {
            this.$(".ok-change-label").hide();
            this.$(".cancel-change-label").hide();
          }
        } catch (e) {
          console.log("Error changing label, error message: " + e);
        }
      },

      /**
       * Cancels changing the portal label
       */
      cancelChangeLabel: function () {
        try {
          //Reset the label
          this.model.set("label", this.model.get("latestLabel"));
          this.$(".label-container input").val(this.model.get("label"));

          //Validate the label
          this.showLabelValidation();

          //Show the label display and Change button
          this.$(".display-label, .change-label").show();
          // Ensure that the OK button is showing, may be hidden if a previous
          // attempt to change the label resulted in an error
          this.$(".ok-change-label").show();
          //Hide the input and controls
          this.$(".label-container").hide();
        } catch (e) {
          console.log(
            "Error cancelling the changes to label, error message: " + e,
          );
        }
      },

      /**
       * Shows the portal label as saved
       */
      okChangeLabel: function () {
        try {
          //Show the label display and Change button
          this.$(".display-label, .change-label").show();
          //Hide the input and controls
          this.$(".label-container").hide();

          //If there is a validation error with the label, revert it back
          if (this.model.validationError && this.model.validationError.label) {
            this.model.set("label", this.model.get("latestLabel"));
            this.$(".label-container input").val(this.model.get("label"));
          } else {
            this.$(".display-label-value").text(this.model.get("label"));
          }

          //Validate the label
          this.showLabelValidation();
        } catch (e) {
          console.log(
            "Error showing the portal label as saved, error message: " + e,
          );
        }
      },
    },
  );

  return PortEditorSettingsView;
});