/*
Script: RedTips.js
  Class for creating nice tips that follow the mouse cursor when hovering an element.
 
License:
  MIT-style license.
*/
 
var RedTips = new Class({
 
  Implements: [Events, Options],
 
  options: {
    onShow: function(tip){
      tip.setStyle('visibility', 'visible');
    },
    onHide: function(tip){
      tip.setStyle('visibility', 'hidden');
    },
    showDelay: 100,
    hideDelay: 100,
    className: null,
    offsets: {x: 16, y: 16},
    fixed: false
  },
 
  initialize: function(){
    var params = Array.link(arguments, {options: Object.type, elements: $defined});
    this.setOptions(params.options || null);
 
    this.tip = new Element('div').inject(document.body);
 
    if (this.options.className) this.tip.addClass(this.options.className);
 
    var top = new Element('div', {'class': 'tip-top'}).inject(this.tip);
    this.container = new Element('div', {'class': 'tip'}).inject(this.tip);
    var bottom = new Element('div', {'class': 'tip-bottom'}).inject(this.tip);
 
    this.tip.setStyles({position: 'absolute', top: 0, left: 0, visibility: 'hidden'});
 
    if (params.elements) this.attach(params.elements);
  },
 
  attach: function(elements){
    $$(elements).each(function(element){
      var title = element.retrieve('tip:title', element.get('title'));
      var text = element.retrieve('tip:text', element.get('rel') || element.get('href'));
      var enter = element.retrieve('tip:enter', this.elementEnter.bindWithEvent(this, element));
      var leave = element.retrieve('tip:leave', this.elementLeave.bindWithEvent(this, element));
      element.addEvents({mouseenter: enter, mouseleave: leave});
      if (!this.options.fixed){
        var move = element.retrieve('tip:move', this.elementMove.bindWithEvent(this, element));
        element.addEvent('mousemove', move);
      }
      element.store('tip:native', element.get('title'));
      element.erase('title');
    }, this);
    return this;
  },
 
  detach: function(elements){
    $$(elements).each(function(element){
      element.removeEvent('mouseenter', element.retrieve('tip:enter') || $empty);
      element.removeEvent('mouseleave', element.retrieve('tip:leave') || $empty);
      element.removeEvent('mousemove', element.retrieve('tip:move') || $empty);
      element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');
      var original = element.retrieve('tip:native');
      if (original) element.set('title', original);
    });
    return this;
  },
 
  elementEnter: function(event, element){
 
    $A(this.container.childNodes).each(Element.dispose);
 
    var title = element.retrieve('tip:title');
 
    if (title){
      this.titleElement = new Element('div', {'class': 'tip-title'}).inject(this.container);
      this.fill(this.titleElement, title);
    }
 
    var text = element.retrieve('tip:text');
    if (text){
      this.textElement = new Element('div', {'class': 'tip-text'}).inject(this.container);
      this.fill(this.textElement, text);
    }
 
    this.timer = $clear(this.timer);
    this.timer = this.show.delay(this.options.showDelay, this);
 
    this.position((!this.options.fixed) ? event : {page: element.getPosition()});
  },
 
  elementLeave: function(event){
    $clear(this.timer);
    this.timer = this.hide.delay(this.options.hideDelay, this);
  },
 
  elementMove: function(event){
    this.position(event);
  },
 
  position: function(event){
    var size = window.getSize(), scroll = window.getScroll();
    var tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight};
	var x = event.page.x + this.options.offsets.x;
	var y = event.page.y + this.options.offsets.y;
	
	if ((x + tip.x - scroll.x) > size.x) {
	  x = event.page.x - this.options.offsets.x - tip.x;
	  this.tip.removeClass('right').addClass('left').setStyle('left', x);
	} else {
	  this.tip.removeClass('left').addClass('right').setStyle('left', x);
	}
	
	if ((y + tip.y - scroll.y) > size.y) {
	  y = event.page.y - this.options.offsets.y - tip.y;
	  this.tip.removeClass('below').addClass('above').setStyle('top', y);
	} else {
	  this.tip.removeClass('above').addClass('below').setStyle('top', y);
	}
  },
 
  fill: function(element, contents){
    (typeof contents == 'string') ? element.set('html', contents) : element.adopt(contents);
  },
 
  show: function(){
    this.fireEvent('show', this.tip);
  },
 
  hide: function(){
    this.fireEvent('hide', this.tip);
  }
 
});