1 /*! Copyright (c) 2011 Piotr Rochala (http://rocha.la)
2 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
3 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
11 slimScroll: function(options) {
15 // width in pixels of the visible scroll area
18 // height in pixels of the visible scroll area
21 // width in pixels of the scrollbar and rail
24 // scrollbar color, accepts any hex/color value
27 // scrollbar position - left/right
30 // distance in pixels between the side edge and the scrollbar
33 // default scroll position on load - top / bottom / $('selector')
36 // sets scrollbar opacity
39 // enables always-on mode for the scrollbar
40 alwaysVisible : false,
42 // check if we should hide the scrollbar when user is hovering over
43 disableFadeOut : false,
45 // sets visibility of the rail
54 // whether we should use jQuery UI Draggable to enable bar dragging
57 // defautlt CSS class of the slimscroll rail
58 railClass : 'slimScrollRail',
60 // defautlt CSS class of the slimscroll bar
61 barClass : 'slimScrollBar',
63 // defautlt CSS class of the slimscroll wrapper
64 wrapperClass : 'slimScrollDiv',
66 // check if mousewheel should scroll the window if we reach top/bottom
67 allowPageScroll : false,
69 // scroll amount applied to each mouse wheel step
72 // scroll amount applied when user is using gestures
73 touchScrollStep : 200,
78 // sets border radius of the rail
79 railBorderRadius : '7px'
82 var o = $.extend(defaults, options);
84 // do it for every element that matches selector
87 var isOverPanel, isOverBar, isDragg, queueHide, touchDif,
88 barHeight, percentScroll, lastScroll,
91 releaseScroll = false;
93 // used in event handlers and for better minification
96 // ensure we are not binding it again
97 if (me.parent().hasClass(o.wrapperClass))
99 // start from last bar position
100 var offset = me.scrollTop();
103 bar = me.parent().find('.' + o.barClass);
104 rail = me.parent().find('.' + o.railClass);
108 // check if we should scroll existing instance
109 if ($.isPlainObject(options))
111 // Pass height: auto to an existing slimscroll object to force a resize after contents have changed
112 if ( 'height' in options && options.height == 'auto' ) {
113 me.parent().css('height', 'auto');
114 me.css('height', 'auto');
115 var height = me.parent().parent().height();
116 me.parent().css('height', height);
117 me.css('height', height);
120 if ('scrollTo' in options)
122 // jump to a static point
123 offset = parseInt(o.scrollTo);
125 else if ('scrollBy' in options)
127 // jump by value pixels
128 offset += parseInt(o.scrollBy);
130 else if ('destroy' in options)
132 // remove slimscroll elements
139 // scroll content by the given offset
140 scrollContent(offset, false, true);
146 // optionally set height to the parent's height
147 o.height = (o.height == 'auto') ? me.parent().height() : o.height;
150 var wrapper = $(divS)
151 .addClass(o.wrapperClass)
153 position: 'relative',
159 // update style for the div
166 // create scrollbar rail
168 .addClass(o.railClass)
172 position: 'absolute',
174 display: (o.alwaysVisible && o.railVisible) ? 'block' : 'none',
175 'border-radius': o.railBorderRadius,
176 background: o.railColor,
177 opacity: o.railOpacity,
183 .addClass(o.barClass)
187 position: 'absolute',
190 display: o.alwaysVisible ? 'block' : 'none',
191 'border-radius' : o.borderRadius,
192 BorderRadius: o.borderRadius,
193 MozBorderRadius: o.borderRadius,
194 WebkitBorderRadius: o.borderRadius,
199 var posCss = (o.position == 'right') ? { right: o.distance } : { left: o.distance };
206 // append to parent div
207 me.parent().append(bar);
208 me.parent().append(rail);
210 // make it draggable and no longer dependent on the jqueryUI
211 if (o.railDraggable){
212 bar.bind("mousedown", function(e) {
213 var $doc = $(document);
215 t = parseFloat(bar.css('top'));
218 $doc.bind("mousemove.slimscroll", function(e){
219 currTop = t + e.pageY - pageY;
220 bar.css('top', currTop);
221 scrollContent(0, bar.position().top, false);// scroll content
224 $doc.bind("mouseup.slimscroll", function(e) {
225 isDragg = false;hideBar();
226 $doc.unbind('.slimscroll');
229 }).bind("selectstart.slimscroll", function(e){
237 rail.hover(function(){
244 bar.hover(function(){
250 // show on parent mouseover
260 // support for mobile
261 me.bind('touchstart', function(e,b){
262 if (e.originalEvent.touches.length)
264 // record where touch started
265 touchDif = e.originalEvent.touches[0].pageY;
269 me.bind('touchmove', function(e){
270 // prevent scrolling the page if necessary
273 e.originalEvent.preventDefault();
275 if (e.originalEvent.touches.length)
277 // see how far user swiped
278 var diff = (touchDif - e.originalEvent.touches[0].pageY) / o.touchScrollStep;
280 scrollContent(diff, true);
281 touchDif = e.originalEvent.touches[0].pageY;
285 // set up initial height
288 // check start position
289 if (o.start === 'bottom')
291 // scroll content to bottom
292 bar.css({ top: me.outerHeight() - bar.outerHeight() });
293 scrollContent(0, true);
295 else if (o.start !== 'top')
297 // assume jQuery selector
298 scrollContent($(o.start).position().top, null, true);
300 // make sure bar stays hidden
301 if (!o.alwaysVisible) { bar.hide(); }
304 // attach scroll events
309 // use mouse wheel only when mouse is over
310 if (!isOverPanel) { return; }
312 var e = e || window.event;
315 if (e.wheelDelta) { delta = -e.wheelDelta/120; }
316 if (e.detail) { delta = e.detail / 3; }
318 var target = e.target || e.srcTarget || e.srcElement;
319 if ($(target).closest('.' + o.wrapperClass).is(me.parent())) {
321 scrollContent(delta, true);
324 // stop window scroll
325 if (e.preventDefault && !releaseScroll) { e.preventDefault(); }
326 if (!releaseScroll) { e.returnValue = false; }
329 function scrollContent(y, isWheel, isJump)
331 releaseScroll = false;
333 var maxTop = me.outerHeight() - bar.outerHeight();
337 // move bar with mouse wheel
338 delta = parseInt(bar.css('top')) + y * parseInt(o.wheelStep) / 100 * bar.outerHeight();
340 // move bar, make sure it doesn't go out
341 delta = Math.min(Math.max(delta, 0), maxTop);
343 // if scrolling down, make sure a fractional change to the
344 // scroll position isn't rounded away when the scrollbar's CSS is set
345 // this flooring of delta would happened automatically when
346 // bar.css is set below, but we floor here for clarity
347 delta = (y > 0) ? Math.ceil(delta) : Math.floor(delta);
349 // scroll the scrollbar
350 bar.css({ top: delta + 'px' });
353 // calculate actual scroll amount
354 percentScroll = parseInt(bar.css('top')) / (me.outerHeight() - bar.outerHeight());
355 delta = percentScroll * (me[0].scrollHeight - me.outerHeight());
360 var offsetTop = delta / me[0].scrollHeight * me.outerHeight();
361 offsetTop = Math.min(Math.max(offsetTop, 0), maxTop);
362 bar.css({ top: offsetTop + 'px' });
368 // fire scrolling event
369 me.trigger('slimscrolling', ~~delta);
371 // ensure bar is visible
374 // trigger hide when scroll is stopped
378 function attachWheel()
380 if (window.addEventListener)
382 this.addEventListener('DOMMouseScroll', _onWheel, false );
383 this.addEventListener('mousewheel', _onWheel, false );
384 this.addEventListener('MozMousePixelScroll', _onWheel, false );
388 document.attachEvent("onmousewheel", _onWheel)
392 function getBarHeight()
394 // calculate scrollbar height and make sure it is not too small
395 barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight);
396 bar.css({ height: barHeight + 'px' });
398 // hide scrollbar if content is not long enough
399 var display = barHeight == me.outerHeight() ? 'none' : 'block';
400 bar.css({ display: display });
405 // recalculate bar height
407 clearTimeout(queueHide);
409 // when bar reached top or bottom
410 if (percentScroll == ~~percentScroll)
413 releaseScroll = o.allowPageScroll;
415 // publish approporiate event
416 if (lastScroll != percentScroll)
418 var msg = (~~percentScroll == 0) ? 'top' : 'bottom';
419 me.trigger('slimscroll', msg);
424 releaseScroll = false;
426 lastScroll = percentScroll;
428 // show only when required
429 if(barHeight >= me.outerHeight()) {
430 //allow window scroll
431 releaseScroll = true;
434 bar.stop(true,true).fadeIn('fast');
435 if (o.railVisible) { rail.stop(true,true).fadeIn('fast'); }
440 // only hide when options allow it
441 if (!o.alwaysVisible)
443 queueHide = setTimeout(function(){
444 if (!(o.disableFadeOut && isOverPanel) && !isOverBar && !isDragg)
447 rail.fadeOut('slow');
455 // maintain chainability
461 slimscroll: jQuery.fn.slimScroll