/* Copyright Notice
 * bootstrap5-toggle v5.0.6
 * https://palcarazm.github.io/bootstrap5-toggle/
 * @author 2011-2014 Min Hur (https://github.com/minhur)
 * @author 2018-2019 Brent Ely (https://github.com/gitbrent)
 * @author 2022 Pablo Alcaraz Martínez (https://github.com/palcarazm)
 * @funding GitHub Sponsors
 * @see https://github.com/sponsors/palcarazm
 * @license MIT
 * @see https://github.com/palcarazm/bootstrap5-toggle/blob/master/LICENSE
 */


+(function ($) {
  "use strict";

  // TOGGLE PUBLIC CLASS DEFINITION
  // ==============================

  let Toggle = function (element, options) {
    // A: Capture ref to HMTL element
    this.$element = $(element);

    // B: Set options
    this.options = $.extend({}, this.defaults(), options);

    // C: Check deprecations
    if (this.options.onlabel === Toggle.DEPRECATION.value) {
      if (this.$element.attr("data-on")) {
        Toggle.DEPRECATION.log(
          Toggle.DEPRECATION.ATTRIBUTE,
          "data-on",
          "data-onlabel"
        );
        this.options.onlabel = this.$element.attr("data-on");
      } else if (options.on) {
        Toggle.DEPRECATION.log(Toggle.DEPRECATION.OPTION, "on", "onlabel");
        this.options.onlabel = options.on;
      } else {
        this.options.onlabel = Toggle.DEFAULTS.onlabel;
      }
    }
    if (this.options.offlabel === Toggle.DEPRECATION.value) {
      if (this.$element.attr("data-off")) {
        Toggle.DEPRECATION.log(
          Toggle.DEPRECATION.ATTRIBUTE,
          "data-off",
          "data-offlabel"
        );
        this.options.offlabel = this.$element.attr("data-off");
      } else if (options.off) {
        Toggle.DEPRECATION.log(Toggle.DEPRECATION.OPTION, "off", "offlabel");
        this.options.offlabel = options.off;
      } else {
        this.options.offlabel = Toggle.DEFAULTS.offlabel;
      }
    }

    // LAST: Render Toggle
    this.render();
  };

  Toggle.DEPRECATION = {
    value:
      "BOOTSTRAP TOGGLE DEPRECATION CHECK -- a0Jhux0QySypjjs4tLtEo8xT2kx0AbYaq9K6mgNjWSs0HF0L8T8J0M0o3Kr7zkm7 --",
    ATTRIBUTE: "attribute",
    OPTION: "option",
    log: function (type, oldlabel, newlabel) {
      console.warn(
        `Bootstrap Toggle deprecation warning: Using ${oldlabel} ${type} is deprected. Use ${newlabel} instead.`
      );
    },
  };

  Toggle.DEFAULTS = {
    onlabel: "On",
    offlabel: "Off",
    onstyle: "primary",
    offstyle: "secondary",
    onvalue: null,
    offvalue: null,
    ontitle: null,
    offtitle: null,
    size: "normal",
    style: "",
    width: null,
    height: null,
    tabindex: 0,
    tristate: false,
    name: null,
  };

  Toggle.prototype.defaults = function () {
    return {
      onlabel:
        this.$element.attr("data-onlabel") ||
        Toggle.DEPRECATION.value ||
        Toggle.DEFAULTS.onlabel,
      offlabel:
        this.$element.attr("data-offlabel") ||
        Toggle.DEPRECATION.value ||
        Toggle.DEFAULTS.offlabel,
      onstyle: this.$element.attr("data-onstyle") || Toggle.DEFAULTS.onstyle,
      offstyle: this.$element.attr("data-offstyle") || Toggle.DEFAULTS.offstyle,
      onvalue:
        this.$element.attr("value") ||
        this.$element.attr("data-onvalue") ||
        Toggle.DEFAULTS.onvalue,
      offvalue: this.$element.attr("data-offvalue") || Toggle.DEFAULTS.offvalue,
      ontitle:
        this.$element.attr("data-ontitle") ||
        this.$element.attr("title") ||
        Toggle.DEFAULTS.ontitle,
      offtitle:
        this.$element.attr("data-offtitle") ||
        this.$element.attr("title") ||
        Toggle.DEFAULTS.offtitle,
      size: this.$element.attr("data-size") || Toggle.DEFAULTS.size,
      style: this.$element.attr("data-style") || Toggle.DEFAULTS.style,
      width: this.$element.attr("data-width") || Toggle.DEFAULTS.width,
      height: this.$element.attr("data-height") || Toggle.DEFAULTS.height,
      tabindex: this.$element.attr("tabindex") || Toggle.DEFAULTS.tabindex,
      tristate: this.$element.is("[tristate]") || Toggle.DEFAULTS.tristate,
      name: this.$element.attr("name") || Toggle.DEFAULTS.name,
    };
  };

  Toggle.prototype.render = function () {
    // 0: Parse size
    let size;
    switch (this.options.size) {
      case "large":
      case "lg":
        size = "btn-lg";
        break;
      case "small":
      case "sm":
        size = "btn-sm";
        break;
      case "mini":
      case "xs":
        size = "btn-xs";
        break;
      default:
        size = "";
        break;
    }

    // 1: On
    let $toggleOn = $('<span class="btn">')
      .html(this.options.onlabel)
      .addClass("btn-" + this.options.onstyle + " " + size);
    if (this.options.ontitle) {
      $toggleOn.attr("title", this.options.ontitle);
    }

    // 2: Off
    let $toggleOff = $('<span class="btn">')
      .html(this.options.offlabel)
      .addClass("btn-" + this.options.offstyle + " " + size);
    if (this.options.offtitle) {
      $toggleOff.attr("title", this.options.offtitle);
    }

    // 3: Handle
    let $toggleHandle = $('<span class="toggle-handle btn">').addClass(size);

    // 4: Toggle Group
    let $toggleGroup = $('<div class="toggle-group">').append(
      $toggleOn,
      $toggleOff,
      $toggleHandle
    );

    // 5: Toggle
    let $toggle = $(
      '<div class="toggle btn" data-toggle="toggle" role="button">'
    )
      .addClass(
        this.$element.prop("checked")
          ? "btn-" + this.options.onstyle
          : "btn-" + this.options.offstyle + " off"
      )
      .addClass(size)
      .addClass(this.options.style)
      .attr("tabindex", this.options.tabindex);
    if (this.$element.prop("disabled") || this.$element.prop("readonly")) {
      $toggle.addClass("disabled");
      $toggle.attr("disabled", "disabled");
    }

    // 6: Set form values
    if (this.options.onvalue) this.$element.val(this.options.onvalue);
    let $invElement = null;
    if (this.options.offvalue) {
      $invElement = this.$element.clone();
      $invElement.val(this.options.offvalue);
      $invElement.attr("data-toggle", "invert-toggle");
      $invElement.removeAttr("id");
      $invElement.prop("checked", !this.$element.prop("checked"));
    }

    // 7: Replace HTML checkbox with Toggle-Button
    this.$element.wrap($toggle);
    $.extend(this, {
      $toggle: this.$element.parent(),
      $toggleOn: $toggleOn,
      $toggleOff: $toggleOff,
      $toggleGroup: $toggleGroup,
      $invElement: $invElement,
    });
    this.$toggle.append($invElement, $toggleGroup);

    // 8: Set button W/H, lineHeight
    {
      // A: Set style W/H
      if (this.options.width) {
        this.$toggle.css("width", this.options.width);
      } else {
        this.$toggle.css("min-width", "100px"); // First approach for better calculation
        this.$toggle.css(
          "min-width",
          `${
            Math.max($toggleOn.outerWidth(), $toggleOff.outerWidth()) +
            $toggleHandle.outerWidth() / 2
          }px`
        );
      }

      if (this.options.height) {
        this.$toggle.css("height", this.options.height);
      } else {
        this.$toggle.css("min-height", "36px"); // First approach for better calculation
        this.$toggle.css(
          "min-height",
          `${Math.max($toggleOn.outerHeight(), $toggleOff.outerHeight())}px`
        );
      }

      // B: Apply on/off class
      $toggleOn.addClass("toggle-on");
      $toggleOff.addClass("toggle-off");

      // C: Finally, set lineHeight if needed
      if (this.options.height) {
        $toggleOn.css("line-height", $toggleOn.height() + "px");
        $toggleOff.css("line-height", $toggleOff.height() + "px");
      }
    }

    // 9: Add listeners
    this.$toggle.on("touchstart", (e) => {
      toggleActionPerformed(e, this);
    });
    this.$toggle.on("click", (e) => {
      toggleActionPerformed(e, this);
    });
    this.$toggle.on("keypress", (e) => {
      if (e.key == " ") {
        toggleActionPerformed(e, this);
      }
    });

    if (this.$element.prop("id")) {
      $('label[for="' + this.$element.prop("id") + '"]').on(
        "touchstart click",
        (_e) => {
          this.toggle();
          this.$toggle.focus();
        }
      );
    }

    // 10: Set elements to bootstrap object (NOT NEEDED)
    // 11: Keep reference to this instance for subsequent calls via `getElementById().bootstrapToggle()` (NOT NEEDED)
  };

  /**
   * Trigger actions
   * @param {Event} e event
   * @param {Toggle} target Toggle
   */
  function toggleActionPerformed(e, target) {
    if (target.options.tristate) {
      if (target.$toggle.hasClass("indeterminate")) {
        target.determinate(true);
        target.toggle();
      } else {
        target.indeterminate();
      }
    } else {
      target.toggle();
    }
    e.preventDefault();
  }

  Toggle.prototype.toggle = function (silent = false) {
    if (this.$element.prop("checked")) this.off(silent);
    else this.on(silent);
  };

  Toggle.prototype.on = function (silent = false) {
    if (this.$element.prop("disabled") || this.$element.prop("readonly"))
      return false;
    this.$toggle
      .removeClass("btn-" + this.options.offstyle + " off")
      .addClass("btn-" + this.options.onstyle);
    this.$element.prop("checked", true);
    if (this.$invElement) this.$invElement.prop("checked", false);
    if (!silent) this.trigger();
  };

  Toggle.prototype.off = function (silent = false) {
    if (this.$element.prop("disabled") || this.$element.prop("readonly"))
      return false;
    this.$toggle
      .removeClass("btn-" + this.options.onstyle)
      .addClass("btn-" + this.options.offstyle + " off");
    this.$element.prop("checked", false);
    if (this.$invElement) this.$invElement.prop("checked", true);
    if (!silent) this.trigger();
  };

  Toggle.prototype.indeterminate = function (silent = false) {
    if (
      !this.options.tristate ||
      this.$element.prop("disabled") ||
      this.$element.prop("readonly")
    )
      return false;
    this.$toggle.addClass("indeterminate");
    this.$element.prop("indeterminate", true);
    this.$element.removeAttr("name");
    if (this.$invElement) this.$invElement.prop("indeterminate", true);
    if (this.$invElement) this.$invElement.removeAttr("name");
    if (!silent) this.trigger();
  };

  Toggle.prototype.determinate = function (silent = false) {
    if (
      !this.options.tristate ||
      this.$element.prop("disabled") ||
      this.$element.prop("readonly")
    )
      return false;
    this.$toggle.removeClass("indeterminate");
    this.$element.prop("indeterminate", false);
    if (this.options.name) this.$element.attr("name", this.options.name);
    if (this.$invElement) this.$invElement.prop("indeterminate", false);
    if (this.$invElement && this.options.name)
      this.$invElement.attr("name", this.options.name);
    if (!silent) this.trigger();
  };

  Toggle.prototype.enable = function () {
    this.$toggle.removeClass("disabled");
    this.$toggle.removeAttr("disabled");
    this.$element.prop("disabled", false);
    this.$element.prop("readonly", false);
    if (this.$invElement) {
      this.$invElement.prop("disabled", false);
      this.$invElement.prop("readonly", false);
    }
  };

  Toggle.prototype.disable = function () {
    this.$toggle.addClass("disabled");
    this.$toggle.attr("disabled", "disabled");
    this.$element.prop("disabled", true);
    this.$element.prop("readonly", false);
    if (this.$invElement) {
      this.$invElement.prop("disabled", true);
      this.$invElement.prop("readonly", false);
    }
  };

  Toggle.prototype.readonly = function () {
    this.$toggle.addClass("disabled");
    this.$toggle.attr("disabled", "disabled");
    this.$element.prop("disabled", false);
    this.$element.prop("readonly", true);
    if (this.$invElement) {
      this.$invElement.prop("disabled", false);
      this.$invElement.prop("readonly", true);
    }
  };

  Toggle.prototype.update = function (silent) {
    if (this.$element.prop("disabled")) this.disable();
    else if (this.$element.prop("readonly")) this.readonly();
    else this.enable();
    if (this.$element.prop("checked")) this.on(silent);
    else this.off(silent);
  };

  Toggle.prototype.trigger = function (silent) {
    this.$element.off("change.bs.toggle");
    if (!silent) this.$element.change();
    this.$element.on(
      "change.bs.toggle",
      $.proxy(function () {
        this.update();
      }, this)
    );
  };

  Toggle.prototype.destroy = function () {
    // A: Remove button-group from UI, replace checkbox element
    this.$element.off("change.bs.toggle");
    this.$toggleGroup.remove();
    if (this.$invElement) this.$invElement.remove();

    // B: Delete internal refs
    this.$element.removeData("bs.toggle");
    this.$element.unwrap();
  };

  // TOGGLE PLUGIN DEFINITION
  // ========================

  function Plugin(option) {
    let optArg = Array.prototype.slice.call(arguments, 1)[0];

    return this.each(function () {
      let $this = $(this);
      let data = $this.data("bs.toggle");
      let options = typeof option == "object" && option;

      if (!data) {
        data = new Toggle(this, options);
        $this.data("bs.toggle", data);
      }
      if (
        typeof option === "string" &&
        data[option] &&
        typeof optArg === "boolean"
      )
        data[option](optArg);
      else if (typeof option === "string" && data[option]) data[option]();
      //else if (option && !data[option]) console.log('bootstrap-toggle: error: method `'+ option +'` does not exist!');
    });
  }

  let old = $.fn.bootstrapToggle;

  $.fn.bootstrapToggle = Plugin;
  $.fn.bootstrapToggle.Constructor = Toggle;

  // TOGGLE NO CONFLICT
  // ==================

  $.fn.toggle.noConflict = function () {
    $.fn.bootstrapToggle = old;
    return this;
  };

  /**
   * Replace all `input[type=checkbox][data-toggle="toggle"]` inputs with "Bootstrap-Toggle"
   * Executes once page elements have rendered enabling script to be placed in `<head>`
   */
  $(function () {
    $("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle();
  });
})(jQuery);
