/* * @name BeautyTips * @desc a tooltips/baloon-help plugin for jQuery * * @author Jeff Robbins - Lullabot - http://www.lullabot.com * @version 0.9.3 (4/19/2009) */ /* * @type jQuery * @cat Plugins/bt * @requires jQuery v1.2+ (not tested on versions prior to 1.2.6) * * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * * Encourage development. If you use BeautyTips for anything cool * or on a site that people have heard of, please drop me a note. * - jeff ^at lullabot > com * * No guarantees, warranties, or promises of any kind * */ ;(function($) { /** * @credit Inspired by Karl Swedberg's ClueTip * (http://plugins.learningjquery.com/cluetip/), which in turn was inspired * by Cody Lindley's jTip (http://www.codylindley.com) * * @fileoverview * Beauty Tips is a jQuery tooltips plugin which uses the canvas drawing element * in the HTML5 spec in order to dynamically draw tooltip "talk bubbles" around * the descriptive help text associated with an item. This is in many ways * similar to Google Maps which both provides similar talk-bubbles and uses the * canvas element to draw them. * * The canvas element is supported in modern versions of FireFox, Safari, and * Opera. However, Internet Explorer needs a separate library called ExplorerCanvas * included on the page in order to support canvas drawing functions. ExplorerCanvas * was created by Google for use with their web apps and you can find it here: * http://excanvas.sourceforge.net/ * * Beauty Tips was written to be simple to use and pretty. All of its options * are documented at the bottom of this file and defaults can be overwritten * globally for the entire page, or individually on each call. * * By default each tooltip will be positioned on the side of the target element * which has the most free space. This is affected by the scroll position and * size of the current window, so each Beauty Tip is redrawn each time it is * displayed. It may appear above an element at the bottom of the page, but when * the page is scrolled down (and the element is at the top of the page) it will * then appear below it. Additionally, positions can be forced or a preferred * order can be defined. See examples below. * * To fix z-index problems in IE6, include the bgiframe plugin on your page * http://plugins.jquery.com/project/bgiframe - BeautyTips will automatically * recognize it and use it. * * BeautyTips also works with the hoverIntent plugin * http://cherne.net/brian/resources/jquery.hoverIntent.html * see hoverIntent example below for usage * * Usage * The function can be called in a number of ways. * $(selector).bt(); * $(selector).bt('Content text'); * $(selector).bt('Content text', {option1: value, option2: value}); * $(selector).bt({option1: value, option2: value}); * * For more/better documentation and lots of examples, visit the demo page included with the distribution * */ jQuery.fn.bt = function(content, options) { if (typeof content != 'string') { var contentSelect = true; options = content; content = false; } else { var contentSelect = false; } // if hoverIntent is installed, use that as default instead of hover if (jQuery.fn.hoverIntent && jQuery.bt.defaults.trigger == 'hover') { jQuery.bt.defaults.trigger = 'hoverIntent'; } return this.each(function(index) { var opts = jQuery.extend(false, jQuery.bt.defaults, options); // clean up the options opts.spikeLength = numb(opts.spikeLength); opts.spikeGirth = numb(opts.spikeGirth); opts.overlap = numb(opts.overlap); var ajaxTimeout = false; /** * This is sort of the "starting spot" for the this.each() * These are sort of the init functions to handle the call */ if (opts.killTitle) { $(this).find('[title]').andSelf().each(function() { if (!$(this).attr('bt-xTitle')) { $(this).attr('bt-xTitle', $(this).attr('title')).attr('title', ''); } }); } if (typeof opts.trigger == 'string') { opts.trigger = [opts.trigger]; } if (opts.trigger[0] == 'hoverIntent') { var hoverOpts = jQuery.extend(opts.hoverIntentOpts, { over: function() { this.btOn(); }, out: function() { this.btOff(); }}); $(this).hoverIntent(hoverOpts); } else if (opts.trigger[0] == 'hover') { $(this).hover( function() { this.btOn(); }, function() { this.btOff(); } ); } else if (opts.trigger[0] == 'now') { // toggle the on/off right now // note that 'none' gives more control (see below) if ($(this).hasClass('bt-active')) { this.btOff(); } else { this.btOn(); } } else if (opts.trigger[0] == 'none') { // initialize the tip with no event trigger // use javascript to turn on/off tip as follows: // $('#selector').btOn(); // $('#selector').btOff(); } else if (opts.trigger.length > 1 && opts.trigger[0] != opts.trigger[1]) { $(this) .bind(opts.trigger[0], function() { this.btOn(); }) .bind(opts.trigger[1], function() { this.btOff(); }); } else { // toggle using the same event $(this).bind(opts.trigger[0], function() { if ($(this).hasClass('bt-active')) { this.btOff(); } else { this.btOn(); } }); } /** * The BIG TURN ON * Any element that has been initiated */ this.btOn = function () { if (typeof $(this).data('bt-box') == 'object') { // if there's already a popup, remove it before creating a new one. this.btOff(); } // trigger preShow function opts.preShow.apply(this); // turn off other tips $(jQuery.bt.vars.closeWhenOpenStack).btOff(); // add the class to the target element (for hilighting, for example) // bt-active is always applied to all, but activeClass can apply another $(this).addClass('bt-active ' + opts.activeClass); if (contentSelect && opts.ajaxPath == null) { // bizarre, I know if (opts.killTitle) { // if we've killed the title attribute, it's been stored in 'bt-xTitle' so get it.. $(this).attr('title', $(this).attr('bt-xTitle')); } // then evaluate the selector... title is now in place content = eval(opts.contentSelector); if (opts.killTitle) { // now remove the title again, so we don't get double tips $(this).attr('title', ''); } } // ---------------------------------------------- // All the Ajax(ish) stuff is in this next bit... // ---------------------------------------------- if (opts.ajaxPath != null && content == false) { if (typeof opts.ajaxPath == 'object') { var url = eval(opts.ajaxPath[0]); url += opts.ajaxPath[1] ? ' ' + opts.ajaxPath[1] : ''; } else { var url = opts.ajaxPath; } var off = url.indexOf(" "); if ( off >= 0 ) { var selector = url.slice(off, url.length); url = url.slice(0, off); } // load any data cached for the given ajax path var cacheData = opts.ajaxCache ? $(document.body).data('btCache-' + url.replace(/\./g, '')) : null; if (typeof cacheData == 'string') { content = selector ? $("
").append(cacheData.replace(/