/** * tools.tooltip 1.1.2 - Tooltips done right. * * Copyright (c) 2009 Tero Piirainen * http://flowplayer.org/tools/tooltip.html * * Dual licensed under MIT and GPL 2+ licenses * http://www.opensource.org/licenses * * Launch : November 2008 * Date: ${date} * Revision: ${revision} */ (function($) { var instances = []; // static constructs $.tools = $.tools || {}; $.tools.tooltip = { version: '1.1.2', conf: { // default effect variables effect: 'toggle', fadeOutSpeed: "fast", tip: null, predelay: 0, delay: 30, opacity: 1, lazy: undefined, // 'top', 'bottom', 'right', 'left', 'center' position: ['top', 'center'], offset: [0, 0], cancelDefault: true, relative: false, oneInstance: false, // type to event mapping events: { def: "mouseover,mouseout", input: "focus,blur", widget: "focus mouseover,blur mouseout", tooltip: "mouseover,mouseout" }, api: false }, 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) { this.getTip().fadeIn(this.getConf().fadeInSpeed, done); }, function(done) { this.getTip().fadeOut(this.getConf().fadeOutSpeed, done); } ] }; function Tooltip(trigger, conf) { var self = this, $self = $(this); trigger.data("tooltip", self); // find the tip var tip = trigger.next(); if (conf.tip) { tip = $(conf.tip); } else { id = 'id' + trigger.data("title"); div = $('#' + id); if (div.length == 0){ div = document.createElement('div'); //div.id = id; div.style.width = '300px'; div.style.height = '200px'; div.style.display = 'none'; $(div).appendTo($(document.body)); } tip = $(div); } /* calculate tip position relative to the trigger */ function getPosition(e) { // 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}; } // event management evt = conf.events['def']; evt = evt.split(/,\s*/); if (evt.length != 2) { throw "Tooltip: bad events configuration for " + type; } trigger.bind(evt[0], function(e) { // close all instances if (conf.oneInstance) { $.each(instances, function() { this.hide(); }); } // see if the tip was launched by this trigger var t = tip.data("trigger"); if (t && t[0] != this) { tip.hide().stop(true, true); } e.target = this; self.show(e); // tooltip close events evt = conf.events.tooltip.split(/,\s*/); if (evt[0]) { tip.bind(evt[0], function() { self.show(e); }); } if (evt[1]) { tip.bind(evt[1], function() { self.hide(e); }); } return false; }); trigger.bind(evt[1], function(e) { self.hide(e); }); // ensure that the tip really shows up. IE cannot catch up with this. if (!$.browser.msie && !conf.predelay) { trigger.mousemove(function() { if (!self.isShown()) { trigger.triggerHandler("mouseover"); } }); } // avoid "black box" bug in IE with PNG background images if (conf.opacity < 1) { tip.css("opacity", conf.opacity); } var pretimer = 0, title = trigger.attr("title"); if (title) { trigger.removeAttr("alt"); trigger.data("title", title); tip.attr("id", 'id' + title); } $.extend(self, { show: function(e) { //console.log('show'); if (e) { trigger = $(e.target); } clearTimeout(tip.data("timer")); if (tip.is(":animated") || tip.is(":visible")) { return self; } function show() { // remember the trigger element for this tip tip.data("trigger", trigger); // get position var pos = getPosition(e); // onBeforeShow e = e || $.Event(); e.type = "onBeforeShow"; $self.trigger(e, [pos]); if (e.isDefaultPrevented()) { return self; } // onBeforeShow may have altered the configuration pos = getPosition(e); // set position tip.css({position:'absolute', top: pos.top, left: pos.left}); //tip.mouseleave = hide(); // invoke effect var eff = effects[conf.effect]; if (!eff) { throw "Nonexistent effect \"" + conf.effect + "\""; } eff[0].call(self, function() { e.type = "onShow"; $self.trigger(e); }); } if (conf.predelay) { clearTimeout(pretimer); pretimer = setTimeout(show, conf.predelay); } else { show(); } return self; }, hide: function(e) { //console.log('hide'); clearTimeout(tip.data("timer")); clearTimeout(pretimer); if (!tip.is(":visible")) { return; } function hide() { // onBeforeHide e = e || $.Event(); /* e.type = "onBeforeHide"; $self.trigger(e); if (e.isDefaultPrevented()) { return; } */ effects[conf.effect][1].call(self, function() { e.type = "onHide"; $self.trigger(e); }); } if (conf.delay && e) { tip.data("timer", setTimeout(hide, conf.delay)); } else { hide(); } return self; }, isShown: function() { return tip.is(":visible, :animated"); }, getConf: function() { return conf; }, getTip: function() { return tip; }, getTrigger: function() { return trigger; }, // callback functions bind: function(name, fn) { $self.bind(name, fn); return self; }, onHide: function(fn) { return this.bind("onHide", fn); }, onBeforeShow: function(fn) { return this.bind("onBeforeShow", fn); }, onShow: function(fn) { return this.bind("onShow", fn); }, onBeforeHide: function(fn) { return this.bind("onBeforeHide", fn); }, unbind: function(name) { $self.unbind(name); return self; } }); // bind all callbacks from configuration $.each(conf, function(name, fn) { if ($.isFunction(fn)) { self.bind(name, fn); } }); } // jQuery plugin implementation $.prototype.tooltip = function(conf) { // return existing instance var api = this.eq(typeof conf == 'number' ? conf : 0).data("tooltip"); if (api) { return api; } // setup options var globals = $.extend(true, {}, $.tools.tooltip.conf); if ($.isFunction(conf)) { conf = {onBeforeShow: conf}; } else if (typeof conf == 'string') { conf = {tip: conf}; } conf = $.extend(true, globals, conf); // can also be given as string if (typeof conf.position == 'string') { conf.position = conf.position.split(/,?\s/); } this.each(function() { api = new Tooltip($(this), conf); instances.push(api); }); return conf.api ? api: this; }; }) (jQuery);