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