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.siblings('.' + o.barClass);
104 rail = me.siblings('.' + 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);
118 } else if ('height' in options) {
119 var h = options.height;
120 me.parent().css('height', h);
124 if ('scrollTo' in options)
126 // jump to a static point
127 offset = parseInt(o.scrollTo);
129 else if ('scrollBy' in options)
131 // jump by value pixels
132 offset += parseInt(o.scrollBy);
134 else if ('destroy' in options)
136 // remove slimscroll elements
143 // scroll content by the given offset
144 scrollContent(offset, false, true);
149 else if ($.isPlainObject(options))
151 if ('destroy' in options)
157 // optionally set height to the parent's height
158 o.height = (o.height == 'auto') ? me.parent().height() : o.height;
161 var wrapper = $(divS)
162 .addClass(o.wrapperClass)
164 position: 'relative',
170 // update style for the div
177 // create scrollbar rail
179 .addClass(o.railClass)
183 position: 'absolute',
185 display: (o.alwaysVisible && o.railVisible) ? 'block' : 'none',
186 'border-radius': o.railBorderRadius,
187 background: o.railColor,
188 opacity: o.railOpacity,
194 .addClass(o.barClass)
198 position: 'absolute',
201 display: o.alwaysVisible ? 'block' : 'none',
202 'border-radius' : o.borderRadius,
203 BorderRadius: o.borderRadius,
204 MozBorderRadius: o.borderRadius,
205 WebkitBorderRadius: o.borderRadius,
210 var posCss = (o.position == 'right') ? { right: o.distance } : { left: o.distance };
217 // append to parent div
218 me.parent().append(bar);
219 me.parent().append(rail);
221 // make it draggable and no longer dependent on the jqueryUI
222 if (o.railDraggable){
223 bar.bind("mousedown", function(e) {
224 var $doc = $(document);
226 t = parseFloat(bar.css('top'));
229 $doc.bind("mousemove.slimscroll", function(e){
230 currTop = t + e.pageY - pageY;
231 bar.css('top', currTop);
232 scrollContent(0, bar.position().top, false);// scroll content
235 $doc.bind("mouseup.slimscroll", function(e) {
236 isDragg = false;hideBar();
237 $doc.unbind('.slimscroll');
240 }).bind("selectstart.slimscroll", function(e){
248 rail.hover(function(){
255 bar.hover(function(){
261 // show on parent mouseover
271 // support for mobile
272 me.bind('touchstart', function(e,b){
273 if (e.originalEvent.touches.length)
275 // record where touch started
276 touchDif = e.originalEvent.touches[0].pageY;
280 me.bind('touchmove', function(e){
281 // prevent scrolling the page if necessary
284 e.originalEvent.preventDefault();
286 if (e.originalEvent.touches.length)
288 // see how far user swiped
289 var diff = (touchDif - e.originalEvent.touches[0].pageY) / o.touchScrollStep;
291 scrollContent(diff, true);
292 touchDif = e.originalEvent.touches[0].pageY;
296 // set up initial height
299 // check start position
300 if (o.start === 'bottom')
302 // scroll content to bottom
303 bar.css({ top: me.outerHeight() - bar.outerHeight() });
304 scrollContent(0, true);
306 else if (o.start !== 'top')
308 // assume jQuery selector
309 scrollContent($(o.start).position().top, null, true);
311 // make sure bar stays hidden
312 if (!o.alwaysVisible) { bar.hide(); }
315 // attach scroll events
320 // use mouse wheel only when mouse is over
321 if (!isOverPanel) { return; }
323 var e = e || window.event;
326 if (e.wheelDelta) { delta = -e.wheelDelta/120; }
327 if (e.detail) { delta = e.detail / 3; }
329 var target = e.target || e.srcTarget || e.srcElement;
330 if ($(target).closest('.' + o.wrapperClass).is(me.parent())) {
332 scrollContent(delta, true);
335 // stop window scroll
336 if (e.preventDefault && !releaseScroll) { e.preventDefault(); }
337 if (!releaseScroll) { e.returnValue = false; }
340 function scrollContent(y, isWheel, isJump)
342 releaseScroll = false;
344 var maxTop = me.outerHeight() - bar.outerHeight();
348 // move bar with mouse wheel
349 delta = parseInt(bar.css('top')) + y * parseInt(o.wheelStep) / 100 * bar.outerHeight();
351 // move bar, make sure it doesn't go out
352 delta = Math.min(Math.max(delta, 0), maxTop);
354 // if scrolling down, make sure a fractional change to the
355 // scroll position isn't rounded away when the scrollbar's CSS is set
356 // this flooring of delta would happened automatically when
357 // bar.css is set below, but we floor here for clarity
358 delta = (y > 0) ? Math.ceil(delta) : Math.floor(delta);
360 // scroll the scrollbar
361 bar.css({ top: delta + 'px' });
364 // calculate actual scroll amount
365 percentScroll = parseInt(bar.css('top')) / (me.outerHeight() - bar.outerHeight());
366 delta = percentScroll * (me[0].scrollHeight - me.outerHeight());
371 var offsetTop = delta / me[0].scrollHeight * me.outerHeight();
372 offsetTop = Math.min(Math.max(offsetTop, 0), maxTop);
373 bar.css({ top: offsetTop + 'px' });
379 // fire scrolling event
380 me.trigger('slimscrolling', ~~delta);
382 // ensure bar is visible
385 // trigger hide when scroll is stopped
389 function attachWheel(target)
391 if (window.addEventListener)
393 target.addEventListener('DOMMouseScroll', _onWheel, false );
394 target.addEventListener('mousewheel', _onWheel, false );
398 document.attachEvent("onmousewheel", _onWheel)
402 function getBarHeight()
404 // calculate scrollbar height and make sure it is not too small
405 barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight);
406 bar.css({ height: barHeight + 'px' });
408 // hide scrollbar if content is not long enough
409 var display = barHeight == me.outerHeight() ? 'none' : 'block';
410 bar.css({ display: display });
415 // recalculate bar height
417 clearTimeout(queueHide);
419 // when bar reached top or bottom
420 if (percentScroll == ~~percentScroll)
423 releaseScroll = o.allowPageScroll;
425 // publish approporiate event
426 if (lastScroll != percentScroll)
428 var msg = (~~percentScroll == 0) ? 'top' : 'bottom';
429 me.trigger('slimscroll', msg);
434 releaseScroll = false;
436 lastScroll = percentScroll;
438 // show only when required
439 if(barHeight >= me.outerHeight()) {
440 //allow window scroll
441 releaseScroll = true;
444 bar.stop(true,true).fadeIn('fast');
445 if (o.railVisible) { rail.stop(true,true).fadeIn('fast'); }
450 // only hide when options allow it
451 if (!o.alwaysVisible)
453 queueHide = setTimeout(function(){
454 if (!(o.disableFadeOut && isOverPanel) && !isOverBar && !isDragg)
457 rail.fadeOut('slow');
465 // maintain chainability
471 slimscroll: $.fn.slimScroll