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