 /**
 * HintBox - The pop-up window thingie with AJAX, based on prototype and script.aculo.us.
 *
 * @author Marek Snopkowski <snpy@snpy.info>
 * @license X11
 * @version 0.1
 */

if (!window.Hintbox) var Hintbox = new Object();

Hintbox.Methods = {
  isVisible: false,
  lastTimeoutId: null,
  timeout: 1000,
  lastTrigger: null,
  options: {
    allowClose: true,
    duration: .15,
    xRightOver: 0,
    xTopOver: 0,
    request: null,
    async: true,
    hintboxWidth: 400,
    title: "Szybki podgląd",
		closeText: "Zamknij (Esc)",
    shadowWidth: 5,
    complete: null
  },
  _options: new Object,

  _setOptions: function(options) {
    Object.extend(this._options, options || {});
    if (this._options.hintboxWidth < 200) this._options.hintboxWidth = 200;
    if (this._options.shadowWidth < 0) this._options.shadowWidth = 0;
  },

  _build: function() {
    if (!this.holder) {
      this.isVisible = false;
      this.closeHintListener = this.closeHint.bindAsEventListener(this);
      this.resizeShadowListener = this.resizeShadow.bindAsEventListener(this);
      this.keyboardListener = this.keyboardAction.bindAsEventListener(this);

      this.holder = Builder.node("div", {id: "KS_hintboxHolder", style: "display: none"}, [
        this.shadow = Builder.node("div", {id: "KS_hintboxShadow", style: "opacity: .3"}),
        this.container = Builder.node("div", {id: "KS_hintboxContainer"}, [
          this.dragBar = Builder.node("span", {id: "KS_hintboxDragBar"}, [
            this.title = Builder.node("span", {id: "KS_hintboxTitle", style: "display: none"}),
            //this.close = Builder.node("img", {src: "/ksHintboxPlugin/images/close.png", id: "KS_hintboxClose"}),
            this.close = Builder.node("div", {id: "KS_hintboxClose"}, [
						  this._options.closeText
						]),
          ]),
          this.text = Builder.node("div", {id: "KS_hintboxText"}),
          this.indicator = Builder.node("img", {src: "/ksHintboxPlugin/images/indicator.gif", id: "KS_hintboxIndicator"}),
          this.pointer = Builder.node("img", {src: "/ksHintboxPlugin/images/pointer.png", id: "KS_hintboxPointer"}),
          this.pointerUp = Builder.node("img", {src: "/ksHintboxPlugin/images/pointer-up.png", id: "KS_hintboxPointerUp"})
        ])
      ]);
      Element.setOpacity(this.shadow, .3);
      Element.setOpacity(this.close, .4);

      document.body.insertBefore(this.holder, document.body.childNodes[0]);
      //document.body.insertBefore(this.shadow, document.body.childNodes[0]);

      this.height = this.holder.offsetHeight;

      if (typeof Draggable != undefined) {
        new Draggable("KS_hintboxHolder", {
          //starteffect: null,
          //endeffect: null,
          snap: function(x, y, draggable) {
            Element.hide(Hintbox.pointer);
            Element.hide(Hintbox.pointerUp);
            function constrain(n, lower, upper) {
              if (n > upper) return upper;
              else if (n < lower) return lower;
              return n;
            }

            hintDim = Element.getDimensions(draggable.element);
            pageDim = Element.getDimensions(document.body);
            pageDim.width -= Hintbox._options.xRightOver;
            return[
              constrain(x, 5, pageDim.width - hintDim.width - 5),
              constrain(y, Hintbox._options.xTopOver + 5, pageDim.height - hintDim.height + 5)
              ];
          },
          handle: "KS_hintboxDragBar"
        });
        //new Draggable("KS_hintboxHolder", {handle:"KS_hintboxDragBar"});
      }
      Event.observe(this.close, "click", this.closeHintListener);
			this.enableKeyboardListener();
    }

    Event.stopObserving(this.text, "click", this.closeHintListener);
    Event.observe(this.text, "click", this.resizeShadowListener);
    this.title.innerHTML = "";
    Element.setStyle(this.title, {display: "none"});

    if (this.isVisible) return;

    Element.setStyle(this.holder, {width: this._options.hintboxWidth + "px"});
    Element.setStyle(this.dragBar, {width: this._options.hintboxWidth + "px"});
    Element.setStyle(this.indicator, {left: (this._options.hintboxWidth - 31) / 2 + "px"});
    Element.setStyle(this.shadow, {height: 40 + 2 * this._options.shadowWidth + "px"});
  },

  enableKeyboardListener: function() {
    document.observe('keydown', this.keyboardListener);
  },

  disableKeyboardListener: function() {
    document.stopObserving('keydown', this.keyboardListener);
  },

  keyboardAction: function(event) {
    // close hintbox
    if (event.keyCode == Event.KEY_ESC) {
      this.closeHint();
    }
  },

  resizeShadow: function() {
    Element.setStyle(Hintbox.shadow, {
      width: Hintbox._options.hintboxWidth + 2 * Hintbox._options.shadowWidth + "px",
      height: Hintbox.holder.offsetHeight + 2 * Hintbox._options.shadowWidth + "px",
      top: "-" + Hintbox._options.shadowWidth + "px",
      left: "-" + Hintbox._options.shadowWidth + "px"
    });
  },

  initiate: function(trigger, options) {
    // Setting up original options with default options
    Object.extend(this._options, this.options);
    this._setOptions(options);

    var trigger = $(trigger);
    this.stopCloseProcess();

    this._build();

    if (this.isVisible) {
      this.closeHint();
      if (this.lastTrigger.id != trigger.id) {
        setTimeout("Hintbox.initiate('" + trigger.id + "', " + Object.toJSON(options) + ", " + this._options.async + ");", 3000*Hintbox._options.duration);
      }
    } else {
      // make background request to retrive text
      if (this._options.async) new Ajax.Request(this._options.request, {onComplete: this.onComplete.bind(this)});

      // clear text hint window holder
      this.text.innerHTML = "";
      //Element.setStyle(this.text, {background: "transparent"});
      // show loading indicator over hint window
      Effect.Appear(this.indicator, {duration:0.1});
      //this.indicator.show();
      Element.hide(this.pointer);
      Element.hide(this.pointerUp);

      var triggerPos = Position.cumulativeOffset(trigger);

      var top = triggerPos[1] - this.height + trigger.offsetHeight;
      if (top < 0) top = 0;

      var left = triggerPos[0] - 40, leftDiff = document.body.offsetWidth - (this._options.hintboxWidth + left), leftOver = 0;
      if (leftDiff < this._options.xRightOver) {
        left = document.body.offsetWidth - this._options.hintboxWidth - this._options.xRightOver - this._options.shadowWidth;
      } else if (left < 0) {
        leftOver = left < -33 ? -33 : left;
        left = 0;
      }

      // safari has some problem with position defined in css class
      Element.setStyle(this.holder, {top: top + "px", position: "absolute", left: left + "px"});
      this.pointer.style.left = this.pointerUp.style.left =
          ((leftDiff < 0 ? 34 - leftDiff + this._options.xRightOver : 34 + leftOver)
            + (trigger.offsetWidth / 2 - 11)) + "px";
      this.openHint();
      if (!this._options.async) setTimeout("Hintbox.showHint('" + this._options.request + "', true);", 500);
    }
    this.lastTrigger = trigger;
  },

  onComplete: function(request) {
    if (this._options.complete) this._options.complete;
    this.showHint(request.responseText);
  },

  showHint: function(innerContent, uriDecode) {
    this.title.innerHTML = this._options.title;
    Effect.Appear(this.title, {duration: this._options.duration});
    if (uriDecode) innerContent = decodeURIComponent(innerContent);
    Effect.Fade(this.indicator, {duration:this._options.duration});

    this.text.innerHTML = innerContent;
    this.resizeShadow();

    var top = this.holder.offsetTop - this.holder.offsetHeight - 2 * this._options.shadowWidth;
    var pos = Position.page(this.holder);

    new Effect.Move(this.holder, { y: (top > 0 ? -this.holder.offsetHeight - 2 * this._options.shadowWidth - 5 : this.height + this._options.shadowWidth), duration: 2 * this._options.duration });
    if (pos[1] < this.holder.offsetHeight && top > 0)
      setTimeout("new Effect.ScrollTo(Hintbox.holder, {duration: 2*Hintbox._options.duration})", 2000 * this._options.duration)
    if (top > 0) {
      //Element.show(this.pointer);
    } else {
      //Element.show(this.pointerUp);
    }
    //this.holder.style.top = (this.holder.offsetTop - this.holder.offsetHeight + this.height) + "px";
    if (typeof Message != undefined) {
      Message.reloadCaptcha();
    }
    setTimeout("if(!Hintbox.focusFirstInput())Event.observe(Hintbox.text, 'click', Hintbox.closeHintListener)", 4000*this._options.duration);
  },

  focusFirstInput: function(holder) {
    var holder = holder || this.text;
    for (var i = 0, max = holder.childNodes.length; i < max; ++i) {
      var e = holder.childNodes[i];
      // check if e is a html element
      if (e.nodeType != 1) {
        continue;
      }
      // check if input, select or textarea
      if (e.type != "hidden" && e.disabled == false &&
          (e.tagName == "SELECT" || e.tagName == "INPUT" || e.tagName == "TEXTAREA")) {
        e.focus();
        return true;
      }
      if (e.childNodes.length) {
        if (this.focusFirstInput(e)) {
          return true;
        }
      }
    }
    return false;
  },

  switchAllowClose: function() {
    Hintbox._options.allowClose = (Hintbox._options.allowClose) ? false : true;
    Hintbox.stopCloseProcess();
  },

  openHint: function() {
    if (Hintbox.holder) {
      Effect.Appear(Hintbox.holder, {duration:Hintbox._options.duration});
    }
    Hintbox.isVisible = true;
  },

  startCloseProcess: function() {
    if (Hintbox.holder && Hintbox.isVisible && Hintbox._options.allowClose) {
			Hintbox.disableKeyboardListener();
      clearTimeout(Hintbox.lastTimeoutId);
      Hintbox.lastTimeoutId = setTimeout("Hintbox.closeHint()", Hintbox.timeout);
    }
  },

  stopCloseProcess: function() {
    if (Hintbox.lastTimeoutId) {
      clearTimeout(Hintbox.lastTimeoutId);
    }
  },

  closeHint: function() {
    Hintbox.isVisible = false;
    if (Hintbox.holder) {
      Effect.Fade(Hintbox.holder, {duration:Hintbox._options.duration});
      new Effect.Move(Hintbox.holder, {y: 100, duration: 2*Hintbox._options.duration});
      //Effect.BlindUp(Hintbox.text);
    }
  }
}

Object.extend(Hintbox, Hintbox.Methods);
