annotate js/bt/jquery.bt.js @ 47:cbfe386cb51b

Add a function to refresh opened libraries. The source URL of each libraries is now tracked, which allows for auto refreshing the libraries based on various events. The obvious use case is to refresh the library when an atom has been added to Scald, for example via a Popups dialog.
author Franck Deroche <defr@ows.fr>
date Mon, 15 Feb 2010 14:08:04 +0000
parents 0d557e6e73f7
children
rev   line source
eads@18 1 /*
eads@18 2 * @name BeautyTips
eads@18 3 * @desc a tooltips/baloon-help plugin for jQuery
eads@18 4 *
eads@18 5 * @author Jeff Robbins - Lullabot - http://www.lullabot.com
eads@18 6 * @version 0.9.1 (2/15/2009)
eads@18 7 *
eads@18 8 * @type jQuery
eads@18 9 * @cat Plugins/bt
eads@18 10 * @requires jQuery v1.2+ (not tested on versions prior to 1.2.6)
eads@18 11 *
eads@18 12 * Dual licensed under the MIT and GPL licenses:
eads@18 13 * http://www.opensource.org/licenses/mit-license.php
eads@18 14 * http://www.gnu.org/licenses/gpl.html
eads@18 15 *
eads@18 16 * Encourage development. If you use BeautyTips for anything cool
eads@18 17 * or on a site that people have heard of, please drop me a note.
eads@18 18 * - jeff ^at lullabot > com
eads@18 19 *
eads@18 20 * No guarantees, warranties, or promises of any kind
eads@18 21 *
eads@18 22 */
eads@18 23
eads@18 24 /**
eads@18 25 * @credit Inspired by Karl Swedberg's ClueTip
eads@18 26 * (http://plugins.learningjquery.com/cluetip/), which in turn was inspired
eads@18 27 * by Cody Lindley's jTip (http://www.codylindley.com)
eads@18 28 *
eads@18 29 * @fileoverview
eads@18 30 * Beauty Tips is a jQuery tooltips plugin which uses the canvas drawing element
eads@18 31 * in the HTML5 spec in order to dynamically draw tooltip "talk bubbles" around
eads@18 32 * the descriptive help text associated with an item. This is in many ways
eads@18 33 * similar to Google Maps which both provides similar talk-bubbles and uses the
eads@18 34 * canvas element to draw them.
eads@18 35 *
eads@18 36 * The canvas element is supported in modern versions of FireFox, Safari, and
eads@18 37 * Opera. However, Internet Explorer needs a separate library called ExplorerCanvas
eads@18 38 * included on the page in order to support canvas drawing functions. ExplorerCanvas
eads@18 39 * was created by Google for use with their web apps and you can find it here:
eads@18 40 * http://excanvas.sourceforge.net/
eads@18 41 *
eads@18 42 * Beauty Tips was written to be simple to use and pretty. All of its options
eads@18 43 * are documented at the bottom of this file and defaults can be overwritten
eads@18 44 * globally for the entire page, or individually on each call.
eads@18 45 *
eads@18 46 * By default each tooltip will be positioned on the side of the target element
eads@18 47 * which has the most free space. This is affected by the scroll position and
eads@18 48 * size of the current window, so each Beauty Tip is redrawn each time it is
eads@18 49 * displayed. It may appear above an element at the bottom of the page, but when
eads@18 50 * the page is scrolled down (and the element is at the top of the page) it will
eads@18 51 * then appear below it. Additionally, positions can be forced or a preferred
eads@18 52 * order can be defined. See examples below.
eads@18 53 *
eads@18 54 * To fix z-index problems in IE6, include the bgiframe plugin on your page
eads@18 55 * http://plugins.jquery.com/project/bgiframe - BeautyTips will automatically
eads@18 56 * recognize it and use it.
eads@18 57 *
eads@18 58 * BeautyTips also works with the hoverIntent plugin
eads@18 59 * http://cherne.net/brian/resources/jquery.hoverIntent.html
eads@18 60 * see hoverIntent example below for usage
eads@18 61 *
eads@18 62 * Usage
eads@18 63 * The function can be called in a number of ways.
eads@18 64 * $(selector).bt();
eads@18 65 * $(selector).bt('Content text');
eads@18 66 * $(selector).bt('Content text', {option1: value, option2: value});
eads@18 67 * $(selector).bt({option1: value, option2: value});
eads@18 68 *
eads@18 69 * For more/better documentation and lots of examples, visit the demo page included with the distribution
eads@18 70 *
eads@18 71 */
eads@18 72 jQuery.fn.bt = function(content, options) {
eads@18 73
eads@18 74 if (typeof content != 'string') {
eads@18 75 var contentSelect = true;
eads@18 76 options = content;
eads@18 77 content = false;
eads@18 78 }
eads@18 79 else {
eads@18 80 var contentSelect = false;
eads@18 81 }
eads@18 82
eads@18 83 // if hoverIntent is installed, use that as default instead of hover
eads@18 84 if (jQuery.fn.hoverIntent && jQuery.bt.defaults.trigger == 'hover') {
eads@18 85 jQuery.bt.defaults.trigger = 'hoverIntent';
eads@18 86 }
eads@18 87
eads@18 88 return this.each(function(index) {
eads@18 89
eads@18 90 var opts = jQuery.extend(false, jQuery.bt.defaults, options);
eads@18 91
eads@18 92 // clean up the options
eads@18 93 opts.spikeLength = numb(opts.spikeLength);
eads@18 94 opts.spikeGirth = numb(opts.spikeGirth);
eads@18 95 opts.overlap = numb(opts.overlap);
eads@18 96
eads@18 97 var ajaxTimeout = false;
eads@18 98
eads@18 99 /**
eads@18 100 * This is sort of the "starting spot" for the this.each()
eads@18 101 * These are sort of the init functions to handle the call
eads@18 102 */
eads@18 103
eads@18 104 if (opts.killTitle) {
eads@18 105 $(this).find('[title]').andSelf().each(function() {
eads@18 106 if (!$(this).attr('bt-xTitle')) {
eads@18 107 $(this).attr('bt-xTitle', $(this).attr('title')).attr('title', '');
eads@18 108 }
eads@18 109 });
eads@18 110 }
eads@18 111
eads@18 112 if (typeof opts.trigger == 'string') {
eads@18 113 opts.trigger = [opts.trigger];
eads@18 114 }
eads@18 115 if (opts.trigger[0] == 'hoverIntent') {
eads@18 116 var hoverOpts = $.extend(opts.hoverIntentOpts, {
eads@18 117 over: function() {
eads@18 118 this.btOn();
eads@18 119 },
eads@18 120 out: function() {
eads@18 121 this.btOff();
eads@18 122 }});
eads@18 123 $(this).hoverIntent(hoverOpts);
eads@18 124
eads@18 125 }
eads@18 126 else if (opts.trigger[0] == 'hover') {
eads@18 127 $(this).hover(
eads@18 128 function() {
eads@18 129 this.btOn();
eads@18 130 },
eads@18 131 function() {
eads@18 132 this.btOff();
eads@18 133 }
eads@18 134 );
eads@18 135 }
eads@18 136 else if (opts.trigger[0] == 'now') {
eads@18 137 // toggle the on/off right now
eads@18 138 // note that 'none' gives more control (see below)
eads@18 139 if ($(this).hasClass('bt-active')) {
eads@18 140 this.btOff();
eads@18 141 }
eads@18 142 else {
eads@18 143 this.btOn();
eads@18 144 }
eads@18 145 }
eads@18 146 else if (opts.trigger[0] == 'none') {
eads@18 147 // initialize the tip with no event trigger
eads@18 148 // use javascript to turn on/off tip as follows:
eads@18 149 // $('#selector').btOn();
eads@18 150 // $('#selector').btOff();
eads@18 151 }
eads@18 152 else if (opts.trigger.length > 1 && opts.trigger[0] != opts.trigger[1]) {
eads@18 153 $(this)
eads@18 154 .bind(opts.trigger[0], function() {
eads@18 155 this.btOn();
eads@18 156 })
eads@18 157 .bind(opts.trigger[1], function() {
eads@18 158 this.btOff();
eads@18 159 });
eads@18 160 }
eads@18 161 else {
eads@18 162 // toggle using the same event
eads@18 163 $(this).bind(opts.trigger[0], function() {
eads@18 164 if ($(this).hasClass('bt-active')) {
eads@18 165 this.btOff();
eads@18 166 }
eads@18 167 else {
eads@18 168 this.btOn();
eads@18 169 }
eads@18 170 });
eads@18 171 }
eads@18 172
eads@18 173
eads@18 174 /**
eads@18 175 * The BIG TURN ON
eads@18 176 * Any element that has been initiated
eads@18 177 */
eads@18 178 this.btOn = function () {
eads@18 179 if (typeof $(this).data('bt-box') == 'object') {
eads@18 180 // if there's already a popup, remove it before creating a new one.
eads@18 181 this.btOff();
eads@18 182 }
eads@18 183
eads@18 184 // trigger preShow function
eads@18 185 opts.preShow.apply(this);
eads@18 186
eads@18 187 // turn off other tips
eads@18 188 $(jQuery.bt.vars.closeWhenOpenStack).btOff();
eads@18 189
eads@18 190 // add the class to the target element (for hilighting, for example)
eads@18 191 // bt-active is always applied to all, but activeClass can apply another
eads@18 192 $(this).addClass('bt-active ' + opts.activeClass);
eads@18 193
eads@18 194 if (contentSelect && opts.ajaxPath == null) {
eads@18 195 // bizarre, I know
eads@18 196 if (opts.killTitle) {
eads@18 197 // if we've killed the title attribute, it's been stored in 'bt-xTitle' so get it..
eads@18 198 $(this).attr('title', $(this).attr('bt-xTitle'));
eads@18 199 }
eads@18 200 // then evaluate the selector... title is now in place
eads@18 201 content = eval(opts.contentSelector);
eads@18 202 if (opts.killTitle) {
eads@18 203 // now remove the title again, so we don't get double tips
eads@18 204 $(this).attr('title', '');
eads@18 205 }
eads@18 206 }
eads@18 207
eads@18 208 // ----------------------------------------------
eads@18 209 // All the Ajax(ish) stuff is in this next bit...
eads@18 210 // ----------------------------------------------
eads@18 211 if (opts.ajaxPath != null && content == false) {
eads@18 212 if (typeof opts.ajaxPath == 'object') {
eads@18 213 var url = eval(opts.ajaxPath[0]);
eads@18 214 url += opts.ajaxPath[1] ? ' ' + opts.ajaxPath[1] : '';
eads@18 215 }
eads@18 216 else {
eads@18 217 var url = opts.ajaxPath;
eads@18 218 }
eads@18 219 var off = url.indexOf(" ");
eads@18 220 if ( off >= 0 ) {
eads@18 221 var selector = url.slice(off, url.length);
eads@18 222 url = url.slice(0, off);
eads@18 223 }
eads@18 224
eads@18 225 // load any data cached for the given ajax path
eads@18 226 var cacheData = opts.ajaxCache ? $(document.body).data('btCache-' + url.replace(/\./g, '')) : null;
eads@18 227 if (typeof cacheData == 'string') {
eads@18 228 content = selector ? jQuery("<div/>").append(cacheData.replace(/<script(.|\s)*?\/script>/g, "")).find(selector) : cacheData;
eads@18 229 }
eads@18 230 else {
eads@18 231 var target = this;
eads@18 232
eads@18 233 // set up the options
eads@18 234 var ajaxOpts = jQuery.extend(false,
eads@18 235 {
eads@18 236 type: opts.ajaxType,
eads@18 237 data: opts.ajaxData,
eads@18 238 cache: opts.ajaxCache,
eads@18 239 url: url,
eads@18 240 complete: function(XMLHttpRequest, textStatus) {
eads@18 241 if (textStatus == 'success' || textStatus == 'notmodified') {
eads@18 242 if (opts.ajaxCache) {
eads@18 243 $(document.body).data('btCache-' + url.replace(/\./g, ''), XMLHttpRequest.responseText);
eads@18 244 }
eads@18 245 ajaxTimeout = false;
eads@18 246 content = selector ?
eads@18 247 // Create a dummy div to hold the results
eads@18 248 jQuery("<div/>")
eads@18 249 // inject the contents of the document in, removing the scripts
eads@18 250 // to avoid any 'Permission Denied' errors in IE
eads@18 251 .append(XMLHttpRequest.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
eads@18 252
eads@18 253 // Locate the specified elements
eads@18 254 .find(selector) :
eads@18 255
eads@18 256 // If not, just inject the full result
eads@18 257 XMLHttpRequest.responseText;
eads@18 258
eads@18 259 }
eads@18 260 else {
eads@18 261 if (textStatus == 'timeout') {
eads@18 262 // if there was a timeout, we don't cache the result
eads@18 263 ajaxTimeout = true;
eads@18 264 }
eads@18 265 content = opts.ajaxError.replace(/%error/g, XMLHttpRequest.statusText);
eads@18 266 }
eads@18 267 // if the user rolls out of the target element before the ajax request comes back, don't show it
eads@18 268 if ($(target).hasClass('bt-active')) {
eads@18 269 target.btOn();
eads@18 270 }
eads@18 271 }
eads@18 272 }, opts.ajaxData);
eads@18 273 // do the ajax request
eads@18 274 $.ajax(ajaxOpts);
eads@18 275 // load the throbber while the magic happens
eads@18 276 content = opts.ajaxLoading;
eads@18 277 }
eads@18 278 }
eads@18 279 // </ ajax stuff >
eads@18 280
eads@18 281
eads@18 282 // now we start actually figuring out where to place the tip
eads@18 283
eads@18 284 var offsetParent = $(this).offsetParent();
eads@18 285 var pos = $(this).btPosition();
eads@18 286 // top, left, width, and height values of the target element
eads@18 287 var top = numb(pos.top) + numb($(this).css('margin-top')); // IE can return 'auto' for margins
eads@18 288 var left = numb(pos.left) + numb($(this).css('margin-left'));
eads@18 289 var width = $(this).outerWidth();
eads@18 290 var height = $(this).outerHeight();
eads@18 291
eads@18 292 if (typeof content == 'object') {
eads@18 293 // if content is a DOM object (as opposed to text)
eads@18 294 // use a clone, rather than removing the original element
eads@18 295 // and ensure that it's visible
eads@18 296 content = $(content).clone(true).show();
eads@18 297
eads@18 298 }
eads@18 299
eads@18 300 // create the tip content div, populate it, and style it
eads@18 301 var $text = $('<div class="bt-content"></div>').append(content).css({padding: opts.padding, position: 'absolute', width: opts.width, zIndex: opts.textzIndex}).css(opts.cssStyles);
eads@18 302 // create the wrapping box which contains text and canvas
eads@18 303 // put the content in it, style it, and append it to the same offset parent as the target
eads@18 304 var $box = $('<div class="bt-wrapper"></div>').append($text).addClass(opts.cssClass).css({position: 'absolute', width: opts.width, zIndex: opts.wrapperzIndex}).appendTo(offsetParent);
eads@18 305
eads@18 306 // use bgiframe to get around z-index problems in IE6
eads@18 307 // http://plugins.jquery.com/project/bgiframe
eads@18 308 if ($.fn.bgiframe) {
eads@18 309 $text.bgiframe();
eads@18 310 $box.bgiframe();
eads@18 311 }
eads@18 312
eads@18 313 $(this).data('bt-box', $box);
eads@18 314
eads@18 315 // see if the text box will fit in the various positions
eads@18 316 var scrollTop = numb($(document).scrollTop());
eads@18 317 var scrollLeft = numb($(document).scrollLeft());
eads@18 318 var docWidth = numb($(window).width());
eads@18 319 var docHeight = numb($(window).height());
eads@18 320 var winRight = scrollLeft + docWidth;
eads@18 321 var winBottom = scrollTop + docHeight;
eads@18 322 var space = new Object();
eads@18 323 space.top = $(this).offset().top - scrollTop;
eads@18 324 space.bottom = docHeight - (($(this).offset().top + height) - scrollTop);
eads@18 325 space.left = $(this).offset().left - scrollLeft;
eads@18 326 space.right = docWidth - (($(this).offset().left + width) - scrollLeft);
eads@18 327 var textOutHeight = numb($text.outerHeight());
eads@18 328 var textOutWidth = numb($text.outerWidth());
eads@18 329 if (opts.positions.constructor == String) {
eads@18 330 opts.positions = opts.positions.replace(/ /, '').split(',');
eads@18 331 }
eads@18 332 if (opts.positions[0] == 'most') {
eads@18 333 // figure out which is the largest
eads@18 334 var position = 'top'; // prime the pump
eads@18 335 for (var pig in space) { // pigs in space!
eads@18 336 position = space[pig] > space[position] ? pig : position;
eads@18 337 }
eads@18 338 }
eads@18 339 else {
eads@18 340 for (var x in opts.positions) {
eads@18 341 var position = opts.positions[x];
eads@18 342 if ((position == 'left' || position == 'right') && space[position] > textOutWidth + opts.spikeLength) {
eads@18 343 break;
eads@18 344 }
eads@18 345 else if ((position == 'top' || position == 'bottom') && space[position] > textOutHeight + opts.spikeLength) {
eads@18 346 break;
eads@18 347 }
eads@18 348 }
eads@18 349 }
eads@18 350
eads@18 351 // horizontal (left) offset for the box
eads@18 352 var horiz = left + ((width - textOutWidth) * .5);
eads@18 353 // vertical (top) offset for the box
eads@18 354 var vert = top + ((height - textOutHeight) * .5);
eads@18 355 var animDist = opts.animate ? numb(opts.distance) : 0;
eads@18 356 var points = new Array();
eads@18 357 var textTop, textLeft, textRight, textBottom, textTopSpace, textBottomSpace, textLeftSpace, textRightSpace, crossPoint, textCenter, spikePoint;
eads@18 358
eads@18 359 // Yes, yes, this next bit really could use to be condensed
eads@18 360 // each switch case is basically doing the same thing in slightly different ways
eads@18 361 switch(position) {
eads@18 362 case 'top':
eads@18 363 // spike on bottom
eads@18 364 $text.css('margin-bottom', opts.spikeLength + 'px');
eads@18 365 $box.css({top: (top - $text.outerHeight(true) - animDist) + opts.overlap, left: horiz});
eads@18 366 // move text left/right if extends out of window
eads@18 367 textRightSpace = (winRight - opts.windowMargin) - ($text.offset().left + $text.outerWidth(true));
eads@18 368 var xShift = 0;
eads@18 369 if (textRightSpace < 0) {
eads@18 370 // shift it left
eads@18 371 $box.css('left', (numb($box.css('left')) + textRightSpace) + 'px');
eads@18 372 xShift -= textRightSpace;
eads@18 373 }
eads@18 374 // we test left space second to ensure that left of box is visible
eads@18 375 textLeftSpace = ($text.offset().left + numb($text.css('margin-left'))) - (scrollLeft + opts.windowMargin);
eads@18 376 if (textLeftSpace < 0) {
eads@18 377 // shift it right
eads@18 378 $box.css('left', (numb($box.css('left')) - textLeftSpace) + 'px');
eads@18 379 xShift += textLeftSpace;
eads@18 380 }
eads@18 381 textTop = $text.btPosition().top + numb($text.css('margin-top'));
eads@18 382 textLeft = $text.btPosition().left + numb($text.css('margin-left'));
eads@18 383 textRight = textLeft + $text.outerWidth();
eads@18 384 textBottom = textTop + $text.outerHeight();
eads@18 385 textCenter = {x: textLeft + ($text.outerWidth()*opts.centerPointX), y: textTop + ($text.outerHeight()*opts.centerPointY)};
eads@18 386 // points[points.length] = {x: x, y: y};
eads@18 387 points[points.length] = spikePoint = {y: textBottom + opts.spikeLength, x: ((textRight-textLeft) * .5) + xShift, type: 'spike'};
eads@18 388 crossPoint = findIntersectX(spikePoint.x, spikePoint.y, textCenter.x, textCenter.y, textBottom);
eads@18 389 // make sure that the crossPoint is not outside of text box boundaries
eads@18 390 crossPoint.x = crossPoint.x < textLeft + opts.spikeGirth/2 + opts.cornerRadius ? textLeft + opts.spikeGirth/2 + opts.cornerRadius : crossPoint.x;
eads@18 391 crossPoint.x = crossPoint.x > (textRight - opts.spikeGirth/2) - opts.cornerRadius ? (textRight - opts.spikeGirth/2) - opts.CornerRadius : crossPoint.x;
eads@18 392 points[points.length] = {x: crossPoint.x - (opts.spikeGirth/2), y: textBottom, type: 'join'};
eads@18 393 points[points.length] = {x: textLeft, y: textBottom, type: 'corner'}; // left bottom corner
eads@18 394 points[points.length] = {x: textLeft, y: textTop, type: 'corner'}; // left top corner
eads@18 395 points[points.length] = {x: textRight, y: textTop, type: 'corner'}; // right top corner
eads@18 396 points[points.length] = {x: textRight, y: textBottom, type: 'corner'}; // right bottom corner
eads@18 397 points[points.length] = {x: crossPoint.x + (opts.spikeGirth/2), y: textBottom, type: 'join'};
eads@18 398 points[points.length] = spikePoint;
eads@18 399 break;
eads@18 400 case 'left':
eads@18 401 // spike on right
eads@18 402 $text.css('margin-right', opts.spikeLength + 'px');
eads@18 403 $box.css({top: vert + 'px', left: ((left - $text.outerWidth(true) - animDist) + opts.overlap) + 'px'});
eads@18 404 // move text up/down if extends out of window
eads@18 405 textBottomSpace = (winBottom - opts.windowMargin) - ($text.offset().top + $text.outerHeight(true));
eads@18 406 var yShift = 0;
eads@18 407 if (textBottomSpace < 0) {
eads@18 408 // shift it up
eads@18 409 $box.css('top', (numb($box.css('top')) + textBottomSpace) + 'px');
eads@18 410 yShift -= textBottomSpace;
eads@18 411 }
eads@18 412 // we ensure top space second to ensure that top of box is visible
eads@18 413 textTopSpace = ($text.offset().top + numb($text.css('margin-top'))) - (scrollTop + opts.windowMargin);
eads@18 414 if (textTopSpace < 0) {
eads@18 415 // shift it down
eads@18 416 $box.css('top', (numb($box.css('top')) - textTopSpace) + 'px');
eads@18 417 yShift += textTopSpace;
eads@18 418 }
eads@18 419 textTop = $text.btPosition().top + numb($text.css('margin-top'));
eads@18 420 textLeft = $text.btPosition().left + numb($text.css('margin-left'));
eads@18 421 textRight = textLeft + $text.outerWidth();
eads@18 422 textBottom = textTop + $text.outerHeight();
eads@18 423 textCenter = {x: textLeft + ($text.outerWidth()*opts.centerPointX), y: textTop + ($text.outerHeight()*opts.centerPointY)};
eads@18 424 points[points.length] = spikePoint = {x: textRight + opts.spikeLength, y: ((textBottom-textTop) * .5) + yShift, type: 'spike'};
eads@18 425 crossPoint = findIntersectY(spikePoint.x, spikePoint.y, textCenter.x, textCenter.y, textRight);
eads@18 426 // make sure that the crossPoint is not outside of text box boundaries
eads@18 427 crossPoint.y = crossPoint.y < textTop + opts.spikeGirth/2 + opts.cornerRadius ? textTop + opts.spikeGirth/2 + opts.cornerRadius : crossPoint.y;
eads@18 428 crossPoint.y = crossPoint.y > (textBottom - opts.spikeGirth/2) - opts.cornerRadius ? (textBottom - opts.spikeGirth/2) - opts.cornerRadius : crossPoint.y;
eads@18 429 points[points.length] = {x: textRight, y: crossPoint.y + opts.spikeGirth/2, type: 'join'};
eads@18 430 points[points.length] = {x: textRight, y: textBottom, type: 'corner'}; // right bottom corner
eads@18 431 points[points.length] = {x: textLeft, y: textBottom, type: 'corner'}; // left bottom corner
eads@18 432 points[points.length] = {x: textLeft, y: textTop, type: 'corner'}; // left top corner
eads@18 433 points[points.length] = {x: textRight, y: textTop, type: 'corner'}; // right top corner
eads@18 434 points[points.length] = {x: textRight, y: crossPoint.y - opts.spikeGirth/2, type: 'join'};
eads@18 435 points[points.length] = spikePoint;
eads@18 436 break;
eads@18 437 case 'bottom':
eads@18 438 // spike on top
eads@18 439 $text.css('margin-top', opts.spikeLength + 'px');
eads@18 440 $box.css({top: (top + height + animDist) - opts.overlap, left: horiz});
eads@18 441 // move text up/down if extends out of window
eads@18 442 textRightSpace = (winRight - opts.windowMargin) - ($text.offset().left + $text.outerWidth(true));
eads@18 443 var xShift = 0;
eads@18 444 if (textRightSpace < 0) {
eads@18 445 // shift it left
eads@18 446 $box.css('left', (numb($box.css('left')) + textRightSpace) + 'px');
eads@18 447 xShift -= textRightSpace;
eads@18 448 }
eads@18 449 // we ensure left space second to ensure that left of box is visible
eads@18 450 textLeftSpace = ($text.offset().left + numb($text.css('margin-left'))) - (scrollLeft + opts.windowMargin);
eads@18 451 if (textLeftSpace < 0) {
eads@18 452 // shift it right
eads@18 453 $box.css('left', (numb($box.css('left')) - textLeftSpace) + 'px');
eads@18 454 xShift += textLeftSpace;
eads@18 455 }
eads@18 456 textTop = $text.btPosition().top + numb($text.css('margin-top'));
eads@18 457 textLeft = $text.btPosition().left + numb($text.css('margin-left'));
eads@18 458 textRight = textLeft + $text.outerWidth();
eads@18 459 textBottom = textTop + $text.outerHeight();
eads@18 460 textCenter = {x: textLeft + ($text.outerWidth()*opts.centerPointX), y: textTop + ($text.outerHeight()*opts.centerPointY)};
eads@18 461 points[points.length] = spikePoint = {x: ((textRight-textLeft) * .5) + xShift, y: 0, type: 'spike'};
eads@18 462 crossPoint = findIntersectX(spikePoint.x, spikePoint.y, textCenter.x, textCenter.y, textTop);
eads@18 463 // make sure that the crossPoint is not outside of text box boundaries
eads@18 464 crossPoint.x = crossPoint.x < textLeft + opts.spikeGirth/2 + opts.cornerRadius ? textLeft + opts.spikeGirth/2 + opts.cornerRadius : crossPoint.x;
eads@18 465 crossPoint.x = crossPoint.x > (textRight - opts.spikeGirth/2) - opts.cornerRadius ? (textRight - opts.spikeGirth/2) - opts.cornerRadius : crossPoint.x;
eads@18 466 points[points.length] = {x: crossPoint.x + opts.spikeGirth/2, y: textTop, type: 'join'};
eads@18 467 points[points.length] = {x: textRight, y: textTop, type: 'corner'}; // right top corner
eads@18 468 points[points.length] = {x: textRight, y: textBottom, type: 'corner'}; // right bottom corner
eads@18 469 points[points.length] = {x: textLeft, y: textBottom, type: 'corner'}; // left bottom corner
eads@18 470 points[points.length] = {x: textLeft, y: textTop, type: 'corner'}; // left top corner
eads@18 471 points[points.length] = {x: crossPoint.x - (opts.spikeGirth/2), y: textTop, type: 'join'};
eads@18 472 points[points.length] = spikePoint;
eads@18 473 break;
eads@18 474 case 'right':
eads@18 475 // spike on left
eads@18 476 $text.css('margin-left', (opts.spikeLength + 'px'));
eads@18 477 $box.css({top: vert + 'px', left: ((left + width + animDist) - opts.overlap) + 'px'});
eads@18 478 // move text up/down if extends out of window
eads@18 479 textBottomSpace = (winBottom - opts.windowMargin) - ($text.offset().top + $text.outerHeight(true));
eads@18 480 var yShift = 0;
eads@18 481 if (textBottomSpace < 0) {
eads@18 482 // shift it up
eads@18 483 $box.css('top', (numb($box.css('top')) + textBottomSpace) + 'px');
eads@18 484 yShift -= textBottomSpace;
eads@18 485 }
eads@18 486 // we ensure top space second to ensure that top of box is visible
eads@18 487 textTopSpace = ($text.offset().top + numb($text.css('margin-top'))) - (scrollTop + opts.windowMargin);
eads@18 488 if (textTopSpace < 0) {
eads@18 489 // shift it down
eads@18 490 $box.css('top', (numb($box.css('top')) - textTopSpace) + 'px');
eads@18 491 yShift += textTopSpace;
eads@18 492 }
eads@18 493 textTop = $text.btPosition().top + numb($text.css('margin-top'));
eads@18 494 textLeft = $text.btPosition().left + numb($text.css('margin-left'));
eads@18 495 textRight = textLeft + $text.outerWidth();
eads@18 496 textBottom = textTop + $text.outerHeight();
eads@18 497 textCenter = {x: textLeft + ($text.outerWidth()*opts.centerPointX), y: textTop + ($text.outerHeight()*opts.centerPointY)};
eads@18 498 points[points.length] = spikePoint = {x: 0, y: ((textBottom-textTop) * .5) + yShift, type: 'spike'};
eads@18 499 crossPoint = findIntersectY(spikePoint.x, spikePoint.y, textCenter.x, textCenter.y, textLeft);
eads@18 500 // make sure that the crossPoint is not outside of text box boundaries
eads@18 501 crossPoint.y = crossPoint.y < textTop + opts.spikeGirth/2 + opts.cornerRadius ? textTop + opts.spikeGirth/2 + opts.cornerRadius : crossPoint.y;
eads@18 502 crossPoint.y = crossPoint.y > (textBottom - opts.spikeGirth/2) - opts.cornerRadius ? (textBottom - opts.spikeGirth/2) - opts.cornerRadius : crossPoint.y;
eads@18 503 points[points.length] = {x: textLeft, y: crossPoint.y - opts.spikeGirth/2, type: 'join'};
eads@18 504 points[points.length] = {x: textLeft, y: textTop, type: 'corner'}; // left top corner
eads@18 505 points[points.length] = {x: textRight, y: textTop, type: 'corner'}; // right top corner
eads@18 506 points[points.length] = {x: textRight, y: textBottom, type: 'corner'}; // right bottom corner
eads@18 507 points[points.length] = {x: textLeft, y: textBottom, type: 'corner'}; // left bottom corner
eads@18 508 points[points.length] = {x: textLeft, y: crossPoint.y + opts.spikeGirth/2, type: 'join'};
eads@18 509 points[points.length] = spikePoint;
eads@18 510 break;
eads@18 511 } // </ switch >
eads@18 512
eads@18 513 var canvas = $('<canvas width="'+ (numb($text.outerWidth(true)) + opts.strokeWidth*2) +'" height="'+ (numb($text.outerHeight(true)) + opts.strokeWidth*2) +'"></canvas>').appendTo($box).css({position: 'absolute', top: $text.btPosition().top, left: $text.btPosition().left, zIndex: opts.boxzIndex}).get(0);
eads@18 514
eads@18 515 // if excanvas is set up, we need to initialize the new canvas element
eads@18 516 if (typeof G_vmlCanvasManager != 'undefined') {
eads@18 517 canvas = G_vmlCanvasManager.initElement(canvas);
eads@18 518 }
eads@18 519
eads@18 520 if (opts.cornerRadius > 0) {
eads@18 521 // round the corners!
eads@18 522 var newPoints = new Array();
eads@18 523 var newPoint;
eads@18 524 for (var i=0; i<points.length; i++) {
eads@18 525 if (points[i].type == 'corner') {
eads@18 526 // create two new arc points
eads@18 527 // find point between this and previous (using modulo in case of ending)
eads@18 528 newPoint = betweenPoint(points[i], points[(i-1)%points.length], opts.cornerRadius);
eads@18 529 newPoint.type = 'arcStart';
eads@18 530 newPoints[newPoints.length] = newPoint;
eads@18 531 // the original corner point
eads@18 532 newPoints[newPoints.length] = points[i];
eads@18 533 // find point between this and next
eads@18 534 newPoint = betweenPoint(points[i], points[(i+1)%points.length], opts.cornerRadius);
eads@18 535 newPoint.type = 'arcEnd';
eads@18 536 newPoints[newPoints.length] = newPoint;
eads@18 537 }
eads@18 538 else {
eads@18 539 newPoints[newPoints.length] = points[i];
eads@18 540 }
eads@18 541 }
eads@18 542 // overwrite points with new version
eads@18 543 points = newPoints;
eads@18 544
eads@18 545 }
eads@18 546
eads@18 547 var ctx = canvas.getContext("2d");
eads@18 548 drawIt.apply(ctx, [points], opts.strokeWidth);
eads@18 549 ctx.fillStyle = opts.fill;
eads@18 550 if (opts.shadow) {
eads@18 551 ctx.shadowOffsetX = 2;
eads@18 552 ctx.shadowOffsetY = 2;
eads@18 553 ctx.shadowBlur = 5;
eads@18 554 ctx.shadowColor = opts.shadowColor;
eads@18 555 }
eads@18 556 ctx.closePath();
eads@18 557 ctx.fill();
eads@18 558 if (opts.strokeWidth > 0) {
eads@18 559 ctx.shadowColor = 'rgba(0, 0, 0, 0)';
eads@18 560 ctx.lineWidth = opts.strokeWidth;
eads@18 561 ctx.strokeStyle = opts.strokeStyle;
eads@18 562 ctx.beginPath();
eads@18 563 drawIt.apply(ctx, [points], opts.strokeWidth);
eads@18 564 ctx.closePath();
eads@18 565 ctx.stroke();
eads@18 566 }
eads@18 567
eads@18 568 if (opts.animate) {
eads@18 569 $box.css({opacity: 0.1});
eads@18 570 }
eads@18 571
eads@18 572 $box.css({visibility: 'visible'});
eads@18 573
eads@18 574 if (opts.overlay) {
eads@18 575 // EXPERIMENTAL!!!!
eads@18 576 var overlay = $('<div class="bt-overlay"></div>').css({
eads@18 577 position: 'absolute',
eads@18 578 backgroundColor: 'blue',
eads@18 579 top: top,
eads@18 580 left: left,
eads@18 581 width: width,
eads@18 582 height: height,
eads@18 583 opacity: '.2'
eads@18 584 }).appendTo(offsetParent);
eads@18 585 $(this).data('overlay', overlay);
eads@18 586 }
eads@18 587
eads@18 588 var animParams = {opacity: 1};
eads@18 589 if (opts.animate) {
eads@18 590 switch (position) {
eads@18 591 case 'top':
eads@18 592 animParams.top = $box.btPosition().top + opts.distance;
eads@18 593 break;
eads@18 594 case 'left':
eads@18 595 animParams.left = $box.btPosition().left + opts.distance;
eads@18 596 break;
eads@18 597 case 'bottom':
eads@18 598 animParams.top = $box.btPosition().top - opts.distance;
eads@18 599 break;
eads@18 600 case 'right':
eads@18 601 animParams.left = $box.btPosition().left - opts.distance;
eads@18 602 break;
eads@18 603 }
eads@18 604 $box.animate(animParams, {duration: opts.speed, easing: opts.easing});
eads@18 605 }
eads@18 606
eads@18 607 if ((opts.ajaxPath != null && opts.ajaxCache == false) || ajaxTimeout) {
eads@18 608 // if ajaxCache is not enabled or if there was a server timeout,
eads@18 609 // remove the content variable so it will be loaded again from server
eads@18 610 content = false;
eads@18 611 }
eads@18 612
eads@18 613 // stick this element into the clickAnywhereToClose stack
eads@18 614 if (opts.clickAnywhereToClose) {
eads@18 615 jQuery.bt.vars.clickAnywhereStack.push(this);
eads@18 616 $(document).click(jQuery.bt.docClick);
eads@18 617 }
eads@18 618
eads@18 619 // stick this element into the closeWhenOthersOpen stack
eads@18 620 if (opts.closeWhenOthersOpen) {
eads@18 621 jQuery.bt.vars.closeWhenOpenStack.push(this);
eads@18 622 }
eads@18 623
eads@18 624 // trigger postShow function
eads@18 625 opts.postShow.apply(this);
eads@18 626
eads@18 627
eads@18 628 }; // </ turnOn() >
eads@18 629
eads@18 630 this.btOff = function() {
eads@18 631
eads@18 632 // trigger preHide function
eads@18 633 opts.preHide.apply(this);
eads@18 634
eads@18 635 var box = $(this).data('bt-box');
eads@18 636 var overlay = $(this).data('bt-overlay');
eads@18 637 if (typeof box == 'object') {
eads@18 638 $(box).remove();
eads@18 639 $(this).removeData('bt-box');
eads@18 640 }
eads@18 641 if (typeof overlay == 'object') {
eads@18 642 $(overlay).remove();
eads@18 643 $(this).removeData('bt-overlay');
eads@18 644 }
eads@18 645
eads@18 646 // remove this from the stacks
eads@18 647 jQuery.bt.vars.clickAnywhereStack = arrayRemove(jQuery.bt.vars.clickAnywhereStack, this);
eads@18 648 jQuery.bt.vars.closeWhenOpenStack = arrayRemove(jQuery.bt.vars.closeWhenOpenStack, this);
eads@18 649
eads@18 650 // trigger postHide function
eads@18 651 opts.postHide.apply(this);
eads@18 652
eads@18 653 // remove the 'bt-active' and activeClass classes from target
eads@18 654 $(this).removeClass('bt-active ' + opts.activeClass);
eads@18 655
eads@18 656 }; // </ turnOff() >
eads@18 657
eads@18 658 var refresh = this.btRefresh = function() {
eads@18 659 this.btOff();
eads@18 660 this.btOn();
eads@18 661 };
eads@18 662
eads@18 663
eads@18 664 }); // </ this.each() >
eads@18 665
eads@18 666
eads@18 667 function drawIt(points, strokeWidth) {
eads@18 668 this.moveTo(points[0].x, points[0].y);
eads@18 669 for (i=1;i<points.length;i++) {
eads@18 670 if (points[i-1].type == 'arcStart') {
eads@18 671 // if we're creating a rounded corner
eads@18 672 //ctx.arc(round5(points[i].x), round5(points[i].y), points[i].startAngle, points[i].endAngle, opts.cornerRadius, false);
eads@18 673 this.quadraticCurveTo(round5(points[i].x, strokeWidth), round5(points[i].y, strokeWidth), round5(points[(i+1)%points.length].x, strokeWidth), round5(points[(i+1)%points.length].y, strokeWidth));
eads@18 674 i++;
eads@18 675 //ctx.moveTo(round5(points[i].x), round5(points[i].y));
eads@18 676 }
eads@18 677 else {
eads@18 678 this.lineTo(round5(points[i].x, strokeWidth), round5(points[i].y, strokeWidth));
eads@18 679 }
eads@18 680 }
eads@18 681 }; // </ drawIt() >
eads@18 682
eads@18 683 /**
eads@18 684 * For odd stroke widths, round to the nearest .5 pixel to avoid antialiasing
eads@18 685 * http://developer.mozilla.org/en/Canvas_tutorial/Applying_styles_and_colors
eads@18 686 */
eads@18 687 function round5(num, strokeWidth) {
eads@18 688 var ret;
eads@18 689 strokeWidth = numb(strokeWidth);
eads@18 690 if (strokeWidth%2) {
eads@18 691 ret = num;
eads@18 692 }
eads@18 693 else {
eads@18 694 ret = Math.round(num - .5) + .5;
eads@18 695 }
eads@18 696 return ret;
eads@18 697 }; // </ round5() >
eads@18 698
eads@18 699 /**
eads@18 700 * Ensure that a number is a number... or zero
eads@18 701 */
eads@18 702 function numb(num) {
eads@18 703 return parseInt(num) || 0;
eads@18 704 }; // </ numb() >
eads@18 705
eads@18 706 /**
eads@18 707 * Remove an element from an array
eads@18 708 */
eads@18 709 function arrayRemove(arr, elem) {
eads@18 710 var x, newArr = new Array();
eads@18 711 for (x in arr) {
eads@18 712 if (arr[x] != elem) {
eads@18 713 newArr.push(arr[x]);
eads@18 714 }
eads@18 715 }
eads@18 716 return newArr;
eads@18 717 }; // </ arrayRemove() >
eads@18 718
eads@18 719 /**
eads@18 720 * Given two points, find a point which is dist pixels from point1 on a line to point2
eads@18 721 */
eads@18 722 function betweenPoint(point1, point2, dist) {
eads@18 723 // figure out if we're horizontal or vertical
eads@18 724 var y, x;
eads@18 725 if (point1.x == point2.x) {
eads@18 726 // vertical
eads@18 727 y = point1.y < point2.y ? point1.y + dist : point1.y - dist;
eads@18 728 return {x: point1.x, y: y};
eads@18 729 }
eads@18 730 else if (point1.y == point2.y) {
eads@18 731 // horizontal
eads@18 732 x = point1.x < point2.x ? point1.x + dist : point1.x - dist;
eads@18 733 return {x:x, y: point1.y};
eads@18 734 }
eads@18 735 }; // </ betweenPoint() >
eads@18 736
eads@18 737 function centerPoint(arcStart, corner, arcEnd) {
eads@18 738 var x = corner.x == arcStart.x ? arcEnd.x : arcStart.x;
eads@18 739 var y = corner.y == arcStart.y ? arcEnd.y : arcStart.y;
eads@18 740 var startAngle, endAngle;
eads@18 741 if (arcStart.x < arcEnd.x) {
eads@18 742 if (arcStart.y > arcEnd.y) {
eads@18 743 // arc is on upper left
eads@18 744 startAngle = (Math.PI/180)*180;
eads@18 745 endAngle = (Math.PI/180)*90;
eads@18 746 }
eads@18 747 else {
eads@18 748 // arc is on upper right
eads@18 749 startAngle = (Math.PI/180)*90;
eads@18 750 endAngle = 0;
eads@18 751 }
eads@18 752 }
eads@18 753 else {
eads@18 754 if (arcStart.y > arcEnd.y) {
eads@18 755 // arc is on lower left
eads@18 756 startAngle = (Math.PI/180)*270;
eads@18 757 endAngle = (Math.PI/180)*180;
eads@18 758 }
eads@18 759 else {
eads@18 760 // arc is on lower right
eads@18 761 startAngle = 0;
eads@18 762 endAngle = (Math.PI/180)*270;
eads@18 763 }
eads@18 764 }
eads@18 765 return {x: x, y: y, type: 'center', startAngle: startAngle, endAngle: endAngle};
eads@18 766 }; // </ centerPoint() >
eads@18 767
eads@18 768 /**
eads@18 769 * Find the intersection point of two lines, each defined by two points
eads@18 770 * arguments are x1, y1 and x2, y2 for r1 (line 1) and r2 (line 2)
eads@18 771 * It's like an algebra party!!!
eads@18 772 */
eads@18 773 function findIntersect(r1x1, r1y1, r1x2, r1y2, r2x1, r2y1, r2x2, r2y2) {
eads@18 774
eads@18 775 if (r2x1 == r2x2) {
eads@18 776 return findIntersectY(r1x1, r1y1, r1x2, r1y2, r2x1);
eads@18 777 }
eads@18 778 if (r2y1 == r2y2) {
eads@18 779 return findIntersectX(r1x1, r1y1, r1x2, r1y2, r2y1);
eads@18 780 }
eads@18 781
eads@18 782 // m = (y1 - y2) / (x1 - x2) // <-- how to find the slope
eads@18 783 // y = mx + b // the 'classic' linear equation
eads@18 784 // b = y - mx // how to find b (the y-intersect)
eads@18 785 // x = (y - b)/m // how to find x
eads@18 786 var r1m = (r1y1 - r1y2) / (r1x1 - r1x2);
eads@18 787 var r1b = r1y1 - (r1m * r1x1);
eads@18 788 var r2m = (r2y1 - r2y2) / (r2x1 - r2x2);
eads@18 789 var r2b = r2y1 - (r2m * r2x1);
eads@18 790
eads@18 791 var x = (r2b - r1b) / (r1m - r2m);
eads@18 792 var y = r1m * x + r1b;
eads@18 793
eads@18 794 return {x: x, y: y};
eads@18 795 }; // </ findIntersect() >
eads@18 796
eads@18 797 /**
eads@18 798 * Find the y intersection point of a line and given x vertical
eads@18 799 */
eads@18 800 function findIntersectY(r1x1, r1y1, r1x2, r1y2, x) {
eads@18 801 if (r1y1 == r1y2) {
eads@18 802 return {x: x, y: r1y1};
eads@18 803 }
eads@18 804 var r1m = (r1y1 - r1y2) / (r1x1 - r1x2);
eads@18 805 var r1b = r1y1 - (r1m * r1x1);
eads@18 806
eads@18 807 var y = r1m * x + r1b;
eads@18 808
eads@18 809 return {x: x, y: y};
eads@18 810 }; // </ findIntersectY() >
eads@18 811
eads@18 812 /**
eads@18 813 * Find the x intersection point of a line and given y horizontal
eads@18 814 */
eads@18 815 function findIntersectX(r1x1, r1y1, r1x2, r1y2, y) {
eads@18 816 if (r1x1 == r1x2) {
eads@18 817 return {x: r1x1, y: y};
eads@18 818 }
eads@18 819 var r1m = (r1y1 - r1y2) / (r1x1 - r1x2);
eads@18 820 var r1b = r1y1 - (r1m * r1x1);
eads@18 821
eads@18 822 // y = mx + b // your old friend, linear equation
eads@18 823 // x = (y - b)/m // linear equation solved for x
eads@18 824 var x = (y - r1b) / r1m;
eads@18 825
eads@18 826 return {x: x, y: y};
eads@18 827
eads@18 828 }; // </ findIntersectX() >
eads@18 829
eads@18 830 }; // </ jQuery.fn.bt() >
eads@18 831
eads@18 832 /**
eads@18 833 * jQuery's compat.js (used in Drupal's jQuery upgrade module, overrides the $().position() function
eads@18 834 * this is a copy of that function to allow the plugin to work when compat.js is present
eads@18 835 * once compat.js is fixed to not override existing functions, this function can be removed
eads@18 836 * and .btPosion() can be replaced with .position() above...
eads@18 837 */
eads@18 838 jQuery.fn.btPosition = function() {
eads@18 839
eads@18 840 function num(elem, prop) {
eads@18 841 return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
eads@18 842 };
eads@18 843
eads@18 844 var left = 0, top = 0, results;
eads@18 845
eads@18 846 if ( this[0] ) {
eads@18 847 // Get *real* offsetParent
eads@18 848 var offsetParent = this.offsetParent(),
eads@18 849
eads@18 850 // Get correct offsets
eads@18 851 offset = this.offset(),
eads@18 852 parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();
eads@18 853
eads@18 854 // Subtract element margins
eads@18 855 // note: when an element has margin: auto the offsetLeft and marginLeft
eads@18 856 // are the same in Safari causing offset.left to incorrectly be 0
eads@18 857 offset.top -= num( this, 'marginTop' );
eads@18 858 offset.left -= num( this, 'marginLeft' );
eads@18 859
eads@18 860 // Add offsetParent borders
eads@18 861 parentOffset.top += num( offsetParent, 'borderTopWidth' );
eads@18 862 parentOffset.left += num( offsetParent, 'borderLeftWidth' );
eads@18 863
eads@18 864 // Subtract the two offsets
eads@18 865 results = {
eads@18 866 top: offset.top - parentOffset.top,
eads@18 867 left: offset.left - parentOffset.left
eads@18 868 };
eads@18 869 }
eads@18 870
eads@18 871 return results;
eads@18 872 }; // </ jQuery.fn.btPosition() >
eads@18 873
eads@18 874
eads@18 875 /**
eads@18 876 * A convenience function to run btOn() (if available)
eads@18 877 * for each selected item
eads@18 878 */
eads@18 879 jQuery.fn.btOn = function() {
eads@18 880 return this.each(function(index){
eads@18 881 if ($.isFunction(this.btOn)) {
eads@18 882 this.btOn();
eads@18 883 }
eads@18 884 });
eads@18 885 }; // </ $().btOn() >
eads@18 886
eads@18 887 /**
eads@18 888 *
eads@18 889 * A convenience function to run btOff() (if available)
eads@18 890 * for each selected item
eads@18 891 */
eads@18 892 jQuery.fn.btOff = function() {
eads@18 893 return this.each(function(index){
eads@18 894 if ($.isFunction(this.btOff)) {
eads@18 895 this.btOff();
eads@18 896 }
eads@18 897 });
eads@18 898 }; // </ $().btOff() >
eads@18 899
eads@18 900 jQuery.bt = {};
eads@18 901 jQuery.bt.vars = {clickAnywhereStack: [], closeWhenOpenStack: []};
eads@18 902
eads@18 903 /**
eads@18 904 * This function gets bound to the document's click event
eads@18 905 * It turns off all of the tips in the click-anywhere-to-close stack
eads@18 906 */
eads@18 907 jQuery.bt.docClick = function(e) {
eads@18 908 if (!e) {
eads@18 909 var e = window.event;
eads@18 910 };
eads@18 911 if (!$(e.target).parents().andSelf().filter('.bt-wrapper, .bt-active').length) {
eads@18 912 // if clicked element isn't inside tip, close tips in stack
eads@18 913 $(jQuery.bt.vars.clickAnywhereStack).btOff();
eads@18 914 $(document).unbind('click', jQuery.bt.docClick);
eads@18 915 }
eads@18 916 }; // </ docClick() >
eads@18 917
eads@18 918 /**
eads@18 919 * Defaults for the beauty tips
eads@18 920 *
eads@18 921 * Note this is a variable definition and not a function. So defaults can be
eads@18 922 * written for an entire page by simply redefining attributes like so:
eads@18 923 *
eads@18 924 * jQuery.bt.defaults.width = 400;
eads@18 925 *
eads@18 926 * This would make all Beauty Tips boxes 400px wide.
eads@18 927 *
eads@18 928 * Each of these options may also be overridden during
eads@18 929 *
eads@18 930 * Can be overriden globally or at time of call.
eads@18 931 *
eads@18 932 */
eads@18 933 jQuery.bt.defaults = {
eads@18 934 trigger: 'hover', // trigger to show/hide tip
eads@18 935 // use [on, off] to define separate on/off triggers
eads@18 936 // also use space character to allow multiple to trigger
eads@18 937 // examples:
eads@18 938 // ['focus', 'blur'] // focus displays, blur hides
eads@18 939 // 'dblclick' // dblclick toggles on/off
eads@18 940 // ['focus mouseover', 'blur mouseout'] // multiple triggers
eads@18 941 // 'now' // shows/hides tip without event
eads@18 942 // 'none' // use $('#selector').btOn(); and ...btOff();
eads@18 943 // 'hoverIntent' // hover using hoverIntent plugin (settings below)
eads@18 944 // note:
eads@18 945 // hoverIntent becomes default if available
eads@18 946
eads@18 947 clickAnywhereToClose: true, // clicking anywhere outside of the tip will close it
eads@18 948 closeWhenOthersOpen: false, // tip will be closed before another opens - stop >= 2 tips being on
eads@18 949
eads@18 950 width: '200px', // width of tooltip box
eads@18 951 // when combined with cssStyles: {width: 'auto'}, this becomes a max-width for the text
eads@18 952 padding: '10px', // padding for content (get more fine grained with cssStyles)
eads@18 953 spikeGirth: 10, // width of spike
eads@18 954 spikeLength: 15, // length of spike
eads@18 955 overlap: 0, // spike overlap (px) onto target (can cause problems with 'hover' trigger)
eads@18 956 overlay: false, // display overlay on target (use CSS to style) -- BUGGY!
eads@18 957 killTitle: true, // kill title tags to avoid double tooltips
eads@18 958
eads@18 959 textzIndex: 9999, // z-index for the text
eads@18 960 boxzIndex: 9998, // z-index for the "talk" box (should always be less than textzIndex)
eads@18 961 wrapperzIndex: 9997,
eads@18 962 positions: ['most'], // preference of positions for tip (will use first with available space)
eads@18 963 // possible values 'top', 'bottom', 'left', 'right' as an array in order of
eads@18 964 // preference. Last value will be used if others don't have enough space.
eads@18 965 // or use 'most' to use the area with the most space
eads@18 966 fill: "rgb(255, 255, 102)", // fill color for the tooltip box
eads@18 967
eads@18 968 windowMargin: 10, // space (px) to leave between text box and browser edge
eads@18 969
eads@18 970 strokeWidth: 1, // width of stroke around box, **set to 0 for no stroke**
eads@18 971 strokeStyle: "#000", // color/alpha of stroke
eads@18 972
eads@18 973 cornerRadius: 5, // radius of corners (px), set to 0 for square corners
eads@18 974
eads@18 975 // following values are on a scale of 0 to 1 with .5 being centered
eads@18 976
eads@18 977 centerPointX: .5, // the spike extends from center of the target edge to this point
eads@18 978 centerPointY: .5, // defined by percentage horizontal (x) and vertical (y)
eads@18 979
eads@18 980 shadow: false, // use drop shadow? (only displays in Safari and FF 3.1) - experimental
eads@18 981 shadowOffsetX: 2, // shadow offset x (px)
eads@18 982 shadowOffsetY: 2, // shadow offset y (px)
eads@18 983 shadowBlur: 3, // shadow blur (px)
eads@18 984 shadowColor: "#000", // shadow color/alpha
eads@18 985
eads@18 986 animate: false, // animate show/hide of box - EXPERIMENTAL (buggy in IE)
eads@18 987 distance: 15, // distance of animation movement (px)
eads@18 988 easing: 'swing', // animation easing
eads@18 989 speed: 200, // speed (ms) of animation
eads@18 990
eads@18 991 cssClass: '', // CSS class to add to the box wrapper div (of the TIP)
eads@18 992 cssStyles: {}, // styles to add the text box
eads@18 993 // example: {fontFamily: 'Georgia, Times, serif', fontWeight: 'bold'}
eads@18 994
eads@18 995 activeClass: 'bt-active', // class added to TARGET element when its BeautyTip is active
eads@18 996
eads@18 997 contentSelector: "$(this).attr('title')", // if there is no content argument, use this selector to retrieve the title
eads@18 998
eads@18 999 ajaxPath: null, // if using ajax request for content, this contains url and (opt) selector
eads@18 1000 // this will override content and contentSelector
eads@18 1001 // examples (see jQuery load() function):
eads@18 1002 // '/demo.html'
eads@18 1003 // '/help/ajax/snip'
eads@18 1004 // '/help/existing/full div#content'
eads@18 1005
eads@18 1006 // ajaxPath can also be defined as an array
eads@18 1007 // in which case, the first value will be parsed as a jQuery selector
eads@18 1008 // the result of which will be used as the ajaxPath
eads@18 1009 // the second (optional) value is the content selector as above
eads@18 1010 // examples:
eads@18 1011 // ["$(this).attr('href')", 'div#content']
eads@18 1012 // ["$(this).parents('.wrapper').find('.title').attr('href')"]
eads@18 1013 // ["$('#some-element').val()"]
eads@18 1014
eads@18 1015 ajaxError: '<strong>ERROR:</strong> <em>%error</em>',
eads@18 1016 // error text, use "%error" to insert error from server
eads@18 1017 ajaxLoading: '<blink>Loading...</blink>', // yes folks, it's the blink tag!
eads@18 1018 ajaxData: {}, // key/value pairs
eads@18 1019 ajaxType: 'GET', // 'GET' or 'POST'
eads@18 1020 ajaxCache: true, // cache ajax results and do not send request to same url multiple times
eads@18 1021 ajaxOpts: {}, // any other ajax options - timeout, passwords, processing functions, etc...
eads@18 1022 // see http://docs.jquery.com/Ajax/jQuery.ajax#options
eads@18 1023
eads@18 1024 preShow: function(){return;}, // function to run before popup is built and displayed
eads@18 1025 postShow: function(){return;}, // function to run after popup is built and displayed
eads@18 1026 preHide: function(){return;}, // function to run before popup is removed
eads@18 1027 postHide: function(){return;}, // function to run after popup is removed
eads@18 1028
eads@18 1029 hoverIntentOpts: { // options for hoverIntent (if installed)
eads@18 1030 interval: 300, // http://cherne.net/brian/resources/jquery.hoverIntent.html
eads@18 1031 timeout: 500
eads@18 1032 }
eads@18 1033
eads@18 1034 }; // </ jQuery.bt.defaults >
eads@18 1035
eads@18 1036
eads@18 1037 // @todo
eads@18 1038 // use larger canvas (extend to edge of page when windowMargin is active)
eads@18 1039 // add options to shift position of tip vert/horiz and position of spike tip
eads@18 1040 // create drawn (canvas) shadows
eads@18 1041 // use overlay to allow overlap with hover
eads@18 1042 // experiment with making tooltip a subelement of the target
eads@18 1043 // rework animation system