tooltipster-rails 3.2.6 → 4.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +15 -7
  3. data/lib/tooltipster-rails/version.rb +1 -1
  4. data/tooltipster-rails.gemspec +1 -1
  5. data/vendor/assets/javascripts/plugins/tooltipster/SVG/tooltipster-SVG.js +171 -0
  6. data/vendor/assets/javascripts/plugins/tooltipster/SVG/tooltipster-SVG.min.js +1 -0
  7. data/vendor/assets/javascripts/tooltipster.bundle.js +4223 -0
  8. data/vendor/assets/javascripts/tooltipster.bundle.min.js +2 -0
  9. data/vendor/assets/javascripts/tooltipster.core.js +3299 -0
  10. data/vendor/assets/javascripts/tooltipster.core.min.js +1 -0
  11. data/vendor/assets/stylesheets/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-borderless.min.css +1 -0
  12. data/vendor/assets/stylesheets/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-light.min.css +1 -0
  13. data/vendor/assets/stylesheets/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-noir.min.css +1 -0
  14. data/vendor/assets/stylesheets/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-punk.min.css +1 -0
  15. data/vendor/assets/stylesheets/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-shadow.min.css +1 -0
  16. data/vendor/assets/stylesheets/plugins/tooltipster/sideTip/tooltipster-sideTip.min.css +1 -0
  17. data/vendor/assets/stylesheets/tooltipster.bundle.css +388 -0
  18. data/vendor/assets/stylesheets/tooltipster.bundle.min.css +1 -0
  19. data/vendor/assets/stylesheets/tooltipster.core.css +231 -0
  20. data/vendor/assets/stylesheets/tooltipster.core.min.css +1 -0
  21. metadata +29 -26
  22. data/vendor/assets/images/tooltipster-rails/map.png +0 -0
  23. data/vendor/assets/javascripts/jquery.tooltipster.min.js +0 -1
  24. data/vendor/assets/stylesheets/tooltipster-themes/tooltipster-light.css +0 -12
  25. data/vendor/assets/stylesheets/tooltipster-themes/tooltipster-noir.css +0 -12
  26. data/vendor/assets/stylesheets/tooltipster-themes/tooltipster-punk.css +0 -12
  27. data/vendor/assets/stylesheets/tooltipster-themes/tooltipster-shadow.css +0 -12
  28. data/vendor/assets/stylesheets/tooltipster.css +0 -274
