/*
* Tooltip - jQuery plugin for styled tooltips
*
* Copyright (c) 2006 Jörn Zaefferer, Stefan Petre
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
/**
* Display a customized tooltip instead of the default one
* for every selected element. The tooltip behaviour mimics
* the default one, but lets you style the tooltip and
* specify the delay before displaying it.
*
* In addition, it displays the href value, if it is available.
*
* To style the tooltip, use these selectors in your stylesheet:
*
* #tooltip - The tooltip container
*
* #tooltip h3 - The tooltip title
*
* #tooltip p.body - The tooltip body, shown when using showBody
*
* #tooltip p.url - The tooltip url, shown when using showURL
*
* @example $('a, input, img').Tooltip();
* @desc Shows tooltips for anchors, inputs and images, if they have a title
*
* @example $('label').Tooltip({
* delay: 0,
* track: true,
* event: "click"
* });
* @desc Shows tooltips for labels with no delay, tracking mousemovement, displaying the tooltip when the label is clicked.
*
* @example // modify global settings
* $.extend($.fn.Tooltip.defaults, {
* track: true,
* delay: 0,
* showURL: false,
* showBody: " - ",
* fixPNG: true
* });
* // setup fancy tooltips
* $('a.pretty').Tooltip({
* extraClass: "fancy"
* });
$('img.pretty').Tooltip({
* extraClass: "fancy-img",
* });
* @desc This example starts with modifying the global settings, applying them to all following Tooltips; Afterwards, Tooltips for anchors with class pretty are created with an extra class for the Tooltip: "fancy" for anchors, "fancy-img" for images
*
* @param Object settings (optional) Customize your Tooltips
* @option Number delay The number of milliseconds before a tooltip is display, default is 250
* @option String event The event on which the tooltip is displayed, default is "mouseover", "click" works fine, too
* @option Boolean track If true, let the tooltip track the mousemovement, default is false
* @option Boolean showURL If true, shows the href or src attribute within p.url, default is true
* @option String showBody If specified, uses the String to split the title, displaying the first part in the h3 tag, all following in the p.body tag, separated with
s, default is null
* @option String extraClass If specified, adds the class to the tooltip helper, default is null
* @option Boolean fixPNG If true, fixes transparent PNGs in IE, default is false
*
* @name Tooltip
* @type jQuery
* @cat Plugins/Tooltip
* @author Jörn Zaefferer (http://bassistance.de)
*/
(function($) {
// the tooltip element
var helper,
// it's title part
tTitle,
// it's body part
tBody,
// it's url part
tUrl,
// the current tooltipped element
current,
// the title of the current element, used for restoring
oldTitle,
// timeout id for delayed tooltips
tID;
// the public plugin method
$.fn.Tooltip = function(settings) {
// setup configuration
// TODO: allow multiple arguments to extend, see bug #344
settings = $.extend($.extend({}, arguments.callee.defaults), settings || {});
// there can be only one tooltip helper
if( !helper ) {
// create the helper, h3 for title, div for url
helper = $('
')
// hide it at first
.hide()
// move to top and position absolute, to let it follow the mouse
.css({ position: 'absolute', zIndex: 3000 })
// add to document
.appendTo('body');
// save references to title and url elements
tTitle = $('h3', helper);
tBody = $('p:eq(0)', helper);
tUrl = $('p:eq(1)', helper);
}
// bind events for every selected element with a title attribute
$(this).filter('[@title]')
// save settings into each element
// TODO: pass settings via event system, not yet possible
.each(function() {
this.tSettings = settings;
})
// bind events
.bind("mouseover", save)
.bind(settings.event, handle);
return this;
};
// main event handler to start showing tooltips
function handle(event) {
// show helper, either with timeout or on instant
if( this.tSettings.delay )
tID = setTimeout(show, this.tSettings.delay);
else
show();
// if selected, update the helper position when the mouse moves
if(this.tSettings.track)
$('body').bind('mousemove', update);
// update at least once
update(event);
// hide the helper when the mouse moves out of the element
$(this).bind('mouseout', hide);
}
// save elements title before the tooltip is displayed
function save() {
// if this is the current source, or it has no title (occurs with click event), stop
if(this == current || !this.title)
return;
// save current
current = this;
var source = $(this),
settings = this.tSettings;
// save title, remove from element and set to helper
oldTitle = title = source.attr('title');
source.attr('title','');
if(settings.showBody) {
var parts = title.split(settings.showBody);
tTitle.html(parts.shift());
tBody.empty();
for(var i = 0, part; part = parts[i]; i++) {
if(i > 0)
tBody.append("
");
tBody.append(part);
}
if(tBody.html())
tBody.show();
else
tBody.hide();
} else {
tTitle.html(title);
tBody.hide();
}
// if element has href or src, add and show it, otherwise hide it
href = (source.attr('href') || source.attr('src'));
if( settings.showURL && href )
tUrl.html(href.replace('http://', '')).show();
else
tUrl.hide();
// add an optional class for this tip
if( settings.extraClass ) {
helper.addClass(settings.extraClass);
}
// fix PNG background for IE
if (settings.fixPNG && $.browser.msie ) {
helper.each(function () {
if (this.currentStyle.backgroundImage != 'none') {
var image = this.currentStyle.backgroundImage;
image = image.substring(5, image.length - 2);
$(this).css({
'backgroundImage': 'none',
'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
});
}
});
}
}
// delete timeout and show helper
function show() {
tID = null;
helper.show();
update();
}
/**
* callback for mousemove
* updates the helper position
* removes itself when no current element
*/
function update(event) {
// if no current element is available, remove this listener
if( current == null ) {
$('body').unbind('mousemove', update);
return;
}
var left = helper[0].offsetLeft;
var top = helper[0].offsetTop;
if(event) {
// get the current mouse position
function pos(c) {
var p = c == 'X' ? 'Left' : 'Top';
return event['page' + c] || (event['client' + c] + (document.documentElement['scroll' + p] || document.body['scroll' + p])) || 0;
}
// position the helper 15 pixel to bottom right, starting from mouse position
left = pos('X') + 15;
top = pos('Y') + 15;
helper.css({
left: left + 'px',
top: top + 'px'
});
}
var v = viewport(),
h = helper[0];
// check horizontal position
if(v.x + v.cx < h.offsetLeft + h.offsetWidth) {
left -= h.offsetWidth + 20;
helper.css({left: left + 'px'});
}
// check vertical position
if(v.y + v.cy < h.offsetTop + h.offsetHeight) {
top -= h.offsetHeight + 20;
helper.css({top: top + 'px'});
}
}
function viewport() {
var e = document.documentElement || {},
b = document.body || {},
w = window;
return {
x: w.pageXOffset || e.scrollLeft || b.scrollLeft || 0,
y: w.pageYOffset || e.scrollTop || b.scrollTop || 0,
cx: min( e.clientWidth, b.clientWidth, w.innerWidth ),
cy: min( e.clientHeight, b.clientHeight, w.innerHeight )
};
function min() {
var v = Infinity;
for( var i = 0; i < arguments.length; i++ ) {
var n = arguments[i];
if( n && n < v ) v = n;
}
return v;
}
}
// hide helper and restore added classes and the title
function hide() {
// clear timeout if possible
if(tID)
clearTimeout(tID);
// no more current element
current = null;
helper.hide();
// remove optional class
if( this.tSettings.extraClass ) {
helper.removeClass( this.tSettings.extraClass);
}
// restore title and remove this listener
$(this)
.attr('title', oldTitle)
.unbind('mouseout', hide);
// remove PNG background fix for IE
if( this.tSettings.fixPNG && $.browser.msie ) {
helper.each(function () {
$(this).css({'filter': '', backgroundImage: ''});
});
}
}
// define global defaults, editable by client
$.fn.Tooltip.defaults = {
delay: 250,
event: "mouseover",
track: false,
showURL: true,
showBody: null,
extraClass: null,
fixPNG: false
};
})(jQuery);