PageSourceSearch

https://www.pordata.pt/ExternalLibs/jquery/jquery.qtip-1.0.0-rc3.js

js pordata.pt collected 2026-09-24 08:57:09 UTC 86,763 bytes, 2,200 lines download raw bytes

1/*!
2 * jquery.qtip. The jQuery tooltip plugin
3 *
4 * Copyright (c) 2009 Craig Thompson
5 * http://craigsworks.com
6 *
7 * Licensed under MIT
8 * http://www.opensource.org/licenses/mit-license.php
9 *
10 * Launch  : February 2009
11 * Version : 1.0.0-rc3
12 * Released: Tuesday 12th May, 2009 - 00:00
13 * Debug: jquery.qtip.debug.js
14 */
15(function($)
16{
17   // Implementation
18   var BrowserDetect = 
19   {
20	init: function () 
21	{
22		this.browser = this.searchString(this.dataBrowser) || "Other";
23		this.version = this.searchVersion(navigator.userAgent) ||       this.searchVersion(navigator.appVersion) || "Unknown";
24	},
25
26	searchString: function (data) 
27	{
28		for (var i=0 ; i < data.length ; i++)   
29		{
30			var dataString = data[i].string;
31			this.versionSearchString = data[i].subString;
32
33			if (dataString.indexOf(data[i].subString) != -1)
34			{
35				return data[i].identity;
36			}
37		}
38	},
39
40	searchVersion: function (dataString) 
41	{
42		var index = dataString.indexOf(this.versionSearchString);
43		if (index == -1) return;
44		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
45	},
46
47	dataBrowser: 
48	[
49		{ string: navigator.userAgent, subString: "Chrome",  identity: "Chrome" },
50		{ string: navigator.userAgent, subString: "MSIE",    identity: "Explorer" },
51		{ string: navigator.userAgent, subString: "Firefox", identity: "Firefox" },
52		{ string: navigator.userAgent, subString: "Safari",  identity: "Safari" },
53		{ string: navigator.userAgent, subString: "Opera",   identity: "Opera" },
54	]
55
56   };
57   BrowserDetect.init();
58   
59   $.fn.qtip = function(options, blanket)
60   {
61      var i, id, interfaces, opts, obj, command, config, api;	  
62
63      // Return API / Interfaces if requested
64      if(typeof options == 'string')
65      {
66         // Make sure API data exists if requested
67         if(typeof $(this).data('qtip') !== 'object')
68            $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.NO_TOOLTIP_PRESENT, false);
69
70         // Return requested object
71		 try {
72			if(options == 'api')
73				return $(this).data('qtip').interfaces[ $(this).data('qtip').current ];
74			else if(options == 'interfaces')
75				return $(this).data('qtip').interfaces;
76		}
77		catch(err) {
78			return null;
79		}
80      }
81
82      // Validate provided options
83      else
84      {
85         // Set null options object if no options are provided
86         if(!options) options = {};
87
88         // Sanitize option data
89         if(typeof options.content !== 'object' || (options.content.jquery && options.content.length > 0)) options.content = { text: options.content };
90         if(typeof options.content.title !== 'object') options.content.title = { text: options.content.title };
91         if(typeof options.position !== 'object') options.position = { corner: options.position };
92         if(typeof options.position.corner !== 'object') options.position.corner = { target: options.position.corner, tooltip: options.position.corner };
93         if(typeof options.show !== 'object') options.show = { when: options.show };
94         if(typeof options.show.when !== 'object') options.show.when = { event: options.show.when };
95         if(typeof options.show.effect !== 'object') options.show.effect = { type: options.show.effect };
96         if(typeof options.hide !== 'object') options.hide = { when: options.hide };
97         if(typeof options.hide.when !== 'object') options.hide.when = { event: options.hide.when };
98         if(typeof options.hide.effect !== 'object') options.hide.effect = { type: options.hide.effect };
99         if(typeof options.style !== 'object') options.style = { name: options.style };
100         options.style = sanitizeStyle(options.style);
101
102         // Build main options object
103         opts = $.extend(true, {}, $.fn.qtip.defaults, options);
104
105         // Inherit all style properties into one syle object and include original options
106         opts.style = buildStyle.call({ options: opts }, opts.style);
107         opts.user = $.extend(true, {}, options);
108      };
109
110      // Iterate each matched element
111      return $(this).each(function() // Return original elements as per jQuery guidelines
112      {
113         // Check for API commands
114         if(typeof options == 'string')
115         {
116            command = options.toLowerCase();
117            interfaces = $(this).qtip('interfaces');
118
119            // Make sure API data exists$('.qtip').qtip('destroy')
120            if(typeof interfaces == 'object')
121            {
122               // Check if API call is a BLANKET DESTROY command
123               if(blanket === true && command == 'destroy')
124                  while(interfaces.length > 0) interfaces[interfaces.length-1].destroy();
125
126               // API call is not a BLANKET DESTROY command
127               else
128               {
129                  // Check if supplied command effects this tooltip only (NOT BLANKET)
130                  if(blanket !== true) interfaces = [ $(this).qtip('api') ];
131
132                  // Execute command on chosen qTips
133                  for(i = 0; i < interfaces.length; i++)
134                  {
135                     // Destroy command doesn't require tooltip to be rendered
136                     if(command == 'destroy') interfaces[i].destroy();
137
138                     // Only call API if tooltip is rendered and it wasn't a destroy call
139                     else if(interfaces[i].status.rendered === true)
140                     {
141                        if(command == 'show') interfaces[i].show();
142                        else if(command == 'hide') interfaces[i].hide();
143                        else if(command == 'focus') interfaces[i].focus();
144                        else if(command == 'disable') interfaces[i].disable(true);
145                        else if(command == 'enable') interfaces[i].disable(false);
146                     };
147                  };
148               };
149            };
150         }
151
152         // No API commands, continue with qTip creation
153         else
154         {
155            // Create unique configuration object
156            config = $.extend(true, {}, opts);
157            config.hide.effect.length = opts.hide.effect.length;
158            config.show.effect.length = opts.show.effect.length;
159
160            // Sanitize target options
161            if(config.position.container === false) config.position.container = $(document.body);
162            if(config.position.target === false) config.position.target = $(this);
163            if(config.show.when.target === false) config.show.when.target = $(this);
164            if(config.hide.when.target === false) config.hide.when.target = $(this);
165
166            // Determine tooltip ID (Reuse array slots if possible)
167            id = $.fn.qtip.interfaces.length;
168            for(i = 0; i < id; i++)
169            {
170               if(typeof $.fn.qtip.interfaces[i] == 'undefined'){ id = i; break; };
171            };
172
173            // Instantiate the tooltip
174            obj = new qTip($(this), config, id);
175
176            // Add API references
177            $.fn.qtip.interfaces[id] = obj;
178
179            // Check if element already has qTip data assigned
180            //OLD: if(typeof $(this).data('qtip') == 'object')
181			if(typeof $(this).data('qtip') === 'object' && $(this).data('qtip'))
182            {
183               // Set new current interface id
184               if(typeof $(this).attr('qtip') === 'undefined')
185                  $(this).data('qtip').current = $(this).data('qtip').interfaces.length;
186
187               // Push new API interface onto interfaces array
188               $(this).data('qtip').interfaces.push(obj);
189            }
190
191            // No qTip data is present, create now
192            else $(this).data('qtip', { current: 0, interfaces: [obj] });
193
194            // If prerendering is disabled, create tooltip on showEvent
195            if(config.content.prerender === false && config.show.when.event !== false && config.show.ready !== true)
196            {
197               config.show.when.target.bind(config.show.when.event+'.qtip-'
197+id+'-create', { qtip: id }, function(event)
198               {
199                  // Retrieve API interface via passed qTip Id
200                  api = $.fn.qtip.interfaces[ event.data.qtip ];
201
202                  // Unbind show event and cache mouse coords
203                  api.options.show.when.target.unbind(api.options.show.when.event+'.qtip-'+event.data.qtip+'-create');
204                  api.cache.mouse = { x: event.pageX, y: event.pageY };
205
206                  // Render tooltip and start the event sequence
207                  construct.call( api );
208                  api.options.show.when.target.trigger(api.options.show.when.event);
209               });
210            }
211
212            // Prerendering is enabled, create tooltip now
213            else
214            {
215               // Set mouse position cache to top left of the element
216               obj.cache.mouse = {
217                  x: config.show.when.target.offset().left,
218                  y: config.show.when.target.offset().top
219               };
220
221               // Construct the tooltip
222               construct.call(obj);
223            }
224         };
225      });
226   };
227
228   // Instantiator
229   function qTip(target, options, id)
230   {
231      // Declare this reference
232      var self = this;
233
234      // Setup class attributes
235      self.id = id;
236      self.options = options;
237      self.status = {
238         animated: false,
239         rendered: false,
240         disabled: false,
241         focused: false
242      };
243      self.elements = {
244         target: target.addClass(self.options.style.classes.target),
245         tooltip: null,
246         wrapper: null,
247         content: null,
248         contentWrapper: null,
249         title: null,
250         button: null,
251         tip: null,
252         bgiframe: null
253      };
254      self.cache = {
255         mouse: {},
256         position: {},
257         toggle: 0
258      };
259      self.timers = {};
260
261      // Define exposed API methods
262      $.extend(self, self.options.api,
263      {
264         show: function(event)
265         {
266            var returned, solo;
267
268            // Make sure tooltip is rendered and if not, return
269            if(!self.status.rendered)
270               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'show');
271
272            // Only continue if element is visible
273            if(self.elements.tooltip.css('display') !== 'none') return self;
274
275            // Clear animation queue
276            self.elements.tooltip.stop(true, false);
277
278            // Call API method and if return value is false, halt
279            returned = self.beforeShow.call(self, event);
280            if(returned === false) return self;
281
282            // Define afterShow callback method
283            function afterShow()
284            {
285               // Call API method and focus if it isn't static
286               if(self.options.position.type !== 'static') self.focus();
287               self.onShow.call(self, event);
288
289               // Prevent antialias from disappearing in IE7 by removing filter attribute
290               if(BrowserDetect.browser == 'Explorer') self.elements.tooltip.get(0).style.removeAttribute('filter');
291            };
292
293            // Maintain toggle functionality if enabled
294            self.cache.toggle = 1;
295
296            // Update tooltip position if it isn't static
297            if(self.options.position.type !== 'static')
298               self.updatePosition(event, (self.options.show.effect.length > 0));
299
300            // Hide other tooltips if tooltip is solo
301            if(typeof self.options.show.solo == 'object') solo = $(self.options.show.solo);
302            else if(self.options.show.solo === true) solo = $('div.qtip').not(self.elements.tooltip);
303            if(solo) solo.each(function(){ if($(this).qtip('api').status.rendered === true) $(this).qtip('api').hide(); });
304
305            // Show tooltip
306            if(typeof self.options.show.effect.type == 'function')
307            {
308               self.options.show.effect.type.call(self.elements.tooltip, self.options.show.effect.length);
309               self.elements.tooltip.queue(function(){ afterShow(); $(this).dequeue(); });
310            }
311            else
312            {
313               switch(self.options.show.effect.type.toLowerCase())
314               {
315                  case 'fade':
316                     self.elements.tooltip.fadeIn(self.options.show.effect.length, afterShow);
317                     break;
318                  case 'slide':
319                     self.elements.tooltip.slideDown(self.options.show.effect.length, function()
320                     {
321                        afterShow();
322                        if(self.options.position.type !== 'static') self.updatePosition(event, true);
323                     });
324                     break;
325                  case 'grow':
326                     self.elements.tooltip.show(self.options.show.effect.length, afterShow);
327                     break;
328                  default:
329                     self.elements.tooltip.show(null, afterShow);
330                     break;
331               };
332
333               // Add active class to tooltip
334               self.elements.tooltip.addClass(self.options.style.classes.active);
335            };
336
337            // Log event and return
338            return $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_SHOWN, 'show');
339         },
340
341         hide: function(event)
342         {
343            var returned;
344
345            // Make sure tooltip is rendered and if not, return
346            if(!self.status.rendered)
347               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'hide');
348
349            // Only continue if element is visible
350            else if(self.elements.tooltip.css('display') === 'none') return self;
351
352            // Stop show timer and animation queue
353            clearTimeout(self.timers.show);
354            self.elements.tooltip.stop(true, false);
355
356            // Call API method and if return value is false, halt
357            returned = self.beforeHide.call(self, event);
358            if(returned === false) return self;
359
360            // Define afterHide callback method
361            function afterHide(){ self.onHide.call(self, event); };
362
363            // Maintain toggle functionality if enabled
364            self.cache.toggle = 0;
365
366            // Hide tooltip
367            if(typeof self.options.hide.effect.type == 'function')
368            {
369               self.options.hide.effect.type.call(self.elements.tooltip, self.options.hide.effect.length);
370               self.elements.tooltip.queue(function(){ afterHide(); $(this).dequeue(); });
371            }
372            else
373            {
374               switch(self.options.hide.effect.type.toLowerCase())
375               {
376                  case 'fade':
377                     self.elements.tooltip.fadeOut(self.options.hide.effect.length, afterHide);
378                     break;
379                  case 'slide':
380                     self.elements.tooltip.slideUp(self.options.hide.effect.length, afterHide);
381                     break;
382                  case 'grow':
383                     self.elements.tooltip.hide(self.options.hide.effect.length, afterHide);
384                     break;
385                  default:
386                     self.elements.tooltip.hide(null, afterHide);
387                     break;
388               };
389
390               // Remove active class to tooltip
391               self.elements.tooltip.removeClass(self.options.style.classes.active);
392            };
393
394            // Log event and return
395            return $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_HIDDEN, 'hide');
396         },
397
398         updatePosition: function(event, animate)
399         {
400            var i, target, tooltip, coords, mapName, imagePos, newPosition, ieAdjust, ie6Adjust, borderAdjust, mouseAdjust, offset, curPosition, returned
401
402            // Make sure tooltip is rendered and if not, return
403            if(!self.status.rendered)
404               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'updatePosition');
405
406            // If tooltip is static, return
407            else if(self.options.position.type == 'static')
408               return $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.CANNOT_POSITION_STATIC, 'updatePosition');
409
410            // Define property objects
411            target = {
412               position: { left: 0, top: 0 },
413               dimensions: { height: 0, width: 0 },
414               corner: self.options.position.corner.target
415            };
416            tooltip = {
417               position: self.getPosition(),
418               dimensions: self.getDimensions(),
419               corner: self.options.position.corner.tooltip
420            };
421
422            // Target is an HTML element
423            if(self.options.position.target !== 'mouse')
424            {
425               // If the HTML element is AREA, calculate position manually
426               if(self.options.position.target.get(0).nodeName.toLowerCase() == 'area')
427               {
428                  // Retrieve coordinates from coords attribute and parse into integers
429                  coords = self.options.position.target.attr('coords').split(',');
430                  for(i = 0; i < coords.length; i++) coords[i] = parseInt(coords[i]);
431
432                  // Setup target position object
433                  mapName = self.options.position.target.parent('map').attr('name');
434                  imagePos = $('img[usemap="#'+mapName+'"]:first').offset();
435                  target.position = {
436                     left: Math.floor(imagePos.left + coords[0]),
437                     top: Math.floor(imagePos.top + coords[1])
438                  };
439
440                  // Determine width and height of the area
441                  switch(self.options.position.target.attr('shape').toLowerCase())
442                  {
443                     case 'rect':
444                        target.dimensions = {
445                           width: Math.ceil(Math.abs(coords[2] - coords[0])),
446                           height: Math.ceil(Math.abs(coords[3] - coords[1]))
447                        };
448                        break;
449
450                     case 'circle':
451                        target.dimensions = {
452                           width: coords[2] + 1,
453                           height: coords[2] + 1
454                        };
455                        break;
456
457                     case 'poly':
458                        target.dimensions = {
459                           width: coords[0],
460                           height: coords[1]
461                        };
462
463                        for(i = 0; i < coords.length; i++)
464                        {
465                           if(i % 2 == 0)
466                           {
467                              if(coords[i] > target.dimensions.width)
468                                 target.dimensions.width = coords[i];
469                              if(coords[i] < coords[0])
470                                 target.position.left = Math.floor(imagePos.left + coords[i]);
471                           }
472                           else
473                           {
474                              if(coords[i] > target.dimensions.height)
475                                 target.dimensions.height = coords[i];
476                              if(coords[i] < coords[1])
477                                 target.position.top = Math.floor(imagePos.top + coords[i]);
478                           };
479                        };
480
481                        target.dimensions.width = target.dimensions.width - (target.position.left - imagePos.left);
482                        target.dimensions.height = target.dimensions.height - (target.position.top - imagePos.top);
483                        break;
484
485                     default:
486                        return $.fn.qtip.log.error.call(self, 4, $.fn.qtip.constants.INVALID_AREA_SHAPE, 'updatePosition');
487                        break;
488                  };
489
490                  // Adjust position by 2 pixels (Positioning bug?)
491                  target.dimensions.width -= 2; target.dimensions.height -= 2;
492               }
493
494               // Target is the document
495               else if(self.options.position.target.add(document.body).length === 1)
496               {
497                  target.position = { left: $(document).scrollLeft(), top: $(document).scrollTop() };
498                  target.dimensions = { height: $(window).height(), width: $(window).width() };
499               }
500
501               // Target is a regular HTML element, find position normally
502               else
503               {
504                  // Check if the target is another tooltip. If its animated, retrieve position from newPosition data
505                  if(typeof self.options.position.target.attr('qtip') !== 'undefined')
506                     target.position = self.options.position.target.qtip('api').cache.position;
507                  else
508                     target.position = self.options.position.target.offset();
509
510                  // Setup dimensions objects
511                  target.dimensions = {
512                     height: self.options.position.target.outerHeight(),
513                     width: self.options.position.target.outerWidth()
514                  };
515               };
516
517               // Calculate correct target corner position
518               newPosition = $.extend({}, target.position);
519               if(target.corner.search(/right/i) !== -1)
520                  newPosition.left += target.dimensions.width;
521
522               if(target.corner.search(/bottom/i) !== -1)
523                  newPosition.top += target.dimensions.height;
524
525               if(target.corner.search(/((top|bottom)Middle)|center/) !== -1)
526                  newPosition.left += (target.dimensions.width / 2);
527
528               if(target.corner.search(/((left|right)Middle)|center/) !== -1)
529                  newPosition.top += (target.dimensions.height / 2);
530            }
531
532            // Mouse is the target, set position to current mouse coordinates
533            else
534            {
535               // Setup target position and dimensions objects
536               target.position = newPosition = { left: self.cache.mouse.x, top: self.cache.mouse.y };
537               target.dimensions = { height: 1, width: 1 };
538            };
539
540            // Calculate correct target corner position
541            if(tooltip.corner.search(/right/i) !== -1)
542               newPosition.left -= tooltip.dimensions.width;
543
544            if(tooltip.corner.search(/bottom/i) !== -1)
545               newPosition.top -= tooltip.dimensions.height;
546
547            if(tooltip.corner.search(/((top|bottom)Middle)|center/) !== -1)
548               newPosition.left -= (tooltip.dimensions.width / 2);
549
550            if(tooltip.corner.search(/((left|right)Middle)|center/) !== -1)
551               newPosition.top -= (tooltip.dimensions.height / 2);
552
553            // Setup IE adjustment variables (Pixel gap bugs)
554            ieAdjust = (BrowserDetect.browser == 'Explorer') ? 1 : 0; // And this is why I hate IE...
555            ie6Adjust = (BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6) ? 1 : 0; // ...and even more so IE6!
556
557            // Adjust for border radius
558            if(self.options.style.border.radius > 0)
559            {
560               if(tooltip.corner.search(/Left/) !== -1)
561                  newPosition.left -= self.options.style.border.radius;
562               else if(tooltip.corner.search(/Right/) !== -1)
563                  newPosition.left += self.options.style.border.radius;
564
565               if(tooltip.corner.search(/Top/) !== -1)
566                  newPosition.top -= self.options.style.border.radius;
567               else if(tooltip.corner.search(/Bottom/) !== -1)
568                  newPosition.top += self.options.style.border.radius;
569            };
570
571            // IE only adjustments (Pixel perfect!)
572            if(ieAdjust)
573            {
574               if(tooltip.corner.search(/top/) !== -1)
575                  newPosition.top -= ieAdjust
576               else if(tooltip.corner.search(/bottom/) !== -1)
577                  newPosition.top += ieAdjust
578
579               if(tooltip.corner.search(/left/) !== -1)
580                  newPosition.left -= ieAdjust
581               else if(tooltip.corner.search(/right/) !== -1)
582                  newPosition.left += ieAdjust
583
584               if(tooltip.corner.search(/leftMiddle|rightMiddle/) !== -1)
585                  newPosition.top -= 1
586            };
587
588            // If screen adjustment is enabled, apply adjustments
589            if(self.options.position.adjust.screen === true)
590               newPosition = screenAdjust.call(self, newPosition, target, tooltip);
591
592            // If mouse is the target, prevent tooltip appearing directly under the mouse
593            if(self.options.position.target === 'mouse' && self.options.position.adjust.mouse === true)
594            {
595               if(self.options.position.adjust.screen === true && self.elements.tip)
596                  mouseAdjust = self.elements.tip.attr('rel');
597               else
598                  mouseAdjust = self.options.position.corner.tooltip;
599
600               newPosition.left += (mouseAdjust.search(/right/i) !== -1) ? -6 : 6;
601               newPosition.top += (mouseAdjust.search(/bottom/i) !== -1) ? -6 : 6;
602            }
603
604            // Initiate bgiframe plugin in IE6 if tooltip overlaps a select box or object element
605            if(!self.elements.bgiframe && BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6)
606            {
607               $('select, object').each(function()
608               {
609                  offset = $(this).offset();
610                  offset.bottom = offset.top + $(this).height();
611                  offset.right = offset.left + $(this).width();
612
613                  if(newPosition.top + tooltip.dimensions.height >= offset.top
614                  && newPosition.left + tooltip.dimensions.width >= offset.left)
615                     bgiframe.call(self);
616               });
617            };
618
619            // Add user xy adjustments
620            newPosition.left += self.options.position.adjust.x;
621            newPosition.top += self.options.position.adjust.y;
622
623            // Set new tooltip position if its moved, animate if enabled
624            curPosition = self.getPosition();
625            if(newPosition.left != curPosition.left || newPosition.top != curPosition.top)
626            {
627               // Call API method and if return value is false, halt
628               returned = self.beforePositionUpdate.call(self, event);
629               if(returned === false) return self;
630
631               // Cache new position
632               self.cache.position = newPosition;
633
634               // Check if animation is enabled
635               if(animate === true)
636               {
637                  // Set animated status
638                  self.status.animated = true;
639
640                  // Animate and reset animated status on animation end
641                  self.elements.tooltip.animate(newPosition, 200, 'swing', function(){ self.status.animated = false });
642               }
643
644               // Set new position via CSS
645               else self.elements.tooltip.css(newPosition);
646
647               // Call API method and log event if its not a mouse move
648               self.onPositionUpdate.call(self, event);
649               if(typeof event !== 'undefined' && event.type && event.type !== 'mousemove')
650                  $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_POSITION_UPDATED, 'updatePosition');
651            };
652
653            return self;
654         },
655
656         updateWidth: function(newWidth)
657         {
658            var hidden;
659
660            // Make sure tooltip is rendered and if not, return
661            if(!self.status.rendered)
662               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'updateWidth');
663
664            // Make sure supplied width is a number and if not, return
665            else if(newWidth && typeof newWidth !== 'number')
666               return $.fn.qtip.log.error.call(self, 2, 'newWidth must be of type number', 'updateWidth');
667
668            // Setup elements which must be hidden during width update
669            hidden = self.elements.contentWrapper.siblings().add(self.elements.tip).add(self.elements.button);
670
671            // Calculate the new width if one is not supplied
672            if(!newWidth)
673            {
674               // Explicit width is set
675               if(typeof self.options.style.width.value == 'number')
676                  newWidth = self.options.style.width.value;
677
678               // No width is set, proceed with auto detection
679               else
680               {
681                  // Set width to auto initally to determine new width and hide other elements
682                  self.elements.tooltip.css({ width: 'auto' });
683                  hidden.hide();
684
685                  // Set position and zoom to defaults to prevent IE hasLayout bug
686                  if(BrowserDetect.browser == 'Explorer')
687                     self.elements.wrapper.add(self.elements.contentWrapper.children()).css({ zoom: 'normal' });
688
689                  // Set the new width
690                  newWidth = self.getDimensions().width + 1;
691
692                  // Make sure its within the maximum and minimum width boun
692dries
693                  if(!self.options.style.width.value)
694                  {
695                     if(newWidth > self.options.style.width.max) newWidth = self.options.style.width.max
696                     if(newWidth < self.options.style.width.min) newWidth = self.options.style.width.min
697                  };
698               };
699            };
700
701            // Adjust newWidth by 1px if width is odd (IE6 rounding bug fix)
702            if(newWidth % 2 !== 0) newWidth -= 1;
703
704            // Set the new calculated width and unhide other elements
705            self.elements.tooltip.width(newWidth);
706            hidden.show();
707
708            // Set the border width, if enabled
709            if(self.options.style.border.radius)
710            {
711               self.elements.tooltip.find('.qtip-betweenCorners').each(function(i)
712               {
713                  $(this).width(newWidth - (self.options.style.border.radius * 2));
714               })
715            };
716
717            // IE only adjustments
718            if(BrowserDetect.browser == 'Explorer')
719            {
720               // Reset position and zoom to give the wrapper layout (IE hasLayout bug)
721               self.elements.wrapper.add(self.elements.contentWrapper.children()).css({ zoom: '1' });
722
723               // Set the new width
724               self.elements.wrapper.width(newWidth);
725
726               // Adjust BGIframe height and width if enabled
727               if(self.elements.bgiframe) self.elements.bgiframe.width(newWidth).height(self.getDimensions.height);
728            };
729
730            // Log event and return
731            return $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_WIDTH_UPDATED, 'updateWidth');
732         },
733
734         updateStyle: function(name)
735         {
736            var tip, borders, context, corner, coordinates;
737
738            // Make sure tooltip is rendered and if not, return
739            if(!self.status.rendered)
740               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'updateStyle');
741
742            // Return if style is not defined or name is not a string
743            else if(typeof name !== 'string' || !$.fn.qtip.styles[name])
744               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.STYLE_NOT_DEFINED, 'updateStyle');
745
746            // Set the new style object
747            self.options.style = buildStyle.call(self, $.fn.qtip.styles[name], self.options.user.style);
748
749            // Update initial styles of content and title elements
750            self.elements.content.css( jQueryStyle(self.options.style) );
751            if(self.options.content.title.text !== false)
752               self.elements.title.css( jQueryStyle(self.options.style.title, true) );
753
754            // Update CSS border colour
755            self.elements.contentWrapper.css({ borderColor: self.options.style.border.color });
756
757            // Update tip color if enabled
758            if(self.options.style.tip.corner !== false)
759            {
760               if($('<canvas>').get(0).getContext)
761               {
762                  // Retrieve canvas context and clear
763                  tip = self.elements.tooltip.find('.qtip-tip canvas:first');
764                  context = tip.get(0).getContext('2d');
765                  context.clearRect(0,0,300,300);
766
767                  // Draw new tip
768                  corner = tip.parent('div[rel]:first').attr('rel');
769                  coordinates = calculateTip(corner, self.options.style.tip.size.width, self.options.style.tip.size.height);
770                  drawTip.call(self, tip, coordinates, self.options.style.tip.color || self.options.style.border.color);
771               }
772               else if(BrowserDetect.browser == 'Explorer')
773               {
774                  // Set new fillcolor attribute
775                  tip = self.elements.tooltip.find('.qtip-tip [nodeName="shape"]');
776                  tip.attr('fillcolor', self.options.style.tip.color || self.options.style.border.color);
777               };
778            };
779
780            // Update border colors if enabled
781            if(self.options.style.border.radius > 0)
782            {
783               self.elements.tooltip.find('.qtip-betweenCorners').css({ backgroundColor: self.options.style.border.color });
784
785               if($('<canvas>').get(0).getContext)
786               {
787                  borders = calculateBorders(self.options.style.border.radiu
787s)
788                  self.elements.tooltip.find('.qtip-wrapper canvas').each(function()
789                  {
790                     // Retrieve canvas context and clear
791                     context = $(this).get(0).getContext('2d');
792                     context.clearRect(0,0,300,300);
793
794                     // Draw new border
795                     corner = $(this).parent('div[rel]:first').attr('rel')
796                     drawBorder.call(self, $(this), borders[corner],
797                        self.options.style.border.radius, self.options.style.border.color);
798                  });
799               }
800               else if(BrowserDetect.browser == 'Explorer')
801               {
802                  // Set new fillcolor attribute on each border corner
803                  self.elements.tooltip.find('.qtip-wrapper [nodeName="arc"]').each(function()
804                  {
805                     $(this).attr('fillcolor', self.options.style.border.color)
806                  });
807               };
808            };
809
810            // Log event and return
811            return $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_STYLE_UPDATED, 'updateStyle');
812         },
813
814         updateContent: function(content, reposition)
815         {
816            var parsedContent, images, loadedImages;
817
818            // Make sure tooltip is rendered and if not, return
819            if(!self.status.rendered)
820               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'updateContent');
821
822            // Make sure content is defined before update
823            else if(!content)
824               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.NO_CONTENT_PROVIDED, 'updateContent');
825
826            // Call API method and set new content if a string is returned
827            parsedContent = self.beforeContentUpdate.call(self, content);
828            if(typeof parsedContent == 'string') content = parsedContent;
829            else if(parsedContent === false) return;
830
831            // Set position and zoom to defaults to prevent IE hasLayout bug
832            if(BrowserDetect.browser == 'Explorer') self.elements.contentWrapper.children().css({ zoom: 'normal' });
833
834            // Append new content if its a DOM array and show it if hidden
835            if(content.jquery && content.length > 0)
836               content.clone(true).appendTo(self.elements.content).show();
837
838            // Content is a regular string, insert the new content
839            else self.elements.content.html(content);
840
841            // Check if images need to be loaded before position is updated to prevent mis-positioning
842            images = self.elements.content.find('img[complete=false]');
843            if(images.length > 0)
844            {
845               loadedImages = 0;
846               images.each(function(i)
847               {
848                  $('<img src="'+ $(this).attr('src') +'" />')
849                     .load(function(){ if(++loadedImages == images.length) afterLoad(); });
850               });
851            }
852            else afterLoad();
853
854            function afterLoad()
855            {
856               // Update the tooltip width
857               self.updateWidth();
858
859               // If repositioning is enabled, update positions
860               if(reposition !== false)
861               {
862                  // Update position if tooltip isn't static
863                  if(self.options.position.type !== 'static')
864                     self.updatePosition(self.elements.tooltip.is(':visible'), true);
865
866                  // Reposition the tip if enabled
867                  if(self.options.style.tip.corner !== false)
868                     positionTip.call(self);
869               };
870            };
871
872            // Call API method and log event
873            self.onContentUpdate.call(self);
874            return $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_CONTENT_UPDATED, 'loadContent');
875         },
876
877         loadContent: function(url, data, method)
878         {
879            var returned;
880
881            // Make sure tooltip is rendered and if not, return
882            if(!self.status.rendered)
883               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'loadContent');
884
885            // Call API method and if return value is false, halt
886            returned = self.beforeContentLoad.call(self);
887            if(returned === false) return self;
888
889            // Load content using specified request type
890            if(method == 'post')
891               $.post(url, data, setupContent);
892            else
893               $.get(url, data, setupContent);
894
895            function setupContent(content)
896            {
897               // Call API method and log event
898               self.onContentLoad.call(self);
899               $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_CONTENT_LOADED, 'loadContent');
900
901               // Update the content
902               self.updateContent(content);
903            };
904
905            return self;
906         },
907
908         updateTitle: function(content)
909         {
910            // Make sure tooltip is rendered and if not, return
911            if(!self.status.rendered)
912               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'updateTitle');
913
914            // Make sure content is defined before update
915            else if(!content)
916               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.NO_CONTENT_PROVIDED, 'updateTitle');
917
918            // Call API method and if return value is false, halt
919            returned = self.beforeTitleUpdate.call(self);
920            if(returned === false) return self;
921
922            // Set the new content and reappend the button if enabled
923            if(self.elements.button) self.elements.button = self.elements.button.clone(true);
924            self.elements.title.html(content)
925            if(self.elements.button) self.elements.title.prepend(self.elements.button);
926
927            // Call API method and log event
928            self.onTitleUpdate.call(self);
929            return $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_TITLE_UPDATED, 'updateTitle');
930         },
931
932         focus: function(event)
933         {
934            var curIndex, newIndex, elemIndex, returned;
935
936            // Make sure tooltip is rendered and if not, return
937            if(!self.status.rendered)
938               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'focu
938s');
939
940            else if(self.options.position.type == 'static')
941               return $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.CANNOT_FOCUS_STATIC, 'focus');
942
943            // Set z-index variables
944            curIndex = parseInt( self.elements.tooltip.css('z-index') );
945            newIndex = 1000 + $('div.qtip[qtip]').length - 1;
946
947            // Only update the z-index if it has changed and tooltip is not already focused
948            if(!self.status.focused && curIndex !== newIndex)
949            {
950               // Call API method and if return value is false, halt
951               returned = self.beforeFocus.call(self, event);
952               if(returned === false) return self;
953
954               // Loop through all other tooltips
955               $('div.qtip[qtip]').not(self.elements.tooltip).each(function()
956               {
957					try {
958						if($(this).qtip('api').status.rendered === true) {
959							elemIndex = parseInt($(this).css('z-index'));
960
961							// Reduce all other tooltip z-index by 1
962							if(typeof elemIndex == 'number' && elemIndex > -1)
963								$(this).css({ zIndex: parseInt( $(this).css('z-index') ) - 1 });
964
965							// Set focused status to false
966							$(this).qtip('api').status.focused = false;
967						}
968					}
969					catch(err) {
970						// do nothing
971					}
972               })
973
974               // Set the new z-index and set focus status to true
975               self.elements.tooltip.css({ zIndex: newIndex });
976               self.status.focused = true;
977
978               // Call API method and log event
979               self.onFocus.call(self, event);
980               $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_FOCUSED, 'focus');
981            };
982
983            return self;
984         },
985
986         disable: function(state)
987         {
988            // Make sure tooltip is rendered and if not, return
989            if(!self.status.rendered)
990               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'disable');
991
992            if(state)
993            {
994               // Tooltip is not already disabled, proceed
995               if(!self.status.disabled)
996               {
997                  // Set the disabled flag and log event
998                  self.status.disabled = true;
999                  $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_DISABLED, 'disable');
1000               }
1001
1002               // Tooltip is already disabled, inform user via log
1003               else  $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.TOOLTIP_ALREADY_DISABLED, 'disable');
1004            }
1005            else
1006            {
1007               // Tooltip is not already enabled, proceed
1008               if(self.status.disabled)
1009               {
1010                  // Reassign events, set disable status and log
1011                  self.status.disabled = false;
1012                  $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_ENABLED, 'disable');
1013               }
1014
1015               // Tooltip is already enabled, inform the user via log
1016               else $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.TOOLTIP_ALREADY_ENABLED, 'disable');
1017            };
1018
1019            return self;
1020         },
1021
1022         destroy: function()
1023         {
1024            var i, returned, interfaces;
1025
1026            // Call API method and if return value is false, halt
1027            returned = self.beforeDestroy.call(self);
1028            if(returned === false) return self;
1029
1030            // Check if tooltip is rendered
1031            if(self.status.rendered)
1032            {
1033               // Remove event handlers and remove element
1034               self.options.show.when.target.unbind('mousemove.qtip', self.updatePosition);
1035               self.options.show.when.target.unbind('mouseout.qtip', self.hide);
1036               self.options.show.when.target.unbind(self.options.show.when.event + '.qtip');
1037               self.options.hide.when.target.unbind(self.options.hide.when.event + '.qtip');
1038               self.elements.tooltip.unbind(self.options.hide.when.event + '.qtip');
1039               self.elements.tooltip.unbind('mouseover.qtip', self.focus);
1040               self.elements.tooltip.remove();
1041            }
1042
1043            // Tooltip isn't yet rendered, remove render event
1044            else self.options.show.when.target.unbind(self.options.show.when.event+'.qtip-create');
1045
1046            // Check to make sure qTip data is present on target element
1047            if(typeof self.elements.target.data('qtip') == 'object')
1048            {
1049               // Remove API references from interfaces object
1050               interfaces = self.elements.target.data('qtip').interfaces;
1051               if(typeof interfaces == 'object' && interfaces.length > 0)
1052               {
1053                  // Remove API from interfaces array
1054                  for(i = 0; i < interfaces.length - 1; i++)
1055                     if(interfaces[i].id == self.id) interfaces.splice(i, 1)
1056               }
1057            }
1058            delete $.fn.qtip.interfaces[self.id];
1059
1060            // Set qTip current id to previous tooltips API if available
1061            if(typeof interfaces == 'object' && interfaces.length > 0)
1062               self.elements.target.data('qtip').current = interfaces.length -1;
1063            else
1064               self.elements.target.removeData('qtip');
1065
1066            // Call API method and log destroy
1067            self.onDestroy.call(self);
1068            $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_DESTROYED, 'destroy');
1069
1070            return self.elements.target
1071         },
1072
1073         getPosition: function()
1074         {
1075            var show, offset;
1076
1077            // Make sure tooltip is rendered and if not, return
1078            if(!self.status.rendered)
1079               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'getPosition');
1080
1081            show = (self.elements.tooltip.css('display') !== 'none') ? false : true;
1082
1083            // Show and hide tooltip to make sure coordinates are returned
1084            if(show) self.elements.tooltip.css({ visiblity: 'hidden' }).show();
1085            offset = self.elements.tooltip.offset();
1086            if(show) self.elements.tooltip.css({ visiblity: 'visible' }).hide();
1087
1088            return offset;
1089         },
1090
1091         getDimensions: function()
1092         {
1093            var show, dimensions;
1094
1095            // Make sure tooltip is rendered and if not, return
1096            if(!self.status.rendered)
1097               return $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.TOOLTIP_NOT_RENDERED, 'getDimensions');
1098
1099            show = (!self.elements.tooltip.is(':visible')) ? true : false;
1100
1101            // Show and hide tooltip to make sure dimensions are returned
1102            if(show) self.elements.tooltip.css({ visiblity: 'hidden' }).show();
1103            dimensions = {
1104               height: self.elements.tooltip.outerHeight(),
1105               width: self.elements.tooltip.outerWidth()
1106            };
1107            if(show) self.elements.tooltip.css({ visiblity: 'visible' }).hide();
1108
1109            return dimensions;
1110         }
1111      });
1112   };
1113
1114   // Define priamry construct function
1115   function construct()
1116   {
1117      var self, adjust, content, url, data, method, tempLength;
1118      self = this;
1119
1120      // Call API method
1121      self.beforeRender.call(self);
1122
1123      // Set rendered status to true
1124      self.status.rendered = true;
1125
1126      // Create initial tooltip elements
1127      self.elements.tooltip =  '<div qtip="'+self.id+'" ' +
1128         'class="qtip '+(self.options.style.classes.tooltip || self.options.style)+'"' +
1129         'style="display:none; -moz-border-radius:0; -webkit-border-radius:0; border-radius:0;' +
1130         'position:'+self.options.position.type+';">' +
1131         '  <div class="qtip-wrapper" style="position:relative; overflow:hidden; text-align:left;">' +
1132         '    <div class="qtip-contentWrapper" style="overflow:hidden;">' +
1133         '       <div class="qtip-content '+self.options.style.classes.content+'"></div>' +
1134         '</div></div></div>';
1135
1136      // Append to container element
1137      self.elements.tooltip = $(self.elements.tooltip);
1138      self.elements.tooltip.appendTo(self.options.position.container)
1139
1140      // Setup tooltip qTip data
1141      self.elements.tooltip.data('qtip', { current: 0, interfaces: [self] });
1142
1143      // Setup element references
1144      self.elements.wrapper = self.elements.tooltip.children('div:first');
1145      self.elements.contentWrapper = self.elements.wrapper.children('div:first').css({ background: self.options.style.background });
1146      self.elements.content = self.elements.contentWrapper.children('div:first').css( jQueryStyle(self.options.style) );
1147
1148      // Apply IE hasLayout fix to wrapper and content elements
1149      if(BrowserDetect.browser == 'Explorer') self.elements.wrapper.add(self.elements.content).css({ zoom: 1 });
1150
1151      // Setup tooltip attributes
1152      if(self.options.hide.when.event == 'unfocus') self.elements.tooltip.attr('unfocus', true);
1153
1154      // If an explicit width is set, updateWidth prior to setting content to prevent dirty rendering
1155      if(typeof self.options.style.width.value == 'number') self.updateWidth();
1156
1157      // Create borders and tips if supported by the browser
1158      if($('<canvas>').get(0).getContext || BrowserDetect.browser == 'Explorer')
1159      {
1160         // Create border
1161         if(self.options.style.border.radius > 0)
1162            createBorder.call(self);
1163         else
1164            self.elements.contentWrapper.css({ border: self.options.style.border.width+'px solid '+self.options.style.border.color  });
1165
1166         // Create tip if enabled
1167         if(self.options.style.tip.corner !== false)
1168            createTip.call(self);
1169      }
1170
1171      // Neither canvas or VML is supported, tips and borders cannot be drawn!
1172      else
1173      {
1174         // Set defined border width
1175         self.elements.contentWrapper.css({ border: self.options.style.border.width+'px solid '+self.options.style.border.color  });
1176
1177         // Reset border radius and tip
1178         self.options.style.border.radius = 0;
1179         self.options.style.tip.corner = false;
1180
1181         // Inform via log
1182         $.fn.qtip.log.error.call(self, 2, $.fn.qtip.constants.CANVAS_VML_NOT_SUPPORTED, 'render');
1183      };
1184
1185      // Use the provided content string or DOM array
1186      if((typeof self.options.content.text == 'string' && self.options.content.text.length > 0)
1187      || (self.options.content.text.jquery && self.options.content.text.length > 0))
1188         content = self.options.content.text;
1189
1190      // Use title string for content if present
1191      else if(typeof self.elements.target.attr('title') == 'string' && self.elements.target.attr('title').length > 0)
1192      {
1193         content = self.elements.target.attr('title').replace("\\n", '<br />');
1194         self.elements.target.attr('title', ''); // Remove title attribute to prevent default tooltip showing
1195      }
1196
1197      // No title is present, use alt attribute instead
1198      else if(typeof self.elements.target.attr('alt') == 'string' && self.elements.target.attr('alt').length > 0)
1199      {
1200         content = self.elements.target.attr('alt').replace("\\n", '<br />');
1201         self.elements.target.attr('alt', ''); // Remove alt attribute to prevent default tooltip showing
1202      }
1203
1204      // No valid content was provided, inform via log
1205      else
1206      {
1207         content = ' ';
1208         $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.NO_VALID_CONTENT, 'render');
1209      };
1210
1211      // Set the tooltips content and create title if enabled
1212      if(self.options.content.title.text !== false) createTitle.call(self);
1213      self.updateContent(content);
1214
1215      // Assign events and toggle tooltip with focus
1216      assignEvents.call(self);
1217      if(self.options.show.ready === true) self.show();
1218
1219      // Retrieve ajax content if provided
1220      if(self.options.content.url !== false)
1221      {
1222         url = self.options.content.url;
1223         data = self.options.content.data;
1224         method = self.options.content.method || 'get';
1225         self.loadContent(url, data, method);
1226      };
1227
1228      // Call API method and log event
1229      self.onRender.call(self);
1230      $.fn.qtip.log.error.call(self, 1, $.fn.qtip.constants.EVENT_RENDERED, 'render');
1231   };
1232
1233   // Create borders using canvas and VML
1234   function createBorder()
1235   {
1236      var self, i, width, radius, color, coordinates, containers, size, betweenWidth, betweenCorners, borderTop, borderBottom, borderCoord, sideWidth, vertWidth;
1237      self = this;
1238
1239      // Destroy previous border elements, if present
1240      self.elements.wrapper.find('.qtip-borderBottom, .qtip-borderTop').remove();
1241
1242      // Setup local variables
1243      width = self.options.style.border.width;
1244      radius = self.options.style.border.radius;
1245      color = self.options.style.border.color || self.options.style.tip.color;
1246
1247      // Calculate border coordinates
1248      coordinates = calculateBorders(radius);
1249
1250      // Create containers for the border shapes
1251      containers = {};
1252      for(i in coordinates)
1253      {
1254         // Create shape container
1255         containers[i] = '<div rel="'+i+'" style="'+((i.search(/Left/) !== -1) ? 'left' : 'right') + ':0; ' +
1256            'position:absolute; height:'+radius+'px; width:'+radius+'px; overflow:hidden; line-height:0.1px; font-size:1px">';
1257
1258         // Canvas is supported
1259         if($('<canvas>').get(0).getContext)
1260            containers[i] += '<canvas height="'+radius+'" width="'+radius+'" style="vertical-align: top"></canvas>';
1261
1262         // No canvas, but if it's IE use VML
1263         else if(BrowserDetect.browser == 'Explorer')
1264         {
1265            size = radius * 2 + 3;
1266            containers[i] += '<v:arc stroked="false" fillcolor="'+color+'" startangle="'+coordinates[i][0]+'" endangle="'+coordinates[i][1]+'" ' +
1267               'style="width:'+size+'px; height:'+size+'px; margin-top:'+((i.search(/bottom/) !== -1) ? -2 : -1)+'px; ' +
1268               'margin-left:'+((i.search(/Right/) !== -1) ? coordinates[i][2] - 3.5 : -1)+'px; ' +
1269               'vertical-align:top; display:inline-block; behavior:url(#default#VML)"></v:arc>';
1270
1271         };
1272
1273         containers[i] += '</div>';
1274      };
1275
1276      // Create between corners elements
1277      betweenWidth = self.getDimensions().width - (Math.max(width, radius) * 2);
1278      betweenCorners = '<div class="qtip-betweenCorners" style="height:'+radius+'px; width:'+betweenWidth+'px; ' +
1279         'overflow:hidden; background-color:'+color+'; line-height:0.1px; font-size:1px;">';
1280
1281      // Create top border container
1282      borderTop = '<div class="qtip-borderTop" dir="ltr" style="height:'+radius+'px; ' +
1283         'margin-left:'+radius+'px; line-height:0.1px; font-size:1px; padding:0;">' +
1284         containers['topLeft'] + containers['topRight'] + betweenCorners;
1285      self.elements.wrapper.prepend(borderTop);
1286
1287      // Create bottom border container
1288      borderBottom = '<div class="qtip-borderBottom" dir="ltr" style="height:'+radius+'px; ' +
1289         'margin-left:'+radius+'px; line-height:0.1px; font-size:1px; padding:0;">' +
1290         containers['bottomLeft'] + containers['bottomRight'] + betweenCorners;
1291      self.elements.wrapper.append(borderBottom);
1292
1293      // Draw the borders if canvas were used (Delayed til after DOM creation)
1294      if($('<canvas>').get(0).getContext)
1295      {
1296         self.elements.wrapper.find('canvas').each(function()
1297         {
1298            borderCoord = coordinates[ $(this).parent('[rel]:first').attr('rel') ];
1299            drawBorder.call(self, $(this), borderCoord, radius, color);
1300         })
1301      }
1302
1303      // Create a phantom VML element (IE won't show the last created VML element otherwise)
1304      else if(BrowserDetect.browser == 'Explorer') self.elements.tooltip.append('<v:image style="behavior:url(#default#VML);"></v:image>');
1305
1306      // Setup contentWrapper border
1307      sideWidth = Math.max(radius, (radius + (width - radius)) )
1308      vertWidth = Math.max(width - radius, 0);
1309      self.elements.contentWrapper.css({
1310         border: '0px solid ' + color,
1311         borderWidth: vertWidth + 'px ' + sideWidth + 'px'
1312      })
1313   };
1314
1315   // Border canvas draw method
1316   function drawBorder(canvas, coordinates, radius, color)
1317   {
1318      // Create corner
1319      var context = canvas.get(0).getContext('2d');
1320      context.fillStyle = color;
1321      context.beginPath();
1322      context.arc(coordinates[0], coordinates[1], radius, 0, Math.PI * 2, false);
1323      context.fill();
1324   };
1325
1326   // Create tip using canvas and VML
1327   function createTip(corner)
1328   {
1329      var self, color, coordinates, coordsize, path;
1330      self = this;
1331
1332      // Destroy previous tip, if there is one
1333      if(self.elements.tip !== null) self.elements.tip.remove();
1334
1335      // Setup color and corner values
1336      color = self.options.style.tip.color || self.options.style.border.color;
1337      if(self.options.style.tip.corner === false) return;
1338      else if(!corner) corner = self.options.style.tip.corner;
1339
1340      // Calculate tip coordinates
1341      coordinates = calculateTip(corner, self.options.style.tip.size.width, self.options.style.tip.size.height);
1342
1343      // Create tip element
1344      self.elements.tip =  '<div class="'+self.options.style.classes.tip+'" dir="ltr" rel="'+corner+'" style="position:absolute; ' +
1345         'height:'+self.options.style.tip.size.height+'px; width:'+self.options.style.tip.size.width+'px; ' +
1346         'margin:0 auto; line-height:0.1px; font-size:1px;">';
1347
1348      // Use canvas element if supported
1349      if($('<canvas>').get(0).getContext)
1350          self.elements.tip += '<canvas height="'+self.options.style.tip.size.height+'" width="'+self.options.style.tip.size.width+'"></canvas>';
1351
1352      // Canvas not supported - Use VML (IE)
1353      else if(BrowserDetect.browser == 'Explorer')
1354      {
1355         // Create coordize and tip path using tip coordinates
1356         coordsize = self.options.style.tip.size.width + ',' + self.options.style.tip.size.height;
1357         path = 'm' + coordinates[0][0] + ',' + coordinates[0][1];
1358         path += ' l' + coordinates[1][0] + ',' + coordinates[1][1];
1359         path += ' ' + coordinates[2][0] + ',' + coordinates[2][1];
1360         path += ' xe';
1361
1362         // Create VML element
1363         self.elements.tip += '<v:shape fillcolor="'+color+'" stroked="false" filled="true" path="'+path+'" coordsize="'+coordsize+'" ' +
1364            'style="width:'+self.options.style.tip.size.width+'px; height:'+self.options.style.tip.size.height+'px; ' +
1365            'line-height:0.1px; display:inline-block; behavior:url(#default#VML); ' +
1366            'vertical-align:'+((corner.search(/top/) !== -1) ? 'bottom' : 'top')+'"></v:shape>';
1367
1368         // Create a phantom VML element (IE won't show the last created VML element otherwise)
1369         self.elements.tip += '<v:image style="behavior:url(#default#VML);"></v:image>';
1370
1371         // Prevent tooltip appearing above the content (IE z-index bug)
1372         self.elements.contentWrapper.css('position', 'relative');
1373      };
1374
1375      // Attach new tip to tooltip element
1376      self.elements.tooltip.prepend(self.elements.tip + '</div>');
1377
1378      // Create element reference and draw the canvas tip (Delayed til after DOM creation)
1379      self.elements.tip = self.elements.tooltip.find('.'+self.options.style.classes.tip).eq(0);
1380      if($('<canvas>').get(0).getContext)
1381         drawTip.call(self, self.elements.tip.find('canvas:first'), coordinates, color);
1382
1383      // Fix IE small tip bug
1384      if(corner.search(/top/) !== -1 && BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6)
1385         self.elements.tip.css({ marginTop: -4 });
1386
1387      // Set the tip position
1388      positionTip.call(self, corner);
1389   };
1390
1391   // Canvas tip drawing method
1392   function drawTip(canvas, coordinates, color)
1393   {
1394      // Setup properties
1395      var context = canvas.get(0).getContext('2d');
1396      context.fillStyle = color;
1397
1398      // Create tip
1399      context.beginPath();
1400      context.moveTo(coordinates[0][0], coordinates[0][1]);
1401      context.lineTo(coordinates[1][0], coordinates[1][1]);
1402      context.lineTo(coordinates[2][0], coordinates[2][1]);
1403      context.fill();
1404   };
1405
1406   function positionTip(corner)
1407   {
1408      var self, ieAdjust, paddingCorner, paddingSize, newMargin;
1409      self = this;
1410
1411      // Return if tips are disabled or tip is not yet rendered
1412      if(self.options.style.tip.corner === false || !self.elements.tip) return;
1413      if(!corner) corner = self.elements.tip.attr('rel');
1414
1415      // Setup adjustment variables
1416      ieAdjust = positionAdjust = (BrowserDetect.browser == 'Explorer') ? 1 : 0;
1417
1418      // Set initial position
1419      self.elements.tip.css(corner.match(/left|right|top|bottom/)[0], 0);
1420
1421      // Set position of tip to correct side
1422      if(corner.search(/top|bottom/) !== -1)
1423      {
1424         // Adjustments for IE6 - 0.5px border gap bug
1425         if(BrowserDetect.browser == 'Explorer')
1426         {
1427            if(BrowserDetect.version == 6)
1428               positionAdjust = (corner.search(/top/) !== -1) ? -3 : 1;
1429            else
1430               positionAdjust = (corner.search(/top/) !== -1) ? 1 : 2;
1431         };
1432
1433         if(corner.search(/Middle/) !== -1)
1434            self.elements.tip.css({ left: '50%', marginLeft: -(self.options.style.tip.size.width / 2) });
1435
1436         else if(corner.search(/Left/) !== -1)
1437            self.elements.tip.css({ left: self.options.style.border.radius - ieAdjust });
1438
1439         else if(corner.search(/Right/) !== -1)
1440            self.elements.tip.css({ right: self.options.style.border.radius + ieAdjust });
1441
1442         if(corner.search(/top/) !== -1)
1443            self.elements.tip.css({ top: -positionAdjust });
1444         else
1445            self.elements.tip.css({ bottom: positionAdjust });
1446
1447      }
1448      else if(corner.search(/left|right/) !== -1)
1449      {
1450         // Adjustments for IE6 - 0.5px border gap bug
1451         if(BrowserDetect.browser == 'Explorer')
1452            positionAdjust = (BrowserDetect.version == 6) ? 1 : ((corner.search(/left/) !== -1) ? 1 : 2);
1453
1454         if(corner.search(/Middle/) !== -1)
1455            self.elements.tip.css({ top: '50%', marginTop: -(self.options.style.tip.size.height / 2) });
1456
1457         else if(corner.search(/Top/) !== -1)
1458            self.elements.tip.css({ top: self.options.style.border.radiu
1458s - ieAdjust });
1459
1460         else if(corner.search(/Bottom/) !== -1)
1461            self.elements.tip.css({ bottom: self.options.style.border.radius + ieAdjust });
1462
1463         if(corner.search(/left/) !== -1)
1464            self.elements.tip.css({ left: -positionAdjust });
1465         else
1466            self.elements.tip.css({ right: positionAdjust });
1467      };
1468
1469      // Adjust tooltip padding to compensate for tip
1470      paddingCorner = 'padding-' + corner.match(/left|right|top|bottom/)[0];
1471      paddingSize = self.options.style.tip.size[ (paddingCorner.search(/left|right/) !== -1) ? 'width' : 'height' ];
1472      self.elements.tooltip.css('padding', 0);
1473      self.elements.tooltip.css(paddingCorner, paddingSize);
1474
1475      // Match content margin to prevent gap bug in IE6 ONLY
1476      if(BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6)
1477      {
1478         newMargin = parseInt(self.elements.tip.css('margin-top')) || 0;
1479         newMargin += parseInt(self.elements.content.css('margin-top')) || 0;
1480
1481         self.elements.tip.css({ marginTop: newMargin });
1482      };
1483   };
1484
1485   // Create title bar for content
1486   function createTitle()
1487   {
1488      var self = this;
1489
1490      // Destroy previous title element, if present
1491      if(self.elements.title !== null) self.elements.title.remove();
1492
1493      // Create title element
1494      self.elements.title = $('<div class="'+self.options.style.classes.title+'">')
1495         .css( jQueryStyle(self.options.style.title, true) )
1496         .css({ zoom: (BrowserDetect.browser == 'Explorer') ? 1 : 0 })
1497         .prependTo(self.elements.contentWrapper);
1498
1499      // Update title with contents if enabled
1500      if(self.options.content.title.text) self.updateTitle.call(self, self.options.content.title.text);
1501
1502      // Create title close buttons if enabled
1503      if(self.options.content.title.button !== false
1504      && typeof self.options.content.title.button == 'string')
1505      {
1506         self.elements.button = $('<a class="'+self.options.style.classes.button+'" style="float:right; position: relative"></a>')
1507            .css( jQueryStyle(self.options.style.button, true) )
1508            .html(self.options.content.title.button)
1509            .prependTo(self.elements.title)
1510            .click(function(event){ if(!self.status.disabled) self.hide(event) });
1511      };
1512   };
1513
1514   // Assign hide and show events
1515   function assignEvents()
1516   {
1517      var self, showTarget, hideTarget, inactiveEvents;
1518      self = this;
1519
1520      // Setup event target variables
1521      showTarget = self.options.show.when.target;
1522      hideTarget = self.options.hide.when.target;
1523
1524      // Add tooltip as a hideTarget is its fixed
1525      if(self.options.hide.fixed) hideTarget = hideTarget.add(self.elements.tooltip);
1526
1527      // Check if the hide event is special 'inactive' type
1528      if(self.options.hide.when.event == 'inactive')
1529      {
1530         // Define events which reset the 'inactive' event handler
1531         inactiveEvents = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove',
1532         'mouseout', 'mouseenter', 'mouseleave', 'mouseover' ];
1533
1534         // Define 'inactive' event timer method
1535         function inactiveMethod(event)
1536         {
1537            if(self.status.disabled === true) return;
1538
1539            //Clear and reset the timer
1540            clearTimeout(self.timers.inactive);
1541            self.timers.inactive = setTimeout(function()
1542            {
1543               // Unassign 'inactive' events
1544               $(inactiveEvents).each(function()
1545               {
1546                  hideTarget.unbind(this+'.qtip-inactive');
1547                  self.elements.content.unbind(this+'.qtip-inactive');
1548               });
1549
1550               // Hide the tooltip
1551               self.hide(event);
1552            }
1553            , self.options.hide.delay);
1554         };
1555      }
1556
1557      // Check if the tooltip is 'fixed'
1558      else if(self.options.hide.fixed === true)
1559      {
1560         self.elements.tooltip.bind('mouseover.qtip', function()
1561         {
1562            if(self.status.disabled === true) return;
1563
1564            // Reset the hide timer
1565            clearTimeout(self.timers.hide);
1566         });
1567      };
1568
1569      // Define show event method
1570      function showMethod(event)
1571      {
1572         if(self.status.disabled === true) return;
1573
1574         // If set, hide tooltip when inactive for delay period
1575         if(self.options.hide.when.event == 'inactive')
1576         {
1577            // Assign each reset event
1578            $(inactiveEvents).each(function()
1579            {
1580               hideTarget.bind(this+'.qtip-inactive', inactiveMethod);
1581               self.elements.content.bind(this+'.qtip-inactive', inactiveMethod);
1582            });
1583
1584            // Start the inactive timer
1585            inactiveMethod();
1586         };
1587
1588         // Clear hide timers
1589         clearTimeout(self.timers.show);
1590         clearTimeout(self.timers.hide);
1591
1592         // Start show timer
1593         self.timers.show = setTimeout(function(){ self.show(event); }, self.options.show.delay);
1594      };
1595
1596      // Define hide event method
1597      function hideMethod(event)
1598      {
1599         if(self.status.disabled === true) return;
1600
1601         // Prevent hiding if tooltip is fixed and event target is the tooltip
1602         if(self.options.hide.fixed === true
1603         && self.options.hide.when.event.search(/mouse(out|leave)/i) !== -1
1604         && $(event.relatedTarget).parents('div.qtip[qtip]').length > 0)
1605         {
1606            // Prevent default and popagation
1607            event.stopPropagation();
1608            event.preventDefault();
1609
1610            // Reset the hide timer
1611            clearTimeout(self.timers.hide);
1612            return false;
1613         };
1614
1615         // Clear timers and stop animation queue
1616         clearTimeout(self.timers.show);
1617         clearTimeout(self.timers.hide);
1618         self.elements.tooltip.stop(true, true);
1619
1620         // If tooltip has displayed, start hide timer
1621         self.timers.hide = setTimeout(function(){ self.hide(event); }, self.options.hide.delay);
1622      };
1623
1624      // Both events and targets are identical, apply events using a toggle
1625      if((self.options.show.when.target.add(self.options.hide.when.target).length === 1
1626      && self.options.show.when.event == self.options.hide.when.event
1627      && self.options.hide.when.event !== 'inactive')
1628      || self.options.hide.when.event == 'unfocus')
1629      {
1630         self.cache.toggle = 0;
1631         // Use a toggle to prevent hide/show conflicts
1632         showTarget.bind(self.options.show.when.event + '.qtip', function(event)
1633         {
1634            if(self.cache.toggle == 0) showMethod(event);
1635            else hideMethod(event);
1636         });
1637      }
1638
1639      // Events are not identical, bind normally
1640      else
1641      {
1642         showTarget.bind(self.options.show.when.event + '.qtip', showMethod);
1643
1644         // If the hide event is not 'inactive', bind the hide method
1645         if(self.options.hide.when.event !== 'inactive')
1646            hideTarget.bind(self.options.hide.when.event + '.qtip', hideMethod);
1647      };
1648
1649      // Focus the tooltip on mouseover
1650      if(self.options.position.type.search(/(fixed|absolute)/) !== -1)
1651         self.elements.tooltip.bind('mouseover.qtip', self.focus);
1652
1653      // If mouse is the target, update tooltip position on mousemove
1654      if(self.options.position.target === 'mouse' && self.options.position.type !== 'static')
1655      {
1656         showTarget.bind('mousemove.qtip', function(event)
1657         {
1658            // Set the new mouse positions if adjustment is enabled
1659            self.cache.mouse = { x: event.pageX, y: event.pageY };
1660
1661            // Update the tooltip position only if the tooltip is visible and adjustment is enabled
1662            if(self.status.disabled === false
1663            && self.options.position.adjust.mouse === true
1664            && self.options.position.type !== 'static'
1665            && self.elements.tooltip.css('display') !== 'none')
1666               self.updatePosition(event);
1667         });
1668      };
1669   };
1670
1671   // Screen position adjustment
1672   function screenAdjust(position, target, tooltip)
1673   {
1674      var self, adjustedPosition, adjust, newCorner, overflow, corner;
1675      self = this;
1676
1677      // Setup corner and adjustment variable
1678      if(tooltip.corner == 'center') return target.position // TODO: 'center' corner adjustment
1679      adjustedPosition = $.extend({}, position);
1680      newCorner = { x: false, y: false };
1681
1682      // Define overflow properties
1683      overflow = {
1684         left: (adjustedPosition.left < $.fn.qtip.cache.screen.scroll.left),
1685         right: (adjustedPosition.left + tooltip.dimensions.width + 2 >= $.fn.qtip.cache.screen.width + $.fn.qtip.cache.screen.scroll.left),
1686         top: (adjustedPosition.top < $.fn.qtip.cache.screen.scroll.top),
1687         bottom: (adjustedPosition.top + tooltip.dimensions.height + 2 >= $.fn.qtip.cache.screen.height + $.fn.qtip.cache.screen.scroll.top)
1688      };
1689
1690      // Determine new positioning properties
1691      adjust = {
1692         left: (overflow.left && (tooltip.corner.search(/right/i) != -1 || (tooltip.corner.search(/right/i) == -1 && !overflow.right))),
1693         right: (overflow.right && (tooltip.corner.search(/left/i) != -1 || (tooltip.corner.search(/left/i) == -1 && !overflow.left))),
1694         top: (overflow.top && tooltip.corner.search(/top/i) == -1),
1695         bottom: (overflow.bottom && tooltip.corner.search(/bottom/i) == -1)
1696      };
1697
1698      // Tooltip overflows off the left side of the screen
1699      if(adjust.left)
1700      {
1701         if(self.options.position.target !== 'mouse')
1702            adjustedPosition.left = target.position.left + target.dimensions.width;
1703         else
1704            adjustedPosition.left = self.cache.mouse.x
1705
1706         newCorner.x = 'Left';
1707      }
1708
1709      // Tooltip overflows off the right side of the screen
1710      else if(adjust.right)
1711      {
1712         if(self.options.position.target !== 'mouse')
1713            adjustedPosition.left = target.position.left - tooltip.dimensions.width;
1714         else
1715            adjustedPosition.left = self.cache.mouse.x - tooltip.dimensions.width;
1716
1717         newCorner.x = 'Right';
1718      };
1719
1720      // Tooltip overflows off the top of the screen
1721      if(adjust.top)
1722      {
1723         if(self.options.position.target !== 'mouse')
1724            adjustedPosition.top = target.position.top + target.dimensions.height;
1725         else
1726            adjustedPosition.top = self.cache.mouse.y
1727
1728         newCorner.y = 'top';
1729      }
1730
1731      // Tooltip overflows off the bottom of the screen
1732      else if(adjust.bottom)
1733      {
1734         if(self.options.position.target !== 'mouse')
1735            adjustedPosition.top = target.position.top - tooltip.dimensions.height;
1736         else
1737            adjustedPosition.top = self.cache.mouse.y - tooltip.dimensions.height;
1738
1739         newCorner.y = 'bottom';
1740      };
1741
1742      // Don't adjust if resulting position is negative
1743      if(adjustedPosition.left < 0)
1744      {
1745         adjustedPosition.left = position.left;
1746         newCorner.x = false;
1747      };
1748      if(adjustedPosition.top < 0)
1749      {
1750         adjustedPosition.top = position.top;
1751         newCorner.y = false;
1752      };
1753
1754      // Change tip corner if positioning has changed and tips are enabled
1755      if(self.options.style.tip.corner !== false)
1756      {
1757         // Determine new corner properties
1758         adjustedPosition.corner = new String(tooltip.corner);
1759         if(newCorner.x !== false) adjustedPosition.corner = adjustedPosition.corner.replace(/Left|Right|Middle/, newCorner.x);
1760         if(newCorner.y !== false) adjustedPosition.corner = adjustedPosition.corner.replace(/top|bottom/, newCorner.y);
1761
1762         // Adjust tip if position has changed and tips are enabled
1763         if(adjustedPosition.corner !== self.elements.tip.attr('rel'))
1764            createTip.call(self, adjustedPosition.corner);
1765      };
1766
1767      return adjustedPosition;
1768   };
1769
1770   // Build a jQuery style object from supplied style object
1771   function jQueryStyle(style, sub)
1772   {
1773      var styleObj, i;
1774
1775      styleObj = $.extend(true, {}, style);
1776      for(i in styleObj)
1777      {
1778         if(sub === true && i.search(/(tip|classes)/i) !== -1)
1779            delete styleObj[i];
1780         else if(!sub && i.search(/(width|border|tip|title|classes|user)/i) !== -1)
1781            delete styleObj[i];
1782      };
1783
1784      return styleObj;
1785   };
1786
1787   // Sanitize styles
1788   function sanitizeStyle(style)
1789   {
1790      if(typeof style.tip !== 'object') style.tip = { corner: style.tip };
1791      if(typeof style.tip.size !== 'object') style.tip.size = { width: style.tip.size, height: style.tip.size };
1792      if(typeof style.border !== 'object') style.border = { width: style.border };
1793      if(typeof style.width !== 'object') style.width = { value: style.width };
1794      if(typeof style.width.max == 'string') style.width.max = parseInt(style.width.max.replace(/([0-9]+)/i, "$1"));
1795      if(typeof style.width.min == 'string') style.width.min = parseInt(style.width.min.replace(/([0-9]+)/i, "$1"));
1796
1797      // Convert deprecated x and y tip values to width/height
1798      if(typeof style.tip.size.x == 'number')
1799      {
1800         style.tip.size.width = style.tip.size.x;
1801         delete style.tip.size.x;
1802      };
1803      if(typeof style.tip.size.y == 'number')
1804      {
1805         style.tip.size.height = style.tip.size.y;
1806         delete style.tip.size.y;
1807      };
1808
1809      return style;
1810   };
1811
1812   // Build styles recursively with inheritance
1813   function buildStyle()
1814   {
1815      var self, i, styleArray, styleExtend, finalStyle, ieAdjust;
1816      self = this;
1817
1818      // Build style options from supplied arguments
1819      styleArray = [true, {}];
1820      for(i = 0; i < arguments.length; i++)
1821         styleArray.push(arguments[i]);
1822      styleExtend = [ $.extend.apply($, styleArray) ];
1823
1824      // Loop through each named style inheritance
1825      while(typeof styleExtend[0].name == 'string')
1826      {
1827         // Sanitize style data and append to extend array
1828         styleExtend.unshift( sanitizeStyle($.fn.qtip.styles[ styleExtend[0].name ]) );
1829      };
1830
1831      // Make sure resulting tooltip className represents final style
1832      styleExtend.unshift(true, {classes:{ tooltip: 'qtip-' + (arguments[0].name || 'defaults') }}, $.fn.qtip.styles.defaults);
1833
1834      // Extend into a single style object
1835      finalStyle = $.extend.apply($, styleExtend);
1836
1837      // Adjust tip size if needed (IE 1px adjustment bug fix)
1838      ieAdjust = (BrowserDetect.browser == 'Explorer') ? 1 : 0;
1839      finalStyle.tip.size.width += ieAdjust;
1840      finalStyle.tip.size.height += ieAdjust;
1841
1842      // Force even numbers for pixel precision
1843      if(finalStyle.tip.size.width % 2 > 0) finalStyle.tip.size.width += 1;
1844      if(finalStyle.tip.size.height % 2 > 0) finalStyle.tip.size.height += 1;
1845
1846      // Sanitize final styles tip corner value
1847      if(finalStyle.tip.corner === true)
1848         finalStyle.tip.corner = (self.options.position.corner.tooltip === 'center') ? false : self.options.position.corner.tooltip;
1849
1850      return finalStyle;
1851   };
1852
1853   // Tip coordinates calculator
1854   function calculateTip(corner, width, height)
1855   {
1856      // Define tip coordinates in terms of height and width values
1857      var tips = {
1858         bottomRight:   [[0,0],              [width,height],      [width,0]],
1859         bottomLeft:    [[0,0],              [width,0],           [0,height]],
1860         topRight:      [[0,height],         [width,0],           [width,height]],
1861         topLeft:       [[0,0],              [0,height],          [width,height]],
1862         topMiddle:     [[0,height],         [width / 2,0],       [width,height]],
1863         bottomMiddle:  [[0,0],              [width,0],           [width / 2,height]],
1864         rightMiddle:   [[0,0],              [width,height / 2],  [0,height]],
1865         leftMiddle:    [[width,0],          [width,height],      [0,height / 2]]
1866      };
1867      tips.leftTop = tips.bottomRight;
1868      tips.rightTop = tips.bottomLeft;
1869      tips.leftBottom = tips.topRight;
1870      tips.rightBottom = tips.topLeft;
1871
1872      return tips[corner];
1873   };
1874
1875   // Border coordinates calculator
1876   function calculateBorders(radius)
1877   {
1878      var borders;
1879
1880      // Use canvas element if supported
1881      if($('<canvas>').get(0).getContext)
1882      {
1883         borders = {
1884            topLeft: [radius,radius], topRight: [0,radius],
1885            bottomLeft: [radius,0], bottomRight: [0,0]
1886         };
1887      }
1888
1889      // Canvas not supported - Use VML (IE)
1890      else if(BrowserDetect.browser == 'Explorer')
1891      {
1892         borders = {
1893            topLeft: [-90,90,0], topRight: [-90,90,-radius],
1894            bottomLeft: [90,270,0], bottomRight: [90, 270,-radius]
1895         };
1896      };
1897
1898      return borders;
1899   };
1900
1901   // BGIFRAME JQUERY PLUGIN ADAPTION
1902   //   Special thanks to Brandon Aaron for this plugin
1903   //   http://plugins.jquery.com/project/bgiframe
1904   function bgiframe()
1905   {
1906      var self, html, dimensions;
1907      self = this;
1908      dimensions = self.getDimensions();
1909
1910      // Setup iframe HTML string
1911      html = '<iframe class="qtip-bgiframe" frameborder="0" tabindex="-1" src="javascript:false" '+
1912         'style="display:block; position:absolute; z-index:-1; filter:alpha(opacity=\'0\'); border: 1px solid red; ' +
1913         'height:'+dimensions.height+'px; width:'+dimensions.width+'px" />';
1914
1915      // Append the new HTML and setup element reference
1916      self.elements.bgiframe = self.elements.wrapper.prepend(html).children('.qtip-bgiframe:first');
1917   };
1918
1919   // Assign cache and event initialisation on document load
1920   $(document).ready(function()
1921   {
1922      // Setup library cache with window scroll and dimensions of document
1923      $.fn.qtip.cache = {
1924         screen: {
1925            scroll: { left: $(window).scrollLeft(), top: $(window).scrollTop() },
1926            width: $(window).width(),
1927            height: $(window).height()
1928         }
1929      };
1930
1931      // Adjust positions of the tooltips on window resize or scroll if enabled
1932      var adjustTimer;
1933      $(window).bind('resize scroll', function(event)
1934      {
1935         clearTimeout(adjustTimer);
1936         adjustTimer = setTimeout(function()
1937         {
1938            // Readjust cached screen values
1939            if(event.type === 'scroll')
1940               $.fn.qtip.cache.screen.scroll = { left: $(window).scrollLeft(), top: $(window).scrollTop() };
1941            else
1942            {
1943               $.fn.qtip.cache.screen.width = $(window).width();
1944               $.fn.qtip.cache.screen.height = $(window).height();
1945            };
1946
1947            for(i = 0; i < $.fn.qtip.interfaces.length; i++)
1948            {
1949               // Access current elements API
1950               var api = $.fn.qtip.interfaces[i];
1951
1952               // Update position if resize or scroll adjustments are enabled
1953               if(api && api.status.rendered === true
1954               && (api.options.position.type !== 'static'
1955               || api.options.position.adjust.scroll && event.type === 'scroll'
1956               || api.options.position.adjust.resize && event.type === 'resize'))
1957               {
1958                  // Queue the animation so positions are updated correctly
1959                  api.updatePosition(event, true);
1960               }
1961            };
1962         }
1963         , 100);
1964      })
1965
1966      // Hide unfocus toolipts on document mousedown
1967      $(document).bind('mousedown.qtip', function(event)
1968      {
1969         if($(event.target).parents('div.qtip').length === 0)
1970         {
1971            $('.qtip[unfocus]').each(function()
1972            {
1973               var api = $(this).qtip("api");
1974
1975               // Only hide if its visible and not the tooltips target
1976               if($(this).is(':visible') && !api.status.disabled
1977               && $(event.target).add(api.elements.target).length > 1)
1978                  api.hide(event);
1979            })
1980         };
1981      })
1982   });
1983
1984   // Define qTip API interfaces array
1985   $.fn.qtip.interfaces = []
1986
1987   // Define log and constant place holders
1988   $.fn.qtip.log = { error: function(){ return this; } };
1989   $.fn.qtip.constants = {};
1990
1991   // Define configuration defaults
1992   $.fn.qtip.defaults = {
1993      // Content
1994      content: {
1995         prerender: false,
1996         text: false,
1997         url: false,
1998         data: null,
1999         title: {
2000            text: false,
2001            button: false
2002         }
2003      },
2004      // Position
2005      position: {
2006         target: false,
2007         corner: {
2008            target: 'bottomRight',
2009            tooltip: 'topLeft'
2010         },
2011         adjust: {
2012            x: 0, y: 0,
2013            mouse: true,
2014            screen: false,
2015            scroll: true,
2016            resize: true
2017         },
2018         type: 'absolute',
2019         container: false
2020      },
2021      // Effects
2022      show: {
2023         when: {
2024            target: false,
2025            event: 'mouseover'
2026         },
2027         effect: {
2028            type: 'fade',
2029            length: 100
2030         },
2031         delay: 140,
2032         solo: false,
2033         ready: false
2034      },
2035      hide: {
2036         when: {
2037            target: false,
2038            event: 'mouseout'
2039         },
2040         effect: {
2041            type: 'fade',
2042            length: 100
2043         },
2044         delay: 0,
2045         fixed: false
2046      },
2047      // Callbacks
2048      api: {
2049         beforeRender: function(){},
2050         onRender: function(){},
2051         beforePositionUpdate: function(){},
2052         onPositionUpdate: function(){},
2053         beforeShow: function(){},
2054         onShow: function(){},
2055         beforeHide: function(){},
2056         onHide: function(){},
2057         beforeContentUpdate: function(){},
2058         onContentUpdate: function(){},
2059         beforeContentLoad: function(){},
2060         onContentLoad: function(){},
2061         beforeTitleUpdate: function(){},
2062         onTitleUpdate: function(){},
2063         beforeDestroy: function(){},
2064         onDestroy: function(){},
2065         beforeFocus: function(){},
2066         onFocus: function(){}
2067      }
2068   };
2069
2070   $.fn.qtip.styles = {
2071      defaults: {
2072         background: 'white',
2073         color: '#111',
2074         overflow: 'hidden',
2075         textAlign: 'left',
2076         width: {
2077            min: 0,
2078            max: 250
2079         },
2080         padding: '5px 9px',
2081         border: {
2082            width: 1,
2083            radius: 0,
2084            color: '#d3d3d3'
2085         },
2086         tip: {
2087            corner: false,
2088            color: false,
2089            size: { width: 13, height: 13 },
2090            opacity: 1
2091         },
2092         title: {
2093            background: '#e1e1e1',
2094            fontWeight: 'bold',
2095            padding: '7px 12px'
2096         },
2097         button: {
2098            cursor: 'pointer'
2099         },
2100         classes: {
2101            target: '',
2102            tip: 'qtip-tip',
2103            title: 'qtip-title',
2104            button: 'qtip-button',
2105            content: 'qtip-content',
2106            active: 'qtip-active'
2107         }
2108      },
2109      cream: {
2110         border: {
2111            width: 3,
2112            radius: 0,
2113            color: '#F9E98E'
2114         },
2115         title: {
2116            background: '#F0DE7D',
2117            color: '#A27D35'
2118         },
2119         background: '#FBF7AA',
2120         color: '#A27D35',
2121
2122         classes: { tooltip: 'qtip-cream' }
2123      },
2124      light: {
2125         border: {
2126            width: 3,
2127            radius: 0,
2128            color: '#E2E2E2'
2129         },
2130         title: {
2131            background: '#f1f1f1',
2132            color: '#454545'
2133         },
2134         background: 'white',
2135         color: '#454545',
2136
2137         classes: { tooltip: 'qtip-light' }
2138      },
2139      dark: {
2140         border: {
2141            width: 3,
2142            radius: 0,
2143            color: '#303030'
2144         },
2145         title: {
2146            background: '#404040',
2147            color: '#f3f3f3'
2148         },
2149         background: '#505050',
2150         color: '#f3f3f3',
2151
2152         classes: { tooltip: 'qtip-dark' }
2153      },
2154      red: {
2155         border: {
2156            width: 3,
2157            radius: 0,
2158            color: '#CE6F6F'
2159         },
2160         title: {
2161            background: '#f28279',
2162            color: '#9C2F2F'
2163         },
2164         background: '#F79992',
2165         color: '#9C2F2F',
2166
2167         classes: { tooltip: 'qtip-red' }
2168      },
2169      green: {
2170         border: {
2171            width: 3,
2172            radius: 0,
2173            color: '#A9DB66'
2174         },
2175         title: {
2176            background: '#b9db8c',
2177            color: '#58792E'
2178         },
2179         background: '#CDE6AC',
2180         color: '#58792E',
2181
2182         classes: { tooltip: 'qtip-green' }
2183      },
2184      blue: {
2185         border: {
2186            width: 3,
2187            radius: 0,
2188            color: '#ADD9ED'
2189         },
2190         title: {
2191            background: '#D0E9F5',
2192            color: '#5E99BD'
2193         },
2194         background: '#E5F6FE',
2195         color: '#4D9FBF',
2196
2197         classes: { tooltip: 'qtip-blue' }
2198      }
2199   };
2200})(jQuery);

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.