PageSourceSearch

https://uia.org/sites/uia.org/files/js/js_rHWeWTYA3NYz-Cyuv25dZ0vdJ27HxiNF_sNCa_dRG9A.js

js uia.org collected 2026-10-01 10:44:02 UTC 144,510 bytes, 4,091 lines download raw bytes

1
2/*!
3 * jQuery UI 1.8.7
4 *
5 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
6 * Dual licensed under the MIT or GPL Version 2 licenses.
7 * http://jquery.org/license
8 *
9 * http://docs.jquery.com/UI
10 */
11(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.7",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,
12NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,
13"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");
14if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f,
15"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,
16d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}});
17c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&
18b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&c.ui.isOverAxis(b,e,i)}})}})(jQuery);
19;
20
21/*!
22 * jQuery UI Widget 1.8.7
23 *
24 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
25 * Dual licensed under the MIT or GPL Version 2 licenses.
26 * http://jquery.org/license
27 *
28 * http://docs.jquery.com/UI/Widget
29 */
30(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)b(d).triggerHandler("remove");k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){b(this).triggerHandler("remove")});
30return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,
31a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.charAt(0)==="_")return h;
32e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=b.extend(true,{},this.options,
33this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},
34widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},
35enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
36;
37/*
38* qTip2 - Pretty powerful tooltips
39* http://craigsworks.com/projects/qtip2/
40*
41* Version: 2.0.0pre
42* Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com
43*
44* Dual licensed under MIT or GPLv2 licenses
45*   http://en.wikipedia.org/wiki/MIT_License
46*   http://en.wikipedia.org/wiki/GNU_General_Public_License
47*
48* Date: Fri Aug 19 08:14:39 2011 +0100
49*/
50
51/*jslint browser: true, onevar: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: true */
52/*global window: false, jQuery: false, console: false */
53
54
55(function($, window, undefined) {
56
57	"use strict"; // Enable ECMAScript "strict" operation for this function. See more: http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
58	
59	// Munge the primitives - Paul Irish tip
60	var TRUE = true,
61		FALSE = false,
62		NULL = null,
63		
64		// Shortcut vars
65		QTIP, PLUGINS, MOUSE,
66		usedIDs = {},
67		uitooltip = 'ui-tooltip',
68		widget = 'ui-widget',
69		disabled = 'ui-state-disabled',
70		selector = 'div.qtip.'+uitooltip,
71		defaultClass = uitooltip + '-default',
72		focusClass = uitooltip + '-focus',
73		hoverClass = uitooltip + '-hover',
74		fluidClass = uitooltip + '-fluid',
75		hideOffset = '-31000px',
76		replaceSuffix = '_replacedByqTip',
77		oldtitle = 'oldtitle',
78		trackingBound;
79		
80	/* Thanks to Paul Irish for this one: http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ */
81	function log() {
82		log.history = log.history || [];
83		log.history.push(arguments);
84		
85		// Make sure console is present
86		if('object' === typeof console) {
87
88			// Setup console and arguments
89			var c = console[ console.warn ? 'warn' : 'log' ],
90			args = Array.prototype.slice.call(arguments), a;
91
92			// Add qTip2 marker to first argument if it's a string
93			if(typeof arguments[0] === 'string') { args[0] = 'qTip2: ' + args[0]; }
94
95			// Apply console.warn or .log if not supported
96			a = c.apply ? c.apply(console, args) : c(args);
97		}
98	}
99
100// Option object sanitizer
101function sanitizeOptions(opts)
102{
103	var content;
104
105	if(!opts || 'object' !== typeof opts) { return FALSE; }
106
107	if('object' !== typeof opts.metadata) {
108		opts.metadata = {
109			type: opts.metadata
110		};
111	}
112
113	if('content' in opts) {
114		if('object' !== typeof opts.content || opts.content.jquery) {
115			opts.content = {
116				text: opts.content
117			};
118		}
119
120		content = opts.content.text || FALSE;
121		if(!$.isFunction(content) && ((!content && !content.attr) || content.length < 1 || ('object' === typeof content && !content.jquery))) {
122			opts.content.text = FALSE;
123		}
124
125		if('title' in opts.content) {
126			if('object' !== typeof opts.content.title) {
127				opts.content.title = {
128					text: opts.content.title
129				};
130			}
131
132			content = opts.content.title.text || FALSE;
133			if(!$.isFunction(content) && ((!content && !content.attr) || content.length < 1 || ('object' === typeof content && !content.jquery))) {
134				opts.content.title.text = FALSE;
135			}
136		}
137	}
138
139	if('position' in opts) {
140		if('object' !== typeof opts.position) {
141			opts.position = {
142				my: opts.position,
143				at: opts.position
144			};
145		}
146	}
147
148	if('show' in opts) {
149		if('object' !== typeof opts.show) {
150			if(opts.show.jquery) {
151				opts.show = { target: opts.show };
152			}
153			else {
154				opts.show = { event: opts.show };
155			}
156		}
157	}
158
159	if('hide' in opts) {
160		if('object' !== typeof opts.hide) {
161			if(opts.hide.jquery) {
162				opts.hide = { target: opts.hide };
163			}
164			else {
165				opts.hide = { event: opts.hide };
166			}
167		}
168	}
169
170	if('style' in opts) {
171		if('object' !== typeof opts.style) {
172			opts.style = {
173				classes: opts.style
174			};
175		}
176	}
177
178	// Sanitize plugin options
179	$.each(PLUGINS, function() {
180		if(this.sanitize) { this.sanitize(opts); }
181	});
182	
183	return opts;
184}
185
186/*
187* Core plugin implementation
188*/
189function QTip(target, options, id, attr)
190{
191	// Declare this reference
192	var self = this,
193		docBody = document.body,
194		tooltipID = uitooltip + '-' + id,
195		isPositioning = 0,
196		isDrawing = 0,
197		tooltip = $(),
198		namespace = '.qtip-' + id,
199		elements, cache;
200
201	// Setup class attributes
202	self.id = id;
203	self.rendered = FALSE;
204	self.elements = elements = { target: target };
205	self.timers = { img: {} };
206	self.options = options;
207	self.checks = {};
208	self.plugins = {};
209	self.cache = cache = {
210		event: {},
211		target: $(),
212		disabled: FALSE,
213		attr: attr
214	};
215
216	/*
217	* Private core functions
218	*/
219	function convertNotation(notation)
220	{
221		var i = 0, obj, option = options, 
222
223		// Split notation into array
224		levels = notation.split('.');
225
226		// Loop through
227		while( option = option[ levels[i++] ] ) {
228			if(i < levels.length) { obj = option; }
229		}
230
231		return [obj || options, levels.pop()];
232	}
233
234	function setWidget() {
235		var on = options.style.widget;
236
237		tooltip.toggleClass(widget, on).toggleClass(defaultClass, !on);
238		elements.content.toggleClass(widget+'-content', on);
239		
240		if(elements.titlebar){
241			elements.titlebar.toggleClass(widget+'-header', on);
242		}
243		if(elements.button){
244			elements.button.toggleClass(uitooltip+'-icon', !on);
245		}
246	}
247
248	function removeTitle(reposition)
249	{
250		if(elements.title) {
251			elements.titlebar.remove();
252			elements.titlebar = elements.title = elements.button = NULL;
253
254			// Reposition if enabled
255			if(reposition !== FALSE) { self.reposition(); }
256		}
257	}
258
259	function createButton()
260	{
261		var button = options.content.title.button,
262			isString = typeof button === 'string',
263			close = isString ? button : 'Close tooltip';
264
265		if(elements.button) { elements.button.remove(); }
266
267		// Use custom button if one was supplied by user, else use default
268		if(button.jquery) {
269			elements.button = button;
270		}
271		else {
272			elements.button = $('<a />', {
273				'class': 'ui-state-default ' + (options.style.widget ? '' : uitooltip+'-icon'),
274				'title': close,
275				'aria-label': close
276			})
277			.prepend(
278				$('<span />', {
279					'class': 'ui-icon ui-icon-close',
280					'html': '&times;'
281				})
282			);
283		}
284
285		// Create button and setup attributes
286		elements.button.appendTo(elements.titlebar)
287			.attr('role', 'button')
288			.hover(function(event){ $(this).toggleClass('ui-state-hover', event.type === 'mouseenter'); })
289			.click(function(event) {
290				if(!tooltip.hasClass(disabled)) { self.hide(event); }
291				return FALSE;
292			})
293			.bind('mousedown keydown mouseup keyup mouseout', function(event) {
294				$(this).toggleClass('ui-state-active ui-state-focus', event.type.substr(-4) === 'down');
295			});
296
297		// Redraw the tooltip when we're done
298		self.redraw();
299	}
300
301	function createTitle()
302	{
303		var id = tooltipID+'-title';
304
305		// Destroy previous title element, if present
306		if(elements.titlebar) { removeTitle(); }
307
308		// Create title bar and title elements
309		elements.titlebar = $('<div />', {
310			'class': uitooltip + '-titlebar ' + (options.style.widget ? 'ui-widget-header' : '')
311		})
312		.append(
313			elements.title = $('<div />', {
314				'id': id,
315				'class': uitooltip + '-title',
316				'aria-atomic': TRUE
317			})
318		)
319		.insertBefore(elements.content);
320
321		// Create button if enabled
322		if(options.content.title.button) { createButton(); }
323
324		// Redraw the tooltip dimensions if it's rendered
325		else if(self.rendered){ self.redraw(); } 
326	}
327
328	function updateButton(button)
329	{
330		var elem = elements.button,
331			title = elements.title;
332
333		// Make sure tooltip is rendered and if not, return
334		if(!self.rendered) { return FALSE; }
335
336		if(!button) {
337			elem.remove();
338		}
339		else {
340			if(!title) {
341				createTitle();
342			}
343			createButton();
344		}
345	}
346
347	function updateTitle(content, reposition)
348	{
349		var elem = elements.title;
350
351		// Make sure tooltip is rendered and if not, return
352		if(!self.rendered || !content) { return FALSE; }
353
354		// Use function to parse content
355		if($.isFunction(content)) {
356			content = content.call(target, cache.event, self);
357		}
358
359		// Remove title if callback returns false
360		if(content === FALSE) { return removeTitle(FALSE); }
361
362		// Append new content if its a DOM array and show it if hidden
363		else if(content.jquery && content.length > 0) {
364			elem.empty().append(content.css({ display: 'block' }));
365		}
366
367		// Content is a regular string, insert the new content
368		else { elem.html(content); }
369
370		// Redraw and reposition
371		self.redraw();
372		if(reposition !== FALSE && self.rendered && tooltip.is(':visible')) {
373			self.reposition(cache.event);
374		}
375	}
376
377	function updateContent(content, reposition)
378	{
379		var elem = elements.content;
380
381		// Make sure tooltip is rendered and content is defined. If not return
382		if(!self.rendered || !content) { return FALSE; }
383
384		// Use function to parse content
385		if($.isFunction(content)) {
386			content = content.call(target, cache.event, self) || '';
387		}
388
389		// Append new content if its a DOM array and show it if hidden
390		if(content.jquery && content.length > 0) {
391			elem.empty().append(content.css({ display: 'block' }));
392		}
393
394		// Content is a regular string, insert the new content
395		else { elem.html(content); }
396
397		// Image detection
398		function detectImages(next) {
399			var images, srcs = {};
400
401			function imageLoad(image) {
402				// Clear src from object and any timers and events associated with the image
403				if(image) {
404					delete srcs[image.src];
405					clearTimeout(self.timers.img[image.src]);
406					$(image).unbind(namespace);
407				}
408
409				// If queue is empty after image removal, update tooltip and continue the queue
410				if($.isEmptyObject(srcs)) {
411					self.redraw();
412					if(reposition !== FALSE) {
413						self.reposition(cache.event);
414					}
415					
416					next();
417				}
418			}
419
420			// Find all content images without dimensions, and if no images were found, continue
421			if((images = elem.find('img:not([height]):not([width])')).length === 0) { return imageLoad(); }
422
423			// Apply timer to each image to poll for dimensions
424			images.each(function(i, elem) {
425				// Skip if the src is already present
426				if(srcs[elem.src] !== undefined) { return; }
427
428				(function timer(){
429					// When the dimensions are found, remove the image from the queue
430					if(elem.height || elem.width) { return imageLoad(elem); }
431
432					// Restart timer
433					self.timers.img[elem.src] = setTimeout(timer, 700);
434				}());
435
436				// Also apply regular load/error event handlers
437				$(elem).bind('error'+namespace+' load'+namespace, function(){ imageLoad(this); });
438
439				// Store the src and element in our object
440				srcs[elem.src] = elem;
441			});
442		}
443
444		/*
445		 * If we're still rendering... insert into 'fx' queue our image dimension
446		 * checker which will halt the showing of the tooltip until image dimensions
447		 * can be detected properly.
448		 */
449		if(self.rendered < 0) { tooltip.queue('fx', detectImages); }
450
451		// We're fully rendered, so reset isDrawing flag and proceed without queue delay
452		else { isDrawing = 0; detectImages($.noop); }
453
454		return self;
455	}
456
457	function assignEvents()
458	{
459		var posOptions = options.position,
460			targets = {
461				show: options.show.target,
462				hide: options.hide.target,
463				viewport: $(posOptions.viewport),
464				document: $(document),
465				window: $(window)
466			},
467			events = {
468				show: $.trim('' + options.show.event).split(' '),
469				hide: $.trim('' + options.hide.event).split(' ')
470			},
471			IE6 = $.browser.msie && parseInt($.browser.version, 10) === 6;
472
473		// Define show event method
474		function showMethod(event)
475		{
476			if(tooltip.hasClass(disabled)) { return FALSE; }
477
478			// If set, hide tooltip when inactive for delay period
479			targets.show.trigger('qtip-'+id+'-inactive');
480
481			// Clear hide timers
482			clearTimeout(self.timers.show);
483			clearTimeout(self.timers.hide);
484
485			// Start show timer
486			var callback = function(){ self.toggle(TRUE, event); };
487			if(options.show.delay > 0) {
488				self.timers.show = setTimeout(callback, options.show.delay);
489			}
490			else{ callback(); }
491		}
492
493		// Define hide method
494		function hideMethod(event)
495		{
496			if(tooltip.hasClass(disabled) || isPositioning || isDrawing) { return FALSE; }
497
498			// Check if new target was actually the tooltip element
499			var relatedTarget = $(event.relatedTarget || event.target),
500				ontoTooltip = relatedTarget.closest(selector)[0] === tooltip[0],
501				ontoTarget = relatedTarget[0] === targets.show[0];
502
503			// Clear timers and stop animation queue
504			clearTimeout(self.timers.show);
505			clearTimeout(self.timers.hide);
506
507			// Prevent hiding if tooltip is fixed and event target is the tooltip. Or if mouse positioning is enabled and curs
507or momentarily overlaps
508			if((posOptions.target === 'mouse' && ontoTooltip) || (options.hide.fixed && ((/mouse(out|leave|move)/).test(event.type) && (ontoTooltip || ontoTarget)))) {
509				event.preventDefault(); event.stopImmediatePropagation(); return;
510			}
511
512			// If tooltip has displayed, start hide timer
513			if(options.hide.delay > 0) {
514				self.timers.hide = setTimeout(function(){ self.hide(event); }, options.hide.delay);
515			}
516			else{ self.hide(event); }
517		}
518
519		// Define inactive method
520		function inactiveMethod(event)
521		{
522			if(tooltip.hasClass(disabled)) { return FALSE; }
523
524			// Clear timer
525			clearTimeout(self.timers.inactive);
526			self.timers.inactive = setTimeout(function(){ self.hide(event); }, options.hide.inactive);
527		}
528
529		function repositionMethod(event) {
530			if(tooltip.is(':visible')) { self.reposition(event); }
531		}
532
533		// On mouseenter/mouseleave...
534		tooltip.bind('mouseenter'+namespace+' mouseleave'+namespace, function(event) {
535			var state = event.type === 'mouseenter';
536
537			// Focus the tooltip on mouseenter (z-index stacking)
538			if(state) { self.focus(event); }
539
540			// Add hover class
541			tooltip.toggleClass(hoverClass, state);
542		});
543
544		// Enable hide.fixed
545		if(options.hide.fixed) {
546			// Add tooltip as a hide target
547			targets.hide = targets.hide.add(tooltip);
548
549			// Clear hide timer on tooltip hover to prevent it from closing
550			tooltip.bind('mouseover'+namespace, function() {
551				if(!tooltip.hasClass(disabled)) { clearTimeout(self.timers.hide); }
552			});
553		}
554
555		// If using mouseout/mouseleave as a hide event...
556		if(/mouse(out|leave)/i.test(options.hide.event)) {
557			// Hide tooltips when leaving current window/frame (but not select/option elements)
558			if(options.hide.leave === 'window') {
559				targets.window.bind('mouseout' + namespace, function(event) {
560					if(/select|option/.test(event.target) && !event.relatedTarget) { self.hide(event); }
561				});
562			}
563		}
564
565		/*
566		 * Make sure hoverIntent functions properly by using mouseleave to clear show timer if
567		 * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
568		 */
569		else if(/mouse(over|enter)/i.test(options.show.event)) {
570			targets.hide.bind('mouseleave'+namespace, function(event) {
571				clearTimeout(self.timers.show);
572			});
573		}
574
575		// Hide tooltip on document mousedown if unfocus events are enabled
576		if(('' + options.hide.event).indexOf('unfocus') > -1) {
577			targets.document.bind('mousedown'+namespace, function(event) {
578				var $target = $(event.target),
579					enabled = !tooltip.hasClass(disabled) && tooltip.is(':visible');
580
581				if($target[0] !== tooltip[0] && $target.parents(selector).length === 0 && $target.add(target).length > 1) {
582					self.hide(event);
583				}
584			});
585		}
586
587		// Check if the tooltip hides when inactive
588		if('number' === typeof options.hide.inactive) {
589			// Bind inactive method to target as a custom event
590			targets.show.bind('qtip-'+id+'-inactive', inactiveMethod);
591			
592			// Define events which reset the 'inactive' event handler
593			$.each(QTIP.inactiveEvents, function(index, type){
594				targets.hide.add(elements.tooltip).bind(type+namespace+'-inactive', inactiveMethod);
595			});
596		}
597
598		// Apply hide events
599		$.each(events.hide, function(index, type) {
600			var showIndex = $.inArray(type, events.show),
601					targetHide = $(targets.hide);
602
603			// Both events and targets are identical, apply events using a toggle
604			if((showIndex > -1 && targetHide.add(targets.show).length === targetHide.length) || type === 'unfocus')
605			{
606				targets.show.bind(type+namespace, function(event) {
607					if(tooltip.is(':visible')) { hideMethod(event); }
608					else { showMethod(event); }
609				});
610
611				// Don't bind the event again
612				delete events.show[ showIndex ];
613			}
614
615			// Events are not identical, bind normally
616			else { targets.hide.bind(type+namespace, hideMethod); }
617		});
618
619		// Apply show events
620		$.each(events.show, function(index, type) {
621			targets.show.bind(type+namespace, showMethod);
622		});
623
624		// Check if the tooltip hides when mouse is moved a certain distance
625		if('number' === typeof options.hide.distance) {
626			// Bind mousemove to target to detect distance difference
627			targets.show.add(tooltip).bind('mousemove'+namespace, function(event) {
628				var origin = cache.origin || {},
629					limit = options.hide.distance,
630					abs = Math.abs;
631
632				// Check if the movement has gone beyond the limit, and hide it if so
633				if(abs(event.pageX - origin.pageX) >= limit || abs(event.pageY - origin.pageY) >= limit) {
634					self.hide(event);
635				}
636			});
637		}
638
639		// Mouse positioning events
640		if(posOptions.target === 'mouse') {
641			// Cache mousemove coords on show targets
642			targets.show.bind('mousemove'+namespace, function(event) {
643				MOUSE = { pageX: event.pageX, pageY: event.pageY, type: 'mousemove' };
644			});
645
646			// If mouse adjustment is on...
647			if(posOptions.adjust.mouse) {
648				// Apply a mouseleave event so we don't get problems with overlapping
649				if(options.hide.event) {
650					tooltip.bind('mouseleave'+namespace, function(event) {
651						if((event.relatedTarget || event.target) !== targets.show[0]) { self.hide(event); }
652					});
653				}
654
655				// Update tooltip position on mousemove
656				targets.document.bind('mousemove'+namespace, function(event) {
657					// Update the tooltip position only if the tooltip is visible and adjustment is enabled
658					if(!tooltip.hasClass(disabled) && tooltip.is(':visible')) {
659						self.reposition(event || MOUSE);
660					}
661				});
662			}
663		}
664
665		// Adjust positions of the tooltip on window resize if enabled
666		if(posOptions.adjust.resize || targets.viewport.length) {
667			($.event.special.resize ? targets.viewport : targets.window).bind('resize'+namespace, repositionMethod);
668		}
669
670		// Adjust tooltip position on scroll if screen adjustment is enabled
671		if(targets.viewport.length || (IE6 && tooltip.css('position') === 'fixed')) {
672			targets.viewport.bind('scroll'+namespace, repositionMethod);
673		}
674	}
675
676	function unassignEvents()
677	{
678		var targets = [
679				options.show.target[0],
680				options.hide.target[0],
681				self.rendered && elements.tooltip[0],
682				options.position.container[0],
683				options.position.viewport[0],
684				window,
685				document
686			];
687
688		// Check if tooltip is rendered
689		if(self.rendered) {
690			$([]).pushStack( $.grep(targets, function(i){ return typeof i === 'object'; }) ).unbind(namespace);
691		}
692
693		// Tooltip isn't yet rendered, remove render event
694		else { options.show.target.unbind(namespace+'-create'); }
695	}
696
697	// Setup builtin .set() option checks
698	self.checks.builtin = {
699		// Core checks
700		'^id$': function(obj, o, v) {
701			var id = v === TRUE ? QTIP.nextid : v,
702				tooltipID = uitooltip + '-' + id;
703
704			if(id !== FALSE && id.length > 0 && !$('#'+tooltipID).length) {
705				tooltip[0].id = tooltipID;
706				elements.content[0].id = tooltipID + '-content';
707				elements.title[0].id = tooltipID + '-title';
708			}
709		},
710
711		// Content checks
712		'^content.text$': function(obj, o, v){ updateContent(v); },
713		'^content.title.text$': function(obj, o, v) {
714			// Remove title if content is null
715			if(!v) { return removeTitle(); }
716
717			// If title isn't already created, create it now and update
718			if(!elements.title && v) { createTitle(); }
719			updateTitle(v);
720		},
721		'^content.title.button$': function(obj, o, v){ updateButton(v); },
722
723		// Position checks
724		'^position.(my|at)$': function(obj, o, v){
725			// Parse new corner value into Corner objecct
726			if('string' === typeof v) {
727				obj[o] = new PLUGINS.Corner(v);
728			}
729		},
730		'^position.container$': function(obj, o, v){
731			if(self.rendered) { tooltip.appendTo(v); }
732		},
733
734		// Show checks
735		'^show.ready$': function() {
736			if(!self.rendered) { self.render(1); }
737			else { self.toggle(TRUE); }
738		},
739
740		// Style checks
741		'^style.classes$': function(obj, o, v) { 
742			tooltip.attr('class', uitooltip + ' qtip ui-helper-reset ' + v);
743		},
744		'^style.widget|content.title': setWidget,
745
746		// Events check
747		'^events.(render|show|move|hide|focus|blur)$': function(obj, o, v) {
748			tooltip[($.isFunction(v) ? '' : 'un') + 'bind']('tooltip'+o, v);
749		},
750
751		// Properties which require event reassignment
752		'^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)': function() {
753			var posOptions = options.position;
754
755			// Set tracking flag
756			tooltip.attr('tracking', posOptions.target === 'mouse' && posOptions.adjust.mouse);
757
758			// Reassign events
759			unassignEvents(); assignEvents();
760		}
761	};
762
763	/*
764	* Public API methods
765	*/
766	$.extend(self, {
767		render: function(show)
768		{
769			if(self.rendered) { return self; } // If tooltip has already been rendered, exit
770
771			var title = options.content.title.text,
772				posOptions = options.position,
773				callback = $.Event('tooltiprender');
774
775			// Add ARIA attributes to target
776			$.attr(target[0], 'aria-describedby', tooltipID);
777
778			// Create tooltip element
779			tooltip = elements.tooltip = $('<div/>', {
780					'id': tooltipID,
781					'class': uitooltip + ' qtip ui-helper-reset ' + defaultClass + ' ' + options.style.classes,
782					'width': options.style.width || '',
783					'height': options.style.height || '',
784					'tracking': posOptions.target === 'mouse' && posOptions.adjust.mouse,
785
786					/* ARIA specific attributes */
787					'role': 'alert',
788					'aria-live': 'polite',
789					'aria-atomic': FALSE,
790					'aria-describedby': tooltipID + '-content',
791					'aria-hidden': TRUE
792				})
793				.toggleClass(disabled, cache.disabled)
794				.data('qtip', self)
795				.appendTo(options.position.container)
796				.append(
797					// Create content element
798					elements.content = $('<div />', {
799						'class': uitooltip + '-content',
800						'id': tooltipID + '-content',
801						'aria-atomic': TRUE
802					})
803				);
804
805			// Set rendered flag and prevent redundant redraw/reposition calls for now
806			self.rendered = -1;
807			isDrawing = 1; isPositioning = 1;
808
809			// Update title
810			if(title) { 
811				createTitle();
812				updateTitle(title, FALSE);
813			}
814
815			// Set proper rendered flag and update content
816			updateContent(options.content.text, FALSE);
817			self.rendered = TRUE;
818
819			// Setup widget classes
820			setWidget();
821
822			// Assign passed event callbacks (before plugins!)
823			$.each(options.events, function(name, callback) {
824				if($.isFunction(callback)) {
825					tooltip.bind(name === 'toggle' ? 'tooltipshow tooltiphide' : 'tooltip'+name, callback);
826				}
827			});
828
829			// Initialize 'render' plugins
830			$.each(PLUGINS, function() {
831				if(this.initialize === 'render') { this(self); }
832			});
833
834			// Assign events
835			assignEvents();
836
837			/* Queue this part of the render process in our fx queue so we can
838			 * load images before the tooltip renders fully.
839			 *
840			 * See: updateContent method
841			*/
842			tooltip.queue('fx', function(next) {
843				// Trigger tooltiprender event and pass original triggering event as original
844				callback.originalEvent = cache.event;
845				tooltip.trigger(callback, [self]);
846
847				// Reset flags
848				isDrawing = 0; isPositioning = 0;
849
850				// Redraw the tooltip manually now we're fully rendered
851				self.redraw();
852
853				// Show tooltip if needed
854				if(options.show.ready || show) {
855					self.toggle(TRUE, cache.event);
856				}
857
858				next(); // Move on to next method in queue
859			});
860
861			return self;
862		},
863
864		get: function(notation)
865		{
866			var result, o;
867
868			switch(notation.toLowerCase())
869			{
870				case 'dimensions':
871					result = {
872						height: tooltip.outerHeight(), width: tooltip.outerWidth()
873					};
874				break;
875
876				case 'offset':
877					result = PLUGINS.offset(tooltip, options.position.container);
878				break;
879
880				default:
881					o = convertNotation(notation.toLowerCase());
882					result = o[0][ o[1] ];
883					result = result.precedance ? result.string() : result;
884				break;
885			}
886
887			return result;
888		},
889
890		set: function(option, value)
891		{
892			var rmove = /^position\.(my|at|adjust|target|container)|style|content|show\.ready/i,
893				rdraw = /^content\.(title|attr)|style/i,
894				reposition = FALSE,
895				redraw = FALSE,
896				checks = self.checks,
897				name;
898
899			function callback(notation, args) {
900				var category, rule, match;
901
902				for(category in checks) {
903					for(rule in checks[category]) {
904						if(match = (new RegExp(rule, 'i')).exec(notation)) {
905							args.push(match);
906							checks[category][rule].apply(self, args);
907						}
908					}
909				}
910			}
911
912			// Convert singular option/value pair into object form
913			if('string' === typeof option) {
914				name = option; option = {}; option[name] = value;
915			}
916			else { option = $.extend(TRUE, {}, option); }
917
918			// Set all of the defined options to their new values
919			$.each(option, function(notation, value) {
920				var obj = convertNotation( notation.toLowerCase() ), previous;
921
922				// Set new obj value
923				previous = obj[0][ obj[1] ];
924				obj[0][ obj[1] ] = 'object' === typeof value && value.nodeType ? $(value) : value;
925
926				// Set the new params for the callback
927				option[notation] = [obj[0], obj[1], value, previous];
928
929				// Also check if we need to reposition / redraw
930				reposition = rmove.test(notation) || reposition;
931				redraw = rdraw.test(notation) || redraw;
932			});
933
934			// Re-sanitize options
935			sanitizeOptions(options);
936
937			/*
938			 * Execute any valid callbacks for the set options
939			 * Also set isPositioning/isDrawing so we don't get loads of redundant repositioning
940			 * and redraw calls.
941			 */
942			isPositioning = isDrawing = 1; $.each(option, callback); isPositioning = isDrawing = 0;
943
944			// Update position / redraw if needed
945			if(tooltip.is(':visible') && self.rendered) {
946				if(reposition) {
947					self.reposition( options.position.target === 'mouse' ? NULL : cac
947he.event );
948				}
949				if(redraw) { self.redraw(); }
950			}
951
952			return self;
953		},
954
955		toggle: function(state, event)
956		{
957			// Make sure tooltip is rendered
958			if(!self.rendered) {
959				if(state) { self.render(1); } // Render the tooltip if showing and it isn't already
960				else { return self; }
961			}
962
963			var type = state ? 'show' : 'hide',
964				opts = options[type],
965				visible = tooltip.is(':visible'),
966				sameTarget = !event || options[type].target.length < 2 || cache.target[0] === event.target,
967				posOptions = options.position,
968				contentOptions = options.content,
969				delay,
970				callback;
971
972			// Detect state if valid one isn't provided
973			if((typeof state).search('boolean|number')) { state = !visible; }
974
975			// Return if element is already in correct state
976			if(!tooltip.is(':animated') && visible === state && sameTarget) { return self; }
977
978			// Try to prevent flickering when tooltip overlaps show element
979			if(event) {
980				if((/over|enter/).test(event.type) && (/out|leave/).test(cache.event.type) &&
981					event.target === options.show.target[0] && tooltip.has(event.relatedTarget).length) {
982					return self;
983				}
984
985				// Cache event
986				cache.event = $.extend({}, event);
987			}
988
989			// Call API methods
990			callback = $.Event('tooltip'+type);
991			callback.originalEvent = event ? cache.event : NULL;
992			tooltip.trigger(callback, [self, 90]);
993			if(callback.isDefaultPrevented()){ return self; }
994
995			// Set ARIA hidden status attribute
996			$.attr(tooltip[0], 'aria-hidden', !!!state);
997
998			// Execute state specific properties
999			if(state) {
1000				// Store show origin coordinates
1001				cache.origin = $.extend({}, MOUSE);
1002
1003				// Focus the tooltip
1004				self.focus(event);
1005
1006				// Update tooltip content & title if it's a dynamic function
1007				if($.isFunction(contentOptions.text)) { updateContent(contentOptions.text, FALSE); }
1008				if($.isFunction(contentOptions.title.text)) { updateTitle(contentOptions.title.text, FALSE); }
1009
1010				// Cache mousemove events for positioning purposes (if not already tracking)
1011				if(!trackingBound && posOptions.target === 'mouse' && posOptions.adjust.mouse) {
1012					$(document).bind('mousemove.qtip', function(event) {
1013						MOUSE = { pageX: event.pageX, pageY: event.pageY, type: 'mousemove' };
1014					});
1015					trackingBound = TRUE;
1016				}
1017
1018				// Update the tooltip position
1019				self.reposition(event);
1020
1021				// Hide other tooltips if tooltip is solo, using it as the context
1022				if(opts.solo) { $(selector, opts.solo).not(tooltip).qtip('hide', callback); }
1023			}
1024			else {
1025				// Clear show timer if we're hiding 
1026				clearTimeout(self.timers.show);
1027
1028				// Remove cached origin on hide
1029				delete cache.origin;
1030
1031				// Remove mouse tracking event if not needed (all tracking qTips are hidden)
1032				if(trackingBound && !$(selector+'[tracking="true"]:visible', opts.solo).not(tooltip).length) {
1033					$(document).unbind('mousemove.qtip');
1034					trackingBound = FALSE;
1035				}
1036				
1037				// Blur the tooltip
1038				self.blur(event);
1039			}
1040
1041			// Define post-animation, state specific properties
1042			function after() {
1043				if(state) {
1044					// Prevent antialias from disappearing in IE by removing filter
1045					if($.browser.msie) { tooltip[0].style.removeAttribute('filter'); }
1046
1047					// Remove overflow setting to prevent tip bugs
1048					tooltip.css('overflow', '');
1049
1050					// Autofocus elements if enabled
1051					if('string' === typeof opts.autofocus) {
1052						$(opts.autofocus, tooltip).focus();
1053					}
1054
1055					// Call API method
1056					callback = $.Event('tooltipvisible');
1057					callback.originalEvent = event ? cache.event : NULL;
1058					tooltip.trigger(callback, [self]);
1059				}
1060				else {
1061					// Reset CSS states
1062					tooltip.css({
1063						display: '',
1064						visibility: '',
1065						opacity: '',
1066						left: '',
1067						top: ''
1068					});
1069				}
1070			}
1071
1072			// Clear animation queue if same target
1073			if(sameTarget) { tooltip.stop(0, 1); }
1074
1075			// If no effect type is supplied, use a simple toggle
1076			if(opts.effect === FALSE) {
1077				tooltip[ type ]();
1078				after.call(tooltip);
1079			}
1080
1081			// Use custom function if provided
1082			else if($.isFunction(opts.effect)) {
1083				opts.effect.call(tooltip, self);
1084				tooltip.queue('fx', function(n){ after(); n(); });
1085			}
1086
1087			// Use basic fade function by default
1088			else { tooltip.fadeTo(90, state ? 1 : 0, after); }
1089
1090			// If inactive hide method is set, active it
1091			if(state) { opts.target.trigger('qtip-'+id+'-inactive'); }
1092
1093			return self;
1094		},
1095
1096		show: function(event){ return self.toggle(TRUE, event); },
1097
1098		hide: function(event){ return self.toggle(FALSE, event); },
1099
1100		focus: function(event)
1101		{
1102			if(!self.rendered) { return self; }
1103
1104			var qtips = $(selector),
1105				curIndex = parseInt(tooltip[0].style.zIndex, 10),
1106				newIndex = QTIP.zindex + qtips.length,
1107				cachedEvent = $.extend({}, event),
1108				focusedElem, callback;
1109
1110			// Only update the z-index if it has changed and tooltip is not already focused
1111			if(!tooltip.hasClass(focusClass))
1112			{
1113				// Call API method
1114				callback = $.Event('tooltipfocus');
1115				callback.originalEvent = cachedEvent;
1116				tooltip.trigger(callback, [self, newIndex]);
1117
1118				// If default action wasn't prevented...
1119				if(!callback.isDefaultPrevented()) {
1120					// Only update z-index's if they've changed
1121					if(curIndex !== newIndex) {
1122						// Reduce our z-index's and keep them properly ordered
1123						qtips.each(function() {
1124							if(this.style.zIndex > curIndex) {
1125								this.style.zIndex = this.style.zIndex - 1;
1126							}
1127						});
1128						
1129						// Fire blur event for focused tooltip
1130						qtips.filter('.' + focusClass).qtip('blur', cachedEvent);
1131					}
1132
1133					// Set the new z-index
1134					tooltip.addClass(focusClass)[0].style.zIndex = newIndex;
1135				}
1136			}
1137
1138			return self;
1139		},
1140
1141		blur: function(event) {
1142			var cachedEvent = $.extend({}, event),
1143				callback;
1144
1145			// Set focused status to FALSE
1146			tooltip.removeClass(focusClass);
1147
1148			// Trigger blur event
1149			callback = $.Event('tooltipblur');
1150			callback.originalEvent = cachedEvent;
1151			tooltip.trigger(callback, [self]);
1152
1153			return self;
1154		},
1155
1156		reposition: function(event, effect)
1157		{
1158			if(!self.rendered || isPositioning) { return self; }
1159
1160			// Set positioning flag
1161			isPositioning = 1;
1162	
1163			var target = options.position.target,
1164				posOptions = options.position,
1165				my = posOptions.my, 
1166				at = posOptions.at,
1167				adjust = posOptions.adjust,
1168				method = adjust.method.split(' '),
1169				elemWidth = tooltip.outerWidth(),
1170				elemHeight = tooltip.outerHeight(),
1171				targetWidth = 0,
1172				targetHeight = 0,
1173				callback = $.Event('tooltipmove'),
1174				fixed = tooltip.css('position') === 'fixed',
1175				viewport = posOptions.viewport,
1176				position = { left: 0, top: 0 },
1177				tip = self.plugins.tip,
1178				readjust = {
1179					// Repositioning method and axis detection
1180					horizontal: method[0],
1181					vertical: method[1] || method[0],
1182
1183					// Reposition methods
1184					left: function(posLeft) {
1185						var isShift = readjust.horizontal === 'shift',
1186							viewportScroll = viewport.offset.left + viewport.scrollLeft,
1187							myWidth = my.x === 'left' ? elemWidth : my.x === 'right' ? -elemWidth : -elemWidth / 2,
1188							atWidth = at.x === 'left' ? targetWidth : at.x === 'right' ? -targetWidth : -targetWidth / 2,
1189							tipWidth = tip && tip.size ? tip.size.width || 0 : 0,
1190							tipAdjust = tip && tip.corner && tip.corner.precedance === 'x' && !isShift ? tipWidth : 0,
1191							overflowLeft = viewportScroll - posLeft + tipAdjust,
1192							overflowRight = posLeft + elemWidth - viewport.width - viewportScroll + tipAdjust,
1193							offset = myWidth - (my.precedance === 'x' || my.x === my.y ? atWidth : 0),
1194							isCenter = my.x === 'center';
1195
1196						// Optional 'shift' style repositioning
1197						if(isShift) {
1198							tipAdjust = tip && tip.corner && tip.corner.precedance === 'y' ? tipWidth : 0;
1199							offset = (my.x === 'left' ? 1 : -1) * myWidth - tipAdjust;
1200
1201							// Adjust position but keep it within viewport dimensions
1202							position.left += overflowLeft > 0 ? overflowLeft : overflowRight > 0 ? -overflowRight : 0;
1203							position.left = Math.max(
1204								viewport.offset.left + (tipAdjust && tip.corner.x === 'center' ? tip.offset : 0),
1205								posLeft - offset,
1206								Math.min(
1207									Math.max(viewport.offset.left + viewport.width, posLeft + offset),
1208									position.left
1209								)
1210							);
1211						}
1212
1213						// Default 'flip' repositioning
1214						else {
1215							if(overflowLeft > 0 && (my.x !== 'left' || overflowRight > 0)) {
1216								position.left -= offset + (isCenter ? 0 : 2 * adjust.x);
1217							}
1218							else if(overflowRight > 0 && (my.x !== 'right' || overflowLeft > 0)  ) {
1219								position.left -= isCenter ? -offset : offset + (2 * adjust.x);
1220							}
1221							if(position.left !== posLeft && isCenter) { position.left -= adjust.x; }
1222							
1223							// Make sure we haven't made things worse with the adjustment and return the adjusted difference
1224							if(position.left < viewportScroll && -position.left > overflowRight) { position.left = posLeft; }
1225						}
1226
1227						return position.left - posLeft;
1228					},
1229					top: function(posTop) {
1230						var isShift = readjust.vertical === 'shift',
1231							viewportScroll = viewport.offset.top + viewport.scrollTop,
1232							myHeight = my.y === 'top' ? elemHeight : my.y === 'bottom' ? -elemHeight : -elemHeight / 2,
1233							atHeight = at.y === 'top' ? targetHeight : at.y === 'bottom' ? -targetHeight : -targetHeight / 2,
1234							tipHeight = tip && tip.size ? tip.size.height || 0 : 0,
1235							tipAdjust = tip && tip.corner && tip.corner.precedance === 'y' && !isShift ? tipHeight : 0,
1236							overflowTop = viewportScroll - posTop + tipAdjust,
1237							overflowBottom = posTop + elemHeight - viewport.height - viewportScroll + tipAdjust,
1238							offset = myHeight - (my.precedance === 'y' || my.x === my.y ? atHeight : 0),
1239							isCenter = my.y === 'center';
1240							
1241						// Optional 'shift' style repositioning
1242						if(isShift) {
1243							tipAdjust = tip && tip.corner && tip.corner.precedance === 'x' ? tipHeight : 0;
1244							offset = (my.y === 'top' ? 1 : -1) * myHeight - tipAdjust;
1245
1246							// Adjust position but keep it within viewport dimensions
1247							position.top += overflowTop > 0 ? overflowTop : overflowBottom > 0 ? -overflowBottom : 0;
1248							position.top = Math.max(
1249								viewport.offset.top + (tipAdjust && tip.corner.x === 'center' ? tip.offset : 0),
1250								posTop - offset,
1251								Math.min(
1252									Math.max(viewport.offset.top + viewport.height, posTop + offset),
1253									position.top
1254								)
1255							);
1256						}
1257
1258						// Default 'flip' repositioning
1259						else {
1260							if(overflowTop > 0 && (my.y !== 'top' || overflowBottom > 0)) {
1261								position.top -= offset + (isCenter ? 0 : 2 * adjust.y);
1262							}
1263							else if(overflowBottom > 0 && (my.y !== 'bottom' || overflowTop > 0)  ) {
1264								position.top -= isCenter ? -offset : offset + (2 * adjust.y);
1265							}
1266							if(position.top !== posTop && isCenter) { position.top -= adjust.y; }
1267
1268							// Make sure we haven't made things worse with the adjustment and return the adjusted difference
1269							if(position.top < 0 && -position.top > overflowBottom) { position.top = posTop; }
1270						}
1271
1272						return position.top - posTop;
1273					}
1274				};
1275
1276			// Check if absolute position was passed
1277			if($.isArray(target) && target.length === 2) {
1278				// Force left top and set position
1279				at = { x: 'left', y: 'top' };
1280				position = { left: target[0], top: target[1] };
1281			}
1282
1283			// Check if mouse was the target
1284			else if(target === 'mouse' && ((event && event.pageX) || cache.event.pageX)) {
1285				// Force left top to allow flipping
1286				at = { x: 'left', y: 'top' };
1287
1288				// Use cached event if one isn't available for positioning
1289				event = (event && (event.type === 'resize' || event.type === 'scroll') ? cache.event :
1290					event && event.pageX && event.type === 'mousemove' ? event :
1291					MOUSE && MOUSE.pageX && (adjust.mouse || !event || !event.pageX) ? { pageX: MOUSE.pageX, pageY: MOUSE.pageY } :
1292					!adjust.mouse && cache.origin && cache.origin.pageX ? cache.origin :
1293					event) || event || cache.event || MOUSE || {};
1294
1295				// Use event coordinates for position
1296				position = { top: event.pageY, left: event.pageX };
1297			}
1298
1299			// Target wasn't mouse or absolute...
1300			else {
1301				// Check if event targetting is being used
1302				if(target === 'event') {
1303					if(event && event.target && event.type !== 'scroll' && event.type !== 'resize') {
1304						target = cache.target = $(event.target);
1305					}
1306					else {
1307						target = cache.target;
1308					}
1309				}
1310				else { cache.target = $(target); }
1311
1312				// Parse the target into a jQuery object and make sure there's an element present
1313				target = $(target).eq(0);
1314				if(target.length === 0) { return self; }
1315
1316				// Check if window or document is the target
1317				else if(target[0] === document || target[0] === window) {
1318					targetWidth = PLUGINS.iOS ? window.innerWidth : target.width();
1319					targetHeight = PLUGINS.iOS ? window.innerHeight : target.height();
1320
1321					if(target[0] === window) {
1322						position = {
1323							top: !fixed || PLUGINS.iOS ? (viewport || target).scrollTop() : 0,
1324							left: !fixed || PLUGINS.iOS ? (viewport || target).scrollLeft() : 0
1325						};
1326					}
1327				}
1328
1329				// Use Imagemap/SVG plugins if needed
1330				else if(target.is('area') && PLUGINS.imagemap) {
1331					position = PLUGINS.imagemap(target, at);
1332				}
1333				else if(target[0].namespaceURI === 'http://www.w3.org/2000/svg' && PLUGINS.svg) {
1334					position = PLUGINS.svg(target, at);
1335				}
1336
1337				else {
1338					targetWidth = target.outerWidth();
1339					targetHeight = target.outerHeight();
1340
1341					position = PLUGINS.offset(target, posOptions.container, fixed);
1342				}
1343
1344				// Parse returned plugin values into proper variables
1345				if(position.offset) {
1346					targetWidth = position.width;
1347					targetHeight = position.height;
1348					position = position.offset;
1349				}
1350
1351				// Adjust position relative to target
1352				position.left += at.x === 'right' ? targetWidth : at.x === 'center' ? targetWidth / 2 : 0;
1353				position.top += at.y === 'bottom' ? targetHeight : at.y === 'center' ? targetHeight / 2 : 0;
1354			}
1355
1356			// Adjust position relative to tooltip
1357			position.left += adjust.x + (my.x === 'right' ? -elemWidth : my.x === 'center' ? -elemWidth / 2 : 0);
1358			position.top += adjust.y + (my.y === 'bottom' ? -elemHeight : my.y === 'center' ? -elemHeight / 2 : 0);
1359
1360			// Calculate collision offset values if viewport positioning is enabled
1361			if(viewport.jquery && target[0] !== window && target[0] !== docBody &&
1362				readjust.vertical+readjust.horizontal !== 'nonenone')
1363			{
1364				// Cache our viewport details
1365				viewport = {
1366					elem: viewport,
1367					height: viewport[ (viewport[0] === window ? 'h' : 'outerH') + 'eight' ](),
1368					width: viewport[ (viewport[0] === window ? 'w' : 'outerW') + 'idth' ](),
1369					scrollLeft: fixed ? 0 : viewport.scrollLeft(),
1370					scrollTop: fixed ? 0 : viewport.scrollTop(),
1371					offset: viewport.offset() || { left: 0, top: 0 }
1372				};
1373
1374				// Adjust position based onviewport and adjustment options
1375				position.adjusted = {
1376					left: readjust.horizontal !== 'none' ? readjust.left(position.left) : 0,
1377					top: readjust.vertical !== 'none' ? readjust.top(position.top) : 0
1378				};
1379			}
1380
1381			//Viewport adjustment is disabled, set values to zero
1382			else { position.adjusted = { left: 0, top: 0 }; }
1383
1384			// Set tooltip position class
1385			tooltip.attr('class', function(i, val) {
1386				return $.attr(this, 'class').replace(/ui-tooltip-pos-\w+/i, '');
1387			})
1388			.addClass(uitooltip + '-pos-' + my.abbreviation());
1389
1390			// Call API method
1391			callback.originalEvent = $.extend({}, event);
1392			tooltip.trigger(callback, [self, position, viewport.elem || viewport]);
1393			if(callback.isDefaultPrevented()){ return self; }
1394			delete position.adjusted;
1395
1396			// If effect is disabled, target it mouse, no animation is defined or positioning gives NaN out, set CSS directly
1397			if(effect === FALSE || isNaN(position.left) || isNaN(position.top) || target === 'mouse' || !$.isFunction(posOptions.effect)) {
1398				tooltip.css(position);
1399			}
1400			
1401			// Use custom function if provided
1402			else if($.isFunction(posOptions.effect)) {
1403				posOptions.effect.call(tooltip, self, $.extend({}, position));
1404				tooltip.queue(function(next) {
1405					// Reset attributes to avoid cross-browser rendering bugs
1406					$(this).css({ opacity: '', height: '' });
1407					if($.browser.msie) { this.style.removeAttribute('filter'); }
1408
1409					next();
1410				});
1411			}
1412
1413			// Set positioning flag
1414			isPositioning = 0;
1415
1416			return self;
1417		},
1418
1419		// Max/min width simulator function for all browsers.. yeaaah!
1420		redraw: function()
1421		{
1422			if(self.rendered < 1 || isDrawing) { return self; }
1423
1424			var container = options.position.container,
1425				perc, width, max, min;
1426
1427			// Set drawing flag
1428			isDrawing = 1;
1429
1430			// If tooltip has a set height, just set it... like a boss!
1431			if(options.style.height) { tooltip.css('height', options.style.height); }
1432
1433			// If tooltip has a set width, just set it... like a boss!
1434			if(options.style.width) { tooltip.css('width', options.style.width); }
1435
1436			// Otherwise simualte max/min width...
1437			else {
1438				// Reset width and add fluid class
1439				tooltip.css('width', '').addClass(fluidClass);
1440
1441				// Grab our tooltip width (add 1 so we don't get wrapping problems.. huzzah!)
1442				width = tooltip.width() + 1;
1443
1444				// Grab our max/min properties
1445				max = tooltip.css('max-width') || '';
1446				min = tooltip.css('min-width') || '';
1447
1448				// Parse into proper pixel values
1449				perc = (max + min).indexOf('%') > -1 ? container.width() / 100 : 0;
1450				max = ((max.indexOf('%') > -1 ? perc : 1) * parseInt(max, 10)) || width;
1451				min = ((min.indexOf('%') > -1 ? perc : 1) * parseInt(min, 10)) || 0;
1452
1453				// Determine new dimension size based on max/min/current values
1454				width = max + min ? Math.min(Math.max(width, min), max) : width;
1455
1456				// Set the newly calculated width and remvoe fluid class
1457				tooltip.css('width', Math.round(width)).removeClass(fluidClass);
1458			}
1459
1460			// Set drawing flag
1461			isDrawing = 0;
1462
1463			return self;
1464		},
1465
1466		disable: function(state)
1467		{
1468			if('boolean' !== typeof state) {
1469				state = !(tooltip.hasClass(disabled) || cache.disabled);
1470			}
1471			 
1472			if(self.rendered) {
1473				tooltip.toggleClass(disabled, state);
1474				$.attr(tooltip[0], 'aria-disabled', state);
1475			}
1476			else {
1477				cache.disabled = !!state;
1478			}
1479
1480			return self;
1481		},
1482		
1483		enable: function() { return self.disable(FALSE); },
1484
1485		destroy: function()
1486		{
1487			var t = target[0],
1488				title = $.attr(t, oldtitle);
1489
1490			// Destroy tooltip and  any associated plugins if rendered
1491			if(self.rendered) {
1492				tooltip.remove();
1493				
1494				$.each(self.plugins, function() {
1495					if(this.destroy) { this.destroy(); }
1496				});
1497			}
1498
1499			// Clear timers and remove bound events
1500			clearTimeout(self.timers.show);
1501			clearTimeout(self.timers.hide);
1502			unassignEvents();
1503
1504			// Remove api object
1505			$.removeData(t, 'qtip');
1506
1507			// Reset old title attribute if removed 
1508			if(options.suppress && title) {
1509				$.attr(t, 'title', title);
1510				target.removeAttr(oldtitle);
1511			}
1512
1513			// Remove ARIA attributes and bound qtip events
1514			target.removeAttr('aria-describedby').unbind('.qtip');
1515
1516			// Remove ID from sued id object
1517			delete usedIDs[self.id];
1518
1519			return target;
1520		}
1521	});
1522}
1523
1524// Initialization method
1525function init(id, opts)
1526{
1527	var obj, posOptions, attr, config, title,
1528
1529	// Setup element references
1530	elem = $(this),
1531	docBody = $(document.body),
1532
1533	// Use document body instead of document element if needed
1534	newTarget = this === document ? docBody : elem,
1535
1536	// Grab metadata from element if plugin is present
1537	metadata = (elem.metadata) ? elem.metadata(opts.metadata) : NULL,
1538
1539	// If metadata type if HTML5, grab 'name' from the object instead, or use the regular data object otherwise
1540	metadata5 = opts.metadata.type === 'html5' && metadata ? metadata[opts.metadata.name] : NULL,
1541
1542	// Grab data from metadata.name (or data-qtipopts as fallback) using .data() method,
1543	html5 = elem.data(opts.metadata.name || 'qtipopts');
1544
1545	// If we don't get an object returned attempt to parse it manualyl without parseJSON
1546	try { html5 = typeof html5 === 'string' ? (new Function("return " + html5))() : html5; }
1547	catch(e) { log('Unable to parse HTML5 attribute data: ' + html5); }
1548
1549	// Merge in and sanitize metadata
1550	config = $.extend(TRUE, {}, QTIP.defaults, opts, 
1551		typeof html5 === 'object' ? sanitizeOptions(html5) : NULL,
1552		sanitizeOptions(metadata5 || metadata));
1553
1554	// Re-grab our positioning options now we've merged our metadata and set id to passed value
1555	posOptions = config.position;
1556	config.id = id;
1557	
1558	// Setup missing content if none is detected
1559	if('boolean' === typeof config.content.text) {
1560		attr = elem.attr(config.content.attr);
1561
1562		// Grab from supplied attribute if available
1563		if(config.content.attr !== FALSE && attr) { config.content.text = attr; }
1564
1565		// No valid content was found, abort render
1566		else {
1567			log('Unable to locate content for tooltip! Aborting render of tooltip on element: ', elem);
1568			return FALSE;
1569		}
1570	}
1571
1572	// Setup target options
1573	if(posOptions.container === FALSE) { posOptions.container = docBody; }
1574	if(posOptions.target === FALSE) { posOptions.target = newTarget; }
1575	if(config.show.target === FALSE) { config.show.target = newTarget; }
1576	if(config.show.solo === TRUE) { config.show.solo = docBody; }
1577	if(config.hide.target === FALSE) { config.hide.target = newTarget; }
1578	if(config.position.viewport === TRUE) { config.position.viewport = posOptions.container; }
1579
1580	// Convert position corner values into x and y strings
1581	posOptions.at = new PLUGINS.Corner(posOptions.at);
1582	posOptions.my = new PLUGINS.Corner(posOptions.my);
1583
1584	// Destroy previous tooltip if overwrite is enabled, or skip element if not
1585	if($.data(this, 'qtip')) {
1586		if(config.overwrite) {
1587			elem.qtip('destroy');
1588		}
1589		else if(config.overwrite === FALSE) {
1590			return FALSE;
1591		}
1592	}
1593
1594	// Remove title attribute and store it if present
1595	if(config.suppress && (title = $.attr(this, 'title'))) {
1596		$(this).removeAttr('title').attr(oldtitle, title);
1597	}
1598
1599	// Initialize the tooltip and add API reference
1600	obj = new QTip(elem, config, id, !!attr);
1601	$.data(this, 'qtip', obj);
1602
1603	// Catch remove events on target element to destroy redundant tooltip
1604	elem.bind('remove.qtip', function(){ obj.destroy(); });
1605
1606	return obj;
1607}
1608
1609// jQuery $.fn extension method
1610QTIP = $.fn.qtip = function(options, notation, newValue)
1611{
1612	var command = ('' + options).toLowerCase(), // Parse command
1613		returned = NULL,
1614		args = command === 'disable' ? [TRUE] : $.makeArray(arguments).slice(1),
1615		event = args[args.length - 1],
1616		opts = this[0] ? $.data(this[0], 'qtip') : NULL;
1617
1618	// Check for API request
1619	if((!arguments.length && opts) || command === 'api') {
1620		return opts;
1621	}
1622
1623	// Execute API command if present
1624	else if('string' === typeof options)
1625	{
1626		this.each(function()
1627		{
1628			var api = $.data(this, 'qtip');
1629			if(!api) { return TRUE; }
1630
1631			// Cache the event if possible
1632			if(event && event.timeStamp) { api.cache.event = event; }
1633
1634			// Check for specific API commands
1635			if((command === 'option' || command === 'options') && notation) {
1636				if($.isPlainObject(notation) || newValue !== undefined) {
1637					api.set(notation, newValue);
1638				}
1639				else {
1640					returned = api.get(notation);
1641					return FALSE;
1642				}
1643			}
1644
1645			// Execute API command
1646			else if(api[command]) {
1647				api[command].apply(api[command], args);
1648			}
1649		});
1650
1651		return returned !== NULL ? returned : this;
1652	}
1653
1654	// No API commands. validate provided options and setup qTips
1655	else if('object' === typeof options || !arguments.length)
1656	{
1657		opts = sanitizeOptions($.extend(TRUE, {}, options));
1658
1659		// Bind the qTips
1660		return QTIP.bind.call(this, opts, event);
1661	}
1662};
1663
1664// $.fn.qtip Bind method
1665QTIP.bind = function(opts, event)
1666{
1667	return this.each(function(i) {
1668		var options, targets, events, namespace, api, id;
1669
1670		// Find next available ID, or use custom ID if provided
1671		id = $.isArray(opts.id) ? opts.id[i] : opts.id;
1672		id = !id || id === FALSE || id.length < 1 || usedIDs[id] ? QTIP.nextid++ : (usedIDs[id] = id);
1673
1674		// Setup events namespace
1675		namespace = '.qtip-'+id+'-create';
1676
1677		// Initialize the qTip and re-grab newly sanitized options
1678		api = init.call(this, id, opts);
1679		if(api === FALSE) { return TRUE; }
1680		options = api.options;
1681
1682		// Initialize plugins
1683		$.each(PLUGINS, function() {
1684			if(this.initialize === 'initialize') { this(api); }
1685		});
1686
1687		// Determine hide and show targets
1688		targets = { show: options.show.target, hide: options.hide.target };
1689		events = {
1690			show: $.trim('' + options.show.event).replace(/ /g, namespace+' ') + namespace,
1691			hide: $.trim('' + options.hide.event).replace(/ /g, namespace+' ') + namespace
1692		};
1693
1694		/*
1695		 * Make sure hoverIntent functions properly by using mouseleave as a hide event if
1696		 * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
1697		 */
1698		if(/mouse(over|enter)/i.test(events.show) && !/mouse(out|leave)/i.test(events.hide)) {
1699			events.hide += ' mouseleave' + namespace;
1700		}
1701
1702		/*
1703		 * Also make sure initial mouse targetting works correctly by caching mousemove coords
1704		 * on show targets before the tooltip has rendered.
1705		 */
1706		targets.show.bind('mousemove'+namespace, function(event) {
1707			MOUSE = { pageX: event.pageX, pageY: event.pageY, type: 'mousemove' };
1708		});
1709
1710		// Define hoverIntent function
1711		function hoverIntent(event) {
1712			function render() {
1713				// Cache mouse coords,render and render the tooltip
1714				api.render(typeof event === 'object' || options.show.ready);
1715
1716				// Unbind show and hide events
1717				targets.show.add(targets.hide).unbind(namespace);
1718			}
1719
1720			// Only continue if tooltip isn't disabled
1721			if(api.cache.disabled) { return FALSE; }
1722
1723			// Cache the event data
1724			api.cache.event = $.extend({}, event);
1725			api.cache.target = event ? $(event.target) : [undefined];
1726
1727			// Start the event sequence
1728			if(options.show.delay > 0) {
1729				clearTimeout(api.timers.show);
1730				api.timers.show = setTimeout(render, options.show.delay);
1731				if(events.show !== events.hide) {
1732					targets.hide.bind(events.hide, function() { clearTimeout(api.timers.show); });
1733				}
1734			}
1735			else { render(); }
1736		}
1737
1738		// Bind show events to target
1739		targets.show.bind(events.show, hoverIntent);
1740
1741		// Prerendering is enabled, create tooltip now
1742		if(options.show.ready || options.prerender) { hoverIntent(event); }
1743	});
1744};
1745
1746// Setup base plugins
1747PLUGINS = QTIP.plugins = {
1748	// Corner object parser
1749	Corner: function(corner) {
1750		corner = ('' + corner).replace(/([A-Z])/, ' $1').replace(/middle/gi, 'center').toLowerCase();
1751		this.x = (corner.match(/left|right/i) || corner.match(/center/) || ['inherit'])[0].toLowerCase();
1752		this.y = (corner.match(/top|bottom|center/i) || ['inherit'])[0].toLowerCase();
1753
1754		this.precedance = (corner.charAt(0).search(/^(t|b)/) > -1) ? 'y' : 'x';
1755		this.string = function() { return this.precedance === 'y' ? this.y+this.x : this.x+this.y; };
1756		this.abbreviation = function() { 
1757			var x = this.x.substr(0,1), y = this.y.substr(0,1);
1758			return x === y ? x : (x === 'c' || (x !== 'c' && y !== 'c')) ? y + x : x + y;
1759		};
1760	},
1761
1762	// Custom (more correct for qTip!) offset calculator
1763	offset: function(elem, container, fixed) {
1764		var pos = elem.offset(),
1765			parent = container,
1766			deep = 0,
1767			docBody = document.body,
1768			coffset;
1769
1770		function scroll(e, i) {
1771			pos.left += i * e.scrollLeft();
1772			pos.top += i * e.scrollTop();
1773		}
1774
1775		if(parent) {
1776			// Compensate for non-static containers offset
1777			do {
1778				if(parent.css('position') !== 'static') {
1779					coffset = parent[0] === docBody ?
1780						{ left: parseInt(parent.css('left'), 10) || 0, top: parseInt(parent.css('top'), 10) || 0 } :
1781						parent.position();
1782
1783					pos.left -= coffset.left + (parseInt(parent.css('borderLeftWidth'), 10) || 0) + (parseInt(parent.css('marginLeft'), 10) || 0);
1784					pos.top -= coffset.top + (parseInt(parent.css('borderTopWidth'), 10) || 0);
1785
1786					deep++;
1787				}
1788				if(parent[0] === docBody) { break; }
1789			}
1790			while(parent = parent.offsetParent());
1791
1792			// Compensate for containers scroll if it also has an offsetParent
1793			if(container[0] !== docBody && deep > 1) { scroll( container, 1 ); }
1794
1795			// Adjust for position.fixed tooltips (and also iOS scroll bug in v3.2 - v4.0)
1796			if((PLUGINS.iOS < 4.1 && PLUGINS.iOS > 3.1) || (!PLUGINS.iOS && fixed)) { scroll( $(window), -1 ); }
1797		}
1798
1799		return pos;
1800	},
1801	
1802	/*
1803	 * iOS 3.2 - 4.0 scroll fix detection used in offset() function.
1804	 */
1805	iOS: parseFloat(
1806		('' + (/CPU.*OS ([0-9_]{1,3})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0,''])[1])
1807			.replace('undefined', '3_2').replace('_','.')
1808	) || FALSE,
1809	
1810	/*
1811	 * jQuery-specific $.fn overrides
1812	 */
1813	fn: {
1814		/* Allow other plugins to successfully retrieve the title of an element with a qTip applied */
1815		attr: function(attr, val) {
1816			if(this.length) {
1817				var self = this[0],
1818					title = 'title',
1819					api = $.data(self, 'qtip');
1820
1821				if(attr === title && 'object' === typeof api && api.options.suppress) {
1822					if(arguments.length < 2) {
1823						return $.attr(self, oldtitle);
1824					}
1825					else {
1826						// If qTip is rendered and title was originally used as content, update it
1827						if(api && api.options.content.attr === title && api.cache.attr) {
1828							api.set('content.text', val);
1829						}
1830
1831						// Use the regular attr method to set, then cache the result
1832						return this.attr(oldtitle, val);
1833					}
1834				}
1835			}
1836
1837			return $.fn['attr'+replaceSuffix].apply(this, arguments);
1838		},
1839		
1840		/* Allow clone to correctly retrieve cached title attributes */
1841		clone: function(keepData) {
1842			var titles = $([]), title = 'title',
1843
1844			// Clone our element using the real clone method
1845			elems = $.fn['clone'+replaceSuffix].apply(this, arguments);
1846
1847			// Grab all elements with an oldtitle set, and change it to regular title attribute, if keepData is false
1848			if(!keepData) {
1849				elems.filter('['+oldtitle+']').attr('title', function() {
1850					return $.attr(this, oldtitle);
1851				})
1852				.removeAttr(oldtitle);
1853			}
1854
1855			return elems;
1856		},
1857
1858		/* 
1859		 * Taken directly from jQuery 1.8.2 widget source code
1860		 * Trigger 'remove' event on all elements on removal if jQuery UI isn't present 
1861		 */
1862		remove: $.ui ? NULL : function( selector, keepData ) {
1863			$(this).each(function() {
1864				if (!keepData) {
1865					if (!selector || $.filter( selector, [ this ] ).length) {
1866						$('*', this).add(this).each(function() {
1867							$(this).triggerHandler('remove');
1868						});
1869					}
1870				}
1871			});
1872		}
1873	}
1874};
1875
1876// Apply the fn overrides above
1877$.each(PLUGINS.fn, function(name, func) {
1878	if(!func) { return TRUE; }
1879	
1880	var old = $.fn[name+replaceSuffix] = $.fn[name];
1881	$.fn[name] = function() {
1882		return func.apply(this, arguments) || old.apply(this, arguments);
1883	};
1884});
1885
1886// Set global qTip properties
1887QTIP.version = '2.0.0pre';
1888QTIP.nextid = 0;
1889QTIP.inactiveEvents = 'click dblclick mousedown mouseup mousemove mouseleave mouseenter'.split(' ');
1890QTIP.zindex = 15000;
1891
1892// Define configuration defaults
1893QTIP.defaults = {
1894	prerender: FALSE,
1895	id: FALSE,
1896	overwrite: TRUE,
1897	suppress: TRUE,
1898	content: {
1899		text: TRUE,
1900		attr: 'title',
1901		title: {
1902			text: FALSE,
1903			button: FALSE
1904		}
1905	},
1906	position: {
1907		my: 'top left',
1908		at: 'bottom right',
1909		target: FALSE,
1910		container: FALSE,
1911		viewport: FALSE,
1912		adjust: {
1913			x: 0, y: 0,
1914			mouse: TRUE,
1915			resize: TRUE,
1916			method: 'flip flip'
1917		},
1918		effect: function(api, pos, viewport) {
1919			$(this).animate(pos, {
1920				duration: 200,
1921				queue: FALSE
1922			});
1923		}
1924	},
1925	show: {
1926		target: FALSE,
1927		event: 'mouseenter',
1928		effect: TRUE,
1929		delay: 90,
1930		solo: FALSE,
1931		ready: FALSE,
1932		autofocus: FALSE
1933	},
1934	hide: {
1935		target: FALSE,
1936		event: 'mouseleave',
1937		effect: TRUE,
1938		delay: 0,
1939		fixed: FALSE,
1940		inactive: FALSE,
1941		leave: 'window',
1942		distance: FALSE
1943	},
1944	style: {
1945		classes: '',
1946		widget: FALSE,
1947		width: FALSE,
1948		height: FALSE
1949	},
1950	events: {
1951		render: NULL,
1952		move: NULL,
1953		show: NULL,
1954		hide: NULL,
1955		toggle: NULL,
1956		visible: NULL,
1957		focus: NULL,
1958		blur: NULL
1959	}
1960};
1961
1962function Ajax(api)
1963{
1964	var self = this,
1965		tooltip = api.elements.tooltip,
1966		opts = api.options.content.ajax,
1967		namespace = '.qtip-ajax',
1968		rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
1969		first = TRUE;
1970
1971	api.checks.ajax = {
1972		'^content.ajax': function(obj, name, v) {
1973			// If content.ajax object was reset, set our local var
1974			if(name === 'ajax') { opts = v; }
1975
1976			if(name === 'once') {
1977				self.init();
1978			}
1979			else if(opts && opts.url) {
1980				self.load();
1981			}
1982			else {
1983				tooltip.unbind(namespace);
1984			}
1985		}
1986	};
1987
1988	$.extend(self, {
1989		init: function()
1990		{
1991			// Make sure ajax options are enabled and bind event
1992			if(opts && opts.url) {
1993				tooltip.unbind(namespace)[ opts.once ? 'one' : 'bind' ]('tooltipshow'+namespace, self.load);
1994			}
1995
1996			return self;
1997		},
1998
1999		load: function(event, first)
2000		{
2001			// Make sure default event hasn't been prevented
2002			if(event && event.isDefaultPrevented()) { return self; }
2003			
2004			var hasSelector = opts.url.indexOf(' '),
2005				url = opts.url,
2006				selector,
2007				hideFirst = opts.once && !opts.loading && first;
2008
2009			// If loading option is disabled, hide the tooltip until content is retrieved (first time only)
2010			if(hideFirst) { tooltip.css('visibility', 'hidden'); }
2011
2012			// Check if user delcared a content selector like in .load()
2013			if(hasSelector > -1) {
2014				selector = url.substr(hasSelector);
2015				url = url.substr(0, hasSelector);
2016			}
2017
2018			// Define common after callback for both success/error handlers
2019			function after() {
2020				// Re-display tip if loading and first time, and reset first flag
2021				if(hideFirst) { tooltip.css('visibility', ''); first = FALSE; }
2022
2023				// Call users complete if it was defined
2024				if($.isFunction(opts.complete)) { opts.complete.apply(this, arguments); }
2025			}
2026
2027			// Define success handler
2028			function successHandler(content) {
2029				if(selector) {
2030					// Create a dummy div to hold the results and grab the selector element
2031					content = $('<div/>')
2032						// inject the contents of the document in, removing the scripts
2033						// to avoid any 'Permission Denied' errors in IE
2034						.append(content.replace(rscript, ""))
2035						
2036						// Locate the specified elements
2037						.find(selector);
2038				}
2039
2040				// Set the content
2041				api.set('content.text', content);
2042			}
2043
2044			// Error handler
2045			function errorHandler(xh, status, error){ api.set('content.text', status + ': ' + error); }
2046
2047			// Setup $.ajax option object and process the request
2048			$.ajax( $.extend({ success: successHandler, error: errorHandler, context: api }, opts, { url: url, complete: after }) );
2049			
2050			return self;
2051		}
2052	});
2053
2054	self.init();
2055}
2056
2057
2058PLUGINS.ajax = function(api)
2059{
2060	var self = api.plugins.ajax;
2061	
2062	return 'object' === typeof self ? self : (api.plugins.ajax = new Ajax(api));
2063};
2064
2065PLUGINS.ajax.initialize = 'render';
2066
2067// Setup plugin sanitization
2068PLUGINS.ajax.sanitize = function(options)
2069{
2070	var content = options.content, opts;
2071	if(content && 'ajax' in content) {
2072		opts = content.ajax;
2073		if(typeof opts !== 'object') { opts = options.content.ajax = { url: opts }; }
2074		if('boolean' !== typeof opts.once && opts.once) { opts.once = !!opts.once; }
2075	}
2076};
2077
2078// Extend original api defaults
2079$.extend(TRUE, QTIP.defaults, {
2080	content: {
2081		ajax: {
2082			loading: TRUE,
2083			once: TRUE
2084		}
2085	}
2086});
2087
2088/* 
2089 * BGIFrame adaption (http://plugins.jquery.com/project/bgiframe)
2090 * Special thanks to Brandon Aaron
2091 */
2092function BGIFrame(api)
2093{
2094	var self = this,
2095		elems = api.elements,
2096		tooltip = elems.tooltip,
2097		namespace = '.bgiframe-' + api.id;
2098
2099	$.extend(self, {
2100		init: function()
2101		{
2102			// Create the BGIFrame element
2103			elems.bgiframe = $('<iframe class="ui-tooltip-bgiframe" frameborder="0" tabindex="-1" src="javascript:\'\';" ' +
2104				' style="display:block; position:absolute; z-index:-1; filter:alpha(opacity=0); ' +
2105					'-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";"></iframe>');
2106
2107			// Append the new element to the tooltip
2108			elems.bgiframe.appendTo(tooltip);
2109
2110			// Update BGIFrame on tooltip move
2111			tooltip.bind('tooltipmove'+namespace, self.adjust);
2112		},
2113
2114		adjust: function()
2115		{
2116			var dimensions = api.get('dimensions'), // Determine current tooltip dimensions
2117				plugin = api.plugins.tip,
2118				tip = elems.tip,
2119				tipAdjust, offset;
2120
2121			// Adjust border offset
2122			offset = parseInt(tooltip.css('border-left-width'), 10) || 0;
2123			offset = { left: -offset, top: -offset };
2124
2125			// Adjust for tips plugin
2126			if(plugin && tip) {
2127				tipAdjust = (plugin.corner.precedance === 'x') ? ['width', 'left'] : ['height', 'top'];
2128				offset[ tipAdjust[1] ] -= tip[ tipAdjust[0] ]();
2129			}
2130
2131			// Update bgiframe
2132			elems.bgiframe.css(offset).css(dimensions);
2133		},
2134
2135		destroy: function()
2136		{
2137			// Remove iframe
2138			elems.bgiframe.remove();
2139
2140			// Remove bound events
2141			tooltip.unbind(namespace);
2142		}
2143	});
2144
2145	self.init();
2146}
2147
2148PLUGINS.bgiframe = function(api)
2149{
2150	var browser = $.browser,
2151		self = api.plugins.bgiframe;
2152	
2153		// Proceed only if the browser is IE6 and offending elements are present
2154		if($('select, object').length < 1 || !(browser.msie && browser.version.charAt(0) === '6')) {
2155		return FALSE;
2156	}
2157
2158	return 'object' === typeof self ? self : (api.plugins.bgiframe = new BGIFrame(api));
2159};
2160
2161// Plugin needs to be initialized on render
2162PLUGINS.bgiframe.initialize = 'render';
2163
2164PLUGINS.imagemap = function(area, corner)
2165{
2166	if(!area.jquery) { area = $(area); }
2167
2168	var shape = area.attr('shape').toLowerCase(),
2169		baseCoords = area.attr('coords').split(','),
2170		coords = [],
2171		image = $('img[usemap="#'+area.parent('map').attr('name')+'"]'),
2172		imageOffset = image.offset(),
2173		result = {
2174			width: 0, height: 0,
2175			offset: { top: 1e10, right: 0, bottom: 0, left: 1e10 }
2176		},
2177		i = 0, next = 0;
2178
2179	// POLY area coordinate calculator
2180	//	Special thanks to Ed Cradock for helping out with this.
2181	//	Uses a binary search algorithm to find suitable coordinates.
2182	function polyCoordinates(result, coords)
2183	{
2184		var i = 0,
2185			compareX = 1, compareY = 1,
2186			realX = 0, realY = 0,
2187			newWidth = result.width,
2188			newHeight = result.height;
2189
2190		// Use a binary search algorithm to locate most suitable coordinate (hopefully)
2191		while(newWidth > 0 && newHeight > 0 && compareX > 0 && compareY > 0)
2192		{
2193			newWidth = Math.floor(newWidth / 2);
2194			newHeight = Math.floor(newHeight / 2);
2195
2196			if(corner.x === 'left'){ compareX = newWidth; }
2197			else if(corner.x === 'right'){ compareX = result.width - newWidth; }
2198			else{ compareX += Math.floor(newWidth / 2); }
2199
2200			if(corner.y === 'top'){ compareY = newHeight; }
2201			else if(corner.y === 'bottom'){ compareY = result.height - newHeight; }
2202			else{ compareY += Math.floor(newHeight / 2); }
2203
2204			i = coords.length; while(i--)
2205			{
2206				if(coords.length < 2){ break; }
2207
2208				realX = coords[i][0] - result.offset.left;
2209				realY = coords[i][1] - result.offset.top;
2210
2211				if((corner.x === 'left' && realX >= compareX) ||
2212				(corner.x === 'right' && realX <= compareX) ||
2213				(corner.x === 'center' && (realX < compareX || realX > (result.width - compareX))) ||
2214				(corner.y === 'top' && realY >= compareY) ||
2215				(corner.y === 'bottom' && realY <= compareY) ||
2216				(corner.y === 'center' && (realY < compareY || realY > (result.height - compareY)))) {
2217					coords.splice(i, 1);
2218				}
2219			}
2220		}
2221
2222		return { left: coords[0][0], top: coords[0][1] };
2223	}
2224
2225	// Make sure we account for padding and borders on the image
2226	imageOffset.left += Math.ceil((image.outerWidth() - image.width()) / 2);
2227	imageOffset.top += Math.ceil((image.outerHeight() - image.height()) / 2);
2228
2229	// Parse coordinates into proper array
2230	if(shape === 'poly') {
2231		i = baseCoords.length; while(i--)
2232		{
2233			next = [ parseInt(baseCoords[--i], 10), parseInt(baseCoords[i+1], 10) ];
2234
2235			if(next[0] > result.offset.right){ result.offset.right = next[0]; }
2236			if(next[0] < result.offset.left){ result.offset.left = next[0]; }
2237			if(next[1] > result.offset.bottom){ result.offset.bottom = next[1]; }
2238			if(next[1] < result.offset.top){ result.offset.top = next[1]; }
2239
2240			coords.push(next);
2241		}
2242	}
2243	else {
2244		coords = $.map(baseCoords, function(coord){ return parseInt(coord, 10); });
2245	}
2246
2247	// Calculate details
2248	switch(shape)
2249	{
2250		case 'rect':
2251			result = {
2252				width: Math.abs(coords[2] - coords[0]),
2253				height: Math.abs(coords[3] - coords[1]),
2254				offset: { left: coords[0], top: coords[1] }
2255			};
2256		break;
2257
2258		case 'circle':
2259			result = {
2260				width: coords[2] + 2,
2261				height: coords[2] + 2,
2262				offset: { left: coords[0], top: coords[1] }
2263			};
2264		break;
2265
2266		case 'poly':
2267			$.extend(result, {
2268				width: Math.abs(result.offset.right - result.offset.left),
2269				height: Math.abs(result.offset.bottom - result.offset.top)
2270			});
2271
2272			if(corner.string() === 'centercenter') {
2273				result.offset = {
2274					left: result.offset.left + (result.width / 2),
2275					top: result.offset.top + (result.height / 2)
2276				};
2277			}
2278			else {
2279				result.offset = polyCoordinates(result, coords.slice());
2280			}
2281
2282			result.width = result.height = 0;
2283		break;
2284	}
2285
2286	// Add image position to offset coordinates
2287	result.offset.left += imageOffset.left;
2288	result.offset.top += imageOffset.top;
2289
2290	return result;
2291};
2292
2293function Modal(api)
2294{
2295	var self = this,
2296		options = api.options.show.modal,
2297		elems = api.elements,
2298		tooltip = elems.tooltip,
2299		overlaySelector = '#qtip-overlay',
2300		globalNamespace = '.qtipmodal',
2301		namespace = globalNamespace + api.id,
2302		attr = 'is-modal-qtip',
2303		docBody = $(document.body),
2304		overlay;
2305
2306	// Setup option set checks
2307	api.checks.modal = {
2308		'^show.modal.(on|blur)$': function() {
2309			// Initialise
2310			self.init();
2311			
2312			// Show the modal if not visible already and tooltip is visible
2313			elems.overlay.toggle( tooltip.is(':visible') );
2314		}
2315	};
2316
2317	$.extend(self, {
2318		init: function()
2319		{
2320			// If modal is disabled... return
2321			if(!options.on) { return self; }
2322
2323			// Create the overlay if needed
2324			overlay = self.create();
2325
2326			// Add unique attribute so we can grab modal tooltips easily via a selector
2327			tooltip.attr(attr, TRUE)
2328
2329			.css('z-index', PLUGINS.modal.zindex + $(selector+'['+attr+']').length)
2330			
2331			// Remove previous bound events in globalNamespace
2332			.unbind(globalNamespace).unbind(namespace)
2333
2334			// Apply our show/hide/focus modal events
2335			.bind('tooltipshow'+globalNamespace+' tooltiphide'+globalNamespace, function(event, api, duration) {
2336				var oEvent = event.originalEvent;
2337				
2338				// Make sure mouseout doesn't trigger a hide when showing the modal and mousing onto backdrop
2339				if(oEvent && event.type === 'tooltiphide' && /mouse(leave|enter)/.test(oEvent.type) && $(oEvent.relatedTarget).closest(overlay[0]).length) {
2340					event.preventDefault();
2341				}
2342				else {
2343					self[ event.type.replace('tooltip', '') ](event, duration);
2344				}
2345			})
2346
2347			// Adjust modal z-index on tooltip focus
2348			.bind('tooltipfocus'+globalNamespace, function(event) {
2349				// If focus was cancelled before it reearch us, don't do anything
2350				if(event.isDefaultPrevented()) { return; }
2351
2352				var qtips = $(selector).filter('['+attr+']'),
2353
2354				// Keep the modal's lower than other, regular qtips
2355				newIndex = PLUGINS.modal.zindex + qtips.length,
2356				curIndex = parseInt(tooltip[0].style.zIndex, 10);
2357
2358				// Set overlay z-index
2359				overlay[0].style.zIndex = newIndex;
2360
2361				// Reduce modal z-index's and keep them properly ordered
2362				qtips.each(function() {
2363					if(this.style.zIndex > curIndex) {
2364						this.style.zIndex -= 1;
2365					}
2366				});
2367
2368				// Fire blur event for focused tooltip
2369				qtips.end().filter('.' + focusClass).qtip('blur', event.originalEvent);
2370
2371				// Set the new z-index
2372				tooltip.addClass(focusClass)[0].style.zIndex = newIndex;
2373
2374				// Prevent default handling
2375				event.preventDefault();
2376			})
2377
2378			// Focus any other visible modals when this one hides
2379			.bind('tooltiphide'+globalNamespace, function(event) {
2380				$('[' + attr + ']').filter(':visible').not(tooltip).last().qtip('focus', event);
2381			});
2382
2383			// Apply keyboard "Escape key" close handler
2384			if(options.escape) {
2385				$(window).unbind(namespace).bind('keydown'+namespace, function(event) {
2386					if(event.keyCode === 27 && tooltip.hasClass(focusClass)) {
2387						api.hide(event);
2388					}
2389				});
2390			}
2391
2392			// Apply click handler for blur option
2393			if(options.blur) {
2394				elems.overlay.unbind(namespace).bind('click'+namespace, function(event) {
2395					if(tooltip.hasClass(focusClass)) { api.hide(event); }
2396				});
2397			}
2398
2399			return self;
2400		},
2401
2402		create: function()
2403		{
2404			var elem = $(overlaySelector);
2405
2406			// Return if overlay is already rendered
2407			if(elem.length) { elems.overlay = elem; return elem; }
2408
2409			// Create document overlay
2410			overlay = elems.overlay = $('<div />', {
2411				id: overlaySelector.substr(1),
2412				html: '<div></div>',
2413				mousedown: function() { return FALSE; }
2414			})
2415			.insertBefore( $(selector).first() );
2416
2417			// Update position on window resize or scroll
2418			$(window).unbind(globalNamespace).bind('resize'+globalNamespace, function() {
2419				overlay.css({
2420					height: $(window).height(),
2421					width: $(window).width()
2422				});
2423			})
2424			.triggerHandler('resize');
2425
2426			return overlay;
2427		},
2428
2429		toggle: function(event, state, duration)
2430		{
2431			// Make sure default event hasn't been prevented
2432			if(event && event.isDefaultPrevented()) { return self; }
2433
2434			var effect = options.effect,
2435				type = state ? 'show': 'hide',
2436				visible = overlay.is(':visible'),
2437				modals = $('[' + attr + ']').filter(':visible').not(tooltip),
2438				zindex;
2439
2440			// Create our overlay if it isn't present already
2441			if(!overlay) { overlay = self.create(); }
2442
2443			// Prevent modal from conflicting with show.solo, and don't hide backdrop is other modals are visible
2444			if((overlay.is(':animated') && visible === state) || (!state && modals.length)) { return self; }
2445
2446			// State specific...
2447			if(state) {
2448				// Set position
2449				overlay.css({ left: 0, top: 0 });
2450
2451				// Toggle backdrop cursor style on show
2452				overlay.toggleClass('blurs', options.blur);
2453
2454				// Make sure we can't focus anything outside the tooltip
2455				docBody.delegate('*', 'focusin'+namespace, function(event) {
2456					if($(event.target).closest(selector)[0] !== tooltip[0]) {
2457						$('a, :input, img', tooltip).add(tooltip).focus();
2458					}
2459				});
2460			}
2461			else {
2462				// Undelegate focus handler
2463				docBody.undelegate('*', 'focusin'+namespace);
2464			}
2465
2466			// Stop all animations
2467			overlay.stop(TRUE, FALSE);
2468
2469			// Use custom function if provided
2470			if($.isFunction(effect)) {
2471				effect.call(overlay, state);
2472			}
2473
2474			// If no effect type is supplied, use a simple toggle
2475			else if(effect === FALSE) {
2476				overlay[ type ]();
2477			}
2478
2479			// Use basic fade function
2480			else {
2481				overlay.fadeTo( parseInt(duration, 10) || 90, state ? 1 : 0, function() {
2482					if(!state) { $(this).hide(); }
2483				});
2484			}
2485
2486			// Reset position on hide
2487			if(!state) {
2488				overlay.queue(function(next) {
2489					overlay.css({ left: '', top: '' });
2490					next();
2491				});
2492			}
2493
2494			return self;
2495		},
2496
2497		show: function(event, duration) { return self.toggle(event, TRUE, duration); },
2498		hide: function(event, duration) { return self.toggle(event, FALSE, duration); },
2499
2500		destroy: function()
2501		{
2502			var delBlanket = overlay;
2503
2504			if(delBlanket) {
2505				// Check if any other modal tooltips are present
2506				delBlanket = $('[' + attr + ']').not(tooltip).length < 1;
2507
2508				// Remove overlay if needed
2509				if(delBlanket) {
2510					elems.overlay.remove();
2511					$(window).unbind(globalNamespace);
2512				}
2513				else {
2514					elems.overlay.unbind(globalNamespace+api.id);
2515				}
2516
2517				// Undelegate focus handler
2518				docBody.undelegate('*', 'focusin'+namespace);
2519			}
2520
2521			// Remove bound events
2522			return tooltip.removeAttr(attr).unbind(globalNamespace);
2523		}
2524	});
2525
2526	self.init();
2527}
2528
2529PLUGINS.modal = function(api) {
2530	var self = api.plugins.modal;
2531
2532	return 'object' === typeof self ? self : (api.plugins.modal = new Modal(api));
2533};
2534
2535// Plugin needs to be initialized on render
2536PLUGINS.modal.initialize = 'render';
2537
2538// Setup sanitiztion rules
2539PLUGINS.modal.sanitize = function(opts) {
2540	if(opts.show) { 
2541		if(typeof opts.show.modal !== 'object') { opts.show.modal = { on: !!opts.show.modal }; }
2542		else if(typeof opts.show.modal.on === 'undefined') { opts.show.modal.on = TRUE; }
2543	}
2544};
2545
2546// Base z-index for all modal tooltips (use qTip core z-index as a base)
2547PLUGINS.modal.zindex = QTIP.zindex -= 200;
2548
2549// Extend original api defaults
2550$.extend(TRUE, QTIP.defaults, {
2551	show: {
2552		modal: {
2553			on: FALSE,
2554			effect: TRUE,
2555			blur: TRUE,
2556			escape: TRUE
2557		}
2558	}
2559});
2560
2561PLUGINS.svg = function(svg, corner)
2562{
2563	var doc = $(document),
2564		elem = svg[0],
2565		result = {
2566			width: 0, height: 0,
2567			offset: { top: 1e10, left: 1e10 }
2568		},
2569		box, mtx, root, point, tPoint;
2570
2571	if (elem.getBBox && elem.parentNode) {
2572		box = elem.getBBox();
2573		mtx = elem.getScreenCTM();
2574		root = elem.farthestViewportElement || elem;
2575
2576		// Return if no method is found
2577		if(!root.createSVGPoint) { return result; }
2578
2579		// Create our point var
2580		point = root.createSVGPoint();
2581
2582		// Adjust top and left
2583		point.x = box.x;
2584		point.y = box.y;
2585		tPoint = point.matrixTransform(mtx);
2586		result.offset.left = tPoint.x;
2587		result.offset.top = tPoint.y;
2588
2589		// Adjust width and height
2590		point.x += box.width;
2591		point.y += box.height;
2592		tPoint = point.matrixTransform(mtx);
2593		result.width = tPoint.x - result.offset.left;
2594		result.height = tPoint.y - result.offset.top;
2595
2596		// Adjust by scroll offset
2597		result.offset.left += doc.scrollLeft();
2598		result.offset.top += doc.scrollTop();
2599	}
2600
2601	return result;
2602};
2603
2604// Tip coordinates calculator
2605function calculateTip(corner, width, height)
2606{	
2607	var width2 = Math.ceil(width / 2), height2 = Math.ceil(height / 2),
2608
2609	// Define tip coordinates in terms of height and width values
2610	tips = {
2611		bottomright:	[[0,0],				[width,height],		[width,0]],
2612		bottomleft:		[[0,0],				[width,0],				[0,height]],
2613		topright:		[[0,height],		[width,0],				[width,height]],
2614		topleft:			[[0,0],				[0,height],				[width,height]],
2615		topcenter:		[[0,height],		[width2,0],				[width,height]],
2616		bottomcenter:	[[0,0],				[width,0],				[width2,height]],
2617		rightcenter:	[[0,0],				[width,height2],		[0,height]],
2618		leftcenter:		[[width,0],			[width,height],		[0,height2]]
2619	};
2620
2621	// Set common side shapes
2622	tips.lefttop = tips.bottomright; tips.righttop = tips.bottomleft;
2623	tips.leftbottom = tips.topright; tips.rightbottom = tips.topleft;
2624
2625	return tips[ corner.string() ];
2626}
2627
2628
2629function Tip(qTip, command)
2630{
2631	var self = this,
2632		opts = qTip.options.style.tip,
2633		elems = qTip.elements,
2634		tooltip = elems.tooltip,
2635		cache = { 
2636			top: 0, 
2637			left: 0, 
2638			corner: ''
2639		},
2640		size = {
2641			width: opts.width,
2642			height: opts.height
2643		},
2644		color = { },
2645		border = opts.border || 0,
2646		namespace = '.qtip-tip',
2647		hasCanvas = !!($('<canvas />')[0] || {}).getContext;
2648
2649	self.corner = NULL;
2650	self.mimic = NULL;
2651	self.border = border;
2652	self.offset = opts.offset;
2653	self.size = size;
2654
2655	// Add new option checks for the plugin
2656	qTip.checks.tip = {
2657		'^position.my|style.tip.(corner|mimic|border)$': function() {
2658			// Make sure a tip can be drawn
2659			if(!self.init()) {
2660				self.destroy();
2661			}
2662
2663			// Reposition the tooltip
2664			qTip.reposition();
2665		},
2666		'^style.tip.(height|width)$': function() {
2667			// Re-set dimensions and redraw the tip
2668			size = {
2669				width: opts.width,
2670				height: opts.height
2671			};
2672			self.create();
2673			self.update();
2674
2675			// Reposition the tooltip
2676			qTip.reposition();
2677		},
2678		'^content.title.text|style.(classes|widget)$': function() {
2679			if(elems.tip) {
2680				self.update();
2681			}
2682		}
2683	};
2684
2685	function reposition(event, api, pos, viewport) {
2686		if(!elems.tip) { return; }
2687
2688		var newCorner = $.extend({}, self.corner),
2689			adjust = pos.adjusted,
2690			method = qTip.options.position.adjust.method.split(' '),
2691			horizontal = method[0],
2692			vertical = method[1] || method[0],
2693			shift = { left: FALSE, top: FALSE, x: 0, y: 0 },
2694			offset, css = {}, props;
2695
2696		// Make sure our tip position isn't fixed e.g. doesn't adjust with viewport
2697		if(self.corner.fixed !== TRUE) {
2698			// Horizontal - Shift or flip method
2699			if(horizontal === 'shift' && newCorner.precedance === 'x' && adjust.left && newCorner.y !== 'center') {
2700				newCorner.precedance = newCorner.precedance === 'x' ? 'y' : 'x';
2701			}
2702			else if(horizontal === 'flip' && adjust.left){
2703				newCorner.x = newCorner.x === 'center' ? (adjust.left > 0 ? 'left' : 'right') : (newCorner.x === 'left' ? 'right' : 'left');
2704			}
2705
2706			// Vertical - Shift or flip method
2707			if(vertical === 'shift' && newCorner.precedance === 'y' && adjust.top && newCorner.x !== 'center') {
2708				newCorner.precedance = newCorner.precedance === 'y' ? 'x' : 'y';
2709			}
2710			else if(vertical === 'flip' && adjust.top) {
2711				newCorner.y = newCorner.y === 'center' ? (adjust.top > 0 ? 'top' : 'bottom') : (newCorner.y === 'top' ? 'bottom' : 'top');
2712			}
2713
2714			// Update and redraw the tip if needed (check cached details of last drawn tip)
2715			if(newCorner.string() !== cache.corner && (cache.top !== adjust.top || cache.left !== adjust.left)) {
2716				self.update(newCorner, FALSE);
2717			}
2718		}
2719
2720		// Setup tip offset properties
2721		offset = self.position(newCorner, adjust);
2722		if(offset.right !== undefined) { offset.left = -offset.right; }
2723		if(offset.bottom !== undefined) { offset.top = -offset.bottom; }
2724		offset.user = Math.max(0, opts.offset);
2725
2726		// Viewport "shift" specific adjustments
2727		if(shift.left = (horizontal === 'shift' && !!adjust.left)) {
2728			if(newCorner.x === 'center') {
2729				css['margin-left'] = shift.x = offset['margin-left'] - adjust.left;
2730			}
2731			else {
2732				props = offset.right !== undefined ?
2733					[ adjust.left, -offset.left ] : [ -adjust.left, offset.left ];
2734
2735				if( (shift.x = Math.max(props[0], props[1])) > props[0] ) {
2736					pos.left -= adjust.left;
2737					shift.left = FALSE;
2738				}
2739				
2740				css[ offset.right !== undefined ? 'right' : 'left' ] = shift.x;
2741			}
2742		}
2743		if(shift.top = (vertical === 'shift' && !!adjust.top)) {
2744			if(newCorner.y === 'center') {
2745				css['margin-top'] = shift.y = offset['margin-top'] - adjust.top;
2746			}
2747			else {
2748				props = offset.bottom !== undefined ?
2749					[ adjust.top, -offset.top ] : [ -adjust.top, offset.top ];
2750
2751				if( (shift.y = Math.max(props[0], props[1])) > props[0] ) {
2752					pos.top -= adjust.top;
2753					shift.top = FALSE;
2754				}
2755
2756				css[ offset.bottom !== undefined ? 'bottom' : 'top' ] = shift.y;
2757			}
2758		}
2759
2760		/*
2761		 * If the tip is adjusted in both dimensions, or in a
2762		 * direction that would cause it to be anywhere but the
2763		 * outer border, hide it!
2764		 */
2765		elems.tip.css(css).toggle(
2766			!((shift.x && shift.y) || (newCorner.x === 'center' && shift.y) || (newCorner.y === 'center' && shift.x))
2767		);
2768
2769		// Adjust position to accomodate tip dimensions
2770		pos.left -= offset.left.charAt ? offset.user : horizontal !== 'shift' || shift.top || !shift.left && !shift.top ? offset.left : 0;
2771		pos.top -= offset.top.charAt ? offset.user : vertical !== 'shift' || shift.left || !shift.left && !shift.top ? offset.top : 0;
2772
2773		// Cache details
2774		cache.left = adjust.left; cache.top = adjust.top;
2775		cache.corner = newCorner.string();
2776	}
2777
2778	/* border width calculator */
2779	function borderWidth(corner, side, backup) {
2780		side = !side ? corner[corner.precedance] : side;
2781		
2782		var isFluid = tooltip.hasClass(fluidClass),
2783			isTitleTop = elems.titlebar && corner.y === 'top',
2784			elem = isTitleTop ? elems.titlebar : elems.content,
2785			css = 'border-' + side + '-width',
2786			val;
2787
2788		// Grab the border-width value (add fluid class if needed)
2789		tooltip.addClass(fluidClass);
2790		val = parseInt(elem.css(css), 10);
2791		val = (backup ? val || parseInt(tooltip.css(css), 10) : val) || 0;
2792		tooltip.toggleClass(fluidClass, isFluid);
2793
2794		return val;
2795	}
2796
2797	function borderRadius(corner) {
2798		var isTitleTop = elems.titlebar && corner.y === 'top',
2799			elem = isTitleTop ? elems.titlebar : elems.content,
2800			moz = $.browser.mozilla,
2801			prefix = moz ? '-moz-' : $.browser.webkit ? '-webkit-' : '',
2802			side = corner.y + (moz ? '' : '-') + corner.x,
2803			css = prefix + (moz ? 'border-radius-' + side : 'border-' + side + '-radius');
2804
2805		return parseInt(elem.css(css), 10) || parseInt(tooltip.css(css), 10) || 0;
2806	}
2807
2808	function calculateSize(corner) {
2809		var y = corner.precedance === 'y',
2810			width = size [ y ? 'width' : 'height' ],
2811			height = size [ y ? 'height' : 'width' ],
2812			isCenter = corner.string().indexOf('center') > -1,
2813			base = width * (isCenter ? 0.5 : 1),
2814			pow = Math.pow,
2815			round = Math.round,
2816			bigHyp, ratio, result,
2817
2818		smallHyp = Math.sqrt( pow(base, 2) + pow(height, 2) ),
2819		
2820		hyp = [
2821			(border / base) * smallHyp, (border / height) * smallHyp
2822		];
2823		hyp[2] = Math.sqrt( pow(hyp[0], 2) - pow(border, 2) );
2824		hyp[3] = Math.sqrt( pow(hyp[1], 2) - pow(border, 2) );
2825
2826		bigHyp = smallHyp + hyp[2] + hyp[3] + (isCenter ? 0 : hyp[0]);
2827		ratio = bigHyp / smallHyp;
2828
2829		result = [ round(ratio * height), round(ratio * width) ];
2830		return { height: result[ y ? 0 : 1 ], width: result[ y ? 1 : 0 ] };
2831	}
2832
2833	$.extend(self, {
2834		init: function()
2835		{
2836			var enabled = self.detectCorner() && (hasCanvas || $.browser.msie);
2837
2838			// Determine tip corner and type
2839			if(enabled) {
2840				// Create a new tip and draw it
2841				self.create();
2842				self.update();
2843
2844				// Bind update events
2845				tooltip.unbind(namespace).bind('tooltipmove'+namespace, reposition);
2846			}
2847			
2848			return enabled;
2849		},
2850
2851		detectCorner: function()
2852		{
2853			var corner = opts.corner,
2854				posOptions = qTip.options.position,
2855				at = posOptions.at,
2856				my = posOptions.my.string ? posOptions.my.string() : posOptions.my;
2857
2858			// Detect corner and mimic properties
2859			if(corner === FALSE || (my === FALSE && at === FALSE)) {
2860				return FALSE;
2861			}
2862			else {
2863				if(corner === TRUE) {
2864					self.corner = new PLUGINS.Corner(my);
2865				}
2866				else if(!corner.string) {
2867					self.corner = new PLUGINS.Corner(corner);
2868					self.corner.fixed = TRUE;
2869				}
2870			}
2871
2872			return self.corner.string() !== 'centercenter';
2873		},
2874
2875		detectColours: function() {
2876			var i, fill, border,
2877				tip = elems.tip.css({ backgroundColor: '', border: '' }),
2878				corner = self.corner,
2879				precedance = corner[ corner.precedance ],
2880
2881				borderSide = 'border-' + precedance + '-color',
2882				borderSideCamel = 'border' + precedance.charAt(0) + precedance.substr(1) + 'Color',
2883
2884				invalid = /rgba?\(0, 0, 0(, 0)?\)|transparent/i,
2885				backgroundColor = 'background-color',
2886				transparent = 'transparent',
2887
2888				bodyBorder = $(document.body).css('color'),
2889				contentColour = qTip.elements.content.css('color'),
2890
2891				useTitle = elems.titlebar && (corner.y === 'top' || (corner.y === 'center' && tip.position().top + (size.height / 2) + opts.offset < elems.titlebar.outerHeight(1))),
2892				colorElem = useTitle ? elems.titlebar : elems.content;
2893
2894			// Apply the fluid class so we can see our CSS values properly
2895			tooltip.addClass(fluidClass);
2896
2897			// Detect tip colours from CSS styles
2898			color.fill = fill = tip.css(backgroundColor);
2899			color.border = border = tip[0].style[ borderSideCamel ] || tip.css(borderSide) || tooltip.css(borderSide);
2900			
2901			// Make sure colours are valid
2902			if(!fill || invalid.test(fill)) {
2903				color.fill = colorElem.css(backgroundColor) || transparent;
2904				if(invalid.test(color.fill)) {
2905					color.fill = tooltip.css(backgroundColor) || fill;
2906				}
2907			}
2908			if(!border || invalid.test(border) || border === bodyBorder) {
2909				color.border = colorElem.css(borderSide) || transparent;
2910				if(invalid.test(color.border) || color.border === contentColour) {
2911					color.border = border;
2912				}
2913			}
2914
2915			// Reset background and border colours
2916			$('*', tip).add(tip).css(backgroundColor, transparent).css('border', '');
2917
2918			// Remove fluid class
2919			tooltip.removeClass(fluidClass);
2920		},
2921
2922		create: function()
2923		{
2924			var width = size.width,
2925				height = size.height,
2926				vml;
2927
2928			// Remove previous tip element if present
2929			if(elems.tip) { elems.tip.remove(); }
2930
2931			// Create tip element and prepend to the tooltip
2932			elems.tip = $('<div />', { 'class': 'ui-tooltip-tip' }).css({ width: width, height: height }).prependTo(tooltip);
2933
2934			// Create tip drawing element(s)
2935			if(hasCanvas) {
2936				// save() as soon as we create the canvas element so FF2 doesn't bork on our first restore()!
2937				$('<canvas />').appendTo(elems.tip)[0].getContext('2d').save();
2938			}
2939			else {
2940				vml = '<vml:shape coordorigin="0,0" style="display:inline-block; position:absolute; behavior:url(#default#VML);"></vml:shape>';
2941				elems.tip.html(vml + vml);
2942			}
2943		},
2944
2945		update: function(corner, position)
2946		{
2947			var tip = elems.tip,
2948				inner = tip.children(),
2949				width = size.width,
2950				height = size.height,
2951				regular = 'px solid ',
2952				transparent = 'px dashed transparent', // Dashed IE6 border-transparency hack. Awesome!
2953				mimic = opts.mimic,
2954				round = Math.round,
2955				precedance, context, coords, translate, newSize;
2956
2957			// Re-determine tip if not already set
2958			if(!corner) { corner = self.corner; }
2959
2960			// Use corner property if we detect an invalid mimic value
2961			if(mimic === FALSE) { mimic = corner; }
2962
2963			// Otherwise inherit mimic properties from the corner object as necessary
2964			else {
2965				mimic = new PLUGINS.Corner(mimic);
2966				mimic.precedance = corner.precedance;
2967
2968				if(mimic.x === 'inherit') { mimic.x = corner.x; }
2969				else if(mimic.y === 'inherit') { mimic.y = corner.y; }
2970				else if(mimic.x === mimic.y) {
2971					mimic[ corner.precedance ] = corner[ corner.precedance ];
2972				}
2973			}
2974			precedance = mimic.precedance;
2975
2976			// Update our colours
2977			self.detectColours();
2978
2979			// Detect border width, taking into account colours
2980			if(color.border !== 'transparent' && color.border !== '#123456') {
2981				// Grab border width
2982				border = borderWidth(corner, NULL, TRUE);
2983
2984				// If border width isn't zero, use border color as fill (1.0 style tips)
2985				if(opts.border === 0 && border > 0) { color.fill = color.border; }
2986
2987				// Set border width (use detected border width if opts.border is true)
2988				self.border = border = opts.border !== TRUE ? opts.border : border;
2989			}
2990
2991			// Border colour was invalid, set border to zero
2992			else { self.border = border = 0; }
2993
2994			// Calculate coordinates
2995			coords = calculateTip(mimic, width , height);
2996
2997			// Determine tip size
2998			self.size = newSize = calculateSize(corner);
2999			tip.css(newSize);
3000
3001			// Calculate tip translation
3002			if(corner.precedance === 'y') {
3003				translate = [
3004					round(mimic.x === 'left' ? border : mimic.x === 'right' ? newSize.width - width - border : (newSize.width - width) / 2),
3005					round(mimic.y === 'top' ?  newSize.height - height : 0)
3006				];
3007			}
3008			else {
3009				translate = [
3010					round(mimic.x === 'left' ? newSize.width - width : 0),
3011					round(mimic.y === 'top' ? border : mimic.y === 'bottom' ? newSize.height - height - border : (newSize.height - height) / 2)
3012				];
3013			}
3014
3015			// Canvas drawing implementation
3016			if(hasCanvas) {
3017				// Set the canvas size using calculated size
3018				inner.attr(newSize);
3019				
3020				// Grab canvas context and clear/save it
3021				context = inner[0].getContext('2d');
3022				context.restore(); context.save();
3023				context.clearRect(0,0,3000,3000);
3024				
3025				// Translate origin
3026				context.translate(translate[0], translate[1]);
3027				
3028				// Draw the tip
3029				context.beginPath();
3030				context.moveTo(coords[0][0], coords[0][1]);
3031				context.lineTo(coords[1][0], coords[1][1]);
3032				context.lineTo(coords[2][0], coords[2][1]);
3033				context.closePath();
3034				context.fillStyle = color.fill;
3035				context.strokeStyle = color.border;
3036				context.lineWidth = border * 2;
3037				context.lineJoin = 'miter';
3038				context.miterLimit = 100;
3039				if(border) { context.stroke(); }
3040				context.fill();
3041			}
3042
3043			// VML (IE Proprietary implementation)
3044			else {
3045				// Setup coordinates string
3046				coords = 'm' + coords[0][0] + ',' + coords[0][1] + ' l' + coords[1][0] +
3047					',' + coords[1][1] + ' ' + coords[2][0] + ',' + coords[2][1] + ' xe';
3048
3049				// Setup VML-specific offset for pixel-perfection
3050				translate[2] = border && /^(r|b)/i.test(corner.string()) ?
3051					parseFloat($.browser.version, 10) === 8 ? 2 : 1 : 0;
3052
3053				// Set initial CSS
3054				inner.css({
3055					antialias: ''+(mimic.string().indexOf('center') > -1),
3056					left: translate[0] - (translate[2] * Number(precedance === 'x')),
3057					top: translate[1] - (translate[2] * Number(precedance === 'y')),
3058					width: width + border,
3059					height: height + border
3060				})
3061				.each(function(i) {
3062					var $this = $(this);
3063
3064					// Set shape specific attributes
3065					$this[ $this.prop ? 'prop' : 'attr' ]({
3066						coordsize: (width+border) + ' ' + (height+border),
3067						path: coords,
3068						fillcolor: color.fill,
3069						filled: !!i,
3070						stroked: !!!i
3071					})
3072					.css({ display: border || i ? 'block' : 'none' });
3073
3074					// Check if border is enabled and add stroke element
3075					if(!i && $this.html() === '') {
3076						$this.html(
3077							'<vml:stroke weight="'+(border*2)+'px" color="'+color.border+'" miterlimit="1000" joinstyle="miter" ' +
3078							' style="behavior:url(#default#VML); display:inline-block;" />'
3079						);
3080					}
3081				});
3082			}
3083
3084			// Position if needed
3085			if(position !== FALSE) { self.position(corner); }
3086		},
3087
3088		// Tip positioning method
3089		position: function(corner)
3090		{
3091			var tip = elems.tip,
3092				position = {},
3093				userOffset = Math.max(0, opts.offset),
3094				precedance, dimensions, corners;
3095
3096			// Return if tips are disabled or tip is not yet rendered
3097			if(opts.corner === FALSE || !tip) { return FALSE; }
3098
3099			// Inherit corner if not provided
3100			corner = corner || self.corner;
3101			precedance = corner.precedance;
3102
3103			// Determine which tip dimension to use for adjustment
3104			dimensions = calculateSize(corner);
3105
3106			// Setup corners and offset array
3107			corners = [ corner.x, corner.y ];
3108			if(precedance === 'x') { corners.reverse(); }
3109
3110			// Calculate tip position
3111			$.each(corners, function(i, side) {
3112				var b, br;
3113
3114				if(side === 'center') {
3115					b = precedance === 'y' ? 'left' : 'top';
3116					position[ b ] = '50%';
3117					position['margin-' + b] = -Math.round(dimensions[ precedance === 'y' ? 'width' : 'height' ] / 2) + userOffset;
3118				}
3119				else {
3120					b = borderWidth(corner, side, TRUE);
3121					br = borderRadius(corner);
3122					
3123					position[ side ] = i ?
3124						border ? borderWidth(corner, side) : 0 : 
3125						userOffset + (br > b ? br : 0);
3126				}
3127			});
3128
3129			// Adjust for tip dimensions
3130			position[ corner[precedance] ] -= dimensions[ precedance === 'x' ? 'width' : 'height' ];
3131
3132			// Set and return new position
3133			tip.css({ top: '', bottom: '', left: '', right: '', margin: '' }).css(position);
3134			return position;
3135		},
3136		
3137		destroy: function()
3138		{
3139			// Remov tip and bound events
3140			if(elems.tip) { elems.tip.remove(); }
3141			tooltip.unbind(namespace);
3142		}
3143	});
3144	
3145	self.init();
3146}
3147
3148PLUGINS.tip = function(api)
3149{
3150	var self = api.plugins.tip;
3151	
3152	return 'object' === typeof self ? self : (api.plugins.tip = new Tip(api));
3153};
3154
3155// Initialize tip on render
3156PLUGINS.tip.initialize = 'render';
3157
3158// Setup plugin sanitization options
3159PLUGINS.tip.sanitize = function(options)
3160{
3161	var style = options.style, opts;
3162	if(style && 'tip' in style) {
3163		opts = options.style.tip;
3164		if(typeof opts !== 'object'){ options.style.tip = { corner: opts }; }
3165		if(!(/string|boolean/i).test(typeof opts.corner)) { opts.corner = TRUE; }
3166		if(typeof opts.width !== 'number'){ delete opts.width; }
3167		if(typeof opts.height !== 'number'){ delete opts.height; }
3168		if(typeof opts.border !== 'number' && opts.border !== TRUE){ delete opts.border; }
3169		if(typeof opts.offset !== 'number'){ delete opts.offset; }
3170	}
3171};
3172
3173// Extend original qTip defaults
3174$.extend(TRUE, QTIP.defaults, {
3175	style: {
3176		tip: {
3177			corner: TRUE,
3178			mimic: FALSE,
3179			width: 6,
3180			height: 6,
3181			border: TRUE,
3182			offset: 0
3183		}
3184	}
3185});
3186
3187
3188}(jQuery, window));;
3189(function ($) {
3190Drupal.behaviors.qtip = {
3191  attach: function(context) {
3192
3193    // Set delay on click to immediate
3194    if (Drupal.settings.qtip.show_event_type == 'click') {
3195      show_delay = 1;
3196    }
3197    else {
3198      show_delay = 140; // This is the default qTip value, set for hover links
3199    }
3200
3201    // Determine how to position the tooltip
3202    if (Drupal.settings.qtip.show_speech_bubble_tip) {
3203      // Set the proper qtip speech-bubble tip position
3204      if (Drupal.settings.qtip.show_speech_bubble_tip_side) {
3205        switch_position = Drupal.settings.qtip.tooltip_position.split('_');
3206        // We do not want to put tooltip on it's side if it is being displayed
3207        // in the center or side of an element
3208        if (switch_position[1] == 'center') {
3209          tip_position = Drupal.settings.qtip.tooltip_position;
3210        }
3211        else {
3212          tip_position = switch_position[1] + ' ' + switch_position[0];
3213        }
3214      }
3215      // Otherwise just set it to the same value as the tooltip
3216      else {
3217        tip_position = Drupal.settings.qtip.tooltip_position;
3218      }
3219    }
3220    else {
3221      tip_position = false;
3222    }
3223    
3224    // Is a custom style declared by the admin?
3225    if(Drupal.settings.qtip.color == 'custom-color') {
3226      Drupal.settings.qtip.color = Drupal.settings.qtip.custom_color;
3227    }
3228
3229    // Do we want a shadow to show under the tooltip?
3230    if(Drupal.settings.qtip.show_shadow) {
3231      style_classes = 'ui-tooltip-shadow ' + Drupal.settings.qtip.color;
3232    }
3233    else {
3234      style_classes = Drupal.settings.qtip.color;
3235    }
3236    
3237    // Do we want to show the tooltips with rounded corners?
3238    if(Drupal.settings.qtip.rounded_corners) {
3239      style_classes += ' ui-tooltip-rounded';
3240    }
3241    
3242    //Do we want a solid tip to display
3243    if (Drupal.settings.qtip.show_speech_bubble_tip_solid) {
3244      solid_tip = false;
3245    }
3246    else {
3247      solid_tip = 5;
3248    }
3249
3250/*** Standard Settings ***/
3251    $('.qtip-link').each(function() {
3252      if(Drupal.settings.qtip.show_event_type == 'click') {
3253        $(this).addClass('ui-tooltip-click');
3254      }
3255      
3256      // if there is a title associated with this qtip...
3257      if ($(this).children('.qtip-header').length) {
3258        tooltip_title = $(this).children('.qtip-header').html();
3259      }
3260      // if there isn't we don't want a blank title area to show on the qtip...
3261      else {
3262        tooltip_title = false;
3263      }
3264      
3265      build_qtip(this, 'node');
3266    });
3267    
3268/*** Additional Elements ***/
3269    if(Drupal.settings.qtip.additional_elements) {
3270      $(Drupal.settings.qtip.additional_elements).each(function() {
3271        $(this).addClass('qtip-additional-element');
3272        if(Drupal.settings.qtip.show_event_type == 'click') {
3273          $(this).addClass('ui-tooltip-click');
3274        }
3275        
3276        // if there is a title associated with this qtip...
3277        if ($(this).children('.qtip-header').length) {
3278          tooltip_title = $(this).children('.qtip-header').html();
3279        }
3280        // if there isn't we don't want a blank title area to show on the qtip...
3281        else {
3282          tooltip_title = false;
3283        }
3284        
3285        build_qtip(this, 'node');
3286      });
3287    }
3288
3289/*** Webform Settings ***/
3290    // Does the admin even want the descriptions to show up in a tooltip?
3291    if (Drupal.settings.qtip.show_webform_descriptions) {
3292      /*
3293       form-text - works well
3294       form-textarea - works, but width can throw off the tooltip
3295       form-radios - works, but the radios element wrapper is 100% width, so throws off tooltip like form-textarea
3296       form-select - works well - should maybe have something like mouseenter or something for usability
3297      */
3298      // Elements where description is a sibling to the selected element
3299      $('form.webform-client-form .form-text,' +
3300        'form.webform-client-form .form-select').each(function() {
3301        description = $(this).siblings('.description');
3302        description.css('display', 'none');
3303        tooltip_title = false;
3304        show_delay = 1;
3305        build_qtip(this, 'form', description.html());
3306      });
3307  
3308      // Elements where description is a sibling to the PARENT of the selected element
3309      $('form.webform-client-form .form-radios,' +
3310        'form.webform-client-form .form-textarea').each(function() {
3311        description = $(this).parent().siblings('.description');
3312        description.css('display', 'none');
3313        tooltip_title = false;
3314        show_delay = 1;
3315        
3316        build_qtip(this, 'form', description.html());
3317      });
3318    }
3319    
3320    
3321    
3322    function build_qtip(el, type, desc) {
3323      if (type == 'form') {
3324        show_text = desc ? desc : '';
3325        // By default, the description text is wrapped inside <p>
3326        // Since we are putting this in a tooltip we want to remove this
3327        // and any other tags that might possibly get set
3328        show_text = show_text.replace(/(<.*?>)/ig,"");
3329        set_my = 'left_center';
3330        tip_position = 'left_center';
3331        set_at = 'right_center';
3332        show_event = 'focus';
3333        hide_event = 'blur';
3334      }
3335      else { // "Normal" tooltip
3336        show_text = $(el).children('.qtip-tooltip');
3337        set_my = Drupal.settings.qtip.tooltip_position;
3338        set_at = Drupal.settings.qtip.target_position;
3339        show_event = Drupal.settings.qtip.show_event_type;
3340        hide_event = Drupal.settings.qtip.hide_event_type;
3341      }
3342      $(el).qtip({
3343        content: {
3344          text: show_text,
3345          title: {
3346            text: tooltip_title
3347          }
3348        },
3349        position: {
3350          my: set_my, // my = speech bubble position on tooltip
3351          at: set_at, // at = where on link text tooltip will appear
3352          adjust: {
3353            screen: true // Keeps tooltip within visible window
3354          }
3355        },
3356        style: {
3357          classes: style_classes,
3358          tip: {
3359            corner: tip_position, // Position of speech bubble tip...false will not display tip
3360            border: solid_tip, // parseInt(Drupal.settings.qtip.border_width)
3361            width: parseInt(Drupal.settings.qtip.speech_bubble_size),
3362            height: parseInt(Drupal.settings.qtip.speech_bubble_size)
3363          }
3364        },
3365        show: {
3366          event: show_event,
3367          solo: true, // Determines whether or not the tooltip will hide all others when the show.event is triggered on the show.target. 
3368          delay: show_delay
3369        },
3370        hide: {
3371          event: hide_event,
3372          fixed: true //When set to true, the tooltip will not hide if moused over, allowing the contents to be clicked and interacted with.
3373        }
3374      });
3375    }
3376  }
3377};
3378})(jQuery);
3379;
3380/*! jquery.cookie v1.4.1 | MIT */
3381!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?a(require("jquery")):a(jQuery)}(function(a){function b(a){return h.raw?a:encodeURIComponent(a)}function c(a){return h.raw?a:decodeURIComponent(a)}function d(a){return b(h.json?JSON.stringify(a):String(a))}function e(a){0===a.indexOf('"')&&(a=a.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return a=decodeURIComponent(a.replace(g," ")),h.json?JSON.parse(a):a}catch(b){}}function f(b,c){var d=h.raw?b:e(b);return a.isFunction(c)?c(d):d}var g=/\+/g,h=a.cookie=function(e,g,i){if(void 0!==g&&!a.isFunction(g)){if(i=a.extend({},h.defaults,i),"number"==typeof i.expires){var j=i.expires,k=i.expires=new Date;k.setTime(+k+864e5*j)}return document.cookie=[b(e),"=",d(g),i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}for(var l=e?void 0:{},m=document.cookie?document.cookie.split("; "):[],n=0,o=m.length;o>n;n++){var p=m[n].split("="),q=c(p.shift()),r=p.join("=");if(e&&e===q){l=f(r,g);break}e||void 0===(r=f(r))||(l[q]=r)}return l};h.defaults={},a.removeCookie=function(b,c){return void 0===a.cookie(b)?!1:(a.cookie(b,"",a.extend({},c,{expires:-1})),!a.cookie(b))}});;
3382
3383/*!
3384 * jQuery Form Plugin
3385 * version: 2.52 (07-DEC-2010)
3386 * @requires jQuery v1.3.2 or later
3387 *
3388 * Examples and documentation at: http://malsup.com/jquery/form/
3389 * Dual licensed under the MIT and GPL licenses:
3390 *   http://www.opensource.org/licenses/mit-license.php
3391 *   http://www.gnu.org/licenses/gpl.html
3392 */
3393;(function(b){function q(){if(b.fn.ajaxSubmit.debug){var a="[jquery.form] "+Array.prototype.join.call(arguments,"");if(window.console&&window.console.log)window.console.log(a);else window.opera&&window.opera.postError&&window.opera.postError(a)}}b.fn.ajaxSubmit=function(a){function f(){function t(){var o=i.attr("target"),m=i.attr("action");l.setAttribute("target",u);l.getAttribute("method")!="POST"&&l.setAttribute("method","POST");l.getAttribute("action")!=e.url&&l.setAttribute("action",e.url);e.skipEncodingOverride|| i.attr({encoding:"multipart/form-data",enctype:"multipart/form-data"});e.timeout&&setTimeout(function(){F=true;s()},e.timeout);var v=[];
3393try{if(e.extraData)for(var w in e.extraData)v.push(b('<input type="hidden" name="'+w+'" value="'+e.extraData[w]+'" />').appendTo(l)[0]);r.appendTo("body");r.data("form-plugin-onload",s);l.submit()}finally{l.setAttribute("action",m);o?l.setAttribute("target",o):i.removeAttr("target");b(v).remove()}}function s(){if(!G){r.removeData("form-plugin-onload");var o=true; try{if(F)throw"timeout";p=x.contentWindow?x.contentWindow.document:x.contentDocument?x.contentDocument:x.document;var m=e.dataType=="xml"||p.XMLDocument||b.isXMLDoc(p);q("isXml="+m);if(!m&&window.opera&&(p.body==null||p.body.innerHTML==""))if(--K){q("requeing onLoad callback, DOM not available");setTimeout(s,250);return}G=true;j.responseText=p.documentElement?p.documentElement.innerHTML:null;j.responseXML=p.XMLDocument?p.XMLDocument:p;j.getResponseHeader=function(L){return{"content-type":e.dataType}[L]}; var v=/(json|script)/.test(e.dataType);if(v||e.textarea){var w=p.getElementsByTagName("textarea")[0];if(w)j.responseText=w.value;else if(v){var H=p.getElementsByTagName("pre")[0],I=p.getElementsByTagName("body")[0];if(H)j.responseText=H.textContent;else if(I)j.responseText=I.innerHTML}}else if(e.dataType=="xml"&&!j.responseXML&&j.responseText!=null)j.responseXML=C(j.responseText);J=b.httpData(j,e.dataType)}catch(D){q("error caught:",D);o=false;j.error=D;b.handleError(e,j,"error",D)}if(j.aborted){q("upload aborted"); o=false}if(o){e.success.call(e.context,J,"success",j);y&&b.event.trigger("ajaxSuccess",[j,e])}y&&b.event.trigger("ajaxComplete",[j,e]);y&&!--b.active&&b.event.trigger("ajaxStop");if(e.complete)e.complete.call(e.context,j,o?"success":"error");setTimeout(function(){r.removeData("form-plugin-onload");r.remove();j.responseXML=null},100)}}function C(o,m){if(window.ActiveXObject){m=new ActiveXObject("Microsoft.XMLDOM");m.async="false";m.loadXML(o)}else m=(new DOMParser).parseFromString(o,"text/xml");return m&& m.documentElement&&m.documentElement.tagName!="parsererror"?m:null}var l=i[0];if(b(":input[name=submit],:input[id=submit]",l).length)alert('Error: Form elements must not have name or id of "submit".');else{var e=b.extend(true,{},b.ajaxSettings,a);e.context=e.context||e;var u="jqFormIO"+(new Date).getTime(),E="_"+u;window[E]=function(){var o=r.data("form-plugin-onload");if(o){o();window[E]=undefined;try{delete window[E]}catch(m){}}};var r=b('<iframe id="'+u+'" name="'+u+'" src="'+e.iframeSrc+'" onload="window[\'_\'+this.id]()" />'), x=r[0];r.css({position:"absolute",top:"-1000px",left:"-1000px"});var j={aborted:0,responseText:null,responseXML:null,status:0,statusText:"n/a",getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){},abort:function(){this.aborted=1;r.attr("src",e.iframeSrc)}},y=e.global;y&&!b.active++&&b.event.trigger("ajaxStart");y&&b.event.trigger("ajaxSend",[j,e]);if(e.beforeSend&&e.beforeSend.call(e.context,j,e)===false)e.global&&b.active--;else if(!j.aborted){var G=false, F=0,z=l.clk;if(z){var A=z.name;if(A&&!z.disabled){e.extraData=e.extraData||{};e.extraData[A]=z.value;if(z.type=="image"){e.extraData[A+".x"]=l.clk_x;e.extraData[A+".y"]=l.clk_y}}}e.forceSync?t():setTimeout(t,10);var J,p,K=50}}}if(!this.length){q("ajaxSubmit: skipping submit process - no element selected");return this}if(typeof a=="function")a={success:a};var d=this.attr("action");if(d=typeof d==="string"?b.trim(d):"")d=(d.match(/^([^#]+)/)||[])[1];d=d||window.location.href||"";a=b.extend(true,{url:d, type:this.attr("method")||"GET",iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank"},a);d={};this.trigger("form-pre-serialize",[this,a,d]);if(d.veto){q("ajaxSubmit: submit vetoed via form-pre-serialize trigger");return this}if(a.beforeSerialize&&a.beforeSerialize(this,a)===false){q("ajaxSubmit: submit aborted via beforeSerialize callback");return this}var c,h,g=this.formToArray(a.semantic);if(a.data){a.extraData=a.data;for(c in a.data)if(a.data[c]instanceof Array)for(var k in a.data[c])g.push({name:c, value:a.data[c][k]});else{h=a.data[c];h=b.isFunction(h)?h():h;g.push({name:c,value:h})}}if(a.beforeSubmit&&a.beforeSubmit(g,this,a)===false){q("ajaxSubmit: submit aborted via beforeSubmit callback");return this}this.trigger("form-submit-validate",[g,this,a,d]);
3393if(d.veto){q("ajaxSubmit: submit vetoed via form-submit-validate trigger");return this}c=b.param(g);if(a.type.toUpperCase()=="GET"){a.url+=(a.url.indexOf("?")>=0?"&":"?")+c;a.data=null}else a.data=c;var i=this,n=[];a.resetForm&&n.push(function(){i.resetForm()}); a.clearForm&&n.push(function(){i.clearForm()});if(!a.dataType&&a.target){var B=a.success||function(){};n.push(function(t){var s=a.replaceTarget?"replaceWith":"html";b(a.target)[s](t).each(B,arguments)})}else a.success&&n.push(a.success);a.success=function(t,s,C){for(var l=a.context||a,e=0,u=n.length;e<u;e++)n[e].apply(l,[t,s,C||i,i])};c=b("input:file",this).length>0;k=i.attr("enctype")=="multipart/form-data"||i.attr("encoding")=="multipart/form-data";if(a.iframe!==false&&(c||a.iframe||k))a.closeKeepAlive? b.get(a.closeKeepAlive,f):f();else b.ajax(a);this.trigger("form-submit-notify",[this,a]);return this};b.fn.ajaxForm=function(a){if(this.length===0){var f={s:this.selector,c:this.context};if(!b.isReady&&f.s){q("DOM not ready, queuing ajaxForm");b(function(){b(f.s,f.c).ajaxForm(a)});return this}q("terminating; zero elements found by selector"+(b.isReady?"":" (DOM not ready)"));return this}return this.ajaxFormUnbind().bind("submit.form-plugin",function(d){if(!d.isDefaultPrevented()){d.preventDefault(); b(this).ajaxSubmit(a)}}).bind("click.form-plugin",function(d){var c=d.target,h=b(c);if(!h.is(":submit,input:image")){c=h.closest(":submit");if(c.length==0)return;c=c[0]}var g=this;g.clk=c;if(c.type=="image")if(d.offsetX!=undefined){g.clk_x=d.offsetX;g.clk_y=d.offsetY}else if(typeof b.fn.offset=="function"){h=h.offset();g.clk_x=d.pageX-h.left;g.clk_y=d.pageY-h.top}else{g.clk_x=d.pageX-c.offsetLeft;g.clk_y=d.pageY-c.offsetTop}setTimeout(function(){g.clk=g.clk_x=g.clk_y=null},100)})};b.fn.ajaxFormUnbind= function(){return this.unbind("submit.form-plugin click.form-plugin")};b.fn.formToArray=function(a){var f=[];if(this.length===0)return f;var d=this[0],c=a?d.getElementsByTagName("*"):d.elements;if(!c)return f;var h,g,k,i,n,B;h=0;for(n=c.length;h<n;h++){g=c[h];if(k=g.name)if(a&&d.clk&&g.type=="image"){if(!g.disabled&&d.clk==g){f.push({name:k,value:b(g).val()});f.push({name:k+".x",value:d.clk_x},{name:k+".y",value:d.clk_y})}}else if((i=b.fieldValue(g,true))&&i.constructor==Array){g=0;for(B=i.length;g< B;g++)f.push({name:k,value:i[g]})}else i!==null&&typeof i!="undefined"&&f.push({name:k,value:i})}if(!a&&d.clk){a=b(d.clk);c=a[0];if((k=c.name)&&!c.disabled&&c.type=="image"){f.push({name:k,value:a.val()});f.push({name:k+".x",value:d.clk_x},{name:k+".y",value:d.clk_y})}}return f};b.fn.formSerialize=function(a){return b.param(this.formToArray(a))};b.fn.fieldSerialize=function(a){var f=[];this.each(function(){var d=this.name;if(d){var c=b.fieldValue(this,a);if(c&&c.constructor==Array)for(var h=0,g=c.length;h< g;h++)f.push({name:d,value:c[h]});else c!==null&&typeof c!="undefined"&&f.push({name:this.name,value:c})}});return b.param(f)};b.fn.fieldValue=function(a){for(var f=[],d=0,c=this.length;d<c;d++){var h=b.fieldValue(this[d],a);h===null||typeof h=="undefined"||h.constructor==Array&&!h.length||(h.constructor==Array?b.merge(f,h):f.push(h))}return f};b.fieldValue=function(a,f){var d=a.name,c=a.type,h=a.tagName.toLowerCase();if(f===undefined)f=true;if(f&&(!d||a.disabled||c=="reset"||c=="button"||(c=="checkbox"|| c=="radio")&&!a.checked||(c=="submit"||c=="image")&&a.form&&a.form.clk!=a||h=="select"&&a.selectedIndex==-1))return null;if(h=="select"){var g=a.selectedIndex;if(g<0)return null;d=[];h=a.options;var k=(c=c=="select-one")?g+1:h.length;for(g=c?g:0;g<k;g++){var i=h[g];if(i.selected){var n=i.value;n||(n=i.attributes&&i.attributes.value&&!i.attributes.value.specified?i.text:i.value);if(c)return n;d.push(n)}}return d}return b(a).val()};b.fn.clearForm=function(){return this.each(function(){b("input,select,textarea", this).clearFields()})};b.fn.clearFields=b.fn.clearInputs=function(){return this.each(function(){var a=this.type,f=this.tagName.toLowerCase();if(a=="text"||a=="password"||f=="textarea")this.value="";else if(a=="checkbox"||a=="radio")this.checked=false;else if(f=="select")this.selectedIndex=-1})};b.fn.resetForm=function(){return this.each(function(){if(typeof this.reset=="function"||typeof this.reset=="object"&&!this.reset.nodeType)this.reset()})};
3393b.fn.enable=function(a){if(a===undefined)a=true;return this.each(function(){this.disabled= !a})};b.fn.selected=function(a){if(a===undefined)a=true;return this.each(function(){var f=this.type;if(f=="checkbox"||f=="radio")this.checked=a;else if(this.tagName.toLowerCase()=="option"){f=b(this).parent("select");a&&f[0]&&f[0].type=="select-one"&&f.find("option").selected(false);this.selected=a}})}})(jQuery);;
3394(function ($) {
3395
3396/**
3397 * Provides Ajax page updating via jQuery $.ajax (Asynchronous JavaScript and XML).
3398 *
3399 * Ajax is a method of making a request via JavaScript while viewing an HTML
3400 * page. The request returns an array of commands encoded in JSON, which is
3401 * then executed to make any changes that are necessary to the page.
3402 *
3403 * Drupal uses this file to enhance form elements with #ajax['path'] and
3404 * #ajax['wrapper'] properties. If set, this file will automatically be included
3405 * to provide Ajax capabilities.
3406 */
3407
3408Drupal.ajax = Drupal.ajax || {};
3409
3410Drupal.settings.urlIsAjaxTrusted = Drupal.settings.urlIsAjaxTrusted || {};
3411
3412/**
3413 * Attaches the Ajax behavior to each Ajax form element.
3414 */
3415Drupal.behaviors.AJAX = {
3416  attach: function (context, settings) {
3417    // Load all Ajax behaviors specified in the settings.
3418    for (var base in settings.ajax) {
3419      if (!$('#' + base + '.ajax-processed').length) {
3420        var element_settings = settings.ajax[base];
3421
3422        if (typeof element_settings.selector == 'undefined') {
3423          element_settings.selector = '#' + base;
3424        }
3425        $(element_settings.selector).each(function () {
3426          element_settings.element = this;
3427          Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
3428        });
3429
3430        $('#' + base).addClass('ajax-processed');
3431      }
3432    }
3433
3434    // Bind Ajax behaviors to all items showing the class.
3435    $('.use-ajax:not(.ajax-processed)').addClass('ajax-processed').each(function () {
3436      var element_settings = {};
3437      // Clicked links look better with the throbber than the progress bar.
3438      element_settings.progress = { 'type': 'throbber' };
3439
3440      // For anchor tags, these will go to the target of the anchor rather
3441      // than the usual location.
3442      if ($(this).attr('href')) {
3443        element_settings.url = $(this).attr('href');
3444        element_settings.event = 'click';
3445      }
3446      var base = $(this).attr('id');
3447      Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
3448    });
3449
3450    // This class means to submit the form to the action using Ajax.
3451    $('.use-ajax-submit:not(.ajax-processed)').addClass('ajax-processed').each(function () {
3452      var element_settings = {};
3453
3454      // Ajax submits specified in this manner automatically submit to the
3455      // normal form action.
3456      element_settings.url = $(this.form).attr('action');
3457      // Form submit button clicks need to tell the form what was clicked so
3458      // it gets passed in the POST request.
3459      element_settings.setClick = true;
3460      // Form buttons use the 'click' event rather than mousedown.
3461      element_settings.event = 'click';
3462      // Clicked form buttons look better with the throbber than the progress bar.
3463      element_settings.progress = { 'type': 'throbber' };
3464
3465      var base = $(this).attr('id');
3466      Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
3467    });
3468  }
3469};
3470
3471/**
3472 * Ajax object.
3473 *
3474 * All Ajax objects on a page are accessible through the global Drupal.ajax
3475 * object and are keyed by the submit button's ID. You can access them from
3476 * your module's JavaScript file to override properties or functions.
3477 *
3478 * For example, if your Ajax enabled button has the ID 'edit-submit', you can
3479 * redefine the function that is called to insert the new content like this
3480 * (inside a Drupal.behaviors attach block):
3481 * @code
3482 *    Drupal.behaviors.myCustomAJAXStuff = {
3483 *      attach: function (context, settings) {
3484 *        Drupal.ajax['edit-submit'].commands.insert = function (ajax, response, status) {
3485 *          new_content = $(response.data);
3486 *          $('#my-wrapper').append(new_content);
3487 *          alert('New content was appended to #my-wrapper');
3488 *        }
3489 *      }
3490 *    };
3491 * @endcode
3492 */
3493Drupal.ajax = function (base, element, element_settings) {
3494  var defaults = {
3495    url: 'system/ajax',
3496    event: 'mousedown',
3497    keypress: true,
3498    selector: '#' + base,
3499    effect: 'none',
3500    speed: 'none',
3501    method: 'replaceWith',
3502    progress: {
3503      type: 'throbber',
3504      message: Drupal.t('Please wait...')
3505    },
3506    submit: {
3507      'js': true
3508    }
3509  };
3510
3511  $.extend(this, defaults, element_settings);
3512
3513  this.element = element;
3514  this.element_settings = element_settings;
3515
3516  // Replacing 'nojs' with 'ajax' in the URL allows for an easy method to let
3517  // the server detect when it needs to degrade gracefully.
3518  // There are five scenarios to check for:
3519  // 1. /nojs/
3520  // 2. /nojs$ - The end of a URL string.
3521  // 3. /nojs? - Followed by a query (with clean URLs enabled).
3522  //      E.g.: path/nojs?destination=foobar
3523  // 4. /nojs& - Followed by a query (without clean URLs enabled).
3524  //      E.g.: ?q=path/nojs&destination=foobar
3525  // 5. /nojs# - Followed by a fragment.
3526  //      E.g.: path/nojs#myfragment
3527  this.url = element_settings.url.replace(/\/nojs(\/|$|\?|&|#)/g, '/ajax$1');
3528  // If the 'nojs' version of the URL is trusted, also trust the 'ajax' version.
3529  if (Drupal.settings.urlIsAjaxTrusted[element_settings.url]) {
3530    Drupal.settings.urlIsAjaxTrusted[this.url] = true;
3531  }
3532
3533  this.wrapper = '#' + element_settings.wrapper;
3534
3535  // If there isn't a form, jQuery.ajax() will be used instead, allowing us to
3536  // bind Ajax to links as well.
3537  if (this.element.form) {
3538    this.form = $(this.element.form);
3539  }
3540
3541  // Set the options for the ajaxSubmit function.
3542  // The 'this' variable will not persist inside of the options object.
3543  var ajax = this;
3544  ajax.options = {
3545    url: Drupal.sanitizeAjaxUrl(ajax.url),
3546    data: ajax.submit,
3547    beforeSerialize: function (element_settings, options) {
3548      return ajax.beforeSerialize(element_settings, options);
3549    },
3550    beforeSubmit: function (form_values, element_settings, options) {
3551      ajax.ajaxing = true;
3552      return ajax.beforeSubmit(form_values, element_settings, options);
3553    },
3554    beforeSend: function (xmlhttprequest, options) {
3555      ajax.ajaxing = true;
3556      return ajax.beforeSend(xmlhttprequest, options);
3557    },
3558    success: function (response, status, xmlhttprequest) {
3559      // Sanity check for browser support (object expected).
3560      // When using iFrame uploads, responses must be returned as a string.
3561      if (typeof response == 'string') {
3562        response = $.parseJSON(response);
3563      }
3564
3565      // Prior to invoking the response's commands, verify that they can be
3566      // trusted by checking for a response header. See
3567      // ajax_set_verification_header() for details.
3568      // - Empty responses are harmless so can bypass verification. This avoids
3569      //   an alert message for server-generated no-op responses that skip Ajax
3570      //   rendering.
3571      // - Ajax objects with trusted URLs (e.g., ones defined server-side via
3572      //   #ajax) can bypass header verification. This is especially useful for
3573      //   Ajax with multipart forms. Because IFRAME transport is used, the
3574      //   response headers cannot be accessed for verification.
3575      if (response !== null && !Drupal.settings.urlIsAjaxTrusted[ajax.url]) {
3576        if (xmlhttprequest.getResponseHeader('X-Drupal-Ajax-Token') !== '1') {
3577          var customMessage = Drupal.t("The response failed verification so will not be processed.");
3578          return ajax.error(xmlhttprequest, ajax.url, customMessage);
3579        }
3580      }
3581
3582      return ajax.success(response, status);
3583    },
3584    complete: function (xmlhttprequest, status) {
3585      ajax.ajaxing = false;
3586      if (status == 'error' || status == 'parsererror') {
3587        return ajax.error(xmlhttprequest, ajax.url);
3588      }
3589    },
3590    dataType: 'json',
3591    jsonp: false,
3592    type: 'POST'
3593  };
3594
3595  // For multipart forms (e.g., file uploads), jQuery Form targets the form
3596  // submission to an iframe instead of using an XHR object. The initial "src"
3597  // of the iframe, prior to the form submission, is set to options.iframeSrc.
3598  // "about:blank" is the semantically correct, standards-compliant, way to
3599  // initialize a blank iframe; however, some old IE versions (possibly only 6)
3600  // incorrectly report a mixed content warning when iframes with an
3601  // "about:blank" src are added to a parent document with an https:// origin.
3602  // jQuery Form works around this by defaulting to "javascript:false" instead,
3603  // but that breaks on Chrome 83, so here we force the semantically correct
3604  // behavior for all browsers except old IE.
3605  // @see https://www.drupal.org/project/drupal/issues/3143016
3606  // @see https://github.com/jquery-form/form/blob/df9cb101b9c9c085c8d75ad980c7ff1cf62063a1/jquery.form.js#L68
3607  // @see https://bugs.chromium.org/p/chromium/issues/detail?id=1084874
3608  // @see https://html.spec.whatwg.org/multipage/browsers.html#creating-browsing-contexts
3609  // @see https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
3610  if (navigator.userAgent.indexOf("MSIE") === -1) {
3611    ajax.options.iframeSrc = 'about:blank';
3612  }
3613
3614  // Bind the ajaxSubmit function to the element event.
3615  $(ajax.element).bind(element_settings.event, function (event) {
3616    if (!Drupal.settings.urlIsAjaxTrusted[ajax.url] && !Drupal.urlIsLocal(ajax.url)) {
3617      throw new Error(Drupal.t('The callback URL is not local and not trusted: !url', {'!url': ajax.url}));
3618    }
3619    return ajax.eventResponse(this, event);
3620  });
3621
3622  // If necessary, enable keyboard submission so that Ajax behaviors
3623  // can be triggered through keyboard input as well as e.g. a mousedown
3624  // action.
3625  if (element_settings.keypress) {
3626    $(ajax.element).keypress(function (event) {
3627      return ajax.keypressResponse(this, event);
3628    });
3629  }
3630
3631  // If necessary, prevent the browser default action of an additional event.
3632  // For example, prevent the browser default action of a click, even if the
3633  // AJAX behavior binds to mousedown.
3634  if (element_settings.prevent) {
3635    $(ajax.element).bind(element_settings.prevent, false);
3636  }
3637};
3638
3639/**
3640 * Handle a key press.
3641 *
3642 * The Ajax object will, if instructed, bind to a key press response. This
3643 * will test to see if the key press is valid to trigger this event and
3644 * if it is, trigger it for us and prevent other keypresses from triggering.
3645 * In this case we're handling RETURN and SPACEBAR keypresses (event codes 13
3646 * and 32. RETURN is often used to submit a form when in a textfield, and 
3647 * SPACE is often used to activate an element without submitting. 
3648 */
3649Drupal.ajax.prototype.keypressResponse = function (element, event) {
3650  // Create a synonym for this to reduce code confusion.
3651  var ajax = this;
3652
3653  // Detect enter key and space bar and allow the standard response for them,
3654  // except for form elements of type 'text' and 'textarea', where the 
3655  // spacebar activation causes inappropriate activation if #ajax['keypress'] is 
3656  // TRUE. On a text-type widget a space should always be a space.
3657  if (event.which == 13 || (event.which == 32 && element.type != 'text' && element.type != 'textarea')) {
3658    $(ajax.element_settings.element).trigger(ajax.element_settings.event);
3659    return false;
3660  }
3661};
3662
3663/**
3664 * Handle an event that triggers an Ajax response.
3665 *
3666 * When an event that triggers an Ajax response happens, this method will
3667 * perform the actual Ajax call. It is bound to the event using
3668 * bind() in the constructor, and it uses the options specified on the
3669 * ajax object.
3670 */
3671Drupal.ajax.prototype.eventResponse = function (element, event) {
3672  // Create a synonym for this to reduce code confusion.
3673  var ajax = this;
3674
3675  // Do not perform another ajax command if one is already in progress.
3676  if (ajax.ajaxing) {
3677    return false;
3678  }
3679
3680  try {
3681    if (ajax.form) {
3682      // If setClick is set, we must set this to ensure that the button's
3683      // value is passed.
3684      if (ajax.setClick) {
3685        // Mark the clicked button. 'form.clk' is a special variable for
3686        // ajaxSubmit that tells the system which element got clicked to
3687        // trigger the submit. Without it there would be no 'op' or
3688        // equivalent.
3689        element.form.clk = element;
3690      }
3691
3692      ajax.form.ajaxSubmit(ajax.options);
3693    }
3694    else {
3695      ajax.beforeSerialize(ajax.element, ajax.options);
3696      $.ajax(ajax.options);
3697    }
3698  }
3699  catch (e) {
3700    // Unset the ajax.ajaxing flag here because it won't be unset during
3701    // the complete response.
3702    ajax.ajaxing = false;
3703    alert("An error occurred while attempting to process " + ajax.options.url + ": " + e.message);
3704  }
3705
3706  // For radio/checkbox, allow the default event. On IE, this means letting
3707  // it actually check the box.
3708  if (typeof element.type != 'undefined' && (element.type == 'checkbox' || element.type == 'radio')) {
3709    return true;
3710  }
3711  else {
3712    return false;
3713  }
3714
3715};
3716
3717/**
3718 * Handler for the form serialization.
3719 *
3720 * Runs before the beforeSend() handler (see below), and unlike that one, runs
3721 * before field data is collected.
3722 */
3723Drupal.ajax.prototype.beforeSerialize = function (element, options) {
3724  // Allow detaching behaviors to update field values before collecting them.
3725  // This is only needed when field values are added to the POST data, so only
3726  // when there is a form such that this.form.ajaxSubmit() is used instead of
3727  // $.ajax(). When there is no form and $.ajax() is used, beforeSerialize()
3728  // isn't called, but don't rely on that: explicitly check this.form.
3729  if (this.form) {
3730    var settings = this.settings || Drupal.settings;
3731    Drupal.detachBehaviors(this.form, settings, 'serialize');
3732  }
3733
3734  // Prevent duplicate HTML ids in the returned markup.
3735  // @see drupal_html_id()
3736  options.data['ajax_html_ids[]'] = [];
3737  $('[id]').each(function () {
3738    options.data['ajax_html_ids[]'].push(this.id);
3739  });
3740
3741  // Allow Drupal to return new JavaScript and CSS files to load without
3742  // returning the ones already loaded.
3743  // @see ajax_base_page_theme()
3744  // @see drupal_get_css()
3745  // @see drupal_get_js()
3746  options.data['ajax_page_state[theme]'] = Drupal.settings.ajaxPageState.theme;
3747  options.data['ajax_page_state[theme_token]'] = Drupal.settings.ajaxPageState.theme_token;
3748  for (var key in Drupal.settings.ajaxPageState.css) {
3749    options.data['ajax_page_state[css][' + key + ']'] = 1;
3750  }
3751  for (var key in Drupal.settings.ajaxPageState.js) {
3752    options.data['ajax_page_state[js][' + key + ']'] = 1;
3753  }
3754};
3755
3756/**
3757 * Modify form values prior to form submission.
3758 */
3759Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) {
3760  // This function is left empty to make it simple to override for modules
3761  // that wish to add functionality here.
3762};
3763
3764/**
3765 * Prepare the Ajax request before it is sent.
3766 */
3767Drupal.ajax.prototype.beforeSend = function (xmlhttprequest, options) {
3768  // For forms without file inputs, the jQuery Form plugin serializes the form
3769  // values, and then calls jQuery's $.ajax() function, which invokes this
3770  // handler. In this circumstance, options.extraData is never used. For forms
3771  // with file inputs, the jQuery Form plugin uses the browser's normal form
3772  // submission mechanism, but captures the response in a hidden IFRAME. In this
3773  // circumstance, it calls this handler first, and then appends hidden fields
3774  // to the form to submit the values in options.extraData. There is no simple
3775  // way to know which submission mechanism will be used, so we add to extraData
3776  // regardless, and allow it to be ignored in the former case.
3777  if (this.form) {
3778    options.extraData = options.extraData || {};
3779
3780    // Let the server know when the IFRAME submission mechanism is used. The
3781    // server can use this information to wrap the JSON response in a TEXTAREA,
3782    // as per http://jquery.malsup.com/form/#file-upload.
3783    options.extraData.ajax_iframe_upload = '1';
3784
3785    // The triggering element is about to be disabled (see below), but if it
3786    // contains a value (e.g., a checkbox, textfield, select, etc.), ensure that
3787    // value is included in the submission. As per above, submissions that use
3788    // $.ajax() are already serialized prior to the element being disabled, so
3789    // this is only needed for IFRAME submissions.
3790    var v = $.fieldValue(this.element);
3791    if (v !== null) {
3792      options.extraData[this.element.name] = Drupal.checkPlain(v);
3793    }
3794  }
3795
3796  // Disable the element that received the change to prevent user interface
3797  // interaction while the Ajax request is in progress. ajax.ajaxing prevents
3798  // the element from triggering a new request, but does not prevent the user
3799  // from changing its value.
3800  $(this.element).addClass('progress-disabled').attr('disabled', true);
3801
3802  // Insert progressbar or throbber.
3803  if (this.progress.type == 'bar') {
3804    var progressBar = new Drupal.progressBar('ajax-progress-' + this.element.i
3804d, $.noop, this.progress.method, $.noop);
3805    if (this.progress.message) {
3806      progressBar.setProgress(-1, this.progress.message);
3807    }
3808    if (this.progress.url) {
3809      progressBar.startMonitoring(this.progress.url, this.progress.interval || 1500);
3810    }
3811    this.progress.element = $(progressBar.element).addClass('ajax-progress ajax-progress-bar');
3812    this.progress.object = progressBar;
3813    $(this.element).after(this.progress.element);
3814  }
3815  else if (this.progress.type == 'throbber') {
3816    this.progress.element = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber">&nbsp;</div></div>');
3817    if (this.progress.message) {
3818      $('.throbber', this.progress.element).after('<div class="message">' + this.progress.message + '</div>');
3819    }
3820    $(this.element).after(this.progress.element);
3821  }
3822};
3823
3824/**
3825 * Handler for the form redirection completion.
3826 */
3827Drupal.ajax.prototype.success = function (response, status) {
3828  // Remove the progress element.
3829  if (this.progress.element) {
3830    $(this.progress.element).remove();
3831  }
3832  if (this.progress.object) {
3833    this.progress.object.stopMonitoring();
3834  }
3835  $(this.element).removeClass('progress-disabled').removeAttr('disabled');
3836
3837  Drupal.freezeHeight();
3838
3839  for (var i in response) {
3840    if (response.hasOwnProperty(i) && response[i]['command'] && this.commands[response[i]['command']]) {
3841      this.commands[response[i]['command']](this, response[i], status);
3842    }
3843  }
3844
3845  // Reattach behaviors, if they were detached in beforeSerialize(). The
3846  // attachBehaviors() called on the new content from processing the response
3847  // commands is not sufficient, because behaviors from the entire form need
3848  // to be reattached.
3849  if (this.form) {
3850    var settings = this.settings || Drupal.settings;
3851    Drupal.attachBehaviors(this.form, settings);
3852  }
3853
3854  Drupal.unfreezeHeight();
3855
3856  // Remove any response-specific settings so they don't get used on the next
3857  // call by mistake.
3858  this.settings = null;
3859};
3860
3861/**
3862 * Build an effect object which tells us how to apply the effect when adding new HTML.
3863 */
3864Drupal.ajax.prototype.getEffect = function (response) {
3865  var type = response.effect || this.effect;
3866  var speed = response.speed || this.speed;
3867
3868  var effect = {};
3869  if (type == 'none') {
3870    effect.showEffect = 'show';
3871    effect.hideEffect = 'hide';
3872    effect.showSpeed = '';
3873  }
3874  else if (type == 'fade') {
3875    effect.showEffect = 'fadeIn';
3876    effect.hideEffect = 'fadeOut';
3877    effect.showSpeed = speed;
3878  }
3879  else {
3880    effect.showEffect = type + 'Toggle';
3881    effect.hideEffect = type + 'Toggle';
3882    effect.showSpeed = speed;
3883  }
3884
3885  return effect;
3886};
3887
3888/**
3889 * Handler for the form redirection error.
3890 */
3891Drupal.ajax.prototype.error = function (xmlhttprequest, uri, customMessage) {
3892  Drupal.displayAjaxError(Drupal.ajaxError(xmlhttprequest, uri, customMessage));
3893  // Remove the progress element.
3894  if (this.progress.element) {
3895    $(this.progress.element).remove();
3896  }
3897  if (this.progress.object) {
3898    this.progress.object.stopMonitoring();
3899  }
3900  // Undo hide.
3901  $(this.wrapper).show();
3902  // Re-enable the element.
3903  $(this.element).removeClass('progress-disabled').removeAttr('disabled');
3904  // Reattach behaviors, if they were detached in beforeSerialize().
3905  if (this.form) {
3906    var settings = this.settings || Drupal.settings;
3907    Drupal.attachBehaviors(this.form, settings);
3908  }
3909};
3910
3911/**
3912 * Provide a series of commands that the server can request the client perform.
3913 */
3914Drupal.ajax.prototype.commands = {
3915  /**
3916   * Command to insert new content into the DOM.
3917   */
3918  insert: function (ajax, response, status) {
3919    // Get information from the response. If it is not there, default to
3920    // our presets.
3921    var wrapper = response.selector ? $(response.selector) : $(ajax.wrapper);
3922    var method = response.method || ajax.method;
3923    var effect = ajax.getEffect(response);
3924
3925    // We don't know what response.data contains: it might be a string of text
3926    // without HTML, so don't rely on jQuery correctly iterpreting
3927    // $(response.data) as new HTML rather than a CSS selector. Also, if
3928    // response.data contains top-level text nodes, they get lost with either
3929    // $(response.data) or $('<div></div>').replaceWith(response.data).
3930    var new_content_wrapped = $('<div></div>').html(response.data);
3931    var new_content = new_content_wrapped.contents();
3932
3933    // For legacy reasons, the effects processing code assumes that new_content
3934    // consists of a single top-level element. Also, it has not been
3935    // sufficiently tested whether attachBehaviors() can be successfully called
3936    // with a context object that includes top-level text nodes. However, to
3937    // give developers full control of the HTML appearing in the page, and to
3938    // enable Ajax content to be inserted in places where DIV elements are not
3939    // allowed (e.g., within TABLE, TR, and SPAN parents), we check if the new
3940    // content satisfies the requirement of a single top-level element, and
3941    // only use the container DIV created above when it doesn't. For more
3942    // information, please see http://drupal.org/node/736066.
3943    if (new_content.length != 1 || new_content.get(0).nodeType != 1) {
3944      new_content = new_content_wrapped;
3945    }
3946
3947    // If removing content from the wrapper, detach behaviors first.
3948    switch (method) {
3949      case 'html':
3950      case 'replaceWith':
3951      case 'replaceAll':
3952      case 'empty':
3953      case 'remove':
3954        var settings = response.settings || ajax.settings || Drupal.settings;
3955        Drupal.detachBehaviors(wrapper, settings);
3956    }
3957
3958    // Add the new content to the page.
3959    wrapper[method](new_content);
3960
3961    // Immediately hide the new content if we're using any effects.
3962    if (effect.showEffect != 'show') {
3963      new_content.hide();
3964    }
3965
3966    // Determine which effect to use and what content will receive the
3967    // effect, then show the new content.
3968    if ($('.ajax-new-content', new_content).length > 0) {
3969      $('.ajax-new-content', new_content).hide();
3970      new_content.show();
3971      $('.ajax-new-content', new_content)[effect.showEffect](effect.showSpeed);
3972    }
3973    else if (effect.showEffect != 'show') {
3974      new_content[effect.showEffect](effect.showSpeed);
3975    }
3976
3977    // Attach all JavaScript behaviors to the new content, if it was successfully
3978    // added to the page, this if statement allows #ajax['wrapper'] to be
3979    // optional.
3980    if (new_content.parents('html').length > 0) {
3981      // Apply any settings from the returned JSON if available.
3982      var settings = response.settings || ajax.settings || Drupal.settings;
3983      Drupal.attachBehaviors(new_content, settings);
3984    }
3985  },
3986
3987  /**
3988   * Command to remove a chunk from the page.
3989   */
3990  remove: function (ajax, response, status) {
3991    var settings = response.settings || ajax.settings || Drupal.settings;
3992    Drupal.detachBehaviors($(response.selector), settings);
3993    $(response.selector).remove();
3994  },
3995
3996  /**
3997   * Command to mark a chunk changed.
3998   */
3999  changed: function (ajax, response, status) {
4000    if (!$(response.selector).hasClass('ajax-changed')) {
4001      $(response.selector).addClass('ajax-changed');
4002      if (response.asterisk) {
4003        $(response.selector).find(response.asterisk).append(' <span class="ajax-changed">*</span> ');
4004      }
4005    }
4006  },
4007
4008  /**
4009   * Command to provide an alert.
4010   */
4011  alert: function (ajax, response, status) {
4012    alert(response.text, response.title);
4013  },
4014
4015  /**
4016   * Command to provide the jQuery css() function.
4017   */
4018  css: function (ajax, response, status) {
4019    $(response.selector).css(response.argument);
4020  },
4021
4022  /**
4023   * Command to set the settings that will be used for other commands in this response.
4024   */
4025  settings: function (ajax, response, status) {
4026    if (response.merge) {
4027      $.extend(true, Drupal.settings, response.settings);
4028    }
4029    else {
4030      ajax.settings = response.settings;
4031    }
4032  },
4033
4034  /**
4035   * Command to attach data using jQuery's data API.
4036   */
4037  data: function (ajax, response, status) {
4038    $(response.selector).data(response.name, response.value);
4039  },
4040
4041  /**
4042   * Command to apply a jQuery method.
4043   */
4044  invoke: function (ajax, response, status) {
4045    var $element = $(response.selector);
4046    $element[response.method].apply($element, response.arguments);
4047  },
4048
4049  /**
4050   * Command to restripe a table.
4051   */
4052  restripe: function (ajax, response, status) {
4053    // :even and :odd are reversed because jQuery counts from 0 and
4054    // we count from 1, so we're out of sync.
4055    // Match immediate children of the parent element to allow nesting.
4056    $('> tbody > tr:visible, > tr:visible', $(response.selector))
4057      .removeClass('odd even')
4058      .filter(':even').addClass('odd').end()
4059      .filter(':odd').addClass('even');
4060  },
4061
4062  /**
4063   * Command to add css.
4064   *
4065   * Uses the proprietary addImport method if available as browsers which
4066   * support that method ignore @import statements in dynamically added
4067   * stylesheets.
4068   */
4069  add_css: function (ajax, response, status) {
4070    // Add the styles in the normal way.
4071    $('head').prepend(response.data);
4072    // Add imports in the styles using the addImport method if available.
4073    var match, importMatch = /^@import url\("(.*)"\);$/igm;
4074    if (document.styleSheets[0].addImport && importMatch.test(response.data)) {
4075      importMatch.lastIndex = 0;
4076      while (match = importMatch.exec(response.data)) {
4077        document.styleSheets[0].addImport(match[1]);
4078      }
4079    }
4080  },
4081
4082  /**
4083   * Command to update a form's build ID.
4084   */
4085  updateBuildId: function(ajax, response, status) {
4086    $('input[name="form_build_id"][value="' + response['old'] + '"]').val(response['new']);
4087  }
4088};
4089
4090})(jQuery);
4091;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.