;(function($,window,document){ var pluginNS="mThumbnailScroller", pluginPfx="mTS", defaultSelector=".mThumbnailScroller", defaults={ /* set element/content width/height programmatically values: boolean, pixels, percentage option default ------------------------------------- setWidth false setHeight false */ /* set the initial css top property of content values: string (e.g. "-100px", "10%" etc.) */ setTop:0, /* set the initial css left property of content values: string (e.g. "-100px", "10%" etc.) */ setLeft:0, /* set the scrolling type values (string): "hover-0"/"hover-100" (number indicating percentage), "hover-precise", "click-0"/"click-100" (number indicating percentage), "click-thumb" */ type:"hover-50", /* scroller axis (vertical and/or horizontal) values (string): "y", "x", "yx" */ axis:"x", /* scrolling speed values: integer (higher=faster) */ speed:15, /* enable content touch-swipe scrolling values: boolean, integer, string (number) integer values define the axis-specific minimum amount required for scrolling momentum */ contentTouchScroll:25, /* markup option parameters */ markup:{ /* thumbnailsContainer sets the element containing your thumbnails. By default this is an unordered list ("ul") thumbnailContainer sets the element containing each thumbnail. By default this is a list-item ("li") thumbnailElement sets the actual thumbnail element. By default this is an image tag ("img") values: boolean, string option default ------------------------------------- thumbnailsContainer null thumbnailContainer null thumbnailElement null */ /* set the placeholder element of the buttons values: boolean, string */ buttonsPlaceholder:false, /* set buttons HTML */ buttonsHTML:{ up:"SVG set 1", down:"SVG set 1", left:"SVG set 1", right:"SVG set 1" } }, /* advanced option parameters */ advanced:{ /* auto-expand content horizontally (for "x" or "yx" axis) values: boolean */ autoExpandHorizontalScroll:true, /* auto-update scrollers on content, element or viewport resize should be true for fluid layouts/elements, adding/removing content dynamically, hiding/showing elements, content with images etc. values: boolean */ updateOnContentResize:true, /* auto-update scrollers each time each image inside the element is fully loaded values: boolean */ updateOnImageLoad:true /* auto-update scrollers based on the amount and size changes of specific selectors useful when you need to update the scroller(s) automatically, each time a type of element is added, removed or changes its size values: boolean, string (e.g. "ul li" will auto-update the scroller each time list-items inside the element are changed) a value of true (boolean) will auto-update the scroller each time any element is changed option default ------------------------------------- updateOnSelectorChange null */ }, /* scroller theme values: string */ theme:"none", /* user defined callback functions */ callbacks:{ /* Available callbacks: callback default ------------------------------------- onInit null onScrollStart null onScroll null onTotalScroll null onTotalScrollBack null whileScrolling null */ onTotalScrollOffset:0, onTotalScrollBackOffset:0, alwaysTriggerOffsets:true } /* add scroller(s) on all elements matching the current selector, now and in the future values: boolean, string string values: "on" (enable), "once" (disable after first invocation), "off" (disable) liveSelector values: string (selector) option default ------------------------------------- live false liveSelector null */ }, /* ---------------------------------------- VARS, CONSTANTS ---------------------------------------- */ totalInstances=0, /* plugin instances amount */ liveTimers={}, /* live option timers */ oldIE=(window.attachEvent && !window.addEventListener) ? 1 : 0, /* detect IE < 9 */ touchActive=false, /* global touch state (for touch and pointer events) */ touchi, /* touch interface */ /* general plugin classes */ classes=["mTS_disabled","mTS_destroyed","mTS_no_scroll"], /* ---------------------------------------- METODOS ---------------------------------------- */ methods={ init:function(options){ var options=$.extend(true,{},defaults,options), selector=_selector.call(this); /* validate selector */ /* if live option is enabled, monitor for elements matching the current selector and apply scroller(s) when found (now and in the future) */ if(options.live){ var liveSelector=options.liveSelector || this.selector || defaultSelector, /* live selector(s) */ $liveSelector=$(liveSelector); /* live selector(s) as jquery object */ if(options.live==="off"){ /* disable live if requested usage: $(selector).mThumbnailScroller({live:"off"}); */ removeLiveTimers(liveSelector); return; } liveTimers[liveSelector]=setTimeout(function(){ /* call mThumbnailScroller fn on live selector(s) every half-second */ $liveSelector.mThumbnailScroller(options); if(options.live==="once" && $liveSelector.length){ /* disable live after first invocation */ removeLiveTimers(liveSelector); } },500); }else{ removeLiveTimers(liveSelector); } /* options normalization */ options.speed=options.speed===0 ? 100 : options.speed; _theme(options); /* theme-specific options */ /* plugin constructor */ return $(selector).each(function(){ var $this=$(this); if(!$this.data(pluginPfx)){ /* prevent multiple instantiations */ /* store options and create objects in jquery data */ $this.data(pluginPfx,{ idx:++totalInstances, /* instance index */ opt:options, /* options */ html:null, /* element original html */ overflowed:null, /* overflowed axis */ bindEvents:false, /* object to check if events are bound */ tweenRunning:false, /* object to check if tween is running */ langDir:$this.css("direction"), /* detect/store direction (ltr or rtl) */ cbOffsets:null, /* object to check whether callback offsets always trigger */ /* object to check how scrolling events where last triggered "internal" (default - triggered by this script), "external" (triggered by other scripts, e.g. via scrollTo method) usage: object.data("mTS").trigger */ trigger:null }); /* HTML data attributes */ var o=$this.data(pluginPfx).opt, htmlDataAxis=$this.data("mts-axis"),htmlDataType=$this.data("mts-type"),htmlDataTheme=$this.data("mts-theme"); if(htmlDataAxis){o.axis=htmlDataAxis;} /* usage example: data-mts-axis="y" */ if(htmlDataType){o.type=htmlDataType;} /* usage example: data-mts-type="hover-50" */ if(htmlDataTheme){ /* usage example: data-mts-theme="theme-name" */ o.theme=htmlDataTheme; _theme(o); /* theme-specific options */ } _pluginMarkup.call(this); /* add plugin markup */ methods.update.call(null,$this); /* call the update method */ } }); }, /* ---------------------------------------- */ /* plugin update method updates content and scroller values, events and status ---------------------------------------- usage: $(selector).mThumbnailScroller("update"); */ update:function(el){ var selector=el || _selector.call(this); /* validate selector */ return $(selector).each(function(){ var $this=$(this); if($this.data(pluginPfx)){ /* check if plugin has initialized */ var d=$this.data(pluginPfx),o=d.opt, mTS_container=$("#mTS_"+d.idx+"_container"); if(!mTS_container.length){return;} if(d.tweenRunning){_stop($this);} /* stop any running tweens while updating */ /* if element was disabled or destroyed, remove class(es) */ if($this.hasClass(classes[0])){$this.removeClass(classes[0]);} if($this.hasClass(classes[1])){$this.removeClass(classes[1]);} _maxHeight.call(this); /* detect/set css max-height value */ _expandContentHorizontally.call(this); /* expand content horizontally */ d.overflowed=_overflowed.call(this); /* determine if scrolling is required */ _buttonsVisibility.call(this); /* show/hide buttons */ _bindEvents.call(this); /* bind scroller events */ /* reset scrolling position and/or events */ var to=[(mTS_container[0].offsetTop),(mTS_container[0].offsetLeft)]; if(o.axis!=="x"){ /* y/yx axis */ if(!d.overflowed[0]){ /* y scrolling is not required */ _resetContentPosition.call(this); /* reset content position */ if(o.axis==="y"){ _scrollTo($this,"0",{dir:"y",dur:0,overwrite:"none"}); _unbindEvents.call(this); }else if(o.axis==="yx" && d.overflowed[1]){ _scrollTo($this,to[1].toString(),{dir:"x",dur:0,overwrite:"none"}); } }else{ /* y scrolling is required */ _scrollTo($this,to[0].toString(),{dir:"y",dur:0,overwrite:"none"}); } } if(o.axis!=="y"){ /* x/yx axis */ if(!d.overflowed[1]){ /* x scrolling is not required */ _resetContentPosition.call(this); /* reset content position */ if(o.axis==="x"){ _scrollTo($this,"0",{dir:"x",dur:0,overwrite:"none"}); _unbindEvents.call(this); }else if(o.axis==="yx" && d.overflowed[0]){ _scrollTo($this,to[0].toString(),{dir:"y",dur:0,overwrite:"none"}); } }else{ /* x scrolling is required */ _scrollTo($this,to[1].toString(),{dir:"x",dur:0,overwrite:"none"}); } } /* no scroll class */ if(!d.overflowed[0] && !d.overflowed[1]){ $this.addClass(classes[2]); }else{ $this.removeClass(classes[2]); } _autoUpdate.call(this); /* initialize automatic updating (for dynamic content, fluid layouts etc.) */ } }); }, /* ---------------------------------------- */ /* plugin scrollTo method triggers a scrolling event to a specific value ---------------------------------------- usage: $(selector).mThumbnailScroller("scrollTo",value,options); */ scrollTo:function(val,options){ /* prevent silly things like $(selector).mThumbnailScroller("scrollTo",undefined); */ if(typeof val=="undefined" || val==null){return;} var selector=_selector.call(this); /* validate selector */ return $(selector).each(function(){ var $this=$(this); if($this.data(pluginPfx)){ /* check if plugin has initialized */ var d=$this.data(pluginPfx),o=d.opt, /* method default options */ methodDefaults={ trigger:"external", /* method is by default triggered externally (e.g. from other scripts) */ speed:o.speed, /* scrolling speed */ duration:1000, /* animation duration */ easing:"easeInOut", /* animation easing */ timeout:60, /* scroll-to delay */ callbacks:true, /* enable/disable callbacks */ onStart:true, onUpdate:true, onComplete:true }, methodOptions=$.extend(true,{},methodDefaults,options), to=_arr.call(this,val),dur=methodOptions.duration ? methodOptions.duration : 7000/(methodOptions.speed || 1); /* translate yx values to actual scroll-to positions */ to[0]=_to.call(this,to[0],"y"); to[1]=_to.call(this,to[1],"x"); methodOptions.dur=dur>0 && dur<17 ? 17 : dur; setTimeout(function(){ /* do the scrolling */ if(to[0]!==null && typeof to[0]!=="undefined" && o.axis!=="x" && d.overflowed[0]){ /* scroll y */ methodOptions.dir="y"; methodOptions.overwrite="all"; _scrollTo($this,-to[0].toString(),methodOptions); } if(to[1]!==null && typeof to[1]!=="undefined" && o.axis!=="y" && d.overflowed[1]){ /* scroll x */ methodOptions.dir="x"; methodOptions.overwrite="none"; _scrollTo($this,-to[1].toString(),methodOptions); } },methodOptions.timeout); } }); }, /* ---------------------------------------- */ /* plugin stop method stops scrolling animation ---------------------------------------- usage: $(selector).mThumbnailScroller("stop"); */ stop:function(){ var selector=_selector.call(this); /* validate selector */ return $(selector).each(function(){ var $this=$(this); if($this.data(pluginPfx)){ /* check if plugin has initialized */ _stop($this); } }); }, /* ---------------------------------------- */ /* plugin disable method temporarily disables the scroller ---------------------------------------- usage: $(selector).mThumbnailScroller("disable",reset); reset (boolean): resets content position to 0 */ disable:function(r){ var selector=_selector.call(this); /* validate selector */ return $(selector).each(function(){ var $this=$(this); if($this.data(pluginPfx)){ /* check if plugin has initialized */ var d=$this.data(pluginPfx),o=d.opt; _autoUpdate.call(this,"remove"); /* remove automatic updating */ _unbindEvents.call(this); /* unbind events */ if(r){_resetContentPosition.call(this);} /* reset content position */ _buttonsVisibility.call(this,true); /* show/hide buttons */ $this.addClass(classes[0]); /* add disable class */ } }); }, /* ---------------------------------------- */ /* plugin destroy method completely removes the scrollber and returns the element to its original state ---------------------------------------- usage: $(selector).mThumbnailScroller("destroy"); */ destroy:function(){ var selector=_selector.call(this); /* validate selector */ return $(selector).each(function(){ var $this=$(this); if($this.data(pluginPfx)){ /* check if plugin has initialized */ var d=$this.data(pluginPfx),o=d.opt, mTS_wrapper=$("#mTS_"+d.idx), mTS_container=$("#mTS_"+d.idx+"_container"), mTS_buttons=$("#mTS_"+d.idx+"_buttonUp,#mTS_"+d.idx+"_buttonDown,#mTS_"+d.idx+"_buttonLeft,#mTS_"+d.idx+"_buttonRight"); if(o.live){removeLiveTimers(selector);} /* remove live timer */ _autoUpdate.call(this,"remove"); /* remove automatic updating */ _unbindEvents.call(this); /* unbind events */ _resetContentPosition.call(this); /* reset content position */ $this.removeData(pluginPfx); /* remove plugin data object */ _delete(this,"mts"); /* delete callbacks object */ /* remove plugin markup */ mTS_buttons.remove(); /* remove buttons first (those can be either inside or outside plugin's inner wrapper) */ mTS_wrapper.replaceWith(d.html); /* replace plugin's inner wrapper with the original content */ /* remove plugin classes from the element and add destroy class */ $this.removeClass(pluginNS+" _"+pluginPfx+"_"+d.idx+" "+pluginPfx+"-"+o.theme+" "+classes[2]+" "+classes[0]).addClass(classes[1]); } }); } /* ---------------------------------------- */ }, /* ---------------------------------------- FUNCTIONS ---------------------------------------- */ /* validates selector (if selector is invalid or undefined uses the default one) */ _selector=function(){ return (typeof $(this)!=="object" || $(this).length<1) ? defaultSelector : this; }, /* -------------------- */ /* changes options according to theme */ _theme=function(obj){ var buttonsPlaceholderOutside=["buttons-out"], buttonsHtmlSet2=["buttons-in"], buttonsHtmlSet3=["buttons-out"], typeHover85=["hover-classic"], typeHoverPrecise=["hover-full"]; obj.markup.buttonsPlaceholder=$.inArray(obj.theme,buttonsPlaceholderOutside) > -1 ? "outside" : obj.markup.buttonsPlaceholder; obj.markup.buttonsHTML=$.inArray(obj.theme,buttonsHtmlSet2) > -1 ? {up:"SVG set 2",down:"SVG set 2",left:"SVG set 2",right:"SVG set 2"} : $.inArray(obj.theme,buttonsHtmlSet3) > -1 ? {up:"SVG set 3",down:"SVG set 3",left:"SVG set 3",right:"SVG set 3"} : obj.markup.buttonsHTML; obj.type=$.inArray(obj.theme,typeHover85) > -1 ? "hover-85" : $.inArray(obj.theme,typeHoverPrecise) > -1 ? "hover-precise" : obj.type; obj.speed=$.inArray(obj.theme,typeHover85) > -1 ? 60 : $.inArray(obj.theme,typeHoverPrecise) > -1 ? 10 : obj.speed; }, /* -------------------- */ /* live option timers removal */ removeLiveTimers=function(selector){ if(liveTimers[selector]){ clearTimeout(liveTimers[selector]); _delete(liveTimers,selector); } }, /* -------------------- */ /* generates plugin markup */ _pluginMarkup=function(){ var $this=$(this),d=$this.data(pluginPfx),o=d.opt, wrapperClass=o.axis==="yx" ? "mTS_vertical_horizontal" : o.axis==="x" ? "mTS_horizontal" : "mTS_vertical", thumbsContainer=o.markup.thumbnailsContainer || "ul", thumbContainer=o.markup.thumbnailContainer || "li", thumbElement=o.markup.thumbnailElement || "img"; d.html=$this.children().clone(true,true); /* store element original html to restore when destroy method is called */ if(!$this.find(thumbsContainer).length){ /* create images container automatically if not present */ var thumbsAutoContainer=$this.find("li").length ? "