/**
 * @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($) {  
    // static constructs
    $.tools = $.tools || {version: '@VERSION'};
    
    $.tools.tooltip = {
        
        conf: { 
            // default effect variables
            effect: 'toggle',           
            tip: 0,
            fadeOutSpeed: "fast",
            predelay: 0,
            delay: 30,
            opacity: 1,         

            //mouse-follow
            mousetracking: true,
            mousetrackingdelay: 0,

            //speechbaloon
            useBaloon: false,
            baloonHTML: '<table border="0" cellpadding="0" cellspacing="0">' + 
            '<tr style="margin-bottom: -100px;"><td style="background:url(\'http://www.YOURDOMAIN.com/images/speechballon_r1_c1.png\') no-repeat bottom left;"><img src="http://www.YOURDOMAIN.com/images/space.gif" width="8" height="11" /></td><td style="background:url(\'http://www.YOURDOMAIN.com/images/speechballon_r1_c2.png\') repeat-x bottom left;"><img src="http://www.YOURDOMAIN.com/images/space.gif" height="11" /></td><td style="background:url(\'http://www.YOURDOMAIN.com/images/speechballon_r1_c3.png\') no-repeat bottom left;"><img src="http://www.YOURDOMAIN.com/images/space.gif" width="8" height="11" /></td></tr>' + 
            '<tr><td style="background:url(\'http://www.YOURDOMAIN.com/images/speechballon_r2_c1.png\') repeat-y;"></td><td id="baloonContent">[HERE TITLE WILL BE INJECTED]</td><td style="background:url(\'http://www.YOURDOMAIN.com/images/speechballon_r2_c3.png\') repeat-y;"></td></tr>' +
            '<tr><td style="background:url(\'http://www.YOURDOMAIN.com/images/speechballon_r3_c1.png\') no-repeat;"><img src="http://www.YOURDOMAIN.com/images/space.gif" width="8" height="11" /></td><td style="background:url(\'http://www.YOURDOMAIN.com/images/speechballon_r3_c2.png\') repeat-x;"></td><td style="background:url(\'http://www.YOURDOMAIN.com/images/speechballon_r3_c3.png\') no-repeat;"><img src="http://www.YOURDOMAIN.com/images/space.gif" width="8" height="11" /></td></tr></table>',
            //there must be an element with the ID 'baloonContent' where the HTML will be injected


            // 'top', 'bottom', 'right', 'left', 'center'
            position: ['top', 'right'], 
            offset: [0, 0],
            size: [0, 0], //width, height -> size of 0 means: auto
            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',
            tipInner: "tooltip-inner"
        },
        
        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, mousepos) {    
        var top, left, pos;

    //mouse-follow
        if (conf.position == "none" || conf.mousetracking == true) {
            left = mousepos[1] + conf.offset[1];
            top = mousepos[0] + conf.offset[0]; // + tip.outerHeight()/2;

            scrolltop = $(window).scrollTop();
            tt_height = tip.outerHeight();
            vp_height = $(window).height();
            m_xpos = mousepos[0];

            var vspace = 45;

            if(((vp_height) - (m_xpos - scrolltop + tt_height + vspace)) < 0) {
                top = mousepos[0] - conf.offset[0] - tip.outerHeight();
            }

        } else {
            // get origin top/left position 
            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];
            
            // iPad position fix
            if (/iPad/i.test(navigator.userAgent)) {
                top -= $(window).scrollTop();
            }
            
            // 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"),
             tipAttr = trigger.attr("data-tooltip"),
             effect = effects[conf.effect],
             shown,
             
             //mouse-follow
             followtimer = 0,
                 
             // 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);       
            }
        }); 
        
        //mouse-follow
        if (conf.mousetracking) {
            trigger.bind("mousemove", function(e) {
                if (conf.mousetrackingdelay) {
                    clearTimeout(followtimer);
                    followtimer = setTimeout(function() { self.update(e); },
                    conf.mousetrackingdelay);
                } else {
                    self.update(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) {
                    
                    // data-tooltip 
                    if (tipAttr) {
                        tip = $(tipAttr);

                    // single tip element for all
                    } else if (conf.tip) { 
                        tip = $(conf.tip).eq(0);
                        
                    // baloon-tooltip
                    } else if (conf.useBaloon) { 
                        tip = $(conf.layout).addClass(conf.tipClass).appendTo(document.body).hide().html(conf.baloonHTML);
                        //inject the image-title
                        tip.find("#baloonContent").html(title);

                    // autogenerated tooltip
                    } else if (title) { 
//                      tip = $(conf.layout).addClass(conf.tipClass).appendTo(document.body)
//                          .hide().append(title);
                    // autogenerated tooltip
//                  if (title) { 
                        tip = $(conf.layout).addClass(conf.tipClass).appendTo(document.body).hide();
    
                        if(conf.tipInner) {
                            tip.find('.' + conf.tipInner).html(title);
                        } else {
                             tip.append(title);
                        }
                    // manual tooltip
                    } else {    
                        tip = trigger.next();  
                        if (!tip.length) { tip = trigger.parent().next(); }      
                    }
                    
                    if (!tip.length) { throw "Cannot find tooltip for " + trigger;  }
                } 

                //set size
                if (!conf.useBaloon) {
                        if(conf.size[0]>0) {tip.css('width', conf.size[0]);}
                    if(conf.size[1]>0) {tip.css('height', conf.size[1]);}
                }

                // hide ALL OTHER tooltips immediately
                $(".tooltip").hide();

                if (self.isShown()) { return self; }  
                
                // stop previous animation
                tip.stop(true, true);               
                
                // get position
                var pos = getPosition(trigger, tip, conf, [e.pageY, e.pageX]);          
        
                // restore title for single tooltip element
                if (conf.tip) {
                    tip.html(trigger.data("title"));
                }

                // 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, [e.pageY, e.pageX]);
                
                // 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*/);

                if (!tip.data("__set")) {
                    
                    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]);
                            }
                        }); 
                    } 
                    
                    tip.data("__set", true);
                }
                
                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";
                    fire.trigger(e);         
                });
                
                return self;
            },

            update: function(e) {
                if (!tip || !self.isShown()) {
                    return self;
                }  
                pos = getPosition(trigger, tip, conf, [e.pageY, e.pageX]);
                // set position
                // console.log(tip);
                for(i=0; i<tip.length; i++) {
                      tip.css({position:'absolute', top: pos.top, left: pos.left});
                }
                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) {
                if (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);

        


