| /** |
| * tools.tooltip 1.1.3 - 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.3', |
| |
| 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: true, |
| |
| |
| // 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); |
| |
| // multiple tip elements |
| if (tip.length > 1) { |
| |
| // find sibling |
| tip = trigger.nextAll(conf.tip).eq(0); |
| |
| // find sibling from the parent element |
| if (!tip.length) { |
| tip = trigger.parent().nextAll(conf.tip).eq(0); |
| } |
| } |
| } |
| |
| /* 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 |
| var isInput = trigger.is(":input"), |
| isWidget = isInput && trigger.is(":checkbox, :radio, select, :button"), |
| type = trigger.attr("type"), |
| evt = conf.events[type] || conf.events[isInput ? (isWidget ? 'widget' : 'input') : '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*/); |
| tip.bind(evt[0], function() { self.show(e); }); |
| if (evt[1]) { tip.bind(evt[1], function() { self.hide(e); }); } |
| |
| }); |
| |
| 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 && !isInput && !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 && conf.cancelDefault) { |
| trigger.removeAttr("title"); |
| trigger.data("title", title); |
| } |
| |
| $.extend(self, { |
| |
| show: function(e) { |
| |
| 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); |
| |
| // title attribute |
| if (conf.tip && title) { |
| tip.html(trigger.data("title")); |
| } |
| |
| // 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}); |
| |
| // 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) { |
| |
| 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/); |
| } |
| |
| // assign tip's only when apiement is being mouseovered |
| if (conf.lazy !== false && (conf.lazy === true || this.length > 20)) { |
| |
| this.one("mouseover", function(e) { |
| api = new Tooltip($(this), conf); |
| api.show(e); |
| instances.push(api); |
| }); |
| |
| } else { |
| |
| // install tooltip for each entry in jQuery object |
| this.each(function() { |
| api = new Tooltip($(this), conf); |
| instances.push(api); |
| }); |
| } |
| |
| return conf.api ? api: this; |
| |
| }; |
| |
| }) (jQuery); |
| |
| |
| |