(function(cash) {
  // Show or hide global event listener
  let events = [];

  // Get highest z-index
  function getHighestZindex() {
    let zIndex = 52;
    cash(".modal").each(function() {
      if (
        cash(this).css("z-index") !== "auto" &&
        cash(this).css("z-index") > zIndex
      ) {
        zIndex = parseInt(cash(this).css("z-index"));
      }
    });

    return zIndex;
  }

  // Get scrollbar width
  function getScrollbarWidth(el) {
    return window.innerWidth - cash(el)[0].clientWidth;
  }

  // Show modal with z-index
  function show(el) {
    // Move modal element to body
    cash(
      '<div data-modal-replacer="' + cash(el).attr("id") + '"></div>'
    ).insertAfter(el);
    cash(el).css({
      "margin-top": 0,
      "margin-left": 0
    });
    cash(el).appendTo("body");

    // Show modal by highest z-index
    setTimeout(() => {
      cash(el)
        .addClass("show")
        .css("z-index", getHighestZindex() + 1);
    }, 200);

    // Setting up modal scroll
    cash("body")
      .css(
        "padding-right",
        parseInt(cash("body").css("padding-right")) +
          getScrollbarWidth("html") +
          "px"
      )
      .addClass("overflow-y-hidden");
    cash(".modal")
      .removeClass("overflow-y-auto")
      .css("padding-left", "0px");
    cash(el)
      .addClass("overflow-y-auto")
      .css("padding-left", getScrollbarWidth(el) + "px")
      .addClass(cash(".modal.show").length ? "modal__overlap" : "");

    // Trigger callback function
    events.forEach(function(val, key) {
      if (
        cash(el).attr("id") == cash(val.el).attr("id") &&
        val.event == "on.show"
      ) {
        events[key].triggerCallback = true;
      }
    });
  }

  // Hide modal & remove modal scroll
  function hide(el) {
    if (cash(el).hasClass("modal") && cash(el).hasClass("show")) {
      cash(el).removeClass("show");

      setTimeout(() => {
        cash(el)
          .removeAttr("style")
          .removeClass("modal__overlap")
          .removeClass("overflow-y-auto");

        // Add scroll to highest z-index modal if exist
        cash(".modal").each(function() {
          if (parseInt(cash(this).css("z-index")) === getHighestZindex()) {
            cash(this)
              .addClass("overflow-y-auto")
              .css("padding-left", getScrollbarWidth(this) + "px");
          }
        });

        // Return back scroll to body if no more modal showed up
        if (getHighestZindex() == 52) {
          cash("body")
            .removeClass("overflow-y-hidden")
            .css("padding-right", "");
        }

        // Return back modal element to it's first place
        cash('[data-modal-replacer="' + cash(el).attr("id") + '"]').replaceWith(
          el
        );
      }, 200);

      // Trigger callback function
      events.forEach(function(val, key) {
        if (
          cash(el).attr("id") == cash(val.el).attr("id") &&
          val.event == "on.hide"
        ) {
          events[key].triggerCallback = true;
        }
      });
    }
  }

  // Toggle modal
  function toggle(el) {
    if (cash(el).hasClass("modal") && cash(el).hasClass("show")) {
      hide(el);
    } else {
      show(el);
    }
  }

  // On show
  function onShow(el, callback) {
    events[events.length] = {
      el: el,
      event: "on.show",
      triggerCallback: false,
      callback: callback
    };
  }

  // On hide
  function onHide(el, callback) {
    events[events.length] = {
      el: el,
      event: "on.hide",
      triggerCallback: false,
      callback: callback
    };
  }

  // Show modal
  cash("body").on("click", 'a[data-toggle="modal"]', function() {
    show(cash(this).attr("data-target"));
  });

  // Hide modal
  cash("body").on("click", function(event) {
    hide(event.target);
  });

  // Dismiss modal by link
  cash("body").on("click", '[data-dismiss="modal"]', function() {
    let modal = cash(this).closest(".modal")[0];
    hide(modal);
  });

  // Detect show or hide event
  setInterval(function() {
    events.forEach(function(val, key) {
      if (val.event == "on.show" && val.triggerCallback) {
        val.callback();
        events[key].triggerCallback = false;
      } else if (val.event == "on.hide" && val.triggerCallback) {
        val.callback();
        events[key].triggerCallback = false;
      }
    });
  }, 300);

  cash.fn.modal = function(event, callback) {
    if (event == "show") {
      show(this);
    } else if (event == "hide") {
      hide(this);
    } else if (event == "toggle") {
      toggle(this);
    } else if (event == "on.show") {
      onShow(this, callback);
    } else if (event == "on.hide") {
      onHide(this, callback);
    }
  };
})(cash);
