PageSourceSearch

https://www.viola-group.com/wp-content/themes/viola/assets/lib/js/jquery.mCustomScrollbar.js

js viola-group.com collected 2026-09-24 19:37:50 UTC 92,951 bytes, 2,458 lines download raw bytes

vendor: 15,032 bytes, lines 1-469
1/*
2== malihu jquery custom scrollbar plugin == 
3Version: 3.1.5 
4Plugin URI: https://manos.malihu.gr/jquery-custom-content-scroller 
5Author: malihu
6Author URI: https://manos.malihu.gr
7License: MIT License (MIT)
8*/
9
10/*
11Copyright Manos Malihutsakis (email: [email protected])
12
13Permission is hereby granted, free of charge, to any person obtaining a copy
14of this software and associated documentation files (the "Software"), to deal
15in the Software without restriction, including without limitation the rights
16to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17copies of the Software, and to permit persons to whom the Software is
18furnished to do so, subject to the following conditions:
19
20The above copyright notice and this permission notice shall be included in
21all copies or substantial portions of the Software.
22
23THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29THE SOFTWARE.
30*/
31
32/*
33The code below is fairly long, fully commented and should be normally used in development. 
34For production, use either the minified jquery.mCustomScrollbar.min.js script or 
35the production-ready jquery.mCustomScrollbar.concat.min.js which contains the plugin 
36and dependencies (minified). 
37*/
38
39(function(factory){
40	if(typeof define==="function" && define.amd){
41		define(["jquery"],factory);
42	}else if(typeof module!=="undefined" && module.exports){
43		module.exports=factory;
44	}else{
45		factory(jQuery,window,document);
46	}
47}(function($){
48(function(init){
49	var _rjs=typeof define==="function" && define.amd, /* RequireJS */
50		_njs=typeof module !== "undefined" && module.exports, /* NodeJS */
51		_dlp=("https:"==document.location.protocol) ? "https:" : "http:", /* location protocol */
52		_url="cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.13/jquery.mousewheel.min.js";
53	if(!_rjs){
54		if(_njs){
55			require("jquery-mousewheel")($);
56		}else{
57			/* load jquery-mousewheel plugin (via CDN) if it's not present or not loaded via RequireJS 
58			(works when mCustomScrollbar fn is called on window load) */
59			$.event.special.mousewheel || $("head").append(decodeURI("%3Cscript src="+_dlp+"//"+_url+"%3E%3C/script%3E"));
60		}
61	}
62	init();
63}(function(){
64	
65	/* 
66	----------------------------------------
67	PLUGIN NAMESPACE, PREFIX, DEFAULT SELECTOR(S) 
68	----------------------------------------
69	*/
70	
71	var pluginNS="mCustomScrollbar",
72		pluginPfx="mCS",
73		defaultSelector=".mCustomScrollbar",
74	
75	
76		
77	
78	
79	/* 
80	----------------------------------------
81	DEFAULT OPTIONS 
82	----------------------------------------
83	*/
84	
85		defaults={
86			/*
87			set element/content width/height programmatically 
88			values: boolean, pixels, percentage 
89				option						default
90				-------------------------------------
91				setWidth					false
92				setHeight					false
93			*/
94			/*
95			set the initial css top property of content  
96			values: string (e.g. "-100px", "10%" etc.)
97			*/
98			setTop:0,
99			/*
100			set the initial css left property of content  
101			values: string (e.g. "-100px", "10%" etc.)
102			*/
103			setLeft:0,
104			/* 
105			scrollbar axis (vertical and/or horizontal scrollbars) 
106			values (string): "y", "x", "yx"
107			*/
108			axis:"y",
109			/*
110			position of scrollbar relative to content  
111			values (string): "inside", "outside" ("outside" requires elements with position:relative)
112			*/
113			scrollbarPosition:"inside",
114			/*
115			scrolling inertia
116			values: integer (milliseconds)
117			*/
118			scrollInertia:950,
119			/* 
120			auto-adjust scrollbar dragger length
121			values: boolean
122			*/
123			autoDraggerLength:true,
124			/*
125			auto-hide scrollbar when idle 
126			values: boolean
127				option						default
128				-------------------------------------
129				autoHideScrollbar			false
130			*/
131			/*
132			auto-expands scrollbar on mouse-over and dragging
133			values: boolean
134				option						default
135				-------------------------------------
136				autoExpandScrollbar			false
137			*/
138			/*
139			always show scrollbar, even when there's nothing to scroll 
140			values: integer (0=disable, 1=always show dragger rail and buttons, 2=always show dragger rail, dragger and buttons), boolean
141			*/
142			alwaysShowScrollbar:0,
143			/*
144			scrolling always snaps to a multiple of this number in pixels
145			values: integer, array ([y,x])
146				option						default
147				-------------------------------------
148				snapAmount					null
149			*/
150			/*
151			when snapping, snap with this number in pixels as an offset 
152			values: integer
153			*/
154			snapOffset:0,
155			/* 
156			mouse-wheel scrolling
157			*/
158			mouseWheel:{
159				/* 
160				enable mouse-wheel scrolling
161				values: boolean
162				*/
163				enable:true,
164				/* 
165				scrolling amount in pixels
166				values: "auto", integer 
167				*/
168				scrollAmount:"auto",
169				/* 
170				mouse-wheel scrolling axis 
171				the default scrolling direction when both vertical and horizontal scrollbars are present 
172				values (string): "y", "x" 
173				*/
174				axis:"y",
175				/* 
176				prevent the default behaviour which automatically scrolls the parent element(s) when end of scrolling is reached 
177				values: boolean
178					option						default
179					-------------------------------------
180					preventDefault				null
181				*/
182				/*
183				the reported mouse-wheel delta value. The number of lines (translated to pixels) one wheel notch scrolls.  
184				values: "auto", integer 
185				"auto" uses the default OS/browser value 
186				*/
187				deltaFactor:"auto",
188				/*
189				normalize mouse-wheel delta to -1 or 1 (disables mouse-wheel acceleration) 
190				values: boolean
191					option						default
192					-------------------------------------
193					normalizeDelta				null
194				*/
195				/*
196				invert mouse-wheel scrolling direction 
197				values: boolean
198					option						default
199					-------------------------------------
200					invert						null
201				*/
202				/*
203				the tags that disable mouse-wheel when cursor is over them
204				*/
205				disableOver:["select","option","keygen","datalist","textarea"]
206			},
207			/* 
208			scrollbar buttons
209			*/
210			scrollButtons:{ 
211				/*
212				enable scrollbar buttons
213				values: boolean
214					option						default
215					-------------------------------------
216					enable						null
217				*/
218				/*
219				scrollbar buttons scrolling type 
220				values (string): "stepless", "stepped"
221				*/
222				scrollType:"stepless",
223				/*
224				scrolling amount in pixels
225				values: "auto", integer 
226				*/
227				scrollAmount:"auto"
228				/*
229				tabindex of the scrollbar buttons
230				values: false, integer
231					option						default
232					-------------------------------------
233					tabindex					null
234				*/
235			},
236			/* 
237			keyboard scrolling
238			*/
239			keyboard:{ 
240				/*
241				enable scrolling via keyboard
242				values: boolean
243				*/
244				enable:true,
245				/*
246				keyboard scrolling type 
247				values (string): "stepless", "stepped"
248				*/
249				scrollType:"stepless",
250				/*
251				scrolling amount in pixels
252				values: "auto", integer 
253				*/
254				scrollAmount:"auto"
255			},
256			/*
257			enable content touch-swipe scrolling 
258			values: boolean, integer, string (number)
259			integer values define the axis-specific minimum amount required for scrolling momentum
260			*/
261			contentTouchScroll:25,
262			/*
263			enable/disable document (default) touch-swipe scrolling 
264			*/
265			documentTouchScroll:true,
266			/*
267			advanced option parameters
268			*/
269			advanced:{
270				/*
271				auto-expand content horizontally (for "x" or "yx" axis) 
272				values: boolean, integer (the value 2 forces the non scrollHeight/scrollWidth method, the value 3 forces the scrollHeight/scrollWidth method)
273					option						default
274					-------------------------------------
275					autoExpandHorizontalScroll	null
276				*/
277				/*
278				auto-scroll to elements with focus
279				*/
280				autoScrollOnFocus:"input,textarea,select,button,datalist,keygen,a[tabindex],area,object,[contenteditable='true']",
281				/*
282				auto-update scrollbars on content, element or viewport resize 
283				should be true for fluid layouts/elements, adding/removing content dynamically, hiding/showing elements, content with images etc. 
284				values: boolean
285				*/
286				updateOnContentResize:true,
287				/*
288				auto-update scrollbars each time each image inside the element is fully loaded 
289				values: "auto", boolean
290				*/
291				updateOnImageLoad:"auto",
292				/*
293				auto-update scrollbars based on the amount and size changes of specific selectors 
294				useful when you need to update the scrollbar(s) automatically, each time a type of element is added, removed or changes its size 
295				values: boolean, string (e.g. "ul li" will auto-update scrollbars each time list-items inside the element are changed) 
296				a value of true (boolean) will auto-update scrollbars each time any element is changed
297					option						default
298					-------------------------------------
299					updateOnSelectorChange		null
300				*/
301				/*
302				extra selectors that'll allow scrollbar dragging upon mousemove/up, pointermove/up, touchend etc. (e.g. "selector-1, selector-2")
303					option						default
304					-------------------------------------
305					extraDraggableSelectors		null
306				*/
307				/*
308				extra selectors that'll release scrollbar dragging upon mouseup, pointerup, touchend etc. (e.g. "selector-1, selector-2")
309					option						default
310					-------------------------------------
311					releaseDraggableSelectors	null
312				*/
313				/*
314				auto-update timeout 
315				values: integer (milliseconds)
316				*/
317				autoUpdateTimeout:60
318			},
319			/* 
320			scrollbar theme 
321			values: string (see CSS/plugin URI for a list of ready-to-use themes)
322			*/
323			theme:"light",
324			/*
325			user defined callback functions
326			*/
327			callbacks:{
328				/*
329				Available callbacks: 
330					callback					default
331					-------------------------------------
332					onCreate					null
333					onInit						null
334					onScrollStart				null
335					onScroll					null
336					onTotalScroll				null
337					onTotalScrollBack			null
338					whileScrolling				null
339					onOverflowY					null
340					onOverflowX					null
341					onOverflowYNone				null
342					onOverflowXNone				null
343					onImageLoad					null
344					onSelectorChange			null
345					onBeforeUpdate				null
346					onUpdate					null
347				*/
348				onTotalScrollOffset:0,
349				onTotalScrollBackOffset:0,
350				alwaysTriggerOffsets:true
351			}
352			/*
353			add scrollbar(s) on all elements matching the current selector, now and in the future 
354			values: boolean, string 
355			string values: "on" (enable), "once" (disable after first invocation), "off" (disable)
356			liveSelector values: string (selector)
357				option						default
358				-------------------------------------
359				live						false
360				liveSelector				null
361			*/
362		},
363	
364	
365	
366	
367	
368	/* 
369	----------------------------------------
370	VARS, CONSTANTS 
371	----------------------------------------
372	*/
373	
374		totalInstances=0, /* plugin instances amount */
375		liveTimers={}, /* live option timers */
376		oldIE=(window.attachEvent && !window.addEventListener) ? 1 : 0, /* detect IE < 9 */
377		touchActive=false,touchable, /* global touch vars (for touch and pointer events) */
378		/* general plugin classes */
379		classes=[
380			"mCSB_dragger_onDrag","mCSB_scrollTools_onDrag","mCS_img_loaded","mCS_disabled","mCS_destroyed","mCS_no_scrollbar",
381			"mCS-autoHide","mCS-dir-rtl","mCS_no_scrollbar_y","mCS_no_scrollbar_x","mCS_y_hidden","mCS_x_hidden","mCSB_draggerContainer",
382			"mCSB_buttonUp","mCSB_buttonDown","mCSB_buttonLeft","mCSB_buttonRight"
383		],
384		
385	
386	
387	
388	
389	/* 
390	----------------------------------------
391	METHODS 
392	----------------------------------------
393	*/
394	
395		methods={
396			
397			/* 
398			plugin initialization method 
399			creates the scrollbar(s), plugin data object and options
400			----------------------------------------
401			*/
402			
403			init:function(options){
404				
405				var options=$.extend(true,{},defaults,options),
406					selector=_selector.call(this); /* validate selector */
407				
408				/* 
409				if live option is enabled, monitor for elements matching the current selector and 
410				apply scrollbar(s) when found (now and in the future) 
411				*/
412				if(options.live){
413					var liveSelector=options.liveSelector || this.selector || defaultSelector, /* live selector(s) */
414						$liveSelector=$(liveSelector); /* live selector(s) as jquery object */
415					if(options.live==="off"){
416						/* 
417						disable live if requested 
418						usage: $(selector).mCustomScrollbar({live:"off"}); 
419						*/
420						removeLiveTimers(liveSelector);
421						return;
422					}
423					liveTimers[liveSelector]=setTimeout(function(){
424						/* call mCustomScrollbar fn on live selector(s) every half-second */
425						$liveSelector.mCustomScrollbar(options);
426						if(options.live==="once" && $liveSelector.length){
427							/* disable live after first invocation */
428							removeLiveTimers(liveSelector);
429						}
430					},500);
431				}else{
432					removeLiveTimers(liveSelector);
433				}
434				
435				/* options backward compatibility (for versions < 3.0.0) and normalization */
436				options.setWidth=(options.set_width) ? options.set_width : options.setWidth;
437				options.setHeight=(options.set_height) ? options.set_height : options.setHeight;
438				options.axis=(options.horizontalScroll) ? "x" : _findAxis(options.axis);
439				options.scrollInertia=options.scrollInertia>0 && options.scrollInertia<17 ? 17 : options.scrollInertia;
440				if(typeof options.mouseWheel!=="object" &&  options.mouseWheel==true){ /* old school mouseWheel option (non-object) */
441					options.mouseWheel={enable:true,scrollAmount:"auto",axis:"y",preventDefault:false,deltaFactor:"auto",normalizeDelta:false,invert:false}
442				}
443				options.mouseWheel.scrollAmount=!options.mouseWheelPixels ? options.mouseWheel.scrollAmount : options.mouseWheelPixels;
444				options.mouseWheel.normalizeDelta=!options.advanced.normalizeMouseWheelDelta ? options.mouseWheel.normalizeDelta : options.advanced.normalizeMouseWheelDelta;
445				options.scrollButtons.scrollType=_findScrollButtonsType(options.scrollButtons.scrollType); 
446				
447				_theme(options); /* theme-specific options */
448				
449				/* plugin constructor */
450				return $(selector).each(function(){
451					
452					var $this=$(this);
453					
454					if(!$this.data(pluginPfx)){ /* prevent multiple instantiations */
455					
456						/* store options and create objects in jquery data */
457						$this.data(pluginPfx,{
458							idx:++totalInstances, /* instance index */
459							opt:options, /* options */
460							scrollRatio:{y:null,x:null}, /* scrollbar to content ratio */
461							overflowed:null, /* overflowed axis */
462							contentReset:{y:null,x:null}, /* object to check when content resets */
463							bindEvents:false, /* object to check if events are bound */
464							tweenRunning:false, /* object to check if tween is running */
465							sequential:{}, /* sequential scrolling object */
466							langDir:$this.css("direction"), /* detect/store direction (ltr or rtl) */
467							cbOffsets:null, /* object to check whether callback offsets always trigger */
468							/* 
469							object to check how scrolling events where last triggered 
470							"internal" (default - triggered by this script), "external" (triggered by other scripts, e.g. via scrollTo method) 
471							usage: object.data("mCS").trigger
472							*/
473							trigger:null,
474							/* 
475							object to check for changes in elements in order to call the update method automatically 
476							*/
477							poll:{size:{o:0,n:0},img:{o:0,n:0},change:{o:0,n:0}}
478						});
479						
480						var d=$this.data(pluginPfx),o=d.opt,
481							/* HTML data attributes */
482							htmlDataAxis=$this.data("mcs-axis"),htmlDataSbPos=$this.data("mcs-scrollbar-position"),htmlDataTheme=$this.data("mcs-theme");
483						 
484						if(htmlDataAxis){o.axis=htmlDataAxis;} /* usage example: data-mcs-axis="y" */
485						if(htmlDataSbPos){o.scrollbarPosition=htmlDataSbPos;} /* usage example: data-mcs-scrollbar-position="outside" */
486						if(htmlDataTheme){ /* usage example: data-mcs-theme="minimal" */
487							o.theme=htmlDataTheme;
488							_theme(o); /* theme-specific options */
489						}
490						
491						_pluginMarkup.call(this); /* add plugin markup */
492						
493						if(d && o.callbacks.onCreate && typeof o.callbacks.onCreate==="function"){o.callbacks.onCreate.call(this);} /* callbacks: onCreate */
494						
495						$("#mCSB_"+d.idx+"_container img:not(."+classes[2]+")").addClass(classe
vendor: 76,658 bytes, lines 495-2458
495s[2]); /* flag loaded images */
496						
497						methods.update.call(null,$this); /* call the update method */
498					
499					}
500					
501				});
502				
503			},
504			/* ---------------------------------------- */
505			
506			
507			
508			/* 
509			plugin update method 
510			updates content and scrollbar(s) values, events and status 
511			----------------------------------------
512			usage: $(selector).mCustomScrollbar("update");
513			*/
514			
515			update:function(el,cb){
516				
517				var selector=el || _selector.call(this); /* validate selector */
518				
519				return $(selector).each(function(){
520					
521					var $this=$(this);
522					
523					if($this.data(pluginPfx)){ /* check if plugin has initialized */
524						
525						var d=$this.data(pluginPfx),o=d.opt,
526							mCSB_container=$("#mCSB_"+d.idx+"_container"),
527							mCustomScrollBox=$("#mCSB_"+d.idx),
528							mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")];
529						
530						if(!mCSB_container.length){return;}
531						
532						if(d.tweenRunning){_stop($this);} /* stop any running tweens while updating */
533						
534						if(cb && d && o.callbacks.onBeforeUpdate && typeof o.callbacks.onBeforeUpdate==="function"){o.callbacks.onBeforeUpdate.call(this);} /* callbacks: onBeforeUpdate */
535						
536						/* if element was disabled or destroyed, remove class(es) */
537						if($this.hasClass(classes[3])){$this.removeClass(classes[3]);}
538						if($this.hasClass(classes[4])){$this.removeClass(classes[4]);}
539						
540						/* css flexbox fix, detect/set max-height */
541						mCustomScrollBox.css("max-height","none");
542						if(mCustomScrollBox.height()!==$this.height()){mCustomScrollBox.css("max-height",$this.height());}
543						
544						_expandContentHorizontally.call(this); /* expand content horizontally */
545						
546						if(o.axis!=="y" && !o.advanced.autoExpandHorizontalScroll){
547							mCSB_container.css("width",_contentWidth(mCSB_container));
548						}
549						
550						d.overflowed=_overflowed.call(this); /* determine if scrolling is required */
551						
552						_scrollbarVisibility.call(this); /* show/hide scrollbar(s) */
553						
554						/* auto-adjust scrollbar dragger length analogous to content */
555						if(o.autoDraggerLength){_setDraggerLength.call(this);}
556						
557						_scrollRatio.call(this); /* calculate and store scrollbar to content ratio */
558						
559						_bindEvents.call(this); /* bind scrollbar events */
560						
561						/* reset scrolling position and/or events */
562						var to=[Math.abs(mCSB_container[0].offsetTop),Math.abs(mCSB_container[0].offsetLeft)];
563						if(o.axis!=="x"){ /* y/yx axis */
564							if(!d.overflowed[0]){ /* y scrolling is not required */
565								_resetContentPosition.call(this); /* reset content position */
566								if(o.axis==="y"){
567									_unbindEvents.call(this);
568								}else if(o.axis==="yx" && d.overflowed[1]){
569									_scrollTo($this,to[1].toString(),{dir:"x",dur:0,overwrite:"none"});
570								}
571							}else if(mCSB_dragger[0].height()>mCSB_dragger[0].parent().height()){
572								_resetContentPosition.call(this); /* reset content position */
573							}else{ /* y scrolling is required */
574								_scrollTo($this,to[0].toString(),{dir:"y",dur:0,overwrite:"none"});
575								d.contentReset.y=null;
576							}
577						}
578						if(o.axis!=="y"){ /* x/yx axis */
579							if(!d.overflowed[1]){ /* x scrolling is not required */
580								_resetContentPosition.call(this); /* reset content position */
581								if(o.axis==="x"){
582									_unbindEvents.call(this);
583								}else if(o.axis==="yx" && d.overflowed[0]){
584									_scrollTo($this,to[0].toString(),{dir:"y",dur:0,overwrite:"none"});
585								}
586							}else if(mCSB_dragger[1].width()>mCSB_dragger[1].parent().width()){
587								_resetContentPosition.call(this); /* reset content position */
588							}else{ /* x scrolling is required */
589								_scrollTo($this,to[1].toString(),{dir:"x",dur:0,overwrite:"none"});
590								d.contentReset.x=null;
591							}
592						}
593						
594						/* callbacks: onImageLoad, onSelectorChange, onUpdate */
595						if(cb && d){
596							if(cb===2 && o.callbacks.onImageLoad && typeof o.callbacks.onImageLoad==="function"){
597								o.callbacks.onImageLoad.call(this);
598							}else if(cb===3 && o.callbacks.onSelectorChange && typeof o.callbacks.onSelectorChange==="function"){
599								o.callbacks.onSelectorChange.call(this);
600							}else if(o.callbacks.onUpdate && typeof o.callbacks.onUpdate==="function"){
601								o.callbacks.onUpdate.call(this);
602							}
603						}
604						
605						_autoUpdate.call(this); /* initialize automatic updating (for dynamic content, fluid layouts etc.) */
606						
607					}
608					
609				});
610				
611			},
612			/* ---------------------------------------- */
613			
614			
615			
616			/* 
617			plugin scrollTo method 
618			triggers a scrolling event to a specific value
619			----------------------------------------
620			usage: $(selector).mCustomScrollbar("scrollTo",value,options);
621			*/
622		
623			scrollTo:function(val,options){
624				
625				/* prevent silly things like $(selector).mCustomScrollbar("scrollTo",undefined); */
626				if(typeof val=="undefined" || val==null){return;}
627				
628				var selector=_selector.call(this); /* validate selector */
629				
630				return $(selector).each(function(){
631					
632					var $this=$(this);
633					
634					if($this.data(pluginPfx)){ /* check if plugin has initialized */
635					
636						var d=$this.data(pluginPfx),o=d.opt,
637							/* method default options */
638							methodDefaults={
639								trigger:"external", /* method is by default triggered externally (e.g. from other scripts) */
640								scrollInertia:o.scrollInertia, /* scrolling inertia (animation duration) */
641								scrollEasing:"mcsEaseInOut", /* animation easing */
642								moveDragger:false, /* move dragger instead of content */
643								timeout:60, /* scroll-to delay */
644								callbacks:true, /* enable/disable callbacks */
645								onStart:true,
646								onUpdate:true,
647								onComplete:true
648							},
649							methodOptions=$.extend(true,{},methodDefaults,options),
650							to=_arr.call(this,val),dur=methodOptions.scrollInertia>0 && methodOptions.scrollInertia<17 ? 17 : methodOptions.scrollInertia;
651						
652						/* translate yx values to actual scroll-to positions */
653						to[0]=_to.call(this,to[0],"y");
654						to[1]=_to.call(this,to[1],"x");
655						
656						/* 
657						check if scroll-to value moves the dragger instead of content. 
658						Only pixel values apply on dragger (e.g. 100, "100px", "-=100" etc.) 
659						*/
660						if(methodOptions.moveDragger){
661							to[0]*=d.scrollRatio.y;
662							to[1]*=d.scrollRatio.x;
663						}
664						
665						methodOptions.dur=_isTabHidden() ? 0 : dur; //skip animations if browser tab is hidden
666						
667						setTimeout(function(){ 
668							/* do the scrolling */
669							if(to[0]!==null && typeof to[0]!=="undefined" && o.axis!=="x" && d.overflowed[0]){ /* scroll y */
670								methodOptions.dir="y";
671								methodOptions.overwrite="all";
672								_scrollTo($this,to[0].toString(),methodOptions);
673							}
674							if(to[1]!==null && typeof to[1]!=="undefined" && o.axis!=="y" && d.overflowed[1]){ /* scroll x */
675								methodOptions.dir="x";
676								methodOptions.overwrite="none";
677								_scrollTo($this,to[1].toString(),methodOptions);
678							}
679						},methodOptions.timeout);
680						
681					}
682					
683				});
684				
685			},
686			/* ---------------------------------------- */
687			
688			
689			
690			/*
691			plugin stop method 
692			stops scrolling animation
693			----------------------------------------
694			usage: $(selector).mCustomScrollbar("stop");
695			*/
696			stop:function(){
697				
698				var selector=_selector.call(this); /* validate selector */
699				
700				return $(selector).each(function(){
701					
702					var $this=$(this);
703					
704					if($this.data(pluginPfx)){ /* check if plugin has initialized */
705										
706						_stop($this);
707					
708					}
709					
710				});
711				
712			},
713			/* ---------------------------------------- */
714			
715			
716			
717			/*
718			plugin disable method 
719			temporarily disables the scrollbar(s) 
720			----------------------------------------
721			usage: $(selector).mCustomScrollbar("disable",reset); 
722			reset (boolean): resets content position to 0 
723			*/
724			disable:function(r){
725				
726				var selector=_selector.call(this); /* validate selector */
727				
728				return $(selector).each(function(){
729					
730					var $this=$(this);
731					
732					if($this.data(pluginPfx)){ /* check if plugin has initialized */
733						
734						var d=$this.data(pluginPfx);
735						
736						_autoUpdate.call(this,"remove"); /* remove automatic updating */
737						
738						_unbindEvents.call(this); /* unbind events */
739						
740						if(r){_resetContentPosition.call(this);} /* reset content position */
741						
742						_scrollbarVisibility.call(this,true); /* show/hide scrollbar(s) */
743						
744						$this.addClass(classes[3]); /* add disable class */
745					
746					}
747					
748				});
749				
750			},
751			/* ---------------------------------------- */
752			
753			
754			
755			/*
756			plugin destroy method 
757			completely removes the scrollbar(s) and returns the element to its original state
758			----------------------------------------
759			usage: $(selector).mCustomScrollbar("destroy"); 
760			*/
761			destroy:function(){
762				
763				var selector=_selector.call(this); /* validate selector */
764				
765				return $(selector).each(function(){
766					
767					var $this=$(this);
768					
769					if($this.data(pluginPfx)){ /* check if plugin has initialized */
770					
771						var d=$this.data(pluginPfx),o=d.opt,
772							mCustomScrollBox=$("#mCSB_"+d.idx),
773							mCSB_container=$("#mCSB_"+d.idx+"_container"),
774							scrollbar=$(".mCSB_"+d.idx+"_scrollbar");
775					
776						if(o.live){removeLiveTimers(o.liveSelector || $(selector).selector);} /* remove live timers */
777						
778						_autoUpdate.call(this,"remove"); /* remove automatic updating */
779						
780						_unbindEvents.call(this); /* unbind events */
781						
782						_resetContentPosition.call(this); /* reset content position */
783						
784						$this.removeData(pluginPfx); /* remove plugin data object */
785						
786						_delete(this,"mcs"); /* delete callbacks object */
787						
788						/* remove plugin markup */
789						scrollbar.remove(); /* remove scrollbar(s) first (those can be either inside or outside plugin's inner wrapper) */
790						mCSB_container.find("img."+classes[2]).removeClass(classes[2]); /* remove loaded images flag */
791						mCustomScrollBox.replaceWith(mCSB_container.contents()); /* replace plugin's inner wrapper with the original content */
792						/* remove plugin classes from the element and add destroy class */
793						$this.removeClass(pluginNS+" _"+pluginPfx+"_"+d.idx+" "+classes[6]+" "+classes[7]+" "+classes[5]+" "+classes[3]).addClass(classes[4]);
794					
795					}
796					
797				});
798				
799			}
800			/* ---------------------------------------- */
801			
802		},
803	
804	
805	
806	
807		
808	/* 
809	----------------------------------------
810	FUNCTIONS
811	----------------------------------------
812	*/
813	
814		/* validates selector (if selector is invalid or undefined uses the default one) */
815		_selector=function(){
816			return (typeof $(this)!=="object" || $(this).length<1) ? defaultSelector : this;
817		},
818		/* -------------------- */
819		
820		
821		/* changes options according to theme */
822		_theme=function(obj){
823			var fixedSizeScrollbarThemes=["rounded","rounded-dark","rounded-dots","rounded-dots-dark"],
824				nonExpandedScrollbarThemes=["rounded-dots","rounded-dots-dark","3d","3d-dark","3d-thick","3d-thick-dark","inset","inset-dark","inset-2","inset-2-dark","inset-3","inset-3-dark"],
825				disabledScrollButtonsThemes=["minimal","minimal-dark"],
826				enabledAutoHideScrollbarThemes=["minimal","minimal-dark"],
827				scrollbarPositionOutsideThemes=["minimal","minimal-dark"];
828			obj.autoDraggerLength=$.inArray(obj.theme,fixedSizeScrollbarThemes) > -1 ? false : obj.autoDraggerLength;
829			obj.autoExpandScrollbar=$.inArray(obj.theme,nonExpandedScrollbarThemes) > -1 ? false : obj.autoExpandScrollbar;
830			obj.scrollButtons.enable=$.inArray(obj.theme,disabledScrollButtonsThemes) > -1 ? false : obj.scrollButtons.enable;
831			obj.autoHideScrollbar=$.inArray(obj.theme,enabledAutoHideScrollbarThemes) > -1 ? true : obj.autoHideScrollbar;
832			obj.scrollbarPosition=$.inArray(obj.theme,scrollbarPositionOutsideThemes) > -1 ? "outside" : obj.scrollbarPosition;
833		},
834		/* -------------------- */
835		
836		
837		/* live option timers removal */
838		removeLiveTimers=function(selector){
839			if(liveTimers[selector]){
840				clearTimeout(liveTimers[selector]);
841				_delete(liveTimers,selector);
842			}
843		},
844		/* -------------------- */
845		
846		
847		/* normalizes axis option to valid values: "y", "x", "yx" */
848		_findAxis=function(val){
849			return (val==="yx" || val==="xy" || val==="auto") ? "yx" : (val==="x" || val==="horizontal") ? "x" : "y";
850		},
851		/* -------------------- */
852		
853		
854		/* normalizes scrollButtons.scrollType option to valid values: "stepless", "stepped" */
855		_findScrollButtonsType=function(val){
856			return (val==="stepped" || val==="pixels" || val==="step" || val==="click") ? "stepped" : "stepless";
857		},
858		/* -------------------- */
859		
860		
861		/* generates plugin markup */
862		_pluginMarkup=function(){
863			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
864				expandClass=o.autoExpandScrollbar ? " "+classes[1]+"_expand" : "",
865				scrollbar=["<div id='mCSB_"+d.idx+"_scrollbar_vertical' class='mCSB_scrollTools mCSB_"+d.idx+"_scrollbar mCS-"+o.theme+" mCSB_scrollTools_vertical"+expandClass+"'><div class='"+classes[12]+"'><div id='mCSB_"+d.idx+"_dragger_vertical' class='mCSB_dragger' style='position:absolute;'><div class='mCSB_dragger_bar' /></div><div class='mCSB_draggerRail' /></div></div>","<div id='mCSB_"+d.idx+"_scrollbar_horizontal' class='mCSB_scrollTools mCSB_"+d.idx+"_scrollbar mCS-"+o.theme+" mCSB_scrollTools_horizontal"+expandClass+"'><div class='"+classes[12]+"'><div id='mCSB_"+d.idx+"_dragger_horizontal' class='mCSB_dragger' style='position:absolute;'><div class='mCSB_dragger_bar' /></div><div class='mCSB_draggerRail' /></div></div>"],
866				wrapperClass=o.axis==="yx" ? "mCSB_vertical_horizontal" : o.axis==="x" ? "mCSB_horizontal" : "mCSB_vertical",
867				scrollbars=o.axis==="yx" ? scrollbar[0]+scrollbar[1] : o.axis==="x" ? scrollbar[1] : scrollbar[0],
868				contentWrapper=o.axis==="yx" ? "<div id='mCSB_"+d.idx+"_container_wrapper' class='mCSB_container_wrapper' />" : "",
869				autoHideClass=o.autoHideScrollbar ? " "+classes[6] : "",
870				scrollbarDirClass=(o.axis!=="x" && d.langDir==="rtl") ? " "+classes[7] : "";
871			if(o.setWidth){$this.css("width",o.setWidth);} /* set element width */
872			if(o.setHeight){$this.css("height",o.setHeight);} /* set element height */
873			o.setLeft=(o.axis!=="y" && d.langDir==="rtl") ? "989999px" : o.setLeft; /* adjust left position for rtl direction */
874			$this.addClass(pluginNS+" _"+pluginPfx+"_"+d.idx+autoHideClass+scrollbarDirClass).wrapInner("<div id='mCSB_"+d.idx+"' class='mCustomScrollBox mCS-"+o.theme+" "+wrapperClass+"'><div id='mCSB_"+d.idx+"_container' class='mCSB_container' style='position:relative; top:"+o.setTop+"; left:"+o.setLeft+";' dir='"+d.langDir+"' /></div>");
875			var mCustomScrollBox=$("#mCSB_"+d.idx),
876				mCSB_container=$("#mCSB_"+d.idx+"_container");
877			if(o.axis!=="y" && !o.advanced.autoExpandHorizontalScroll){
878				mCSB_container.css("width",_contentWidth(mCSB_container));
879			}
880			if(o.scrollbarPosition==="outside"){
881				if($this.css("position")==="static"){ /* requires elements with non-static position */
882					$this.css("position","relative");
883				}
884				$this.css("overflow","visible");
885				mCustomScrollBox.addClass("mCSB_outside").after(scrollbars);
886			}else{
887				mCustomScrollBox.addClass("mCSB_inside").append(scrollbars);
888				mCSB_container.wrap(contentWrapper);
889			}
890			_scrollButtons.call(this); /* add scrollbar buttons */
891			/* minimum dragger length */
892			var mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")];
893			mCSB_dragger[0].css("min-height",mCSB_dragger[0].height());
894			mCSB_dragger[1].css("min-width",mCSB_dragger[1].width());
895		},
896		/* -------------------- */
897		
898		
899		/* calculates content width */
900		_contentWidth=function(el){
901			var val=[el[0].scrollWidth,Math.max.apply(Math,el.children().map(function(){return $(this).outerWidth(true);}).get())],w=el.parent().width();
902			return val[0]>w ? val[0] : val[1]>w ? val[1] : "100%";
903		},
904		/* -------------------- */
905		
906		
907		/* expands content horizontally */
908		_expandContentHorizontally=function(){
909			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
910				mCSB_container=$("#mCSB_"+d.idx+"_container");
911			if(o.advanced.autoExpandHorizontalScroll && o.axis!=="y"){
912				/* calculate scrollWidth */
913				mCSB_container.css({"width":"auto","min-width":0,"overflow-x":"scroll"});
914				var w=Math.ceil(mCSB_container[0].scrollWidth);
915				if(o.advanced.autoExpandHorizontalScroll===3 || (o.advanced.autoExpandHorizontalScroll!==2 && w>mCSB_container.parent().width())){
916					mCSB_container.css({"width":w,"min-width":"100%","overflow-x":"inherit"});
917				}else{
918					/* 
919					wrap content with an infinite width div and set its position to absolute and width to auto. 
920					Setting width to auto before calculating the actual width is important! 
921					We must let the browser set the width as browser zoom values are impossible to calculate.
922					*/
923					mCSB_container.css({"overflow-x":"inherit","position":"absolute"})
924						.wrap("<div class='mCSB_h_wrapper' style='position:relative; left:0; width:999999px;' />")
925						.css({ /* set actual width, original position and un-wrap */
926							/* 
927							get the exact width (with decimals) and then round-up. 
928							Using jquery outerWidth() will round the width value which will mess up with inner elements that have non-integer width
929							*/
930							"width":(Math.ceil(mCSB_container[0].getBoundingClientRect().right+0.4)-Math.floor(mCSB_container[0].getBoundingClientRect().left)),
931							"min-width":"100%",
932							"position":"relative"
933						}).unwrap();
934				}
935			}
936		},
937		/* -------------------- */
938		
939		
940		/* adds scrollbar buttons */
941		_scrollButtons=function(){
942			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
943				mCSB_scrollTools=$(".mCSB_"+d.idx+"_scrollbar:first"),
944				tabindex=!_isNumeric(o.scrollButtons.tabindex) ? "" : "tabindex='"+o.scrollButtons.tabindex+"'",
945				btnHTML=[
946					"<a href='#' class='"+classes[13]+"' "+tabindex+" />",
947					"<a href='#' class='"+classes[14]+"' "+tabindex+" />",
948					"<a href='#' class='"+classes[15]+"' "+tabindex+" />",
949					"<a href='#' class='"+classes[16]+"' "+tabindex+" />"
950				],
951				btn=[(o.axis==="x" ? btnHTML[2] : btnHTML[0]),(o.axis==="x" ? btnHTML[3] : btnHTML[1]),btnHTML[2],btnHTML[3]];
952			if(o.scrollButtons.enable){
953				mCSB_scrollTools.prepend(btn[0]).append(btn[1]).next(".mCSB_scrollTools").prepend(btn[2]).append(btn[3]);
954			}
955		},
956		/* -------------------- */
957		
958		
959		/* auto-adjusts scrollbar dragger length */
960		_setDraggerLength=function(){
961			var $this=$(this),d=$this.data(pluginPfx),
962				mCustomScrollBox=$("#mCSB_"+d.idx),
963				mCSB_container=$("#mCSB_"+d.idx+"_container"),
964				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
965				ratio=[mCustomScrollBox.height()/mCSB_container.outerHeight(false),mCustomScrollBox.width()/mCSB_container.outerWidth(false)],
966				l=[
967					parseInt(mCSB_dragger[0].css("min-height")),Math.round(ratio[0]*mCSB_dragger[0].parent().height()),
968					parseInt(mCSB_dragger[1].css("min-width")),Math.round(ratio[1]*mCSB_dragger[1].parent().width())
969				],
970				h=oldIE && (l[1]<l[0]) ? l[0] : l[1],w=oldIE && (l[3]<l[2]) ? l[2] : l[3];
971			mCSB_dragger[0].css({
972				"height":h,"max-height":(mCSB_dragger[0].parent().height()-10)
973			}).find(".mCSB_dragger_bar").css({"line-height":l[0]+"px"});
974			mCSB_dragger[1].css({
975				"width":w,"max-width":(mCSB_dragger[1].parent().width()-10)
976			});
977		},
978		/* -------------------- */
979		
980		
981		/* calculates scrollbar to content ratio */
982		_scrollRatio=function(){
983			var $this=$(this),d=$this.data(pluginPfx),
984				mCustomScrollBox=$("#mCSB_"+d.idx),
985				mCSB_container=$("#mCSB_"+d.idx+"_container"),
986				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
987				scrollAmount=[mCSB_container.outerHeight(false)-mCustomScrollBox.height(),mCSB_container.outerWidth(false)-mCustomScrollBox.width()],
988				ratio=[
989					scrollAmount[0]/(mCSB_dragger[0].parent().height()-mCSB_dragger[0].height()),
990					scrollAmount[1]/(mCSB_dragger[1].parent().width()-mCSB_dragger[1].width())
991				];
992			d.scrollRatio={y:ratio[0],x:ratio[1]};
993		},
994		/* -------------------- */
995		
996		
997		/* toggles scrolling classes */
998		_onDragClasses=function(el,action,xpnd){
999			var expandClass=xpnd ? classes[0]+"_expanded" : "",
1000				scrollbar=el.closest(".mCSB_scrollTools");
1001			if(action==="active"){
1002				el.toggleClass(classes[0]+" "+expandClass); scrollbar.toggleClass(classes[1]); 
1003				el[0]._draggable=el[0]._draggable ? 0 : 1;
1004			}else{
1005				if(!el[0]._draggable){
1006					if(action==="hide"){
1007						el.removeClass(classes[0]); scrollbar.removeClass(classes[1]);
1008					}else{
1009						el.addClass(classes[0]); scrollbar.addClass(classes[1]);
1010					}
1011				}
1012			}
1013		},
1014		/* -------------------- */
1015		
1016		
1017		/* checks if content overflows its container to determine if scrolling is required */
1018		_overflowed=function(){
1019			var $this=$(this),d=$this.data(pluginPfx),
1020				mCustomScrollBox=$("#mCSB_"+d.idx),
1021				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1022				contentHeight=d.overflowed==null ? mCSB_container.height() : mCSB_container.outerHeight(false),
1023				contentWidth=d.overflowed==null ? mCSB_container.width() : mCSB_container.outerWidth(false),
1024				h=mCSB_container[0].scrollHeight,w=mCSB_container[0].scrollWidth;
1025			if(h>contentHeight){contentHeight=h;}
1026			if(w>contentWidth){contentWidth=w;}
1027			return [contentHeight>mCustomScrollBox.height(),contentWidth>mCustomScrollBox.width()];
1028		},
1029		/* -------------------- */
1030		
1031		
1032		/* resets content position to 0 */
1033		_resetContentPosition=function(){
1034			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1035				mCustomScrollBox=$("#mCSB_"+d.idx),
1036				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1037				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")];
1038			_stop($this); /* stop any current scrolling before resetting */
1039			if((o.axis!=="x" && !d.overflowed[0]) || (o.axis==="y" && d.overflowed[0])){ /* reset y */
1040				mCSB_dragger[0].add(mCSB_container).css("top",0);
1041				_scrollTo($this,"_resetY");
1042			}
1043			if((o.axis!=="y" && !d.overflowed[1]) || (o.axis==="x" && d.overflowed[1])){ /* reset x */
1044				var cx=dx=0;
1045				if(d.langDir==="rtl"){ /* adjust left position for rtl direction */
1046					cx=mCustomScrollBox.width()-mCSB_container.outerWidth(false);
1047					dx=Math.abs(cx/d.scrollRatio.x);
1048				}
1049				mCSB_container.css("left",cx);
1050				mCSB_dragger[1].css("left",dx);
1051				_scrollTo($this,"_resetX");
1052			}
1053		},
1054		/* -------------------- */
1055		
1056		
1057		/* binds scrollbar events */
1058		_bindEvents=function(){
1059			var $this=$(this),d=$this.data(pluginPfx),o=d.opt;
1060			if(!d.bindEvents){ /* check if events are already bound */
1061				_draggable.call(this);
1062				if(o.contentTouchScroll){_contentDraggable.call(this);}
1063				_selectable.call(this);
1064				if(o.mouseWheel.enable){ /* bind mousewheel fn when plugin is available */
1065					function _mwt(){
1066						mousewheelTimeout=setTimeout(function(){
1067							if(!$.event.special.mousewheel){
1068								_mwt();
1069							}else{
1070								clearTimeout(mousewheelTimeout);
1071								_mousewheel.call($this[0]);
1072							}
1073						},100);
1074					}
1075					var mousewheelTimeout;
1076					_mwt();
1077				}
1078				_draggerRail.call(this);
1079				_wrapperScroll.call(this);
1080				if(o.advanced.autoScrollOnFocus){_focus.call(this);}
1081				if(o.scrollButtons.enable){_buttons.call(this);}
1082				if(o.keyboard.enable){_keyboard.call(this);}
1083				d.bindEvents=true;
1084			}
1085		},
1086		/* -------------------- */
1087		
1088		
1089		/* unbinds scrollbar events */
1090		_unbindEvents=function(){
1091			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1092				namespace=pluginPfx+"_"+d.idx,
1093				sb=".mCSB_"+d.idx+"_scrollbar",
1094				sel=$("#mCSB_"+d.idx+",#mCSB_"+d.idx+"_container,#mCSB_"+d.idx+"_container_wrapper,"+sb+" ."+classes[12]+",#mCSB_"+d.idx+"_dragger_vertical,#mCSB_"+d.idx+"_dragger_horizontal,"+sb+">a"),
1095				mCSB_container=$("#mCSB_"+d.idx+"_container");
1096			if(o.advanced.releaseDraggableSelectors){sel.add($(o.advanced.releaseDraggableSelectors));}
1097			if(o.advanced.extraDraggableSelectors){sel.add($(o.advanced.extraDraggableSelectors));}
1098			if(d.bindEvents){ /* check if events are bound */
1099				/* unbind namespaced events from document/selectors */
1100				$(document).add($(!_canAccessIFrame() || top.document)).unbind("."+namespace);
1101				sel.each(function(){
1102					$(this).unbind("."+namespace);
1103				});
1104				/* clear and delete timeouts/objects */
1105				clearTimeout($this[0]._focusTimeout); _delete($this[0],"_focusTimeout");
1106				clearTimeout(d.sequential.step); _delete(d.sequential,"step");
1107				clearTimeout(mCSB_container[0].onCompleteTimeout); _delete(mCSB_container[0],"onCompleteTimeout");
1108				d.bindEvents=false;
1109			}
1110		},
1111		/* -------------------- */
1112		
1113		
1114		/* toggles scrollbar visibility */
1115		_scrollbarVisibility=function(disabled){
1116			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1117				contentWrapper=$("#mCSB_"+d.idx+"_container_wrapper"),
1118				content=contentWrapper.length ? contentWrapper : $("#mCSB_"+d.idx+"_container"),
1119				scrollbar=[$("#mCSB_"+d.idx+"_scrollbar_vertical"),$("#mCSB_"+d.idx+"_scrollbar_horizontal")],
1120				mCSB_dragger=[scrollbar[0].find(".mCSB_dragger"),scrollbar[1].find(".mCSB_dragger")];
1121			if(o.axis!=="x"){
1122				if(d.overflowed[0] && !disabled){
1123					scrollbar[0].add(mCSB_dragger[0]).add(scrollbar[0].children("a")).css("display","block");
1124					content.removeClass(classes[8]+" "+classes[10]);
1125				}else{
1126					if(o.alwaysShowScrollbar){
1127						if(o.alwaysShowScrollbar!==2){mCSB_dragger[0].css("display","none");}
1128						content.removeClass(classes[10]);
1129					}else{
1130						scrollbar[0].css("display","none");
1131						content.addClass(classes[10]);
1132					}
1133					content.addClass(classes[8]);
1134				}
1135			}
1136			if(o.axis!=="y"){
1137				if(d.overflowed[1] && !disabled){
1138					scrollbar[1].add(mCSB_dragger[1]).add(scrollbar[1].children("a")).css("display","block");
1139					content.removeClass(classes[9]+" "+classes[11]);
1140				}else{
1141					if(o.alwaysShowScrollbar){
1142						if(o.alwaysShowScrollbar!==2){mCSB_dragger[1].css("display","none");}
1143						content.removeClass(classes[11]);
1144					}else{
1145						scrollbar[1].css("display","none");
1146						content.addClass(classes[11]);
1147					}
1148					content.addClass(classes[9]);
1149				}
1150			}
1151			if(!d.overflowed[0] && !d.overflowed[1]){
1152				$this.addClass(classes[5]);
1153			}else{
1154				$this.removeClass(classes[5]);
1155			}
1156		},
1157		/* -------------------- */
1158		
1159		
1160		/* returns input coordinates of pointer, touch and mouse events (relative to document) */
1161		_coordinates=function(e){
1162			var t=e.type,o=e.target.ownerDocument!==document && frameElement!==null ? [$(frameElement).offset().top,$(frameElement).offset().left] : null,
1163				io=_canAccessIFrame() && e.target.ownerDocument!==top.document && frameElement!==null ? [$(e.view.frameElement).offset().top,$(e.view.frameElement).offset().left] : [0,0];
1164			switch(t){
1165				case "pointerdown": case "MSPointerDown": case "pointermove": case "MSPointerMove": case "pointerup": case "MSPointerUp":
1166					return o ? [e.originalEvent.pageY-o[0]+io[0],e.originalEvent.pageX-o[1]+io[1],false] : [e.originalEvent.pageY,e.originalEvent.pageX,false];
1167					break;
1168				case "touchstart": case "touchmove": case "touchend":
1169					var touch=e.originalEvent.touches[0] || e.originalEvent.changedTouches[0],
1170						touches=e.originalEvent.touches.length || e.originalEvent.changedTouches.length;
1171					return e.target.ownerDocument!==document ? [touch.screenY,touch.screenX,touches>1] : [touch.pageY,touch.pageX,touches>1];
1172					break;
1173				default:
1174					return o ? [e.pageY-o[0]+io[0],e.pageX-o[1]+io[1],false] : [e.pageY,e.pageX,false];
1175			}
1176		},
1177		/* -------------------- */
1178		
1179		
1180		/* 
1181		SCROLLBAR DRAG EVENTS
1182		scrolls content via scrollbar dragging 
1183		*/
1184		_draggable=function(){
1185			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1186				namespace=pluginPfx+"_"+d.idx,
1187				draggerId=["mCSB_"+d.idx+"_dragger_vertical","mCSB_"+d.idx+"_dragger_horizontal"],
1188				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1189				mCSB_dragger=$("#"+draggerId[0]+",#"+draggerId[1]),
1190				draggable,dragY,dragX,
1191				rds=o.advanced.releaseDraggableSelectors ? mCSB_dragger.add($(o.advanced.releaseDraggableSelectors)) : mCSB_dragger,
1192				eds=o.advanced.extraDraggableSelectors ? $(!_canAccessIFrame() || top.document).add($(o.advanced.extraDraggableSelectors)) : $(!_canAccessIFrame() || top.document);
1193			mCSB_dragger.bind("contextmenu."+namespace,function(e){
1194				e.preventDefault(); //prevent right click
1195			}).bind("mousedown."+namespace+" touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace,function(e){
1196				e.stopImmediatePropagation();
1197				e.preventDefault();
1198				if(!_mouseBtnLeft(e)){return;} /* left mouse button only */
1199				touchActive=true;
1200				if(oldIE){document.onselectstart=function(){return false;}} /* disable text selection for IE < 9 */
1201				_iframe.call(mCSB_container,false); /* enable scrollbar dragging over iframes by disabling their events */
1202				_stop($this);
1203				draggable=$(this);
1204				var offset=draggable.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left,
1205					h=draggable.height()+offset.top,w=draggable.width()+offset.left;
1206				if(y<h && y>0 && x<w && x>0){
1207					dragY=y; 
1208					dragX=x;
1209				}
1210				_onDragClasses(draggable,"active",o.autoExpandScrollbar); 
1211			}).bind("touchmove."+namespace,function(e){
1212				e.stopImmediatePropagation();
1213				e.preventDefault();
1214				var offset=draggable.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left;
1215				_drag(dragY,dragX,y,x);
1216			});
1217			$(document).add(eds).bind("mousemove."+namespace+" pointermove."+namespace+" MSPointerMove."+namespace,function(e){
1218				if(draggable){
1219					var offset=draggable.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left;
1220					if(dragY===y && dragX===x){return;} /* has it really moved? */
1221					_drag(dragY,dragX,y,x);
1222				}
1223			}).add(rds).bind("mouseup."+namespace+" touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace,function(e){
1224				if(draggable){
1225					_onDragClasses(draggable,"active",o.autoExpandScrollbar); 
1226					draggable=null;
1227				}
1228				touchActive=false;
1229				if(oldIE){document.onselectstart=null;} /* enable text selection for IE < 9 */
1230				_iframe.call(mCSB_container,true); /* enable iframes events */
1231			});
1232			function _drag(dragY,dragX,y,x){
1233				mCSB_container[0].idleTimer=o.scrollInertia<233 ? 250 : 0;
1234				if(draggable.attr("id")===draggerId[1]){
1235					var dir="x",to=((draggable[0].offsetLeft-dragX)+x)*d.scrollRatio.x;
1236				}else{
1237					var dir="y",to=((draggable[0].offsetTop-dragY)+y)*d.scrollRatio.y;
1238				}
1239				_scrollTo($this,to.toString(),{dir:dir,drag:true});
1240			}
1241		},
1242		/* -------------------- */
1243		
1244		
1245		/* 
1246		TOUCH SWIPE EVENTS
1247		scrolls content via touch swipe 
1248		Emulates the native touch-swipe scrolling with momentum found in iOS, Android and WP devices 
1249		*/
1250		_contentDraggable=function(){
1251			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1252				namespace=pluginPfx+"_"+d.idx,
1253				mCustomScrollBox=$("#mCSB_"+d.idx),
1254				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1255				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
1256				draggable,dragY,dragX,touchStartY,touchStartX,touchMoveY=[],touchMoveX=[],startTime,runningTime,endTime,distance,speed,amount,
1257				durA=0,durB,overwrite=o.axis==="yx" ? "none" : "all",touchIntent=[],touchDrag,docDrag,
1258				iframe=mCSB_container.find("iframe"),
1259				events=[
1260					"touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace, //start
1261					"touchmove."+namespace+" pointermove."+namespace+" MSPointerMove."+namespace, //move
1262					"touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace //end
1263				],
1264				touchAction=document.body.style.touchAction!==undefined && document.body.style.touchAction!=="";
1265			mCSB_container.bind(events[0],function(e){
1266				_onTouchstart(e);
1267			}).bind(events[1],function(e){
1268				_onTouchmove(e);
1269			});
1270			mCustomScrollBox.bind(events[0],function(e){
1271				_onTouchstart2(e);
1272			}).bind(events[2],function(e){
1273				_onTouchend(e);
1274			});
1275			if(iframe.length){
1276				iframe.each(function(){
1277					$(this).bind("load",function(){
1278						/* bind events on accessible iframes */
1279						if(_canAccessIFrame(this)){
1280							$(this.contentDocument || this.contentWindow.document).bind(events[0],function(e){
1281								_onTouchstart(e);
1282								_onTouchstart2(e);
1283							}).bind(events[1],function(e){
1284								_onTouchmove(e);
1285							}).bind(events[2],function(e){
1286								_onTouchend(e);
1287							});
1288						}
1289					});
1290				});
1291			}
1292			function _onTouchstart(e){
1293				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){touchable=0; return;}
1294				touchable=1; touchDrag=0; docDrag=0; draggable=1;
1295				$this.removeClass("mCS_touch_action");
1296				var offset=mCSB_container.offset();
1297				dragY=_coordinates(e)[0]-offset.top;
1298				dragX=_coordinates(e)[1]-offset.left;
1299				touchIntent=[_coordinates(e)[0],_coordinates(e)[1]];
1300			}
1301			function _onTouchmove(e){
1302				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){return;}
1303				if(!o.documentTouchScroll){e.preventDefault();} 
1304				e.stopImmediatePropagation();
1305				if(docDrag && !touchDrag){return;}
1306				if(draggable){
1307					runningTime=_getTime();
1308					var offset=mCustomScrollBox.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left,
1309						easing="mcsLinearOut";
1310					touchMoveY.push(y);
1311					touchMoveX.push(x);
1312					touchIntent[2]=Math.abs(_coordinates(e)[0]-touchIntent[0]); touchIntent[3]=Math.abs(_coordinates(e)[1]-touchIntent[1]);
1313					if(d.overflowed[0]){
1314						var limit=mCSB_dragger[0].parent().height()-mCSB_dragger[0].height(),
1315							prevent=((dragY-y)>0 && (y-dragY)>-(limit*d.scrollRatio.y) && (touchIntent[3]*2<touchIntent[2] || o.axis==="yx"));
1316					}
1317					if(d.overflowed[1]){
1318						var limitX=mCSB_dragger[1].parent().width()-mCSB_dragger[1].width(),
1319							preventX=((dragX-x)>0 && (x-dragX)>-(limitX*d.scrollRatio.x) && (touchIntent[2]*2<touchIntent[3] || o.axis==="yx"));
1320					}
1321					if(prevent || preventX){ /* prevent native document scrolling */
1322						if(!touchAction){e.preventDefault();} 
1323						touchDrag=1;
1324					}else{
1325						docDrag=1;
1326						$this.addClass("mCS_touch_action");
1327					}
1328					if(touchAction){e.preventDefault();} 
1329					amount=o.axis==="yx" ? [(dragY-y),(dragX-x)] : o.axis==="x" ? [null,(dragX-x)] : [(dragY-y),null];
1330					mCSB_container[0].idleTimer=250;
1331					if(d.overflowed[0]){_drag(amount[0],durA,easing,"y","all",true);}
1332					if(d.overflowed[1]){_drag(amount[1],durA,easing,"x",overwrite,true);}
1333				}
1334			}
1335			function _onTouchstart2(e){
1336				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){touchable=0; return;}
1337				touchable=1;
1338				e.stopImmediatePropagation();
1339				_stop($this);
1340				startTime=_getTime();
1341				var offset=mCustomScrollBox.offset();
1342				touchStartY=_coordinates(e)[0]-offset.top;
1343				touchStartX=_coordinates(e)[1]-offset.left;
1344				touchMoveY=[]; touchMoveX=[];
1345			}
1346			function _onTouchend(e){
1347				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){return;}
1348				draggable=0;
1349				e.stopImmediatePropagation();
1350				touchDrag=0; docDrag=0;
1351				endTime=_getTime();
1352				var offset=mCustomScrollBox.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left;
1353				if((endTime-runningTime)>30){return;}
1354				speed=1000/(endTime-startTime);
1355				var easing="mcsEaseOut",slow=speed<2.5,
1356					diff=slow ? [touchMoveY[touchMoveY.length-2],touchMoveX[touchMoveX.length-2]] : [0,0];
1357				distance=slow ? [(y-diff[0]),(x-diff[1])] : [y-touchStartY,x-touchStartX];
1358				var absDistance=[Math.abs(distance[0]),Math.abs(distance[1])];
1359				speed=slow ? [Math.abs(distance[0]/4),Math.abs(distance[1]/4)] : [speed,speed];
1360				var a=[
1361					Math.abs(mCSB_container[0].offsetTop)-(distance[0]*_m((absDistance[0]/speed[0]),speed[0])),
1362					Math.abs(mCSB_container[0].offsetLeft)-(distance[1]*_m((absDistance[1]/speed[1]),speed[1]))
1363				];
1364				amount=o.axis==="yx" ? [a[0],a[1]] : o.axis==="x" ? [null,a[1]] : [a[0],null];
1365				durB=[(absDistance[0]*4)+o.scrollInertia,(absDistance[1]*4)+o.scrollInertia];
1366				var md=parseInt(o.contentTouchScroll) || 0; /* absolute minimum distance required */
1367				amount[0]=absDistance[0]>md ? amount[0] : 0;
1368				amount[1]=absDistance[1]>md ? amount[1] : 0;
1369				if(d.overflowed[0]){_drag(amount[0],durB[0],easing,"y",overwrite,false);}
1370				if(d.overflowed[1]){_drag(amount[1],durB[1],easing,"x",overwrite,false);}
1371			}
1372			function _m(ds,s){
1373				var r=[s*1.5,s*2,s/1.5,s/2];
1374				if(ds>90){
1375					return s>4 ? r[0] : r[3];
1376				}else if(ds>60){
1377					return s>3 ? r[3] : r[2];
1378				}else if(ds>30){
1379					return s>8 ? r[1] : s>6 ? r[0] : s>4 ? s : r[2];
1380				}else{
1381					return s>8 ? s : r[3];
1382				}
1383			}
1384			function _drag(amount,dur,easing,dir,overwrite,drag){
1385				if(!amount){return;}
1386				_scrollTo($this,amount.toString(),{dur:dur,scrollEasing:easing,dir:dir,overwrite:overwrite,drag:drag});
1387			}
1388		},
1389		/* -------------------- */
1390		
1391		
1392		/* 
1393		SELECT TEXT EVENTS 
1394		scrolls content when text is selected 
1395		*/
1396		_selectable=function(){
1397			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,seq=d.sequential,
1398				namespace=pluginPfx+"_"+d.idx,
1399				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1400				wrapper=mCSB_container.parent(),
1401				action;
1402			mCSB_container.bind("mousedown."+namespace,function(e){
1403				if(touchable){return;}
1404				if(!action){action=1; touchActive=true;}
1405			}).add(document).bind("mousemove."+namespace,function(e){
1406				if(!touchable && action && _sel()){
1407					var offset=mCSB_container.offset(),
1408						y=_coordinates(e)[0]-offset.top+mCSB_container[0].offsetTop,x=_coordinates(e)[1]-offset.left+mCSB_container[0].offsetLeft;
1409					if(y>0 && y<wrapper.height() && x>0 && x<wrapper.width()){
1410						if(seq.step){_seq("off",null,"stepped");}
1411					}else{
1412						if(o.axis!=="x" && d.overflowed[0]){
1413							if(y<0){
1414								_seq("on",38);
1415							}else if(y>wrapper.height()){
1416								_seq("on",40);
1417							}
1418						}
1419						if(o.axis!=="y" && d.overflowed[1]){
1420							if(x<0){
1421								_seq("on",37);
1422							}else if(x>wrapper.width()){
1423								_seq("on",39);
1424							}
1425						}
1426					}
1427				}
1428			}).bind("mouseup."+namespace+" dragend."+namespace,function(e){
1429				if(touchable){return;}
1430				if(action){action=0; _seq("off",null);}
1431				touchActive=false;
1432			});
1433			function _sel(){
1434				return 	window.getSelection ? window.getSelection().toString() : 
1435						document.selection && document.selection.type!="Control" ? document.selection.createRange().text : 0;
1436			}
1437			function _seq(a,c,s){
1438				seq.type=s && action ? "stepped" : "stepless";
1439				seq.scrollAmount=10;
1440				_sequentialScroll($this,a,c,"mcsLinearOut",s ? 60 : null);
1441			}
1442		},
1443		/* -------------------- */
1444		
1445		
1446		/* 
1447		MOUSE WHEEL EVENT
1448		scrolls content via mouse-wheel 
1449		via mouse-wheel plugin (https://github.com/brandonaaron/jquery-mousewheel)
1450		*/
1451		_mousewheel=function(){
1452			if(!$(this).data(pluginPfx)){return;} /* Check if the scrollbar is ready to use mousewheel events (issue: #185) */
1453			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1454				namespace=pluginPfx+"_"+d.idx,
1455				mCustomScrollBox=$("#mCSB_"+d.idx),
1456				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
1457				iframe=$("#mCSB_"+d.idx+"_container").find("iframe");
1458			if(iframe.length){
1459				iframe.each(function(){
1460					$(this).bind("load",function(){
1461						/* bind events on accessible iframes */
1462						if(_canAccessIFrame(this)){
1463							$(this.contentDocument || this.contentWindow.document).bind("mousewheel."+namespace,function(e,delta){
1464								_onMousewheel(e,delta);
1465							});
1466						}
1467					});
1468				});
1469			}
1470			mCustomScrollBox.bind("mousewheel."+namespace,function(e,delta){
1471				_onMousewheel(e,delta);
1472			});
1473			function _onMousewheel(e,delta){
1474				_stop($this);
1475				if(_disableMousewheel($this,e.target)){return;} /* disables mouse-wheel when hovering specific elements */
1476				var deltaFactor=o.mouseWheel.deltaFactor!=="auto" ? parseInt(o.mouseWheel.deltaFactor) : (oldIE && e.deltaFactor<100) ? 100 : e.deltaFactor || 100,
1477					dur=o.scrollInertia;
1478				if(o.axis==="x" || o.mouseWheel.axis==="x"){
1479					var dir="x",
1480						px=[Math.round(deltaFactor*d.scrollRatio.x),parseInt(o.mouseWheel.scrollAmount)],
1481						amount=o.mouseWheel.scrollAmount!=="auto" ? px[1] : px[0]>=mCustomScrollBox.width() ? mCustomScrollBox.width()*0.9 : px[0],
1482						contentPos=Math.abs($("#mCSB_"+d.idx+"_container")[0].offsetLeft),
1483						draggerPos=mCSB_dragger[1][0].offsetLeft,
1484						limit=mCSB_dragger[1].parent().width()-mCSB_dragger[1].width(),
1485						dlt=o.mouseWheel.axis==="y" ? (e.deltaY || delta) : e.deltaX;
1486				}else{
1487					var dir="y",
1488						px=[Math.round(deltaFactor*d.scrollRatio.y),parseInt(o.mouseWheel.scrollAmount)],
1489						amount=o.mouseWheel.scrollAmount!=="auto" ? px[1] : px[0]>=mCustomScrollBox.height() ? mCustomScrollBox.height()*0.9 : px[0],
1490						contentPos=Math.abs($("#mCSB_"+d.idx+"_container")[0].offsetTop),
1491						draggerPos=mCSB_dragger[0][0].offsetTop,
1492						limit=mCSB_dragger[0].parent().height()-mCSB_dragger[0].height(),
1493						dlt=e.deltaY || delta;
1494				}
1495				if((dir==="y" && !d.overflowed[0]) || (dir==="x" && !d.overflowed[1])){return;}
1496				if(o.mouseWheel.invert || e.webkitDirectionInvertedFromDevice){dlt=-dlt;}
1497				if(o.mouseWheel.normalizeDelta){dlt=dlt<0 ? -1 : 1;}
1498				if((dlt>0 && draggerPos!==0) || (dlt<0 && draggerPos!==limit) || o.mouseWheel.preventDefault){
1499					e.stopImmediatePropagation();
1500					e.preventDefault();
1501				}
1502				if(e.deltaFactor<5 && !o.mouseWheel.normalizeDelta){
1503					//very low deltaFactor values mean some kind of delta acceleration (e.g. osx trackpad), so adjusting scrolling accordingly
1504					amount=e.deltaFactor; dur=17;
1505				}
1506				_scrollTo($this,(contentPos-(dlt*amount)).toString(),{dir:dir,dur:dur});
1507			}
1508		},
1509		/* -------------------- */
1510		
1511		
1512		/* checks if iframe can be accessed */
1513		_canAccessIFrameCache=new Object(),
1514		_canAccessIFrame=function(iframe){
1515		    var result=false,cacheKey=false,html=null;
1516		    if(iframe===undefined){
1517				cacheKey="#empty";
1518		    }else if($(iframe).attr("id")!==undefined){
1519				cacheKey=$(iframe).attr("id");
1520		    }
1521			if(cacheKey!==false && _canAccessIFrameCache[cacheKey]!==undefined){
1522				return _canAccessIFrameCache[cacheKey];
1523			}
1524			if(!iframe){
1525				try{
1526					var doc=top.document;
1527					html=doc.body.innerHTML;
1528				}catch(err){/* do nothing */}
1529				result=(html!==null);
1530			}else{
1531				try{
1532					var doc=iframe.contentDocument || iframe.contentWindow.document;
1533					html=doc.body.innerHTML;
1534				}catch(err){/* do nothing */}
1535				result=(html!==null);
1536			}
1537			if(cacheKey!==false){_canAccessIFrameCache[cacheKey]=result;}
1538			return result;
1539		},
1540		/* -------------------- */
1541		
1542		
1543		/* switches iframe's pointer-events property (drag, mousewheel etc. over cross-domain iframes) */
1544		_iframe=function(evt){
1545			var el=this.find("iframe");
1546			if(!el.length){return;} /* check if content contains iframes */
1547			var val=!evt ? "none" : "auto";
1548			el.css("pointer-events",val); /* for IE11, iframe's display property should not be "block" */
1549		},
1550		/* -------------------- */
1551		
1552		
1553		/* disables mouse-wheel when hovering specific elements like select, datalist etc. */
1554		_disableMousewheel=function(el,target){
1555			var tag=target.nodeName.toLowerCase(),
1556				tags=el.data(pluginPfx).opt.mouseWheel.disableOver,
1557				/* elements that require focus */
1558				focusTags=["select","textarea"];
1559			return $.inArray(tag,tags) > -1 && !($.inArray(tag,focusTags) > -1 && !$(target).is(":focus"));
1560		},
1561		/* -------------------- */
1562		
1563		
1564		/* 
1565		DRAGGER RAIL CLICK EVENT
1566		scrolls content via dragger rail 
1567		*/
1568		_draggerRail=function(){
1569			var $this=$(this),d=$this.data(pluginPfx),
1570				namespace=pluginPfx+"_"+d.idx,
1571				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1572				wrapper=mCSB_container.parent(),
1573				mCSB_draggerContainer=$(".mCSB_"+d.idx+"_scrollbar ."+classes[12]),
1574				clickable;
1575			mCSB_draggerContainer.bind("mousedown."+namespace+" touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace,function(e){
1576				touchActive=true;
1577				if(!$(e.target).hasClass("mCSB_dragger")){clickable=1;}
1578			}).bind("touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace,function(e){
1579				touchActive=false;
1580			}).bind("click."+namespace,function(e){
1581				if(!clickable){return;}
1582				clickable=0;
1583				if($(e.target).hasClass(classes[12]) || $(e.target).hasClass("mCSB_draggerRail")){
1584					_stop($this);
1585					var el=$(this),mCSB_dragger=el.find(".mCSB_dragger");
1586					if(el.parent(".mCSB_scrollTools_horizontal").length>0){
1587						if(!d.overflowed[1]){return;}
1588						var dir="x",
1589							clickDir=e.pageX>mCSB_dragger.offset().left ? -1 : 1,
1590							to=Math.abs(mCSB_container[0].offsetLeft)-(clickDir*(wrapper.width()*0.9));
1591					}else{
1592						if(!d.overflowed[0]){return;}
1593						var dir="y",
1594							clickDir=e.pageY>mCSB_dragger.offset().top ? -1 : 1,
1595							to=Math.abs(mCSB_container[0].offsetTop)-(clickDir*(wrapper.height()*0.9));
1596					}
1597					_scrollTo($this,to.toString(),{dir:dir,scrollEasing:"mcsEaseInOut"});
1598				}
1599			});
1600		},
1601		/* -------------------- */
1602		
1603		
1604		/* 
1605		FOCUS EVENT
1606		scrolls content via element focus (e.g. clicking an input, pressing TAB key etc.)
1607		*/
1608		_focus=function(){
1609			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1610				namespace=pluginPfx+"_"+d.idx,
1611				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1612				wrapper=mCSB_container.parent();
1613			mCSB_container.bind("focusin."+namespace,function(e){
1614				var el=$(document.activeElement),
1615					nested=mCSB_container.find(".mCustomScrollBox").length,
1616					dur=0;
1617				if(!el.is(o.advanced.autoScrollOnFocus)){return;}
1618				_stop($this);
1619				clearTimeout($this[0]._focusTimeout);
1620				$this[0]._focusTimer=nested ? (dur+17)*nested : 0;
1621				$this[0]._focusTimeout=setTimeout(function(){
1622					var	to=[_childPos(el)[0],_childPos(el)[1]],
1623						contentPos=[mCSB_container[0].offsetTop,mCSB_container[0].offsetLeft],
1624						isVisible=[
1625							(contentPos[0]+to[0]>=0 && contentPos[0]+to[0]<wrapper.height()-el.outerHeight(false)),
1626							(contentPos[1]+to[1]>=0 && contentPos[0]+to[1]<wrapper.width()-el.outerWidth(false))
1627						],
1628						overwrite=(o.axis==="yx" && !isVisible[0] && !isVisible[1]) ? "none" : "all";
1629					if(o.axis!=="x" && !isVisible[0]){
1630						_scrollTo($this,to[0].toString(),{dir:"y",scrollEasing:"mcsEaseInOut",overwrite:overwrite,dur:dur});
1631					}
1632					if(o.axis!=="y" && !isVisible[1]){
1633						_scrollTo($this,to[1].toString(),{dir:"x",scrollEasing:"mcsEaseInOut",overwrite:overwrite,dur:dur});
1634					}
1635				},$this[0]._focusTimer);
1636			});
1637		},
1638		/* -------------------- */
1639		
1640		
1641		/* sets content wrapper scrollTop/scrollLeft always to 0 */
1642		_wrapperScroll=function(){
1643			var $this=$(this),d=$this.data(pluginPfx),
1644				namespace=pluginPfx+"_"+d.idx,
1645				wrapper=$("#mCSB_"+d.idx+"_container").parent();
1646			wrapper.bind("scroll."+namespace,function(e){
1647				if(wrapper.scrollTop()!==0 || wrapper.scrollLeft()!==0){
1648					$(".mCSB_"+d.idx+"_scrollbar").css("visibility","hidden"); /* hide scrollbar(s) */
1649				}
1650			});
1651		},
1652		/* -------------------- */
1653		
1654		
1655		/* 
1656		BUTTONS EVENTS
1657		scrolls content via up, down, left and right buttons 
1658		*/
1659		_buttons=function(){
1660			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,seq=d.sequential,
1661				namespace=pluginPfx+"_"+d.idx,
1662				sel=".mCSB_"+d.idx+"_scrollbar",
1663				btn=$(sel+">a");
1664			btn.bind("contextmenu."+namespace,function(e){
1665				e.preventDefault(); //prevent right click
1666			}).bind("mousedown."+namespace+" touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace+" mouseup."+namespace+" touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace+" mouseout."+namespace+" pointerout."+namespace+" MSPointerOut."+namespace+" click."+namespace,function(e){
1667				e.preventDefault();
1668				if(!_mouseBtnLeft(e)){return;} /* left mouse button only */
1669				var btnClass=$(this).attr("class");
1670				seq.type=o.scrollButtons.scrollType;
1671				switch(e.type){
1672					case "mousedown": case "touchstart": case "pointerdown": case "MSPointerDown":
1673						if(seq.type==="stepped"){return;}
1674						touchActive=true;
1675						d.tweenRunning=false;
1676						_seq("on",btnClass);
1677						break;
1678					case "mouseup": case "touchend": case "pointerup": case "MSPointerUp":
1679					case "mouseout": case "pointerout": case "MSPointerOut":
1680						if(seq.type==="stepped"){return;}
1681						touchActive=false;
1682						if(seq.dir){_seq("off",btnClass);}
1683						break;
1684					case "click":
1685						if(seq.type!=="stepped" || d.tweenRunning){return;}
1686						_seq("on",btnClass);
1687						break;
1688				}
1689				function _seq(a,c){
1690					seq.scrollAmount=o.scrollButtons.scrollAmount;
1691					_sequentialScroll($this,a,c);
1692				}
1693			});
1694		},
1695		/* -------------------- */
1696		
1697		
1698		/* 
1699		KEYBOARD EVENTS
1700		scrolls content via keyboard 
1701		Keys: up arrow, down arrow, left arrow, right arrow, PgUp, PgDn, Home, End
1702		*/
1703		_keyboard=function(){
1704			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,seq=d.sequential,
1705				namespace=pluginPfx+"_"+d.idx,
1706				mCustomScrollBox=$("#mCSB_"+d.idx),
1707				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1708				wrapper=mCSB_container.parent(),
1709				editables="input,textarea,select,datalist,keygen,[contenteditable='true']",
1710				iframe=mCSB_container.find("iframe"),
1711				events=["blur."+namespace+" keydown."+namespace+" keyup."+namespace];
1712			if(iframe.length){
1713				iframe.each(function(){
1714					$(this).bind("load",function(){
1715						/* bind events on accessible iframes */
1716						if(_canAccessIFrame(this)){
1717							$(this.contentDocument || this.contentWindow.document).bind(events[0],function(e){
1718								_onKeyboard(e);
1719							});
1720						}
1721					});
1722				});
1723			}
1724			mCustomScrollBox.attr("tabindex","0").bind(events[0],function(e){
1725				_onKeyboard(e);
1726			});
1727			function _onKeyboard(e){
1728				switch(e.type){
1729					case "blur":
1730						if(d.tweenRunning && seq.dir){_seq("off",null);}
1731						break;
1732					case "keydown": case "keyup":
1733						var code=e.keyCode ? e.keyCode : e.which,action="on";
1734						if((o.axis!=="x" && (code===38 || code===40)) || (o.axis!=="y" && (code===37 || code===39))){
1735							/* up (38), down (40), left (37), right (39) arrows */
1736							if(((code===38 || code===40) && !d.overflowed[0]) || ((code===37 || code===39) && !d.overflowed[1])){return;}
1737							if(e.type==="keyup"){action="off";}
1738							if(!$(document.activeElement).is(editables)){
1739								e.preventDefault();
1740								e.stopImmediatePropagation();
1741								_seq(action,code);
1742							}
1743						}else if(code===33 || code===34){
1744							/* PgUp (33), PgDn (34) */
1745							if(d.overflowed[0] || d.overflowed[1]){
1746								e.preventDefault();
1747								e.stopImmediatePropagation();
1748							}
1749							if(e.type==="keyup"){
1750								_stop($this);
1751								var keyboardDir=code===34 ? -1 : 1;
1752								if(o.axis==="x" || (o.axis==="yx" && d.overflowed[1] && !d.overflowed[0])){
1753									var dir="x",to=Math.abs(mCSB_container[0].offsetLeft)-(keyboardDir*(wrapper.width()*0.9));
1754								}else{
1755									var dir="y",to=Math.abs(mCSB_container[0].offsetTop)-(keyboardDir*(wrapper.height()*0.9));
1756								}
1757								_scrollTo($this,to.toString(),{dir:dir,scrollEasing:"mcsEaseInOut"});
1758							}
1759						}else if(code===35 || code===36){
1760							/* End (35), Home (36) */
1761							if(!$(document.activeElement).is(editables)){
1762								if(d.overflowed[0] || d.overflowed[1]){
1763									e.preventDefault();
1764									e.stopImmediatePropagation();
1765								}
1766								if(e.type==="keyup"){
1767									if(o.axis==="x" || (o.axis==="yx" && d.overflowed[1] && !d.overflowed[0])){
1768										var dir="x",to=code===35 ? Math.abs(wrapper.width()-mCSB_container.outerWidth(false)) : 0;
1769									}else{
1770										var dir="y",to=code===35 ? Math.abs(wrapper.height()-mCSB_container.outerHeight(false)) : 0;
1771									}
1772									_scrollTo($this,to.toString(),{dir:dir,scrollEasing:"mcsEaseInOut"});
1773								}
1774							}
1775						}
1776						break;
1777				}
1778				function _seq(a,c){
1779					seq.type=o.keyboard.scrollType;
1780					seq.scrollAmount=o.keyboard.scrollAmount;
1781					if(seq.type==="stepped" && d.tweenRunning){return;}
1782					_sequentialScroll($this,a,c);
1783				}
1784			}
1785		},
1786		/* -------------------- */
1787		
1788		
1789		/* scrolls content sequentially (used when scrolling via buttons, keyboard arrows etc.) */
1790		_sequentialScroll=function(el,action,trigger,e,s){
1791			var d=el.data(pluginPfx),o=d.opt,seq=d.sequential,
1792				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1793				once=seq.type==="stepped" ? true : false,
1794				steplessSpeed=o.scrollInertia < 26 ? 26 : o.scrollInertia, /* 26/1.5=17 */
1795				steppedSpeed=o.scrollInertia < 1 ? 17 : o.scrollInertia;
1796			switch(action){
1797				case "on":
1798					seq.dir=[
1799						(trigger===classes[16] || trigger===classes[15] || trigger===39 || trigger===37 ? "x" : "y"),
1800						(trigger===classes[13] || trigger===classes[15] || trigger===38 || trigger===37 ? -1 : 1)
1801					];
1802					_stop(el);
1803					if(_isNumeric(trigger) && seq.type==="stepped"){return;}
1804					_on(once);
1805					break;
1806				case "off":
1807					_off();
1808					if(once || (d.tweenRunning && seq.dir)){
1809						_on(true);
1810					}
1811					break;
1812			}
1813			
1814			/* starts sequence */
1815			function _on(once){
1816				if(o.snapAmount){seq.scrollAmount=!(o.snapAmount instanceof Array) ? o.snapAmount : seq.dir[0]==="x" ? o.snapAmount[1] : o.snapAmount[0];} /* scrolling snapping */
1817				var c=seq.type!=="stepped", /* continuous scrolling */
1818					t=s ? s : !once ? 1000/60 : c ? steplessSpeed/1.5 : steppedSpeed, /* timer */
1819					m=!once ? 2.5 : c ? 7.5 : 40, /* multiplier */
1820					contentPos=[Math.abs(mCSB_container[0].offsetTop),Math.abs(mCSB_container[0].offsetLeft)],
1821					ratio=[d.scrollRatio.y>10 ? 10 : d.scrollRatio.y,d.scrollRatio.x>10 ? 10 : d.scrollRatio.x],
1822					amount=seq.dir[0]==="x" ? contentPos[1]+(seq.dir[1]*(ratio[1]*m)) : contentPos[0]+(seq.dir[1]*(ratio[0]*m)),
1823					px=seq.dir[0]==="x" ? contentPos[1]+(seq.dir[1]*parseInt(seq.scrollAmount)) : contentPos[0]+(seq.dir[1]*parseInt(seq.scrollAmount)),
1824					to=seq.scrollAmount!=="auto" ? px : amount,
1825					easing=e ? e : !once ? "mcsLinear" : c ? "mcsLinearOut" : "mcsEaseInOut",
1826					onComplete=!once ? false : true;
1827				if(once && t<17){
1828					to=seq.dir[0]==="x" ? contentPos[1] : contentPos[0];
1829				}
1830				_scrollTo(el,to.toString(),{dir:seq.dir[0],scrollEasing:easing,dur:t,onComplete:onComplete});
1831				if(once){
1832					seq.dir=false;
1833					return;
1834				}
1835				clearTimeout(seq.step);
1836				seq.step=setTimeout(function(){
1837					_on();
1838				},t);
1839			}
1840			/* stops sequence */
1841			function _off(){
1842				clearTimeout(seq.step);
1843				_delete(seq,"step");
1844				_stop(el);
1845			}
1846		},
1847		/* -------------------- */
1848		
1849		
1850		/* returns a yx array from value */
1851		_arr=function(val){
1852			var o=$(this).data(pluginPfx).opt,vals=[];
1853			if(typeof val==="function"){val=val();} /* check if the value is a single anonymous function */
1854			/* check if value is object or array, its length and create an array with yx values */
1855			if(!(val instanceof Array)){ /* object value (e.g. {y:"100",x:"100"}, 100 etc.) */
1856				vals[0]=val.y ? val.y : val.x || o.axis==="x" ? null : val;
1857				vals[1]=val.x ? val.x : val.y || o.axis==="y" ? null : val;
1858			}else{ /* array value (e.g. [100,100]) */
1859				vals=val.length>1 ? [val[0],val[1]] : o.axis==="x" ? [null,val[0]] : [val[0],null];
1860			}
1861			/* check if array values are anonymous functions */
1862			if(typeof vals[0]==="function"){vals[0]=vals[0]();}
1863			if(typeof vals[1]==="function"){vals[1]=vals[1]();}
1864			return vals;
1865		},
1866		/* -------------------- */
1867		
1868		
1869		/* translates values (e.g. "top", 100, "100px", "#id") to actual scroll-to positions */
1870		_to=function(val,dir){
1871			if(val==null || typeof val=="undefined"){return;}
1872			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1873				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1874				wrapper=mCSB_container.parent(),
1875				t=typeof val;
1876			if(!dir){dir=o.axis==="x" ? "x" : "y";}
1877			var contentLength=dir==="x" ? mCSB_container.outerWidth(false)-wrapper.width() : mCSB_container.outerHeight(false)-wrapper.height(),
1878				contentPos=dir==="x" ? mCSB_container[0].offsetLeft : mCSB_container[0].offsetTop,
1879				cssProp=dir==="x" ? "left" : "top";
1880			switch(t){
1881				case "function": /* this currently is not used. Consider removing it */
1882					return val();
1883					break;
1884				case "object": /* js/jquery object */
1885					var obj=val.jquery ? val : $(val);
1886					if(!obj.length){return;}
1887					return dir==="x" ? _childPos(obj)[1] : _childPos(obj)[0];
1888					break;
1889				case "string": case "number":
1890					if(_isNumeric(val)){ /* numeric value */
1891						return Math.abs(val);
1892					}else if(val.indexOf("%")!==-1){ /* percentage value */
1893						return Math.abs(contentLength*parseInt(val)/100);
1894					}else if(val.indexOf("-=")!==-1){ /* decrease value */
1895						return Math.abs(contentPos-parseInt(val.split("-=")[1]));
1896					}else if(val.indexOf("+=")!==-1){ /* inrease value */
1897						var p=(contentPos+parseInt(val.split("+=")[1]));
1898						return p>=0 ? 0 : Math.abs(p);
1899					}else if(val.indexOf("px")!==-1 && _isNumeric(val.split("px")[0])){ /* pixels string value (e.g. "100px") */
1900						return Math.abs(val.split("px")[0]);
1901					}else{
1902						if(val==="top" || val==="left"){ /* special strings */
1903							return 0;
1904						}else if(val==="bottom"){
1905							return Math.abs(wrapper.height()-mCSB_container.outerHeight(false));
1906						}else if(val==="right"){
1907							return Math.abs(wrapper.width()-mCSB_container.outerWidth(false));
1908						}else if(val==="first" || val==="last"){
1909							var obj=mCSB_container.find(":"+val);
1910							return dir==="x" ? _childPos(obj)[1] : _childPos(obj)[0];
1911						}else{
1912							if($(val).length){ /* jquery selector */
1913								return dir==="x" ? _childPos($(val))[1] : _childPos($(val))[0];
1914							}else{ /* other values (e.g. "100em") */
1915								mCSB_container.css(cssProp,val);
1916								methods.update.call(null,$this[0]);
1917								return;
1918							}
1919						}
1920					}
1921					break;
1922			}
1923		},
1924		/* -------------------- */
1925		
1926		
1927		/* calls the update method automatically */
1928		_autoUpdate=function(rem){
1929			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1930				mCSB_container=$("#mCSB_"+d.idx+"_container");
1931			if(rem){
1932				/* 
1933				removes autoUpdate timer 
1934				usage: _autoUpdate.call(this,"remove");
1935				*/
1936				clearTimeout(mCSB_container[0].autoUpdate);
1937				_delete(mCSB_container[0],"autoUpdate");
1938				return;
1939			}
1940			upd();
1941			function upd(){
1942				clearTimeout(mCSB_container[0].autoUpdate);
1943				if($this.parents("html").length===0){
1944					/* check element in dom tree */
1945					$this=null;
1946					return;
1947				}
1948				mCSB_container[0].autoUpdate=setTimeout(function(){
1949					/* update on specific selector(s) length and size change */
1950					if(o.advanced.updateOnSelectorChange){
1951						d.poll.change.n=sizesSum();
1952						if(d.poll.change.n!==d.poll.change.o){
1953							d.poll.change.o=d.poll.change.n;
1954							doUpd(3);
1955							return;
1956						}
1957					}
1958					/* update on main element and scrollbar size changes */
1959					if(o.advanced.updateOnContentResize){
1960						d.poll.size.n=$this[0].scrollHeight+$this[0].scrollWidth+mCSB_container[0].offsetHeight+$this[0].offsetHeight+$this[0].offsetWidth;
1961						if(d.poll.size.n!==d.poll.size.o){
1962							d.poll.size.o=d.poll.size.n;
1963							doUpd(1);
1964							return;
1965						}
1966					}
1967					/* update on image load */
1968					if(o.advanced.updateOnImageLoad){
1969						if(!(o.advanced.updateOnImageLoad==="auto" && o.axis==="y")){ //by default, it doesn't run on vertical content
1970							d.poll.img.n=mCSB_container.find("img").length;
1971							if(d.poll.img.n!==d.poll.img.o){
1972								d.poll.img.o=d.poll.img.n;
1973								mCSB_container.find("img").each(function(){
1974									imgLoader(this);
1975								});
1976								return;
1977							}
1978						}
1979					}
1980					if(o.advanced.updateOnSelectorChange || o.advanced.updateOnContentResize || o.advanced.updateOnImageLoad){upd();}
1981				},o.advanced.autoUpdateTimeout);
1982			}
1983			/* a tiny image loader */
1984			function imgLoader(el){
1985				if($(el).hasClass(classes[2])){doUpd(); return;}
1986				var img=new Image();
1987				function createDelegate(contextObject,delegateMethod){
1988					return function(){return delegateMethod.apply(contextObject,arguments);}
1989				}
1990				function imgOnLoad(){
1991					this.onload=null;
1992					$(el).addClass(classes[2]);
1993					doUpd(2);
1994				}
1995				img.onload=createDelegate(img,imgOnLoad);
1996				img.src=el.src;
1997			}
1998			/* returns the total height and width sum of all elements matching the selector */
1999			function sizesSum(){
2000				if(o.advanced.updateOnSelectorChange===true){o.advanced.updateOnSelectorChange="*";}
2001				var total=0,sel=mCSB_container.find(o.advanced.updateOnSelectorChange);
2002				if(o.advanced.updateOnSelectorChange && sel.length>0){sel.each(function(){total+=this.offsetHeight+this.offsetWidth;});}
2003				return total;
2004			}
2005			/* calls the update method */
2006			function doUpd(cb){
2007				clearTimeout(mCSB_container[0].autoUpdate);
2008				methods.update.call(null,$this[0],cb);
2009			}
2010		},
2011		/* -------------------- */
2012		
2013		
2014		/* snaps scrolling to a multiple of a pixels number */
2015		_snapAmount=function(to,amount,offset){
2016			return (Math.round(to/amount)*amount-offset); 
2017		},
2018		/* -------------------- */
2019		
2020		
2021		/* stops content and scrollbar animations */
2022		_stop=function(el){
2023			var d=el.data(pluginPfx),
2024				sel=$("#mCSB_"+d.idx+"_container,#mCSB_"+d.idx+"_container_wrapper,#mCSB_"+d.idx+"_dragger_vertical,#mCSB_"+d.idx+"_dragger_horizontal");
2025			sel.each(function(){
2026				_stopTween.call(this);
2027			});
2028		},
2029		/* -------------------- */
2030		
2031		
2032		/* 
2033		ANIMATES CONTENT 
2034		This is where the actual scrolling happens
2035		*/
2036		_scrollTo=function(el,to,options){
2037			var d=el.data(pluginPfx),o=d.opt,
2038				defaults={
2039					trigger:"internal",
2040					dir:"y",
2041					scrollEasing:"mcsEaseOut",
2042					drag:false,
2043					dur:o.scrollInertia,
2044					overwrite:"all",
2045					callbacks:true,
2046					onStart:true,
2047					onUpdate:true,
2048					onComplete:true
2049				},
2050				options=$.extend(defaults,options),
2051				dur=[options.dur,(options.drag ? 0 : options.dur)],
2052				mCustomScrollBox=$("#mCSB_"+d.idx),
2053				mCSB_container=$("#mCSB_"+d.idx+"_container"),
2054				wrapper=mCSB_container.parent(),
2055				totalScrollOffsets=o.callbacks.onTotalScrollOffset ? _arr.call(el,o.callbacks.onTotalScrollOffset) : [0,0],
2056				totalScrollBackOffsets=o.callbacks.onTotalScrollBackOffset ? _arr.call(el,o.callbacks.onTotalScrollBackOffset) : [0,0];
2057			d.trigger=options.trigger;
2058			if(wrapper.scrollTop()!==0 || wrapper.scrollLeft()!==0){ /* always reset scrollTop/Left */
2059				$(".mCSB_"+d.idx+"_scrollbar").css("visibility","visible");
2060				wrapper.scrollTop(0).scrollLeft(0);
2061			}
2062			if(to==="_resetY" && !d.contentReset.y){
2063				/* callbacks: onOverflowYNone */
2064				if(_cb("onOverflowYNone")){o.callbacks.onOverflowYNone.call(el[0]);}
2065				d.contentReset.y=1;
2066			}
2067			if(to==="_resetX" && !d.contentReset.x){
2068				/* callbacks: onOverflowXNone */
2069				if(_cb("onOverflowXNone")){o.callbacks.onOverflowXNone.call(el[0]);}
2070				d.contentReset.x=1;
2071			}
2072			if(to==="_resetY" || to==="_resetX"){return;}
2073			if((d.contentReset.y || !el[0].mcs) && d.overflowed[0]){
2074				/* callbacks: onOverflowY */
2075				if(_cb("onOverflowY")){o.callbacks.onOverflowY.call(el[0]);}
2076				d.contentReset.x=null;
2077			}
2078			if((d.contentReset.x || !el[0].mcs) && d.overflowed[1]){
2079				/* callbacks: onOverflowX */
2080				if(_cb("onOverflowX")){o.callbacks.onOverflowX.call(el[0]);}
2081				d.contentReset.x=null;
2082			}
2083			if(o.snapAmount){ /* scrolling snapping */
2084				var snapAmount=!(o.snapAmount instanceof Array) ? o.snapAmount : options.dir==="x" ? o.snapAmount[1] : o.snapAmount[0];
2085				to=_snapAmount(to,snapAmount,o.snapOffset);
2086			}
2087			switch(options.dir){
2088				case "x":
2089					var mCSB_dragger=$("#mCSB_"+d.idx+"_dragger_horizontal"),
2090						property="left",
2091						contentPos=mCSB_container[0].offsetLeft,
2092						limit=[
2093							mCustomScrollBox.width()-mCSB_container.outerWidth(false),
2094							mCSB_dragger.parent().width()-mCSB_dragger.width()
2095						],
2096						scrollTo=[to,to===0 ? 0 : (to/d.scrollRatio.x)],
2097						tso=totalScrollOffsets[1],
2098						tsbo=totalScrollBackOffsets[1],
2099						totalScrollOffset=tso>0 ? tso/d.scrollRatio.x : 0,
2100						totalScrollBackOffset=tsbo>0 ? tsbo/d.scrollRatio.x : 0;
2101					break;
2102				case "y":
2103					var mCSB_dragger=$("#mCSB_"+d.idx+"_dragger_vertical"),
2104						property="top",
2105						contentPos=mCSB_container[0].offsetTop,
2106						limit=[
2107							mCustomScrollBox.height()-mCSB_container.outerHeight(false),
2108							mCSB_dragger.parent().height()-mCSB_dragger.height()
2109						],
2110						scrollTo=[to,to===0 ? 0 : (to/d.scrollRatio.y)],
2111						tso=totalScrollOffsets[0],
2112						tsbo=totalScrollBackOffsets[0],
2113						totalScrollOffset=tso>0 ? tso/d.scrollRatio.y : 0,
2114						totalScrollBackOffset=tsbo>0 ? tsbo/d.scrollRatio.y : 0;
2115					break;
2116			}
2117			if(scrollTo[1]<0 || (scrollTo[0]===0 && scrollTo[1]===0)){
2118				scrollTo=[0,0];
2119			}else if(scrollTo[1]>=limit[1]){
2120				scrollTo=[limit[0],limit[1]];
2121			}else{
2122				scrollTo[0]=-scrollTo[0];
2123			}
2124			if(!el[0].mcs){
2125				_mcs();  /* init mcs object (once) to make it available before callbacks */
2126				if(_cb("onInit")){o.callbacks.onInit.call(el[0]);} /* callbacks: onInit */
2127			}
2128			clearTimeout(mCSB_container[0].onCompleteTimeout);
2129			_tweenTo(mCSB_dragger[0],property,Math.round(scrollTo[1]),dur[1],options.scrollEasing);
2130			if(!d.tweenRunning && ((contentPos===0 && scrollTo[0]>=0) || (contentPos===limit[0] && scrollTo[0]<=limit[0]))){return;}
2131			_tweenTo(mCSB_container[0],property,Math.round(scrollTo[0]),dur[0],options.scrollEasing,options.overwrite,{
2132				onStart:function(){
2133					if(options.callbacks && options.onStart && !d.tweenRunning){
2134						/* callbacks: onScrollStart */
2135						if(_cb("onScrollStart")){_mcs(); o.callbacks.onScrollStart.call(el[0]);}
2136						d.tweenRunning=true;
2137						_onDragClasses(mCSB_dragger);
2138						d.cbOffsets=_cbOffsets();
2139					}
2140				},onUpdate:function(){
2141					if(options.callbacks && options.onUpdate){
2142						/* callbacks: whileScrolling */
2143						if(_cb("whileScrolling")){_mcs(); o.callbacks.whileScrolling.call(el[0]);}
2144					}
2145				},onComplete:function(){
2146					if(options.callbacks && options.onComplete){
2147						if(o.axis==="yx"){clearTimeout(mCSB_container[0].onCompleteTimeout);}
2148						var t=mCSB_container[0].idleTimer || 0;
2149						mCSB_container[0].onCompleteTimeout=setTimeout(function(){
2150							/* callbacks: onScroll, onTotalScroll, onTotalScrollBack */
2151							if(_cb("onScroll")){_mcs(); o.callbacks.onScroll.call(el[0]);}
2152							if(_cb("onTotalScroll") && scrollTo[1]>=limit[1]-totalScrollOffset && d.cbOffsets[0]){_mcs(); o.callbacks.onTotalScroll.call(el[0]);}
2153							if(_cb("onTotalScrollBack") && scrollTo[1]<=totalScrollBackOffset && d.cbOffsets[1]){_mcs(); o.callbacks.onTotalScrollBack.call(el[0]);}
2154							d.tweenRunning=false;
2155							mCSB_container[0].idleTimer=0;
2156							_onDragClasses(mCSB_dragger,"hide");
2157						},t);
2158					}
2159				}
2160			});
2161			/* checks if callback function exists */
2162			function _cb(cb){
2163				return d && o.callbacks[cb] && typeof o.callbacks[cb]==="function";
2164			}
2165			/* checks whether callback offsets always trigger */
2166			function _cbOffsets(){
2167				return [o.callbacks.alwaysTriggerOffsets || contentPos>=limit[0]+tso,o.callbacks.alwaysTriggerOffsets || contentPos<=-tsbo];
2168			}
2169			/* 
2170			populates object with useful values for the user 
2171			values: 
2172				content: this.mcs.content
2173				content top position: this.mcs.top 
2174				content left position: this.mcs.left 
2175				dragger top position: this.mcs.draggerTop 
2176				dragger left position: this.mcs.draggerLeft 
2177				scrolling y percentage: this.mcs.topPct 
2178				scrolling x percentage: this.mcs.leftPct 
2179				scrolling direction: this.mcs.direction
2180			*/
2181			function _mcs(){
2182				var cp=[mCSB_container[0].offsetTop,mCSB_container[0].offsetLeft], /* content position */
2183					dp=[mCSB_dragger[0].offsetTop,mCSB_dragger[0].offsetLeft], /* dragger position */
2184					cl=[mCSB_container.outerHeight(false),mCSB_container.outerWidth(false)], /* content length */
2185					pl=[mCustomScrollBox.height(),mCustomScrollBox.width()]; /* content parent length */
2186				el[0].mcs={
2187					content:mCSB_container, /* original content wrapper as jquery object */
2188					top:cp[0],left:cp[1],draggerTop:dp[0],draggerLeft:dp[1],
2189					topPct:Math.round((100*Math.abs(cp[0]))/(Math.abs(cl[0])-pl[0])),leftPct:Math.round((100*Math.abs(cp[1]))/(Math.abs(cl[1])-pl[1])),
2190					direction:options.dir
2191				};
2192				/* 
2193				this refers to the original element containing the scrollbar(s)
2194				usage: this.mcs.top, this.mcs.leftPct etc. 
2195				*/
2196			}
2197		},
2198		/* -------------------- */
2199		
2200		
2201		/* 
2202		CUSTOM JAVASCRIPT ANIMATION TWEEN 
2203		Lighter and faster than jquery animate() and css transitions 
2204		Animates top/left properties and includes easings 
2205		*/
2206		_tweenTo=function(el,prop,to,duration,easing,overwrite,callbacks){
2207			if(!el._mTween){el._mTween={top:{},left:{}};}
2208			var callbacks=callbacks || {},
2209				onStart=callbacks.onStart || function(){},onUpdate=callbacks.onUpdate || function(){},onComplete=callbacks.onComplete || function(){},
2210				startTime=_getTime(),_delay,progress=0,from=el.offsetTop,elStyle=el.style,_request,tobj=el._mTween[prop];
2211			if(prop==="left"){from=el.offsetLeft;}
2212			var diff=to-from;
2213			tobj.stop=0;
2214			if(overwrite!=="none"){_cancelTween();}
2215			_startTween();
2216			function _step(){
2217				if(tobj.stop){return;}
2218				if(!progress){onStart.call();}
2219				progress=_getTime()-startTime;
2220				_tween();
2221				if(progress>=tobj.time){
2222					tobj.time=(progress>tobj.time) ? progress+_delay-(progress-tobj.time) : progress+_delay-1;
2223					if(tobj.time<progress+1){tobj.time=progress+1;}
2224				}
2225				if(tobj.time<duration){tobj.id=_request(_step);}else{onComplete.call();}
2226			}
2227			function _tween(){
2228				if(duration>0){
2229					tobj.currVal=_ease(tobj.time,from,diff,duration,easing);
2230					elStyle[prop]=Math.round(tobj.currVal)+"px";
2231				}else{
2232					elStyle[prop]=to+"px";
2233				}
2234				onUpdate.call();
2235			}
2236			function _startTween(){
2237				_delay=1000/60;
2238				tobj.time=progress+_delay;
2239				_request=(!window.requestAnimationFrame) ? function(f){_tween(); return setTimeout(f,0.01);} : window.requestAnimationFrame;
2240				tobj.id=_request(_step);
2241			}
2242			function _cancelTween(){
2243				if(tobj.id==null){return;}
2244				if(!window.requestAnimationFrame){clearTimeout(tobj.id);
2245				}else{window.cancelAnimationFrame(tobj.id);}
2246				tobj.id=null;
2247			}
2248			function _ease(t,b,c,d,type){
2249				switch(type){
2250					case "linear": case "mcsLinear":
2251						return c*t/d + b;
2252						break;
2253					case "mcsLinearOut":
2254						t/=d; t--; return c * Math.sqrt(1 - t*t) + b;
2255						break;
2256					case "easeInOutSmooth":
2257						t/=d/2;
2258						if(t<1) return c/2*t*t + b;
2259						t--;
2260						return -c/2 * (t*(t-2) - 1) + b;
2261						break;
2262					case "easeInOutStrong":
2263						t/=d/2;
2264						if(t<1) return c/2 * Math.pow( 2, 10 * (t - 1) ) + b;
2265						t--;
2266						return c/2 * ( -Math.pow( 2, -10 * t) + 2 ) + b;
2267						break;
2268					case "easeInOut": case "mcsEaseInOut":
2269						t/=d/2;
2270						if(t<1) return c/2*t*t*t + b;
2271						t-=2;
2272						return c/2*(t*t*t + 2) + b;
2273						break;
2274					case "easeOutSmooth":
2275						t/=d; t--;
2276						return -c * (t*t*t*t - 1) + b;
2277						break;
2278					case "easeOutStrong":
2279						return c * ( -Math.pow( 2, -10 * t/d ) + 1 ) + b;
2280						break;
2281					case "easeOut": case "mcsEaseOut": default:
2282						var ts=(t/=d)*t,tc=ts*t;
2283						return b+c*(0.499999999999997*tc*ts + -2.5*ts*ts + 5.5*tc + -6.5*ts + 4*t);
2284				}
2285			}
2286		},
2287		/* -------------------- */
2288		
2289		
2290		/* returns current time */
2291		_getTime=function(){
2292			if(window.performance && window.performance.now){
2293				return window.performance.now();
2294			}else{
2295				if(window.performance && window.performance.webkitNow){
2296					return window.performance.webkitNow();
2297				}else{
2298					if(Date.now){return Date.now();}else{return new Date().getTime();}
2299				}
2300			}
2301		},
2302		/* -------------------- */
2303		
2304		
2305		/* stops a tween */
2306		_stopTween=function(){
2307			var el=this;
2308			if(!el._mTween){el._mTween={top:{},left:{}};}
2309			var props=["top","left"];
2310			for(var i=0; i<props.length; i++){
2311				var prop=props[i];
2312				if(el._mTween[prop].id){
2313					if(!window.requestAnimationFrame){clearTimeout(el._mTween[prop].id);
2314					}else{window.cancelAnimationFrame(el._mTween[prop].id);}
2315					el._mTween[prop].id=null;
2316					el._mTween[prop].stop=1;
2317				}
2318			}
2319		},
2320		/* -------------------- */
2321		
2322		
2323		/* deletes a property (avoiding the exception thrown by IE) */
2324		_delete=function(c,m){
2325			try{delete c[m];}catch(e){c[m]=null;}
2326		},
2327		/* -------------------- */
2328		
2329		
2330		/* detects left mouse button */
2331		_mouseBtnLeft=function(e){
2332			return !(e.which && e.which!==1);
2333		},
2334		/* -------------------- */
2335		
2336		
2337		/* detects if pointer type event is touch */
2338		_pointerTouch=function(e){
2339			var t=e.originalEvent.pointerType;
2340			return !(t && t!=="touch" && t!==2);
2341		},
2342		/* -------------------- */
2343		
2344		
2345		/* checks if value is numeric */
2346		_isNumeric=function(val){
2347			return !isNaN(parseFloat(val)) && isFinite(val);
2348		},
2349		/* -------------------- */
2350		
2351		
2352		/* returns element position according to content */
2353		_childPos=function(el){
2354			var p=el.parents(".mCSB_container");
2355			return [el.offset().top-p.offset().top,el.offset().left-p.offset().left];
2356		},
2357		/* -------------------- */
2358		
2359		
2360		/* checks if browser tab is hidden/inactive via Page Visibility API */
2361		_isTabHidden=function(){
2362			var prop=_getHiddenProp();
2363			if(!prop) return false;
2364			return document[prop];
2365			function _getHiddenProp(){
2366				var pfx=["webkit","moz","ms","o"];
2367				if("hidden" in document) return "hidden"; //natively supported
2368				for(var i=0; i<pfx.length; i++){ //prefixed
2369				    if((pfx[i]+"Hidden") in document) 
2370				        return pfx[i]+"Hidden";
2371				}
2372				return null; //not supported
2373			}
2374		};
2375		/* -------------------- */
2376		
2377	
2378	
2379	
2380	
2381	/* 
2382	----------------------------------------
2383	PLUGIN SETUP 
2384	----------------------------------------
2385	*/
2386	
2387	/* plugin constructor functions */
2388	$.fn[pluginNS]=function(method){ /* usage: $(selector).mCustomScrollbar(); */
2389		if(methods[method]){
2390			return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
2391		}else if(typeof method==="object" || !method){
2392			return methods.init.apply(this,arguments);
2393		}else{
2394			$.error("Method "+method+" does not exist");
2395		}
2396	};
2397	$[pluginNS]=function(method){ /* usage: $.mCustomScrollbar(); */
2398		if(methods[method]){
2399			return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
2400		}else if(typeof method==="object" || !method){
2401			return methods.init.apply(this,arguments);
2402		}else{
2403			$.error("Method "+method+" does not exist");
2404		}
2405	};
2406	
2407	/* 
2408	allow setting plugin default options. 
2409	usage: $.mCustomScrollbar.defaults.scrollInertia=500; 
2410	to apply any changed default options on default selectors (below), use inside document ready fn 
2411	e.g.: $(document).ready(function(){ $.mCustomScrollbar.defaults.scrollInertia=500; });
2412	*/
2413	$[pluginNS].defaults=defaults;
2414	
2415	/* 
2416	add window object (window.mCustomScrollbar) 
2417	usage: if(window.mCustomScrollbar){console.log("custom scrollbar plugin loaded");}
2418	*/
2419	window[pluginNS]=true;
2420	
2421	$(window).bind("load",function(){
2422		
2423		$(defaultSelector)[pluginNS](); /* add scrollbars automatically on default selector */
2424		
2425		/* extend jQuery expressions */
2426		$.extend($.expr[":"],{
2427			/* checks if element is within scrollable viewport */
2428			mcsInView:$.expr[":"].mcsInView || function(el){
2429				var $el=$(el),content=$el.parents(".mCSB_container"),wrapper,cPos;
2430				if(!content.length){return;}
2431				wrapper=content.parent();
2432				cPos=[content[0].offsetTop,content[0].offsetLeft];
2433				return 	cPos[0]+_childPos($el)[0]>=0 && cPos[0]+_childPos($el)[0]<wrapper.height()-$el.outerHeight(false) && 
2434						cPos[1]+_childPos($el)[1]>=0 && cPos[1]+_childPos($el)[1]<wrapper.width()-$el.outerWidth(false);
2435			},
2436			/* checks if element or part of element is in view of scrollable viewport */
2437			mcsInSight:$.expr[":"].mcsInSight || function(el,i,m){
2438				var $el=$(el),elD,content=$el.parents(".mCSB_container"),wrapperView,pos,wrapperViewPct,
2439					pctVals=m[3]==="exact" ? [[1,0],[1,0]] : [[0.9,0.1],[0.6,0.4]];
2440				if(!content.length){return;}
2441				elD=[$el.outerHeight(false),$el.outerWidth(false)];
2442				pos=[content[0].offsetTop+_childPos($el)[0],content[0].offsetLeft+_childPos($el)[1]];
2443				wrapperView=[content.parent()[0].offsetHeight,content.parent()[0].offsetWidth];
2444				wrapperViewPct=[elD[0]<wrapperView[0] ? pctVals[0] : pctVals[1],elD[1]<wrapperView[1] ? pctVals[0] : pctVals[1]];
2445				return 	pos[0]-(wrapperView[0]*wrapperViewPct[0][0])<0 && pos[0]+elD[0]-(wrapperView[0]*wrapperViewPct[0][1])>=0 && 
2446						pos[1]-(wrapperView[1]*wrapperViewPct[1][0])<0 && pos[1]+elD[1]-(wrapperView[1]*wrapperViewPct[1][1])>=0;
2447			},
2448			/* checks if element is overflowed having visible scrollbar(s) */
2449			mcsOverflow:$.expr[":"].mcsOverflow || function(el){
2450				var d=$(el).data(pluginPfx);
2451				if(!d){return;}
2452				return d.overflowed[0] || d.overflowed[1];
2453			}
2454		});
2455	
2456	});
2457
2458}))}));

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