jquery-qtip-wrapper-rails 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0d8b1af1c9db9dd156f3cbf8d93914eb9b11e247
4
+ data.tar.gz: ef58bbd6c75fb54a5ee328e2a9cfa43d58a7be0e
5
+ SHA512:
6
+ metadata.gz: a81cbb1213db25bab4fb7e49e8bd2651160a9a3e867a6df22842f216a434d4b5eda4c68d86ce17d4fb981e4a9a33e32b9f311618a733db70e0d1c375d27edc2f
7
+ data.tar.gz: 54fcd991857c9ff9e48409220ac804ade9738ec4640ff772cb2d7227518fb1296c68c8c5567e2ccbb25c078e5271f30b00f780d7dac402188ddb8f8cafc55715
@@ -0,0 +1,12 @@
1
+ require "jquery/qtip/wrapper/rails/version"
2
+
3
+ module Jquery
4
+ module Qtip
5
+ module Wrapper
6
+ module Rails
7
+ class Engine < ::Rails::Engine
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module Jquery
2
+ module Qtip
3
+ module Wrapper
4
+ module Rails
5
+ VERSION = "1.0.1"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ Installing qTip is relatively straight forward. Simply include the both the jQuery and qTip library files e.g. jquery-qtip-1.0.0.min.js using script html element(s), like so:
2
+
3
+ <script type="text/javascript" src="/projects/qtip/js/jquery.1.3.2.min.js"></script>
4
+ <script type="text/javascript" src="/projects/qtip/js/jquery.qtip-1.0.0.min.js"></script>
5
+
6
+ Notice the jQuery library is included before qTip. This is _absolutely essential_ for correct functioning of the library and its accompanying methods.
7
+
8
+ It is HIGHLY RECOMMENDED that all JavaScript includes like the one above be placed after all your content at the footer of the page, just before the end </body> tag. This ensures that all content is loaded before manipulation of the DOM occurs.
@@ -0,0 +1,7 @@
1
+ Copyright © 2009 Craig Thompson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,5 @@
1
+ JQUERY IS REQUIRED FOR USE OF THIS PLUGIN
2
+
3
+ You can grab the latest version here: http://jquery.com
4
+
5
+ We recommend version 1.3 due to its significant speed increases in several areas, but qTip supports the use of jQuery version 1.2.6 and above for all you who have decided not to upgrade.
@@ -0,0 +1,2155 @@
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);
2150
+
2151
+
2152
+
2153
+
2154
+
2155
+