Files
iTop/js/hovertip.js
2009-04-28 09:03:12 +00:00

459 lines
14 KiB
JavaScript

/**
* Hovertip - easy and elegant tooltips
*
* By Dave Cohen <http://dave-cohen.com>
* With ideas and and javascript code borrowed from many folks.
* (See URLS in the comments)
*
* Licensed under GPL.
* Requires jQuery.js. <http://jquery.com>,
* which may be distributed under a different licence.
*
* $Date: 2006-09-15 12:49:19 -0700 (Fri, 15 Sep 2006) $
* $Rev: $
* $Id:$
*
* This plugin helps you create tooltips. It supports:
*
* hovertips - these appear under the mouse when mouse is over the target
* element.
*
* clicktips - these appear in the document when the target element is
* clicked.
*
* You may define behaviors for additional types of tooltips.
*
* There are a variety of ways to add tooltips. Each of the following is
* supported:
*
* <p>blah blah blah
* <span>important term</span>
* <span class="tooltip">text that appears.</span>
* blah blah blah</p>
*
* or,
*
* <p>blah blah blah
* <span hovertip="termdefinition">important term</span>
* blah blah blah</p>
* <div id="termdefinition" class="hovertip"><h1>term definition</h1><p>the term means...</p></div>
*
* or,
*
* <p>blah blah blah
* <span id="term">important term</span>
* blah blah blah</p>
* <div target="term" class="hovertip"><h1>term definition</h1><p>the term means...</p></div>
*
*
* Hooks are available to customize both the behavior of activated tooltips,
* and the syntax used to mark them up.
*
*/
//// mouse events ////
/**
* To make hovertips appear correctly we need the exact mouse position.
* These functions make that possible.
*/
// use globals to track mouse position
var hovertipMouseX;
var hovertipMouseY;
function hovertipMouseUpdate(e) {
var mouse = hovertipMouseXY(e);
hovertipMouseX = mouse[0];
hovertipMouseY = mouse[1];
}
// http://www.howtocreate.co.uk/tutorials/javascript/eventinfo
function hovertipMouseXY(e) {
if( !e ) {
if( window.event ) {
//Internet Explorer
e = window.event;
} else {
//total failure, we have no way of referencing the event
return;
}
}
if( typeof( e.pageX ) == 'number' ) {
//most browsers
var xcoord = e.pageX;
var ycoord = e.pageY;
} else if( typeof( e.clientX ) == 'number' ) {
//Internet Explorer and older browsers
//other browsers provide this, but follow the pageX/Y branch
var xcoord = e.clientX;
var ycoord = e.clientY;
var badOldBrowser = ( window.navigator.userAgent.indexOf( 'Opera' ) + 1 ) ||
( window.ScriptEngine && ScriptEngine().indexOf( 'InScript' ) + 1 ) ||
( navigator.vendor == 'KDE' );
if( !badOldBrowser ) {
if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
//IE 4, 5 & 6 (in non-standards compliant mode)
xcoord += document.body.scrollLeft;
ycoord += document.body.scrollTop;
} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
//IE 6 (in standards compliant mode)
xcoord += document.documentElement.scrollLeft;
ycoord += document.documentElement.scrollTop;
}
}
} else {
//total failure, we have no way of obtaining the mouse coordinates
return;
}
return [xcoord, ycoord];
}
//// target selectors ////
/**
* These selectors find the targets for a given tooltip element.
* Several methods are supported.
*
* You may write your own selector functions to customize.
*/
/**
* For this model:
* <span hovertip="ht1">target term</span>...
* <div class="hovertip" id="ht1">tooltip text</div>
*/
targetSelectById = function(el, config) {
var id;
var selector;
if (id = el.getAttribute('id')) {
selector = '*[@'+config.attribute+'=\''+id+'\']';
return $(selector);
}
};
/**
* For this model:
* <span id="ht1">target term</span>...
* <div class="hovertip" target="ht1">tooltip text</div>
*/
targetSelectByTargetAttribute = function(el, config) {
target_list = el.getAttribute('target');
if (target_list) {
// use for attribute to specify targets
target_ids = target_list.split(' ');
var selector = '#' + target_ids.join(',#');
return $(selector);
}
};
/**
* For this model:
* <span>target term</span><span class="hovertip">tooltip text</span>
*/
targetSelectByPrevious = function(el, config) {
return $(el.previousSibling);
}
/**
* Make all siblings targets. Experimental.
*/
targetSelectBySiblings = function(el, config) {
return $(el).siblings();
}
//// prepare tip elements ////
/**
* The tooltip element needs special preparation. You may define your own
* prepare functions to cusomize the behavior.
*/
// adds a close link to clicktips
clicktipPrepareWithCloseLink = function(o, config) {
return o.append("<a class='clicktip_close'><span>close</span></a>")
.find('a.clicktip_close').click(function(e) {
o.hide();
return false;
}).end();
};
// ensure that hovertips do not disappear when the mouse is over them.
// also position the hovertip as an absolutely positioned child of body.
hovertipPrepare = function(o, config) {
return o.hover(function() {
hovertipHideCancel(this);
}, function() {
hovertipHideLater(this);
}).css('position', 'absolute').each(hovertipPosition);
};
// do not modify tooltips when preparing
hovertipPrepareNoOp = function(o, config) {
return o;
}
//// manipulate tip elements /////
/**
* A variety of functions to modify tooltip elements
*/
// move tooltips to body, so they are not descended from other absolutely
// positioned elements.
hovertipPosition = function(i) {
document.body.appendChild(this);
}
hovertipIsVisible = function(el) {
return (jQuery.css(el, 'display') != 'none');
}
// show the tooltip under the mouse.
// Introduce a delay, so tip appears only if cursor rests on target for more than an instant.
hovertipShowUnderMouse = function(el) {
hovertipHideCancel(el);
if (!hovertipIsVisible(el)) {
el.ht.showing = // keep reference to timer
window.setTimeout(function() {
el.ht.tip.css({
'position':'absolute',
'top': hovertipMouseY + 'px',
'left': hovertipMouseX + 'px'})
.show();
}, el.ht.config.showDelay);
}
};
// do not hide
hovertipHideCancel = function(el) {
if (el.ht.hiding) {
window.clearTimeout(el.ht.hiding);
el.ht.hiding = null;
}
};
// Hide a tooltip, but only after a delay.
// The delay allow the tip to remain when user moves mouse from target to tooltip
hovertipHideLater = function(el) {
if (el.ht.showing) {
window.clearTimeout(el.ht.showing);
el.ht.showing = null;
}
if (el.ht.hiding) {
window.clearTimeout(el.ht.hiding);
el.ht.hiding = null;
}
el.ht.hiding =
window.setTimeout(function() {
if (el.ht.hiding) {
// fadeOut, slideUp do not work on Konqueror
el.ht.tip.hide();
}
}, el.ht.config.hideDelay);
};
//// prepare target elements ////
/**
* As we prepared the tooltip elements, the targets also need preparation.
*
* You may define your own custom behavior.
*/
// when clicked on target, toggle visibilty of tooltip
clicktipTargetPrepare = function(o, el, config) {
return o.addClass(config.attribute + '_target')
.click(function() {
el.ht.tip.toggle();
return false;
});
};
// when hover over target, make tooltip appear
hovertipTargetPrepare = function(o, el, config) {
return o.addClass(config.attribute + '_target')
.hover(function() {
// show tip when mouse over target
hovertipShowUnderMouse(el);
},
function() {
// hide the tip
// add a delay so user can move mouse from the target to the tip
hovertipHideLater(el);
});
};
/**
* hovertipActivate() is our jQuery plugin function. It turns on hovertip or
* clicktip behavior for a set of elements.
*
* @param config
* controls aspects of tooltip behavior. Be sure to define
* 'attribute', 'showDelay' and 'hideDelay'.
*
* @param targetSelect
* function finds the targets of a given tooltip element.
*
* @param tipPrepare
* function alters the tooltip to display and behave properly
*
* @param targetPrepare
* function alters the target to display and behave properly.
*/
jQuery.fn.hovertipActivate = function(config, targetSelect, tipPrepare, targetPrepare) {
//alert('activating ' + this.size());
// unhide so jquery show/hide will work.
return this.css('display', 'block')
.hide() // don't show it until click
.each(function() {
if (!this.ht)
this.ht = new Object();
this.ht.config = config;
// find our targets
var targets = targetSelect(this, config);
if (targets && targets.size()) {
if (!this.ht.targets)
this.ht.targets = targetPrepare(targets, this, config);
else
this.ht.targets.add(targetPrepare(targets, this, config));
// listen to mouse move events so we know exatly where to place hovetips
targets.mousemove(hovertipMouseUpdate);
// prepare the tooltip element
// is it bad form to call $(this) here?
if (!this.ht.tip)
this.ht.tip = tipPrepare($(this), config);
}
})
;
};
/**
* Here's an example ready function which shows how to enable tooltips.
*
* You can make this considerably shorter by choosing only the markup style(s)
* you will use.
*
* You may also remove the code that wraps hovertips to produce drop-shadow FX
*
* Invoke this function or one like it from your $(document).ready().
*
* Here, we break the action up into several timout callbacks, to avoid
* locking up browsers.
*/
function hovertipInit() {
// specify the attribute name we use for our clicktips
var clicktipConfig = {'attribute':'clicktip'};
/**
* To enable this style of markup (id on tooltip):
* <span clicktip="foo">target</span>...
* <div id="foo" class="clicktip">blah blah</div>
*/
window.setTimeout(function() {
$('.clicktip').hovertipActivate(clicktipConfig,
targetSelectById,
clicktipPrepareWithCloseLink,
clicktipTargetPrepare);
}, 0);
/**
* To enable this style of markup (id on target):
* <span id="foo">target</span>...
* <div target="foo" class="clicktip">blah blah</div>
*/
window.setTimeout(function() {
$('.clicktip').hovertipActivate(clicktipConfig,
targetSelectByTargetAttribute,
clicktipPrepareWithCloseLink,
clicktipTargetPrepare);
}, 0);
// specify our configuration for hovertips, including delay times (millisec)
var hovertipConfig = {'attribute':'hovertip',
'showDelay': 300,
'hideDelay': 700};
// use <div class='hovertip'>blah blah</div>
var hovertipSelect = 'div.hovertip';
// OPTIONAL: here we wrap each hovertip to apply special effect. (i.e. drop shadow):
$(hovertipSelect).css('display', 'block').addClass('hovertip_wrap3').
wrap("<div class='hovertip_wrap0'><div class='hovertip_wrap1'><div class='hovertip_wrap2'>" +
"</div></div></div>").each(function() {
// fix class and attributes for newly wrapped elements
var tooltip = this.parentNode.parentNode.parentNode;
if (this.getAttribute('target'))
tooltip.setAttribute('target', this.getAttribute('target'));
if (this.getAttribute('id')) {
var id = this.getAttribute('id');
this.removeAttribute('id');
tooltip.setAttribute('id', id);
}
});
hovertipSelect = 'div.hovertip_wrap0';
// end optional FX section
/**
* To enable this style of markup (id on tooltip):
* <span hovertip="foo">target</span>...
* <div id="foo" class="hovertip">blah blah</div>
*/
window.setTimeout(function() {
$(hovertipSelect).hovertipActivate(hovertipConfig,
targetSelectById,
hovertipPrepare,
hovertipTargetPrepare);
}, 0);
/**
* To enable this style of markup (id on target):
* <span id="foo">target</span>...
* <div target="foo" class="hovertip">blah blah</div>
*/
window.setTimeout(function() {
$(hovertipSelect).hovertipActivate(hovertipConfig,
targetSelectByTargetAttribute,
hovertipPrepare,
hovertipTargetPrepare);
}, 0);
/**
* This next section enables this style of markup:
* <foo><span>target</span><span class="hovertip">blah blah</span></foo>
*
* With drop shadow effect.
*
*/
var hovertipSpanSelect = 'span.hovertip';
// activate hovertips with wrappers for FX (drop shadow):
$(hovertipSpanSelect).css('display', 'block').addClass('hovertip_wrap3').
wrap("<span class='hovertip_wrap0'><span class='hovertip_wrap1'><span class='hovertip_wrap2'>" +
"</span></span></span>").each(function() {
// fix class and attributes for newly wrapped elements
var tooltip = this.parentNode.parentNode.parentNode;
if (this.getAttribute('target'))
tooltip.setAttribute('target', this.getAttribute('target'));
if (this.getAttribute('id')) {
var id = this.getAttribute('id');
this.removeAttribute('id');
tooltip.setAttribute('id', id);
}
});
hovertipSpanSelect = 'span.hovertip_wrap0';
window.setTimeout(function() {
$(hovertipSpanSelect)
.hovertipActivate(hovertipConfig,
targetSelectByPrevious,
hovertipPrepare,
hovertipTargetPrepare);
}, 0);
}