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