@@ -0,0 +1,2 @@
1
+ /*! tooltipster v4.1.2 */!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("jquery")):b(jQuery)}(this,function(a){function b(a){this.$container,this.constraints=null,this.__$tooltip,this.__init(a)}function c(b,c){var d=!0;return a.each(b,function(a,e){return void 0===c[a]||b[a]!==c[a]?(d=!1,!1):void 0}),d}function d(b){var c=b.attr("id"),d=c?h.window.document.getElementById(c):null;return d?d===b[0]:a.contains(h.window.document.body,b[0])}function e(){if(!g)return!1;var a=g.document.body||g.document.documentElement,b=a.style,c="transition",d=["Moz","Webkit","Khtml","O","ms"];if("string"==typeof b[c])return!0;c=c.charAt(0).toUpperCase()+c.substr(1);for(var e=0;e<d.length;e++)if("string"==typeof b[d[e]+c])return!0;return!1}var f={animation:"fade",animationDuration:350,content:null,contentAsHTML:!1,contentCloning:!1,debug:!0,delay:300,delayTouch:[300,500],functionInit:null,functionBefore:null,functionReady:null,functionAfter:null,functionFormat:null,IEmin:6,interactive:!1,multiple:!1,parent:"body",plugins:["sideTip"],repositionOnScroll:!1,restoration:"none",selfDestruction:!0,theme:[],timer:0,trackerInterval:500,trackOrigin:!1,trackTooltip:!1,trigger:"hover",triggerClose:{click:!1,mouseleave:!1,originClick:!1,scroll:!1,tap:!1,touchleave:!1},triggerOpen:{click:!1,mouseenter:!1,tap:!1,touchstart:!1},updateAnimation:"rotate",zIndex:9999999},g="undefined"!=typeof window?window:null,h={hasTouchCapability:!(!g||!("ontouchstart"in g||g.DocumentTouch&&g.document instanceof g.DocumentTouch||g.navigator.maxTouchPoints)),hasTransitions:e(),IE:!1,semVer:"4.1.2",window:g},i=function(){this.__$emitterPrivate=a({}),this.__$emitterPublic=a({}),this.__instancesLatestArr=[],this.__plugins={},this._env=h};i.prototype={__bridge:function(b,c,d){if(!c[d]){var e=function(){};e.prototype=b;var g=new e;g.__init&&g.__init(c),a.each(b,function(a,b){0!=a.indexOf("__")&&(c[a]?f.debug&&console.log("The "+a+" method of the "+d+" plugin conflicts with another plugin or native methods"):(c[a]=function(){return g[a].apply(g,Array.prototype.slice.apply(arguments))},c[a].bridged=g))}),c[d]=g}return this},__setWindow:function(a){return h.window=a,this},_getRuler:function(a){return new b(a)},_off:function(){return this.__$emitterPrivate.off.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_on:function(){return this.__$emitterPrivate.on.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_one:function(){return this.__$emitterPrivate.one.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_plugin:function(b){var c=this;if("string"==typeof b){var d=b,e=null;return d.indexOf(".")>0?e=c.__plugins[d]:a.each(c.__plugins,function(a,b){return b.name.substring(b.name.length-d.length-1)=="."+d?(e=b,!1):void 0}),e}if(b.name.indexOf(".")<0)throw new Error("Plugins must be namespaced");return c.__plugins[b.name]=b,b.core&&c.__bridge(b.core,c,b.name),this},_trigger:function(){var a=Array.prototype.slice.apply(arguments);return"string"==typeof a[0]&&(a[0]={type:a[0]}),this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate,a),this.__$emitterPublic.trigger.apply(this.__$emitterPublic,a),this},instances:function(b){var c=[],d=b||".tooltipstered";return a(d).each(function(){var b=a(this),d=b.data("tooltipster-ns");d&&a.each(d,function(a,d){c.push(b.data(d))})}),c},instancesLatest:function(){return this.__instancesLatestArr},off:function(){return this.__$emitterPublic.off.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},on:function(){return this.__$emitterPublic.on.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},one:function(){return this.__$emitterPublic.one.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},origins:function(b){var c=b?b+" ":"";return a(c+".tooltipstered").toArray()},setDefaults:function(b){return a.extend(f,b),this},triggerHandler:function(){return this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this}},a.tooltipster=new i,a.Tooltipster=function(b,c){this.__callbacks={close:[],open:[]},this.__closingTime,this.__Content,this.__contentBcr,this.__destroyed=!1,this.__destroying=!1,this.__$emitterPrivate=a({}),this.__$emitterPublic=a({}),this.__enabled=!0,this.__garbageCollector,this.__Geometry,this.__lastPosition,this.__namespace="tooltipster-"+Math.round(1e6*Math.random()),this.__options,this.__$originParents,this.__pointerIsOverOrigin=!1,this.__previousThemes=[],this.__state="closed",this.__timeouts={close:[],open:null},this.__touchEvents=[],this.__tracker=null,this._$origin,this._$tooltip,this.__init(b,c)},a.Tooltipster.prototype={__init:function(b,c){var d=this;if(d._$origin=a(b),d.__options=a.extend(!0,{},f,c),d.__optionsFormat(),!h.IE||h.IE>=d.__options.IEmin){var e=null;if(void 0===d._$origin.data("tooltipster-initialTitle")&&(e=d._$origin.attr("title"),void 0===e&&(e=null),d._$origin.data("tooltipster-initialTitle",e)),null!==d.__options.content)d.__contentSet(d.__options.content);else{var g,i=d._$origin.attr("data-tooltip-content");i&&(g=a(i)),g&&g[0]?d.__contentSet(g.first()):d.__contentSet(e)}d._$origin.removeAttr("title").addClass("tooltipstered"),d.__prepareOrigin(),d.__prepareGC(),a.each(d.__options.plugins,function(a,b){d._plug(b)}),h.hasTouchCapability&&a("body").on("touchmove."+d.__namespace+"-triggerOpen",function(a){d._touchRecordEvent(a)}),d._on("created",function(){d.__prepareTooltip()})._on("repositioned",function(a){d.__lastPosition=a.position})}else d.__options.disabled=!0},__contentInsert:function(){var a=this,b=a._$tooltip.find(".tooltipster-content"),c=a.__Content,d=function(a){c=a};return a._trigger({type:"format",content:a.__Content,format:d}),a.__options.functionFormat&&(c=a.__options.functionFormat.call(a,a,{origin:a._$origin[0]},a.__Content)),"string"!=typeof c||a.__options.contentAsHTML?b.empty().append(c):b.text(c),a},__contentSet:function(b){return b instanceof a&&this.__options.contentCloning&&(b=b.clone(!0)),this.__Content=b,this._trigger({type:"updated",content:b}),this},__destroyError:function(){throw new Error("This tooltip has been destroyed and cannot execute your method call.")},__geometry:function(){var b=this,c=b._$origin,d=b._$origin.is("area");if(d){var e=b._$origin.parent().attr("name");c=a('img[usemap="#'+e+'"]')}var f=c[0].getBoundingClientRect(),g=a(h.window.document),i=a(h.window),j=c,k={available:{document:null,window:null},document:{size:{height:g.height(),width:g.width()}},window:{scroll:{left:h.window.scrollX||h.window.document.documentElement.scrollLeft,top:h.window.scrollY||h.window.document.documentElement.scrollTop},size:{height:i.height(),width:i.width()}},origin:{fixedLineage:!1,offset:{},size:{height:f.bottom-f.top,width:f.right-f.left},usemapImage:d?c[0]:null,windowOffset:{bottom:f.bottom,left:f.left,right:f.right,top:f.top}}};if(d){var l=b._$origin.attr("shape"),m=b._$origin.attr("coords");if(m&&(m=m.split(","),a.map(m,function(a,b){m[b]=parseInt(a)})),"default"!=l)switch(l){case"circle":var n=m[0],o=m[1],p=m[2],q=o-p,r=n-p;k.origin.size.height=2*p,k.origin.size.width=k.origin.size.height,k.origin.windowOffset.left+=r,k.origin.windowOffset.top+=q;break;case"rect":var s=m[0],t=m[1],u=m[2],v=m[3];k.origin.size.height=v-t,k.origin.size.width=u-s,k.origin.windowOffset.left+=s,k.origin.windowOffset.top+=t;break;case"poly":for(var w=0,x=0,y=0,z=0,A="even",B=0;B<m.length;B++){var C=m[B];"even"==A?(C>y&&(y=C,0===B&&(w=y)),w>C&&(w=C),A="odd"):(C>z&&(z=C,1==B&&(x=z)),x>C&&(x=C),A="even")}k.origin.size.height=z-x,k.origin.size.width=y-w,k.origin.windowOffset.left+=w,k.origin.windowOffset.top+=x}}var D=function(a){k.origin.size.height=a.height,k.origin.windowOffset.left=a.left,k.origin.windowOffset.top=a.top,k.origin.size.width=a.width};for(b._trigger({type:"geometry",edit:D,geometry:{height:k.origin.size.height,left:k.origin.windowOffset.left,top:k.origin.windowOffset.top,width:k.origin.size.width}}),k.origin.windowOffset.right=k.origin.windowOffset.left+k.origin.size.width,k.origin.windowOffset.bottom=k.origin.windowOffset.top+k.origin.size.height,k.origin.offset.left=k.origin.windowOffset.left+h.window.scrollX,k.origin.offset.top=k.origin.windowOffset.top+h.window.scrollY,k.origin.offset.bottom=k.origin.offset.top+k.origin.size.height,k.origin.offset.right=k.origin.offset.left+k.origin.size.width,k.available.document={bottom:{height:k.document.size.height-k.origin.offset.bottom,width:k.document.size.width},left:{height:k.document.size.height,width:k.origin.offset.left},right:{height:k.document.size.height,width:k.document.size.width-k.origin.offset.right},top:{height:k.origin.offset.top,width:k.document.size.width}},k.available.window={bottom:{height:Math.max(k.window.size.height-Math.max(k.origin.windowOffset.bottom,0),0),width:k.window.size.width},left:{height:k.window.size.height,width:Math.max(k.origin.windowOffset.left,0)},right:{height:k.window.size.height,width:Math.max(k.window.size.width-Math.max(k.origin.windowOffset.right,0),0)},top:{height:Math.max(k.origin.windowOffset.top,0),width:k.window.size.width}};"html"!=j[0].tagName.toLowerCase();){if("fixed"==j.css("position")){k.origin.fixedLineage=!0;break}j=j.parent()}return k},__optionsFormat:function(){return"number"==typeof this.__options.animationDuration&&(this.__options.animationDuration=[this.__options.animationDuration,this.__options.animationDuration]),"number"==typeof this.__options.delay&&(this.__options.delay=[this.__options.delay,this.__options.delay]),"number"==typeof this.__options.delayTouch&&(this.__options.delayTouch=[this.__options.delayTouch,this.__options.delayTouch]),"string"==typeof this.__options.theme&&(this.__options.theme=[this.__options.theme]),"string"==typeof this.__options.parent&&(this.__options.parent=a(this.__options.parent)),"hover"==this.__options.trigger?(this.__options.triggerOpen={mouseenter:!0,touchstart:!0},this.__options.triggerClose={mouseleave:!0,originClick:!0,touchleave:!0}):"click"==this.__options.trigger&&(this.__options.triggerOpen={click:!0,tap:!0},this.__options.triggerClose={click:!0,tap:!0}),this._trigger("options"),this},__prepareGC:function(){var b=this;return b.__options.selfDestruction?b.__garbageCollector=setInterval(function(){var c=(new Date).getTime();b.__touchEvents=a.grep(b.__touchEvents,function(a,b){return c-a.time>6e4}),d(b._$origin)||b.destroy()},2e4):clearInterval(b.__garbageCollector),b},__prepareOrigin:function(){var a=this;if(a._$origin.off("."+a.__namespace+"-triggerOpen"),h.hasTouchCapability&&a._$origin.on("touchstart."+a.__namespace+"-triggerOpen touchend."+a.__namespace+"-triggerOpen touchcancel."+a.__namespace+"-triggerOpen",function(b){a._touchRecordEvent(b)}),a.__options.triggerOpen.click||a.__options.triggerOpen.tap&&h.hasTouchCapability){var b="";a.__options.triggerOpen.click&&(b+="click."+a.__namespace+"-triggerOpen "),a.__options.triggerOpen.tap&&h.hasTouchCapability&&(b+="touchend."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){a._touchIsMeaningfulEvent(b)&&a._open(b)})}if(a.__options.triggerOpen.mouseenter||a.__options.triggerOpen.touchstart&&h.hasTouchCapability){var b="";a.__options.triggerOpen.mouseenter&&(b+="mouseenter."+a.__namespace+"-triggerOpen "),a.__options.triggerOpen.touchstart&&h.hasTouchCapability&&(b+="touchstart."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){!a._touchIsTouchEvent(b)&&a._touchIsEmulatedEvent(b)||(a.__pointerIsOverOrigin=!0,a._openShortly(b))})}if(a.__options.triggerClose.mouseleave||a.__options.triggerClose.touchleave&&h.hasTouchCapability){var b="";a.__options.triggerClose.mouseleave&&(b+="mouseleave."+a.__namespace+"-triggerOpen "),a.__options.triggerClose.touchleave&&h.hasTouchCapability&&(b+="touchend."+a.__namespace+"-triggerOpen touchcancel."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){a._touchIsMeaningfulEvent(b)&&(a.__pointerIsOverOrigin=!1)})}return a},__prepareTooltip:function(){var b=this,c=b.__options.interactive?"auto":"";return b._$tooltip.attr("id",b.__namespace).css({"pointer-events":c,zIndex:b.__options.zIndex}),a.each(b.__previousThemes,function(a,c){b._$tooltip.removeClass(c)}),a.each(b.__options.theme,function(a,c){b._$tooltip.addClass(c)}),b.__previousThemes=a.merge([],b.__options.theme),b},__scrollHandler:function(b){var c=this;if(c.__options.triggerClose.scroll)c._close(b);else{if(b.target===h.window.document)c.__Geometry.origin.fixedLineage||c.__options.repositionOnScroll&&c.reposition(b);else{var d=c.__geometry(),e=!1;if("fixed"!=c._$origin.css("position")&&c.__$originParents.each(function(b,c){var f=a(c),g=f.css("overflow-x"),h=f.css("overflow-y");if("visible"!=g||"visible"!=h){var i=c.getBoundingClientRect();if("visible"!=g&&(d.origin.windowOffset.left<i.left||d.origin.windowOffset.right>i.right))return e=!0,!1;if("visible"!=h&&(d.origin.windowOffset.top<i.top||d.origin.windowOffset.bottom>i.bottom))return e=!0,!1}return"fixed"==f.css("position")?!1:void 0}),e)c._$tooltip.css("visibility","hidden");else if(c._$tooltip.css("visibility","visible"),c.__options.repositionOnScroll)c.reposition(b);else{var f=d.origin.offset.left-c.__Geometry.origin.offset.left,g=d.origin.offset.top-c.__Geometry.origin.offset.top;c._$tooltip.css({left:c.__lastPosition.coord.left+f,top:c.__lastPosition.coord.top+g})}}c._trigger({type:"scroll",event:b})}return c},__stateSet:function(a){return this.__state=a,this._trigger({type:"state",state:a}),this},__timeoutsClear:function(){return clearTimeout(this.__timeouts.open),this.__timeouts.open=null,a.each(this.__timeouts.close,function(a,b){clearTimeout(b)}),this.__timeouts.close=[],this},__trackerStart:function(){var a=this,b=a._$tooltip.find(".tooltipster-content");return a.__options.trackTooltip&&(a.__contentBcr=b[0].getBoundingClientRect()),a.__tracker=setInterval(function(){if(d(a._$origin)&&d(a._$tooltip)){if(a.__options.trackOrigin){var e=a.__geometry(),f=!1;c(e.origin.size,a.__Geometry.origin.size)&&(a.__Geometry.origin.fixedLineage?c(e.origin.windowOffset,a.__Geometry.origin.windowOffset)&&(f=!0):c(e.origin.offset,a.__Geometry.origin.offset)&&(f=!0)),f||(a.__options.triggerClose.mouseleave?a._close():a.reposition())}if(a.__options.trackTooltip){var g=b[0].getBoundingClientRect();g.height===a.__contentBcr.height&&g.width===a.__contentBcr.width||(a.reposition(),a.__contentBcr=g)}}else a._close()},a.__options.trackerInterval),a},_close:function(b,c){var d=this,e=!0;if(d._trigger({type:"close",event:b,stop:function(){e=!1}}),e||d.__destroying){c&&d.__callbacks.close.push(c),d.__callbacks.open=[],d.__timeoutsClear();var f=function(){a.each(d.__callbacks.close,function(a,c){c.call(d,d,{event:b,origin:d._$origin[0]})}),d.__callbacks.close=[]};if("closed"!=d.__state){var g=!0,i=new Date,j=i.getTime(),k=j+d.__options.animationDuration[1];if("disappearing"==d.__state&&k>d.__closingTime&&(g=!1),g){d.__closingTime=k,"disappearing"!=d.__state&&d.__stateSet("disappearing");var l=function(){clearInterval(d.__tracker),d._trigger({type:"closing",event:b}),d._$tooltip.off("."+d.__namespace+"-triggerClose").removeClass("tooltipster-dying"),a(h.window).off("."+d.__namespace+"-triggerClose"),d.__$originParents.each(function(b,c){a(c).off("scroll."+d.__namespace+"-triggerClose")}),d.__$originParents=null,a("body").off("."+d.__namespace+"-triggerClose"),d._$origin.off("."+d.__namespace+"-triggerClose"),d._off("dismissable"),d.__stateSet("closed"),d._trigger({type:"after",event:b}),d.__options.functionAfter&&d.__options.functionAfter.call(d,d,{event:b}),f()};h.hasTransitions?(d._$tooltip.css({"-moz-animation-duration":d.__options.animationDuration[1]+"ms","-ms-animation-duration":d.__options.animationDuration[1]+"ms","-o-animation-duration":d.__options.animationDuration[1]+"ms","-webkit-animation-duration":d.__options.animationDuration[1]+"ms","animation-duration":d.__options.animationDuration[1]+"ms","transition-duration":d.__options.animationDuration[1]+"ms"}),d._$tooltip.clearQueue().removeClass("tooltipster-show").addClass("tooltipster-dying"),d.__options.animationDuration[1]>0&&d._$tooltip.delay(d.__options.animationDuration[1]),d._$tooltip.queue(l)):d._$tooltip.stop().fadeOut(d.__options.animationDuration[1],l)}}else f()}return d},_off:function(){return this.__$emitterPrivate.off.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_on:function(){return this.__$emitterPrivate.on.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_one:function(){return this.__$emitterPrivate.one.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_open:function(b,c){var e=this;if(!e.__destroying&&d(e._$origin)&&e.__enabled){var f=!0;if("closed"==e.__state&&(e._trigger({type:"before",event:b,stop:function(){f=!1}}),f&&e.__options.functionBefore&&(f=e.__options.functionBefore.call(e,e,{event:b,origin:e._$origin[0]}))),f!==!1&&null!==e.__Content){c&&e.__callbacks.open.push(c),e.__callbacks.close=[],e.__timeoutsClear();var g,i=function(){"stable"!=e.__state&&e.__stateSet("stable"),a.each(e.__callbacks.open,function(a,b){b.call(e,e,{origin:e._$origin[0],tooltip:e._$tooltip[0]})}),e.__callbacks.open=[]};if("closed"!==e.__state)g=0,"disappearing"===e.__state?(e.__stateSet("appearing"),h.hasTransitions?(e._$tooltip.clearQueue().removeClass("tooltipster-dying").addClass("tooltipster-show"),e.__options.animationDuration[0]>0&&e._$tooltip.delay(e.__options.animationDuration[0]),e._$tooltip.queue(i)):e._$tooltip.stop().fadeIn(i)):"stable"==e.__state&&i();else{if(e.__stateSet("appearing"),g=e.__options.animationDuration[0],e.__contentInsert(),e.reposition(b,!0),h.hasTransitions?(e._$tooltip.addClass("tooltipster-"+e.__options.animation).addClass("tooltipster-initial").css({"-moz-animation-duration":e.__options.animationDuration[0]+"ms","-ms-animation-duration":e.__options.animationDuration[0]+"ms","-o-animation-duration":e.__options.animationDuration[0]+"ms","-webkit-animation-duration":e.__options.animationDuration[0]+"ms","animation-duration":e.__options.animationDuration[0]+"ms","transition-duration":e.__options.animationDuration[0]+"ms"}),setTimeout(function(){"closed"!=e.__state&&(e._$tooltip.addClass("tooltipster-show").removeClass("tooltipster-initial"),e.__options.animationDuration[0]>0&&e._$tooltip.delay(e.__options.animationDuration[0]),e._$tooltip.queue(i))},0)):e._$tooltip.css("display","none").fadeIn(e.__options.animationDuration[0],i),e.__trackerStart(),a(h.window).on("resize."+e.__namespace+"-triggerClose",function(a){e.reposition(a)}).on("scroll."+e.__namespace+"-triggerClose",function(a){e.__scrollHandler(a)}),e.__$originParents=e._$origin.parents(),e.__$originParents.each(function(b,c){a(c).on("scroll."+e.__namespace+"-triggerClose",function(a){e.__scrollHandler(a)})}),e.__options.triggerClose.mouseleave||e.__options.triggerClose.touchleave&&h.hasTouchCapability){e._on("dismissable",function(a){a.dismissable?a.delay?(m=setTimeout(function(){e._close(a.event)},a.delay),e.__timeouts.close.push(m)):e._close(a):clearTimeout(m)});var j=e._$origin,k="",l="",m=null;e.__options.interactive&&(j=j.add(e._$tooltip)),e.__options.triggerClose.mouseleave&&(k+="mouseenter."+e.__namespace+"-triggerClose ",l+="mouseleave."+e.__namespace+"-triggerClose "),e.__options.triggerClose.touchleave&&h.hasTouchCapability&&(k+="touchstart."+e.__namespace+"-triggerClose",l+="touchend."+e.__namespace+"-triggerClose touchcancel."+e.__namespace+"-triggerClose"),j.on(l,function(a){if(e._touchIsTouchEvent(a)||!e._touchIsEmulatedEvent(a)){var b="mouseleave"==a.type?e.__options.delay:e.__options.delayTouch;e._trigger({delay:b[1],dismissable:!0,event:a,type:"dismissable"})}}).on(k,function(a){!e._touchIsTouchEvent(a)&&e._touchIsEmulatedEvent(a)||e._trigger({dismissable:!1,event:a,type:"dismissable"})})}e.__options.triggerClose.originClick&&e._$origin.on("click."+e.__namespace+"-triggerClose",function(a){e._touchIsTouchEvent(a)||e._touchIsEmulatedEvent(a)||e._close(a)}),(e.__options.triggerClose.click||e.__options.triggerClose.tap&&h.hasTouchCapability)&&setTimeout(function(){if("closed"!=e.__state){var b="";e.__options.triggerClose.click&&(b+="click."+e.__namespace+"-triggerClose "),e.__options.triggerClose.tap&&h.hasTouchCapability&&(b+="touchend."+e.__namespace+"-triggerClose"),a("body").on(b,function(b){e._touchIsMeaningfulEvent(b)&&(e._touchRecordEvent(b),e.__options.interactive&&a.contains(e._$tooltip[0],b.target)||e._close(b))}),e.__options.triggerClose.tap&&h.hasTouchCapability&&a("body").on("touchstart."+e.__namespace+"-triggerClose",function(a){e._touchRecordEvent(a)})}},0),e._trigger("ready"),e.__options.functionReady&&e.__options.functionReady.call(e,e,{origin:e._$origin[0],tooltip:e._$tooltip[0]})}if(e.__options.timer>0){var m=setTimeout(function(){e._close()},e.__options.timer+g);e.__timeouts.close.push(m)}}}return e},_openShortly:function(a){var b=this,c=!0;if("stable"!=b.__state&&"appearing"!=b.__state&&!b.__timeouts.open&&(b._trigger({type:"start",event:a,stop:function(){c=!1}}),c)){var d=0==a.type.indexOf("touch")?b.__options.delayTouch:b.__options.delay;d[0]?b.__timeouts.open=setTimeout(function(){b.__timeouts.open=null,b.__pointerIsOverOrigin&&b._touchIsMeaningfulEvent(a)?(b._trigger("startend"),b._open(a)):b._trigger("startcancel")},d[0]):(b._trigger("startend"),b._open(a))}return b},_optionsExtract:function(b,c){var d=this,e=a.extend(!0,{},c),f=d.__options[b];return f||(f={},a.each(c,function(a,b){var c=d.__options[a];void 0!==c&&(f[a]=c)})),a.each(e,function(b,c){void 0!==f[b]&&("object"!=typeof c||c instanceof Array||null==c||"object"!=typeof f[b]||f[b]instanceof Array||null==f[b]?e[b]=f[b]:a.extend(e[b],f[b]))}),e},_plug:function(b){var c=a.tooltipster._plugin(b);if(!c)throw new Error('The "'+b+'" plugin is not defined');return c.instance&&a.tooltipster.__bridge(c.instance,this,c.name),this},_touchIsEmulatedEvent:function(a){for(var b=!1,c=(new Date).getTime(),d=this.__touchEvents.length-1;d>=0;d--){var e=this.__touchEvents[d];if(!(c-e.time<500))break;e.target===a.target&&(b=!0)}return b},_touchIsMeaningfulEvent:function(a){return this._touchIsTouchEvent(a)&&!this._touchSwiped(a.target)||!this._touchIsTouchEvent(a)&&!this._touchIsEmulatedEvent(a)},_touchIsTouchEvent:function(a){return 0==a.type.indexOf("touch")},_touchRecordEvent:function(a){return this._touchIsTouchEvent(a)&&(a.time=(new Date).getTime(),this.__touchEvents.push(a)),this},_touchSwiped:function(a){for(var b=!1,c=this.__touchEvents.length-1;c>=0;c--){var d=this.__touchEvents[c];if("touchmove"==d.type){b=!0;break}if("touchstart"==d.type&&a===d.target)break}return b},_trigger:function(){var b=Array.prototype.slice.apply(arguments);return"string"==typeof b[0]&&(b[0]={type:b[0]}),b[0].instance=this,b[0].origin=this._$origin?this._$origin[0]:null,b[0].tooltip=this._$tooltip?this._$tooltip[0]:null,this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate,b),a.tooltipster._trigger.apply(a.tooltipster,b),this.__$emitterPublic.trigger.apply(this.__$emitterPublic,b),this},_unplug:function(b){var c=this;if(c[b]){var d=a.tooltipster._plugin(b);d.instance&&a.each(d.instance,function(a,d){c[a]&&c[a].bridged===c[b]&&delete c[a]}),c[b].__destroy&&c[b].__destroy(),delete c[b]}return c},close:function(a){return this.__destroyed?this.__destroyError():this._close(null,a),this},content:function(a){var b=this;if(void 0===a)return b.__Content;if(b.__destroyed)b.__destroyError();else if(b.__contentSet(a),null!==b.__Content){if("closed"!==b.__state&&(b.__contentInsert(),b.reposition(),b.__options.updateAnimation))if(h.hasTransitions){var c=b.__options.updateAnimation;b._$tooltip.addClass("tooltipster-update-"+c),setTimeout(function(){"closed"!=b.__state&&b._$tooltip.removeClass("tooltipster-update-"+c)},1e3)}else b._$tooltip.fadeTo(200,.5,function(){"closed"!=b.__state&&b._$tooltip.fadeTo(200,1)})}else b._close();return b},destroy:function(){var b=this;return b.__destroyed?b.__destroyError():b.__destroying||(b.__destroying=!0,b._close(null,function(){b._trigger("destroy"),b.__destroying=!1,b.__destroyed=!0,b._$origin.removeData(b.__namespace).off("."+b.__namespace+"-triggerOpen"),a("body").off("."+b.__namespace+"-triggerOpen");var c=b._$origin.data("tooltipster-ns");if(c)if(1===c.length){var d=null;"previous"==b.__options.restoration?d=b._$origin.data("tooltipster-initialTitle"):"current"==b.__options.restoration&&(d="string"==typeof b.__Content?b.__Content:a("<div></div>").append(b.__Content).html()),d&&b._$origin.attr("title",d),b._$origin.removeClass("tooltipstered"),b._$origin.removeData("tooltipster-ns").removeData("tooltipster-initialTitle")}else c=a.grep(c,function(a,c){return a!==b.__namespace}),b._$origin.data("tooltipster-ns",c);b._trigger("destroyed"),b._off(),b.off(),b.__Content=null,b.__$emitterPrivate=null,b.__$emitterPublic=null,b.__options.parent=null,b._$origin=null,b._$tooltip=null,a.tooltipster.__instancesLatestArr=a.grep(a.tooltipster.__instancesLatestArr,function(a,c){return b!==a}),clearInterval(b.__garbageCollector)})),b},disable:function(){return this.__destroyed?(this.__destroyError(),this):(this._close(),this.__enabled=!1,this)},elementOrigin:function(){return this.__destroyed?void this.__destroyError():this._$origin[0]},elementTooltip:function(){return this._$tooltip?this._$tooltip[0]:null},enable:function(){return this.__enabled=!0,this},hide:function(a){return this.close(a)},instance:function(){return this},off:function(){return this.__destroyed||this.__$emitterPublic.off.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},on:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.on.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},one:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.one.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},open:function(a){return this.__destroyed||this.__destroying?this.__destroyError():this._open(null,a),this},option:function(b,c){return void 0===c?this.__options[b]:(this.__destroyed?this.__destroyError():(this.__options[b]=c,this.__optionsFormat(),a.inArray(b,["trigger","triggerClose","triggerOpen"])>=0&&this.__prepareOrigin(),"selfDestruction"===b&&this.__prepareGC()),this)},reposition:function(a,b){var c=this;return c.__destroyed?c.__destroyError():(d(c._$tooltip)||b)&&(b||c._$tooltip.detach(),c.__Geometry=c.__geometry(),c._trigger({type:"reposition",event:a,helper:{geo:c.__Geometry}})),c},show:function(a){return this.open(a)},status:function(){return{destroyed:this.__destroyed,destroying:this.__destroying,enabled:this.__enabled,open:"closed"!==this.__state,state:this.__state}},triggerHandler:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this}},a.fn.tooltipster=function(){var b=Array.prototype.slice.apply(arguments),c="You are using a single HTML element as content for several tooltips. You probably want to set the contentCloning option to TRUE.";if(0===this.length)return this;if("string"==typeof b[0]){var d="#*$~&";return this.each(function(){var e=a(this).data("tooltipster-ns"),f=e?a(this).data(e[0]):null;if(!f)throw new Error("You called Tooltipster's \""+b[0]+'" method on an uninitialized element');if("function"!=typeof f[b[0]])throw new Error('Unknown method "'+b[0]+'"');this.length>1&&"content"==b[0]&&(b[1]instanceof a||"object"==typeof b[1]&&null!=b[1]&&b[1].tagName)&&!f.__options.contentCloning&&f.__options.debug&&console.log(c);var g=f[b[0]](b[1],b[2]);return g!==f||"instance"===b[0]?(d=g,!1):void 0}),"#*$~&"!==d?d:this}a.tooltipster.__instancesLatestArr=[];var e=b[0]&&void 0!==b[0].multiple,g=e&&b[0].multiple||!e&&f.multiple,h=b[0]&&void 0!==b[0].content,i=h&&b[0].content||!h&&f.content,j=b[0]&&void 0!==b[0].contentCloning,k=j&&b[0].contentCloning||!j&&f.contentCloning,l=b[0]&&void 0!==b[0].debug,m=l&&b[0].debug||!l&&f.debug;return this.length>1&&(i instanceof a||"object"==typeof i&&null!=i&&i.tagName)&&!k&&m&&console.log(c),this.each(function(){var c=!1,d=a(this),e=d.data("tooltipster-ns"),f=null;e?g?c=!0:m&&(console.log("Tooltipster: one or more tooltips are already attached to the element below. Ignoring."),console.log(this)):c=!0,c&&(f=new a.Tooltipster(this,b[0]),e||(e=[]),e.push(f.__namespace),d.data("tooltipster-ns",e),d.data(f.__namespace,f),f.__options.functionInit&&f.__options.functionInit.call(f,f,{origin:this}),f._trigger("init")),a.tooltipster.__instancesLatestArr.push(f)}),this},b.prototype={__init:function(b){this.__$tooltip=b,this.__$tooltip.css({left:0,overflow:"hidden",position:"absolute",top:0}).find(".tooltipster-content").css("overflow","auto"),this.$container=a('<div class="tooltipster-ruler"></div>').append(this.__$tooltip).appendTo("body")},__forceRedraw:function(){var a=this.__$tooltip.parent();this.__$tooltip.detach(),this.__$tooltip.appendTo(a)},constrain:function(a,b){return this.constraints={width:a,height:b},this.__$tooltip.css({display:"block",height:"",overflow:"auto",width:a}),this},destroy:function(){this.__$tooltip.detach().find(".tooltipster-content").css({display:"",overflow:""}),this.$container.remove()},free:function(){return this.constraints=null,this.__$tooltip.css({display:"",height:"",overflow:"visible",width:""}),this},measure:function(){this.__forceRedraw();var a=this.__$tooltip[0].getBoundingClientRect(),b={size:{height:a.height||a.bottom,width:a.width||a.right}};if(this.constraints){var c=this.__$tooltip.find(".tooltipster-content"),d=this.__$tooltip.outerHeight(),e=c[0].getBoundingClientRect(),f={height:d<=this.constraints.height,width:a.width<=this.constraints.width&&e.width>=c[0].scrollWidth-1};b.fits=f.height&&f.width}return h.IE&&h.IE<=11&&(b.size.width=Math.ceil(b.size.width)+1),b}};var j=navigator.userAgent.toLowerCase();-1!=j.indexOf("msie")?h.IE=parseInt(j.split("msie")[1]):-1!==j.toLowerCase().indexOf("trident")&&-1!==j.indexOf(" rv:11")?h.IE=11:-1!=j.toLowerCase().indexOf("edge/")&&(h.IE=parseInt(j.toLowerCase().split("edge/")[1]));var k="tooltipster.sideTip";return a.tooltipster._plugin({name:k,instance:{__defaults:function(){return{arrow:!0,distance:6,functionPosition:null,maxWidth:null,minIntersection:16,minWidth:0,position:null,side:"top",viewportAware:!0}},__init:function(a){var b=this;b.__instance=a,b.__namespace="tooltipster-sideTip-"+Math.round(1e6*Math.random()),b.__previousState="closed",b.__options,b.__optionsFormat(),b.__instance._on("state."+b.__namespace,function(a){"closed"==a.state?b.__close():"appearing"==a.state&&"closed"==b.__previousState&&b.__create(),b.__previousState=a.state}),b.__instance._on("options."+b.__namespace,function(){b.__optionsFormat()}),b.__instance._on("reposition."+b.__namespace,function(a){b.__reposition(a.event,a.helper)})},__close:function(){this.__instance.content()instanceof a&&this.__instance.content().detach(),this.__instance._$tooltip.remove(),this.__instance._$tooltip=null},__create:function(){var b=a('<div class="tooltipster-base tooltipster-sidetip"><div class="tooltipster-box"><div class="tooltipster-content"></div></div><div class="tooltipster-arrow"><div class="tooltipster-arrow-uncropped"><div class="tooltipster-arrow-border"></div><div class="tooltipster-arrow-background"></div></div></div></div>');this.__options.arrow||b.find(".tooltipster-box").css("margin",0).end().find(".tooltipster-arrow").hide(),this.__options.minWidth&&b.css("min-width",this.__options.minWidth+"px"),this.__options.maxWidth&&b.css("max-width",this.__options.maxWidth+"px"),this.__instance._$tooltip=b,this.__instance._trigger("created")},__destroy:function(){this.__instance._off("."+self.__namespace)},__optionsFormat:function(){var b=this;if(b.__options=b.__instance._optionsExtract(k,b.__defaults()),b.__options.position&&(b.__options.side=b.__options.position),"object"!=typeof b.__options.distance&&(b.__options.distance=[b.__options.distance]),b.__options.distance.length<4&&(void 0===b.__options.distance[1]&&(b.__options.distance[1]=b.__options.distance[0]),
2
+ void 0===b.__options.distance[2]&&(b.__options.distance[2]=b.__options.distance[0]),void 0===b.__options.distance[3]&&(b.__options.distance[3]=b.__options.distance[1]),b.__options.distance={top:b.__options.distance[0],right:b.__options.distance[1],bottom:b.__options.distance[2],left:b.__options.distance[3]}),"string"==typeof b.__options.side){var c={top:"bottom",right:"left",bottom:"top",left:"right"};b.__options.side=[b.__options.side,c[b.__options.side]],"left"==b.__options.side[0]||"right"==b.__options.side[0]?b.__options.side.push("top","bottom"):b.__options.side.push("right","left")}6===a.tooltipster._env.IE&&b.__options.arrow!==!0&&(b.__options.arrow=!1)},__reposition:function(b,c){var d,e=this,f=e.__targetFind(c),g=[];e.__instance._$tooltip.detach();var h=e.__instance._$tooltip.clone(),i=a.tooltipster._getRuler(h),j=!1;switch(a.each(["window","document"],function(d,k){var l=null;if(e.__instance._trigger({container:k,helper:c,satisfied:j,takeTest:function(a){l=a},results:g,type:"positionTest"}),1==l||0!=l&&0==j&&("window"!=k||e.__options.viewportAware))for(var d=0;d<e.__options.side.length;d++){var m={horizontal:0,vertical:0},n=e.__options.side[d];"top"==n||"bottom"==n?m.vertical=e.__options.distance[n]:m.horizontal=e.__options.distance[n],e.__sideChange(h,n),a.each(["natural","constrained"],function(a,d){if(l=null,e.__instance._trigger({container:k,event:b,helper:c,mode:d,results:g,satisfied:j,side:n,takeTest:function(a){l=a},type:"positionTest"}),1==l||0!=l&&0==j){var h={container:k,distance:m,fits:null,mode:d,outerSize:null,side:n,size:null,target:f[n],whole:null},o="natural"==d?i.free():i.constrain(c.geo.available[k][n].width-m.horizontal,c.geo.available[k][n].height-m.vertical),p=o.measure();if(h.size=p.size,h.outerSize={height:p.size.height+m.vertical,width:p.size.width+m.horizontal},"natural"==d?c.geo.available[k][n].width>=h.outerSize.width&&c.geo.available[k][n].height>=h.outerSize.height?h.fits=!0:h.fits=!1:h.fits=p.fits,"window"==k&&(h.fits?"top"==n||"bottom"==n?h.whole=c.geo.origin.windowOffset.right>=e.__options.minIntersection&&c.geo.window.size.width-c.geo.origin.windowOffset.left>=e.__options.minIntersection:h.whole=c.geo.origin.windowOffset.bottom>=e.__options.minIntersection&&c.geo.window.size.height-c.geo.origin.windowOffset.top>=e.__options.minIntersection:h.whole=!1),g.push(h),h.whole)j=!0;else if("natural"==h.mode&&(h.fits||h.size.width<=c.geo.available[k][n].width))return!1}})}}),e.__instance._trigger({edit:function(a){g=a},event:b,helper:c,results:g,type:"positionTested"}),g.sort(function(a,b){if(a.whole&&!b.whole)return-1;if(!a.whole&&b.whole)return 1;if(a.whole&&b.whole){var c=e.__options.side.indexOf(a.side),d=e.__options.side.indexOf(b.side);return d>c?-1:c>d?1:"natural"==a.mode?-1:1}if(a.fits&&!b.fits)return-1;if(!a.fits&&b.fits)return 1;if(a.fits&&b.fits){var c=e.__options.side.indexOf(a.side),d=e.__options.side.indexOf(b.side);return d>c?-1:c>d?1:"natural"==a.mode?-1:1}return"document"==a.container&&"bottom"==a.side&&"natural"==a.mode?-1:1}),d=g[0],d.coord={},d.side){case"left":case"right":d.coord.top=Math.floor(d.target-d.size.height/2);break;case"bottom":case"top":d.coord.left=Math.floor(d.target-d.size.width/2)}switch(d.side){case"left":d.coord.left=c.geo.origin.windowOffset.left-d.outerSize.width;break;case"right":d.coord.left=c.geo.origin.windowOffset.right+d.distance.horizontal;break;case"top":d.coord.top=c.geo.origin.windowOffset.top-d.outerSize.height;break;case"bottom":d.coord.top=c.geo.origin.windowOffset.bottom+d.distance.vertical}"window"==d.container?"top"==d.side||"bottom"==d.side?d.coord.left<0?c.geo.origin.windowOffset.right-this.__options.minIntersection>=0?d.coord.left=0:d.coord.left=c.geo.origin.windowOffset.right-this.__options.minIntersection-1:d.coord.left>c.geo.window.size.width-d.size.width&&(c.geo.origin.windowOffset.left+this.__options.minIntersection<=c.geo.window.size.width?d.coord.left=c.geo.window.size.width-d.size.width:d.coord.left=c.geo.origin.windowOffset.left+this.__options.minIntersection+1-d.size.width):d.coord.top<0?c.geo.origin.windowOffset.bottom-this.__options.minIntersection>=0?d.coord.top=0:d.coord.top=c.geo.origin.windowOffset.bottom-this.__options.minIntersection-1:d.coord.top>c.geo.window.size.height-d.size.height&&(c.geo.origin.windowOffset.top+this.__options.minIntersection<=c.geo.window.size.height?d.coord.top=c.geo.window.size.height-d.size.height:d.coord.top=c.geo.origin.windowOffset.top+this.__options.minIntersection+1-d.size.height):(d.coord.left>c.geo.window.size.width-d.size.width&&(d.coord.left=c.geo.window.size.width-d.size.width),d.coord.left<0&&(d.coord.left=0)),e.__sideChange(h,d.side),c.tooltipClone=h[0],c.tooltipParent=e.__instance.option("parent").parent[0],c.mode=d.mode,c.whole=d.whole,c.origin=e.__instance._$origin[0],c.tooltip=e.__instance._$tooltip[0],delete d.container,delete d.fits,delete d.mode,delete d.outerSize,delete d.whole,d.distance=d.distance.horizontal||d.distance.vertical;var k=a.extend(!0,{},d);if(e.__instance._trigger({edit:function(a){d=a},event:b,helper:c,position:k,type:"position"}),e.__options.functionPosition){var l=e.__options.functionPosition.call(e,e.__instance,c,k);l&&(d=l)}i.destroy();var m,n;"top"==d.side||"bottom"==d.side?(m={prop:"left",val:d.target-d.coord.left},n=d.size.width-this.__options.minIntersection):(m={prop:"top",val:d.target-d.coord.top},n=d.size.height-this.__options.minIntersection),m.val<this.__options.minIntersection?m.val=this.__options.minIntersection:m.val>n&&(m.val=n);var o;o=c.geo.origin.fixedLineage?c.geo.origin.windowOffset:{left:c.geo.origin.windowOffset.left+c.geo.window.scroll.left,top:c.geo.origin.windowOffset.top+c.geo.window.scroll.top},d.coord={left:o.left+(d.coord.left-c.geo.origin.windowOffset.left),top:o.top+(d.coord.top-c.geo.origin.windowOffset.top)},e.__sideChange(e.__instance._$tooltip,d.side),c.geo.origin.fixedLineage?e.__instance._$tooltip.css("position","fixed"):e.__instance._$tooltip.css("position",""),e.__instance._$tooltip.css({left:d.coord.left,top:d.coord.top,height:d.size.height,width:d.size.width}).find(".tooltipster-arrow").css({left:"",top:""}).css(m.prop,m.val),e.__instance._$tooltip.appendTo(e.__instance.option("parent")),e.__instance._trigger({type:"repositioned",event:b,position:d})},__sideChange:function(a,b){a.removeClass("tooltipster-bottom").removeClass("tooltipster-left").removeClass("tooltipster-right").removeClass("tooltipster-top").addClass("tooltipster-"+b)},__targetFind:function(a){var b={},c=this.__instance._$origin[0].getClientRects();if(c.length>1){var d=this.__instance._$origin.css("opacity");1==d&&(this.__instance._$origin.css("opacity",.99),c=this.__instance._$origin[0].getClientRects(),this.__instance._$origin.css("opacity",1))}if(c.length<2)b.top=Math.floor(a.geo.origin.windowOffset.left+a.geo.origin.size.width/2),b.bottom=b.top,b.left=Math.floor(a.geo.origin.windowOffset.top+a.geo.origin.size.height/2),b.right=b.left;else{var e=c[0];b.top=Math.floor(e.left+(e.right-e.left)/2),e=c.length>2?c[Math.ceil(c.length/2)-1]:c[0],b.right=Math.floor(e.top+(e.bottom-e.top)/2),e=c[c.length-1],b.bottom=Math.floor(e.left+(e.right-e.left)/2),e=c.length>2?c[Math.ceil((c.length+1)/2)-1]:c[c.length-1],b.left=Math.floor(e.top+(e.bottom-e.top)/2)}return b}}}),a});
@@ -0,0 +1,3299 @@
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
+
25
+ var 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',
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
110
+ core.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 once 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;
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
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
+ self.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').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 (!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
+ /**
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
+ */
3032
+ function 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
+
3043
+ Ruler.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
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
3242
+ function 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
+ */
3262
+ function 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
3270
+ var uA = navigator.userAgent.toLowerCase();
3271
+ if (uA.indexOf('msie') != -1) env.IE = parseInt(uA.split('msie')[1]);
3272
+ else if (uA.toLowerCase().indexOf('trident') !== -1 && uA.indexOf(' rv:11') !== -1) env.IE = 11;
3273
+ else if (uA.toLowerCase().indexOf('edge/') != -1) env.IE = parseInt(uA.toLowerCase().split('edge/')[1]);
3274
+
3275
+ // detecting support for CSS transitions
3276
+ function 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 file with a pluginreturn $;
3298
+
3299
+ }));