PageSourceSearch

https://www.filippodicostanzo.com/wp-content/themes/LDA-child/js/tooltipster.bundle.js?ver=1

js filippodicostanzo.com collected 2026-10-02 01:30:10 UTC 117,784 bytes, 4,223 lines download raw bytes

vendor: 2,723 bytes, lines 1-92
1/**
2 * tooltipster http://iamceege.github.io/tooltipster/
3 * A rockin' custom tooltip jQuery plugin
4 * Developed by Caleb Jacob and Louis Ameline
5 * MIT license
6 */
7(function (root, factory) {
8  if (typeof define === 'function' && define.amd) {
9    // AMD. Register as an anonymous module unless amdModuleId is set
10    define(["jquery"], function (a0) {
11      return (factory(a0));
12    });
13  } else if (typeof exports === 'object') {
14    // Node. Does not work with strict CommonJS, but
15    // only CommonJS-like environments that support module.exports,
16    // like Node.
17    module.exports = factory(require("jquery"));
18  } else {
19    factory(jQuery);
20  }
21}(this, function ($) {
22
23// This file will be UMDified by a build task.
24
25var defaults = {
26		animation: 'fade',
27		animationDuration: 350,
28		content: null,
29		contentAsHTML: false,
30		contentCloning: false,
31		debug: true,
32		delay: 300,
33		delayTouch: [300, 500],
34		functionInit: null,
35		functionBefore: null,
36		functionReady: null,
37		functionAfter: null,
38		functionFormat: null,
39		IEmin: 6,
40		interactive: false,
41		multiple: false,
42		// must be 'body' for now, or an element positioned at (0, 0)
43		// in the document, typically like the very top views of an app.
44		parent: 'body',
45		plugins: ['sideTip'],
46		repositionOnScroll: false,
47		restoration: 'none',
48		selfDestruction: true,
49		theme: [],
50		timer: 0,
51		trackerInterval: 500,
52		trackOrigin: false,
53		trackTooltip: false,
54		trigger: 'hover',
55		triggerClose: {
56			click: false,
57			mouseleave: false,
58			originClick: false,
59			scroll: false,
60			tap: false,
61			touchleave: false
62		},
63		triggerOpen: {
64			click: false,
65			mouseenter: false,
66			tap: false,
67			touchstart: false
68		},
69		updateAnimation: 'rotate',
70		zIndex: 9999999
71	},
72	// we'll avoid using the 'window' global as a good practice but npm's
73	// jquery@<2.1.0 package actually requires a 'window' global, so not sure
74	// it's useful at all
75	win = (typeof window != 'undefined') ? window : null,
76	// env will be proxied by the core for plugins to have access its properties
77	env = {
78		// detect if this device can trigger touch events. Better have a false
79		// positive (unused listeners, that's ok) than a false negative.
80		// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js
81		// http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
82		hasTouchCapability: !!(
83			win
84			&&	(	'ontouchstart' in win
85				||	(win.DocumentTouch && win.document instanceof win.DocumentTouch)
86				||	win.navigator.maxTouchPoints
87			)
88		),
89		hasTransitions: transitionSupport(),
90		IE: false,
91		// don't set manually, it will be updated by a build task after the manifest
92		semVer: '4.1.2
vendor: 8,006 bytes, lines 92-435
92',
93		window: win
94	},
95	core = function() {
96		
97		// core variables
98		
99		// the core emitters
100		this.__$emitterPrivate = $({});
101		this.__$emitterPublic = $({});
102		this.__instancesLatestArr = [];
103		// collects plugin constructors
104		this.__plugins = {};
105		// proxy env variables for plugins who might use them
106		this._env = env;
107	};
108
109// core methods
110core.prototype = {
111	
112	/**
113	 * A function to proxy the public methods of an object onto another
114	 *
115	 * @param {object} constructor The constructor to bridge
116	 * @param {object} obj The object that will get new methods (an instance or the core)
117	 * @param {string} pluginName A plugin name for the console log message
118	 * @return {core}
119	 * @private
120	 */
121	__bridge: function(constructor, obj, pluginName) {
122		
123		// if it's not already bridged
124		if (!obj[pluginName]) {
125			
126			var fn = function() {};
127			fn.prototype = constructor;
128			
129			var pluginInstance = new fn();
130			
131			// the _init method has to exist in instance constructors but might be missing
132			// in core constructors
133			if (pluginInstance.__init) {
134				pluginInstance.__init(obj);
135			}
136			
137			$.each(constructor, function(methodName, fn) {
138				
139				// don't proxy "private" methods, only "protected" and public ones
140				if (methodName.indexOf('__') != 0) {
141					
142					// if the method does not exist yet
143					if (!obj[methodName]) {
144						
145						obj[methodName] = function() {
146							return pluginInstance[methodName].apply(pluginInstance, Array.prototype.slice.apply(arguments));
147						};
148						
149						// remember to which plugin this method corresponds (several plugins may
150						// have methods of the same name, we need to be sure)
151						obj[methodName].bridged = pluginInstance;
152					}
153					else if (defaults.debug) {
154						
155						console.log('The '+ methodName +' method of the '+ pluginName
156							+' plugin conflicts with another plugin or native methods');
157					}
158				}
159			});
160			
161			obj[pluginName] = pluginInstance;
162		}
163		
164		return this;
165	},
166	
167	/**
168	 * For mockup in Node env if need be, for testing purposes
169	 *
170	 * @return {core}
171	 * @private
172	 */
173	__setWindow: function(window) {
174		env.window = window;
175		return this;
176	},
177	
178	/**
179	 * Returns a ruler, a tool to help measure the size of a tooltip under
180	 * various settings. Meant for plugins
181	 * 
182	 * @see Ruler
183	 * @return {object} A Ruler instance
184	 * @protected
185	 */
186	_getRuler: function($tooltip) {
187		return new Ruler($tooltip);
188	},
189	
190	/**
191	 * For internal use by plugins, if needed
192	 *
193	 * @return {core}
194	 * @protected
195	 */
196	_off: function() {
197		this.__$emitterPrivate.off.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
198		return this;
199	},
200	
201	/**
202	 * For internal use by plugins, if needed
203	 *
204	 * @return {core}
205	 * @protected
206	 */
207	_on: function() {
208		this.__$emitterPrivate.on.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
209		return this;
210	},
211	
212	/**
213	 * For internal use by plugins, if needed
214	 *
215	 * @return {core}
216	 * @protected
217	 */
218	_one: function() {
219		this.__$emitterPrivate.one.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
220		return this;
221	},
222	
223	/**
224	 * Returns (getter) or adds (setter) a plugin
225	 *
226	 * @param {string|object} plugin Provide a string (in the full form
227	 * "namespace.name") to use as as getter, an object to use as a setter
228	 * @return {object|core}
229	 * @protected
230	 */
231	_plugin: function(plugin) {
232		
233		var self = this;
234		
235		// getter
236		if (typeof plugin == 'string') {
237			
238			var pluginName = plugin,
239				p = null;
240			
241			// if the namespace is provided, it's easy to search
242			if (pluginName.indexOf('.') > 0) {
243				p = self.__plugins[pluginName];
244			}
245			// otherwise, return the first name that matches
246			else {
247				$.each(self.__plugins, function(i, plugin) {
248					
249					if (plugin.name.substring(plugin.name.length - pluginName.length - 1) == '.'+ pluginName) {
250						p = plugin;
251						return false;
252					}
253				});
254			}
255			
256			return p;
257		}
258		// setter
259		else {
260			
261			// force namespaces
262			if (plugin.name.indexOf('.') < 0) {
263				throw new Error('Plugins must be namespaced');
264			}
265			
266			self.__plugins[plugin.name] = plugin;
267			
268			// if the plugin has core features
269			if (plugin.core) {
270				
271				// bridge non-private methods onto the core to allow new core methods
272				self.__bridge(plugin.core, self, plugin.name);
273			}
274			
275			return this;
276		}
277	},
278	
279	/**
280	 * Trigger events on the core emitters
281	 * 
282	 * @returns {core}
283	 * @protected
284	 */
285	_trigger: function() {
286		
287		var args = Array.prototype.slice.apply(arguments);
288		
289		if (typeof args[0] == 'string') {
290			args[0] = { type: args[0] };
291		}
292		
293		// note: the order of emitters matters
294		this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate, args);
295		this.__$emitterPublic.trigger.apply(this.__$emitterPublic, args);
296		
297		return this;
298	},
299	
300	/**
301	 * Returns instances of all tooltips in the page or an a given element
302	 *
303	 * @param {string|HTML object collection} selector optional Use this
304	 * parameter to restrict the set of objects that will be inspected
305	 * for the retrieval of instances. By default, all instances in the
306	 * page are returned.
307	 * @return {array} An array of instance objects
308	 * @public
309	 */
310	instances: function(selector) {
311		
312		var instances = [],
313			sel = selector || '.tooltipstered';
314		
315		$(sel).each(function() {
316			
317			var $this = $(this),
318				ns = $this.data('tooltipster-ns');
319			
320			if (ns) {
321				
322				$.each(ns, function(i, namespace) {
323					instances.push($this.data(namespace));
324				});
325			}
326		});
327		
328		return instances;
329	},
330	
331	/**
332	 * Returns the Tooltipster objects generated by the last initializing call
333	 *
334	 * @return {array} An array of instance objects
335	 * @public
336	 */
337	instancesLatest: function() {
338		return this.__instancesLatestArr;
339	},
340	
341	/**
342	 * For public use only, not to be used by plugins (use ::_off() instead)
343	 *
344	 * @return {core}
345	 * @public
346	 */
347	off: function() {
348		this.__$emitterPublic.off.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
349		return this;
350	},
351	
352	/**
353	 * For public use only, not to be used by plugins (use ::_on() instead)
354	 *
355	 * @return {core}
356	 * @public
357	 */
358	on: function() {
359		this.__$emitterPublic.on.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
360		return this;
361	},
362	
363	/**
364	 * For public use only, not to be used by plugins (use ::_one() instead)
365	 * 
366	 * @return {core}
367	 * @public
368	 */
369	one: function() {
370		this.__$emitterPublic.one.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
371		return this;
372	},
373	
374	/**
375	 * Returns all HTML elements which have one or more tooltips
376	 *
377	 * @param {string} selector optional Use this to restrict the results
378	 * to the descendants of an element
379	 * @return {array} An array of HTML elements
380	 * @public
381	 */
382	origins: function(selector) {
383		
384		var sel = selector ?
385			selector +' ' :
386			'';
387		
388		return $(sel +'.tooltipstered').toArray();
389	},
390	
391	/**
392	 * Change default options for all future instances
393	 *
394	 * @param {object} d The options that should be made defaults
395	 * @return {core}
396	 * @public
397	 */
398	setDefaults: function(d) {
399		$.extend(defaults, d);
400		return this;
401	},
402	
403	/**
404	 * For users to trigger their handlers on the public emitter
405	 * 
406	 * @returns {core}
407	 * @public
408	 */
409	triggerHandler: function() {
410		this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
411		return this;
412	}
413};
414
415// $.tooltipster will be used to call core methods
416$.tooltipster = new core();
417
418// the Tooltipster instance class (mind the capital T)
419$.Tooltipster = function(element, options) {
420	
421	// list of instance variables
422	
423	// stack of custom callbacks provided as parameters to API methods
424	this.__callbacks = {
425		close: [],
426		open: []
427	};
428	// the schedule time of DOM removal
429	this.__closingTime;
430	// this will be the user content shown in the tooltip. A capital "C" is used
431	// because there is also a method called content()
432	this.__Content;
433	// for the size tracker
434	this.__contentBcr;
435	// to disable the tooltip 
435once the destruction has begun
436	this.__destroyed = false;
437	this.__destroying = false;
438	// we can't emit directly on the instance because if a method with the same
439	// name as the event exists, it will be called by jQuery. Se we use a plain
440	// object as emitter. This emitter is for internal use by plugins,
441	// if needed.
442	this.__$emitterPrivate = $({});
443	// this emitter is for the user to listen to events without risking to mess
444	// with our internal listeners
445	this.__$emitterPublic = $({});
446	this.__enabled = true;
447	// the reference to the gc interval
448	this.__garbageCollector;
449	// various position and size data recomputed before each repositioning
450	this.__Geometry;
451	// the tooltip position, saved after each repositioning by a plugin
452	this.__lastPosition;
453	// a unique namespace per instance
454	this.__namespace = 'tooltipster-'+ Math.round(Math.random()*1000000);
455	this.__options;
456	// will be used to support origins in scrollable areas
457	this.__$originParents;
458	this.__pointerIsOverOrigin = false;
vendor: 31,582 bytes, lines 459-1645
459	// to remove themes if needed
460	this.__previousThemes = [];
461	// the state can be either: appearing, stable, disappearing, closed
462	this.__state = 'closed';
463	// timeout references
464	this.__timeouts = {
465		close: [],
466		open: null
467	};
468	// store touch events to be able to detect emulated mouse events
469	this.__touchEvents = [];
470	// the reference to the tracker interval
471	this.__tracker = null;
472	// the element to which this tooltip is associated
473	this._$origin;
474	// this will be the tooltip element (jQuery wrapped HTML element).
475	// It's the job of a plugin to create it and append it to the DOM
476	this._$tooltip;
477	
478	// launch
479	this.__init(element, options);
480};
481
482$.Tooltipster.prototype = {
483	
484	/**
485	 * @param origin
486	 * @param options
487	 * @private
488	 */
489	__init: function(origin, options) {
490		
491		var self = this;
492		
493		self._$origin = $(origin);
494		self.__options = $.extend(true, {}, defaults, options);
495		
496		// some options may need to be reformatted
497		self.__optionsFormat();
498		
499		// don't run on old IE if asked no to
500		if (	!env.IE
501			||	env.IE >= self.__options.IEmin
502		) {
503			
504			// note: the content is null (empty) by default and can stay that
505			// way if the plugin remains initialized but not fed any content. The
506			// tooltip will just not appear.
507			
508			// let's save the initial value of the title attribute for later
509			// restoration if need be.
510			var initialTitle = null;
511			
512			// it will already have been saved in case of multiple tooltips
513			if (self._$origin.data('tooltipster-initialTitle') === undefined) {
514				
515				initialTitle = self._$origin.attr('title');
516				
517				// we do not want initialTitle to be "undefined" because
518				// of how jQuery's .data() method works
519				if (initialTitle === undefined) initialTitle = null;
520				
521				self._$origin.data('tooltipster-initialTitle', initialTitle);
522			}
523			
524			// If content is provided in the options, it has precedence over the
525			// title attribute.
526			// Note: an empty string is considered content, only 'null' represents
527			// the absence of content.
528			// Also, an existing title="" attribute will result in an empty string
529			// content
530			if (self.__options.content !== null) {
531				self.__contentSet(self.__options.content);
532			}
533			else {
534				
535				var selector = self._$origin.attr('data-tooltip-content'),
536					$el;
537				
538				if (selector){
539					$el = $(selector);
540				}
541				
542				if ($el && $el[0]) {
543					self.__contentSet($el.first());
544				}
545				else {
546					self.__contentSet(initialTitle);
547				}
548			}
549			
550			self._$origin
551				// strip the title off of the element to prevent the default tooltips
552				// from popping up
553				.removeAttr('title')
554				// to be able to find all instances on the page later (upon window
555				// events in particular)
556				.addClass('tooltipstered');
557			
558			// set listeners on the origin
559			self.__prepareOrigin();
560			
561			// set the garbage collector
562			self.__prepareGC();
563			
564			// init plugins
565			$.each(self.__options.plugins, function(i, pluginName) {
566				self._plug(pluginName);
567			});
568			
569			// to detect swiping
570			if (env.hasTouchCapability) {
571				$('body').on('touchmove.'+ self.__namespace +'-triggerOpen', function(event) {
572					self._touchRecordEvent(event);
573				});
574			}
575			
576			self
577				// prepare the tooltip when it gets created. This event must
578				// be fired by a plugin
579				._on('created', function() {
580					self.__prepareTooltip();
581				})
582				// save position information when it's sent by a plugin
583				._on('repositioned', function(e) {
584					self.__lastPosition = e.position;
585				});
586		}
587		else {
588			self.__options.disabled = true;
589		}
590	},
591	
592	/**
593	 * Insert the content into the appropriate HTML element of the tooltip
594	 * 
595	 * @returns {self}
596	 * @private
597	 */
598	__contentInsert: function() {
599		
600		var self = this,
601			$el = self._$tooltip.find('.tooltipster-content'),
602			formattedContent = self.__Content,
603			format = function(content) {
604				formattedContent = content;
605			};
606		
607		self._trigger({
608			type: 'format',
609			content: self.__Content,
610			format: format
611		});
612		
613		if (self.__options.functionFormat) {
614			
615			formattedContent = self.__options.functionFormat.call(
616				self,
617				self,
618				{ origin: self._$origin[0] },
619				self.__Content
620			);
621		}
622		
623		if (typeof formattedContent === 'string' && !self.__options.contentAsHTML) {
624			$el.text(formattedContent);
625		}
626		else {
627			$el
628				.empty()
629				.append(formattedContent);
630		}
631		
632		return self;
633	},
634	
635	/**
636	 * Save the content, cloning it beforehand if need be
637	 * 
638	 * @param content
639	 * @returns {self}
640	 * @private
641	 */
642	__contentSet: function(content) {
643		
644		// clone if asked. Cloning the object makes sure that each instance has its
645		// own version of the content (in case a same object were provided for several
646		// instances)
647		// reminder: typeof null === object
648		if (content instanceof $ && this.__options.contentCloning) {
649			content = content.clone(true);
650		}
651		
652		this.__Content = content;
653		
654		this._trigger({
655			type: 'updated',
656			content: content
657		});
658		
659		return this;
660	},
661	
662	/**
663	 * Error message about a method call made after destruction
664	 * 
665	 * @private
666	 */
667	__destroyError: function() {
668		throw new Error('This tooltip has been destroyed and cannot execute your method call.');
669	},
670	
671	/**
672	 * Gather all information about dimensions and available space,
673	 * called before every repositioning
674	 * 
675	 * @private
676	 * @returns {object}
677	 */
678	__geometry: function() {
679		
680		var	self = this,
681			$target = self._$origin,
682			originIsArea = self._$origin.is('area');
683		
684		// if this._$origin is a map area, the target we'll need
685		// the dimensions of is actually the image using the map,
686		// not the area itself
687		if (originIsArea) {
688			
689			var mapName = self._$origin.parent().attr('name');
690			
691			$target = $('img[usemap="#'+ mapName +'"]');
692		}
693		
694		var bcr = $target[0].getBoundingClientRect(),
695			$document = $(env.window.document),
696			$window = $(env.window),
697			$parent = $target,
698			// some useful properties of important elements
699			geo = {
700				// available space for the tooltip, see down below
701				available: {
702					document: null,
703					window: null
704				},
705				document: {
706					size: {
707						height: $document.height(),
708						width: $document.width()
709					}
710				},
711				window: {
712					scroll: {
713						// the second ones are for IE compatibility
714						left: env.window.scrollX || env.window.document.documentElement.scrollLeft,
715						top: env.window.scrollY || env.window.document.documentElement.scrollTop
716					},
717					size: {
718						height: $window.height(),
719						width: $window.width()
720					}
721				},
722				origin: {
723					// the origin has a fixed lineage if itself or one of its
724					// ancestors has a fixed position
725					fixedLineage: false,
726					// relative to the document
727					offset: {},
728					size: {
729						height: bcr.bottom - bcr.top,
730						width: bcr.right - bcr.left
731					},
732					usemapImage: originIsArea ? $target[0] : null,
733					// relative to the window
734					windowOffset: {
735						bottom: bcr.bottom,
736						left: bcr.left,
737						right: bcr.right,
738						top: bcr.top
739					}
740				}
741			},
742			geoFixed = false;
743		
744		// if the element is a map area, some properties may need
745		// to be recalculated
746		if (originIsArea) {
747			
748			var shape = self._$origin.attr('shape'),
749				coords = self._$origin.attr('coords');
750			
751			if (coords) {
752				
753				coords = coords.split(',');
754				
755				$.map(coords, function(val, i) {
756					coords[i] = parseInt(val);
757				});
758			}
759			
760			// if the image itself is the area, nothing more to do
761			if (shape != 'default') {
762				
763				switch(shape) {
764					
765					case 'circle':
766						
767						var circleCenterLeft = coords[0],
768							circleCenterTop = coords[1],
769							circleRadius = coords[2],
770							areaTopOffset = circleCenterTop - circleRadius,
771							areaLeftOffset = circleCenterLeft - circleRadius;
772						
773						geo.origin.size.height = circleRadius * 2;
774						geo.origin.size.width = geo.origin.size.height;
775						
776						geo.origin.windowOffset.left += areaLeftOffset;
777						geo.origin.windowOffset.top += areaTopOffset;
778						
779						break;
780					
781					case 'rect':
782						
783						var areaLeft = coords[0],
784							areaTop = coords[1],
785							areaRight = coords[2],
786							areaBottom = coords[3];
787						
788						geo.origin.size.height = areaBottom - areaTop;
789						geo.origin.size.width = areaRight - areaLeft;
790						
791						geo.origin.windowOffset.left += areaLeft;
792						geo.origin.windowOffset.top += areaTop;
793						
794						break;
795					
796					case 'poly':
797						
798						var areaSmallestX = 0,
799							areaSmallestY = 0,
800							areaGreatestX = 0,
801							areaGreatestY = 0,
802							arrayAlternate = 'even';
803						
804						for (var i = 0; i < coords.length; i++) {
805							
806							var areaNumber = coords[i];
807							
808							if (arrayAlternate == 'even') {
809								
810								if (areaNumber > areaGreatestX) {
811									
812									areaGreatestX = areaNumber;
813									
814									if (i === 0) {
815										areaSmallestX = areaGreatestX;
816									}
817								}
818								
819								if (areaNumber < areaSmallestX) {
820									areaSmallestX = areaNumber;
821								}
822								
823								arrayAlternate = 'odd';
824							}
825							else {
826								if (areaNumber > areaGreatestY) {
827									
828									areaGreatestY = areaNumber;
829									
830									if (i == 1) {
831										areaSmallestY = areaGreatestY;
832									}
833								}
834								
835								if (areaNumber < areaSmallestY) {
836									areaSmallestY = areaNumber;
837								}
838								
839								arrayAlternate = 'even';
840							}
841						}
842						
843						geo.origin.size.height = areaGreatestY - areaSmallestY;
844						geo.origin.size.width = areaGreatestX - areaSmallestX;
845						
846						geo.origin.windowOffset.left += areaSmallestX;
847						geo.origin.windowOffset.top += areaSmallestY;
848						
849						break;
850				}
851			}
852		}
853		
854		// user callback through an event
855		var edit = function(r) {
856			geo.origin.size.height = r.height,
857				geo.origin.windowOffset.left = r.left,
858				geo.origin.windowOffset.top = r.top,
859				geo.origin.size.width = r.width
860		};
861		
862		self._trigger({
863			type: 'geometry',
864			edit: edit,
865			geometry: {
866				height: geo.origin.size.height,
867				left: geo.origin.windowOffset.left,
868				top: geo.origin.windowOffset.top,
869				width: geo.origin.size.width
870			}
871		});
872		
873		// calculate the remaining properties with what we got
874		
875		geo.origin.windowOffset.right = geo.origin.windowOffset.left + geo.origin.size.width;
876		geo.origin.windowOffset.bottom = geo.origin.windowOffset.top + geo.origin.size.height;
877		
878		geo.origin.offset.left = geo.origin.windowOffset.left + env.window.scrollX;
879		geo.origin.offset.top = geo.origin.windowOffset.top + env.window.scrollY;
880		geo.origin.offset.bottom = geo.origin.offset.top + geo.origin.size.height;
881		geo.origin.offset.right = geo.origin.offset.left + geo.origin.size.width;
882		
883		// the space that is available to display the tooltip relatively to the document
884		geo.available.document = {
885			bottom: {
886				height: geo.document.size.height - geo.origin.offset.bottom,
887				width: geo.document.size.width
888			},
889			left: {
890				height: geo.document.size.height,
891				width: geo.origin.offset.left
892			},
893			right: {
894				height: geo.document.size.height,
895				width: geo.document.size.width - geo.origin.offset.right
896			},
897			top: {
898				height: geo.origin.offset.top,
899				width: geo.document.size.width
900			}
901		};
902		
903		// the space that is available to display the tooltip relatively to the viewport
904		// (the resulting values may be negative if the origin overflows the viewport)
905		geo.available.window = {
906			bottom: {
907				// the inner max is here to make sure the available height is no bigger
908				// than the viewport height (when the origin is off screen at the top).
909				// The outer max just makes sure that the height is not negative (when
910				// the origin overflows at the bottom).
911				height: Math.max(geo.window.size.height - Math.max(geo.origin.windowOffset.bottom, 0), 0),
912				width: geo.window.size.width
913			},
914			left: {
915				height: geo.window.size.height,
916				width: Math.max(geo.origin.windowOffset.left, 0)
917			},
918			right: {
919				height: geo.window.size.height,
920				width: Math.max(geo.window.size.width - Math.max(geo.origin.windowOffset.right, 0), 0)
921			},
922			top: {
923				height: Math.max(geo.origin.windowOffset.top, 0),
924				width: geo.window.size.width
925			}
926		};
927		
928		while ($parent[0].tagName.toLowerCase() != 'html') {
929			
930			if ($parent.css('position') == 'fixed') {
931				geo.origin.fixedLineage = true;
932				break;
933			}
934			
935			$parent = $parent.parent();
936		}
937		
938		return geo;
939	},
940	
941	/**
942	 * Some options may need to be formated before being used
943	 * 
944	 * @returns {self}
945	 * @private
946	 */
947	__optionsFormat: function() {
948		
949		if (typeof this.__options.animationDuration == 'number') {
950			this.__options.animationDuration = [this.__options.animationDuration, this.__options.animationDuration];
951		}
952		
953		if (typeof this.__options.delay == 'number') {
954			this.__options.delay = [this.__options.delay, this.__options.delay];
955		}
956		
957		if (typeof this.__options.delayTouch == 'number') {
958			this.__options.delayTouch = [this.__options.delayTouch, this.__options.delayTouch];
959		}
960		
961		if (typeof this.__options.theme == 'string') {
962			this.__options.theme = [this.__options.theme];
963		}
964		
965		// determine the future parent
966		if (typeof this.__options.parent == 'string') {
967			this.__options.parent = $(this.__options.parent);
968		}
969		
970		if (this.__options.trigger == 'hover') {
971			
972			this.__options.triggerOpen = {
973				mouseenter: true,
974				touchstart: true
975			};
976			
977			this.__options.triggerClose = {
978				mouseleave: true,
979				originClick: true,
980				touchleave: true
981			};
982		}
983		else if (this.__options.trigger == 'click') {
984			
985			this.__options.triggerOpen = {
986				click: true,
987				tap: true
988			};
989			
990			this.__options.triggerClose = {
991				click: true,
992				tap: true
993			};
994		}
995		
996		// for the plugins
997		this._trigger('options');
998		
999		return this;
1000	},
1001	
1002	/**
1003	 * Schedules or cancels the garbage collector task
1004	 *
1005	 * @returns {self}
1006	 * @private
1007	 */
1008	__prepareGC: function() {
1009		
1010		var self = this;
1011		
1012		// in case the selfDestruction option has been changed by a method call
1013		if (self.__options.selfDestruction) {
1014			
1015			// the GC task
1016			self.__garbageCollector = setInterval(function() {
1017				
1018				var now = new Date().getTime();
1019				
1020				// forget the old events
1021				self.__touchEvents = $.grep(self.__touchEvents, function(event, i) {
1022					// 1 minute
1023					return now - event.time > 60000;
1024				});
1025				
1026				// auto-destruct if the origin is gone
1027				if (!bodyContains(self._$origin)) {
1028					self.destroy();
1029				}
1030			}, 20000);
1031		}
1032		else {
1033			clearInterval(self.__garbageCollector);
1034		}
1035		
1036		return self;
1037	},
1038	
1039	/**
1040	 * Sets listeners on the origin if the open triggers require them.
1041	 * Unlike the listeners set at opening time, these ones
1042	 * remain even when the tooltip is closed. It has been made a
1043	 * separate method so it can be called when the triggers are
1044	 * changed in the options. Closing is handled in _open()
1045	 * because of the bindings that may be needed on the tooltip
1046	 * itself
1047	 *
1048	 * @returns {self}
1049	 * @private
1050	 */
1051	__prepareOrigin: function() {
1052		
1053		var self = this;
1054		
1055		// in case we're resetting the triggers
1056		self._$origin.off('.'+ self.__namespace +'-triggerOpen');
1057		
1058		// if the device is touch capable, even if only mouse triggers
1059		// are asked, we need to listen to touch events to know if the mouse
1060		// events are actually emulated (so we can ignore them)
1061		if (env.hasTouchCapability) {
1062			
1063			self._$origin.on(
1064				'touchstart.'+ self.__namespace +'-triggerOpen ' +
1065				'touchend.'+ self.__namespace +'-triggerOpen ' +
1066				'touchcancel.'+ self.__namespace +'-triggerOpen',
1067				function(event){
1068					self._touchRecordEvent(event);
1069				}
1070			);
1071		}
1072		
1073		// mouse click and touch tap work the same way
1074		if (	self.__options.triggerOpen.click
1075			||	(self.__options.triggerOpen.tap && env.hasTouchCapability)
1076		) {
1077			
1078			var eventNames = '';
1079			if (self.__options.triggerOpen.click) {
1080				eventNames += 'click.'+ self.__namespace +'-triggerOpen ';
1081			}
1082			if (self.__options.triggerOpen.tap && env.hasTouchCapability) {
1083				eventNames += 'touchend.'+ self.__namespace +'-triggerOpen';
1084			}
1085			
1086			self._$origin.on(eventNames, function(event) {
1087				if (self._touchIsMeaningfulEvent(event)) {
1088					self._open(event);
1089				}
1090			});
1091		}
1092		
1093		// mouseenter and touch start work the same way
1094		if (	self.__options.triggerOpen.mouseenter
1095			||	(self.__options.triggerOpen.touchstart && env.hasTouchCapability)
1096		) {
1097			
1098			var eventNames = '';
1099			if (self.__options.triggerOpen.mouseenter) {
1100				eventNames += 'mouseenter.'+ self.__namespace +'-triggerOpen ';
1101			}
1102			if (self.__options.triggerOpen.touchstart && env.hasTouchCapability) {
1103				eventNames += 'touchstart.'+ self.__namespace +'-triggerOpen';
1104			}
1105			
1106			self._$origin.on(eventNames, function(event) {
1107				if (	self._touchIsTouchEvent(event)
1108					||	!self._touchIsEmulatedEvent(event)
1109				) {
1110					self.__pointerIsOverOrigin = true;
1111					self._openShortly(event);
1112				}
1113			});
1114		}
1115		
1116		// info for the mouseleave/touchleave close triggers when they use a delay
1117		if (	self.__options.triggerClose.mouseleave
1118			||	(self.__options.triggerClose.touchleave && env.hasTouchCapability)
1119		) {
1120			
1121			var eventNames = '';
1122			if (self.__options.triggerClose.mouseleave) {
1123				eventNames += 'mouseleave.'+ self.__namespace +'-triggerOpen ';
1124			}
1125			if (self.__options.triggerClose.touchleave && env.hasTouchCapability) {
1126				eventNames += 'touchend.'+ self.__namespace +'-triggerOpen touchcancel.'+ self.__namespace +'-triggerOpen';
1127			}
1128			
1129			self._$origin.on(eventNames, function(event) {
1130				
1131				if (self._touchIsMeaningfulEvent(event)) {
1132					self.__pointerIsOverOrigin = false;
1133				}
1134			});
1135		}
1136		
1137		return self;
1138	},
1139	
1140	/**
1141	 * Do the things that need to be done only once after the tooltip
1142	 * HTML element it has been created. It has been made a separate
1143	 * method so it can be called when options are changed. Remember
1144	 * that the tooltip may actually exist in the DOM before it is
1145	 * opened, and present after it has been closed: it's the display
1146	 * plugin that takes care of handling it.
1147	 * 
1148	 * @returns {self}
1149	 * @private
1150	 */
1151	__prepareTooltip: function() {
1152		
1153		var self = this,
1154			p = self.__options.interactive ? 'auto' : '';
1155		
1156		// this will be useful to know quickly if the tooltip is in
1157		// the DOM or not 
1158		self._$tooltip
1159			.attr('id', self.__namespace)
1160			.css({
1161				// pointer events
1162				'pointer-events': p,
1163				zIndex: self.__options.zIndex
1164			});
1165		
1166		// themes
1167		// remove the old ones and add the new ones
1168		$.each(self.__previousThemes, function(i, theme) {
1169			self._$tooltip.removeClass(theme);
1170		});
1171		$.each(self.__options.theme, function(i, theme) {
1172			self._$tooltip.addClass(theme);
1173		});
1174		
1175		self.__previousThemes = $.merge([], self.__options.theme);
1176		
1177		return self;
1178	},
1179	
1180	/**
1181	 * Handles the scroll on any of the parents of the origin (when the
1182	 * tooltip is open)
1183	 *
1184	 * @param {object} event
1185	 * @returns {self}
1186	 * @private
1187	 */
1188	__scrollHandler: function(event) {
1189		
1190		var self = this;
1191		
1192		if (self.__options.triggerClose.scroll) {
1193			self._close(event);
1194		}
1195		else {
1196			
1197			// if the scroll happened on the window
1198			if (event.target === env.window.document) {
1199				
1200				// if the origin has a fixed lineage, window scroll will have no
1201				// effect on its position nor on the position of the tooltip
1202				if (!self.__Geometry.origin.fixedLineage) {
1203					
1204					// we don't need to do anything unless repositionOnScroll is true
1205					// because the tooltip will already have moved with the window
1206					// (and of course with the origin)
1207					if (self.__options.repositionOnScroll) {
1208						self.reposition(event);
1209					}
1210				}
1211			}
1212			// if the scroll happened on another parent of the tooltip, it means
1213			// that it's in a scrollable area and now needs to have its position
1214			// adjusted or recomputed, depending ont the repositionOnScroll
1215			// option. Also, if the origin is partly hidden due to a parent that
1216			// hides its overflow, we'll just hide (not close) the tooltip.
1217			else {
1218				
1219				var g = self.__geometry(),
1220					overflows = false;
1221				
1222				// a fixed position origin is not affected by the overflow hiding
1223				// of a parent
1224				if (self._$origin.css('position') != 'fixed') {
1225					
1226					self.__$originParents.each(function(i, el) {
1227						
1228						var $el = $(el),
1229							overflowX = $el.css('overflow-x'),
1230							overflowY = $el.css('overflow-y');
1231						
1232						if (overflowX != 'visible' || overflowY != 'visible') {
1233							
1234							var bcr = el.getBoundingClientRect();
1235							
1236							if (overflowX != 'visible') {
1237								
1238								if (	g.origin.windowOffset.left < bcr.left
1239									||	g.origin.windowOffset.right > bcr.right
1240								) {
1241									overflows = true;
1242									return false;
1243								}
1244							}
1245							
1246							if (overflowY != 'visible') {
1247								
1248								if (	g.origin.windowOffset.top < bcr.top
1249									||	g.origin.windowOffset.bottom > bcr.bottom
1250								) {
1251									overflows = true;
1252									return false;
1253								}
1254							}
1255						}
1256						
1257						// no need to go further if fixed, for the same reason as above
1258						if ($el.css('position') == 'fixed') {
1259							return false;
1260						}
1261					});
1262				}
1263				
1264				if (overflows) {
1265					self._$tooltip.css('visibility', 'hidden');
1266				}
1267				else {
1268					self._$tooltip.css('visibility', 'visible');
1269					
1270					// reposition
1271					if (self.__options.repositionOnScroll) {
1272						self.reposition(event);
1273					}
1274					// or just adjust offset
1275					else {
1276						
1277						// we have to use offset and not windowOffset because this way,
1278						// only the scroll distance of the scrollable areas are taken into
1279						// account (the scrolltop value of the main window must be
1280						// ignored since the tooltip already moves with it)
1281						var offsetLeft = g.origin.offset.left - self.__Geometry.origin.offset.left,
1282							offsetTop = g.origin.offset.top - self.__Geometry.origin.offset.top;
1283						
1284						// add the offset to the position initially computed by the display plugin
1285						self._$tooltip.css({
1286							left: self.__lastPosition.coord.left + offsetLeft,
1287							top: self.__lastPosition.coord.top + offsetTop
1288						});
1289					}
1290				}
1291			}
1292			
1293			self._trigger({
1294				type: 'scroll',
1295				event: event
1296			});
1297		}
1298		
1299		return self;
1300	},
1301	
1302	/**
1303	 * Changes the state of the tooltip
1304	 *
1305	 * @param {string} state
1306	 * @returns {self}
1307	 * @private
1308	 */
1309	__stateSet: function(state) {
1310		
1311		this.__state = state;
1312		
1313		this._trigger({
1314			type: 'state',
1315			state: state
1316		});
1317		
1318		return this;
1319	},
1320	
1321	/**
1322	 * Clear appearance timeouts
1323	 *
1324	 * @returns {self}
1325	 * @private
1326	 */
1327	__timeoutsClear: function() {
1328		
1329		// there is only one possible open timeout: the delayed opening
1330		// when the mouseenter/touchstart open triggers are used
1331		clearTimeout(this.__timeouts.open);
1332		this.__timeouts.open = null;
1333		
1334		// ... but several close timeouts: the delayed closing when the
1335		// mouseleave close trigger is used and the timer option
1336		$.each(this.__timeouts.close, function(i, timeout) {
1337			clearTimeout(timeout);
1338		});
1339		this.__timeouts.close = [];
1340		
1341		return this;
1342	},
1343	
1344	/**
1345	 * Start the tracker that will make checks at regular intervals
1346	 * 
1347	 * @returns {self}
1348	 * @private
1349	 */
1350	__trackerStart: function() {
1351		
1352		var self = this,
1353			$content = self._$tooltip.find('.tooltipster-content');
1354		
1355		// get the initial content size
1356		if (self.__options.trackTooltip) {
1357			self.__contentBcr = $content[0].getBoundingClientRect();
1358		}
1359		
1360		self.__tracker = setInterval(function() {
1361			
1362			// if the origin or tooltip elements have been removed.
1363			// Note: we could destroy the instance now if the origin has
1364			// been removed but we'll leave that task to our garbage collector
1365			if (!bodyContains(self._$origin) || !bodyContains(self._$tooltip)) {
1366				self._close();
1367			}
1368			// if everything is alright
1369			else {
1370				
1371				// compare the former and current positions of the origin to reposition
1372				// the tooltip if need be
1373				if (self.__options.trackOrigin) {
1374					
1375					var g = self.__geometry(),
1376						identical = false;
1377					
1378					// compare size first (a change requires repositioning too)
1379					if (areEqual(g.origin.size, self.__Geometry.origin.size)) {
1380						
1381						// for elements that have a fixed lineage (see __geometry()), we track the
1382						// top and left properties (relative to window)
1383						if (self.__Geometry.origin.fixedLineage) {
1384							if (areEqual(g.origin.windowOffset, self.__Geometry.origin.windowOffset)) {
1385								identical = true;
1386							}
1387						}
1388						// otherwise, track total offset (relative to document)
1389						else {
1390							if (areEqual(g.origin.offset, self.__Geometry.origin.offset)) {
1391								identical = true;
1392							}
1393						}
1394					}
1395					
1396					if (!identical) {
1397						
1398						// close the tooltip when using the mouseleave close trigger
1399						// (see https://github.com/iamceege/tooltipster/pull/253)
1400						if (self.__options.triggerClose.mouseleave) {
1401							self._close();
1402						}
1403						else {
1404							self.reposition();
1405						}
1406					}
1407				}
1408				
1409				if (self.__options.trackTooltip) {
1410					
1411					var currentBcr = $content[0].getBoundingClientRect();
1412					
1413					if (	currentBcr.height !== self.__contentBcr.height
1414						||	currentBcr.width !== self.__contentBcr.width
1415					) {
1416						self.reposition();
1417						self.__contentBcr = currentBcr;
1418					}
1419				}
1420			}
1421		}, self.__options.trackerInterval);
1422		
1423		return self;
1424	},
1425	
1426	/**
1427	 * Closes the tooltip (after the closing delay)
1428	 * 
1429	 * @param event
1430	 * @param callback
1431	 * @returns {self}
1432	 * @protected
1433	 */
1434	_close: function(event, callback) {
1435		
1436		var self = this,
1437			ok = true;
1438		
1439		self._trigger({
1440			type: 'close',
1441			event: event,
1442			stop: function() {
1443				ok = false;
1444			}
1445		});
1446		
1447		// a destroying tooltip may not refuse to close
1448		if (ok || self.__destroying) {
1449			
1450			// save the method custom callback and cancel any open method custom callbacks
1451			if (callback) self.__callbacks.close.push(callback);
1452			self.__callbacks.open = [];
1453			
1454			// clear open/close timeouts
1455			self.__timeoutsClear();
1456			
1457			var finishCallbacks = function() {
1458				
1459				// trigger any close method custom callbacks and reset them
1460				$.each(self.__callbacks.close, function(i,c) {
1461					c.call(self, self, {
1462						event: event,
1463						origin: self._$origin[0]
1464					});
1465				});
1466				
1467				self.__callbacks.close = [];
1468			};
1469			
1470			if (self.__state != 'closed') {
1471				
1472				var necessary = true,
1473					d = new Date(),
1474					now = d.getTime(),
1475					newClosingTime = now + self.__options.animationDuration[1];
1476				
1477				// the tooltip may already already be disappearing, but if a new
1478				// call to close() is made after the animationDuration was changed
1479				// to 0 (for example), we ought to actually close it sooner than
1480				// previously scheduled. In that case it should be noted that the
1481				// browser will not adapt the animation duration to the new
1482				// animationDuration that was set after the start of the closing
1483				// animation.
1484				// Note: the same thing could be considered at opening, but is not
1485				// really useful since the tooltip is actually opened immediately
1486				// upon a call to _open(). Since it would not make the opening
1487				// animation finish sooner, its sole impact would be to trigger the
1488				// state event and the open callbacks sooner than the actual end of
1489				// the opening animation, which is not great.
1490				if (self.__state == 'disappearing') {
1491					
1492					if (newClosingTime > self.__closingTime) {
1493						necessary = false;
1494					}
1495				}
1496				
1497				if (necessary) {
1498					
1499					self.__closingTime = newClosingTime;
1500					
1501					if (self.__state != 'disappearing') {
1502						self.__stateSet('disappearing');
1503					}
1504					
1505					var finish = function() {
1506						
1507						// stop the tracker
1508						clearInterval(self.__tracker);
1509						
1510						// a "beforeClose" option has been asked several times but would
1511						// probably useless since the content element is still accessible
1512						// via ::content(), and because people can always use listeners
1513						// inside their content to track what's going on. For the sake of
1514						// simplicity, this has been denied. Bur for the rare people who
1515						// really need the option (for old browsers or for the case where
1516						// detaching the content is actually destructive, for file or
1517						// password inputs for example), this event will do the work.
1518						self._trigger({
1519							type: 'closing',
1520							event: event
1521						});
1522						
1523						// unbind listeners which are no longer needed
1524						
1525						self._$tooltip
1526							.off('.'+ self.__namespace +'-triggerClose')
1527							.removeClass('tooltipster-dying');
1528						
1529						// orientationchange, scroll and resize listeners
1530						$(env.window).off('.'+ self.__namespace +'-triggerClose');
1531						
1532						// scroll listeners
1533						self.__$originParents.each(function(i, el) {
1534							$(el).off('scroll.'+ self.__namespace +'-triggerClose');
1535						});
1536						// clear the array to prevent memory leaks
1537						self.__$originParents = null;
1538						
1539						$('body').off('.'+ self.__namespace +'-triggerClose');
1540						
1541						self._$origin.off('.'+ self.__namespace +'-triggerClose');
1542						
1543						self._off('dismissable');
1544						
1545						// a plugin that would like to remove the tooltip from the
1546						// DOM when closed should bind on this
1547						self.__stateSet('closed');
1548						
1549						// trigger event
1550						self._trigger({
1551							type: 'after',
1552							event: event
1553						});
1554						
1555						// call our constructor custom callback function
1556						if (self.__options.functionAfter) {
1557							self.__options.functionAfter.call(self, self, {
1558								event: event
1559							});
1560						}
1561						
1562						// call our method custom callbacks functions
1563						finishCallbacks();
1564					};
1565					
1566					if (env.hasTransitions) {
1567						
1568						self._$tooltip.css({
1569							'-moz-animation-duration': self.__options.animationDuration[1] + 'ms',
1570							'-ms-animation-duration': self.__options.animationDuration[1] + 'ms',
1571							'-o-animation-duration': self.__options.animationDuration[1] + 'ms',
1572							'-webkit-animation-duration': self.__options.animationDuration[1] + 'ms',
1573							'animation-duration': self.__options.animationDuration[1] + 'ms',
1574							'transition-duration': self.__options.animationDuration[1] + 'ms'
1575						});
1576						
1577						self._$tooltip
1578							// clear both potential open and close tasks
1579							.clearQueue()
1580							.removeClass('tooltipster-show')
1581							// for transitions only
1582							.addClass('tooltipster-dying');
1583						
1584						if (self.__options.animationDuration[1] > 0) {
1585							self._$tooltip.delay(self.__options.animationDuration[1]);
1586						}
1587						
1588						self._$tooltip.queue(finish);
1589					}
1590					else {
1591						
1592						self._$tooltip
1593							.stop()
1594							.fadeOut(self.__options.animationDuration[1], finish);
1595					}
1596				}
1597			}
1598			// if the tooltip is already closed, we still need to trigger
1599			// the method custom callbacks
1600			else {
1601				finishCallbacks();
1602			}
1603		}
1604		
1605		return self;
1606	},
1607	
1608	/**
1609	 * For internal use by plugins, if needed
1610	 * 
1611	 * @returns {self}
1612	 * @protected
1613	 */
1614	_off: function() {
1615		this.__$emitterPrivate.off.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
1616		return this;
1617	},
1618	
1619	/**
1620	 * For internal use by plugins, if needed
1621	 *
1622	 * @returns {self}
1623	 * @protected
1624	 */
1625	_on: function() {
1626		this.__$emitterPrivate.on.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
1627		return this;
1628	},
1629	
1630	/**
1631	 * For internal use by plugins, if needed
1632	 *
1633	 * @returns {self}
1634	 * @protected
1635	 */
1636	_one: function() {
1637		this.__$emitterPrivate.one.apply(this.__$emitterPrivate, Array.prototype.slice.apply(arguments));
1638		return this;
1639	},
1640	
1641	/**
1642	 * Opens the tooltip right away
1643	 *
1644	 * @param event
1645	 * @param callback
vendor: 6,093 bytes, lines 1645-1837
1645
1646	 * @returns {self}
1647	 * @protected
1648	 */
1649	_open: function(event, callback) {
1650		
1651		var self = this;
1652		
1653		// if the destruction process has not begun and if this was not
1654		// triggered by an unwanted emulated click event
1655		if (!self.__destroying) {
1656			
1657			// check that the origin is still in the DOM
1658			if (	bodyContains(self._$origin)
1659				// if the tooltip is enabled
1660				&&	self.__enabled
1661			) {
1662				
1663				var ok = true;
1664				
1665				// if the tooltip is not open yet, we need to call functionBefore.
1666				// otherwise we can jst go on
1667				if (self.__state == 'closed') {
1668					
1669					// trigger an event. The event.stop function allows the callback
1670					// to prevent the opening of the tooltip
1671					self._trigger({
1672						type: 'before',
1673						event: event,
1674						stop: function() {
1675							ok = false;
1676						}
1677					});
1678					
1679					if (ok && self.__options.functionBefore) {
1680						
1681						// call our custom function before continuing
1682						ok = self.__options.functionBefore.call(self, self, {
1683							event: event,
1684							origin: self._$origin[0]
1685						});
1686					}
1687				}
1688				
1689				if (ok !== false) {
1690					
1691					// if there is some content
1692					if (self.__Content !== null) {
1693						
1694						// save the method callback and cancel close method callbacks
1695						if (callback) {
1696							self.__callbacks.open.push(callback);
1697						}
1698						self.__callbacks.close = [];
1699						
1700						// get rid of any appearance timeouts
1701						self.__timeoutsClear();
1702						
1703						var extraTime,
1704							finish = function() {
1705								
1706								if (self.__state != 'stable') {
1707									self.__stateSet('stable');
1708								}
1709								
1710								// trigger any open method custom callbacks and reset them
1711								$.each(self.__callbacks.open, function(i,c) {
1712									c.call(self, self, {
1713										origin: self._$origin[0],
1714										tooltip: self._$tooltip[0]
1715									});
1716								});
1717								
1718								self.__callbacks.open = [];
1719							};
1720						
1721						// if the tooltip is already open
1722						if (self.__state !== 'closed') {
1723							
1724							// the timer (if any) will start (or restart) right now
1725							extraTime = 0;
1726							
1727							// if it was disappearing, cancel that
1728							if (self.__state === 'disappearing') {
1729								
1730								self.__stateSet('appearing');
1731								
1732								if (env.hasTransitions) {
1733									
1734									self._$tooltip
1735										.clearQueue()
1736										.removeClass('tooltipster-dying')
1737										.addClass('tooltipster-show');
1738									
1739									if (self.__options.animationDuration[0] > 0) {
1740										self._$tooltip.delay(self.__options.animationDuration[0]);
1741									}
1742									
1743									self._$tooltip.queue(finish);
1744								}
1745								else {
1746									// in case the tooltip was currently fading out, bring it back
1747									// to life
1748									self._$tooltip
1749										.stop()
1750										.fadeIn(finish);
1751								}
1752							}
1753							// if the tooltip is already open, we still need to trigger the method
1754							// custom callback
1755							else if (self.__state == 'stable') {
1756								finish();
1757							}
1758						}
1759						// if the tooltip isn't already open, open it
1760						else {
1761							
1762							// a plugin must bind on this and store the tooltip in this._$tooltip
1763							self.__stateSet('appearing');
1764							
1765							// the timer (if any) will start when the tooltip has fully appeared
1766							// after its transition
1767							extraTime = self.__options.animationDuration[0];
1768							
1769							// insert the content inside the tooltip
1770							self.__contentInsert();
1771							
1772							// reposition the tooltip and attach to the DOM
1773							self.reposition(event, true);
1774							
1775							// animate in the tooltip. If the display plugin wants no css
1776							// animations, it may override the animation option with a
1777							// dummy value that will produce no effect
1778							if (env.hasTransitions) {
1779								
1780								// note: there seems to be an issue with start animations which
1781								// are randomly not played on fast devices in both Chrome and FF,
1782								// couldn't find a way to solve it yet. It seems that applying
1783								// the classes before appending to the DOM helps a little, but
1784								// it messes up some CSS transitions. The issue almost never
1785								// happens when delay[0]==0 though
1786								self._$tooltip
1787									.addClass('tooltipster-'+ self.__options.animation)
1788									.addClass('tooltipster-initial')
1789									.css({
1790										'-moz-animation-duration': self.__options.animationDuration[0] + 'ms',
1791										'-ms-animation-duration': self.__options.animationDuration[0] + 'ms',
1792										'-o-animation-duration': self.__options.animationDuration[0] + 'ms',
1793										'-webkit-animation-duration': self.__options.animationDuration[0] + 'ms',
1794										'animation-duration': self.__options.animationDuration[0] + 'ms',
1795										'transition-duration': self.__options.animationDuration[0] + 'ms'
1796									});
1797								
1798								setTimeout(
1799									function() {
1800										
1801										// a quick hover may have already triggered a mouseleave
1802										if (self.__state != 'closed') {
1803											
1804											self._$tooltip
1805												.addClass('tooltipster-show')
1806												.removeClass('tooltipster-initial');
1807											
1808											if (self.__options.animationDuration[0] > 0) {
1809												self._$tooltip.delay(self.__options.animationDuration[0]);
1810											}
1811											
1812											self._$tooltip.queue(finish);
1813										}
1814									},
1815									0
1816								);
1817							}
1818							else {
1819								
1820								// old browsers will have to live with this
1821								self._$tooltip
1822									.css('display', 'none')
1823									.fadeIn(self.__options.animationDuration[0], finish);
1824							}
1825							
1826							// checks if the origin is removed while the tooltip is open
1827							self.__trackerStart();
1828							
1829							// NOTE: the listeners below have a '-triggerClose' namespace
1830							// because we'll remove them when the tooltip closes (unlike
1831							// the '-triggerOpen' listeners). So some of them are actually
1832							// not about close triggers, rather about positioning.
1833							
1834							$(env.window)
1835								// reposition on resize
1836								.on('resize.'+ self.__namespace +'-triggerClose', function(e) {
1837									
vendor: 5,688 bytes, lines 1837-1995
1837self.reposition(e);
1838								})
1839								// same as below for parents
1840								.on('scroll.'+ self.__namespace +'-triggerClose', function(e) {
1841									self.__scrollHandler(e);
1842								});
1843							
1844							self.__$originParents = self._$origin.parents();
1845							
1846							// scrolling may require the tooltip to be moved or even
1847							// repositioned in some cases
1848							self.__$originParents.each(function(i, parent) {
1849								
1850								$(parent).on('scroll.'+ self.__namespace +'-triggerClose', function(e) {
1851									self.__scrollHandler(e);
1852								});
1853							});
1854							
1855							if (	self.__options.triggerClose.mouseleave
1856								||	(self.__options.triggerClose.touchleave && env.hasTouchCapability)
1857							) {
1858								
1859								// we use an event to allow users/plugins to control when the mouseleave/touchleave
1860								// close triggers will come to action. It allows to have more triggering elements
1861								// than just the origin and the tooltip for example, or to cancel/delay the closing,
1862								// or to make the tooltip interactive even if it wasn't when it was open, etc.
1863								self._on('dismissable', function(event) {
1864									
1865									if (event.dismissable) {
1866										
1867										if (event.delay) {
1868											
1869											timeout = setTimeout(function() {
1870												// event.event may be undefined
1871												self._close(event.event);
1872											}, event.delay);
1873											
1874											self.__timeouts.close.push(timeout);
1875										}
1876										else {
1877											self._close(event);
1878										}
1879									}
1880									else {
1881										clearTimeout(timeout);
1882									}
1883								});
1884								
1885								// now set the listeners that will trigger 'dismissable' events
1886								var $elements = self._$origin,
1887									eventNamesIn = '',
1888									eventNamesOut = '',
1889									timeout = null;
1890								
1891								// if we have to allow interaction, bind on the tooltip too
1892								if (self.__options.interactive) {
1893									$elements = $elements.add(self._$tooltip);
1894								}
1895								
1896								if (self.__options.triggerClose.mouseleave) {
1897									eventNamesIn += 'mouseenter.'+ self.__namespace +'-triggerClose ';
1898									eventNamesOut += 'mouseleave.'+ self.__namespace +'-triggerClose ';
1899								}
1900								if (self.__options.triggerClose.touchleave && env.hasTouchCapability) {
1901									eventNamesIn += 'touchstart.'+ self.__namespace +'-triggerClose';
1902									eventNamesOut += 'touchend.'+ self.__namespace +'-triggerClose touchcancel.'+ self.__namespace +'-triggerClose';
1903								}
1904								
1905								$elements
1906									// close after some time spent outside of the elements
1907									.on(eventNamesOut, function(event) {
1908										
1909										// it's ok if the touch gesture ended up to be a swipe,
1910										// it's still a "touch leave" situation
1911										if (	self._touchIsTouchEvent(event)
1912											||	!self._touchIsEmulatedEvent(event)
1913										) {
1914											
1915											var delay = (event.type == 'mouseleave') ?
1916												self.__options.delay :
1917												self.__options.delayTouch;
1918											
1919											self._trigger({
1920												delay: delay[1],
1921												dismissable: true,
1922												event: event,
1923												type: 'dismissable'
1924											});
1925										}
1926									})
1927									// suspend the mouseleave timeout when the pointer comes back
1928									// over the elements
1929									.on(eventNamesIn, function(event) {
1930										
1931										// it's also ok if the touch event is a swipe gesture
1932										if (	self._touchIsTouchEvent(event)
1933											||	!self._touchIsEmulatedEvent(event)
1934										) {
1935											self._trigger({
1936												dismissable: false,
1937												event: event,
1938												type: 'dismissable'
1939											});
1940										}
1941									});
1942							}
1943							
1944							// close the tooltip when the origin gets a mouse click (common behavior of
1945							// native tooltips)
1946							if (self.__options.triggerClose.originClick) {
1947								
1948								self._$origin.on('click.'+ self.__namespace + '-triggerClose', function(event) {
1949									
1950									// we could actually let a tap trigger this but this feature just
1951									// does not make sense on touch devices
1952									if (	!self._touchIsTouchEvent(event)
1953										&&	!self._touchIsEmulatedEvent(event)
1954									) {
1955										self._close(event);
1956									}
1957								});
1958							}
1959							
1960							// set the same bindings for click and touch on the body to close the tooltip
1961							if (	self.__options.triggerClose.click
1962								||	(self.__options.triggerClose.tap && env.hasTouchCapability)
1963							) {
1964								
1965								// don't set right away since the click/tap event which triggered this method
1966								// (if it was a click/tap) is going to bubble up to the body, we don't want it
1967								// to close the tooltip immediately after it opened
1968								setTimeout(function() {
1969									
1970									if (self.__state != 'closed') {
1971										
1972										var eventNames = '';
1973										if (self.__options.triggerClose.click) {
1974											eventNames += 'click.'+ self.__namespace +'-triggerClose ';
1975										}
1976										if (self.__options.triggerClose.tap && env.hasTouchCapability) {
1977											eventNames += 'touchend.'+ self.__namespace +'-triggerClose';
1978										}
1979										
1980										$('body').on(eventNames, function(event) {
1981											
1982											if (self._touchIsMeaningfulEvent(event)) {
1983												
1984												self._touchRecordEvent(event);
1985												
1986												if (!self.__options.interactive || !$.contains(self._$tooltip[0], event.target)) {
1987													self._close(event);
1988												}
1989											}
1990										});
1991										
1992										// needed to detect and ignore swiping
1993										if (self.__options.triggerClose.tap && env.hasTouchCapability) {
1994											
1995											$('body')
vendor: 11,059 bytes, lines 1995-2477
1995.on('touchstart.'+ self.__namespace +'-triggerClose', function(event) {
1996												self._touchRecordEvent(event);
1997											});
1998										}
1999									}
2000								}, 0);
2001							}
2002							
2003							self._trigger('ready');
2004							
2005							// call our custom callback
2006							if (self.__options.functionReady) {
2007								self.__options.functionReady.call(self, self, {
2008									origin: self._$origin[0],
2009									tooltip: self._$tooltip[0]
2010								});
2011							}
2012						}
2013						
2014						// if we have a timer set, let the countdown begin
2015						if (self.__options.timer > 0) {
2016							
2017							var timeout = setTimeout(function() {
2018								self._close();
2019							}, self.__options.timer + extraTime);
2020							
2021							self.__timeouts.close.push(timeout);
2022						}
2023					}
2024				}
2025			}
2026		}
2027		
2028		return self;
2029	},
2030	
2031	/**
2032	 * When using the mouseenter/touchstart open triggers, this function will
2033	 * schedule the opening of the tooltip after the delay, if there is one
2034	 *
2035	 * @param event
2036	 * @returns {self}
2037	 * @protected
2038 	 */
2039	_openShortly: function(event) {
2040		
2041		var self = this,
2042			ok = true;
2043		
2044		if (self.__state != 'stable' && self.__state != 'appearing') {
2045			
2046			// if a timeout is not already running
2047			if (!self.__timeouts.open) {
2048				
2049				self._trigger({
2050					type: 'start',
2051					event: event,
2052					stop: function() {
2053						ok = false;
2054					}
2055				});
2056				
2057				if (ok) {
2058					
2059					var delay = (event.type.indexOf('touch') == 0) ?
2060						self.__options.delayTouch :
2061						self.__options.delay;
2062					
2063					if (delay[0]) {
2064						
2065						self.__timeouts.open = setTimeout(function() {
2066							
2067							self.__timeouts.open = null;
2068							
2069							// open only if the pointer (mouse or touch) is still over the origin.
2070							// The check on the "meaningful event" can only be made here, after some
2071							// time has passed (to know if the touch was a swipe or not)
2072							if (self.__pointerIsOverOrigin && self._touchIsMeaningfulEvent(event)) {
2073								
2074								// signal that we go on
2075								self._trigger('startend');
2076								
2077								self._open(event);
2078							}
2079							else {
2080								// signal that we cancel
2081								self._trigger('startcancel');
2082							}
2083						}, delay[0]);
2084					}
2085					else {
2086						// signal that we go on
2087						self._trigger('startend');
2088						
2089						self._open(event);
2090					}
2091				}
2092			}
2093		}
2094		
2095		return self;
2096	},
2097	
2098	/**
2099	 * Meant for plugins to get their options
2100	 * 
2101	 * @param {string} pluginName The name of the plugin that asks for its options
2102	 * @param {object} defaultOptions The default options of the plugin
2103	 * @returns {object} The options
2104	 * @protected
2105	 */
2106	_optionsExtract: function(pluginName, defaultOptions) {
2107		
2108		var self = this,
2109			options = $.extend(true, {}, defaultOptions);
2110		
2111		// if the plugin options were isolated in a property named after the
2112		// plugin, use them (prevents conflicts with other plugins)
2113		var pluginOptions = self.__options[pluginName];
2114		
2115		// if not, try to get them as regular options
2116		if (!pluginOptions){
2117			
2118			pluginOptions = {};
2119			
2120			$.each(defaultOptions, function(optionName, value) {
2121				
2122				var o = self.__options[optionName];
2123				
2124				if (o !== undefined) {
2125					pluginOptions[optionName] = o;
2126				}
2127			});
2128		}
2129		
2130		// let's merge the default options and the ones that were provided. We'd want
2131		// to do a deep copy but not let jQuery merge arrays, so we'll do a shallow
2132		// extend on two levels, that will be enough if options are not more than 1
2133		// level deep
2134		$.each(options, function(optionName, value) {
2135			
2136			if (pluginOptions[optionName] !== undefined) {
2137				
2138				if ((		typeof value == 'object'
2139						&&	!(value instanceof Array)
2140						&&	value != null
2141					)
2142					&&
2143					(		typeof pluginOptions[optionName] == 'object'
2144						&&	!(pluginOptions[optionName] instanceof Array)
2145						&&	pluginOptions[optionName] != null
2146					)
2147				) {
2148					$.extend(options[optionName], pluginOptions[optionName]);
2149				}
2150				else {
2151					options[optionName] = pluginOptions[optionName];
2152				}
2153			}
2154		});
2155		
2156		return options;
2157	},
2158	
2159	/**
2160	 * Used at instantiation of the plugin, or afterwards by plugins that activate themselves
2161	 * on existing instances
2162	 * 
2163	 * @param {object} pluginName
2164	 * @returns {self}
2165	 * @protected
2166	 */
2167	_plug: function(pluginName) {
2168		
2169		var plugin = $.tooltipster._plugin(pluginName);
2170		
2171		if (plugin) {
2172			
2173			// if there is a constructor for instances
2174			if (plugin.instance) {
2175				
2176				// proxy non-private methods on the instance to allow new instance methods
2177				$.tooltipster.__bridge(plugin.instance, this, plugin.name);
2178			}
2179		}
2180		else {
2181			throw new Error('The "'+ pluginName +'" plugin is not defined');
2182		}
2183		
2184		return this;
2185	},
2186	
2187	/**
2188	 * This will return true if the event is a mouse event which was
2189	 * emulated by the browser after a touch event. This allows us to
2190	 * really dissociate mouse and touch triggers.
2191	 * 
2192	 * There is a margin of error if a real mouse event is fired right
2193	 * after (within the delay shown below) a touch event on the same
2194	 * element, but hopefully it should not happen often.
2195	 * 
2196	 * @returns {boolean}
2197	 * @protected
2198	 */
2199	_touchIsEmulatedEvent: function(event) {
2200		
2201		var isEmulated = false,
2202			now = new Date().getTime();
2203		
2204		for (var i = this.__touchEvents.length - 1; i >= 0; i--) {
2205			
2206			var e = this.__touchEvents[i];
2207			
2208			// delay, in milliseconds. It's supposed to be 300ms in
2209			// most browsers (350ms on iOS) to allow a double tap but
2210			// can be less (check out FastClick for more info)
2211			if (now - e.time < 500) {
2212				
2213				if (e.target === event.target) {
2214					isEmulated = true;
2215				}
2216			}
2217			else {
2218				break;
2219			}
2220		}
2221		
2222		return isEmulated;
2223	},
2224	
2225	/**
2226	 * Returns false if the event was an emulated mouse event or
2227	 * a touch event involved in a swipe gesture.
2228	 * 
2229	 * @param {object} event
2230	 * @returns {boolean}
2231	 * @protected
2232	 */
2233	_touchIsMeaningfulEvent: function(event) {
2234		return (
2235				(this._touchIsTouchEvent(event) && !this._touchSwiped(event.target))
2236			||	(!this._touchIsTouchEvent(event) && !this._touchIsEmulatedEvent(event))
2237		);
2238	},
2239	
2240	/**
2241	 * Checks if an event is a touch event
2242	 * 
2243	 * @param {object} event
2244	 * @returns {boolean}
2245	 * @protected
2246	 */
2247	_touchIsTouchEvent: function(event){
2248		return event.type.indexOf('touch') == 0;
2249	},
2250	
2251	/**
2252	 * Store touch events for a while to detect swiping and emulated mouse events
2253	 * 
2254	 * @param {object} event
2255	 * @returns {self}
2256	 * @protected
2257	 */
2258	_touchRecordEvent: function(event) {
2259		
2260		if (this._touchIsTouchEvent(event)) {
2261			event.time = new Date().getTime();
2262			this.__touchEvents.push(event);
2263		}
2264		
2265		return this;
2266	},
2267	
2268	/**
2269	 * Returns true if a swipe happened after the last touchstart event fired on
2270	 * event.target.
2271	 * 
2272	 * We need to differentiate a swipe from a tap before we let the event open
2273	 * or close the tooltip. A swipe is when a touchmove (scroll) event happens
2274	 * on the body between the touchstart and the touchend events of an element.
2275	 * 
2276	 * @param {object} target The HTML element that may have triggered the swipe
2277	 * @returns {boolean}
2278	 * @protected
2279	 */
2280	_touchSwiped: function(target) {
2281		
2282		var swiped = false;
2283		
2284		for (var i = this.__touchEvents.length - 1; i >= 0; i--) {
2285			
2286			var e = this.__touchEvents[i];
2287			
2288			if (e.type == 'touchmove') {
2289				swiped = true;
2290				break;
2291			}
2292			else if (
2293				e.type == 'touchstart'
2294				&&	target === e.target
2295			) {
2296				break;
2297			}
2298		}
2299		
2300		return swiped;
2301	},
2302	
2303	/**
2304	 * Triggers an event on the instance emitters
2305	 * 
2306	 * @returns {self}
2307	 * @protected
2308	 */
2309	_trigger: function() {
2310		
2311		var args = Array.prototype.slice.apply(arguments);
2312		
2313		if (typeof args[0] == 'string') {
2314			args[0] = { type: args[0] };
2315		}
2316		
2317		// add properties to the event
2318		args[0].instance = this;
2319		args[0].origin = this._$origin ? this._$origin[0] : null;
2320		args[0].tooltip = this._$tooltip ? this._$tooltip[0] : null;
2321		
2322		// note: the order of emitters matters
2323		this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate, args);
2324		$.tooltipster._trigger.apply($.tooltipster, args);
2325		this.__$emitterPublic.trigger.apply(this.__$emitterPublic, args);
2326		
2327		return this;
2328	},
2329	
2330	/**
2331	 * Deactivate a plugin on this instance
2332	 * 
2333	 * @returns {self}
2334	 * @protected
2335	 */
2336	_unplug: function(pluginName) {
2337		
2338		var self = this;
2339		
2340		// if the plugin has been activated on this instance
2341		if (self[pluginName]) {
2342			
2343			var plugin = $.tooltipster._plugin(pluginName);
2344			
2345			// if there is a constructor for instances
2346			if (plugin.instance) {
2347				
2348				// unbridge
2349				$.each(plugin.instance, function(methodName, fn) {
2350					
2351					// if the method exists (privates methods do not) and comes indeed from
2352					// this plugin (may be missing or come from a conflicting plugin).
2353					if (	self[methodName]
2354						&&	self[methodName].bridged === self[pluginName]
2355					) {
2356						delete self[methodName];
2357					}
2358				});
2359			}
2360			
2361			// destroy the plugin
2362			if (self[pluginName].__destroy) {
2363				self[pluginName].__destroy();
2364			}
2365			
2366			// remove the reference to the plugin instance
2367			delete self[pluginName];
2368		}
2369		
2370		return self;
2371	},
2372	
2373	/**
2374	 * @see self::_close
2375	 * @returns {self}
2376	 * @public
2377	 */
2378	close: function(callback) {
2379		
2380		if (!this.__destroyed) {
2381			this._close(null, callback);
2382		}
2383		else {
2384			this.__destroyError();
2385		}
2386		
2387		return this;
2388	},
2389	
2390	/**
2391	 * Sets or gets the content of the tooltip
2392	 * 
2393	 * @returns {mixed|self}
2394	 * @public
2395	 */
2396	content: function(content) {
2397		
2398		var self = this;
2399		
2400		// getter method
2401		if (content === undefined) {
2402			return self.__Content;
2403		}
2404		// setter method
2405		else {
2406			
2407			if (!self.__destroyed) {
2408				
2409				// change the content
2410				self.__contentSet(content);
2411				
2412				if (self.__Content !== null) {
2413					
2414					// update the tooltip if it is open
2415					if (self.__state !== 'closed') {
2416						
2417						// reset the content in the tooltip
2418						self.__contentInsert();
2419						
2420						// reposition and resize the tooltip
2421						self.reposition();
2422						
2423						// if we want to play a little animation showing the content changed
2424						if (self.__options.updateAnimation) {
2425							
2426							if (env.hasTransitions) {
2427								
2428								// keep the reference in the local scope
2429								var animation = self.__options.updateAnimation;
2430								
2431								self._$tooltip.addClass('tooltipster-update-'+ animation);
2432								
2433								// remove the class after a while. The actual duration of the
2434								// update animation may be shorter, it's set in the CSS rules
2435								setTimeout(function() {
2436									
2437									if (self.__state != 'closed') {
2438										
2439										self._$tooltip.removeClass('tooltipster-update-'+ animation);
2440									}
2441								}, 1000);
2442							}
2443							else {
2444								self._$tooltip.fadeTo(200, 0.5, function() {
2445									if (self.__state != 'closed') {
2446										self._$tooltip.fadeTo(200, 1);
2447									}
2448								});
2449							}
2450						}
2451					}
2452				}
2453				else {
2454					self._close();
2455				}
2456			}
2457			else {
2458				self.__destroyError();
2459			}
2460			
2461			return self;
2462		}
2463	},
2464	
2465	/**
2466	 * Destroys the tooltip
2467	 * 
2468	 * @returns {self}
2469	 * @public
2470	 */
2471	destroy: function() {
2472		
2473		var self = this;
2474		
2475		if (!self.__destroyed) {
2476			
2477			if
vendor: 3,706 bytes, lines 2477-2637
2477 (!self.__destroying) {
2478				
2479				self.__destroying = true;
2480				
2481				self._close(null, function() {
2482					
2483					self._trigger('destroy');
2484					
2485					self.__destroying = false;
2486					self.__destroyed = true;
2487					
2488					self._$origin
2489						.removeData(self.__namespace)
2490						// remove the open trigger listeners
2491						.off('.'+ self.__namespace +'-triggerOpen');
2492					
2493					// remove the touch listener
2494					$('body').off('.' + self.__namespace +'-triggerOpen');
2495					
2496					var ns = self._$origin.data('tooltipster-ns');
2497					
2498					// if the origin has been removed from DOM, its data may
2499					// well have been destroyed in the process and there would
2500					// be nothing to clean up or restore
2501					if (ns) {
2502						
2503						// if there are no more tooltips on this element
2504						if (ns.length === 1) {
2505							
2506							// optional restoration of a title attribute
2507							var title = null;
2508							if (self.__options.restoration == 'previous') {
2509								title = self._$origin.data('tooltipster-initialTitle');
2510							}
2511							else if (self.__options.restoration == 'current') {
2512								
2513								// old school technique to stringify when outerHTML is not supported
2514								title = (typeof self.__Content == 'string') ?
2515									self.__Content :
2516									$('<div></div>').append(self.__Content).html();
2517							}
2518							
2519							if (title) {
2520								self._$origin.attr('title', title);
2521							}
2522							
2523							// final cleaning
2524							
2525							self._$origin.removeClass('tooltipstered');
2526							
2527							self._$origin
2528								.removeData('tooltipster-ns')
2529								.removeData('tooltipster-initialTitle');
2530						}
2531						else {
2532							// remove the instance namespace from the list of namespaces of
2533							// tooltips present on the element
2534							ns = $.grep(ns, function(el, i) {
2535								return el !== self.__namespace;
2536							});
2537							self._$origin.data('tooltipster-ns', ns);
2538						}
2539					}
2540					
2541					// last event
2542					self._trigger('destroyed');
2543					
2544					// unbind private and public event listeners
2545					self._off();
2546					self.off();
2547					
2548					// remove external references, just in case
2549					self.__Content = null;
2550					self.__$emitterPrivate = null;
2551					self.__$emitterPublic = null;
2552					self.__options.parent = null;
2553					self._$origin = null;
2554					self._$tooltip = null;
2555					
2556					// make sure the object is no longer referenced in there to prevent
2557					// memory leaks
2558					$.tooltipster.__instancesLatestArr = $.grep($.tooltipster.__instancesLatestArr, function(el, i) {
2559						return self !== el;
2560					});
2561					
2562					clearInterval(self.__garbageCollector);
2563				});
2564			}
2565		}
2566		else {
2567			self.__destroyError();
2568		}
2569		
2570		// we return the scope rather than true so that the call to
2571		// .tooltipster('destroy') actually returns the matched elements
2572		// and applies to all of them
2573		return self;
2574	},
2575	
2576	/**
2577	 * Disables the tooltip
2578	 * 
2579	 * @returns {self}
2580	 * @public
2581	 */
2582	disable: function() {
2583		
2584		if (!this.__destroyed) {
2585			
2586			// close first, in case the tooltip would not disappear on
2587			// its own (no close trigger)
2588			this._close();
2589			this.__enabled = false;
2590			
2591			return this;
2592		}
2593		else {
2594			this.__destroyError();
2595		}
2596		
2597		return this;
2598	},
2599	
2600	/**
2601	 * Returns the HTML element of the origin
2602	 *
2603	 * @returns {self}
2604	 * @public
2605	 */
2606	elementOrigin: function() {
2607		
2608		if (!this.__destroyed) {
2609			return this._$origin[0];
2610		}
2611		else {
2612			this.__destroyError();
2613		}
2614	},
2615	
2616	/**
2617	 * Returns the HTML element of the tooltip
2618	 *
2619	 * @returns {self}
2620	 * @public
2621	 */
2622	elementTooltip: function() {
2623		return this._$tooltip ? this._$tooltip[0] : null;
2624	},
2625	
2626	/**
2627	 * Enables the tooltip
2628	 * 
2629	 * @returns {self}
2630	 * @public
2631	 */
2632	enable: function() {
2633		this.__enabled = true;
2634		return this;
2635	},
2636	
2637	/**
2638	 * Alias, deprecated in 4.0.0
2639	 * 
2640	 * @param {function} callback
2641	 * @returns {self}
2642	 * @public
2643	 */
2644	hide: function(callback) {
2645		return this.close(callback);
2646	},
2647	
2648	/**
2649	 * Returns the instance
2650	 * 
2651	 * @returns {self}
2652	 * @public
2653	 */
2654	instance: function() {
2655		return this;
2656	},
2657	
2658	/**
2659	 * For public use only, not to be used by plugins (use ::_off() instead)
2660	 * 
2661	 * @returns {self}
2662	 * @public
2663	 */
2664	off: function() {
2665		
2666		if (!this.__destroyed) {
2667			this.__$emitterPublic.off.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
2668		}
2669		
2670		return this;
2671	},
2672	
2673	/**
2674	 * For public use only, not to be used by plugins (use ::_on() instead)
2675	 *
2676	 * @returns {self}
2677	 * @public
2678	 */
2679	on: function() {
2680		
2681		if (!this.__destroyed) {
2682			this.__$emitterPublic.on.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
2683		}
2684		else {
2685			this.__destroyError();
2686		}
2687		
2688		return this;
2689	},
2690	
2691	/**
2692	 * For public use only, not to be used by plugins
2693	 *
2694	 * @returns {self}
2695	 * @public
2696	 */
2697	one: function() {
2698		
2699		if (!this.__destroyed) {
2700			this.__$emitterPublic.one.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
2701		}
2702		else {
2703			this.__destroyError();
2704		}
2705		
2706		return this;
2707	},
2708	
2709	/**
2710	 * @see self::_open
2711	 * @returns {self}
2712	 * @public
2713	 */
2714	open: function(callback) {
2715		
2716		if (!this.__destroyed && !this.__destroying) {
2717			this._open(null, callback);
2718		}
2719		else {
2720			this.__destroyError();
2721		}
2722		
2723		return this;
2724	},
2725	
2726	/**
2727	 * Get or set options. For internal use and advanced users only.
2728	 * 
2729	 * @param {string} o Option name
2730	 * @param {mixed} val optional A new value for the option
2731	 * @return {mixed|self} If val is omitted, the value of the option
2732	 * is returned, otherwise the instance itself is returned
2733	 * @public
2734	 */ 
2735	option: function(o, val) {
2736		
2737		// getter
2738		if (val === undefined) {
2739			return this.__options[o];
2740		}
2741		// setter
2742		else {
2743			
2744			if (!this.__destroyed) {
2745				
2746				// change value
2747				this.__options[o] = val;
2748				
2749				// format
2750				this.__optionsFormat();
2751				
2752				// re-prepare the triggers if needed
2753				if ($.inArray(o, ['trigger', 'triggerClose', 'triggerOpen']) >= 0) {
2754					this.__prepareOrigin();
2755				}
2756				
2757				if (o === 'selfDestruction') {
2758					this.__prepareGC();
2759				}
2760			}
2761			else {
2762				this.__destroyError();
2763			}
2764			
2765			return this;
2766		}
2767	},
2768	
2769	/**
2770	 * This method is in charge of setting the position and size properties of the tooltip.
2771	 * All the hard work is delegated to the display plugin.
2772	 * Note: The tooltip may be detached from the DOM at the moment the method is called 
2773	 * but must be attached by the end of the method call.
2774	 * 
2775	 * @param {object} event For internal use only. Defined if an event such as
2776	 * window resizing triggered the repositioning
2777	 * @param {boolean} tooltipIsDetached For internal use only. Set this to true if you
2778	 * know that the tooltip not being in the DOM is not an issue (typically when the
2779	 * tooltip element has just been created but has not been added to the DOM yet).
2780	 * @returns {self}
2781	 * @public
2782	 */
2783	reposition: function(event, tooltipIsDetached) {
2784		
2785		var self = this;
2786		
2787		if (!self.__destroyed) {
2788			
2789			// if the tooltip has not been removed from DOM manually (or if it
2790			// has been detached on purpose)
2791			if (bodyContains(self._$tooltip) || tooltipIsDetached) {
2792				
2793				if (!tooltipIsDetached) {
2794					// detach in case the tooltip overflows the window and adds
2795					// scrollbars to it, so __geometry can be accurate
2796					self._$tooltip.detach();
2797				}
2798				
2799				// refresh the geometry object before passing it as a helper
2800				self.__Geometry = self.__geometry();
2801				
2802				// let a plugin fo the rest
2803				self._trigger({
2804					type: 'reposition',
2805					event: event,
2806					helper: {
2807						geo: self.__Geometry
2808					}
2809				});
2810			}
2811		}
2812		else {
2813			self.__destroyError();
2814		}
2815		
2816		return self;
2817	},
2818	
2819	/**
vendor: 6,935 bytes, lines 2820-3072
2820	 * Alias, deprecated in 4.0.0
2821	 *
2822	 * @param callback
2823	 * @returns {self}
2824	 * @public
2825	 */
2826	show: function(callback) {
2827		return this.open(callback);
2828	},
2829	
2830	/**
2831	 * Returns some properties about the instance
2832	 * 
2833	 * @returns {object}
2834	 * @public
2835	 */
2836	status: function() {
2837		
2838		return {
2839			destroyed: this.__destroyed,
2840			destroying: this.__destroying,
2841			enabled: this.__enabled,
2842			open: this.__state !== 'closed',
2843			state: this.__state
2844		};
2845	},
2846	
2847	/**
2848	 * For public use only, not to be used by plugins
2849	 *
2850	 * @returns {self}
2851	 * @public
2852	 */
2853	triggerHandler: function() {
2854		
2855		if (!this.__destroyed) {
2856			this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic, Array.prototype.slice.apply(arguments));
2857		}
2858		else {
2859			this.__destroyError();
2860		}
2861		
2862		return this;
2863	}
2864};
2865
2866$.fn.tooltipster = function() {
2867	
2868	// for using in closures
2869	var args = Array.prototype.slice.apply(arguments),
2870		// common mistake: an HTML element can't be in several tooltips at the same time
2871		contentCloningWarning = 'You are using a single HTML element as content for several tooltips. You probably want to set the contentCloning option to TRUE.';
2872	
2873	// this happens with $(sel).tooltipster(...) when $(sel) does not match anything
2874	if (this.length === 0) {
2875		
2876		// still chainable
2877		return this;
2878	}
2879	// this happens when calling $(sel).tooltipster('methodName or options')
2880	// where $(sel) matches one or more elements
2881	else {
2882		
2883		// method calls
2884		if (typeof args[0] === 'string') {
2885			
2886			var v = '#*$~&';
2887			
2888			this.each(function() {
2889				
2890				// retrieve the namepaces of the tooltip(s) that exist on that element.
2891				// We will interact with the first tooltip only.
2892				var ns = $(this).data('tooltipster-ns'),
2893					// self represents the instance of the first tooltipster plugin
2894					// associated to the current HTML object of the loop
2895					self = ns ? $(this).data(ns[0]) : null;
2896				
2897				// if the current element holds a tooltipster instance
2898				if (self) {
2899					
2900					if (typeof self[args[0]] === 'function') {
2901						
2902						if (	this.length > 1
2903							&&	args[0] == 'content'
2904							&&	(	args[1] instanceof $
2905								|| (typeof args[1] == 'object' && args[1] != null && args[1].tagName)
2906							)
2907							&&	!self.__options.contentCloning
2908							&&	self.__options.debug
2909						) {
2910							console.log(contentCloningWarning);
2911						}
2912						
2913						// note : args[1] and args[2] may not be defined
2914						var resp = self[args[0]](args[1], args[2]);
2915					}
2916					else {
2917						throw new Error('Unknown method "'+ args[0] +'"');
2918					}
2919					
2920					// if the function returned anything other than the instance
2921					// itself (which implies chaining, except for the `instance` method)
2922					if (resp !== self || args[0] === 'instance') {
2923						
2924						v = resp;
2925						
2926						// return false to stop .each iteration on the first element
2927						// matched by the selector
2928						return false;
2929					}
2930				}
2931				else {
2932					throw new Error('You called Tooltipster\'s "'+ args[0] +'" method on an uninitialized element');
2933				}
2934			});
2935			
2936			return (v !== '#*$~&') ? v : this;
2937		}
2938		// first argument is undefined or an object: the tooltip is initializing
2939		else {
2940			
2941			// reset the array of last initialized objects
2942			$.tooltipster.__instancesLatestArr = [];
2943			
2944			// is there a defined value for the multiple option in the options object ?
2945			var	multipleIsSet = args[0] && args[0].multiple !== undefined,
2946				// if the multiple option is set to true, or if it's not defined but
2947				// set to true in the defaults
2948				multiple = (multipleIsSet && args[0].multiple) || (!multipleIsSet && defaults.multiple),
2949				// same for content
2950				contentIsSet = args[0] && args[0].content !== undefined,
2951				content = (contentIsSet && args[0].content) || (!contentIsSet && defaults.content),
2952				// same for contentCloning
2953				contentCloningIsSet = args[0] && args[0].contentCloning !== undefined,
2954				contentCloning =
2955						(contentCloningIsSet && args[0].contentCloning)
2956					||	(!contentCloningIsSet && defaults.contentCloning),
2957				// same for debug
2958				debugIsSet = args[0] && args[0].debug !== undefined,
2959				debug = (debugIsSet && args[0].debug) || (!debugIsSet && defaults.debug);
2960			
2961			if (	this.length > 1
2962				&&	(	content instanceof $
2963					|| (typeof content == 'object' && content != null && content.tagName)
2964				)
2965				&&	!contentCloning
2966				&&	debug
2967			) {
2968				console.log(contentCloningWarning);
2969			}
2970			
2971			// create a tooltipster instance for each element if it doesn't
2972			// already have one or if the multiple option is set, and attach the
2973			// object to it
2974			this.each(function() {
2975				
2976				var go = false,
2977					$this = $(this),
2978					ns = $this.data('tooltipster-ns'),
2979					obj = null;
2980				
2981				if (!ns) {
2982					go = true;
2983				}
2984				else if (multiple) {
2985					go = true;
2986				}
2987				else if (debug) {
2988					console.log('Tooltipster: one or more tooltips are already attached to the element below. Ignoring.');
2989					console.log(this);
2990				}
2991				
2992				if (go) {
2993					obj = new $.Tooltipster(this, args[0]);
2994					
2995					// save the reference of the new instance
2996					if (!ns) ns = [];
2997					ns.push(obj.__namespace);
2998					$this.data('tooltipster-ns', ns);
2999					
3000					// save the instance itself
3001					$this.data(obj.__namespace, obj);
3002					
3003					// call our constructor custom function.
3004					// we do this here and not in ::init() because we wanted
3005					// the object to be saved in $this.data before triggering
3006					// it
3007					if (obj.__options.functionInit) {
3008						obj.__options.functionInit.call(obj, obj, {
3009							origin: this
3010						});
3011					}
3012					
3013					// and now the event, for the plugins and core emitter
3014					obj._trigger('init');
3015				}
3016				
3017				$.tooltipster.__instancesLatestArr.push(obj);
3018			});
3019			
3020			return this;
3021		}
3022	}
3023};
3024
3025// Utilities
3026
3027/**
3028 * A class to check if a tooltip can fit in given dimensions
3029 * 
3030 * @param {object} $tooltip The jQuery wrapped tooltip element, or a clone of it
3031 */
3032function Ruler($tooltip) {
3033	
3034	// list of instance variables
3035	
3036	this.$container;
3037	this.constraints = null;
3038	this.__$tooltip;
3039	
3040	this.__init($tooltip);
3041}
3042
3043Ruler.prototype = {
3044	
3045	/**
3046	 * Move the tooltip into an invisible div that does not allow overflow to make
3047	 * size tests. Note: the tooltip may or may not be attached to the DOM at the
3048	 * moment this method is called, it does not matter.
3049	 * 
3050	 * @param {object} $tooltip The object to test. May be just a clone of the
3051	 * actual tooltip.
3052	 * @private
3053	 */
3054	__init: function($tooltip) {
3055		
3056		this.__$tooltip = $tooltip;
3057		
3058		this.__$tooltip
3059			.css({
3060				// for some reason we have to specify top and left 0
3061				left: 0,
3062				// any overflow will be ignored while measuring
3063				overflow: 'hidden',
3064				// positions at (0,0) without the div using 100% of the available width
3065				position: 'absolute',
3066				top: 0
3067			})
3068			// overflow must be auto during the test. We re-set this in case
3069			// it were modified by the user
3070			.find('.tooltipster-content')
3071				.css('overflow', 'auto');
3072		
3073		this.$container = $('<div class="tooltipster-ruler"></div>')
3074			.append(this.__$tooltip)
3075			.appendTo('body');
3076	},
3077	
3078	/**
3079	 * Force the browser to redraw (re-render) the tooltip immediately. This is required
3080	 * when you changed some CSS properties and need to make something with it
3081	 * immediately, without waiting for the browser to redraw at the end of instructions.
3082	 *
3083	 * @see http://stackoverflow.com/questions/3485365/how-can-i-force-webkit-to-redraw-repaint-to-propagate-style-changes
3084	 * @private
3085	 */
3086	__forceRedraw: function() {
3087		
3088		// note: this would work but for Webkit only
3089		//this.__$tooltip.close();
3090		//this.__$tooltip[0].offsetHeight;
3091		//this.__$tooltip.open();
3092		
3093		// works in FF too
3094		var $p = this.__$tooltip.parent();
3095		this.__$tooltip.detach();
3096		this.__$tooltip.appendTo($p);
3097	},
3098	
3099	/**
3100	 * Set maximum dimensions for the tooltip. A call to ::measure afterwards
3101	 * will tell us if the content overflows or if it's ok
3102	 *
3103	 * @param {int} width
3104	 * @param {int} height
3105	 * @return {Ruler}
3106	 * @public
3107	 */
3108	constrain: function(width, height) {
3109		
3110		this.constraints = {
3111			width: width,
3112			height: height
3113		};
3114		
3115		this.__$tooltip.css({
3116			// we disable display:flex, otherwise the content would overflow without
3117			// creating horizontal scrolling (which we need to detect).
3118			display: 'block',
3119			// reset any previous height
3120			height: '',
3121			// we'll check if horizontal scrolling occurs
3122			overflow: 'auto',
3123			// we'll set the width and see what height is generated and if there
3124			// is horizontal overflow
3125			width: width
3126		});
3127		
3128		return this;
3129	},
3130	
3131	/**
3132	 * Reset the tooltip content overflow and remove the test container
3133	 * 
3134	 * @returns {Ruler}
3135	 * @public
3136	 */
3137	destroy: function() {
3138		
3139		// in case the element was not a clone
3140		this.__$tooltip
3141			.detach()
3142			.find('.tooltipster-content')
3143				.css({
3144					// reset to CSS value
3145					display: '',
3146					overflow: ''
3147				});
3148		
3149		this.$container.remove();
3150	},
3151	
3152	/**
3153	 * Removes any constraints
3154	 * 
3155	 * @returns {Ruler}
3156	 * @public
3157	 */
3158	free: function() {
3159		
3160		this.constraints = null;
3161		
3162		// reset to natural size
3163		this.__$tooltip.css({
3164			display: '',
3165			height: '',
3166			overflow: 'visible',
3167			width: ''
3168		});
3169		
3170		return this;
3171	},
3172	
3173	/**
3174	 * Returns the size of the tooltip. When constraints are applied, also returns
3175	 * whether the tooltip fits in the provided dimensions.
3176	 * The idea is to see if the new height is small enough and if the content does
3177	 * not overflow horizontally.
3178	 *
3179	 * @param {int} width
3180	 * @param {int} height
3181	 * @returns {object} An object with a bool `fits` property and a `size` property
3182	 * @public
3183	 */
3184	measure: function() {
3185		
3186		this.__forceRedraw();
3187		
3188		var tooltipBcr = this.__$tooltip[0].getBoundingClientRect(),
3189			result = { size: {
3190				// bcr.width/height are not defined in IE8- but in this
3191				// case, bcr.right/bottom will have the same value
3192				// except in iOS 8+ where tooltipBcr.bottom/right are wrong
3193				// after scrolling for reasons yet to be determined
3194				height: tooltipBcr.height || tooltipBcr.bottom,
3195				width: tooltipBcr.width || tooltipBcr.right
3196			}};
3197		
3198		if (this.constraints) {
3199			
3200			// note: we used to use offsetWidth instead of boundingRectClient but
3201			// it returned rounded values, causing issues with sub-pixel layouts.
3202			
3203			// note2: noticed that the bcrWidth of text content of a div was once
3204			// greater than the bcrWidth of its container by 1px, causing the final
3205			// tooltip box to be too small for its content. However, evaluating
3206			// their widths one against the other (below) surprisingly returned
vendor: 3,113 bytes, lines 3207-3297
3207			// equality. Happened only once in Chrome 48, was not able to reproduce
3208			// => just having fun with float position values...
3209			
3210			var $content = this.__$tooltip.find('.tooltipster-content'),
3211				height = this.__$tooltip.outerHeight(),
3212				contentBcr = $content[0].getBoundingClientRect(),
3213				fits = {
3214					height: height <= this.constraints.height,
3215					width: (
3216						// this condition accounts for min-width property that
3217						// may apply
3218						tooltipBcr.width <= this.constraints.width
3219							// the -1 is here because scrollWidth actually returns
3220							// a rounded value, and may be greater than bcr.width if
3221							// it was rounded up. This may cause an issue for contents
3222							// which actually really overflow  by 1px or so, but that
3223							// should be rare. Not sure how to solve this efficiently.
3224							// See http://blogs.msdn.com/b/ie/archive/2012/02/17/sub-pixel-rendering-and-the-css-object-model.aspx
3225						&&	contentBcr.width >= $content[0].scrollWidth - 1
3226					)
3227				};
3228			
3229			result.fits = fits.height && fits.width;
3230		}
3231		
3232		// old versions of IE get the width wrong for some reason
3233		if (env.IE && env.IE <= 11) {
3234			result.size.width = Math.ceil(result.size.width) + 1;
3235		}
3236		
3237		return result;
3238	}
3239};
3240
3241// quick & dirty compare function, not bijective nor multidimensional
3242function areEqual(a,b) {
3243	var same = true;
3244	$.each(a, function(i, _) {
3245		if (b[i] === undefined || a[i] !== b[i]) {
3246			same = false;
3247			return false;
3248		}
3249	});
3250	return same;
3251}
3252
3253/**
3254 * A fast function to check if an element is still in the DOM. It
3255 * tries to use an id as ids are indexed by the browser, or falls
3256 * back to jQuery's `contains` method. May fail if two elements
3257 * have the same id, but so be it
3258 *
3259 * @param {object} $obj A jQuery-wrapped HTML element
3260 * @return {boolean}
3261 */
3262function bodyContains($obj) {
3263	var id = $obj.attr('id'),
3264		el = id ? env.window.document.getElementById(id) : null;
3265	// must also check that the element with the id is the one we want
3266	return el ? el === $obj[0] : $.contains(env.window.document.body, $obj[0]);
3267}
3268
3269// detect IE versions for dirty fixes
3270var uA = navigator.userAgent.toLowerCase();
3271if (uA.indexOf('msie') != -1) env.IE = parseInt(uA.split('msie')[1]);
3272else if (uA.toLowerCase().indexOf('trident') !== -1 && uA.indexOf(' rv:11') !== -1) env.IE = 11;
3273else if (uA.toLowerCase().indexOf('edge/') != -1) env.IE = parseInt(uA.toLowerCase().split('edge/')[1]);
3274
3275// detecting support for CSS transitions
3276function transitionSupport() {
3277	
3278	// env.window is not defined yet when this is called
3279	if (!win) return false;
3280	
3281	var b = win.document.body || win.document.documentElement,
3282		s = b.style,
3283		p = 'transition',
3284		v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'];
3285	
3286	if (typeof s[p] == 'string') { return true; }
3287	
3288	p = p.charAt(0).toUpperCase() + p.substr(1);
3289	for (var i=0; i<v.length; i++) {
3290		if (typeof s[v[i] + p] == 'string') { return true; }
3291	}
3292	return false;
3293}
3294
3295// we'll return jQuery for plugins not to have to declare it as a dependency,
3296// but it's done by a build task since it should be included only once at the
3297// end when we concatenate the core
vendor: 5,237 bytes, lines 3297-3483
3297 file with a plugin
3298// sideTip is Tooltipster's default plugin.
3299// This file will be UMDified by a build task.
3300
3301var pluginName = 'tooltipster.sideTip';
3302
3303$.tooltipster._plugin({
3304	name: pluginName,
3305	instance: {
3306		/**
3307		 * Defaults are provided as a function for an easy override by inheritance
3308		 *
3309		 * @return {object} An object with the defaults options
3310		 * @private
3311		 */
3312		__defaults: function() {
3313			
3314			return {
3315				// if the tooltip should display an arrow that points to the origin
3316				arrow: true,
3317				// the distance in pixels between the tooltip and the origin
3318				distance: 6,
3319				// allows to easily change the position of the tooltip
3320				functionPosition: null,
3321				maxWidth: null,
3322				// used to accomodate the arrow of tooltip if there is one.
3323				// First to make sure that the arrow target is not too close
3324				// to the edge of the tooltip, so the arrow does not overflow
3325				// the tooltip. Secondly when we reposition the tooltip to
3326				// make sure that it's positioned in such a way that the arrow is
3327				// still pointing at the target (and not a few pixels beyond it).
3328				// It should be equal to or greater than half the width of
3329				// the arrow (by width we mean the size of the side which touches
3330				// the side of the tooltip).
3331				minIntersection: 16,
3332				minWidth: 0,
3333				// deprecated in 4.0.0. Listed for _optionsExtract to pick it up
3334				position: null,
3335				side: 'top',
3336				// set to false to position the tooltip relatively to the document rather
3337				// than the window when we open it
3338				viewportAware: true
3339			};
3340		},
3341		
3342		/**
3343		 * Run once: at instantiation of the plugin
3344		 *
3345		 * @param {object} instance The tooltipster object that instantiated this plugin
3346		 * @private
3347		 */
3348		__init: function(instance) {
3349			
3350			var self = this;
3351			
3352			// list of instance variables
3353			
3354			self.__instance = instance;
3355			self.__namespace = 'tooltipster-sideTip-'+ Math.round(Math.random()*1000000);
3356			self.__previousState = 'closed';
3357			self.__options;
3358			
3359			// initial formatting
3360			self.__optionsFormat();
3361			
3362			self.__instance._on('state.'+ self.__namespace, function(event) {
3363				
3364				if (event.state == 'closed') {
3365					self.__close();
3366				}
3367				else if (event.state == 'appearing' && self.__previousState == 'closed') {
3368					self.__create();
3369				}
3370				
3371				self.__previousState = event.state;
3372			});
3373			
3374			// reformat every time the options are changed
3375			self.__instance._on('options.'+ self.__namespace, function() {
3376				self.__optionsFormat();
3377			});
3378			
3379			self.__instance._on('reposition.'+ self.__namespace, function(e) {
3380				self.__reposition(e.event, e.helper);
3381			});
3382		},
3383		
3384		/**
3385		 * Called when the tooltip has closed
3386		 * 
3387		 * @private
3388		 */
3389		__close: function() {
3390			
3391			// detach our content object first, so the next jQuery's remove()
3392			// call does not unbind its event handlers
3393			if (this.__instance.content() instanceof $) {
3394				this.__instance.content().detach();
3395			}
3396			
3397			// remove the tooltip from the DOM
3398			this.__instance._$tooltip.remove();
3399			this.__instance._$tooltip = null;
3400		},
3401		
3402		/**
3403		 * Creates the HTML element of the tooltip.
3404		 * 
3405		 * @private
3406		 */
3407		__create: function() {
3408			
3409			// note: we wrap with a .tooltipster-box div to be able to set a margin on it
3410			// (.tooltipster-base must not have one)
3411			var $html = $(
3412				'<div class="tooltipster-base tooltipster-sidetip">' +
3413					'<div class="tooltipster-box">' +
3414						'<div class="tooltipster-content"></div>' +
3415					'</div>' +
3416					'<div class="tooltipster-arrow">' +
3417						'<div class="tooltipster-arrow-uncropped">' +
3418							'<div class="tooltipster-arrow-border"></div>' +
3419							'<div class="tooltipster-arrow-background"></div>' +
3420						'</div>' +
3421					'</div>' +
3422				'</div>'
3423			);
3424			
3425			// hide arrow if asked
3426			if (!this.__options.arrow) {
3427				$html
3428					.find('.tooltipster-box')
3429						.css('margin', 0)
3430						.end()
3431					.find('.tooltipster-arrow')
3432						.hide();
3433			}
3434			
3435			// apply min/max width if asked
3436			if (this.__options.minWidth) {
3437				$html.css('min-width', this.__options.minWidth + 'px');
3438			}
3439			if (this.__options.maxWidth) {
3440				$html.css('max-width', this.__options.maxWidth + 'px');
3441			}
3442			
3443			this.__instance._$tooltip = $html;
3444			
3445			// tell the instance that the tooltip element has been created
3446			this.__instance._trigger('created');
3447		},
3448		
3449		/**
3450		 * Used when the plugin is to be unplugged
3451		 *
3452		 * @private
3453		 */
3454		__destroy: function() {
3455			this.__instance._off('.'+ self.__namespace);
3456		},
3457		
3458		/**
3459		 * (Re)compute this.__options from the options declared to the instance
3460		 *
3461		 * @private
3462		 */
3463		__optionsFormat: function() {
3464			
3465			var self = this;
3466			
3467			// get the options
3468			self.__options = self.__instance._optionsExtract(pluginName, self.__defaults());
3469			
3470			// for backward compatibility, deprecated in v4.0.0
3471			if (self.__options.position) {
3472				self.__options.side = self.__options.position;
3473			}
3474			
3475			// options formatting
3476			
3477			// format distance as a four-cell array if it ain't one yet and then make
3478			// it an object with top/bottom/left/right properties
3479			if (typeof self.__options.distance != 'object') {
3480				self.__options.distance = [self.__options.distance];
3481			}
3482			if (self.__options.distance.length < 4) {
3483				
vendor: 3,391 bytes, lines 3483-3569
3483
3484				if (self.__options.distance[1] === undefined) self.__options.distance[1] = self.__options.distance[0];
3485				if (self.__options.distance[2] === undefined) self.__options.distance[2] = self.__options.distance[0];
3486				if (self.__options.distance[3] === undefined) self.__options.distance[3] = self.__options.distance[1];
3487				
3488				self.__options.distance = {
3489					top: self.__options.distance[0],
3490					right: self.__options.distance[1],
3491					bottom: self.__options.distance[2],
3492					left: self.__options.distance[3]
3493				};
3494			}
3495			
3496			// let's transform:
3497			// 'top' into ['top', 'bottom', 'right', 'left']
3498			// 'right' into ['right', 'left', 'top', 'bottom']
3499			// 'bottom' into ['bottom', 'top', 'right', 'left']
3500			// 'left' into ['left', 'right', 'top', 'bottom']
3501			if (typeof self.__options.side == 'string') {
3502				
3503				var opposites = {
3504					'top': 'bottom',
3505					'right': 'left',
3506					'bottom': 'top',
3507					'left': 'right'
3508				};
3509				
3510				self.__options.side = [self.__options.side, opposites[self.__options.side]];
3511				
3512				if (self.__options.side[0] == 'left' || self.__options.side[0] == 'right') {
3513					self.__options.side.push('top', 'bottom');
3514				}
3515				else {
3516					self.__options.side.push('right', 'left');
3517				}
3518			}
3519			
3520			// misc
3521			// disable the arrow in IE6 unless the arrow option was explicitly set to true
3522			if (	$.tooltipster._env.IE === 6
3523				&&	self.__options.arrow !== true
3524			) {
3525				self.__options.arrow = false;
3526			}
3527		},
3528		
3529		/**
3530		 * This method must compute and set the positioning properties of the
3531		 * tooltip (left, top, width, height, etc.). It must also make sure the
3532		 * tooltip is eventually appended to its parent (since the element may be
3533		 * detached from the DOM at the moment the method is called).
3534		 *
3535		 * We'll evaluate positioning scenarios to find which side can contain the
3536		 * tooltip in the best way. We'll consider things relatively to the window
3537		 * (unless the user asks not to), then to the document (if need be, or if the
3538		 * user explicitly requires the tests to run on the document). For each
3539		 * scenario, measures are taken, allowing us to know how well the tooltip
3540		 * is going to fit. After that, a sorting function will let us know what
3541		 * the best scenario is (we also allow the user to choose his favorite
3542		 * scenario by using an event).
3543		 * 
3544		 * @param {object} helper An object that contains variables that plugin
3545		 * creators may find useful (see below)
3546		 * @param {object} helper.geo An object with many layout properties
3547		 * about objects of interest (window, document, origin). This should help
3548		 * plugin users compute the optimal position of the tooltip
3549		 * @private
3550		 */
3551		__reposition: function(event, helper) {
3552			
3553			var self = this,
3554				finalResult,
3555				// to know where to put the tooltip, we need to know on which point
3556				// of the x or y axis we should center it. That coordinate is the target
3557				targets = self.__targetFind(helper),
3558				testResults = [];
3559			
3560			// make sure the tooltip is detached while we make tests on a clone
3561			self.__instance._$tooltip.detach();
3562			
3563			// we could actually provide the original element to the Ruler and
3564			// not a clone, but it just feels right to keep it out of the
3565			// machinery.
3566			var $clone = self.__instance._$tooltip.clone(),
3567				// start position tests session
3568				ruler = $.tooltipster._getRuler($clone),
3569				satisfied = false;
vendor: 15,069 bytes, lines 3569-4012
3569
3570			
3571			// start evaluating scenarios
3572			$.each(['window', 'document'], function(i, container) {
3573				
3574				var takeTest = null;
3575				
3576				// let the user decide to keep on testing or not
3577				self.__instance._trigger({
3578					container: container,
3579					helper: helper,
3580					satisfied: satisfied,
3581					takeTest: function(bool) {
3582						takeTest = bool;
3583					},
3584					results: testResults,
3585					type: 'positionTest'
3586				});
3587				
3588				if (	takeTest == true
3589					||	(	takeTest != false
3590						&&	satisfied == false
3591							// skip the window scenarios if asked. If they are reintegrated by
3592							// the callback of the positionTest event, they will have to be
3593							// excluded using the callback of positionTested
3594						&&	(container != 'window' || self.__options.viewportAware)
3595					)
3596				) {
3597					
3598					// for each allowed side
3599					for (var i=0; i < self.__options.side.length; i++) {
3600						
3601						var distance = {
3602								horizontal: 0,
3603								vertical: 0
3604							},
3605							side = self.__options.side[i];
3606						
3607						if (side == 'top' || side == 'bottom') {
3608							distance.vertical = self.__options.distance[side];
3609						}
3610						else {
3611							distance.horizontal = self.__options.distance[side];
3612						}
3613						
3614						// this may have an effect on the size of the tooltip if there are css
3615						// rules for the arrow or something else
3616						self.__sideChange($clone, side);
3617						
3618						$.each(['natural', 'constrained'], function(i, mode) {
3619							
3620							takeTest = null;
3621							
3622							// emit an event on the instance
3623							self.__instance._trigger({
3624								container: container,
3625								event: event,
3626								helper: helper,
3627								mode: mode,
3628								results: testResults,
3629								satisfied: satisfied,
3630								side: side,
3631								takeTest: function(bool) {
3632									takeTest = bool;
3633								},
3634								type: 'positionTest'
3635							});
3636							
3637							if (	takeTest == true
3638								||	(	takeTest != false
3639									&&	satisfied == false
3640								)
3641							) {
3642								
3643								var testResult = {
3644									container: container,
3645									// we let the distance as an object here, it can make things a little easier
3646									// during the user's calculations at positionTest/positionTested
3647									distance: distance,
3648									// whether the tooltip can fit in the size of the viewport (does not mean
3649									// that we'll be able to make it initially entirely visible, see 'whole')
3650									fits: null,
3651									mode: mode,
3652									outerSize: null,
3653									side: side,
3654									size: null,
3655									target: targets[side],
3656									// check if the origin has enough surface on screen for the tooltip to
3657									// aim at it without overflowing the viewport (this is due to the thickness
3658									// of the arrow represented by the minIntersection length).
3659									// If not, the tooltip will have to be partly or entirely off screen in
3660									// order to stay docked to the origin. This value will stay null when the
3661									// container is the document, as it is not relevant
3662									whole: null
3663								};
3664								
3665								// get the size of the tooltip with or without size constraints
3666								var rulerConfigured = (mode == 'natural') ?
3667										ruler.free() :
3668										ruler.constrain(
3669											helper.geo.available[container][side].width - distance.horizontal,
3670											helper.geo.available[container][side].height - distance.vertical
3671										),
3672									rulerResults = rulerConfigured.measure();
3673								
3674								testResult.size = rulerResults.size;
3675								testResult.outerSize = {
3676									height: rulerResults.size.height + distance.vertical,
3677									width: rulerResults.size.width + distance.horizontal
3678								};
3679								
3680								if (mode == 'natural') {
3681									
3682									if(		helper.geo.available[container][side].width >= testResult.outerSize.width
3683										&&	helper.geo.available[container][side].height >= testResult.outerSize.height
3684									) {
3685										testResult.fits = true;
3686									}
3687									else {
3688										testResult.fits = false;
3689									}
3690								}
3691								else {
3692									testResult.fits = rulerResults.fits;
3693								}
3694								
3695								if (container == 'window') {
3696									
3697									if (!testResult.fits) {
3698										testResult.whole = false;
3699									}
3700									else {
3701										if (side == 'top' || side == 'bottom') {
3702											
3703											testResult.whole = (
3704													helper.geo.origin.windowOffset.right >= self.__options.minIntersection
3705												&&	helper.geo.window.size.width - helper.geo.origin.windowOffset.left >= self.__options.minIntersection
3706											);
3707										}
3708										else {
3709											testResult.whole = (
3710													helper.geo.origin.windowOffset.bottom >= self.__options.minIntersection
3711												&&	helper.geo.window.size.height - helper.geo.origin.windowOffset.top >= self.__options.minIntersection
3712											);
3713										}
3714									}
3715								}
3716								
3717								testResults.push(testResult);
3718								
3719								// we don't need to compute more positions if we have one fully on screen
3720								if (testResult.whole) {
3721									satisfied = true;
3722								}
3723								else {
3724									// don't run the constrained test unless the natural width was greater
3725									// than the available width, otherwise it's pointless as we know it
3726									// wouldn't fit either
3727									if (	testResult.mode == 'natural'
3728										&&	(	testResult.fits
3729											||	testResult.size.width <= helper.geo.available[container][side].width
3730										)
3731									) {
3732										return false;
3733									}
3734								}
3735							}
3736						});
3737					}
3738				}
3739			});
3740			
3741			// the user may eliminate the unwanted scenarios from testResults, but he's
3742			// not supposed to alter them at this point. functionPosition and the
3743			// position event serve that purpose.
3744			self.__instance._trigger({
3745				edit: function(r) {
3746					testResults = r;
3747				},
3748				event: event,
3749				helper: helper,
3750				results: testResults,
3751				type: 'positionTested'
3752			});
3753			
3754			/**
3755			 * Sort the scenarios to find the favorite one.
3756			 * 
3757			 * The favorite scenario is when we can fully display the tooltip on screen,
3758			 * even if it means that the middle of the tooltip is no longer centered on
3759			 * the middle of the origin (when the origin is near the edge of the screen
3760			 * or even partly off screen). We want the tooltip on the preferred side,
3761			 * even if it means that we have to use a constrained size rather than a
3762			 * natural one (as long as it fits). When the origin is off screen at the top
3763			 * the tooltip will be positioned at the bottom (if allowed), if the origin
3764			 * is off screen on the right, it will be positioned on the left, etc.
3765			 * If there are no scenarios where the tooltip can fit on screen, or if the
3766			 * user does not want the tooltip to fit on screen (viewportAware == false),
3767			 * we fall back to the scenarios relative to the document.
3768			 * 
3769			 * When the tooltip is bigger than the viewport in either dimension, we stop
3770			 * looking at the window scenarios and consider the document scenarios only,
3771			 * with the same logic to find on which side it would fit best.
3772			 * 
3773			 * If the tooltip cannot fit the document on any side, we force it at the
3774			 * bottom, so at least the user can scroll to see it.
3775 			 */
3776			testResults.sort(function(a, b) {
3777				
3778				// best if it's whole (the tooltip fits and adapts to the viewport)
3779				if (a.whole && !b.whole) {
3780					return -1;
3781				}
3782				else if (!a.whole && b.whole) {
3783					return 1;
3784				}
3785				else if (a.whole && b.whole) {
3786					
3787					var ai = self.__options.side.indexOf(a.side),
3788						bi = self.__options.side.indexOf(b.side);
3789					
3790					// use the user's sides fallback array
3791					if (ai < bi) {
3792						return -1;
3793					}
3794					else if (ai > bi) {
3795						return 1;
3796					}
3797					else {
3798						// will be used if the user forced the tests to continue
3799						return a.mode == 'natural' ? -1 : 1;
3800					}
3801				}
3802				else {
3803					
3804					// better if it fits
3805					if (a.fits && !b.fits) {
3806						return -1;
3807					}
3808					else if (!a.fits && b.fits) {
3809						return 1;
3810					}
3811					else if (a.fits && b.fits) {
3812						
3813						var ai = self.__options.side.indexOf(a.side),
3814							bi = self.__options.side.indexOf(b.side);
3815						
3816						// use the user's sides fallback array
3817						if (ai < bi) {
3818							return -1;
3819						}
3820						else if (ai > bi) {
3821							return 1;
3822						}
3823						else {
3824							// will be used if the user forced the tests to continue
3825							return a.mode == 'natural' ? -1 : 1;
3826						}
3827					}
3828					else {
3829						
3830						// if everything failed, this will give a preference to the case where
3831						// the tooltip overflows the document at the bottom
3832						if (	a.container == 'document'
3833							&&	a.side == 'bottom'
3834							&&	a.mode == 'natural'
3835						) {
3836							return -1;
3837						}
3838						else {
3839							return 1;
3840						}
3841					}
3842				}
3843			});
3844			
3845			finalResult = testResults[0];
3846			
3847			
3848			// now let's find the coordinates of the tooltip relatively to the window
3849			finalResult.coord = {};
3850			
3851			switch (finalResult.side) {
3852				
3853				case 'left':
3854				case 'right':
3855					finalResult.coord.top = Math.floor(finalResult.target - finalResult.size.height / 2);
3856					break;
3857				
3858				case 'bottom':
3859				case 'top':
3860					finalResult.coord.left = Math.floor(finalResult.target - finalResult.size.width / 2);
3861					break;
3862			}
3863			
3864			switch (finalResult.side) {
3865				
3866				case 'left':
3867					finalResult.coord.left = helper.geo.origin.windowOffset.left - finalResult.outerSize.width;
3868					break;
3869				
3870				case 'right':
3871					finalResult.coord.left = helper.geo.origin.windowOffset.right + finalResult.distance.horizontal;
3872					break;
3873				
3874				case 'top':
3875					finalResult.coord.top = helper.geo.origin.windowOffset.top - finalResult.outerSize.height;
3876					break;
3877				
3878				case 'bottom':
3879					finalResult.coord.top = helper.geo.origin.windowOffset.bottom + finalResult.distance.vertical;
3880					break;
3881			}
3882			
3883			// if the tooltip can potentially be contained within the viewport dimensions
3884			// and that we are asked to make it fit on screen
3885			if (finalResult.container == 'window') {
3886				
3887				// if the tooltip overflows the viewport, we'll move it accordingly (then it will
3888				// not be centered on the middle of the origin anymore). We only move horizontally
3889				// for top and bottom tooltips and vice versa.
3890				if (finalResult.side == 'top' || finalResult.side == 'bottom') {
3891					
3892					// if there is an overflow on the left
3893					if (finalResult.coord.left < 0) {
3894						
3895						// prevent the overflow unless the origin itself gets off screen (minus the
3896						// margin needed to keep the arrow pointing at the target)
3897						if (helper.geo.origin.windowOffset.right - this.__options.minIntersection >= 0) {
3898							finalResult.coord.left = 0;
3899						}
3900						else {
3901							finalResult.coord.left = helper.geo.origin.windowOffset.right - this.__options.minIntersection - 1;
3902						}
3903					}
3904					// or an overflow on the right
3905					else if (finalResult.coord.left > helper.geo.window.size.width - finalResult.size.width) {
3906						
3907						if (helper.geo.origin.windowOffset.left + this.__options.minIntersection <= helper.geo.window.size.width) {
3908							finalResult.coord.left = helper.geo.window.size.width - finalResult.size.width;
3909						}
3910						else {
3911							finalResult.coord.left = helper.geo.origin.windowOffset.left + this.__options.minIntersection + 1 - finalResult.size.width;
3912						}
3913					}
3914				}
3915				else {
3916					
3917					// overflow at the top
3918					if (finalResult.coord.top < 0) {
3919						
3920						if (helper.geo.origin.windowOffset.bottom - this.__options.minIntersection >= 0) {
3921							finalResult.coord.top = 0;
3922						}
3923						else {
3924							finalResult.coord.top = helper.geo.origin.windowOffset.bottom - this.__options.minIntersection - 1;
3925						}
3926					}
3927					// or at the bottom
3928					else if (finalResult.coord.top > helper.geo.window.size.height - finalResult.size.height) {
3929						
3930						if (helper.geo.origin.windowOffset.top + this.__options.minIntersection <= helper.geo.window.size.height) {
3931							finalResult.coord.top = helper.geo.window.size.height - finalResult.size.height;
3932						}
3933						else {
3934							finalResult.coord.top = helper.geo.origin.windowOffset.top + this.__options.minIntersection + 1 - finalResult.size.height;
3935						}
3936					}
3937				}
3938			}
3939			else {
3940				
3941				// there might be overflow here too but it's easier to handle. If there has
3942				// to be an overflow, we'll make sure it's on the right side of the screen
3943				// (because the browser will extend the document size if there is an overflow
3944				// on the right, but not on the left). The sort function above has already
3945				// made sure that a bottom document overflow is preferred to a top overflow,
3946				// so we don't have to care about it.
3947				
3948				// if there is an overflow on the right
3949				if (finalResult.coord.left > helper.geo.window.size.width - finalResult.size.width) {
3950					
3951					// this may actually create on overflow on the left but we'll fix it in a sec
3952					finalResult.coord.left = helper.geo.window.size.width - finalResult.size.width;
3953				}
3954				
3955				// if there is an overflow on the left
3956				if (finalResult.coord.left < 0) {
3957					
3958					// don't care if it overflows the right after that, we made our best
3959					finalResult.coord.left = 0;
3960				}
3961			}
3962			
3963			
3964			// submit the positioning proposal to the user function which may choose to change
3965			// the side, size and/or the coordinates
3966			
3967			// first, set the rules that corresponds to the proposed side: it may change
3968			// the size of the tooltip, and the custom functionPosition may want to detect the
3969			// size of something before making a decision. So let's make things easier for the
3970			// implementor
3971			self.__sideChange($clone, finalResult.side);
3972			
3973			// add some variables to the helper
3974			helper.tooltipClone = $clone[0];
3975			helper.tooltipParent = self.__instance.option('parent').parent[0];
3976			// move informative values to the helper
3977			helper.mode = finalResult.mode;
3978			helper.whole = finalResult.whole;
3979			// add some variables to the helper for the functionPosition callback (these
3980			// will also be added to the event fired by self.__instance._trigger but that's
3981			// ok, we're just being consistent)
3982			helper.origin = self.__instance._$origin[0];
3983			helper.tooltip = self.__instance._$tooltip[0];
3984			
3985			// leave only the actionable values in there for functionPosition
3986			delete finalResult.container;
3987			delete finalResult.fits;
3988			delete finalResult.mode;
3989			delete finalResult.outerSize;
3990			delete finalResult.whole;
3991			
3992			// keep only the distance on the relevant side, for clarity
3993			finalResult.distance = finalResult.distance.horizontal || finalResult.distance.vertical;
3994			
3995			// beginners may not be comfortable with the concept of editing the object
3996			//  passed by reference, so we provide an edit function and pass a clone
3997			var finalResultClone = $.extend(true, {}, finalResult);
3998			
3999			// emit an event on the instance
4000			self.__instance._trigger({
4001				edit: function(result) {
4002					finalResult = result;
4003				},
4004				event: event,
4005				helper: helper,
4006				position: finalResultClone,
4007				type: 'position'
4008			});
4009			
4010			if (self.__options.functionPosition) {
4011				
4012				
4012
4013				var result = self.__options.functionPosition.call(self, self.__instance, helper, finalResultClone);
4014				
4015				if (result) finalResult = result;
4016			}
4017			
4018			// end the positioning tests session (the user might have had a
4019			// use for it during the position event, now it's over)
4020			ruler.destroy();
4021			
vendor: 6,410 bytes, lines 4021-4223
4021
4022			
4023			// compute the position of the target relatively to the tooltip root
4024			// element so we can place the arrow and make the needed adjustments
4025			var arrowCoord,
4026				maxVal;
4027			
4028			if (finalResult.side == 'top' || finalResult.side == 'bottom') {
4029				
4030				arrowCoord = {
4031					prop: 'left',
4032					val: finalResult.target - finalResult.coord.left
4033				};
4034				maxVal = finalResult.size.width - this.__options.minIntersection;
4035			}
4036			else {
4037				
4038				arrowCoord = {
4039					prop: 'top',
4040					val: finalResult.target - finalResult.coord.top
4041				};
4042				maxVal = finalResult.size.height - this.__options.minIntersection;
4043			}
4044			
4045			// cannot lie beyond the boundaries of the tooltip, minus the
4046			// arrow margin
4047			if (arrowCoord.val < this.__options.minIntersection) {
4048				arrowCoord.val = this.__options.minIntersection;
4049			}
4050			else if (arrowCoord.val > maxVal) {
4051				arrowCoord.val = maxVal;
4052			}
4053			
4054			var originParentOffset;
4055			
4056			// let's convert the window-relative coordinates into coordinates relative to the
4057			// future positioned parent that the tooltip will be appended to
4058			if (helper.geo.origin.fixedLineage) {
4059				
4060				// same as windowOffset when the position is fixed
4061				originParentOffset = helper.geo.origin.windowOffset;
4062			}
4063			else {
4064				
4065				// this assumes that the parent of the tooltip is located at
4066				// (0, 0) in the document, typically like when the parent is
4067				// <body>.
4068				// If we ever allow other types of parent, .tooltipster-ruler
4069				// will have to be appended to the parent to inherit css style
4070				// values that affect the display of the text and such.
4071				originParentOffset = {
4072					left: helper.geo.origin.windowOffset.left + helper.geo.window.scroll.left,
4073					top: helper.geo.origin.windowOffset.top + helper.geo.window.scroll.top
4074				};
4075			}
4076			
4077			finalResult.coord = {
4078				left: originParentOffset.left + (finalResult.coord.left - helper.geo.origin.windowOffset.left),
4079				top: originParentOffset.top + (finalResult.coord.top - helper.geo.origin.windowOffset.top)
4080			};
4081			
4082			// set position values on the original tooltip element
4083			
4084			self.__sideChange(self.__instance._$tooltip, finalResult.side);
4085			
4086			if (helper.geo.origin.fixedLineage) {
4087				self.__instance._$tooltip
4088					.css('position', 'fixed');
4089			}
4090			else {
4091				// CSS default
4092				self.__instance._$tooltip
4093					.css('position', '');
4094			}
4095			
4096			self.__instance._$tooltip
4097				.css({
4098					left: finalResult.coord.left,
4099					top: finalResult.coord.top,
4100					// we need to set a size even if the tooltip is in its natural size
4101					// because when the tooltip is positioned beyond the width of the body
4102					// (which is by default the width of the window; it will happen when
4103					// you scroll the window horizontally to get to the origin), its text
4104					// content will otherwise break lines at each word to keep up with the
4105					// body overflow strategy.
4106					height: finalResult.size.height,
4107					width: finalResult.size.width
4108				})
4109				.find('.tooltipster-arrow')
4110					.css({
4111						'left': '',
4112						'top': ''
4113					})
4114					.css(arrowCoord.prop, arrowCoord.val);
4115			
4116			// append the tooltip HTML element to its parent
4117			self.__instance._$tooltip.appendTo(self.__instance.option('parent'));
4118			
4119			self.__instance._trigger({
4120				type: 'repositioned',
4121				event: event,
4122				position: finalResult
4123			});
4124		},
4125		
4126		/**
4127		 * Make whatever modifications are needed when the side is changed. This has
4128		 * been made an independant method for easy inheritance in custom plugins based
4129		 * on this default plugin.
4130		 *
4131		 * @param {object} $obj
4132		 * @param {string} side
4133		 * @private
4134		 */
4135		__sideChange: function($obj, side) {
4136			
4137			$obj
4138				.removeClass('tooltipster-bottom')
4139				.removeClass('tooltipster-left')
4140				.removeClass('tooltipster-right')
4141				.removeClass('tooltipster-top')
4142				.addClass('tooltipster-'+ side);
4143		},
4144		
4145		/**
4146		 * Returns the target that the tooltip should aim at for a given side.
4147		 * The calculated value is a distance from the edge of the window
4148		 * (left edge for top/bottom sides, top edge for left/right side). The
4149		 * tooltip will be centered on that position and the arrow will be
4150		 * positioned there (as much as possible).
4151		 *
4152		 * @param {object} helper
4153		 * @return {integer}
4154		 * @private
4155		 */
4156		__targetFind: function(helper) {
4157			
4158			var target = {},
4159				rects = this.__instance._$origin[0].getClientRects();
4160			
4161			// these lines fix a Chrome bug (issue #491)
4162			if (rects.length > 1) {
4163				var opacity = this.__instance._$origin.css('opacity');
4164				if(opacity == 1) {
4165					this.__instance._$origin.css('opacity', 0.99);
4166					rects = this.__instance._$origin[0].getClientRects();
4167					this.__instance._$origin.css('opacity', 1);
4168				}
4169			}
4170			
4171			// by default, the target will be the middle of the origin
4172			if (rects.length < 2) {
4173				
4174				target.top = Math.floor(helper.geo.origin.windowOffset.left + (helper.geo.origin.size.width / 2));
4175				target.bottom = target.top;
4176				
4177				target.left = Math.floor(helper.geo.origin.windowOffset.top + (helper.geo.origin.size.height / 2));
4178				target.right = target.left;
4179			}
4180			// if multiple client rects exist, the element may be text split
4181			// up into multiple lines and the middle of the origin may not be
4182			// best option anymore. We need to choose the best target client rect
4183			else {
4184				
4185				// top: the first
4186				var targetRect = rects[0];
4187				target.top = Math.floor(targetRect.left + (targetRect.right - targetRect.left) / 2);
4188		
4189				// right: the middle line, rounded down in case there is an even
4190				// number of lines (looks more centered => check out the
4191				// demo with 4 split lines)
4192				if (rects.length > 2) {
4193					targetRect = rects[Math.ceil(rects.length / 2) - 1];
4194				}
4195				else {
4196					targetRect = rects[0];
4197				}
4198				target.right = Math.floor(targetRect.top + (targetRect.bottom - targetRect.top) / 2);
4199		
4200				// bottom: the last
4201				targetRect = rects[rects.length - 1];
4202				target.bottom = Math.floor(targetRect.left + (targetRect.right - targetRect.left) / 2);
4203		
4204				// left: the middle line, rounded up
4205				if (rects.length > 2) {
4206					targetRect = rects[Math.ceil((rects.length + 1) / 2) - 1];
4207				}
4208				else {
4209					targetRect = rects[rects.length - 1];
4210				}
4211				
4212				target.left = Math.floor(targetRect.top + (targetRect.bottom - targetRect.top) / 2);
4213			}
4214			
4215			return target;
4216		}
4217	}
4218});
4219
4220/* a build task will add "return $;" here */
4221return $;
4222
4223}));

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.