diff --git a/static/js/jquery.qtip.css b/static/js/jquery.qtip.css new file mode 100644 index 0000000..5cfeb93 --- /dev/null +++ b/static/js/jquery.qtip.css @@ -0,0 +1,556 @@ +/* +* qTip2 - Pretty powerful tooltips +* http://craigsworks.com/projects/qtip2/ +* +* Version: nightly +* Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com +* +* Dual licensed under MIT or GPLv2 licenses +* http://en.wikipedia.org/wiki/MIT_License +* http://en.wikipedia.org/wiki/GNU_General_Public_License +* +* Date: Mon Dec 19 15:43:28.0000000000 2011 +*/ + +/* Core qTip styles */ +.ui-tooltip, .qtip{ + position: absolute; + left: -28000px; + top: -28000px; + display: none; + + max-width: 280px; + min-width: 50px; + + font-size: 10.5px; + line-height: 12px; +} + + /* Fluid class for determining actual width in IE */ + .ui-tooltip-fluid{ + display: block; + visibility: hidden; + position: static !important; + float: left !important; + } + + .ui-tooltip-content{ + position: relative; + padding: 5px 9px; + overflow: hidden; + + border: 1px solid #000001; + + text-align: left; + word-wrap: break-word; + overflow: hidden; + } + + .ui-tooltip-titlebar{ + position: relative; + min-height: 14px; + padding: 5px 35px 5px 10px; + overflow: hidden; + + border: 1px solid #000001; + border-width: 1px 1px 0; + + font-weight: bold; + } + + .ui-tooltip-titlebar + .ui-tooltip-content{ border-top-width: 0px !important; } + + /*! Default close button class */ + .ui-tooltip-titlebar .ui-state-default{ + position: absolute; + right: 4px; + top: 50%; + margin-top: -9px; + + cursor: pointer; + outline: medium none; + + border-width: 1px; + border-style: solid; + } + + * html .ui-tooltip-titlebar .ui-state-default{ top: 16px; } /* IE fix */ + + .ui-tooltip-titlebar .ui-icon, + .ui-tooltip-icon .ui-icon{ + display: block; + text-indent: -1000em; + } + + .ui-tooltip-icon, .ui-tooltip-icon .ui-icon{ + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + } + + .ui-tooltip-icon .ui-icon{ + width: 18px; + height: 14px; + + text-align: center; + text-indent: 0; + font: normal bold 10px/13px Tahoma,sans-serif; + + color: inherit; + background: transparent none no-repeat -100em -100em; + } + + +/* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */ +.ui-tooltip-focus{ + +} + +/* Applied on hover of tooltips i.e. added/removed on mouseenter/mouseleave respectively */ +.ui-tooltip-hover{ + +} + + +/*! Default tooltip style */ +.ui-tooltip-default .ui-tooltip-titlebar, +.ui-tooltip-default .ui-tooltip-content{ + border-color: #F1D031; + background-color: #FFFFA3; + color: #555; +} + + .ui-tooltip-default .ui-tooltip-titlebar{ + background-color: #FFEF93; + } + + .ui-tooltip-default .ui-tooltip-icon{ + border-color: #CCC; + background: #F1F1F1; + color: #777; + } + + .ui-tooltip-default .ui-tooltip-titlebar .ui-state-hover{ + border-color: #AAA; + color: #111; + } + +/* Tips plugin */ +.ui-tooltip .ui-tooltip-tip{ + margin: 0 auto; + overflow: hidden; + z-index: 10; +} + + .ui-tooltip .ui-tooltip-tip, + .ui-tooltip .ui-tooltip-tip *{ + position: absolute; + + line-height: 0.1px !important; + font-size: 0.1px !important; + color: #123456; + + background: transparent; + border: 0px dashed transparent; + } + + .ui-tooltip .ui-tooltip-tip canvas{ top: 0; left: 0; } + + +/* Modal plugin */ +#qtip-overlay{ + position: fixed; + left: -10000em; + top: -10000em; +} + + /* Applied to modals with show.modal.blur set to true */ + #qtip-overlay.blurs{ cursor: pointer; } + + /* Change opacity of overlay here */ + #qtip-overlay div{ + position: absolute; + left: 0; top: 0; + width: 100%; height: 100%; + + background-color: black; + + opacity: 0.7; + filter:alpha(opacity=70); + -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; + } + +/*! Light tooltip style */ +.ui-tooltip-light .ui-tooltip-titlebar, +.ui-tooltip-light .ui-tooltip-content{ + border-color: #E2E2E2; + color: #454545; +} + + .ui-tooltip-light .ui-tooltip-content{ + background-color: white; + } + + .ui-tooltip-light .ui-tooltip-titlebar{ + background-color: #f1f1f1; + } + + +/*! Dark tooltip style */ +.ui-tooltip-dark .ui-tooltip-titlebar, +.ui-tooltip-dark .ui-tooltip-content{ + border-color: #303030; + color: #f3f3f3; +} + + .ui-tooltip-dark .ui-tooltip-content{ + background-color: #505050; + } + + .ui-tooltip-dark .ui-tooltip-titlebar{ + background-color: #404040; + } + + .ui-tooltip-dark .ui-tooltip-icon{ + border-color: #444; + } + + .ui-tooltip-dark .ui-tooltip-titlebar .ui-state-hover{ + border-color: #303030; + } + + +/*! Cream tooltip style */ +.ui-tooltip-cream .ui-tooltip-titlebar, +.ui-tooltip-cream .ui-tooltip-content{ + border-color: #F9E98E; + color: #A27D35; +} + + .ui-tooltip-cream .ui-tooltip-content{ + background-color: #FBF7AA; + } + + .ui-tooltip-cream .ui-tooltip-titlebar{ + background-color: #F0DE7D; + } + + .ui-tooltip-cream .ui-state-default .ui-tooltip-icon{ + background-position: -82px 0; + } + + +/*! Red tooltip style */ +.ui-tooltip-red .ui-tooltip-titlebar, +.ui-tooltip-red .ui-tooltip-content{ + border-color: #D95252; + color: #912323; +} + + .ui-tooltip-red .ui-tooltip-content{ + background-color: #F78B83; + } + + .ui-tooltip-red .ui-tooltip-titlebar{ + background-color: #F06D65; + } + + .ui-tooltip-red .ui-state-default .ui-tooltip-icon{ + background-position: -102px 0; + } + + .ui-tooltip-red .ui-tooltip-icon{ + border-color: #D95252; + } + + .ui-tooltip-red .ui-tooltip-titlebar .ui-state-hover{ + border-color: #D95252; + } + + +/*! Green tooltip style */ +.ui-tooltip-green .ui-tooltip-titlebar, +.ui-tooltip-green .ui-tooltip-content{ + border-color: #90D93F; + color: #3F6219; +} + + .ui-tooltip-green .ui-tooltip-content{ + background-color: #CAED9E; + } + + .ui-tooltip-green .ui-tooltip-titlebar{ + background-color: #B0DE78; + } + + .ui-tooltip-green .ui-state-default .ui-tooltip-icon{ + background-position: -42px 0; + } + + +/*! Blue tooltip style */ +.ui-tooltip-blue .ui-tooltip-titlebar, +.ui-tooltip-blue .ui-tooltip-content{ + border-color: #ADD9ED; + color: #5E99BD; +} + + .ui-tooltip-blue .ui-tooltip-content{ + background-color: #E5F6FE; + } + + .ui-tooltip-blue .ui-tooltip-titlebar{ + background-color: #D0E9F5; + } + + .ui-tooltip-blue .ui-state-default .ui-tooltip-icon{ + background-position: -2px 0; + } + +/*! Add shadows to your tooltips in: FF3+, Chrome 2+, Opera 10.6+, IE6+, Safari 2+ */ +.ui-tooltip-shadow{ + -webkit-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15); + box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15); +} + + .ui-tooltip-shadow .ui-tooltip-titlebar, + .ui-tooltip-shadow .ui-tooltip-content{ + filter: progid:DXImageTransform.Microsoft.Shadow(Color='gray', Direction=135, Strength=3); + -ms-filter:"progid:DXImageTransform.Microsoft.Shadow(Color='gray', Direction=135, Strength=3)"; + + _margin-bottom: -3px; /* IE6 */ + .margin-bottom: -3px; /* IE7 */ + } + + +/*! Add rounded corners to your tooltips in: FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+ */ +.ui-tooltip-rounded, +.ui-tooltip-rounded .ui-tooltip-content, +.ui-tooltip-tipsy, +.ui-tooltip-tipsy .ui-tooltip-content, +.ui-tooltip-youtube, +.ui-tooltip-youtube .ui-tooltip-content{ + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} + +.ui-tooltip-rounded .ui-tooltip-titlebar, +.ui-tooltip-tipsy .ui-tooltip-titlebar, +.ui-tooltip-youtube .ui-tooltip-titlebar{ + -moz-border-radius: 5px 5px 0 0; + -webkit-border-radius: 5px 5px 0 0; + border-radius: 5px 5px 0 0; +} + +.ui-tooltip-rounded .ui-tooltip-titlebar + .ui-tooltip-content, +.ui-tooltip-tipsy .ui-tooltip-titlebar + .ui-tooltip-content, +.ui-tooltip-youtube .ui-tooltip-titlebar + .ui-tooltip-content{ + -moz-border-radius: 0 0 5px 5px; + -webkit-border-radius: 0 0 5px 5px; + border-radius: 0 0 5px 5px; +} + + +/*! Youtube tooltip style */ +.ui-tooltip-youtube{ + -webkit-box-shadow: 0 0 3px #333; + -moz-box-shadow: 0 0 3px #333; + box-shadow: 0 0 3px #333; +} + + .ui-tooltip-youtube .ui-tooltip-titlebar, + .ui-tooltip-youtube .ui-tooltip-content{ + _margin-bottom: 0; /* IE6 */ + .margin-bottom: 0; /* IE7 */ + + background: transparent; + background: rgba(0, 0, 0, 0.85); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#D9000000,endColorstr=#D9000000); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#D9000000,endColorstr=#D9000000)"; + + color: white; + border-color: #CCCCCC; + } + + .ui-tooltip-youtube .ui-tooltip-icon{ + border-color: #222; + } + + .ui-tooltip-youtube .ui-tooltip-titlebar .ui-state-hover{ + border-color: #303030; + } + + +/* jQuery TOOLS Tooltip style */ +.ui-tooltip-jtools{ + background: #232323; + background: rgba(0, 0, 0, 0.7); + background-image: -moz-linear-gradient(top, #717171, #232323); + background-image: -webkit-gradient(linear, left top, left bottom, from(#717171), to(#232323)); + + border: 2px solid #ddd; + border: 2px solid rgba(241,241,241,1); + + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + + -webkit-box-shadow: 0 0 12px #333; + -moz-box-shadow: 0 0 12px #333; + box-shadow: 0 0 12px #333; +} + + /* IE Specific */ + .ui-tooltip-jtools .ui-tooltip-titlebar{ + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)"; + } + .ui-tooltip-jtools .ui-tooltip-content{ + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)"; + } + + .ui-tooltip-jtools .ui-tooltip-titlebar, + .ui-tooltip-jtools .ui-tooltip-content{ + background: transparent; + color: white; + border: 0 dashed transparent; + } + + .ui-tooltip-jtools .ui-tooltip-icon{ + border-color: #555; + } + + .ui-tooltip-jtools .ui-tooltip-titlebar .ui-state-hover{ + border-color: #333; + } + + +/* Cluetip style */ +.ui-tooltip-cluetip{ + -webkit-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4); + -moz-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4); + box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4); +} + + .ui-tooltip-cluetip .ui-tooltip-titlebar{ + background-color: #87876A; + color: white; + border: 0 dashed transparent; + } + + .ui-tooltip-cluetip .ui-tooltip-content{ + background-color: #D9D9C2; + color: #111; + border: 0 dashed transparent; + } + + .ui-tooltip-cluetip .ui-tooltip-icon{ + border-color: #808064; + } + + .ui-tooltip-cluetip .ui-tooltip-titlebar .ui-state-hover{ + border-color: #696952; + color: #696952; + } + + +/* Tipsy style */ +.ui-tooltip-tipsy{ + border: 0; +} + + .ui-tooltip-tipsy .ui-tooltip-titlebar, + .ui-tooltip-tipsy .ui-tooltip-content{ + _margin-bottom: 0; /* IE6 */ + .margin-bottom: 0; /* IE7 */ + + background: transparent; + background: rgba(0, 0, 0, .87); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#D9000000,endColorstr=#D9000000); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#D9000000,endColorstr=#D9000000)"; + + color: white; + border: 0px transparent; + + font-size: 11px; + font-family: 'Lucida Grande', sans-serif; + font-weight: bold; + line-height: 16px; + text-shadow: 0 1px black; + } + + .ui-tooltip-tipsy .ui-tooltip-titlebar{ + padding: 6px 35px 0 10; + } + + .ui-tooltip-tipsy .ui-tooltip-content{ + padding: 6px 10; + } + + .ui-tooltip-tipsy .ui-tooltip-icon{ + border-color: #222; + text-shadow: none; + } + + .ui-tooltip-tipsy .ui-tooltip-titlebar .ui-state-hover{ + border-color: #303030; + } + + +/* Tipped style */ +.ui-tooltip-tipped{ + +} + + .ui-tooltip-tipped .ui-tooltip-titlebar, + .ui-tooltip-tipped .ui-tooltip-content{ + border: 3px solid #959FA9; + + filter: none; -ms-filter: none; + } + + .ui-tooltip-tipped .ui-tooltip-titlebar{ + background: #3A79B8; + background-image: -moz-linear-gradient(top, #3A79B8, #2E629D); + background-image: -webkit-gradient(linear, left top, left bottom, from(#3A79B8), to(#2E629D)); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)"; + + color: white; + font-weight: normal; + font-family: serif; + + border-bottom-width: 0; + -moz-border-radius: 3px 3px 0 0; + -webkit-border-radius: 3px 3px 0 0; + border-radius: 3px 3px 0 0; + } + + .ui-tooltip-tipped .ui-tooltip-content{ + background-color: #F9F9F9; + color: #454545; + + -moz-border-radius: 0 0 3px 3px; + -webkit-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; + } + + .ui-tooltip-tipped .ui-tooltip-icon{ + border: 2px solid #285589; + background: #285589; + } + + .ui-tooltip-tipped .ui-tooltip-icon .ui-icon{ + background-color: #FBFBFB; + color: #555; + } + +/* IE9 fix - removes all filters */ +.ui-tooltip:not(.ie9haxors) div.ui-tooltip-content, +.ui-tooltip:not(.ie9haxors) div.ui-tooltip-titlebar{ + filter: none; + -ms-filter: none; +} \ No newline at end of file diff --git a/static/js/jquery.qtip.js b/static/js/jquery.qtip.js new file mode 100644 index 0000000..1b859f1 --- /dev/null +++ b/static/js/jquery.qtip.js @@ -0,0 +1,3249 @@ +/* +* qTip2 - Pretty powerful tooltips +* http://craigsworks.com/projects/qtip2/ +* +* Version: nightly +* Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com +* +* Dual licensed under MIT or GPLv2 licenses +* http://en.wikipedia.org/wiki/MIT_License +* http://en.wikipedia.org/wiki/GNU_General_Public_License +* +* Date: Mon Dec 19 15:43:28.0000000000 2011 +*/ + +/*jslint browser: true, onevar: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: true */ +/*global window: false, jQuery: false, console: false */ + + +(function($, window, undefined) { + + "use strict"; // Enable ECMAScript "strict" operation for this function. See more: http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ + + // Munge the primitives - Paul Irish tip + var TRUE = true, + FALSE = false, + NULL = null, + + // Shortcut vars + QTIP, PLUGINS, MOUSE, + usedIDs = {}, + uitooltip = 'ui-tooltip', + widget = 'ui-widget', + disabled = 'ui-state-disabled', + selector = 'div.qtip.'+uitooltip, + defaultClass = uitooltip + '-default', + focusClass = uitooltip + '-focus', + hoverClass = uitooltip + '-hover', + fluidClass = uitooltip + '-fluid', + hideOffset = '-31000px', + replaceSuffix = '_replacedByqTip', + oldtitle = 'oldtitle', + trackingBound; + + /* Thanks to Paul Irish for this one: http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ */ + function log() { + log.history = log.history || []; + log.history.push(arguments); + + // Make sure console is present + if('object' === typeof console) { + + // Setup console and arguments + var c = console[ console.warn ? 'warn' : 'log' ], + args = Array.prototype.slice.call(arguments), a; + + // Add qTip2 marker to first argument if it's a string + if(typeof arguments[0] === 'string') { args[0] = 'qTip2: ' + args[0]; } + + // Apply console.warn or .log if not supported + a = c.apply ? c.apply(console, args) : c(args); + } + } + + + + +// Option object sanitizer +function sanitizeOptions(opts) +{ + var content; + + if(!opts || 'object' !== typeof opts) { return FALSE; } + + if(opts.metadata === NULL || 'object' !== typeof opts.metadata) { + opts.metadata = { + type: opts.metadata + }; + } + + if('content' in opts) { + if(opts.content === NULL || 'object' !== typeof opts.content || opts.content.jquery) { + opts.content = { + text: opts.content + }; + } + + content = opts.content.text || FALSE; + if(!$.isFunction(content) && ((!content && !content.attr) || content.length < 1 || ('object' === typeof content && !content.jquery))) { + opts.content.text = FALSE; + } + + if('title' in opts.content) { + if(opts.content.title === NULL || 'object' !== typeof opts.content.title) { + opts.content.title = { + text: opts.content.title + }; + } + + content = opts.content.title.text || FALSE; + if(!$.isFunction(content) && ((!content && !content.attr) || content.length < 1 || ('object' === typeof content && !content.jquery))) { + opts.content.title.text = FALSE; + } + } + } + + if('position' in opts) { + if(opts.position === NULL || 'object' !== typeof opts.position) { + opts.position = { + my: opts.position, + at: opts.position + }; + } + } + + if('show' in opts) { + if(opts.show === NULL || 'object' !== typeof opts.show) { + if(opts.show.jquery) { + opts.show = { target: opts.show }; + } + else { + opts.show = { event: opts.show }; + } + } + } + + if('hide' in opts) { + if(opts.hide === NULL || 'object' !== typeof opts.hide) { + if(opts.hide.jquery) { + opts.hide = { target: opts.hide }; + } + else { + opts.hide = { event: opts.hide }; + } + } + } + + if('style' in opts) { + if(opts.style === NULL || 'object' !== typeof opts.style) { + opts.style = { + classes: opts.style + }; + } + } + + // Sanitize plugin options + $.each(PLUGINS, function() { + if(this.sanitize) { this.sanitize(opts); } + }); + + return opts; +} + +/* +* Core plugin implementation +*/ +function QTip(target, options, id, attr) +{ + // Declare this reference + var self = this, + docBody = document.body, + tooltipID = uitooltip + '-' + id, + isPositioning = 0, + isDrawing = 0, + tooltip = $(), + namespace = '.qtip-' + id, + elements, cache; + + // Setup class attributes + self.id = id; + self.rendered = FALSE; + self.elements = elements = { target: target }; + self.timers = { img: {} }; + self.options = options; + self.checks = {}; + self.plugins = {}; + self.cache = cache = { + event: {}, + target: $(), + disabled: FALSE, + attr: attr + }; + + /* + * Private core functions + */ + function convertNotation(notation) + { + var i = 0, obj, option = options, + + // Split notation into array + levels = notation.split('.'); + + // Loop through + while( option = option[ levels[i++] ] ) { + if(i < levels.length) { obj = option; } + } + + return [obj || options, levels.pop()]; + } + + function setWidget() { + var on = options.style.widget; + + tooltip.toggleClass(widget, on).toggleClass(defaultClass, options.style['default'] && !on); + elements.content.toggleClass(widget+'-content', on); + + if(elements.titlebar){ + elements.titlebar.toggleClass(widget+'-header', on); + } + if(elements.button){ + elements.button.toggleClass(uitooltip+'-icon', !on); + } + } + + function removeTitle(reposition) + { + if(elements.title) { + elements.titlebar.remove(); + elements.titlebar = elements.title = elements.button = NULL; + + // Reposition if enabled + if(reposition !== FALSE) { self.reposition(); } + } + } + + function createButton() + { + var button = options.content.title.button, + isString = typeof button === 'string', + close = isString ? button : 'Close tooltip'; + + if(elements.button) { elements.button.remove(); } + + // Use custom button if one was supplied by user, else use default + if(button.jquery) { + elements.button = button; + } + else { + elements.button = $('', { + 'class': 'ui-state-default ui-tooltip-close ' + (options.style.widget ? '' : uitooltip+'-icon'), + 'title': close, + 'aria-label': close + }) + .prepend( + $('', { + 'class': 'ui-icon ui-icon-close', + 'html': '×' + }) + ); + } + + // Create button and setup attributes + elements.button.appendTo(elements.titlebar) + .attr('role', 'button') + .click(function(event) { + if(!tooltip.hasClass(disabled)) { self.hide(event); } + return FALSE; + }); + + // Redraw the tooltip when we're done + self.redraw(); + } + + function createTitle() + { + var id = tooltipID+'-title'; + + // Destroy previous title element, if present + if(elements.titlebar) { removeTitle(); } + + // Create title bar and title elements + elements.titlebar = $('
', { + 'class': uitooltip + '-titlebar ' + (options.style.widget ? 'ui-widget-header' : '') + }) + .append( + elements.title = $('', { + 'id': id, + 'class': uitooltip + '-title', + 'aria-atomic': TRUE + }) + ) + .insertBefore(elements.content) + + // Button-specific events + .delegate('.ui-tooltip-close', 'mousedown keydown mouseup keyup mouseout', function(event) { + $(this).toggleClass('ui-state-active ui-state-focus', event.type.substr(-4) === 'down'); + }) + .delegate('.ui-tooltip-close', 'mouseover mouseout', function(event){ + $(this).toggleClass('ui-state-hover', event.type === 'mouseover'); + }); + + // Create button if enabled + if(options.content.title.button) { createButton(); } + + // Redraw the tooltip dimensions if it's rendered + else if(self.rendered){ self.redraw(); } + } + + function updateButton(button) + { + var elem = elements.button, + title = elements.title; + + // Make sure tooltip is rendered and if not, return + if(!self.rendered) { return FALSE; } + + if(!button) { + elem.remove(); + } + else { + if(!title) { + createTitle(); + } + createButton(); + } + } + + function updateTitle(content, reposition) + { + var elem = elements.title; + + // Make sure tooltip is rendered and if not, return + if(!self.rendered || !content) { return FALSE; } + + // Use function to parse content + if($.isFunction(content)) { + content = content.call(target, cache.event, self); + } + + // Remove title if callback returns false + if(content === FALSE) { return removeTitle(FALSE); } + + // Append new content if its a DOM array and show it if hidden + else if(content.jquery && content.length > 0) { + elem.empty().append(content.css({ display: 'block' })); + } + + // Content is a regular string, insert the new content + else { elem.html(content); } + + // Redraw and reposition + self.redraw(); + if(reposition !== FALSE && self.rendered && tooltip.is(':visible')) { + self.reposition(cache.event); + } + } + + function updateContent(content, reposition) + { + var elem = elements.content; + + // Make sure tooltip is rendered and content is defined. If not return + if(!self.rendered || !content) { return FALSE; } + + // Use function to parse content + if($.isFunction(content)) { + content = content.call(target, cache.event, self) || ''; + } + + // Append new content if its a DOM array and show it if hidden + if(content.jquery && content.length > 0) { + elem.empty().append(content.css({ display: 'block' })); + } + + // Content is a regular string, insert the new content + else { elem.html(content); } + + // Image detection + function detectImages(next) { + var images, srcs = {}; + + function imageLoad(image) { + // Clear src from object and any timers and events associated with the image + if(image) { + delete srcs[image.src]; + clearTimeout(self.timers.img[image.src]); + $(image).unbind(namespace); + } + + // If queue is empty after image removal, update tooltip and continue the queue + if($.isEmptyObject(srcs)) { + self.redraw(); + if(reposition !== FALSE) { + self.reposition(cache.event); + } + + next(); + } + } + + // Find all content images without dimensions, and if no images were found, continue + if((images = elem.find('img:not([height]):not([width])')).length === 0) { return imageLoad(); } + + // Apply timer to each image to poll for dimensions + images.each(function(i, elem) { + // Skip if the src is already present + if(srcs[elem.src] !== undefined) { return; } + + // Keep track of how many times we poll for image dimensions. + // If it doesn't return in a reasonable amount of time, it's better + // to display the tooltip, rather than hold up the queue. + var iterations = 0, maxIterations = 3; + + (function timer(){ + // When the dimensions are found, remove the image from the queue + if(elem.height || elem.width || (iterations > maxIterations)) { return imageLoad(elem); } + + iterations += 1; + + // Restart timer + self.timers.img[elem.src] = setTimeout(timer, 700); + }()); + + // Also apply regular load/error event handlers + $(elem).bind('error'+namespace+' load'+namespace, function(){ imageLoad(this); }); + + // Store the src and element in our object + srcs[elem.src] = elem; + }); + } + + /* + * If we're still rendering... insert into 'fx' queue our image dimension + * checker which will halt the showing of the tooltip until image dimensions + * can be detected properly. + */ + if(self.rendered < 0) { tooltip.queue('fx', detectImages); } + + // We're fully rendered, so reset isDrawing flag and proceed without queue delay + else { isDrawing = 0; detectImages($.noop); } + + return self; + } + + function assignEvents() + { + var posOptions = options.position, + targets = { + show: options.show.target, + hide: options.hide.target, + viewport: $(posOptions.viewport), + document: $(document), + body: $(document.body), + window: $(window) + }, + events = { + show: $.trim('' + options.show.event).split(' '), + hide: $.trim('' + options.hide.event).split(' ') + }, + IE6 = $.browser.msie && parseInt($.browser.version, 10) === 6; + + // Define show event method + function showMethod(event) + { + if(tooltip.hasClass(disabled)) { return FALSE; } + + // Clear hide timers + clearTimeout(self.timers.show); + clearTimeout(self.timers.hide); + + // Start show timer + var callback = function(){ self.toggle(TRUE, event); }; + if(options.show.delay > 0) { + self.timers.show = setTimeout(callback, options.show.delay); + } + else{ callback(); } + } + + // Define hide method + function hideMethod(event) + { + if(tooltip.hasClass(disabled) || isPositioning || isDrawing) { return FALSE; } + + // Check if new target was actually the tooltip element + var relatedTarget = $(event.relatedTarget || event.target), + ontoTooltip = relatedTarget.closest(selector)[0] === tooltip[0], + ontoTarget = relatedTarget[0] === targets.show[0]; + + // Clear timers and stop animation queue + clearTimeout(self.timers.show); + clearTimeout(self.timers.hide); + + // Prevent hiding if tooltip is fixed and event target is the tooltip. Or if mouse positioning is enabled and cursor momentarily overlaps + if((posOptions.target === 'mouse' && ontoTooltip) || (options.hide.fixed && ((/mouse(out|leave|move)/).test(event.type) && (ontoTooltip || ontoTarget)))) { + try { event.preventDefault(); event.stopImmediatePropagation(); } catch(e) {} return; + } + + // If tooltip has displayed, start hide timer + if(options.hide.delay > 0) { + self.timers.hide = setTimeout(function(){ self.hide(event); }, options.hide.delay); + } + else{ self.hide(event); } + } + + // Define inactive method + function inactiveMethod(event) + { + if(tooltip.hasClass(disabled)) { return FALSE; } + + // Clear timer + clearTimeout(self.timers.inactive); + self.timers.inactive = setTimeout(function(){ self.hide(event); }, options.hide.inactive); + } + + function repositionMethod(event) { + if(tooltip.is(':visible')) { self.reposition(event); } + } + + // On mouseenter/mouseleave... + tooltip.bind('mouseenter'+namespace+' mouseleave'+namespace, function(event) { + var state = event.type === 'mouseenter'; + + // Focus the tooltip on mouseenter (z-index stacking) + if(state) { self.focus(event); } + + // Add hover class + tooltip.toggleClass(hoverClass, state); + }); + + // Enable hide.fixed + if(options.hide.fixed) { + // Add tooltip as a hide target + targets.hide = targets.hide.add(tooltip); + + // Clear hide timer on tooltip hover to prevent it from closing + tooltip.bind('mouseover'+namespace, function() { + if(!tooltip.hasClass(disabled)) { clearTimeout(self.timers.hide); } + }); + } + + // If using mouseout/mouseleave as a hide event... + if(/mouse(out|leave)/i.test(options.hide.event)) { + // Hide tooltips when leaving current window/frame (but not select/option elements) + if(options.hide.leave === 'window') { + targets.window.bind('mouseout'+namespace+' blur'+namespace, function(event) { + if(/select|option/.test(event.target) && !event.relatedTarget) { self.hide(event); } + }); + } + } + + /* + * Make sure hoverIntent functions properly by using mouseleave to clear show timer if + * mouseenter/mouseout is used for show.event, even if it isn't in the users options. + */ + else if(/mouse(over|enter)/i.test(options.show.event)) { + targets.hide.bind('mouseleave'+namespace, function(event) { + clearTimeout(self.timers.show); + }); + } + + // Hide tooltip on document mousedown if unfocus events are enabled + if(('' + options.hide.event).indexOf('unfocus') > -1) { + targets.body.bind('mousedown'+namespace, function(event) { + var $target = $(event.target), + enabled = !tooltip.hasClass(disabled) && tooltip.is(':visible'); + + if($target[0] !== tooltip[0] && $target.parents(selector).length === 0 && $target.add(target).length > 1 && !$target.attr('disabled')) { + self.hide(event); + } + }); + } + + // Check if the tooltip hides when inactive + if('number' === typeof options.hide.inactive) { + // Bind inactive method to target as a custom event + targets.show.bind('qtip-'+id+'-inactive', inactiveMethod); + + // Define events which reset the 'inactive' event handler + $.each(QTIP.inactiveEvents, function(index, type){ + targets.hide.add(elements.tooltip).bind(type+namespace+'-inactive', inactiveMethod); + }); + } + + // Apply hide events + $.each(events.hide, function(index, type) { + var showIndex = $.inArray(type, events.show), + targetHide = $(targets.hide); + + // Both events and targets are identical, apply events using a toggle + if((showIndex > -1 && targetHide.add(targets.show).length === targetHide.length) || type === 'unfocus') + { + targets.show.bind(type+namespace, function(event) { + if(tooltip.is(':visible')) { hideMethod(event); } + else { showMethod(event); } + }); + + // Don't bind the event again + delete events.show[ showIndex ]; + } + + // Events are not identical, bind normally + else { targets.hide.bind(type+namespace, hideMethod); } + }); + + // Apply show events + $.each(events.show, function(index, type) { + targets.show.bind(type+namespace, showMethod); + }); + + // Check if the tooltip hides when mouse is moved a certain distance + if('number' === typeof options.hide.distance) { + // Bind mousemove to target to detect distance difference + targets.show.add(tooltip).bind('mousemove'+namespace, function(event) { + var origin = cache.origin || {}, + limit = options.hide.distance, + abs = Math.abs; + + // Check if the movement has gone beyond the limit, and hide it if so + if(abs(event.pageX - origin.pageX) >= limit || abs(event.pageY - origin.pageY) >= limit) { + self.hide(event); + } + }); + } + + // Mouse positioning events + if(posOptions.target === 'mouse') { + // Cache mousemove coords on show targets + targets.show.bind('mousemove'+namespace, function(event) { + MOUSE = { pageX: event.pageX, pageY: event.pageY, type: 'mousemove' }; + }); + + // If mouse adjustment is on... + if(posOptions.adjust.mouse) { + // Apply a mouseleave event so we don't get problems with overlapping + if(options.hide.event) { + tooltip.bind('mouseleave'+namespace, function(event) { + if((event.relatedTarget || event.target) !== targets.show[0]) { self.hide(event); } + }); + } + + // Update tooltip position on mousemove + targets.document.bind('mousemove'+namespace, function(event) { + // Update the tooltip position only if the tooltip is visible and adjustment is enabled + if(!tooltip.hasClass(disabled) && tooltip.is(':visible')) { + self.reposition(event || MOUSE); + } + }); + } + } + + // Adjust positions of the tooltip on window resize if enabled + if(posOptions.adjust.resize || targets.viewport.length) { + ($.event.special.resize ? targets.viewport : targets.window).bind('resize'+namespace, repositionMethod); + } + + // Adjust tooltip position on scroll if screen adjustment is enabled + if(targets.viewport.length || (IE6 && tooltip.css('position') === 'fixed')) { + targets.viewport.bind('scroll'+namespace, repositionMethod); + } + } + + function unassignEvents() + { + var targets = [ + options.show.target[0], + options.hide.target[0], + self.rendered && elements.tooltip[0], + options.position.container[0], + options.position.viewport[0], + window, + document + ]; + + // Check if tooltip is rendered + if(self.rendered) { + $([]).pushStack( $.grep(targets, function(i){ return typeof i === 'object'; }) ).unbind(namespace); + } + + // Tooltip isn't yet rendered, remove render event + else { options.show.target.unbind(namespace+'-create'); } + } + + // Setup builtin .set() option checks + self.checks.builtin = { + // Core checks + '^id$': function(obj, o, v) { + var id = v === TRUE ? QTIP.nextid : v, + tooltipID = uitooltip + '-' + id; + + if(id !== FALSE && id.length > 0 && !$('#'+tooltipID).length) { + tooltip[0].id = tooltipID; + elements.content[0].id = tooltipID + '-content'; + elements.title[0].id = tooltipID + '-title'; + } + }, + + // Content checks + '^content.text$': function(obj, o, v){ updateContent(v); }, + '^content.title.text$': function(obj, o, v) { + // Remove title if content is null + if(!v) { return removeTitle(); } + + // If title isn't already created, create it now and update + if(!elements.title && v) { createTitle(); } + updateTitle(v); + }, + '^content.title.button$': function(obj, o, v){ updateButton(v); }, + + // Position checks + '^position.(my|at)$': function(obj, o, v){ + // Parse new corner value into Corner objecct + if('string' === typeof v) { + obj[o] = new PLUGINS.Corner(v); + } + }, + '^position.container$': function(obj, o, v){ + if(self.rendered) { tooltip.appendTo(v); } + }, + + // Show checks + '^show.ready$': function() { + if(!self.rendered) { self.render(1); } + else { self.toggle(TRUE); } + }, + + // Style checks + '^style.classes$': function(obj, o, v) { + tooltip.attr('class', uitooltip + ' qtip ui-helper-reset ' + v); + }, + '^style.widget|content.title': setWidget, + + // Events check + '^events.(render|show|move|hide|focus|blur)$': function(obj, o, v) { + tooltip[($.isFunction(v) ? '' : 'un') + 'bind']('tooltip'+o, v); + }, + + // Properties which require event reassignment + '^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)': function() { + var posOptions = options.position; + + // Set tracking flag + tooltip.attr('tracking', posOptions.target === 'mouse' && posOptions.adjust.mouse); + + // Reassign events + unassignEvents(); assignEvents(); + } + }; + + /* + * Public API methods + */ + $.extend(self, { + render: function(show) + { + if(self.rendered) { return self; } // If tooltip has already been rendered, exit + + var text = options.content.text, + title = options.content.title.text, + posOptions = options.position, + callback = $.Event('tooltiprender'); + + // Add ARIA attributes to target + $.attr(target[0], 'aria-describedby', tooltipID); + + // Create tooltip element + tooltip = elements.tooltip = $('', { + 'id': tooltipID, + 'class': uitooltip + ' qtip ui-helper-reset ' + defaultClass + ' ' + options.style.classes + ' '+ uitooltip + '-pos-' + options.position.my.abbrev(), + 'width': options.style.width || '', + 'height': options.style.height || '', + 'tracking': posOptions.target === 'mouse' && posOptions.adjust.mouse, + + /* ARIA specific attributes */ + 'role': 'alert', + 'aria-live': 'polite', + 'aria-atomic': FALSE, + 'aria-describedby': tooltipID + '-content', + 'aria-hidden': TRUE + }) + .toggleClass(disabled, cache.disabled) + .data('qtip', self) + .appendTo(options.position.container) + .append( + // Create content element + elements.content = $('', { + 'class': uitooltip + '-content', + 'id': tooltipID + '-content', + 'aria-atomic': TRUE + }) + ); + + // Set rendered flag and prevent redundant redraw/reposition calls for now + self.rendered = -1; + isDrawing = 1; isPositioning = 1; + + // Create title... + if(title) { + createTitle(); + + // Update title only if its not a callback (called in toggle if so) + if(!$.isFunction(title)) { updateTitle(title, FALSE); } + } + + // Set proper rendered flag and update content if not a callback function (called in toggle) + if(!$.isFunction(text)) { updateContent(text, FALSE); } + self.rendered = TRUE; + + // Setup widget classes + setWidget(); + + // Assign passed event callbacks (before plugins!) + $.each(options.events, function(name, callback) { + if($.isFunction(callback)) { + tooltip.bind(name === 'toggle' ? 'tooltipshow tooltiphide' : 'tooltip'+name, callback); + } + }); + + // Initialize 'render' plugins + $.each(PLUGINS, function() { + if(this.initialize === 'render') { this(self); } + }); + + // Assign events + assignEvents(); + + /* Queue this part of the render process in our fx queue so we can + * load images before the tooltip renders fully. + * + * See: updateContent method + */ + tooltip.queue('fx', function(next) { + // Trigger tooltiprender event and pass original triggering event as original + callback.originalEvent = cache.event; + tooltip.trigger(callback, [self]); + + // Reset flags + isDrawing = 0; isPositioning = 0; + + // Redraw the tooltip manually now we're fully rendered + self.redraw(); + + // Show tooltip if needed + if(options.show.ready || show) { + self.toggle(TRUE, cache.event, FALSE); + } + + next(); // Move on to next method in queue + }); + + return self; + }, + + get: function(notation) + { + var result, o; + + switch(notation.toLowerCase()) + { + case 'dimensions': + result = { + height: tooltip.outerHeight(), width: tooltip.outerWidth() + }; + break; + + case 'offset': + result = PLUGINS.offset(tooltip, options.position.container); + break; + + default: + o = convertNotation(notation.toLowerCase()); + result = o[0][ o[1] ]; + result = result.precedance ? result.string() : result; + break; + } + + return result; + }, + + set: function(option, value) + { + var rmove = /^position\.(my|at|adjust|target|container)|style|content|show\.ready/i, + rdraw = /^content\.(title|attr)|style/i, + reposition = FALSE, + redraw = FALSE, + checks = self.checks, + name; + + function callback(notation, args) { + var category, rule, match; + + for(category in checks) { + for(rule in checks[category]) { + if(match = (new RegExp(rule, 'i')).exec(notation)) { + args.push(match); + checks[category][rule].apply(self, args); + } + } + } + } + + // Convert singular option/value pair into object form + if('string' === typeof option) { + name = option; option = {}; option[name] = value; + } + else { option = $.extend(TRUE, {}, option); } + + // Set all of the defined options to their new values + $.each(option, function(notation, value) { + var obj = convertNotation( notation.toLowerCase() ), previous; + + // Set new obj value + previous = obj[0][ obj[1] ]; + obj[0][ obj[1] ] = 'object' === typeof value && value.nodeType ? $(value) : value; + + // Set the new params for the callback + option[notation] = [obj[0], obj[1], value, previous]; + + // Also check if we need to reposition / redraw + reposition = rmove.test(notation) || reposition; + redraw = rdraw.test(notation) || redraw; + }); + + // Re-sanitize options + sanitizeOptions(options); + + /* + * Execute any valid callbacks for the set options + * Also set isPositioning/isDrawing so we don't get loads of redundant repositioning + * and redraw calls. + */ + isPositioning = isDrawing = 1; $.each(option, callback); isPositioning = isDrawing = 0; + + // Update position / redraw if needed + if(tooltip.is(':visible') && self.rendered) { + if(reposition) { + self.reposition( options.position.target === 'mouse' ? NULL : cache.event ); + } + if(redraw) { self.redraw(); } + } + + return self; + }, + + toggle: function(state, event) + { + // Render the tooltip if showing and it isn't already + if(!self.rendered) { return state ? self.render(1) : self; } + + var type = state ? 'show' : 'hide', + opts = options[type], + visible = tooltip.is(':visible'), + sameTarget = !event || options[type].target.length < 2 || cache.target[0] === event.target, + posOptions = options.position, + contentOptions = options.content, + delay, + callback; + + // Detect state if valid one isn't provided + if((typeof state).search('boolean|number')) { state = !visible; } + + // Return if element is already in correct state + if(!tooltip.is(':animated') && visible === state && sameTarget) { return self; } + + // Try to prevent flickering when tooltip overlaps show element + if(event) { + if((/over|enter/).test(event.type) && (/out|leave/).test(cache.event.type) && + event.target === options.show.target[0] && tooltip.has(event.relatedTarget).length) { + return self; + } + + // Cache event + cache.event = $.extend({}, event); + } + + // Call API methods + callback = $.Event('tooltip'+type); + callback.originalEvent = event ? cache.event : NULL; + tooltip.trigger(callback, [self, 90]); + if(callback.isDefaultPrevented()){ return self; } + + // Set ARIA hidden status attribute + $.attr(tooltip[0], 'aria-hidden', !!!state); + + // Execute state specific properties + if(state) { + // Store show origin coordinates + cache.origin = $.extend({}, MOUSE); + + // Focus the tooltip + self.focus(event); + + // Update tooltip content & title if it's a dynamic function + if($.isFunction(contentOptions.text)) { updateContent(contentOptions.text, FALSE); } + if($.isFunction(contentOptions.title.text)) { updateTitle(contentOptions.title.text, FALSE); } + + // Cache mousemove events for positioning purposes (if not already tracking) + if(!trackingBound && posOptions.target === 'mouse' && posOptions.adjust.mouse) { + $(document).bind('mousemove.qtip', function(event) { + MOUSE = { pageX: event.pageX, pageY: event.pageY, type: 'mousemove' }; + }); + trackingBound = TRUE; + } + + // Update the tooltip position + self.reposition(event, arguments[2]); + + // Hide other tooltips if tooltip is solo, using it as the context + if((callback.solo = !!opts.solo)) { $(selector, opts.solo).not(tooltip).qtip('hide', callback); } + } + else { + // Clear show timer if we're hiding + clearTimeout(self.timers.show); + + // Remove cached origin on hide + delete cache.origin; + + // Remove mouse tracking event if not needed (all tracking qTips are hidden) + if(trackingBound && !$(selector+'[tracking="true"]:visible', opts.solo).not(tooltip).length) { + $(document).unbind('mousemove.qtip'); + trackingBound = FALSE; + } + + // Blur the tooltip + self.blur(event); + } + + // Define post-animation, state specific properties + function after() { + if(state) { + // Prevent antialias from disappearing in IE by removing filter + if($.browser.msie) { tooltip[0].style.removeAttribute('filter'); } + + // Remove overflow setting to prevent tip bugs + tooltip.css('overflow', ''); + + // Autofocus elements if enabled + if('string' === typeof opts.autofocus) { + $(opts.autofocus, tooltip).focus(); + } + + // Call API method + callback = $.Event('tooltipvisible'); + callback.originalEvent = event ? cache.event : NULL; + tooltip.trigger(callback, [self]); + + // If set, hide tooltip when inactive for delay period + opts.target.trigger('qtip-'+id+'-inactive'); + } + else { + // Reset CSS states + tooltip.css({ + display: '', + visibility: '', + opacity: '', + left: '', + top: '' + }); + } + } + + // Clear animation queue if same target + if(sameTarget) { tooltip.stop(0, 1); } + + // If no effect type is supplied, use a simple toggle + if(opts.effect === FALSE) { + tooltip[ type ](); + after.call(tooltip); + } + + // Use custom function if provided + else if($.isFunction(opts.effect)) { + opts.effect.call(tooltip, self); + tooltip.queue('fx', function(n){ after(); n(); }); + } + + // Use basic fade function by default + else { tooltip.fadeTo(90, state ? 1 : 0, after); } + + // If inactive hide method is set, active it + if(state) { opts.target.trigger('qtip-'+id+'-inactive'); } + + return self; + }, + + show: function(event){ return self.toggle(TRUE, event); }, + + hide: function(event){ return self.toggle(FALSE, event); }, + + focus: function(event) + { + if(!self.rendered) { return self; } + + var qtips = $(selector), + curIndex = parseInt(tooltip[0].style.zIndex, 10), + newIndex = QTIP.zindex + qtips.length, + cachedEvent = $.extend({}, event), + focusedElem, callback; + + // Only update the z-index if it has changed and tooltip is not already focused + if(!tooltip.hasClass(focusClass)) + { + // Call API method + callback = $.Event('tooltipfocus'); + callback.originalEvent = cachedEvent; + tooltip.trigger(callback, [self, newIndex]); + + // If default action wasn't prevented... + if(!callback.isDefaultPrevented()) { + // Only update z-index's if they've changed + if(curIndex !== newIndex) { + // Reduce our z-index's and keep them properly ordered + qtips.each(function() { + if(this.style.zIndex > curIndex) { + this.style.zIndex = this.style.zIndex - 1; + } + }); + + // Fire blur event for focused tooltip + qtips.filter('.' + focusClass).qtip('blur', cachedEvent); + } + + // Set the new z-index + tooltip.addClass(focusClass)[0].style.zIndex = newIndex; + } + } + + return self; + }, + + blur: function(event) { + var cachedEvent = $.extend({}, event), + callback; + + // Set focused status to FALSE + tooltip.removeClass(focusClass); + + // Trigger blur event + callback = $.Event('tooltipblur'); + callback.originalEvent = cachedEvent; + tooltip.trigger(callback, [self]); + + return self; + }, + + reposition: function(event, effect) + { + if(!self.rendered || isPositioning) { return self; } + + // Set positioning flag + isPositioning = 1; + + var target = options.position.target, + posOptions = options.position, + my = posOptions.my, + at = posOptions.at, + adjust = posOptions.adjust, + method = adjust.method.split(' '), + elemWidth = tooltip.outerWidth(), + elemHeight = tooltip.outerHeight(), + targetWidth = 0, + targetHeight = 0, + callback = $.Event('tooltipmove'), + fixed = tooltip.css('position') === 'fixed', + viewport = posOptions.viewport, + position = { left: 0, top: 0 }, + container = posOptions.container, + flipoffset = FALSE, + tip = self.plugins.tip, + readjust = { + // Axis detection and readjustment indicator + horizontal: method[0], + vertical: (method[1] = method[1] || method[0]), + enabled: viewport.jquery && target[0] !== window && target[0] !== docBody && adjust.method !== 'none', + + // Reposition methods + left: function(posLeft) { + var isShift = readjust.horizontal === 'shift', + viewportScroll = -container.offset.left + viewport.offset.left + viewport.scrollLeft, + myWidth = my.x === 'left' ? elemWidth : my.x === 'right' ? -elemWidth : -elemWidth / 2, + atWidth = at.x === 'left' ? targetWidth : at.x === 'right' ? -targetWidth : -targetWidth / 2, + tipWidth = tip && tip.size ? tip.size.width || 0 : 0, + tipAdjust = tip && tip.corner && tip.corner.precedance === 'x' && !isShift ? tipWidth : 0, + overflowLeft = viewportScroll - posLeft + tipAdjust, + overflowRight = posLeft + elemWidth - viewport.width - viewportScroll + tipAdjust, + offset = myWidth - (my.precedance === 'x' || my.x === my.y ? atWidth : 0), + isCenter = my.x === 'center'; + + // Optional 'shift' style repositioning + if(isShift) { + tipAdjust = tip && tip.corner && tip.corner.precedance === 'y' ? tipWidth : 0; + offset = (my.x === 'left' ? 1 : -1) * myWidth - tipAdjust; + + // Adjust position but keep it within viewport dimensions + position.left += overflowLeft > 0 ? overflowLeft : overflowRight > 0 ? -overflowRight : 0; + position.left = Math.max( + -container.offset.left + viewport.offset.left + (tipAdjust && tip.corner.x === 'center' ? tip.offset : 0), + posLeft - offset, + Math.min( + Math.max(-container.offset.left + viewport.offset.left + viewport.width, posLeft + offset), + position.left + ) + ); + } + + // Default 'flip' repositioning + else { + if(overflowLeft > 0 && (my.x !== 'left' || overflowRight > 0)) { + position.left -= offset; + } + else if(overflowRight > 0 && (my.x !== 'right' || overflowLeft > 0) ) { + position.left -= isCenter ? -offset : offset; + } + if(position.left !== posLeft && isCenter) { position.left -= adjust.x; } + + // Make sure we haven't made things worse with the adjustment and return the adjusted difference + if(position.left < viewportScroll && -position.left > overflowRight) { position.left = posLeft; } + } + + return position.left - posLeft; + }, + top: function(posTop) { + var isShift = readjust.vertical === 'shift', + viewportScroll = -container.offset.top + viewport.offset.top + viewport.scrollTop, + myHeight = my.y === 'top' ? elemHeight : my.y === 'bottom' ? -elemHeight : -elemHeight / 2, + atHeight = at.y === 'top' ? targetHeight : at.y === 'bottom' ? -targetHeight : -targetHeight / 2, + tipHeight = tip && tip.size ? tip.size.height || 0 : 0, + tipAdjust = tip && tip.corner && tip.corner.precedance === 'y' && !isShift ? tipHeight : 0, + overflowTop = viewportScroll - posTop + tipAdjust, + overflowBottom = posTop + elemHeight - viewport.height - viewportScroll + tipAdjust, + offset = myHeight - (my.precedance === 'y' || my.x === my.y ? atHeight : 0), + isCenter = my.y === 'center'; + + // Optional 'shift' style repositioning + if(isShift) { + tipAdjust = tip && tip.corner && tip.corner.precedance === 'x' ? tipHeight : 0; + offset = (my.y === 'top' ? 1 : -1) * myHeight - tipAdjust; + + // Adjust position but keep it within viewport dimensions + position.top += overflowTop > 0 ? overflowTop : overflowBottom > 0 ? -overflowBottom : 0; + position.top = Math.max( + -container.offset.top + viewport.offset.top + (tipAdjust && tip.corner.x === 'center' ? tip.offset : 0), + posTop - offset, + Math.min( + Math.max(-container.offset.top + viewport.offset.top + viewport.height, posTop + offset), + position.top + ) + ); + } + + // Default 'flip' repositioning + else { + if(overflowTop > 0 && (my.y !== 'top' || overflowBottom > 0)) { + position.top -= offset; + } + else if(overflowBottom > 0 && (my.y !== 'bottom' || overflowTop > 0) ) { + position.top -= isCenter ? -offset : offset; + } + if(position.top !== posTop && isCenter) { position.top -= adjust.y; } + + // Make sure we haven't made things worse with the adjustment and return the adjusted difference + if(position.top < 0 && -position.top > overflowBottom) { position.top = posTop; } + } + + return position.top - posTop; + } + }, + win; + + // Check if absolute position was passed + if($.isArray(target) && target.length === 2) { + // Force left top and set position + at = { x: 'left', y: 'top' }; + position = { left: target[0], top: target[1] }; + } + + // Check if mouse was the target + else if(target === 'mouse' && ((event && event.pageX) || cache.event.pageX)) { + // Force left top to allow flipping + at = { x: 'left', y: 'top' }; + + // Use cached event if one isn't available for positioning + event = (event && (event.type === 'resize' || event.type === 'scroll') ? cache.event : + event && event.pageX && event.type === 'mousemove' ? event : + MOUSE && MOUSE.pageX && (adjust.mouse || !event || !event.pageX) ? { pageX: MOUSE.pageX, pageY: MOUSE.pageY } : + !adjust.mouse && cache.origin && cache.origin.pageX ? cache.origin : + event) || event || cache.event || MOUSE || {}; + + // Use event coordinates for position + position = { top: event.pageY, left: event.pageX }; + } + + // Target wasn't mouse or absolute... + else { + // Check if event targetting is being used + if(target === 'event') { + if(event && event.target && event.type !== 'scroll' && event.type !== 'resize') { + target = cache.target = $(event.target); + } + else { + target = cache.target; + } + } + else { cache.target = $(target); } + + // Parse the target into a jQuery object and make sure there's an element present + target = $(target).eq(0); + if(target.length === 0) { return self; } + + // Check if window or document is the target + else if(target[0] === document || target[0] === window) { + targetWidth = PLUGINS.iOS ? window.innerWidth : target.width(); + targetHeight = PLUGINS.iOS ? window.innerHeight : target.height(); + + if(target[0] === window) { + position = { + top: fixed || PLUGINS.iOS ? (viewport || target).scrollTop() : 0, + left: fixed || PLUGINS.iOS ? (viewport || target).scrollLeft() : 0 + }; + } + } + + // Use Imagemap/SVG plugins if needed + else if(target.is('area') && PLUGINS.imagemap) { + position = PLUGINS.imagemap(target, at, readjust.enabled ? method : FALSE); + } + else if(target[0].namespaceURI === 'http://www.w3.org/2000/svg' && PLUGINS.svg) { + position = PLUGINS.svg(target, at); + } + + else { + targetWidth = target.outerWidth(); + targetHeight = target.outerHeight(); + + position = PLUGINS.offset(target, container); + } + + // Parse returned plugin values into proper variables + if(position.offset) { + targetWidth = position.width; + targetHeight = position.height; + flipoffset = position.flipoffset; + position = position.offset; + } + + // Adjust for position.fixed tooltips (and also iOS scroll bug in v3.2 - v4.0) + if((PLUGINS.iOS < 4.1 && PLUGINS.iOS > 3.1) || PLUGINS.iOS == 4.3 || (!PLUGINS.iOS && fixed)) { + win = $(window); + position.left -= win.scrollLeft(); + position.top -= win.scrollTop(); + } + + // Adjust position relative to target + position.left += at.x === 'right' ? targetWidth : at.x === 'center' ? targetWidth / 2 : 0; + position.top += at.y === 'bottom' ? targetHeight : at.y === 'center' ? targetHeight / 2 : 0; + } + + // Adjust position relative to tooltip + position.left += adjust.x + (my.x === 'right' ? -elemWidth : my.x === 'center' ? -elemWidth / 2 : 0); + position.top += adjust.y + (my.y === 'bottom' ? -elemHeight : my.y === 'center' ? -elemHeight / 2 : 0); + + // Calculate collision offset values if viewport positioning is enabled + if(readjust.enabled) { + // Cache our viewport details + viewport = { + elem: viewport, + height: viewport[ (viewport[0] === window ? 'h' : 'outerH') + 'eight' ](), + width: viewport[ (viewport[0] === window ? 'w' : 'outerW') + 'idth' ](), + scrollLeft: fixed ? 0 : viewport.scrollLeft(), + scrollTop: fixed ? 0 : viewport.scrollTop(), + offset: viewport.offset() || { left: 0, top: 0 } + }; + container = { + elem: container, + scrollLeft: container.scrollLeft(), + scrollTop: container.scrollTop(), + offset: container.offset() || { left: 0, top: 0 } + }; + + // Adjust position based onviewport and adjustment options + position.adjusted = { + left: readjust.horizontal !== 'none' ? readjust.left(position.left) : 0, + top: readjust.vertical !== 'none' ? readjust.top(position.top) : 0 + }; + + // Set tooltip position class + if(position.adjusted.left + position.adjusted.top) { + tooltip.attr('class', tooltip[0].className.replace(/ui-tooltip-pos-\w+/i, uitooltip + '-pos-' + my.abbrev())); + } + + // Apply flip offsets supplied by positioning plugins + if(flipoffset && position.adjusted.left) { position.left += flipoffset.left; } + if(flipoffset && position.adjusted.top) { position.top += flipoffset.top; } + } + + //Viewport adjustment is disabled, set values to zero + else { position.adjusted = { left: 0, top: 0 }; } + + // Call API method + callback.originalEvent = $.extend({}, event); + tooltip.trigger(callback, [self, position, viewport.elem || viewport]); + if(callback.isDefaultPrevented()){ return self; } + delete position.adjusted; + + // If effect is disabled, target it mouse, no animation is defined or positioning gives NaN out, set CSS directly + if(effect === FALSE || isNaN(position.left) || isNaN(position.top) || target === 'mouse' || !$.isFunction(posOptions.effect)) { + tooltip.css(position); + } + + // Use custom function if provided + else if($.isFunction(posOptions.effect)) { + posOptions.effect.call(tooltip, self, $.extend({}, position)); + tooltip.queue(function(next) { + // Reset attributes to avoid cross-browser rendering bugs + $(this).css({ opacity: '', height: '' }); + if($.browser.msie) { this.style.removeAttribute('filter'); } + + next(); + }); + } + + // Set positioning flag + isPositioning = 0; + + return self; + }, + + // Max/min width simulator function for all browsers.. yeaaah! + redraw: function() + { + if(self.rendered < 1 || isDrawing) { return self; } + + var container = options.position.container, + perc, width, max, min; + + // Set drawing flag + isDrawing = 1; + + // If tooltip has a set height, just set it... like a boss! + if(options.style.height) { tooltip.css('height', options.style.height); } + + // If tooltip has a set width, just set it... like a boss! + if(options.style.width) { tooltip.css('width', options.style.width); } + + // Otherwise simualte max/min width... + else { + // Reset width and add fluid class + tooltip.css('width', '').addClass(fluidClass); + + // Grab our tooltip width (add 1 so we don't get wrapping problems.. huzzah!) + width = tooltip.width() + 1; + + // Grab our max/min properties + max = tooltip.css('max-width') || ''; + min = tooltip.css('min-width') || ''; + + // Parse into proper pixel values + perc = (max + min).indexOf('%') > -1 ? container.width() / 100 : 0; + max = ((max.indexOf('%') > -1 ? perc : 1) * parseInt(max, 10)) || width; + min = ((min.indexOf('%') > -1 ? perc : 1) * parseInt(min, 10)) || 0; + + // Determine new dimension size based on max/min/current values + width = max + min ? Math.min(Math.max(width, min), max) : width; + + // Set the newly calculated width and remvoe fluid class + tooltip.css('width', Math.round(width)).removeClass(fluidClass); + } + + // Set drawing flag + isDrawing = 0; + + return self; + }, + + disable: function(state) + { + if('boolean' !== typeof state) { + state = !(tooltip.hasClass(disabled) || cache.disabled); + } + + if(self.rendered) { + tooltip.toggleClass(disabled, state); + $.attr(tooltip[0], 'aria-disabled', state); + } + else { + cache.disabled = !!state; + } + + return self; + }, + + enable: function() { return self.disable(FALSE); }, + + destroy: function() + { + var t = target[0], + title = $.attr(t, oldtitle), + elemAPI = target.data('qtip'); + + // Destroy tooltip and any associated plugins if rendered + if(self.rendered) { + tooltip.remove(); + + $.each(self.plugins, function() { + if(this.destroy) { this.destroy(); } + }); + } + + // Clear timers and remove bound events + clearTimeout(self.timers.show); + clearTimeout(self.timers.hide); + unassignEvents(); + + // If the API if actually this qTip API... + if(!elemAPI || self === elemAPI) { + // Remove api object + $.removeData(t, 'qtip'); + + // Reset old title attribute if removed + if(options.suppress && title) { + $.attr(t, 'title', title); + target.removeAttr(oldtitle); + } + + // Remove ARIA attributes + target.removeAttr('aria-describedby'); + } + + // Remove qTip events associated with this API + target.unbind('.qtip-'+id); + + // Remove ID from sued id object + delete usedIDs[self.id]; + + return target; + } + }); +} + +// Initialization method +function init(id, opts) +{ + var obj, posOptions, attr, config, title, + + // Setup element references + elem = $(this), + docBody = $(document.body), + + // Use document body instead of document element if needed + newTarget = this === document ? docBody : elem, + + // Grab metadata from element if plugin is present + metadata = (elem.metadata) ? elem.metadata(opts.metadata) : NULL, + + // If metadata type if HTML5, grab 'name' from the object instead, or use the regular data object otherwise + metadata5 = opts.metadata.type === 'html5' && metadata ? metadata[opts.metadata.name] : NULL, + + // Grab data from metadata.name (or data-qtipopts as fallback) using .data() method, + html5 = elem.data(opts.metadata.name || 'qtipopts'); + + // If we don't get an object returned attempt to parse it manualyl without parseJSON + try { html5 = typeof html5 === 'string' ? (new Function("return " + html5))() : html5; } + catch(e) { log('Unable to parse HTML5 attribute data: ' + html5); } + + // Merge in and sanitize metadata + config = $.extend(TRUE, {}, QTIP.defaults, opts, + typeof html5 === 'object' ? sanitizeOptions(html5) : NULL, + sanitizeOptions(metadata5 || metadata)); + + // Re-grab our positioning options now we've merged our metadata and set id to passed value + posOptions = config.position; + config.id = id; + + // Setup missing content if none is detected + if('boolean' === typeof config.content.text) { + attr = elem.attr(config.content.attr); + + // Grab from supplied attribute if available + if(config.content.attr !== FALSE && attr) { config.content.text = attr; } + + // No valid content was found, abort render + else { + log('Unable to locate content for tooltip! Aborting render of tooltip on element: ', elem); + return FALSE; + } + } + + // Setup target options + if(!posOptions.container.length) { posOptions.container = docBody; } + if(posOptions.target === FALSE) { posOptions.target = newTarget; } + if(config.show.target === FALSE) { config.show.target = newTarget; } + if(config.show.solo === TRUE) { config.show.solo = docBody; } + if(config.hide.target === FALSE) { config.hide.target = newTarget; } + if(config.position.viewport === TRUE) { config.position.viewport = posOptions.container; } + + // Convert position corner values into x and y strings + posOptions.at = new PLUGINS.Corner(posOptions.at); + posOptions.my = new PLUGINS.Corner(posOptions.my); + + // Destroy previous tooltip if overwrite is enabled, or skip element if not + if($.data(this, 'qtip')) { + if(config.overwrite) { + elem.qtip('destroy'); + } + else if(config.overwrite === FALSE) { + return FALSE; + } + } + + // Remove title attribute and store it if present + if(config.suppress && (title = $.attr(this, 'title'))) { + $(this).removeAttr('title').attr(oldtitle, title); + } + + // Initialize the tooltip and add API reference + obj = new QTip(elem, config, id, !!attr); + $.data(this, 'qtip', obj); + + // Catch remove events on target element to destroy redundant tooltip + elem.bind('remove.qtip-'+id, function(){ obj.destroy(); }); + + return obj; +} + +// jQuery $.fn extension method +QTIP = $.fn.qtip = function(options, notation, newValue) +{ + var command = ('' + options).toLowerCase(), // Parse command + returned = NULL, + args = $.makeArray(arguments).slice(1), + event = args[args.length - 1], + opts = this[0] ? $.data(this[0], 'qtip') : NULL; + + // Check for API request + if((!arguments.length && opts) || command === 'api') { + return opts; + } + + // Execute API command if present + else if('string' === typeof options) + { + this.each(function() + { + var api = $.data(this, 'qtip'); + if(!api) { return TRUE; } + + // Cache the event if possible + if(event && event.timeStamp) { api.cache.event = event; } + + // Check for specific API commands + if((command === 'option' || command === 'options') && notation) { + if($.isPlainObject(notation) || newValue !== undefined) { + api.set(notation, newValue); + } + else { + returned = api.get(notation); + return FALSE; + } + } + + // Execute API command + else if(api[command]) { + api[command].apply(api[command], args); + } + }); + + return returned !== NULL ? returned : this; + } + + // No API commands. validate provided options and setup qTips + else if('object' === typeof options || !arguments.length) + { + opts = sanitizeOptions($.extend(TRUE, {}, options)); + + // Bind the qTips + return QTIP.bind.call(this, opts, event); + } +}; + +// $.fn.qtip Bind method +QTIP.bind = function(opts, event) +{ + return this.each(function(i) { + var options, targets, events, namespace, api, id; + + // Find next available ID, or use custom ID if provided + id = $.isArray(opts.id) ? opts.id[i] : opts.id; + id = !id || id === FALSE || id.length < 1 || usedIDs[id] ? QTIP.nextid++ : (usedIDs[id] = id); + + // Setup events namespace + namespace = '.qtip-'+id+'-create'; + + // Initialize the qTip and re-grab newly sanitized options + api = init.call(this, id, opts); + if(api === FALSE) { return TRUE; } + options = api.options; + + // Initialize plugins + $.each(PLUGINS, function() { + if(this.initialize === 'initialize') { this(api); } + }); + + // Determine hide and show targets + targets = { show: options.show.target, hide: options.hide.target }; + events = { + show: $.trim('' + options.show.event).replace(/ /g, namespace+' ') + namespace, + hide: $.trim('' + options.hide.event).replace(/ /g, namespace+' ') + namespace + }; + + /* + * Make sure hoverIntent functions properly by using mouseleave as a hide event if + * mouseenter/mouseout is used for show.event, even if it isn't in the users options. + */ + if(/mouse(over|enter)/i.test(events.show) && !/mouse(out|leave)/i.test(events.hide)) { + events.hide += ' mouseleave' + namespace; + } + + /* + * Also make sure initial mouse targetting works correctly by caching mousemove coords + * on show targets before the tooltip has rendered. + */ + targets.show.bind('mousemove'+namespace, function(event) { + MOUSE = { pageX: event.pageX, pageY: event.pageY, type: 'mousemove' }; + }); + + // Define hoverIntent function + function hoverIntent(event) { + function render() { + // Cache mouse coords,render and render the tooltip + api.render(typeof event === 'object' || options.show.ready); + + // Unbind show and hide events + targets.show.add(targets.hide).unbind(namespace); + } + + // Only continue if tooltip isn't disabled + if(api.cache.disabled) { return FALSE; } + + // Cache the event data + api.cache.event = $.extend({}, event); + api.cache.target = event ? $(event.target) : [undefined]; + + // Start the event sequence + if(options.show.delay > 0) { + clearTimeout(api.timers.show); + api.timers.show = setTimeout(render, options.show.delay); + if(events.show !== events.hide) { + targets.hide.bind(events.hide, function() { clearTimeout(api.timers.show); }); + } + } + else { render(); } + } + + // Bind show events to target + targets.show.bind(events.show, hoverIntent); + + // Prerendering is enabled, create tooltip now + if(options.show.ready || options.prerender) { hoverIntent(event); } + }); +}; + +// Setup base plugins +PLUGINS = QTIP.plugins = { + // Corner object parser + Corner: function(corner) { + corner = ('' + corner).replace(/([A-Z])/, ' $1').replace(/middle/gi, 'center').toLowerCase(); + this.x = (corner.match(/left|right/i) || corner.match(/center/) || ['inherit'])[0].toLowerCase(); + this.y = (corner.match(/top|bottom|center/i) || ['inherit'])[0].toLowerCase(); + + var f = corner.charAt(0); this.precedance = (f === 't' || f === 'b' ? 'y' : 'x'); + + this.string = function() { return this.precedance === 'y' ? this.y+this.x : this.x+this.y; }; + this.abbrev = function() { + var x = this.x.substr(0,1), y = this.y.substr(0,1); + return x === y ? x : (x === 'c' || (x !== 'c' && y !== 'c')) ? y + x : x + y; + }; + + this.clone = function() { + return { x: this.x, y: this.y, precedance: this.precedance, string: this.string, abbrev: this.abbrev, clone: this.clone }; + }; + }, + + // Custom (more correct for qTip!) offset calculator + offset: function(elem, container) { + var pos = elem.offset(), + parent = container, + deep = 0, + docBody = document.body, + coffset, overflow; + + function scroll(e, i) { + pos.left += i * e.scrollLeft(); + pos.top += i * e.scrollTop(); + } + + if(parent) { + // Compensate for non-static containers offset + do { + if(parent.css('position') !== 'static') { + coffset = parent[0] === docBody ? + { left: parseInt(parent.css('left'), 10) || 0, top: parseInt(parent.css('top'), 10) || 0 } : + parent.position(); + + pos.left -= coffset.left + (parseInt(parent.css('borderLeftWidth'), 10) || 0) + (parseInt(parent.css('marginLeft'), 10) || 0); + pos.top -= coffset.top + (parseInt(parent.css('borderTopWidth'), 10) || 0); + + overflow = parent.css('overflow'); + if(overflow === 'scroll' || overflow === 'auto') { deep++; } + } + + if(parent[0] === docBody) { break; } + } + while(parent = parent.offsetParent()); + + // Compensate for containers scroll if it also has an offsetParent + if(container[0] !== docBody && deep) { scroll( container, 1 ); } + } + + return pos; + }, + + /* + * iOS 3.2 - 4.0 scroll fix detection used in offset() function. + */ + iOS: parseFloat( + ('' + (/CPU.*OS ([0-9_]{1,3})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0,''])[1]) + .replace('undefined', '3_2').replace('_','.') + ) || FALSE, + + /* + * jQuery-specific $.fn overrides + */ + fn: { + /* Allow other plugins to successfully retrieve the title of an element with a qTip applied */ + attr: function(attr, val) { + if(this.length) { + var self = this[0], + title = 'title', + api = $.data(self, 'qtip'); + + if(attr === title && api && 'object' === typeof api && api.options.suppress) { + if(arguments.length < 2) { + return $.attr(self, oldtitle); + } + else { + // If qTip is rendered and title was originally used as content, update it + if(api && api.options.content.attr === title && api.cache.attr) { + api.set('content.text', val); + } + + // Use the regular attr method to set, then cache the result + return this.attr(oldtitle, val); + } + } + } + + return $.fn['attr'+replaceSuffix].apply(this, arguments); + }, + + /* Allow clone to correctly retrieve cached title attributes */ + clone: function(keepData) { + var titles = $([]), title = 'title', + + // Clone our element using the real clone method + elems = $.fn['clone'+replaceSuffix].apply(this, arguments); + + // Grab all elements with an oldtitle set, and change it to regular title attribute, if keepData is false + if(!keepData) { + elems.filter('['+oldtitle+']').attr('title', function() { + return $.attr(this, oldtitle); + }) + .removeAttr(oldtitle); + } + + return elems; + }, + + /* + * Taken directly from jQuery 1.8.2 widget source code + * Trigger 'remove' event on all elements on removal + */ + remove: $.ui ? NULL : function( selector, keepData ) { + if($.ui) { return; } // We don't need to do this if jQuery UI is present! + + $(this).each(function() { + if (!keepData) { + if (!selector || $.filter( selector, [ this ] ).length) { + $('*', this).add(this).each(function() { + $(this).triggerHandler('remove'); + }); + } + } + }); + } + } +}; + +// Apply the fn overrides above +$.each(PLUGINS.fn, function(name, func) { + if(!func || $.fn[name+replaceSuffix]) { return TRUE; } + + var old = $.fn[name+replaceSuffix] = $.fn[name]; + $.fn[name] = function() { + return func.apply(this, arguments) || old.apply(this, arguments); + }; +}); + +// Set global qTip properties +QTIP.version = 'nightly'; +QTIP.nextid = 0; +QTIP.inactiveEvents = 'click dblclick mousedown mouseup mousemove mouseleave mouseenter'.split(' '); +QTIP.zindex = 15000; + +// Define configuration defaults +QTIP.defaults = { + prerender: FALSE, + id: FALSE, + overwrite: TRUE, + suppress: TRUE, + content: { + text: TRUE, + attr: 'title', + title: { + text: FALSE, + button: FALSE + } + }, + position: { + my: 'top left', + at: 'bottom right', + target: FALSE, + container: FALSE, + viewport: FALSE, + adjust: { + x: 0, y: 0, + mouse: TRUE, + resize: TRUE, + method: 'flip flip' + }, + effect: function(api, pos, viewport) { + $(this).animate(pos, { + duration: 200, + queue: FALSE + }); + } + }, + show: { + target: FALSE, + event: 'mouseenter', + effect: TRUE, + delay: 90, + solo: FALSE, + ready: FALSE, + autofocus: FALSE + }, + hide: { + target: FALSE, + event: 'mouseleave', + effect: TRUE, + delay: 0, + fixed: FALSE, + inactive: FALSE, + leave: 'window', + distance: FALSE + }, + style: { + classes: '', + widget: FALSE, + width: FALSE, + height: FALSE, + 'default': TRUE + }, + events: { + render: NULL, + move: NULL, + show: NULL, + hide: NULL, + toggle: NULL, + visible: NULL, + focus: NULL, + blur: NULL + } +}; + + + + +function Ajax(api) +{ + var self = this, + tooltip = api.elements.tooltip, + opts = api.options.content.ajax, + namespace = '.qtip-ajax', + rscript = / + + + {% endblock %} {%block header%} {% include "nav.html" with active="profile" %} @@ -35,8 +38,8 @@