|
|
/*! 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.0 * */ (function ($) { jQuery.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: '100%',
// 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.parent().find('.' + o.barClass); rail = me.parent().find('.' + 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); }
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;
if (o.reduce && typeof o.height === 'string' && o.height.indexOf('px') > -1) { o.height = (o.height.replace('px', '') - o.reduce) + 'px'; }
// 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();
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() { if (window.addEventListener) { this.addEventListener('DOMMouseScroll', _onWheel, false); this.addEventListener('mousewheel', _onWheel, false); this.addEventListener('MozMousePixelScroll', _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; } });
jQuery.fn.extend({ slimscroll: jQuery.fn.slimScroll }); })(jQuery);
|