PageSourceSearch

https://sdg6data.org/themes/custom/unwater/js/jquery.tooltipster.js?tligis

js sdg6data.org collected 2026-10-02 04:10:03 UTC 47,327 bytes, 1,282 lines download raw bytes

1/*
2
3Tooltipster 3.2.6 | 2014-07-16
4A rockin' custom tooltip jQuery plugin
5
6Developed by Caleb Jacob under the MIT license http://opensource.org/licenses/MIT
7
8THE 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.
9
10*/
11
12;(function ($, window, document) {
13
14	var pluginName = "tooltipster",
15		defaults = {
16			animation: 'fade',
17			arrow: true,
18			arrowColor: '',
19			autoClose: true,
20			content: null,
21			contentAsHTML: false,
22			contentCloning: true,
23			debug: true,
24			delay: 200,
25			minWidth: 0,
26			maxWidth: null,
27			functionInit: function(origin, content) {},
28			functionBefore: function(origin, continueTooltip) {
29				continueTooltip();
30			},
31			functionReady: function(origin, tooltip) {},
32			functionAfter: function(origin) {},
33			icon: '(?)',
34			iconCloning: true,
35			iconDesktop: false,
36			iconTouch: false,
37			iconTheme: 'tooltipster-icon',
38			interactive: false,
39			interactiveTolerance: 350,
40			multiple: false,
41			offsetX: 0,
42			offsetY: 0,
43			onlyOne: false,
44			position: 'top',
45			positionTracker: false,
46			speed: 350,
47			timer: 0,
48			theme: 'tooltipster-default',
49			touchDevices: true,
50			trigger: 'hover',
51			updateAnimation: true
52		};
53	
54	function Plugin(element, options) {
55		
56		// list of instance variables
57		
58		this.bodyOverflowX;
59		// stack of custom callbacks provided as parameters to API methods
60		this.callbacks = {
61			hide: [],
62			show: []
63		};
64		this.checkInterval = null;
65		// this will be the user content shown in the tooltip. A capital "C" is used because there is also a method called content()
66		this.Content;
67		// this is the original element which is being applied the tooltipster plugin
68		this.$el = $(element);
69		// this will be the element which triggers the appearance of the tooltip on hover/click/custom events.
70		// it will be the same as this.$el if icons are not used (see in the options), otherwise it will correspond to the created icon
71		this.$elProxy;
72		this.elProxyPosition;
73		this.enabled = true;
74		this.options = $.extend({}, defaults, options);
75		this.mouseIsOverProxy = false;
76		// a unique namespace per instance, for easy selective unbinding
77		this.namespace = 'tooltipster-'+ Math.round(Math.random()*100000);
78		// Status (capital S) can be either : appearing, shown, disappearing, hidden
79		this.Status = 'hidden';
80		this.timerHide = null;
81		this.timerShow = null;
82		// this will be the tooltip element (jQuery wrapped HTML element)
83		this.$tooltip;
84		
85		// for backward compatibility
86		this.options.iconTheme = this.options.iconTheme.replace('.', '');
87		this.options.theme = this.options.theme.replace('.', '');
88		
89		// launch
90		
91		this._init();
92	}
93	
94	Plugin.prototype = {
95		
96		_init: function() {
97			
98			var self = this;
99			
100			// disable the plugin on old browsers (including IE7 and lower)
101			if (document.querySelector) {
102				
103				// note : the content is null (empty) by default and can stay that way if the plugin remains initialized but not fed any content. The tooltip will just not appear.
104				
105				// if content is provided in the options, its has precedence over the title attribute. Remark : an empty string is considered content, only 'null' represents the absence of content.
106				if (self.options.content !== null){
107					self._content_set(self.options.content);
108				}
109				else {
110					// the same remark as above applies : empty strings (like title="") are considered content and will be shown. Do not define any attribute at all if you want to initialize the plugin without content at start.
111					var t = self.$el.attr('title');
112					if(typeof t === 'undefined') t = null;
113					
114					self._content_set(t);
115				}
116				
117				var c = self.options.functionInit.call(self.$el, self.$el, self.Content);
118				if(typeof c !== 'undefined') self._content_set(c);
119				
120				self.$el
121					// strip the title off of the element to prevent the default tooltips from popping up
122					.removeAttr('title')
123					// to be able to find all instances on the page later (upon window events in particular)
124					.addClass('tooltipstered');
125
126				// detect if we're changing the tooltip origin to an icon
127				// note about this condition : if the device has touch capability and self.options.iconTouch is false, you'll have no icons event though you may consider your device as a desktop if it also has a mouse. Not sure why someone would have this use case though.
128				if ((!deviceHasTouchCapability && self.options.iconDesktop) || (deviceHasTouchCapability && self.options.iconTouch)) {
129					
130					// TODO : the tooltip should be automatically be given an absolute position to be near the origin. Otherwise, when the origin 
130is floating or what, it's going to be nowhere near it and disturb the position flow of the page elements. It will imply that the icon also detects when its origin moves, to follow it : not trivial.
131					// Until it's done, the icon feature does not really make sense since the user still has most of the work to do by himself
132					
133					// if the icon provided is in the form of a string
134					if(typeof self.options.icon === 'string'){
135						// wrap it in a span with the icon class
136						self.$elProxy = $('<span class="'+ self.options.iconTheme +'"></span>');
137						self.$elProxy.text(self.options.icon);
138					}
139					// if it is an object (sensible choice)
140					else {
141						// (deep) clone the object if iconCloning == true, to make sure every instance has its own proxy. We use the icon without wrapping, no need to. We do not give it a class either, as the user will undoubtedly style the object on his own and since our css properties may conflict with his own
142						if (self.options.iconCloning) self.$elProxy = self.options.icon.clone(true);
143						else self.$elProxy = self.options.icon;
144					}
145					
146					self.$elProxy.insertAfter(self.$el);
147				}
148				else {
149					self.$elProxy = self.$el;
150				}
151				
152				// for 'click' and 'hover' triggers : bind on events to open the tooltip. Closing is now handled in _showNow() because of its bindings.
153				// Notes about touch events :
154					// - mouseenter, mouseleave and clicks happen even on pure touch devices because they are emulated. deviceIsPureTouch() is a simple attempt to detect them.
155					// - on hybrid devices, we do not prevent touch gesture from opening tooltips. It would be too complex to differentiate real mouse events from emulated ones.
156					// - we check deviceIsPureTouch() at each event rather than prior to binding because the situation may change during browsing
157				if (self.options.trigger == 'hover') {
158					
159					// these binding are for mouse interaction only
160					self.$elProxy
161						.on('mouseenter.'+ self.namespace, function() {
162							if (!deviceIsPureTouch() || self.options.touchDevices) {
163								self.mouseIsOverProxy = true;
164								self._show();
165							}
166						})
167						.on('mouseleave.'+ self.namespace, function() {
168							if (!deviceIsPureTouch() || self.options.touchDevices) {
169								self.mouseIsOverProxy = false;
170							}
171						});
172					
173					// for touch interaction only
174					if (deviceHasTouchCapability && self.options.touchDevices) {
175						
176						// for touch devices, we immediately display the tooltip because we cannot rely on mouseleave to handle the delay
177						self.$elProxy.on('touchstart.'+ self.namespace, function() {
178							self._showNow();
179						});
180					}
181				}
182				else if (self.options.trigger == 'click') {
183					
184					// note : for touch devices, we do not bind on touchstart, we only rely on the emulated clicks (triggered by taps)
185					self.$elProxy.on('click.'+ self.namespace, function() {
186						if (!deviceIsPureTouch() || self.options.touchDevices) {
187							self._show();
188						}
189					});
190				}
191			}
192		},
193		
194		// this function will schedule the opening of the tooltip after the delay, if there is one
195		_show: function() {
196			
197			var self = this;
198			
199			if (self.Status != 'shown' && self.Status != 'appearing') {
200				
201				if (self.options.delay) {
202					self.timerShow = setTimeout(function(){
203						
204						// for hover trigger, we check if the mouse is still over the proxy, otherwise we do not show anything
205						if (self.options.trigger == 'click' || (self.options.trigger == 'hover' && self.mouseIsOverProxy)) {
206							self._showNow();
207						}
208					}, self.options.delay);
209				}
210				else self._showNow();
211			}
212		},
213		
214		// this function will open the tooltip right away
215		_showNow: function(callback) {
216			
217			var self = this;
218			
219			// call our constructor custom function before continuing
220			self.options.functionBefore.call(self.$el, self.$el, function() {
221				
222				// continue only if the tooltip is enabled and has any content
223				if (self.enabled && self.Content !== null) {
224				
225					// save the method callback and cancel hide method callbacks
226					if (callback) self.callbacks.show.push(callback);
227					self.callbacks.hide = [];
228					
229					//get rid of any appearance timer
230					clearTimeout(self.timerShow);
231					self.timerShow = null;
232					clearTimeout(self.timerHide);
233					self.timerHide = null;
234					
235					// if we only want one tooltip open at a time, close all auto-closing tooltips currently open and not already disappearing
236					if (self.options.onlyOne) {
237						$('.tooltipstered').not(self.$el).each(function(i,el) {
238							
239							var $el = $(el),
240								nss = $el.data('tooltipster-ns');
241							
242							// iterate on all tooltips of the element
243							$.each(nss, function(i, ns){
244								var instance = $el.data(ns),
245									// we have to use the public methods here
246									s = instance.status(),
247									ac = instance.option('autoClose');
248								
249								if (s !== 'hidden' && s !== 'disappearing' && ac) {
250									instance.hide();
251								}
252							});
253						});
254					}
255					
256					var finish = function() {
257						self.Status = 'shown';
258						
259						// trigger any show method custom callbacks and reset them
260						$.each(self.callbacks.show, function(i,c) { c.call(self.$el); });
261						self.callbacks.show = [];
262					};
263					
264					// if this origin already has its tooltip open
265					if (self.Status !== 'hidden') {
266						
267						// the timer (if any) will start (or restart) right now
268						var extraTime = 0;
269						
270						// if it was disappearing, cancel that
271						if (self.Status === 'disappearing') {
272							
273							self.Status = 'appearing';
274							
275							if (supportsTransitions()) {
276								
277								self.$tooltip
278									.clearQueue()
279									.removeClass('tooltipster-dying')
280									.addClass('tooltipster-'+ self.options.animation +'-show');
281								
282								if (self.options.speed > 0) self.$tooltip.delay(self.options.speed);
283								
284								self.$tooltip.queue(finish);
285							}
286							else {
287								// in case the tooltip was currently fading out, bring it back to life
288								self.$tooltip
289									.stop()
290									.fadeIn(finish);
291							}
292						}
293						// if the tooltip is already open, we still need to trigger the method custom callback
294						else if(self.Status === 'shown') {
295							finish();
296						}
297					}
298					// if the tooltip isn't already open, open that sucker up!
299					else {
300						
301						self.Status = 'appearing';
302						
303						// the timer (if any) will start when the tooltip has fully appeared after its transition
304						var extraTime = self.options.speed;
305						
306						// disable horizontal scrollbar to keep overflowing tooltips from jacking with it and then restore it to its previous value
307						self.bodyOverflowX = $('body').css('overflow-x');
308						$('body').css('overflow-x', 'hidden');
309						
310						// get some other settings related to building the tooltip
311						var animation = 'tooltipster-' + self.options.animation,
312							animationSpeed = '-webkit-transition-duration: '+ self.options.speed +'ms; -webkit-animation-duration: '+ self.options.speed +'ms; -moz-transition-duration: '+ self.options.speed +'ms; -moz-animation-duration: '+ self.options.speed +'ms; -o-transition-duration: '+ self.options.speed +'ms; -o-animation-duration: '+ self.options.speed +'ms; -ms-transition-duration: '+ self.options.speed +'ms; -ms-animation-duration: '+ self.options.speed +'ms; transition-duration: '+ self.options.speed +'ms; animation-duration: '+ self.options.speed +'ms;',
313							minWidth = self.options.minWidth ? 'min-width:'+ Math.round(self.options.minWidth) +'px;' : '',
314							maxWidth = self.options.maxWidth ? 'max-width:'+ Math.round(self.options.maxWidth) +'px;' : '',
315							pointerEvents = self.options.interactive ? 'pointer-events: auto;' : '';
316						
317						// build the base of our tooltip
318						self.$tooltip = $('<div class="tooltipster-base '+ self.options.theme +'" style="'+ minWidth +' '+ maxWidth +' '+ pointerEvents +' '+ animationSpeed +'"><div class="tooltipster-content"></div></div>');
319						
320						// only add the animation class if the user has a browser that supports animations
321						if (supportsTransitions()) self.$tooltip.addClass(animation);
322						
323						// insert the content
324						self._content_insert();
325						
326						// attach
327						self.$tooltip.appendTo('body');
328						
329						// do all the crazy calculations and positioning
330						self.reposition();
331						
332						// call our custom callback since the content of the tooltip is now part of the DOM
333						self.options.functionReady.call(self.$el, self.$el, self.$tooltip);
334						
335						// animate in the tooltip
336						if (supportsTransitions()) {
337							
338							self.$tooltip.addClass(animation + '-show');
339							
340							if(self.options.speed > 0) self.$tooltip.delay(self.options.speed);
341							
342							self.$tooltip.queue(finish);
343						}
344						else {
345							self.$tooltip.css('display', 'none').fadeIn(self.options.speed, finish);
346						}
347						
348						// will check if our tooltip origin is removed while the tooltip is shown
349						self._interval_set();
350						
351						// reposition on scroll (otherwise position:fixed element's tooltips will move away form their origin) and on resize (in case position can/has to be changed)
352						$(window).on('scroll.'+ self.namespace +' resize.'+ self.namespace, function() {
353							self.reposition();
354						});
355						
356						// auto-close bindings
357						if (self.options.autoClose) {
358							
359							// in case a listener is already bound for autoclosing (mouse or touch, hover or click), unbind it first
360							$('body').off('.'+ self.namespace);
361							
362							// here we'll have to set different sets of bindings for both touch and mouse
363							if (self.options.trigger == 'hover') {
364								
365								// if the user touches the body, hide
366								if (deviceHasTouchCapability) {
367									// timeout 0 : explanation below in click section
368									setTimeout(function() {
369										// we don't want to bind on click here because the initial touchstart event has not yet triggered its click event, which is thus about to happen
370										$('body').on('touchstart.'+ self.namespace, function() {
371											self.hide();
372										});
373									}, 0);
374								}
375								
376								// if we have to allow interaction
377								if (self.options.interactive) {
378									
379									// touch events inside the tooltip must not close it
380									if (deviceHasTouchCapability) {
381										self.$tooltip.on('touchstart.'+ self.namespace, function(event) {
382											event.stopPropagation();
383										});
384									}
385									
386									// as for mouse interaction, we get rid of the tooltip only after the mouse has spent some time out of it
387									var tolerance = null;
388									
389									self.$elProxy.add(self.$tooltip)
390										// hide after some time out of the proxy and the tooltip
391										.on('mouseleave.'+ self.namespace + '-autoClose', function() {
392											clearTimeout(tolerance);
393											tolerance = setTimeout(function(){
394												self.hide();
395											}, self.options.interactiveTolerance);
396										})
397										// suspend timeout when the mouse is over the proxy or the tooltip
398										.on('mouseenter.'+ self.namespace + '-autoClose', function() {
399											clearTimeout(tolerance);
400										});
401								}
402								// if this is a non-interactive tooltip, get rid of it if the mouse leaves
403								else {
404									self.$elProxy.on('mouseleave.'+ self.namespace + '-autoClose', function() {
405										self.hide();
406									});
407								}
408							}
409							// here we'll set the same bindings for both clicks and touch on the body to hide the tooltip
410							else if(self.options.trigger == 'click'){
411								
412								// use a timeout to prevent immediate closing if the method was called on a click event and if options.delay == 0 (because of bubbling)
413								setTimeout(function() {
414									$('body').on('click.'+ self.namespace +' touchstart.'+ self.namespace, function() {
415										self.hide();
416									});
417								}, 0);
418								
419								// if interactive, we'll stop the events that were emitted from inside the tooltip to stop autoClosing
420								if (self.options.interactive) {
421									
422									// note : the touch events will just not be used if the plugin is not enabled on touch devices
423									self.$tooltip.on('click.'+ self.namespace +' touchstart.'+ self.namespace, function(event) {
424										event.stopPropagation();
425									});
426								}
427							}
428						}
429					}
430					
431					// if we have a timer set, let the countdown begin
432					if (self.options.timer > 0) {
433						
434						self.timerHide = setTimeout(function() {
435							self.timerHide = null;
436							self.hide();
437						}, self.options.timer + extraTime);
438					}
439				}
440			});
441		},
442		
443		_interval_set: function() {
444			
445			var self = this;
446			
447			self.checkInterval = setInterval(function() {
448				
449				// if the tooltip and/or its interval should be stopped
450				if (
451						// if the origin has been removed
452						$('body').find(self.$el).length === 0
453						// if the elProxy has been removed
454					||	$('body').find(self.$elProxy).length === 0
455						// if the tooltip has been closed
456					||	self.Status == 'hidden'
457						// if the tooltip has somehow been removed
458					||	$('body').find(self.$tooltip).length === 0
459				) {
460					// remove the tooltip if it's still here
461					if (self.Status == 'shown' || self.Status == 'appearing') self.hide();
462					
463					// clear this interval as it is no longer necessary
464					self._interval_cancel();
465				}
466				// if everything is alright
467				else {
468					// compare the former and current positions of the elProxy to reposition the tooltip if need be
469					if(self.options.positionTracker){
470						
471						var p = self._repositionInfo(self.$elProxy),
472							identical = false;
473						
474						// compare size first (a change requires repositioning too)
475						if(areEqual(p.dimension, self.elProxyPosition.dimension)){
476							
477							// for elements with a fixed position, we track the top and left properties (relative to window)
478							if(self.$elProxy.css('position') === 'fixed'){
479								if(areEqual(p.position, self.elProxyPosition.position)) identical = true;
480							}
481							// otherwise, track total offset (relative to document)
482							else {
483								if(areEqual(p.offset, self.elProxyPosition.offset)) identical = true;
484							}
485						}
486						
487						if(!identical){
488							self.reposition();
489						}
490					}
491				}
492			}, 200);
493		},
494		
495		_interval_cancel: function() {
496			clearInterval(this.checkInterval);
497			// clean delete
498			this.checkInterval = null;
499		},
500		
501		_content_set: function(content) {
502			// clone if asked. Cloning the object makes sure that each instance has its own version of the content (in case a same object were provided for several instances)
503			// reminder : typeof null === object
504			if (typeof content === 'object' && content !== null && this.options.contentCloning) {
505				content = content.clone(true);
506			}
507			this.Content = content;
508		},
509		
510		_content_insert: function() {
511			
512			var self = this,
513				$d = this.$tooltip.find('.tooltipster-content');
514			
515			if (typeof self.Content === 'string' && !self.options.contentAsHTML) {
516				$d.text(self.Content);
517			}
518			else {
519				$d
520					.empty()
521					.append(self.Content);
522			}
523		},
524		
525		_update: function(content) {
526			
527			var self = this;
528			
529			// change the content
530			self._content_set(content);
531			
532			if (self.Content !== null) {
533				
534				// update the tooltip if it is open
535				if (self.Status !== 'hidden') {
536					
537					// reset the content in the tooltip
538					self._content_insert();
539					
540					// reposition and resize the tooltip
541					self.reposition();
542					
543					// if we want to play a little animation showing the content changed
544					if (self.options.updateAnimation) {
545						
546						if (supportsTransitions()) {
547							
548							self.$tooltip.css({
549								'width': '',
550								'-webkit-transition': 'all ' + self.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
551								'-moz-transition': 'all ' + self.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
552								'-o-transition': 'all ' + self.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
553								'-ms-transition': 'all ' + self.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
554								'transition': 'all ' + self.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms'
555							}).addClass('tooltipster-content-changing');
556							
557							// reset the CSS transitions and finish the change animation
558							setTimeout(function() {
559								
560								if(self.Status != 'hidden'){
561									
562									self.$tooltip.removeClass('tooltipster-content-changing');
563									
564									// after the changing animation has completed, reset the CSS transitions
565									setTimeout(function() {
566										
567										if(self.Status !== 'hidden'){
568											self.$tooltip.css({
569												'-webkit-transition': self.options.speed + 'ms',
570												'-moz-transition': self.options.speed + 'ms',
571												'-o-transition': self.options.speed + 'ms',
572												'-ms-transition': self.options.speed + 'ms',
573												'transition': self.options.speed + 'ms'
574											});
575										}
576									}, self.options.speed);
577								}
578							}, self.options.speed);
579						}
580						else {
581							self.$tooltip.fadeTo(self.options.speed, 0.5, function() {
582								if(self.Status != 'hidden'){
583									self.$tooltip.fadeTo(self.options.speed, 1);
584								}
585							});
586						}
587					}
588				}
589			}
590			else {
591				self.hide();
592			}
593		},
594		
595		_repositionInfo: function($el) {
596			return {
597				dimension: {
598					height: $el.outerHeight(false),
599					width: $el.outerWidth(false)
600				},
601				offset: $el.offset(),
602				position: {
603					left: parseInt($el.css('left')),
604					top: parseInt($el.css('top'))
605				}
606			};
607		},
608		
609		hide: function(callback) {
610			
611			var self = this;
612			
613			// save the method custom callback and cancel any show method custom callbacks
614			if (callback) self.callbacks.hide.push(callback);
615			self.callbacks.show = [];
616			
617			// get rid of any appearance timeout
618			clearTimeout(self.timerShow);
619			self.timerShow = null;
620			clearTimeout(self.timerHide);
621			self.timerHide = null;
622			
623			var finishCallbacks = function() {
624				// trigger any hide method custom callbacks and reset them
625				$.each(self.callbacks.hide, function(i,c) { c.call(self.$el); });
626				self.callbacks.hide = [];
627			};
628			
629			// hide
630			if (self.Status == 'shown' || self.Status == 'appearing') {
631				
632				self.Status = 'disappearing';
633				
634				var finish = function() {
635					
636					self.Status = 'hidden';
637					
638					// detach our content object first, so the next jQuery's remove() call does not unbind its event handlers
639					if (typeof self.Content == 'object' && self.Content !== null) {
640						self.Content.detach();
641					}
642					
643					self.$tooltip.remove();
644					self.$tooltip = null;
645					
646					// unbind orientationchange, scroll and resize listeners
647					$(window).off('.'+ self.namespace);
648					
649					$('body')
650						// unbind any auto-closing click/touch listeners
651						.off('.'+ self.namespace)
652						.css('overflow-x', self.bodyOverflowX);
653					
654					// unbind any auto-closing click/touch listeners
655					$('body').off('.'+ self.namespace);
656					
657					// unbind any auto-closing hover listeners
658					self.$elProxy.off('.'+ self.namespace + '-autoClose');
659					
660					// call our constructor custom callback function
661					self.options.functionAfter.call(self.$el, self.$el);
662					
663					// call our method custom callbacks functions
664					finishCallbacks();
665				};
666				
667				if (supportsTransitions()) {
668					
669					self.$tooltip
670						.clearQueue()
671						.removeClass('tooltipster-' + self.options.animation + '-show')
672						// for transitions only
673						.addClass('tooltipster-dying');
674					
675					if(self.options.speed > 0) self.$tooltip.delay(self.options.speed);
676					
677					self.$tooltip.queue(finish);
678				}
679				else {
680					self.$tooltip
681						.stop()
682						.fadeOut(self.options.speed, finish);
683				}
684			}
685			// if the tooltip is already hidden, we still need to trigger the method custom callback
686			else if(self.Status == 'hidden') {
687				finishCallbacks();
688			}
689			
690			return self;
691		},
692		
693		// the public show() method is actually an alias for the private showNow() method
694		show: function(callback) {
695			this._showNow(callback);
696			return this;
697		},
698		
699		// 'update' is deprecated in favor of 'content' but is kept for backward compatibility
700		update: function(c) {
701			return this.content(c);
702		},
703		content: function(c) {
704			// getter method
705			if(typeof c === 'undefined'){
706				return this.Content;
707			}
708			// setter method
709			else {
710				this._update(c);
711				return this;
712			}
713		},
714		
715		reposition: function() {
716			
717			var self = this;
718			
719			// in case the tooltip has been removed from DOM manually
720			if ($('body').find(self.$tooltip).length !== 0) {
721				
722				// reset width
723				self.$tooltip.css('width', '');
724				
725				// find variables to determine placement
726				self.elProxyPosition = self._repositionInfo(self.$elProxy);
727				var arrowReposition = null,
728					windowWidth = $(window).width(),
729					// shorthand
730					proxy = self.elProxyPosition,
731					tooltipWidth = self.$tooltip.outerWidth(false),
732					tooltipInnerWidth = self.$tooltip.innerWidth() + 1, // this +1 stops FireFox from sometimes forcing an additional text line
733					tooltipHeight = self.$tooltip.outerHeight(false);
734				
735				// if this is an <area> tag inside a <map>, all hell breaks loose. Recalculate all the measurements based on coordinates
736				if (self.$elProxy.is('area')) {
737					var areaShape = self.$elProxy.attr('shape'),
738						mapName = self.$elProxy.parent().attr('name'),
739						map = $('img[usemap="#'+ mapName +'"]'),
740						mapOffsetLeft = map.offset().left,
741						mapOffsetTop = map.offset().top,
742						areaMeasurements = self.$elProxy.attr('coords') !== undefined ? self.$elProxy.attr('coords').split(',') : undefined;
743					
744					if (areaShape == 'circle') {
745						var areaLeft = parseInt(areaMeasurements[0]),
746							areaTop = parseInt(areaMeasurements[1]),
747							areaWidth = parseInt(areaMeasurements[2]);
748						proxy.dimension.height = areaWidth * 2;
749						proxy.dimension.width = areaWidth * 2;
750						proxy.offset.top = mapOffsetTop + areaTop - areaWidth;
751						proxy.offset.left = mapOffsetLeft + areaLeft - areaWidth;
752					}
753					else if (areaShape == 'rect') {
754						var areaLeft = parseInt(areaMeasurements[0]),
755							areaTop = parseInt(areaMeasurements[1]),
756							areaRight = parseInt(areaMeasurements[2]),
757							areaBottom = parseInt(areaMeasurements[3]);
758						proxy.dimension.height = areaBottom - areaTop;
759						proxy.dimension.width = areaRight - areaLeft;
760						proxy.offset.top = mapOffsetTop + areaTop;
761						proxy.offset.left = mapOffsetLeft + areaLeft;
762					}
763					else if (areaShape == 'poly') {
764						var areaXs = [],
765							areaYs = [],
766							areaSmallestX = 0,
767							areaSmallestY = 0,
768							areaGreatestX = 0,
769							areaGreatestY = 0,
770							arrayAlternate = 'even';
771						
772						for (var i = 0; i < areaMeasurements.length; i++) {
773							var areaNumber = parseInt(areaMeasurements[i]);
774							
775							if (arrayAlternate == 'even') {
776								if (areaNumber > areaGreatestX) {
777									areaGreatestX = areaNumber;
778									if (i === 0) {
779										areaSmallestX = areaGreatestX;
780									}
781								}
782								
783								if (areaNumber < areaSmallestX) {
784									areaSmallestX = areaNumber;
785								}
786								
787								arrayAlternate = 'odd';
788							}
789							else {
790								if (areaNumber > areaGreatestY) {
791									areaGreatestY = areaNumber;
792									if (i == 1) {
793										areaSmallestY = areaGreatestY;
794									}
795								}
796								
797								if (areaNumber < areaSmallestY) {
798									areaSmallestY = areaNumber;
799								}
800								
801								arrayAlternate = 'even';
802							}
803						}
804					
805						proxy.dimension.height = areaGreatestY - areaSmallestY;
806						proxy.dimension.width = areaGreatestX - areaSmallestX;
807						proxy.offset.top = mapOffsetTop + areaSmallestY;
808						proxy.offset.left = mapOffsetLeft + areaSmallestX;
809					}
810					else {
811						proxy.dimension.height = map.outerHeight(false);
812						proxy.dimension.width = map.outerWidth(false);
813						proxy.offset.top = mapOffsetTop;
814						proxy.offset.left = mapOffsetLeft;
815					}
816				}
817				
818				// our function and global vars for positioning our tooltip
819				var myLeft = 0,
820					myLeftMirror = 0,
821					myTop = 0,
822					offsetY = parseInt(self.options.offsetY),
823					offsetX = parseInt(self.options.offsetX),
824					// this is the arrow position that will eventually be used. It may differ from the position option if the tooltip cannot be displayed in this position
825					practicalPosition = self.options.position;
826				
827				// a function to detect if the tooltip is going off the screen horizontally. If so, reposition the crap out of it!
828				function dontGoOffScreenX() {
829				
830					var windowLeft = $(window).scrollLeft();
831					
832					// if the tooltip goes off the left side of the screen, line it up with the left side of the window
833					if((myLeft - windowLeft) < 0) {
834						arrowReposition = myLeft - windowLeft;
835						myLeft = windowLeft;
836					}
837					
838					// if the tooltip goes off the right of the screen, line it up with the right side of the window
839					if (((myLeft + tooltipWidth) - windowLeft) > windowWidth) {
840						arrowReposition = myLeft - ((windowWidth + windowLeft) - tooltipWidth);
841						myLeft = (windowWidth + windowLeft) - tooltipWidth;
842					}
843				}
844				
845				// a function to detect if the tooltip is going off the screen vertically. If so, switch to the opposite!
846				function dontGoOffScreenY(switchTo, switchFrom) {
847					// if it goes off the top off the page
848					if(((proxy.offset.top - $(window).scrollTop() - tooltipHeight - offsetY - 12) < 0) && (switchFrom.indexOf('top') > -1)) {
849						practicalPosition = switchTo;
850					}
851					
852					// if it goes off the bottom of the page
853					if (((proxy.offset.top + proxy.dimension.height + tooltipHeight + 12 + offsetY) > ($(window).scrollTop() + $(window).height())) && (switchFrom.indexOf('bottom') > -1)) {
854						practicalPosition = switchTo;
855						myTop = (proxy.offset.top - tooltipHeight) - offsetY - 12;
856					}
857				}
858				
859				if(practicalPosition == 'top') {
860					var leftDifference = (proxy.offset.left + tooltipWidth) - (proxy.offset.left + proxy.dimension.width);
861					myLeft = (proxy.offset.left + offsetX) - (leftDifference / 2);
862					myTop = (proxy.offset.top - tooltipHeight) - offsetY - 12;
863					dontGoOffScreenX();
864					dontGoOffScreenY('bottom', 'top');
865				}
866				
867				if(practicalPosition == 'top-left') {
868					myLeft = proxy.offset.left + offsetX;
869					myTop = (proxy.offset.top - tooltipHeight) - offsetY - 12;
870					dontGoOffScreenX();
871					dontGoOffScreenY('bottom-left', 'top-left');
872				}
873				
874				if(practicalPosition == 'top-right') {
875					myLeft = (proxy.offset.left + proxy.dimension.width + offsetX) - tooltipWidth;
876					myTop = (proxy.offset.top - tooltipHeight) - offsetY - 12;
877					dontGoOffScreenX();
878					dontGoOffScreenY('bottom-right', 'top-right');
879				}
880				
881				if(practicalPosition == 'bottom') {
882					var leftDifference = (proxy.offset.left + tooltipWidth) - (proxy.offset.left + proxy.dimension.width);
883					myLeft = proxy.offset.left - (leftDifference / 2) + offsetX;
884					myTop = (proxy.offset.top + proxy.dimension.height) + offsetY + 12;
885					dontGoOffScreenX();
886					dontGoOffScreenY('top', 'bottom');
887				}
888				
889				if(practicalPosition == 'bottom-left') {
890					myLeft = proxy.offset.left + offsetX;
891					myTop = (proxy.offset.top + proxy.dimension.height) + offsetY + 12;
892					dontGoOffScreenX();
893					dontGoOffScreenY('top-left', 'bottom-left');
894				}
895				
896				if(practicalPosition == 'bottom-right') {
897					myLeft = (proxy.offset.left + proxy.dimension.width + offsetX) - tooltipWidth;
898					myTop = (proxy.offset.top + proxy.dimension.height) + offsetY + 12;
899					dontGoOffScreenX();
900					dontGoOffScreenY('top-right', 'bottom-right');
901				}
902				
903				if(practicalPosition == 'left') {
904					myLeft = proxy.offset.left - offsetX - tooltipWidth - 12;
905					myLeftMirror = proxy.offset.left + offsetX + proxy.dimension.width + 12;
906					var topDifference = (proxy.offset.top + tooltipHeight) - (proxy.offset.top + proxy.dimension.height);
907					myTop = proxy.offset.top - (topDifference / 2) - offsetY;
908					
909					// if the tooltip goes off boths sides of the page
910					if((myLeft < 0) && ((myLeftMirror + tooltipWidth) > windowWidth)) {
911						var borderWidth = parseFloat(self.$tooltip.css('border-width')) * 2,
912							newWidth = (tooltipWidth + myLeft) - borderWidth;
913						self.$tooltip.css('width', newWidth + 'px');
914						
915						tooltipHeight = self.$tooltip.outerHeight(false);
916						myLeft = proxy.offset.left - offsetX - newWidth - 12 - borderWidth;
917						topDifference = (proxy.offset.top + tooltipHeight) - (proxy.offset.top + proxy.dimension.height);
918						myTop = proxy.offset.top - (topDifference / 2) - offsetY;
919					}
920					
921					// if it only goes off one side, flip it to the other side
922					else if(myLeft < 0) {
923						myLeft = proxy.offset.left + offsetX + proxy.dimension.width + 12;
924						arrowReposition = 'left';
925					}
926				}
927				
928				if(practicalPosition == 'right') {
929					myLeft = proxy.offset.left + offsetX + proxy.dimension.width + 12;
930					myLeftMirror = proxy.offset.left - offsetX - tooltipWidth - 12;
931					var topDifference = (proxy.offset.top + tooltipHeight) - (proxy.offset.top + proxy.dimension.height);
932					myTop = proxy.offset.top - (topDifference / 2) - offsetY;
933					
934					// if the tooltip goes off boths sides of the page
935					if(((myLeft + tooltipWidth) > windowWidth) && (myLeftMirror < 0)) {
936						var borderWidth = parseFloat(self.$tooltip.css('border-width')) * 2,
937							newWidth = (windowWidth - myLeft) - borderWidth;
938						self.$tooltip.css('width', newWidth + 'px');
939						
940						tooltipHeight = self.$tooltip.outerHeight(false);
941						topDifference = (proxy.offset.top + tooltipHeight) - (proxy.offset.top + proxy.dimension.height);
942						myTop = proxy.offset.top - (topDifference / 2) - offsetY;
943					}
944						
945					// if it only goes off one side, flip it to the other side
946					else if((myLeft + tooltipWidth) > windowWidth) {
947						myLeft = proxy.offset.left - offsetX - tooltipWidth - 12;
948						arrowReposition = 'right';
949					}
950				}
951				
952				// if arrow is set true, style it and append it
953				if (self.options.arrow) {
954	
955					var arrowClass = 'tooltipster-arrow-' + practicalPosition;
956					
957					// set color of the arrow
958					if(self.options.arrowColor.length < 1) {
959						var arrowColor = self.$tooltip.css('background-color');
960					}
961					else {
962						var arrowColor = self.options.arrowColor;
963					}
964					
965					// if the tooltip was going off the page and had to re-adjust, we need to update the arrow's position
966					if (!arrowReposition) {
967						arrowReposition = '';
968					}
969					else if (arrowReposition == 'left') {
970						arrowClass = 'tooltipster-arrow-right';
971						arrowReposition = '';
972					}
973					else if (arrowReposition == 'right') {
974						arrowClass = 'tooltipster-arrow-left';
975						arrowReposition = '';
976					}
977					else {
978						arrowReposition = 'left:'+ Math.round(arrowReposition) +'px;';
979					}
980					
981					// building the logic to create the border around the arrow of the tooltip
982					if ((practicalPosition == 'top') || (practicalPosition == 'top-left') || (practicalPosition == 'top-right')) {
983						var tooltipBorderWidth = parseFloat(self.$tooltip.css('border-bottom-width')),
984							tooltipBorderColor = self.$tooltip.css('border-bottom-color');
985					}
986					else if ((practicalPosition == 'bottom') || (practicalPosition == 'bottom-left') || (practicalPosition == 'bottom-right')) {
987						var tooltipBorderWidth = parseFloat(self.$tooltip.css('border-top-width')),
988							tooltipBorderColor = self.$tooltip.css('border-top-color');
989					}
990					else if (practicalPosition == 'left') {
991						var tooltipBorderWidth = parseFloat(self.$tooltip.css('border-right-width')),
992							tooltipBorderColor = self.$tooltip.css('border-right-color');
993					}
994					else if (practicalPosition == 'right') {
995						var tooltipBorderWidth = parseFloat(self.$tooltip.css('border-left-width')),
996							tooltipBorderColor = self.$tooltip.css('border-left-color');
997					}
998					else {
999						var tooltipBorderWidth = parseFloat(self.$tooltip.css('border-bottom-width')),
1000							tooltipBorderColor = self.$tooltip.css('border-bottom-color');
1001					}
1002					
1003					if (tooltipBorderWidth > 1) {
1004						tooltipBorderWidth++;
1005					}
1006					
1007					var arrowBorder = '';
1008					if (tooltipBorderWidth !== 0) {
1009						var arrowBorderSize = '',
1010							arrowBorderColor = 'border-color: '+ tooltipBorderColor +';';
1011						if (arrowClass.indexOf('bottom') !== -1) {
1012							arrowBorderSize = 'margin-top: -'+ Math.round(tooltipBorderWidth) +'px;';
1013						}
1014						else if (arrowClass.indexOf('top') !== -1) {
1015							arrowBorderSize = 'margin-bottom: -'+ Math.round(tooltipBorderWidth) +'px;';
1016						}
1017						else if (arrowClass.indexOf('left') !== -1) {
1018							arrowBorderSize = 'margin-right: -'+ Math.round(tooltipBorderWidth) +'px;';
1019						}
1020						else if (arrowClass.indexOf('right') !== -1) {
1021							arrowBorderSize = 'margin-left: -'+ Math.round(tooltipBorderWidth) +'px;';
1022						}
1023						arrowBorder = '<span class="tooltipster-arrow-border" style="'+ arrowBorderSize +' '+ arrowBorderColor +';"></span>';
1024					}
1025					
1026					// if the arrow already exists, remove and replace it
1027					self.$tooltip.find('.tooltipster-arrow').remove();
1028					
1029					// build out the arrow and append it		
1030					var arrowConstruct = '<div class="'+ arrowClass +' tooltipster-arrow" style="'+ arrowReposition +'">'+ arrowBorder +'<span style="border-color:'+ arrowColor +';"></span></div>';
1031					self.$tooltip.append(arrowConstruct);
1032				}
1033				
1034				// position the tooltip
1035				self.$tooltip.css({'top': Math.round(myTop) + 'px', 'left': Math.round(myLeft) + 'px'});
1036			}
1037			
1038			return self;
1039		},
1040		
1041		enable: function() {
1042			this.enabled = true;
1043			return this;
1044		},
1045		
1046		disable: function() {
1047			// hide first, in case the tooltip would not disappear on its own (autoClose false)
1048			this.hide();
1049			this.enabled = false;
1050			return this;
1051		},
1052		
1053		destroy: function() {
1054			
1055			var self = this;
1056			
1057			self.hide();
1058			
1059			// remove the icon, if any
1060			if(self.$el[0] !== self.$elProxy[0]) self.$elProxy.remove();
1061			
1062			self.$el
1063				.removeData(self.namespace)
1064				.off('.'+ self.namespace);
1065			
1066			var ns = self.$el.data('tooltipster-ns');
1067			
1068			// if there are no more tooltips on this element
1069			if(ns.length === 1){
1070				
1071				// old school technique when outerHTML is not supported
1072				var stringifiedContent = (typeof self.Content === 'string') ? self.Content : $('<div></div>').append(self.Content).html();
1073				
1074				self.$el
1075					.removeClass('tooltipstered')
1076					.attr('title', stringifiedContent)
1077					.removeData(self.namespace)
1078					.removeData('tooltipster-ns')
1079					.off('.'+ self.namespace);
1080			}
1081			else {
1082				// remove the instance namespace from the list of namespaces of tooltips present on the element
1083				ns = $.grep(ns, function(el, i){
1084					return el !== self.namespace;
1085				});
1086				self.$el.data('tooltipster-ns', ns);
1087			}
1088			
1089			return self;
1090		},
1091		
1092		elementIcon: function() {
1093			return (this.$el[0] !== this.$elProxy[0]) ? this.$elProxy[0] : undefined;
1094		},
1095		
1096		elementTooltip: function() {
1097			return this.$tooltip ? this.$tooltip[0] : undefined;
1098		},
1099		
1100		// public methods but for internal use only
1101		// getter if val is ommitted, setter otherwise
1102		option: function(o, val) {
1103			if (typeof val == 'undefined') return this.options[o];
1104			else {
1105				this.options[o] = val;
1106				return this;
1107			}
1108		},
1109		status: function() {
1110			return this.Status;
1111		}
1112	};
1113	
1114	$.fn[pluginName] = function () {
1115		
1116		// for using in closures
1117		var args = arguments;
1118		
1119		// if we are not in the context of jQuery wrapped HTML element(s) :
1120		// this happens when calling static methods in the form $.fn.tooltipster('methodName'), or when calling $(sel).tooltipster('methodName or options') where $(sel) does not match anything
1121		if (this.length === 0) {
1122			
1123			// if the first argument is a method name
1124			if (typeof args[0] === 'string') {
1125				
1126				var methodIsStatic = true;
1127				
1128				// list static methods here (usable by calling $.fn.tooltipster('methodName');)
1129				switch (args[0]) {
1130					
1131					case 'setDefaults':
1132						// change default options for all future instances
1133						$.extend(defaults, args[1]);
1134						break;
1135					
1136					default:
1137						methodIsStatic = false;
1138						break;
1139				}
1140				
1141				// $.fn.tooltipster('methodName') calls will return true
1142				if (methodIsStatic) return true;
1143				// $(sel).tooltipster('methodName') calls will return the list of objects event though it's empty because chaining should work on empty lists
1144				else return this;
1145			}
1146			// the first argument is undefined or an object of options : we are initalizing but there is no element matched by selector
1147			else {
1148				// still chainable : same as above
1149				return this;
1150			}
1151		}
1152		// this happens when calling $(sel).tooltipster('methodName or options') where $(sel) matches one or more elements
1153		else {
1154			
1155			// method calls
1156			if (typeof args[0] === 'string') {
1157				
1158				var v = '#*$~&';
1159				
1160				this.each(function() {
1161					
1162					// retrieve the namepaces of the tooltip(s) that exist on that element. We will interact with the first tooltip only.
1163					var ns = $(this).data('tooltipster-ns'),
1164						// self represents the instance of the first tooltipster plugin associated to the current HTML object of the loop
1165						self = ns ? $(this).data(ns[0]) : null;
1166					
1167					// if the current element holds a tooltipster instance
1168					if (self) {
1169						
1170						if (typeof self[args[0]] === 'function') {
1171							// note : args[1] and args[2] may not be defined
1172							var resp = self[args[0]](args[1], args[2]);
1173						}
1174						else {
1175							throw new Error('Unknown method .tooltipster("' + args[0] + '")');
1176						}
1177						
1178						// if the function returned anything other than the instance itself (which implies chaining)
1179						if (resp !== self){
1180							v = resp;
1181							// return false to stop .each iteration on the first element matched by the selector
1182							return false;
1183						}
1184					}
1185					else {
1186						throw new Error('You called Tooltipster\'s "' + args[0] + '" method on an uninitialized element');
1187					}
1188				});
1189				
1190				return (v !== '#*$~&') ? v : this;
1191			}
1192			// first argument is undefined or an object : the tooltip is initializing
1193			else {
1194				
1195				var instances = [],
1196					// is there a defined value for the multiple option in the options object ?
1197					multipleIsSet = args[0] && typeof args[0].multiple !== 'undefined',
1198					// if the multiple option is set to true, or if it's not defined but set to true in the defaults
1199					multiple = (multipleIsSet && args[0].multiple) || (!multipleIsSet && defaults.multiple),
1200					// same for debug
1201					debugIsSet = args[0] && typeof args[0].debug !== 'undefined',
1202					debug = (debugIsSet && args[0].debug) || (!debugIsSet && defaults.debug);
1203				
1204				// initialize a tooltipster instance for each element if it doesn't already have one or if the multiple option is set, and attach the object to it
1205				this.each(function () {
1206					
1207					var go = false,
1208						ns = $(this).data('tooltipster-ns'),
1209						instance = null;
1210					
1211					if (!ns) {
1212						go = true;
1213					}
1214					else if (multiple) {
1215						go = true;
1216					}
1217					else if (debug) {
1218						console.log('Tooltipster: one or more tooltips are already attached to this element: ignoring. Use the "multiple" option to attach more tooltips.');
1219					}
1220					
1221					if (go) {
1222						instance = new Plugin(this, args[0]);
1223						
1224						// save the reference of the new instance
1225						if (!ns) ns = [];
1226						ns.push(instance.namespace);
1227						$(this).data('tooltipster-ns', ns)
1228						
1229						// save the instance itself
1230						$(this).data(instance.namespace, instance);
1231					}
1232					
1233					instances.push(instance);
1234				});
1235				
1236				if (multiple) return instances;
1237				else return this;
1238			}
1239		}
1240	};
1241	
1242	// quick & dirty compare function (not bijective nor multidimensional)
1243	function areEqual(a,b) {
1244		var same = true;
1245		$.each(a, function(i, el){
1246			if(typeof b[i] === 'undefined' || a[i] !== b[i]){
1247				same = false;
1248				return false;
1249			}
1250		});
1251		return same;
1252	}
1253	
1254	// detect if this device can trigger touch events
1255	var deviceHasTouchCapability = !!('ontouchstart' in window);
1256	
1257	// we'll assume the device has no mouse until we detect any mouse movement
1258	var deviceHasMouse = false;
1259	$('body').one('mousemove', function() {
1260		deviceHasMouse = true;
1261	});
1262	
1263	function deviceIsPureTouch() {
1264		return (!deviceHasMouse && deviceHasTouchCapability);
1265	}
1266	
1267	// detecting support for CSS transitions
1268	function supportsTransitions() {
1269		var b = document.body || document.documentElement,
1270			s = b.style,
1271			p = 'transition';
1272		
1273		if(typeof s[p] == 'string') {return true; }
1274
1275		v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'],
1276		p = p.charAt(0).toUpperCase() + p.substr(1);
1277		for(var i=0; i<v.length; i++) {
1278			if(typeof s[v[i] + p] == 'string') { return true; }
1279		}
1280		return false;
1281	}
1282})( jQuery, window, document );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.