jQuery.jScrollPane = { active : [] }; jQuery.fn.jScrollPane = function(settings) { settings = jQuery.extend( { scrollbarWidth : 10, scrollbarMargin : 5, wheelSpeed : 19, showArrows : true, arrowSize : 0, animateTo : false, dragMinHeight : 1, dragMaxHeight : 55, animateInterval : 100, animateStep: 3, maintainPosition: true, scrollbarOnLeft: false }, settings ); return this.each( function() { var $this = jQuery(this); if (jQuery(this).parent().is('.jScrollPaneContainer')) { var currentScrollPosition = settings.maintainPosition ? $this.offset({relativeTo:jQuery(this).parent()[0]}).top : 0; var $c = jQuery(this).parent(); var paneWidth = $c.innerWidth(); var paneHeight = $c.outerHeight(); var trackHeight = paneHeight; if ($c.unmousewheel) { $c.unmousewheel(); } jQuery('>.jScrollPaneTrack, >.jScrollArrowUp, >.jScrollArrowDown', $c).remove(); $this.css({'top':0}); } else { var currentScrollPosition = 0; this.originalPadding = $this.css('paddingTop') + ' ' + $this.css('paddingRight') + ' ' + $this.css('paddingBottom') + ' ' + $this.css('paddingLeft'); this.originalSidePaddingTotal = (parseInt($this.css('paddingLeft')) || 0) + (parseInt($this.css('paddingRight')) || 0); var paneWidth = $this.innerWidth(); var paneHeight = $this.innerHeight(); var trackHeight = paneHeight; $this.wrap( jQuery('
').attr( {'className':'jScrollPaneContainer'} ).css( { 'height':paneHeight+'px', 'width':paneWidth+'px' } ) ); // deal with text size changes (if the jquery.em plugin is included) // and re-initialise the scrollPane so the track maintains the // correct size jQuery(document).bind( 'emchange', function(e, cur, prev) { $this.jScrollPane(settings); } ); } var p = this.originalSidePaddingTotal; var cssToApply = { 'height':'auto', 'width':paneWidth - settings.scrollbarWidth - settings.scrollbarMargin - p + 'px' } if(settings.scrollbarOnLeft) { cssToApply.paddingLeft = settings.scrollbarMargin + settings.scrollbarWidth + 'px'; } else { cssToApply.paddingRight = settings.scrollbarMargin + 'px'; } $this.css(cssToApply); var contentHeight = $this.outerHeight(); var percentInView = paneHeight / contentHeight; if (percentInView < .99) { var $container = $this.parent(); $container.append( jQuery('
').attr({'className':'jScrollPaneTrack'}).css({'width':settings.scrollbarWidth+'px'}).append( jQuery('
').attr({'className':'jScrollPaneDrag'}).css({'width':settings.scrollbarWidth+'px'}).append( jQuery('
').attr({'className':'jScrollPaneDragTop'}).css({'width':settings.scrollbarWidth+'px'}), jQuery('
').attr({'className':'jScrollPaneDragBottom'}).css({'width':settings.scrollbarWidth+'px'}) ) ) ); $('.jScrollPaneTrack').prepend(''); var $track = jQuery('>.jScrollPaneTrack', $container); var $drag = jQuery('>.jScrollPaneTrack .jScrollPaneDrag', $container); if (settings.showArrows) { var currentArrowButton; var currentArrowDirection; var currentArrowInterval; var currentArrowInc; var whileArrowButtonDown = function() { if (currentArrowInc > 4 || currentArrowInc%4==0) { positionDrag(dragPosition + currentArrowDirection * mouseWheelMultiplier); } currentArrowInc ++; }; var onArrowMouseUp = function(event) { jQuery('html').unbind('mouseup', onArrowMouseUp); currentArrowButton.removeClass('jScrollActiveArrowButton'); clearInterval(currentArrowInterval); //console.log($(event.target)); //currentArrowButton.parent().removeClass('jScrollArrowUpClicked jScrollArrowDownClicked'); }; var onArrowMouseDown = function() { //console.log(direction); //currentArrowButton = $(this); jQuery('html').bind('mouseup', onArrowMouseUp); currentArrowButton.addClass('jScrollActiveArrowButton'); currentArrowInc = 0; whileArrowButtonDown(); currentArrowInterval = setInterval(whileArrowButtonDown, 100); }; $container .append( jQuery('') .attr({'href':'javascript:;', 'className':'jScrollArrowUp'}) .css({'width':settings.scrollbarWidth+'px'}) .html('Scroll up') .bind('mousedown', function() { currentArrowButton = jQuery(this); currentArrowDirection = -1; onArrowMouseDown(); this.blur(); return false; }), jQuery('') .attr({'href':'javascript:;', 'className':'jScrollArrowDown'}) .css({'width':settings.scrollbarWidth+'px'}) .html('Scroll down') .bind('mousedown', function() { currentArrowButton = jQuery(this); currentArrowDirection = 1; onArrowMouseDown(); this.blur(); return false; }) ); var $upArrow = jQuery('>.jScrollArrowUp', $container); var $downArrow = jQuery('>.jScrollArrowDown', $container); if (settings.arrowSize) { trackHeight = paneHeight - settings.arrowSize - settings.arrowSize; $track .css({'height': trackHeight+'px', top:settings.arrowSize+'px'}) } else { var topArrowHeight = $upArrow.height(); settings.arrowSize = topArrowHeight; trackHeight = paneHeight - topArrowHeight - $downArrow.height(); $track .css({'height': trackHeight+'px', top:topArrowHeight+'px'}) } } var $pane = jQuery(this).css({'position':'absolute', 'overflow':'visible'}); var currentOffset; var maxY; var mouseWheelMultiplier; // store this in a seperate variable so we can keep track more accurately than just updating the css property.. var dragPosition = 0; var dragMiddle = percentInView*paneHeight/2; // pos function borrowed from tooltip plugin and adapted... var getPos = function (event, c) { var p = c == 'X' ? 'Left' : 'Top'; return event['page' + c] || (event['client' + c] + (document.documentElement['scroll' + p] || document.body['scroll' + p])) || 0; }; var ignoreNativeDrag = function() { return false; }; var initDrag = function() { ceaseAnimation(); currentOffset = $drag.offset(false); currentOffset.top -= dragPosition; maxY = trackHeight - $drag[0].offsetHeight; mouseWheelMultiplier = 2 * settings.wheelSpeed * maxY / contentHeight; }; var onStartDrag = function(event) { initDrag(); dragMiddle = getPos(event, 'Y') - dragPosition - currentOffset.top; jQuery('html').bind('mouseup', onStopDrag).bind('mousemove', updateScroll); if (jQuery.browser.msie) { jQuery('html').bind('dragstart', ignoreNativeDrag).bind('selectstart', ignoreNativeDrag); } return false; }; var onStopDrag = function() { jQuery('html').unbind('mouseup', onStopDrag).unbind('mousemove', updateScroll); dragMiddle = percentInView*paneHeight/2; if (jQuery.browser.msie) { jQuery('html').unbind('dragstart', ignoreNativeDrag).unbind('selectstart', ignoreNativeDrag); } }; var positionDrag = function(destY) { destY = destY < 0 ? 0 : (destY > maxY ? maxY : destY); dragPosition = destY; $drag.css({'top':destY+'px'}); var p = destY / maxY; $pane.css({'top':((paneHeight-contentHeight)*p) + 'px'}); $this.trigger('scroll'); if (settings.showArrows) { $upArrow[destY == 0 ? 'addClass' : 'removeClass']('disabled'); $downArrow[destY == maxY ? 'addClass' : 'removeClass']('disabled'); } }; var updateScroll = function(e) { positionDrag(getPos(e, 'Y') - currentOffset.top - dragMiddle); }; var dragH = Math.max(Math.min(percentInView*(paneHeight-settings.arrowSize*2), settings.dragMaxHeight), settings.dragMinHeight); $drag.css( {'height':dragH+'px'} ).bind('mousedown', onStartDrag); var trackScrollInterval; var trackScrollInc; var trackScrollMousePos; var doTrackScroll = function() { if (trackScrollInc > 8 || trackScrollInc%4==0) { positionDrag((dragPosition - ((dragPosition - trackScrollMousePos) / 2))); } trackScrollInc ++; }; var onStopTrackClick = function() { clearInterval(trackScrollInterval); jQuery('html').unbind('mouseup', onStopTrackClick).unbind('mousemove', onTrackMouseMove); }; var onTrackMouseMove = function(event) { trackScrollMousePos = getPos(event, 'Y') - currentOffset.top - dragMiddle; }; var onTrackClick = function(event) { initDrag(); onTrackMouseMove(event); trackScrollInc = 0; jQuery('html').bind('mouseup', onStopTrackClick).bind('mousemove', onTrackMouseMove); trackScrollInterval = setInterval(doTrackScroll, 100); doTrackScroll(); }; $track.bind('mousedown', onTrackClick); // if the mousewheel plugin has been included then also react to the mousewheel if ($container.mousewheel) { $container.mousewheel( function (event, delta) { initDrag(); ceaseAnimation(); var d = dragPosition; positionDrag(dragPosition - delta * mouseWheelMultiplier); var dragOccured = d != dragPosition; return !dragOccured; }, false ); } var _animateToPosition; var _animateToInterval; function animateToPosition() { var diff = (_animateToPosition - dragPosition) / settings.animateStep; if (diff > 1 || diff < -1) { positionDrag(dragPosition + diff); } else { positionDrag(_animateToPosition); ceaseAnimation(); } } var ceaseAnimation = function() { if (_animateToInterval) { clearInterval(_animateToInterval); delete _animateToPosition; } }; var scrollTo = function(pos, preventAni) { if (typeof pos == "string") { $e = jQuery(pos, this); if (!$e.length) return; pos = $e.offset().top - $this.offset().top; } ceaseAnimation(); var destDragPosition = -pos/(paneHeight-contentHeight) * maxY; if (!preventAni || settings.animateTo) { _animateToPosition = destDragPosition; _animateToInterval = setInterval(animateToPosition, settings.animateInterval); } else { positionDrag(destDragPosition); } }; $this[0].scrollTo = scrollTo; $this[0].scrollBy = function(delta) { var currentPos = -parseInt($pane.css('top')) || 0; scrollTo(currentPos + delta); }; initDrag(); scrollTo(-currentScrollPosition, true); jQuery.jScrollPane.active.push($this[0]); } else { $this.css( { 'height':paneHeight+'px', 'width':paneWidth-this.originalSidePaddingTotal+'px', 'padding':this.originalPadding } ); // remove from active list? } } ) }; // clean up the scrollTo expandos jQuery(window) .bind('unload', function() { var els = jQuery.jScrollPane.active; for (var i=0; i