Guest User

Untitled

a guest
Feb 16th, 2012
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 83.29 KB | None | 0 0
  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 !== undefined)
  1903. {
  1904. if(api.status.rendered === true
  1905. && (api.options.position.type !== 'static'
  1906. || api.options.position.adjust.scroll && event.type === 'scroll'
  1907. || api.options.position.adjust.resize && event.type === 'resize'))
  1908. {
  1909. // Queue the animation so positions are updated correctly
  1910. api.updatePosition(event, true);
  1911. }
  1912. }
  1913. };
  1914. }
  1915. , 100);
  1916. })
  1917.  
  1918. // Hide unfocus toolipts on document mousedown
  1919. $(document).bind('mousedown.qtip', function(event)
  1920. {
  1921. if($(event.target).parents('div.qtip').length === 0)
  1922. {
  1923. $('.qtip[unfocus]').each(function()
  1924. {
  1925. var api = $(this).qtip("api");
  1926.  
  1927. // Only hide if its visible and not the tooltips target
  1928. if($(this).is(':visible') && !api.status.disabled
  1929. && $(event.target).add(api.elements.target).length > 1)
  1930. api.hide(event);
  1931. })
  1932. };
  1933. })
  1934. });
  1935.  
  1936. // Define qTip API interfaces array
  1937. $.fn.qtip.interfaces = []
  1938.  
  1939. // Define log and constant place holders
  1940. $.fn.qtip.log = { error: function(){ return this; } };
  1941. $.fn.qtip.constants = {};
  1942.  
  1943. // Define configuration defaults
  1944. $.fn.qtip.defaults = {
  1945. // Content
  1946. content: {
  1947. prerender: false,
  1948. text: false,
  1949. url: false,
  1950. data: null,
  1951. title: {
  1952. text: false,
  1953. button: false
  1954. }
  1955. },
  1956. // Position
  1957. position: {
  1958. target: false,
  1959. corner: {
  1960. target: 'bottomRight',
  1961. tooltip: 'topLeft'
  1962. },
  1963. adjust: {
  1964. x: 0, y: 0,
  1965. mouse: true,
  1966. screen: false,
  1967. scroll: true,
  1968. resize: true
  1969. },
  1970. type: 'absolute',
  1971. container: false
  1972. },
  1973. // Effects
  1974. show: {
  1975. when: {
  1976. target: false,
  1977. event: 'mouseover'
  1978. },
  1979. effect: {
  1980. type: 'fade',
  1981. length: 100
  1982. },
  1983. delay: 140,
  1984. solo: false,
  1985. ready: false
  1986. },
  1987. hide: {
  1988. when: {
  1989. target: false,
  1990. event: 'mouseout'
  1991. },
  1992. effect: {
  1993. type: 'fade',
  1994. length: 100
  1995. },
  1996. delay: 0,
  1997. fixed: false
  1998. },
  1999. // Callbacks
  2000. api: {
  2001. beforeRender: function(){},
  2002. onRender: function(){},
  2003. beforePositionUpdate: function(){},
  2004. onPositionUpdate: function(){},
  2005. beforeShow: function(){},
  2006. onShow: function(){},
  2007. beforeHide: function(){},
  2008. onHide: function(){},
  2009. beforeContentUpdate: function(){},
  2010. onContentUpdate: function(){},
  2011. beforeContentLoad: function(){},
  2012. onContentLoad: function(){},
  2013. beforeTitleUpdate: function(){},
  2014. onTitleUpdate: function(){},
  2015. beforeDestroy: function(){},
  2016. onDestroy: function(){},
  2017. beforeFocus: function(){},
  2018. onFocus: function(){}
  2019. }
  2020. };
  2021.  
  2022. $.fn.qtip.styles = {
  2023. defaults: {
  2024. background: 'white',
  2025. color: '#111',
  2026. overflow: 'hidden',
  2027. textAlign: 'left',
  2028. width: {
  2029. min: 0,
  2030. max: 250
  2031. },
  2032. padding: '5px 9px',
  2033. border: {
  2034. width: 1,
  2035. radius: 0,
  2036. color: '#d3d3d3'
  2037. },
  2038. tip: {
  2039. corner: false,
  2040. color: false,
  2041. size: { width: 13, height: 13 },
  2042. opacity: 1
  2043. },
  2044. title: {
  2045. background: '#e1e1e1',
  2046. fontWeight: 'bold',
  2047. padding: '7px 12px'
  2048. },
  2049. button: {
  2050. cursor: 'pointer'
  2051. },
  2052. classes: {
  2053. target: '',
  2054. tip: 'qtip-tip',
  2055. title: 'qtip-title',
  2056. button: 'qtip-button',
  2057. content: 'qtip-content',
  2058. active: 'qtip-active'
  2059. }
  2060. },
  2061. cream: {
  2062. border: {
  2063. width: 3,
  2064. radius: 0,
  2065. color: '#F9E98E'
  2066. },
  2067. title: {
  2068. background: '#F0DE7D',
  2069. color: '#A27D35'
  2070. },
  2071. background: '#FBF7AA',
  2072. color: '#A27D35',
  2073.  
  2074. classes: { tooltip: 'qtip-cream' }
  2075. },
  2076. light: {
  2077. border: {
  2078. width: 3,
  2079. radius: 0,
  2080. color: '#E2E2E2'
  2081. },
  2082. title: {
  2083. background: '#f1f1f1',
  2084. color: '#454545'
  2085. },
  2086. background: 'white',
  2087. color: '#454545',
  2088.  
  2089. classes: { tooltip: 'qtip-light' }
  2090. },
  2091. dark: {
  2092. border: {
  2093. width: 3,
  2094. radius: 0,
  2095. color: '#303030'
  2096. },
  2097. title: {
  2098. background: '#404040',
  2099. color: '#f3f3f3'
  2100. },
  2101. background: '#505050',
  2102. color: '#f3f3f3',
  2103.  
  2104. classes: { tooltip: 'qtip-dark' }
  2105. },
  2106. red: {
  2107. border: {
  2108. width: 3,
  2109. radius: 0,
  2110. color: '#CE6F6F'
  2111. },
  2112. title: {
  2113. background: '#f28279',
  2114. color: '#9C2F2F'
  2115. },
  2116. background: '#F79992',
  2117. color: '#9C2F2F',
  2118.  
  2119. classes: { tooltip: 'qtip-red' }
  2120. },
  2121. green: {
  2122. border: {
  2123. width: 3,
  2124. radius: 0,
  2125. color: '#A9DB66'
  2126. },
  2127. title: {
  2128. background: '#b9db8c',
  2129. color: '#58792E'
  2130. },
  2131. background: '#CDE6AC',
  2132. color: '#58792E',
  2133.  
  2134. classes: { tooltip: 'qtip-green' }
  2135. },
  2136. blue: {
  2137. border: {
  2138. width: 3,
  2139. radius: 0,
  2140. color: '#ADD9ED'
  2141. },
  2142. title: {
  2143. background: '#D0E9F5',
  2144. color: '#5E99BD'
  2145. },
  2146. background: '#E5F6FE',
  2147. color: '#4D9FBF',
  2148.  
  2149. classes: { tooltip: 'qtip-blue' }
  2150. }
  2151. };
  2152. })(jQuery);
Add Comment
Please, Sign In to add comment