2 == malihu jquery custom scrollbars plugin ==
4 author: malihu (http://manos.malihu.gr)
5 plugin home: http://manos.malihu.gr/jquery-custom-content-scroller
9 init:function(options){
11 set_width:false, /*optional element width: boolean, pixels, percentage*/
12 set_height:false, /*optional element height: boolean, pixels, percentage*/
13 horizontalScroll:false, /*scroll horizontally: boolean*/
14 scrollInertia:550, /*scrolling inertia: integer (milliseconds)*/
15 scrollEasing:"easeOutCirc", /*scrolling easing: string*/
16 mouseWheel:"pixels", /*mousewheel support and velocity: boolean, "auto", integer, "pixels"*/
17 mouseWheelPixels:60, /*mousewheel pixels amount: integer*/
18 autoDraggerLength:true, /*auto-adjust scrollbar dragger length: boolean*/
19 scrollButtons:{ /*scroll buttons*/
20 enable:false, /*scroll buttons support: boolean*/
21 scrollType:"continuous", /*scroll buttons scrolling type: "continuous", "pixels"*/
22 scrollSpeed:20, /*scroll buttons continuous scrolling speed: integer*/
23 scrollAmount:40 /*scroll buttons pixels scroll amount: integer (pixels)*/
26 updateOnBrowserResize:true, /*update scrollbars on browser resize (for layouts based on percentages): boolean*/
27 updateOnContentResize:false, /*auto-update scrollbars on content resize (for dynamic content): boolean*/
28 autoExpandHorizontalScroll:false, /*auto-expand width for horizontal scrolling: boolean*/
29 autoScrollOnFocus:true /*auto-scroll on focused elements: boolean*/
32 onScrollStart:function(){}, /*user custom callback function on scroll start event*/
33 onScroll:function(){}, /*user custom callback function on scroll event*/
34 onTotalScroll:function(){}, /*user custom callback function on scroll end reached event*/
35 onTotalScrollBack:function(){}, /*user custom callback function on scroll begin reached event*/
36 onTotalScrollOffset:0, /*scroll end reached offset: integer (pixels)*/
37 whileScrolling:false, /*user custom callback function on scrolling event*/
38 whileScrollingInterval:30 /*interval for calling whileScrolling callback: integer (milliseconds)*/
41 options=$.extend(true,defaults,options);
42 /*check for touch device*/
43 $(document).data("mCS-is-touch-device",false);
44 if(is_touch_device()){
45 $(document).data("mCS-is-touch-device",true);
47 function is_touch_device(){
48 return !!("ontouchstart" in window) ? 1 : 0;
50 return this.each(function(){
52 /*set element width/height, create markup for custom scrollbars, add classes*/
53 if(options.set_width){
54 $this.css("width",options.set_width);
56 if(options.set_height){
57 $this.css("height",options.set_height);
59 if(!$(document).data("mCustomScrollbar-index")){
60 $(document).data("mCustomScrollbar-index","1");
62 var mCustomScrollbarIndex=parseInt($(document).data("mCustomScrollbar-index"));
63 $(document).data("mCustomScrollbar-index",mCustomScrollbarIndex+1);
65 $this.wrapInner("<div class='mCustomScrollBox' id='mCSB_"+$(document).data("mCustomScrollbar-index")+"' style='position:relative; height:100%; overflow:hidden; max-width:100%;' />").addClass("mCustomScrollbar _mCS_"+$(document).data("mCustomScrollbar-index"));
66 var mCustomScrollBox=$this.children(".mCustomScrollBox");
67 if(options.horizontalScroll){
68 mCustomScrollBox.addClass("mCSB_horizontal").wrapInner("<div class='mCSB_h_wrapper' style='position:relative; left:0; width:999999px;' />");
69 var mCSB_h_wrapper=mCustomScrollBox.children(".mCSB_h_wrapper");
70 mCSB_h_wrapper.wrapInner("<div class='mCSB_container' style='position:absolute; left:0;' />").children(".mCSB_container").css({"width":mCSB_h_wrapper.children().outerWidth(),"position":"relative"}).unwrap();
72 mCustomScrollBox.wrapInner("<div class='mCSB_container' style='position:relative; top:0;' />");
74 var mCSB_container=mCustomScrollBox.children(".mCSB_container");
75 if($(document).data("mCS-is-touch-device")){
76 mCSB_container.addClass("mCS_touch");
78 mCSB_container.after("<div class='mCSB_scrollTools' style='position:absolute;'><div class='mCSB_draggerContainer' style='position:relative;'><div class='mCSB_dragger' style='position:absolute;'><div class='mCSB_dragger_bar' style='position:relative;'></div></div><div class='mCSB_draggerRail'></div></div></div>");
79 var mCSB_scrollTools=mCustomScrollBox.children(".mCSB_scrollTools"),
80 mCSB_draggerContainer=mCSB_scrollTools.children(".mCSB_draggerContainer"),
81 mCSB_dragger=mCSB_draggerContainer.children(".mCSB_dragger");
82 if(options.horizontalScroll){
83 mCSB_dragger.data("minDraggerWidth",mCSB_dragger.width());
85 mCSB_dragger.data("minDraggerHeight",mCSB_dragger.height());
87 if(options.scrollButtons.enable){
88 if(options.horizontalScroll){
89 mCSB_scrollTools.prepend("<a class='mCSB_buttonLeft' style='display:block; position:relative;'></a>").append("<a class='mCSB_buttonRight' style='display:block; position:relative;'></a>");
91 mCSB_scrollTools.prepend("<a class='mCSB_buttonUp' style='display:block; position:relative;'></a>").append("<a class='mCSB_buttonDown' style='display:block; position:relative;'></a>");
94 /*mCustomScrollBox scrollTop and scrollLeft is always 0 to prevent browser focus scrolling*/
95 mCustomScrollBox.bind("scroll",function(){
96 if(!$this.is(".mCS_disabled")){ /*native focus scrolling for disabled scrollbars*/
97 mCustomScrollBox.scrollTop(0).scrollLeft(0);
100 /*store options, global vars/states, intervals and update element*/
104 /*option parameters*/
105 "horizontalScroll":options.horizontalScroll,
106 "scrollInertia":options.scrollInertia,
107 "scrollEasing":options.scrollEasing,
108 "mouseWheel":options.mouseWheel,
109 "mouseWheelPixels":options.mouseWheelPixels,
110 "autoDraggerLength":options.autoDraggerLength,
111 "scrollButtons_enable":options.scrollButtons.enable,
112 "scrollButtons_scrollType":options.scrollButtons.scrollType,
113 "scrollButtons_scrollSpeed":options.scrollButtons.scrollSpeed,
114 "scrollButtons_scrollAmount":options.scrollButtons.scrollAmount,
115 "autoExpandHorizontalScroll":options.advanced.autoExpandHorizontalScroll,
116 "autoScrollOnFocus":options.advanced.autoScrollOnFocus,
117 "onScrollStart_Callback":options.callbacks.onScrollStart,
118 "onScroll_Callback":options.callbacks.onScroll,
119 "onTotalScroll_Callback":options.callbacks.onTotalScroll,
120 "onTotalScrollBack_Callback":options.callbacks.onTotalScrollBack,
121 "onTotalScroll_Offset":options.callbacks.onTotalScrollOffset,
122 "whileScrolling_Callback":options.callbacks.whileScrolling,
123 "whileScrolling_Interval":options.callbacks.whileScrollingInterval,
124 /*events binding state*/
125 "bindEvent_scrollbar_click":false,
126 "bindEvent_mousewheel":false,
127 "bindEvent_focusin":false,
128 "bindEvent_buttonsContinuous_y":false,
129 "bindEvent_buttonsContinuous_x":false,
130 "bindEvent_buttonsPixels_y":false,
131 "bindEvent_buttonsPixels_x":false,
132 "bindEvent_scrollbar_touch":false,
133 "bindEvent_content_touch":false,
134 /*buttons intervals*/
135 "mCSB_buttonScrollRight":false,
136 "mCSB_buttonScrollLeft":false,
137 "mCSB_buttonScrollDown":false,
138 "mCSB_buttonScrollUp":false,
139 /*callback intervals*/
140 "whileScrolling":false
141 }).mCustomScrollbar("update");
143 if(options.horizontalScroll){
144 if($this.css("max-width")!=="none"){
145 if(!options.advanced.updateOnContentResize){ /*needs updateOnContentResize*/
146 options.advanced.updateOnContentResize=true;
148 $this.data({"mCS_maxWidth":parseInt($this.css("max-width")),"mCS_maxWidth_Interval":setInterval(function(){
149 if(parseInt($this.css("width"))>$this.data("mCS_maxWidth")){
150 clearInterval($this.data("mCS_maxWidth_Interval"));
151 $this.mCustomScrollbar("update");
156 /*detect max-height*/
157 if($this.css("max-height")!=="none"){
158 $this.data({"mCS_maxHeight":parseInt($this.css("max-height")),"mCS_maxHeight_Interval":setInterval(function(){
159 mCustomScrollBox.css("max-height",$this.data("mCS_maxHeight"));
160 if(parseInt($this.css("height"))>$this.data("mCS_maxHeight")){
161 clearInterval($this.data("mCS_maxHeight_Interval"));
162 $this.mCustomScrollbar("update");
167 /*window resize fn (for layouts based on percentages)*/
168 if(options.advanced.updateOnBrowserResize){
169 var mCSB_resizeTimeout;
170 $(window).resize(function(){
171 if(mCSB_resizeTimeout){
172 clearTimeout(mCSB_resizeTimeout);
174 mCSB_resizeTimeout=setTimeout(function(){
175 if(!$this.is(".mCS_disabled") && !$this.is(".mCS_destroyed")){
176 $this.mCustomScrollbar("update");
181 /*content resize fn (for dynamically generated content)*/
182 if(options.advanced.updateOnContentResize){
183 var mCSB_onContentResize;
184 if(options.horizontalScroll){
185 var mCSB_containerOldSize=mCSB_container.outerWidth();
187 var mCSB_containerOldSize=mCSB_container.outerHeight();
189 mCSB_onContentResize=setInterval(function(){
190 if(options.horizontalScroll){
191 if(options.advanced.autoExpandHorizontalScroll){
192 mCSB_container.css({"position":"absolute","width":"auto"}).wrap("<div class='mCSB_h_wrapper' style='position:relative; left:0; width:999999px;' />").css({"width":mCSB_container.outerWidth(),"position":"relative"}).unwrap();
194 var mCSB_containerNewSize=mCSB_container.outerWidth();
196 var mCSB_containerNewSize=mCSB_container.outerHeight();
198 if(mCSB_containerNewSize!=mCSB_containerOldSize){
199 $this.mCustomScrollbar("update");
200 mCSB_containerOldSize=mCSB_containerNewSize;
208 mCustomScrollBox=$this.children(".mCustomScrollBox"),
209 mCSB_container=mCustomScrollBox.children(".mCSB_container");
210 mCSB_container.removeClass("mCS_no_scrollbar");
211 $this.removeClass("mCS_disabled mCS_destroyed");
212 mCustomScrollBox.scrollTop(0).scrollLeft(0); /*reset scrollTop/scrollLeft to prevent browser focus scrolling*/
213 var mCSB_scrollTools=mCustomScrollBox.children(".mCSB_scrollTools"),
214 mCSB_draggerContainer=mCSB_scrollTools.children(".mCSB_draggerContainer"),
215 mCSB_dragger=mCSB_draggerContainer.children(".mCSB_dragger");
216 if($this.data("horizontalScroll")){
217 var mCSB_buttonLeft=mCSB_scrollTools.children(".mCSB_buttonLeft"),
218 mCSB_buttonRight=mCSB_scrollTools.children(".mCSB_buttonRight"),
219 mCustomScrollBoxW=mCustomScrollBox.width();
220 if($this.data("autoExpandHorizontalScroll")){
221 mCSB_container.css({"position":"absolute","width":"auto"}).wrap("<div class='mCSB_h_wrapper' style='position:relative; left:0; width:999999px;' />").css({"width":mCSB_container.outerWidth(),"position":"relative"}).unwrap();
223 var mCSB_containerW=mCSB_container.outerWidth();
225 var mCSB_buttonUp=mCSB_scrollTools.children(".mCSB_buttonUp"),
226 mCSB_buttonDown=mCSB_scrollTools.children(".mCSB_buttonDown"),
227 mCustomScrollBoxH=mCustomScrollBox.height(),
228 mCSB_containerH=mCSB_container.outerHeight();
230 if(mCSB_containerH>mCustomScrollBoxH && !$this.data("horizontalScroll")){ /*content needs vertical scrolling*/
231 mCSB_scrollTools.css("display","block");
232 var mCSB_draggerContainerH=mCSB_draggerContainer.height();
233 /*auto adjust scrollbar dragger length analogous to content*/
234 if($this.data("autoDraggerLength")){
235 var draggerH=Math.round(mCustomScrollBoxH/mCSB_containerH*mCSB_draggerContainerH),
236 minDraggerH=mCSB_dragger.data("minDraggerHeight");
237 if(draggerH<=minDraggerH){ /*min dragger height*/
238 mCSB_dragger.css({"height":minDraggerH});
239 }else if(draggerH>=mCSB_draggerContainerH-10){ /*max dragger height*/
240 var mCSB_draggerContainerMaxH=mCSB_draggerContainerH-10;
241 mCSB_dragger.css({"height":mCSB_draggerContainerMaxH});
243 mCSB_dragger.css({"height":draggerH});
245 mCSB_dragger.children(".mCSB_dragger_bar").css({"line-height":mCSB_dragger.height()+"px"});
247 var mCSB_draggerH=mCSB_dragger.height(),
248 /*calculate and store scroll amount, add scrolling*/
249 scrollAmount=(mCSB_containerH-mCustomScrollBoxH)/(mCSB_draggerContainerH-mCSB_draggerH);
250 $this.data("scrollAmount",scrollAmount).mCustomScrollbar("scrolling",mCustomScrollBox,mCSB_container,mCSB_draggerContainer,mCSB_dragger,mCSB_buttonUp,mCSB_buttonDown,mCSB_buttonLeft,mCSB_buttonRight);
252 var mCSB_containerP=Math.abs(Math.round(mCSB_container.position().top));
253 $this.mCustomScrollbar("scrollTo",mCSB_containerP,{callback:false});
254 }else if(mCSB_containerW>mCustomScrollBoxW && $this.data("horizontalScroll")){ /*content needs horizontal scrolling*/
255 mCSB_scrollTools.css("display","block");
256 var mCSB_draggerContainerW=mCSB_draggerContainer.width();
257 /*auto adjust scrollbar dragger length analogous to content*/
258 if($this.data("autoDraggerLength")){
259 var draggerW=Math.round(mCustomScrollBoxW/mCSB_containerW*mCSB_draggerContainerW),
260 minDraggerW=mCSB_dragger.data("minDraggerWidth");
261 if(draggerW<=minDraggerW){ /*min dragger height*/
262 mCSB_dragger.css({"width":minDraggerW});
263 }else if(draggerW>=mCSB_draggerContainerW-10){ /*max dragger height*/
264 var mCSB_draggerContainerMaxW=mCSB_draggerContainerW-10;
265 mCSB_dragger.css({"width":mCSB_draggerContainerMaxW});
267 mCSB_dragger.css({"width":draggerW});
270 var mCSB_draggerW=mCSB_dragger.width(),
271 /*calculate and store scroll amount, add scrolling*/
272 scrollAmount=(mCSB_containerW-mCustomScrollBoxW)/(mCSB_draggerContainerW-mCSB_draggerW);
273 $this.data("scrollAmount",scrollAmount).mCustomScrollbar("scrolling",mCustomScrollBox,mCSB_container,mCSB_draggerContainer,mCSB_dragger,mCSB_buttonUp,mCSB_buttonDown,mCSB_buttonLeft,mCSB_buttonRight);
275 var mCSB_containerP=Math.abs(Math.round(mCSB_container.position().left));
276 $this.mCustomScrollbar("scrollTo",mCSB_containerP,{callback:false});
277 }else{ /*content does not need scrolling*/
278 /*unbind events, reset content position, hide scrollbars, remove classes*/
279 mCustomScrollBox.unbind("mousewheel focusin");
280 if($this.data("horizontalScroll")){
281 mCSB_dragger.add(mCSB_container).css("left",0);
283 mCSB_dragger.add(mCSB_container).css("top",0);
285 mCSB_scrollTools.css("display","none");
286 mCSB_container.addClass("mCS_no_scrollbar");
287 $this.data({"bindEvent_mousewheel":false,"bindEvent_focusin":false});
290 scrolling:function(mCustomScrollBox,mCSB_container,mCSB_draggerContainer,mCSB_dragger,mCSB_buttonUp,mCSB_buttonDown,mCSB_buttonLeft,mCSB_buttonRight){
292 /*while scrolling callback*/
293 $this.mCustomScrollbar("callbacks","whileScrolling");
295 if(!mCSB_dragger.hasClass("ui-draggable")){ /*apply drag function once*/
296 if($this.data("horizontalScroll")){
297 var draggableAxis="x";
299 var draggableAxis="y";
301 mCSB_dragger.draggable({
303 containment:"parent",
304 drag:function(event,ui){
305 $this.mCustomScrollbar("scroll");
306 mCSB_dragger.addClass("mCSB_dragger_onDrag");
308 stop:function(event,ui){
309 mCSB_dragger.removeClass("mCSB_dragger_onDrag");
313 if(!$this.data("bindEvent_scrollbar_click")){ /*bind once*/
314 mCSB_draggerContainer.bind("click",function(e){
315 if($this.data("horizontalScroll")){
316 var mouseCoord=(e.pageX-mCSB_draggerContainer.offset().left);
317 if(mouseCoord<mCSB_dragger.position().left || mouseCoord>(mCSB_dragger.position().left+mCSB_dragger.width())){
318 var scrollToPos=mouseCoord;
319 if(scrollToPos>=mCSB_draggerContainer.width()-mCSB_dragger.width()){ /*max dragger position is bottom*/
320 scrollToPos=mCSB_draggerContainer.width()-mCSB_dragger.width();
322 mCSB_dragger.css("left",scrollToPos);
323 $this.mCustomScrollbar("scroll");
326 var mouseCoord=(e.pageY-mCSB_draggerContainer.offset().top);
327 if(mouseCoord<mCSB_dragger.position().top || mouseCoord>(mCSB_dragger.position().top+mCSB_dragger.height())){
328 var scrollToPos=mouseCoord;
329 if(scrollToPos>=mCSB_draggerContainer.height()-mCSB_dragger.height()){ /*max dragger position is bottom*/
330 scrollToPos=mCSB_draggerContainer.height()-mCSB_dragger.height();
332 mCSB_dragger.css("top",scrollToPos);
333 $this.mCustomScrollbar("scroll");
337 $this.data({"bindEvent_scrollbar_click":true});
339 /*mousewheel scrolling*/
340 if($this.data("mouseWheel")){
341 var mousewheelVel=$this.data("mouseWheel");
342 if($this.data("mouseWheel")==="auto"){
343 mousewheelVel=8; /*default mousewheel velocity*/
344 /*check for safari browser on mac osx to lower mousewheel velocity*/
345 var os=navigator.userAgent;
346 if(os.indexOf("Mac")!=-1 && os.indexOf("Safari")!=-1 && os.indexOf("AppleWebKit")!=-1 && os.indexOf("Chrome")==-1){
350 if(!$this.data("bindEvent_mousewheel")){ /*bind once*/
351 mCustomScrollBox.bind("mousewheel",function(event,delta){
352 event.preventDefault();
353 var vel=Math.abs(delta*mousewheelVel);
354 if($this.data("horizontalScroll")){
355 if($this.data("mouseWheel")==="pixels"){
361 var scrollTo=Math.abs(Math.round(mCSB_container.position().left))-(delta*$this.data("mouseWheelPixels"));
362 $this.mCustomScrollbar("scrollTo",scrollTo);
364 var posX=mCSB_dragger.position().left-(delta*vel);
365 mCSB_dragger.css("left",posX);
366 if(mCSB_dragger.position().left<0){
367 mCSB_dragger.css("left",0);
369 var mCSB_draggerContainerW=mCSB_draggerContainer.width(),
370 mCSB_draggerW=mCSB_dragger.width();
371 if(mCSB_dragger.position().left>mCSB_draggerContainerW-mCSB_draggerW){
372 mCSB_dragger.css("left",mCSB_draggerContainerW-mCSB_draggerW);
374 $this.mCustomScrollbar("scroll");
377 if($this.data("mouseWheel")==="pixels"){
383 var scrollTo=Math.abs(Math.round(mCSB_container.position().top))-(delta*$this.data("mouseWheelPixels"));
384 $this.mCustomScrollbar("scrollTo",scrollTo);
386 var posY=mCSB_dragger.position().top-(delta*vel);
387 mCSB_dragger.css("top",posY);
388 if(mCSB_dragger.position().top<0){
389 mCSB_dragger.css("top",0);
391 var mCSB_draggerContainerH=mCSB_draggerContainer.height(),
392 mCSB_draggerH=mCSB_dragger.height();
393 if(mCSB_dragger.position().top>mCSB_draggerContainerH-mCSB_draggerH){
394 mCSB_dragger.css("top",mCSB_draggerContainerH-mCSB_draggerH);
396 $this.mCustomScrollbar("scroll");
400 $this.data({"bindEvent_mousewheel":true});
403 /*buttons scrolling*/
404 if($this.data("scrollButtons_enable")){
405 if($this.data("scrollButtons_scrollType")==="pixels"){ /*scroll by pixels*/
407 if($.browser.msie && parseInt($.browser.version)<9){ /*stupid ie8*/
408 $this.data("scrollInertia",0);
410 if($this.data("horizontalScroll")){
411 mCSB_buttonRight.add(mCSB_buttonLeft).unbind("mousedown touchstart onmsgesturestart mouseup mouseout touchend onmsgestureend",mCSB_buttonRight_stop,mCSB_buttonLeft_stop);
412 $this.data({"bindEvent_buttonsContinuous_x":false});
413 if(!$this.data("bindEvent_buttonsPixels_x")){ /*bind once*/
415 mCSB_buttonRight.bind("click",function(e){
417 if(!mCSB_container.is(":animated")){
418 pixelsScrollTo=Math.abs(mCSB_container.position().left)+$this.data("scrollButtons_scrollAmount");
419 $this.mCustomScrollbar("scrollTo",pixelsScrollTo);
423 mCSB_buttonLeft.bind("click",function(e){
425 if(!mCSB_container.is(":animated")){
426 pixelsScrollTo=Math.abs(mCSB_container.position().left)-$this.data("scrollButtons_scrollAmount");
427 if(mCSB_container.position().left>=-$this.data("scrollButtons_scrollAmount")){
428 pixelsScrollTo="left";
430 $this.mCustomScrollbar("scrollTo",pixelsScrollTo);
433 $this.data({"bindEvent_buttonsPixels_x":true});
436 mCSB_buttonDown.add(mCSB_buttonUp).unbind("mousedown touchstart onmsgesturestart mouseup mouseout touchend onmsgestureend",mCSB_buttonRight_stop,mCSB_buttonLeft_stop);
437 $this.data({"bindEvent_buttonsContinuous_y":false});
438 if(!$this.data("bindEvent_buttonsPixels_y")){ /*bind once*/
440 mCSB_buttonDown.bind("click",function(e){
442 if(!mCSB_container.is(":animated")){
443 pixelsScrollTo=Math.abs(mCSB_container.position().top)+$this.data("scrollButtons_scrollAmount");
444 $this.mCustomScrollbar("scrollTo",pixelsScrollTo);
448 mCSB_buttonUp.bind("click",function(e){
450 if(!mCSB_container.is(":animated")){
451 pixelsScrollTo=Math.abs(mCSB_container.position().top)-$this.data("scrollButtons_scrollAmount");
452 if(mCSB_container.position().top>=-$this.data("scrollButtons_scrollAmount")){
453 pixelsScrollTo="top";
455 $this.mCustomScrollbar("scrollTo",pixelsScrollTo);
458 $this.data({"bindEvent_buttonsPixels_y":true});
461 }else{ /*continuous scrolling*/
462 if($this.data("horizontalScroll")){
463 mCSB_buttonRight.add(mCSB_buttonLeft).unbind("click");
464 $this.data({"bindEvent_buttonsPixels_x":false});
465 if(!$this.data("bindEvent_buttonsContinuous_x")){ /*bind once*/
467 mCSB_buttonRight.bind("mousedown touchstart onmsgesturestart",function(e){
470 $this.data({"mCSB_buttonScrollRight":setInterval(function(){
471 var scrollTo=Math.round((Math.abs(Math.round(mCSB_container.position().left))+$this.data("scrollButtons_scrollSpeed"))/$this.data("scrollAmount"));
472 $this.mCustomScrollbar("scrollTo",scrollTo,{moveDragger:true});
475 var mCSB_buttonRight_stop=function(e){
478 clearInterval($this.data("mCSB_buttonScrollRight"));
480 mCSB_buttonRight.bind("mouseup touchend onmsgestureend mouseout",mCSB_buttonRight_stop);
482 mCSB_buttonLeft.bind("mousedown touchstart onmsgesturestart",function(e){
485 $this.data({"mCSB_buttonScrollLeft":setInterval(function(){
486 var scrollTo=Math.round((Math.abs(Math.round(mCSB_container.position().left))-$this.data("scrollButtons_scrollSpeed"))/$this.data("scrollAmount"));
487 $this.mCustomScrollbar("scrollTo",scrollTo,{moveDragger:true});
490 var mCSB_buttonLeft_stop=function(e){
493 clearInterval($this.data("mCSB_buttonScrollLeft"));
495 mCSB_buttonLeft.bind("mouseup touchend onmsgestureend mouseout",mCSB_buttonLeft_stop);
496 $this.data({"bindEvent_buttonsContinuous_x":true});
499 mCSB_buttonDown.add(mCSB_buttonUp).unbind("click");
500 $this.data({"bindEvent_buttonsPixels_y":false});
501 if(!$this.data("bindEvent_buttonsContinuous_y")){ /*bind once*/
503 mCSB_buttonDown.bind("mousedown touchstart onmsgesturestart",function(e){
506 $this.data({"mCSB_buttonScrollDown":setInterval(function(){
507 var scrollTo=Math.round((Math.abs(Math.round(mCSB_container.position().top))+$this.data("scrollButtons_scrollSpeed"))/$this.data("scrollAmount"));
508 $this.mCustomScrollbar("scrollTo",scrollTo,{moveDragger:true});
511 var mCSB_buttonDown_stop=function(e){
514 clearInterval($this.data("mCSB_buttonScrollDown"));
516 mCSB_buttonDown.bind("mouseup touchend onmsgestureend mouseout",mCSB_buttonDown_stop);
518 mCSB_buttonUp.bind("mousedown touchstart onmsgesturestart",function(e){
521 $this.data({"mCSB_buttonScrollUp":setInterval(function(){
522 var scrollTo=Math.round((Math.abs(Math.round(mCSB_container.position().top))-$this.data("scrollButtons_scrollSpeed"))/$this.data("scrollAmount"));
523 $this.mCustomScrollbar("scrollTo",scrollTo,{moveDragger:true});
526 var mCSB_buttonUp_stop=function(e){
529 clearInterval($this.data("mCSB_buttonScrollUp"));
531 mCSB_buttonUp.bind("mouseup touchend onmsgestureend mouseout",mCSB_buttonUp_stop);
532 $this.data({"bindEvent_buttonsContinuous_y":true});
537 /*scrolling on element focus (e.g. via TAB key)*/
538 if($this.data("autoScrollOnFocus")){
539 if(!$this.data("bindEvent_focusin")){ /*bind once*/
540 mCustomScrollBox.bind("focusin",function(){
541 mCustomScrollBox.scrollTop(0).scrollLeft(0);
542 var focusedElem=$(document.activeElement);
543 if(focusedElem.is("input,textarea,select,button,a[tabindex],area,object")){
544 if($this.data("horizontalScroll")){
545 var mCSB_containerX=mCSB_container.position().left,
546 focusedElemX=focusedElem.position().left,
547 mCustomScrollBoxW=mCustomScrollBox.width(),
548 focusedElemW=focusedElem.outerWidth();
549 if(mCSB_containerX+focusedElemX>=0 && mCSB_containerX+focusedElemX<=mCustomScrollBoxW-focusedElemW){
551 }else{ /*scroll, then focus*/
552 var moveDragger=focusedElemX/$this.data("scrollAmount");
553 if(moveDragger>=mCSB_draggerContainer.width()-mCSB_dragger.width()){ /*max dragger position is bottom*/
554 moveDragger=mCSB_draggerContainer.width()-mCSB_dragger.width();
556 mCSB_dragger.css("left",moveDragger);
557 $this.mCustomScrollbar("scroll");
560 var mCSB_containerY=mCSB_container.position().top,
561 focusedElemY=focusedElem.position().top,
562 mCustomScrollBoxH=mCustomScrollBox.height(),
563 focusedElemH=focusedElem.outerHeight();
564 if(mCSB_containerY+focusedElemY>=0 && mCSB_containerY+focusedElemY<=mCustomScrollBoxH-focusedElemH){
566 }else{ /*scroll, then focus*/
567 var moveDragger=focusedElemY/$this.data("scrollAmount");
568 if(moveDragger>=mCSB_draggerContainer.height()-mCSB_dragger.height()){ /*max dragger position is bottom*/
569 moveDragger=mCSB_draggerContainer.height()-mCSB_dragger.height();
571 mCSB_dragger.css("top",moveDragger);
572 $this.mCustomScrollbar("scroll");
577 $this.data({"bindEvent_focusin":true});
581 if($(document).data("mCS-is-touch-device")){
582 /*scrollbar touch-drag*/
583 if(!$this.data("bindEvent_scrollbar_touch")){ /*bind once*/
584 var mCSB_draggerTouchY,
586 mCSB_dragger.bind("touchstart onmsgesturestart",function(e){
589 var touch=e.originalEvent.touches[0] || e.originalEvent.changedTouches[0],
591 elemOffset=elem.offset(),
592 x=touch.pageX-elemOffset.left,
593 y=touch.pageY-elemOffset.top;
594 if(x<elem.width() && x>0 && y<elem.height() && y>0){
595 mCSB_draggerTouchY=y;
596 mCSB_draggerTouchX=x;
599 mCSB_dragger.bind("touchmove onmsgesturechange",function(e){
602 var touch=e.originalEvent.touches[0] || e.originalEvent.changedTouches[0],
604 elemOffset=elem.offset(),
605 x=touch.pageX-elemOffset.left,
606 y=touch.pageY-elemOffset.top;
607 if($this.data("horizontalScroll")){
608 $this.mCustomScrollbar("scrollTo",(mCSB_dragger.position().left-(mCSB_draggerTouchX))+x,{moveDragger:true});
610 $this.mCustomScrollbar("scrollTo",(mCSB_dragger.position().top-(mCSB_draggerTouchY))+y,{moveDragger:true});
613 $this.data({"bindEvent_scrollbar_touch":true});
615 /*content touch-drag*/
616 if(!$this.data("bindEvent_content_touch")){ /*bind once*/
622 mCSB_containerTouchY,
623 mCSB_containerTouchX;
624 mCSB_container.bind("touchstart onmsgesturestart",function(e){
625 touch=e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
627 elemOffset=elem.offset();
628 x=touch.pageX-elemOffset.left;
629 y=touch.pageY-elemOffset.top;
630 mCSB_containerTouchY=y;
631 mCSB_containerTouchX=x;
633 mCSB_container.bind("touchmove onmsgesturechange",function(e){
636 touch=e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
637 elem=$(this).parent();
638 elemOffset=elem.offset();
639 x=touch.pageX-elemOffset.left;
640 y=touch.pageY-elemOffset.top;
641 if($this.data("horizontalScroll")){
642 $this.mCustomScrollbar("scrollTo",mCSB_containerTouchX-x);
644 $this.mCustomScrollbar("scrollTo",mCSB_containerTouchY-y);
647 $this.data({"bindEvent_content_touch":true});
651 scroll:function(bypassCallbacks){
653 mCSB_dragger=$this.find(".mCSB_dragger"),
654 mCSB_container=$this.find(".mCSB_container"),
655 mCustomScrollBox=$this.find(".mCustomScrollBox");
656 if($this.data("horizontalScroll")){
657 var draggerX=mCSB_dragger.position().left,
658 targX=-draggerX*$this.data("scrollAmount"),
659 thisX=mCSB_container.position().left,
660 posX=Math.round(thisX-targX);
662 var draggerY=mCSB_dragger.position().top,
663 targY=-draggerY*$this.data("scrollAmount"),
664 thisY=mCSB_container.position().top,
665 posY=Math.round(thisY-targY);
667 if($.browser.webkit){ /*fix webkit zoom and jquery animate*/
668 var screenCssPixelRatio=(window.outerWidth-8)/window.innerWidth,
669 isZoomed=(screenCssPixelRatio<.98 || screenCssPixelRatio>1.02);
671 if($this.data("scrollInertia")===0 || isZoomed){
672 if(!bypassCallbacks){
673 $this.mCustomScrollbar("callbacks","onScrollStart"); /*user custom callback functions*/
675 if($this.data("horizontalScroll")){
676 mCSB_container.css("left",targX);
678 mCSB_container.css("top",targY);
680 if(!bypassCallbacks){
681 /*user custom callback functions*/
682 if($this.data("whileScrolling")){
683 $this.data("whileScrolling_Callback").call();
685 $this.mCustomScrollbar("callbacks","onScroll");
687 $this.data({"mCS_Init":false});
689 if(!bypassCallbacks){
690 $this.mCustomScrollbar("callbacks","onScrollStart"); /*user custom callback functions*/
692 if($this.data("horizontalScroll")){
693 mCSB_container.stop().animate({left:"-="+posX},$this.data("scrollInertia"),$this.data("scrollEasing"),function(){
694 if(!bypassCallbacks){
695 $this.mCustomScrollbar("callbacks","onScroll"); /*user custom callback functions*/
697 $this.data({"mCS_Init":false});
700 mCSB_container.stop().animate({top:"-="+posY},$this.data("scrollInertia"),$this.data("scrollEasing"),function(){
701 if(!bypassCallbacks){
702 $this.mCustomScrollbar("callbacks","onScroll"); /*user custom callback functions*/
704 $this.data({"mCS_Init":false});
709 scrollTo:function(scrollTo,options){
714 options=$.extend(defaults,options),
717 mCustomScrollBox=$this.find(".mCustomScrollBox"),
718 mCSB_container=mCustomScrollBox.children(".mCSB_container"),
719 mCSB_draggerContainer=$this.find(".mCSB_draggerContainer"),
720 mCSB_dragger=mCSB_draggerContainer.children(".mCSB_dragger"),
722 if(scrollTo || scrollTo===0){
723 if(typeof(scrollTo)==="number"){ /*if integer, scroll by number of pixels*/
724 if(options.moveDragger){ /*scroll dragger*/
725 scrollToPos=scrollTo;
726 }else{ /*scroll content by default*/
728 scrollToPos=Math.round(targetPos/$this.data("scrollAmount"));
730 }else if(typeof(scrollTo)==="string"){ /*if string, scroll by element position*/
732 if(scrollTo==="top"){ /*scroll to top*/
734 }else if(scrollTo==="bottom" && !$this.data("horizontalScroll")){ /*scroll to bottom*/
735 target=mCSB_container.outerHeight()-mCustomScrollBox.height();
736 }else if(scrollTo==="left"){ /*scroll to left*/
738 }else if(scrollTo==="right" && $this.data("horizontalScroll")){ /*scroll to right*/
739 target=mCSB_container.outerWidth()-mCustomScrollBox.width();
740 }else if(scrollTo==="first"){ /*scroll to first element position*/
741 target=$this.find(".mCSB_container").find(":first");
742 }else if(scrollTo==="last"){ /*scroll to last element position*/
743 target=$this.find(".mCSB_container").find(":last");
744 }else{ /*scroll to element position*/
745 target=$this.find(scrollTo);
747 if(target.length===1){ /*if such unique element exists, scroll to it*/
748 if($this.data("horizontalScroll")){
749 targetPos=target.position().left;
751 targetPos=target.position().top;
753 scrollToPos=Math.ceil(targetPos/$this.data("scrollAmount"));
762 if($this.data("horizontalScroll")){
763 if(scrollToPos>=mCSB_draggerContainer.width()-mCSB_dragger.width()){ /*max dragger position is bottom*/
764 scrollToPos=mCSB_draggerContainer.width()-mCSB_dragger.width();
766 mCSB_dragger.css("left",scrollToPos);
768 if(scrollToPos>=mCSB_draggerContainer.height()-mCSB_dragger.height()){ /*max dragger position is bottom*/
769 scrollToPos=mCSB_draggerContainer.height()-mCSB_dragger.height();
771 mCSB_dragger.css("top",scrollToPos);
773 if(options.callback){
774 $this.mCustomScrollbar("scroll",false);
776 $this.mCustomScrollbar("scroll",true);
780 callbacks:function(callback){
782 mCustomScrollBox=$this.find(".mCustomScrollBox"),
783 mCSB_container=$this.find(".mCSB_container");
785 /*start scrolling callback*/
786 case "onScrollStart":
787 if(!mCSB_container.is(":animated")){
788 $this.data("onScrollStart_Callback").call();
791 /*end scrolling callback*/
793 if($this.data("horizontalScroll")){
794 var mCSB_containerX=Math.round(mCSB_container.position().left);
795 if(mCSB_containerX<0 && mCSB_containerX<=mCustomScrollBox.width()-mCSB_container.outerWidth()+$this.data("onTotalScroll_Offset")){
796 $this.data("onTotalScroll_Callback").call();
797 }else if(mCSB_containerX>=-$this.data("onTotalScroll_Offset")){
798 $this.data("onTotalScrollBack_Callback").call();
800 $this.data("onScroll_Callback").call();
803 var mCSB_containerY=Math.round(mCSB_container.position().top);
804 if(mCSB_containerY<0 && mCSB_containerY<=mCustomScrollBox.height()-mCSB_container.outerHeight()+$this.data("onTotalScroll_Offset")){
805 $this.data("onTotalScroll_Callback").call();
806 }else if(mCSB_containerY>=-$this.data("onTotalScroll_Offset")){
807 $this.data("onTotalScrollBack_Callback").call();
809 $this.data("onScroll_Callback").call();
813 /*while scrolling callback*/
814 case "whileScrolling":
815 if($this.data("whileScrolling_Callback") && !$this.data("whileScrolling")){
816 $this.data({"whileScrolling":setInterval(function(){
817 if(mCSB_container.is(":animated") && !$this.data("mCS_Init")){
818 $this.data("whileScrolling_Callback").call();
820 },$this.data("whileScrolling_Interval"))});
825 disable:function(resetScroll){
827 mCustomScrollBox=$this.children(".mCustomScrollBox"),
828 mCSB_container=mCustomScrollBox.children(".mCSB_container"),
829 mCSB_scrollTools=mCustomScrollBox.children(".mCSB_scrollTools"),
830 mCSB_dragger=mCSB_scrollTools.find(".mCSB_dragger");
831 mCustomScrollBox.unbind("mousewheel focusin");
833 if($this.data("horizontalScroll")){
834 mCSB_dragger.add(mCSB_container).css("left",0);
836 mCSB_dragger.add(mCSB_container).css("top",0);
839 mCSB_scrollTools.css("display","none");
840 mCSB_container.addClass("mCS_no_scrollbar");
841 $this.data({"bindEvent_mousewheel":false,"bindEvent_focusin":false}).addClass("mCS_disabled");
845 content=$this.find(".mCSB_container").html();
846 $this.find(".mCustomScrollBox").remove();
847 $this.html(content).removeClass("mCustomScrollbar _mCS_"+$(document).data("mCustomScrollbar-index")).addClass("mCS_destroyed");
850 $.fn.mCustomScrollbar=function(method){
852 return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
853 }else if(typeof method==="object" || !method){
854 return methods.init.apply(this,arguments);
856 $.error("Method "+method+" does not exist");
861 iOS 6 suffers from a bug that kills timers that are created while a page is scrolling.
862 The following fixes that problem by recreating timers after scrolling finishes (with interval correction).*/
863 var iOSVersion=iOSVersion();
865 (function(h){var a={};var d={};var e=h.setTimeout;var f=h.setInterval;var i=h.clearTimeout;var c=h.clearInterval;if(!h.addEventListener){return false}function j(q,n,l){var p,k=l[0],m=(q===f);function o(){if(k){k.apply(h,arguments);if(!m){delete n[p];k=null}}}l[0]=o;p=q.apply(h,l);n[p]={args:l,created:Date.now(),cb:k,id:p};return p}function b(q,o,k,r,t){var l=k[r];if(!l){return}var m=(q===f);o(l.id);if(!m){var n=l.args[1];var p=Date.now()-l.created;if(p<0){p=0}n-=p;if(n<0){n=0}l.args[1]=n}function s(){if(l.cb){l.cb.apply(h,arguments);if(!m){delete k[r];l.cb=null}}}l.args[0]=s;l.created=Date.now();l.id=q.apply(h,l.args)}h.setTimeout=function(){return j(e,a,arguments)};h.setInterval=function(){return j(f,d,arguments)};h.clearTimeout=function(l){var k=a[l];if(k){delete a[l];i(k.id)}};h.clearInterval=function(l){var k=d[l];if(k){delete d[l];c(k.id)}};var g=h;while(g.location!=g.parent.location){g=g.parent}g.addEventListener("scroll",function(){var k;for(k in a){b(e,i,a,k)}for(k in d){b(f,c,d,k)}})}(window));
867 function iOSVersion(){
868 var agent=window.navigator.userAgent,
869 start=agent.indexOf('OS ');
870 if((agent.indexOf('iPhone')>-1 || agent.indexOf('iPad')>-1) && start>-1){
871 return window.Number(agent.substr(start+3,3).replace('_','.'));