/* ==========================================================
 * bootstrapx-clickover.js
 * https://github.com/lecar-red/bootstrapx-clickover
 * version: 1.0
 * ==========================================================
 *
 * Based on work from Twitter Bootstrap and
 * from Popover library http://twitter.github.com/bootstrap/javascript.html#popover
 * from the great guys at Twitter.
 *
 * Untested with 2.1.0 but should worked with 2.0.x
 *
 * ========================================================== */
!(function ($) {
  "use strict";

  /* class definition */
  var Clickover = function (element, options) {
    // local init
    this.cinit("clickover", element, options);
  };

  Clickover.prototype = $.extend({}, $.fn.popover.Constructor.prototype, {
    constructor: Clickover,

    cinit: function (type, element, options) {
      this.attr = {};

      // choose random attrs instead of timestamp ones
      this.attr.me = (Math.random() * 10 + "").replace(/\D/g, "");
      this.attr.click_event_ns =
        "click." + this.attr.me + " touchstart." + this.attr.me;

      if (!options) options = {};

      options.trigger = "manual";

      // call parent
      this.init(type, element, options);

      // setup our own handlers
      this.$element.on(
        "click",
        this.options.selector,
        $.proxy(this.clickery, this),
      );

      // soon add click hanlder to body to close this element
      // will need custom handler inside here
    },
    clickery: function (e) {
      // clickery isn't only run by event handlers can be called by timeout or manually
      // only run our click handler and
      // need to stop progration or body click handler would fire right away
      if (e) {
        e.preventDefault();
        e.stopPropagation();
      }

      // set popover's dim's
      this.options.width && this.tip().width(this.options.width);
      this.options.height && this.tip().height(this.options.height);

      // set popover's tip 'id' for greater control of rendering or css rules
      this.options.tip_id && this.tip().attr("id", this.options.tip_id);

      // add a custom class
      this.options.class_name && this.tip().addClass(this.options.class_name);

      // we could override this to provide show and hide hooks
      this[this.isShown() ? "hide" : "show"]();

      // if shown add global click closer
      if (this.isShown()) {
        var that = this;

        // close on global request, exclude clicks inside clickover
        this.options.global_close &&
          $("body").on(this.attr.click_event_ns, function (e) {
            if (!that.tip().has(e.target).length) {
              that.clickery();
            }
          });

        this.options.esc_close &&
          $(document).bind("keyup.clickery", function (e) {
            if (e.keyCode == 27) {
              that.clickery();
            }
            return;
          });

        // first check for others that might be open
        // wanted to use 'click' but might accidently trigger other custom click handlers
        // on clickover elements
        !this.options.allow_multiple &&
          $("[data-clickover-open=1]").each(function () {
            $(this).data("clickover") && $(this).data("clickover").clickery();
          });

        // help us track elements w/ open clickovers using html5
        this.$element.attr("data-clickover-open", 1);

        // if element has close button then make that work, like to
        // add option close_selector
        this.tip().on(
          "click",
          '[data-dismiss="clickover"]',
          $.proxy(this.clickery, this),
        );

        // trigger timeout hide
        if (this.options.auto_close && this.options.auto_close > 0) {
          this.attr.tid = setTimeout(
            $.proxy(this.clickery, this),
            this.options.auto_close,
          );
        }

        // provide callback hooks for post shown event
        typeof this.options.onShown == "function" &&
          this.options.onShown.call(this);
        this.$element.trigger("shown");
      } else {
        this.$element.removeAttr("data-clickover-open");

        this.options.esc_close && $(document).unbind("keyup.clickery");

        $("body").off(this.attr.click_event_ns);

        if (typeof this.attr.tid == "number") {
          clearTimeout(this.attr.tid);
          delete this.attr.tid;
        }

        // provide some callback hooks
        typeof this.options.onHidden == "function" &&
          this.options.onHidden.call(this);
        this.$element.trigger("hidden");
      }
    },
    isShown: function () {
      return this.tip().hasClass("in");
    },
    resetPosition: function () {
      var $tip, inside, pos, actualWidth, actualHeight, placement, tp;

      if (this.hasContent() && this.enabled) {
        $tip = this.tip();

        placement =
          typeof this.options.placement == "function"
            ? this.options.placement.call(this, $tip[0], this.$element[0])
            : this.options.placement;

        inside = /in/.test(placement);

        pos = this.getPosition(inside);

        actualWidth = $tip[0].offsetWidth;
        actualHeight = $tip[0].offsetHeight;

        switch (inside ? placement.split(" ")[1] : placement) {
          case "bottom":
            tp = {
              top: pos.top + pos.height,
              left: pos.left + pos.width / 2 - actualWidth / 2,
            };
            break;
          case "top":
            tp = {
              top: pos.top - actualHeight,
              left: pos.left + pos.width / 2 - actualWidth / 2,
            };
            break;
          case "left":
            tp = {
              top: pos.top + pos.height / 2 - actualHeight / 2,
              left: pos.left - actualWidth,
            };
            break;
          case "right":
            tp = {
              top: pos.top + pos.height / 2 - actualHeight / 2,
              left: pos.left + pos.width,
            };
            break;
        }

        $tip.css(tp);
      }
    },
    debughide: function () {
      var dt = new Date().toString();

      console.log(dt + ": clickover hide");
      this.hide();
    },
  });

  /* plugin definition */
  /* stolen from bootstrap tooltip.js */
  $.fn.clickover = function (option) {
    return this.each(function () {
      var $this = $(this),
        data = $this.data("clickover"),
        options = typeof option == "object" && option;

      if (!data) $this.data("clickover", (data = new Clickover(this, options)));
      if (typeof option == "string") data[option]();
    });
  };

  $.fn.clickover.Constructor = Clickover;

  // these defaults are passed directly to parent classes
  $.fn.clickover.defaults = $.extend({}, $.fn.popover.defaults, {
    trigger: "manual",
    auto_close: 0 /* ms to auto close clickover, 0 means none */,
    global_close: 1 /* allow close when clicked away from clickover */,
    esc_close: 1 /* allow clickover to close when esc key is pressed */,
    onShown: null /* function to be run once clickover has been shown */,
    onHidden: null /* function to be run once clickover has been hidden */,
    width:
      null /* number is px (don't add px), null or 0 - don't set anything */,
    height:
      null /* number is px (don't add px), null or 0 - don't set anything */,
    tip_id: null /* id of popover container */,
    class_name:
      "clickover" /* default class name in addition to other classes */,
    allow_multiple: 0 /* enable to allow for multiple clickovers to be open at the same time */,
  });
})(window.jQuery);
