| /*! Copyright (c) 2011 Piotr Rochala (http://rocha.la) |
| * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) |
| * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. |
| * |
| * Version: 1.3.8 |
| * |
| */ |
| (function($) { |
| |
| $.fn.extend({ |
| slimScroll: function(options) { |
| |
| var defaults = { |
| |
| // width in pixels of the visible scroll area |
| width : 'auto', |
| |
| // height in pixels of the visible scroll area |
| height : '250px', |
| |
| // width in pixels of the scrollbar and rail |
| size : '7px', |
| |
| // scrollbar color, accepts any hex/color value |
| color: '#000', |
| |
| // scrollbar position - left/right |
| position : 'right', |
| |
| // distance in pixels between the side edge and the scrollbar |
| distance : '1px', |
| |
| // default scroll position on load - top / bottom / $('selector') |
| start : 'top', |
| |
| // sets scrollbar opacity |
| opacity : .4, |
| |
| // enables always-on mode for the scrollbar |
| alwaysVisible : false, |
| |
| // check if we should hide the scrollbar when user is hovering over |
| disableFadeOut : false, |
| |
| // sets visibility of the rail |
| railVisible : false, |
| |
| // sets rail color |
| railColor : '#333', |
| |
| // sets rail opacity |
| railOpacity : .2, |
| |
| // whether we should use jQuery UI Draggable to enable bar dragging |
| railDraggable : true, |
| |
| // defautlt CSS class of the slimscroll rail |
| railClass : 'slimScrollRail', |
| |
| // defautlt CSS class of the slimscroll bar |
| barClass : 'slimScrollBar', |
| |
| // defautlt CSS class of the slimscroll wrapper |
| wrapperClass : 'slimScrollDiv', |
| |
| // check if mousewheel should scroll the window if we reach top/bottom |
| allowPageScroll : false, |
| |
| // scroll amount applied to each mouse wheel step |
| wheelStep : 20, |
| |
| // scroll amount applied when user is using gestures |
| touchScrollStep : 200, |
| |
| // sets border radius |
| borderRadius: '7px', |
| |
| // sets border radius of the rail |
| railBorderRadius : '7px' |
| }; |
| |
| var o = $.extend(defaults, options); |
| |
| // do it for every element that matches selector |
| this.each(function(){ |
| |
| var isOverPanel, isOverBar, isDragg, queueHide, touchDif, |
| barHeight, percentScroll, lastScroll, |
| divS = '<div></div>', |
| minBarHeight = 30, |
| releaseScroll = false; |
| |
| // used in event handlers and for better minification |
| var me = $(this); |
| |
| // ensure we are not binding it again |
| if (me.parent().hasClass(o.wrapperClass)) |
| { |
| // start from last bar position |
| var offset = me.scrollTop(); |
| |
| // find bar and rail |
| bar = me.siblings('.' + o.barClass); |
| rail = me.siblings('.' + o.railClass); |
| |
| getBarHeight(); |
| |
| // check if we should scroll existing instance |
| if ($.isPlainObject(options)) |
| { |
| // Pass height: auto to an existing slimscroll object to force a resize after contents have changed |
| if ( 'height' in options && options.height == 'auto' ) { |
| me.parent().css('height', 'auto'); |
| me.css('height', 'auto'); |
| var height = me.parent().parent().height(); |
| me.parent().css('height', height); |
| me.css('height', height); |
| } else if ('height' in options) { |
| var h = options.height; |
| me.parent().css('height', h); |
| me.css('height', h); |
| } |
| |
| if ('scrollTo' in options) |
| { |
| // jump to a static point |
| offset = parseInt(o.scrollTo); |
| } |
| else if ('scrollBy' in options) |
| { |
| // jump by value pixels |
| offset += parseInt(o.scrollBy); |
| } |
| else if ('destroy' in options) |
| { |
| // remove slimscroll elements |
| bar.remove(); |
| rail.remove(); |
| me.unwrap(); |
| return; |
| } |
| |
| // scroll content by the given offset |
| scrollContent(offset, false, true); |
| } |
| |
| return; |
| } |
| else if ($.isPlainObject(options)) |
| { |
| if ('destroy' in options) |
| { |
| return; |
| } |
| } |
| |
| // optionally set height to the parent's height |
| o.height = (o.height == 'auto') ? me.parent().height() : o.height; |
| |
| // wrap content |
| var wrapper = $(divS) |
| .addClass(o.wrapperClass) |
| .css({ |
| position: 'relative', |
| overflow: 'hidden', |
| width: o.width, |
| height: o.height |
| }); |
| |
| // update style for the div |
| me.css({ |
| overflow: 'hidden', |
| width: o.width, |
| height: o.height |
| }); |
| |
| // create scrollbar rail |
| var rail = $(divS) |
| .addClass(o.railClass) |
| .css({ |
| width: o.size, |
| height: '100%', |
| position: 'absolute', |
| top: 0, |
| display: (o.alwaysVisible && o.railVisible) ? 'block' : 'none', |
| 'border-radius': o.railBorderRadius, |
| background: o.railColor, |
| opacity: o.railOpacity, |
| zIndex: 90 |
| }); |
| |
| // create scrollbar |
| var bar = $(divS) |
| .addClass(o.barClass) |
| .css({ |
| background: o.color, |
| width: o.size, |
| position: 'absolute', |
| top: 0, |
| opacity: o.opacity, |
| display: o.alwaysVisible ? 'block' : 'none', |
| 'border-radius' : o.borderRadius, |
| BorderRadius: o.borderRadius, |
| MozBorderRadius: o.borderRadius, |
| WebkitBorderRadius: o.borderRadius, |
| zIndex: 99 |
| }); |
| |
| // set position |
| var posCss = (o.position == 'right') ? { right: o.distance } : { left: o.distance }; |
| rail.css(posCss); |
| bar.css(posCss); |
| |
| // wrap it |
| me.wrap(wrapper); |
| |
| // append to parent div |
| me.parent().append(bar); |
| me.parent().append(rail); |
| |
| // make it draggable and no longer dependent on the jqueryUI |
| if (o.railDraggable){ |
| bar.bind("mousedown", function(e) { |
| var $doc = $(document); |
| isDragg = true; |
| t = parseFloat(bar.css('top')); |
| pageY = e.pageY; |
| |
| $doc.bind("mousemove.slimscroll", function(e){ |
| currTop = t + e.pageY - pageY; |
| bar.css('top', currTop); |
| scrollContent(0, bar.position().top, false);// scroll content |
| }); |
| |
| $doc.bind("mouseup.slimscroll", function(e) { |
| isDragg = false;hideBar(); |
| $doc.unbind('.slimscroll'); |
| }); |
| return false; |
| }).bind("selectstart.slimscroll", function(e){ |
| e.stopPropagation(); |
| e.preventDefault(); |
| return false; |
| }); |
| } |
| |
| // on rail over |
| rail.hover(function(){ |
| showBar(); |
| }, function(){ |
| hideBar(); |
| }); |
| |
| // on bar over |
| bar.hover(function(){ |
| isOverBar = true; |
| }, function(){ |
| isOverBar = false; |
| }); |
| |
| // show on parent mouseover |
| me.hover(function(){ |
| isOverPanel = true; |
| showBar(); |
| hideBar(); |
| }, function(){ |
| isOverPanel = false; |
| hideBar(); |
| }); |
| |
| // support for mobile |
| me.bind('touchstart', function(e,b){ |
| if (e.originalEvent.touches.length) |
| { |
| // record where touch started |
| touchDif = e.originalEvent.touches[0].pageY; |
| } |
| }); |
| |
| me.bind('touchmove', function(e){ |
| // prevent scrolling the page if necessary |
| if(!releaseScroll) |
| { |
| e.originalEvent.preventDefault(); |
| } |
| if (e.originalEvent.touches.length) |
| { |
| // see how far user swiped |
| var diff = (touchDif - e.originalEvent.touches[0].pageY) / o.touchScrollStep; |
| // scroll content |
| scrollContent(diff, true); |
| touchDif = e.originalEvent.touches[0].pageY; |
| } |
| }); |
| |
| // set up initial height |
| getBarHeight(); |
| |
| // check start position |
| if (o.start === 'bottom') |
| { |
| // scroll content to bottom |
| bar.css({ top: me.outerHeight() - bar.outerHeight() }); |
| scrollContent(0, true); |
| } |
| else if (o.start !== 'top') |
| { |
| // assume jQuery selector |
| scrollContent($(o.start).position().top, null, true); |
| |
| // make sure bar stays hidden |
| if (!o.alwaysVisible) { bar.hide(); } |
| } |
| |
| // attach scroll events |
| attachWheel(this); |
| |
| function _onWheel(e) |
| { |
| // use mouse wheel only when mouse is over |
| if (!isOverPanel) { return; } |
| |
| var e = e || window.event; |
| |
| var delta = 0; |
| if (e.wheelDelta) { delta = -e.wheelDelta/120; } |
| if (e.detail) { delta = e.detail / 3; } |
| |
| var target = e.target || e.srcTarget || e.srcElement; |
| if ($(target).closest('.' + o.wrapperClass).is(me.parent())) { |
| // scroll content |
| scrollContent(delta, true); |
| } |
| |
| // stop window scroll |
| if (e.preventDefault && !releaseScroll) { e.preventDefault(); } |
| if (!releaseScroll) { e.returnValue = false; } |
| } |
| |
| function scrollContent(y, isWheel, isJump) |
| { |
| releaseScroll = false; |
| var delta = y; |
| var maxTop = me.outerHeight() - bar.outerHeight(); |
| |
| if (isWheel) |
| { |
| // move bar with mouse wheel |
| delta = parseInt(bar.css('top')) + y * parseInt(o.wheelStep) / 100 * bar.outerHeight(); |
| |
| // move bar, make sure it doesn't go out |
| delta = Math.min(Math.max(delta, 0), maxTop); |
| |
| // if scrolling down, make sure a fractional change to the |
| // scroll position isn't rounded away when the scrollbar's CSS is set |
| // this flooring of delta would happened automatically when |
| // bar.css is set below, but we floor here for clarity |
| delta = (y > 0) ? Math.ceil(delta) : Math.floor(delta); |
| |
| // scroll the scrollbar |
| bar.css({ top: delta + 'px' }); |
| } |
| |
| // calculate actual scroll amount |
| percentScroll = parseInt(bar.css('top')) / (me.outerHeight() - bar.outerHeight()); |
| delta = percentScroll * (me[0].scrollHeight - me.outerHeight()); |
| |
| if (isJump) |
| { |
| delta = y; |
| var offsetTop = delta / me[0].scrollHeight * me.outerHeight(); |
| offsetTop = Math.min(Math.max(offsetTop, 0), maxTop); |
| bar.css({ top: offsetTop + 'px' }); |
| } |
| |
| // scroll content |
| me.scrollTop(delta); |
| |
| // fire scrolling event |
| me.trigger('slimscrolling', ~~delta); |
| |
| // ensure bar is visible |
| showBar(); |
| |
| // trigger hide when scroll is stopped |
| hideBar(); |
| } |
| |
| function attachWheel(target) |
| { |
| if (window.addEventListener) |
| { |
| target.addEventListener('DOMMouseScroll', _onWheel, false ); |
| target.addEventListener('mousewheel', _onWheel, false ); |
| } |
| else |
| { |
| document.attachEvent("onmousewheel", _onWheel) |
| } |
| } |
| |
| function getBarHeight() |
| { |
| // calculate scrollbar height and make sure it is not too small |
| barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight); |
| bar.css({ height: barHeight + 'px' }); |
| |
| // hide scrollbar if content is not long enough |
| var display = barHeight == me.outerHeight() ? 'none' : 'block'; |
| bar.css({ display: display }); |
| } |
| |
| function showBar() |
| { |
| // recalculate bar height |
| getBarHeight(); |
| clearTimeout(queueHide); |
| |
| // when bar reached top or bottom |
| if (percentScroll == ~~percentScroll) |
| { |
| //release wheel |
| releaseScroll = o.allowPageScroll; |
| |
| // publish approporiate event |
| if (lastScroll != percentScroll) |
| { |
| var msg = (~~percentScroll == 0) ? 'top' : 'bottom'; |
| me.trigger('slimscroll', msg); |
| } |
| } |
| else |
| { |
| releaseScroll = false; |
| } |
| lastScroll = percentScroll; |
| |
| // show only when required |
| if(barHeight >= me.outerHeight()) { |
| //allow window scroll |
| releaseScroll = true; |
| return; |
| } |
| bar.stop(true,true).fadeIn('fast'); |
| if (o.railVisible) { rail.stop(true,true).fadeIn('fast'); } |
| } |
| |
| function hideBar() |
| { |
| // only hide when options allow it |
| if (!o.alwaysVisible) |
| { |
| queueHide = setTimeout(function(){ |
| if (!(o.disableFadeOut && isOverPanel) && !isOverBar && !isDragg) |
| { |
| bar.fadeOut('slow'); |
| rail.fadeOut('slow'); |
| } |
| }, 1000); |
| } |
| } |
| |
| }); |
| |
| // maintain chainability |
| return this; |
| } |
| }); |
| |
| $.fn.extend({ |
| slimscroll: $.fn.slimScroll |
| }); |
| |
| })(jQuery); |