jquery.qtip-1.0.0-rc3.js 83KB

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