/**
* @license 
* jQuery Tools @VERSION Tooltip - UI essentials
* 
* NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
* 
* http://flowplayer.org/tools/tooltip/
*
* Since: November 2008
* Date: @DATE 
*/
(function ($) {

    /* 
    removed: oneInstance, lazy, 
    tip must next to the trigger 
    isShown(fully), layout, tipClass, layout
    */

    // static constructs
    $.tools = $.tools || { version: '@VERSION' };

    $.tools.tooltip = {

        conf: {

            // default effect variables
            effect: 'toggle',
            fadeOutSpeed: "fast",
            predelay: 0,
            delay: 30,
            opacity: 1,
            tip: 0,

            // 'top', 'bottom', 'right', 'left', 'center'
            position: ['top', 'center'],
            offset: [0, 0],
            relative: false,
            cancelDefault: true,

            // type to event mapping 
            events: {
                def: "mouseenter,mouseleave",
                input: "focus,blur",
                widget: "focus mouseenter,blur mouseleave",
                tooltip: "mouseenter,mouseleave"
            },

            // 1.2
            layout: '<div/>',
            tipClass: 'tooltip'
        },

        addEffect: function (name, loadFn, hideFn) {
            effects[name] = [loadFn, hideFn];
        }
    };


    var effects = {
        toggle: [
			function (done) {
			    var conf = this.getConf(), tip = this.getTip(), o = conf.opacity;
			    if (o < 1) { tip.css({ opacity: o }); }
			    tip.show();
			    done.call();
			},

			function (done) {
			    this.getTip().hide();
			    done.call();
			}
		],

        fade: [
			function (done) {
			    var conf = this.getConf();
			    this.getTip().fadeTo(conf.fadeInSpeed, conf.opacity, done);
			},
			function (done) {
			    this.getTip().fadeOut(this.getConf().fadeOutSpeed, done);
			}
		]
    };


    /* calculate tip position relative to the trigger */
    function getPosition(trigger, tip, conf) {


        // get origin top/left position 
        var top = conf.relative ? trigger.position().top : trigger.offset().top,
			 left = conf.relative ? trigger.position().left : trigger.offset().left,
			 pos = conf.position[0];

        top -= tip.outerHeight() - conf.offset[0];
        left += trigger.outerWidth() + conf.offset[1];

        // adjust Y		
        var height = tip.outerHeight() + trigger.outerHeight();
        if (pos == 'center') { top += height / 2; }
        if (pos == 'bottom') { top += height; }

        // adjust X
        pos = conf.position[1];
        var width = tip.outerWidth() + trigger.outerWidth();
        if (pos == 'center') { left -= width / 2; }
        if (pos == 'left') { left -= width; }

        return { top: top, left: left };
    }



    function Tooltip(trigger, conf) {

        var self = this,
			 fire = trigger.add(self),
			 tip,
			 timer = 0,
			 pretimer = 0,
			 title = trigger.attr("title"),
			 effect = effects[conf.effect],
			 shown,

        // get show/hide configuration
			 isInput = trigger.is(":input"),
			 isWidget = isInput && trigger.is(":checkbox, :radio, select, :button, :submit"),
			 type = trigger.attr("type"),
			 evt = conf.events[type] || conf.events[isInput ? (isWidget ? 'widget' : 'input') : 'def'];


        // check that configuration is sane
        if (!effect) { throw "Nonexistent effect \"" + conf.effect + "\""; }

        evt = evt.split(/,\s*/);
        if (evt.length != 2) { throw "Tooltip: bad events configuration for " + type; }


        // trigger --> show  
        trigger.bind(evt[0], function (e) {
            clearTimeout(timer);
            if (conf.predelay) {
                pretimer = setTimeout(function () { self.show(e); }, conf.predelay);

            } else {
                self.show(e);
            }

            // trigger --> hide
        }).bind(evt[1], function (e) {
            clearTimeout(pretimer);
            if (conf.delay) {
                timer = setTimeout(function () { self.hide(e); }, conf.delay);

            } else {
                self.hide(e);
            }

        });


        // remove default title
        if (title && conf.cancelDefault) {
            trigger.removeAttr("title");
            trigger.data("title", title);
        }

        $.extend(self, {

            show: function (e) {

                // tip not initialized yet
                if (!tip) {

                    // autogenerated tooltip
                    if (title) {
                        tip = $(conf.layout).addClass(conf.tipClass).appendTo(document.body)
							.hide().append(title);

                        // single tip element for all
                    } else if (conf.tip) {
                        tip = $(conf.tip).eq(0);

                        // manual tooltip
                    } else {
                        tip = trigger.next();
                        if (!tip.length) { tip = trigger.parent().next(); }
                    }

                    if (!tip.length) { throw "Cannot find tooltip for " + trigger; }
                }

                if (self.isShown()) { return self; }

                // stop previous animation
                tip.stop(true, true);

                // get position
                var pos = getPosition(trigger, tip, conf);


                // onBeforeShow
                e = e || $.Event();
                e.type = "onBeforeShow";
                fire.trigger(e, [pos]);
                if (e.isDefaultPrevented()) { return self; }


                // onBeforeShow may have altered the configuration
                pos = getPosition(trigger, tip, conf);

                // set position
                tip.css({ position: 'absolute', top: pos.top, left: pos.left });

                shown = true;

                // invoke effect 
                effect[0].call(self, function () {
                    e.type = "onShow";
                    shown = 'full';
                    fire.trigger(e);
                });


                // tooltip events       
                var event = conf.events.tooltip.split(/,\s*/);

                tip.bind(event[0], function () {
                    clearTimeout(timer);
                    clearTimeout(pretimer);
                });

                if (event[1] && !trigger.is("input:not(:checkbox, :radio), textarea")) {
                    tip.bind(event[1], function (e) {

                        // being moved to the trigger element
                        if (e.relatedTarget != trigger[0]) {
                            trigger.trigger(evt[1].split(" ")[0]);
                        }
                    });
                }

                return self;
            },

            hide: function (e) {

                if (!tip || !self.isShown()) { return self; }

                // onBeforeHide
                e = e || $.Event();
                e.type = "onBeforeHide";
                fire.trigger(e);
                if (e.isDefaultPrevented()) { return; }

                shown = false;

                effects[conf.effect][1].call(self, function () {
                    e.type = "onHide";
                    shown = false;
                    fire.trigger(e);
                });

                return self;
            },

            isShown: function (fully) {
                return fully ? shown == 'full' : shown;
            },

            getConf: function () {
                return conf;
            },

            getTip: function () {
                return tip;
            },

            getTrigger: function () {
                return trigger;
            }

        });

        // callbacks	
        $.each("onHide,onBeforeShow,onShow,onBeforeHide".split(","), function (i, name) {

            // configuration
            if ($.isFunction(conf[name])) {
                $(self).bind(name, conf[name]);
            }

            // API
            self[name] = function (fn) {
                $(self).bind(name, fn);
                return self;
            };
        });

    }


    // jQuery plugin implementation
    $.fn.tooltip = function (conf) {

        // return existing instance
        var api = this.data("tooltip");
        if (api) { return api; }

        conf = $.extend(true, {}, $.tools.tooltip.conf, conf);

        // position can also be given as string
        if (typeof conf.position == 'string') {
            conf.position = conf.position.split(/,?\s/);
        }

        // install tooltip for each entry in jQuery object
        this.each(function () {
            api = new Tooltip($(this), conf);
            $(this).data("tooltip", api);
        });

        return conf.api ? api : this;
    };

})(jQuery);

		


