Source: src/js/views/filters/ToggleFilterView.js

define([
  "jquery",
  "underscore",
  "backbone",
  "models/filters/ToggleFilter",
  "views/filters/FilterView",
  "text!templates/filters/toggleFilter.html",
  "text!templates/filters/booleanFilter.html",
], function (
  $,
  _,
  Backbone,
  ToggleFilter,
  FilterView,
  Template,
  BooleanTemplate,
) {
  "use strict";

  /**
   * @class ToggleFilterView
   * @classdesc Render a view of a single ToggleFilter model
   * @classcategory Views/Filters
   * @extends FilterView
   */
  var ToggleFilterView = FilterView.extend(
    /** @lends ToggleFilterView.prototype */ {
      /**
       *  A ToggleFilter model to be rendered in this view
       * @type {ToggleFilter} */
      model: null,

      /**
       * @inheritdoc
       */
      modelClass: ToggleFilter,

      className: "filter toggle",

      template: _.template(Template),
      booleanTemplate: _.template(BooleanTemplate),

      /**
       * @inheritdoc
       */
      events: function () {
        try {
          var events = FilterView.prototype.events.call(this);
          events["click input[type='checkbox']"] = "updateModel";

          return events;
        } catch (error) {
          console.log(
            "There was an error creating the events object for a ToggleFilterView" +
              " Error details: " +
              error,
          );
        }
      },

      /**
       * @inheritdoc
       */
      render: function (templateVars = {}) {
        try {
          templateVars = _.extend(this.model.toJSON(), templateVars);
          templateVars.id = this.model.cid;

          if (!this.model.get("falseLabel")) {
            //If the value is the same as the trueValue, the checkbox should be checked
            templateVars.checked =
              this.model.get("values")[0] == this.model.get("trueValue")
                ? true
                : false;

            //Use the BooleanFilter template for toggles with only a true value
            this.$el.addClass("boolean");
            this.template = this.booleanTemplate;
          }

          // Renders the template and inserts the FilterEditorView if the mode is uiBuilder
          FilterView.prototype.render.call(this, templateVars);

          this.listenTo(this.model, "change:values", this.updateToggle);
        } catch (error) {
          console.log(
            "There was an error rendering a ToggleFilterView." +
              " Error details: " +
              error,
          );
        }
      },

      /**
       * Actions to perform after the render() function has completed and this view's
       * element is added to the webpage.
       */
      postRender: function () {
        this.setToggleWidth();
      },

      updateToggle: function () {
        //If the model is set to true
        if (
          this.model.get("values").length &&
          this.model.get("values")[0] == this.model.get("trueValue")
        ) {
          this.$("input").prop("checked", true);
        } else if (
          this.model.get("values").length &&
          this.model.get("values")[0] == this.model.get("falseValue")
        ) {
          this.$("input").prop("checked", false);
        } else if (!this.model.get("values").length) {
          this.$("input").prop("checked", false);
        }

        this.setToggleWidth();
      },

      /**
       * Gets the width of the toggle labels and sets the various CSS attributes
       * necessary for the switch to fully display each label
       */
      setToggleWidth: function () {
        //If there is no toggle element, exit now
        if (!this.$(".can-toggle-switch").length) {
          return;
        }

        //Get the padding and widths of the switch elements
        var switchPadding = 24,
          onSwitchWidth = this.$(".true-label").width(),
          offSwitchWidth = this.$(".false-label").width(),
          totalSwitchWidth =
            onSwitchWidth + offSwitchWidth + switchPadding * 2 + 2,
          isChecked = this.$("input[type='checkbox']").prop("checked");

        //Set the width on the whole view
        this.$el.width(totalSwitchWidth + "px");

        //Get the toggle switch element
        var toggleSwitch = this.$(".can-toggle-switch");

        //Add an identifier to the toggle switch element
        toggleSwitch.attr("id", "toggle-" + this.model.cid);

        //Change the width of the toggle switch
        toggleSwitch.css("flex", "0 0 " + totalSwitchWidth + "px");

        //Create CSS for the :before and :after pseudo elements, which is best done
        // by adding a style tag directly to the DOM
        if (isChecked) {
          var newCSS =
            "#" +
            "toggle-" +
            this.model.cid +
            ":before{ " +
            "transform: translate3d(" +
            (onSwitchWidth + switchPadding) +
            "px, 0, 0);" +
            "width: " +
            (offSwitchWidth + switchPadding) +
            "px ;" +
            "}" +
            "#" +
            "toggle-" +
            this.model.cid +
            ":after{ " +
            "width: " +
            (onSwitchWidth + switchPadding) +
            "px;" +
            "transform: translate3d(0px, 0, 0);" +
            "}";
        } else {
          var newCSS =
            "#" +
            "toggle-" +
            this.model.cid +
            ":before{ " +
            "width: " +
            (offSwitchWidth + switchPadding) +
            "px ;" +
            "left: 0px ;" +
            "}" +
            "#" +
            "toggle-" +
            this.model.cid +
            ":after{ " +
            "width: " +
            (onSwitchWidth + switchPadding) +
            "px;" +
            "transform: translate3d(" +
            (offSwitchWidth + switchPadding) +
            "px, 0, 0);" +
            "}";
        }

        //Get or create a style tag
        var styleTag = toggleSwitch.children("style");
        if (!styleTag.length) {
          styleTag = $(document.createElement("style"));
          toggleSwitch.append(styleTag);
        }

        //Add the CSS to the style tag
        styleTag.html(newCSS);
      },

      /**
       * Updates the value set on the ToggleFilter Model associated with this view.
       * The filter value is grabbed from the checkbox element in this view.
       *
       */
      updateModel: function () {
        //Check if the checkbox is checked
        var isChecked = this.$("input").prop("checked");

        //If the toggle is checked, then set the true toggle value on the model
        if (isChecked) {
          if (this.model.get("values")[0] !== this.model.get("trueValue")) {
            this.model.set("values", [this.model.get("trueValue")]);
          }
        }
        //If the toggle is not checked and there is no false value specified,
        // then remove the value from the model completely
        else if (!this.model.get("falseValue")) {
          if (this.model.get("values").length > 0) {
            this.model.set("values", []);
          }
        }
        //If the toggle is not checked and there is a false value specified,
        // then set the false toggle value on the model
        else {
          if (this.model.get("values")[0] !== this.model.get("falseValue")) {
            this.model.set("values", [this.model.get("falseValue")]);
          }
        }
      },
    },
  );
  return ToggleFilterView;
});