1436 lines
52 KiB
JavaScript
1436 lines
52 KiB
JavaScript
/*!
|
|
* jScrollPane - v2.0.0beta12 - 2012-09-27
|
|
* http://jscrollpane.kelvinluck.com/
|
|
*
|
|
* Copyright (c) 2010 Kelvin Luck
|
|
* Dual licensed under the MIT or GPL licenses.
|
|
*/
|
|
|
|
// Script: jScrollPane - cross browser customisable scrollbars
|
|
//
|
|
// *Version: 2.0.0beta12, Last updated: 2012-09-27*
|
|
//
|
|
// Project Home - http://jscrollpane.kelvinluck.com/
|
|
// GitHub - http://github.com/vitch/jScrollPane
|
|
// Source - http://github.com/vitch/jScrollPane/raw/master/script/jquery.jscrollpane.js
|
|
// (Minified) - http://github.com/vitch/jScrollPane/raw/master/script/jquery.jscrollpane.min.js
|
|
//
|
|
// About: License
|
|
//
|
|
// Copyright (c) 2012 Kelvin Luck
|
|
// Dual licensed under the MIT or GPL Version 2 licenses.
|
|
// http://jscrollpane.kelvinluck.com/MIT-LICENSE.txt
|
|
// http://jscrollpane.kelvinluck.com/GPL-LICENSE.txt
|
|
//
|
|
// About: Examples
|
|
//
|
|
// All examples and demos are available through the jScrollPane example site at:
|
|
// http://jscrollpane.kelvinluck.com/
|
|
//
|
|
// About: Support and Testing
|
|
//
|
|
// This plugin is tested on the browsers below and has been found to work reliably on them. If you run
|
|
// into a problem on one of the supported browsers then please visit the support section on the jScrollPane
|
|
// website (http://jscrollpane.kelvinluck.com/) for more information on getting support. You are also
|
|
// welcome to fork the project on GitHub if you can contribute a fix for a given issue.
|
|
//
|
|
// jQuery Versions - tested in 1.4.2+ - reported to work in 1.3.x
|
|
// Browsers Tested - Firefox 3.6.8, Safari 5, Opera 10.6, Chrome 5.0, IE 6, 7, 8
|
|
//
|
|
// About: Release History
|
|
//
|
|
// 2.0.0beta12 - (2012-09-27) fix for jQuery 1.8+
|
|
// 2.0.0beta11 - (2012-05-14)
|
|
// 2.0.0beta10 - (2011-04-17) cleaner required size calculation, improved keyboard support, stickToBottom/Left, other small fixes
|
|
// 2.0.0beta9 - (2011-01-31) new API methods, bug fixes and correct keyboard support for FF/OSX
|
|
// 2.0.0beta8 - (2011-01-29) touchscreen support, improved keyboard support
|
|
// 2.0.0beta7 - (2011-01-23) scroll speed consistent (thanks Aivo Paas)
|
|
// 2.0.0beta6 - (2010-12-07) scrollToElement horizontal support
|
|
// 2.0.0beta5 - (2010-10-18) jQuery 1.4.3 support, various bug fixes
|
|
// 2.0.0beta4 - (2010-09-17) clickOnTrack support, bug fixes
|
|
// 2.0.0beta3 - (2010-08-27) Horizontal mousewheel, mwheelIntent, keyboard support, bug fixes
|
|
// 2.0.0beta2 - (2010-08-21) Bug fixes
|
|
// 2.0.0beta1 - (2010-08-17) Rewrite to follow modern best practices and enable horizontal scrolling, initially hidden
|
|
// elements and dynamically sized elements.
|
|
// 1.x - (2006-12-31 - 2010-07-31) Initial version, hosted at googlecode, deprecated
|
|
|
|
(function($,window,undefined){
|
|
|
|
$.fn.jScrollPane = function(settings)
|
|
{
|
|
// JScrollPane "class" - public methods are available through $('selector').data('jsp')
|
|
function JScrollPane(elem, s)
|
|
{
|
|
var settings, jsp = this, pane, paneWidth, paneHeight, container, contentWidth, contentHeight,
|
|
percentInViewH, percentInViewV, isScrollableV, isScrollableH, verticalDrag, dragMaxY,
|
|
verticalDragPosition, horizontalDrag, dragMaxX, horizontalDragPosition,
|
|
verticalBar, verticalTrack, scrollbarWidth, verticalTrackHeight, verticalDragHeight, arrowUp, arrowDown,
|
|
horizontalBar, horizontalTrack, horizontalTrackWidth, horizontalDragWidth, arrowLeft, arrowRight,
|
|
reinitialiseInterval, originalPadding, originalPaddingTotalWidth, previousContentWidth,
|
|
wasAtTop = true, wasAtLeft = true, wasAtBottom = false, wasAtRight = false,
|
|
originalElement = elem.clone(false, false).empty(),
|
|
mwEvent = $.fn.mwheelIntent ? 'mwheelIntent.jsp' : 'mousewheel.jsp';
|
|
|
|
originalPadding = elem.css('paddingTop') + ' ' +
|
|
elem.css('paddingRight') + ' ' +
|
|
elem.css('paddingBottom') + ' ' +
|
|
elem.css('paddingLeft');
|
|
originalPaddingTotalWidth = (parseInt(elem.css('paddingLeft'), 10) || 0) +
|
|
(parseInt(elem.css('paddingRight'), 10) || 0);
|
|
|
|
function initialise(s)
|
|
{
|
|
|
|
var /*firstChild, lastChild, */isMaintainingPositon, lastContentX, lastContentY,
|
|
hasContainingSpaceChanged, originalScrollTop, originalScrollLeft,
|
|
maintainAtBottom = false, maintainAtRight = false;
|
|
|
|
settings = s;
|
|
|
|
if (pane === undefined) {
|
|
originalScrollTop = elem.scrollTop();
|
|
originalScrollLeft = elem.scrollLeft();
|
|
|
|
elem.css(
|
|
{
|
|
overflow: 'hidden',
|
|
padding: 0
|
|
}
|
|
);
|
|
// TODO: Deal with where width/ height is 0 as it probably means the element is hidden and we should
|
|
// come back to it later and check once it is unhidden...
|
|
paneWidth = elem.innerWidth() + originalPaddingTotalWidth;
|
|
paneHeight = elem.innerHeight();
|
|
|
|
elem.width(paneWidth);
|
|
|
|
pane = $('<div class="jspPane" />').css('padding', originalPadding).append(elem.children());
|
|
container = $('<div class="jspContainer" />')
|
|
.css({
|
|
'width': paneWidth + 'px',
|
|
'height': paneHeight + 'px'
|
|
}
|
|
).append(pane).appendTo(elem);
|
|
|
|
/*
|
|
// Move any margins from the first and last children up to the container so they can still
|
|
// collapse with neighbouring elements as they would before jScrollPane
|
|
firstChild = pane.find(':first-child');
|
|
lastChild = pane.find(':last-child');
|
|
elem.css(
|
|
{
|
|
'margin-top': firstChild.css('margin-top'),
|
|
'margin-bottom': lastChild.css('margin-bottom')
|
|
}
|
|
);
|
|
firstChild.css('margin-top', 0);
|
|
lastChild.css('margin-bottom', 0);
|
|
*/
|
|
} else {
|
|
elem.css('width', '');
|
|
|
|
maintainAtBottom = settings.stickToBottom && isCloseToBottom();
|
|
maintainAtRight = settings.stickToRight && isCloseToRight();
|
|
|
|
hasContainingSpaceChanged = elem.innerWidth() + originalPaddingTotalWidth != paneWidth || elem.outerHeight() != paneHeight;
|
|
|
|
if (hasContainingSpaceChanged) {
|
|
paneWidth = elem.innerWidth() + originalPaddingTotalWidth;
|
|
paneHeight = elem.innerHeight();
|
|
container.css({
|
|
width: paneWidth + 'px',
|
|
height: paneHeight + 'px'
|
|
});
|
|
}
|
|
|
|
// If nothing changed since last check...
|
|
if (!hasContainingSpaceChanged && previousContentWidth == contentWidth && pane.outerHeight() == contentHeight) {
|
|
elem.width(paneWidth);
|
|
return;
|
|
}
|
|
previousContentWidth = contentWidth;
|
|
|
|
pane.css('width', '');
|
|
elem.width(paneWidth);
|
|
|
|
container.find('>.jspVerticalBar,>.jspHorizontalBar').remove().end();
|
|
}
|
|
|
|
pane.css('overflow', 'auto');
|
|
if (s.contentWidth) {
|
|
contentWidth = s.contentWidth;
|
|
} else {
|
|
contentWidth = pane[0].scrollWidth;
|
|
}
|
|
contentHeight = pane[0].scrollHeight;
|
|
pane.css('overflow', '');
|
|
|
|
percentInViewH = contentWidth / paneWidth;
|
|
percentInViewV = contentHeight / paneHeight;
|
|
isScrollableV = percentInViewV > 1;
|
|
|
|
isScrollableH = percentInViewH > 1;
|
|
|
|
//console.log(paneWidth, paneHeight, contentWidth, contentHeight, percentInViewH, percentInViewV, isScrollableH, isScrollableV);
|
|
|
|
if (!(isScrollableH || isScrollableV)) {
|
|
elem.removeClass('jspScrollable');
|
|
pane.css({
|
|
top: 0,
|
|
width: container.width() - originalPaddingTotalWidth
|
|
});
|
|
removeMousewheel();
|
|
removeFocusHandler();
|
|
removeKeyboardNav();
|
|
removeClickOnTrack();
|
|
} else {
|
|
elem.addClass('jspScrollable');
|
|
|
|
isMaintainingPositon = settings.maintainPosition && (verticalDragPosition || horizontalDragPosition);
|
|
if (isMaintainingPositon) {
|
|
lastContentX = contentPositionX();
|
|
lastContentY = contentPositionY();
|
|
}
|
|
|
|
initialiseVerticalScroll();
|
|
initialiseHorizontalScroll();
|
|
resizeScrollbars();
|
|
|
|
if (isMaintainingPositon) {
|
|
scrollToX(maintainAtRight ? (contentWidth - paneWidth ) : lastContentX, false);
|
|
scrollToY(maintainAtBottom ? (contentHeight - paneHeight) : lastContentY, false);
|
|
}
|
|
|
|
initFocusHandler();
|
|
initMousewheel();
|
|
initTouch();
|
|
|
|
if (settings.enableKeyboardNavigation) {
|
|
initKeyboardNav();
|
|
}
|
|
if (settings.clickOnTrack) {
|
|
initClickOnTrack();
|
|
}
|
|
|
|
observeHash();
|
|
if (settings.hijackInternalLinks) {
|
|
hijackInternalLinks();
|
|
}
|
|
}
|
|
|
|
if (settings.autoReinitialise && !reinitialiseInterval) {
|
|
reinitialiseInterval = setInterval(
|
|
function()
|
|
{
|
|
initialise(settings);
|
|
},
|
|
settings.autoReinitialiseDelay
|
|
);
|
|
} else if (!settings.autoReinitialise && reinitialiseInterval) {
|
|
clearInterval(reinitialiseInterval);
|
|
}
|
|
|
|
originalScrollTop && elem.scrollTop(0) && scrollToY(originalScrollTop, false);
|
|
originalScrollLeft && elem.scrollLeft(0) && scrollToX(originalScrollLeft, false);
|
|
|
|
elem.trigger('jsp-initialised', [isScrollableH || isScrollableV]);
|
|
}
|
|
|
|
function initialiseVerticalScroll()
|
|
{
|
|
if (isScrollableV) {
|
|
|
|
container.append(
|
|
$('<div class="jspVerticalBar" />').append(
|
|
$('<div class="jspCap jspCapTop" />'),
|
|
$('<div class="jspTrack" />').append(
|
|
$('<div class="jspDrag" />').append(
|
|
$('<div class="jspDragTop" />'),
|
|
$('<div class="jspDragBottom" />')
|
|
)
|
|
),
|
|
$('<div class="jspCap jspCapBottom" />')
|
|
)
|
|
);
|
|
|
|
verticalBar = container.find('>.jspVerticalBar');
|
|
verticalTrack = verticalBar.find('>.jspTrack');
|
|
verticalDrag = verticalTrack.find('>.jspDrag');
|
|
|
|
if (settings.showArrows) {
|
|
arrowUp = $('<a class="jspArrow jspArrowUp" />').bind(
|
|
'mousedown.jsp', getArrowScroll(0, -1)
|
|
).bind('click.jsp', nil);
|
|
arrowDown = $('<a class="jspArrow jspArrowDown" />').bind(
|
|
'mousedown.jsp', getArrowScroll(0, 1)
|
|
).bind('click.jsp', nil);
|
|
if (settings.arrowScrollOnHover) {
|
|
arrowUp.bind('mouseover.jsp', getArrowScroll(0, -1, arrowUp));
|
|
arrowDown.bind('mouseover.jsp', getArrowScroll(0, 1, arrowDown));
|
|
}
|
|
|
|
appendArrows(verticalTrack, settings.verticalArrowPositions, arrowUp, arrowDown);
|
|
}
|
|
|
|
verticalTrackHeight = paneHeight;
|
|
container.find('>.jspVerticalBar>.jspCap:visible,>.jspVerticalBar>.jspArrow').each(
|
|
function()
|
|
{
|
|
verticalTrackHeight -= $(this).outerHeight();
|
|
}
|
|
);
|
|
|
|
|
|
verticalDrag.hover(
|
|
function()
|
|
{
|
|
verticalDrag.addClass('jspHover');
|
|
},
|
|
function()
|
|
{
|
|
verticalDrag.removeClass('jspHover');
|
|
}
|
|
).bind(
|
|
'mousedown.jsp',
|
|
function(e)
|
|
{
|
|
// Stop IE from allowing text selection
|
|
$('html').bind('dragstart.jsp selectstart.jsp', nil);
|
|
|
|
verticalDrag.addClass('jspActive');
|
|
|
|
var startY = e.pageY - verticalDrag.position().top;
|
|
|
|
$('html').bind(
|
|
'mousemove.jsp',
|
|
function(e)
|
|
{
|
|
positionDragY(e.pageY - startY, false);
|
|
}
|
|
).bind('mouseup.jsp mouseleave.jsp', cancelDrag);
|
|
return false;
|
|
}
|
|
);
|
|
sizeVerticalScrollbar();
|
|
}
|
|
}
|
|
|
|
function sizeVerticalScrollbar()
|
|
{
|
|
verticalTrack.height(verticalTrackHeight + 'px');
|
|
verticalDragPosition = 0;
|
|
scrollbarWidth = settings.verticalGutter + verticalTrack.outerWidth();
|
|
|
|
// Make the pane thinner to allow for the vertical scrollbar
|
|
pane.width(paneWidth - scrollbarWidth - originalPaddingTotalWidth);
|
|
|
|
// Add margin to the left of the pane if scrollbars are on that side (to position
|
|
// the scrollbar on the left or right set it's left or right property in CSS)
|
|
try {
|
|
if (verticalBar.position().left === 0) {
|
|
pane.css('margin-left', scrollbarWidth + 'px');
|
|
}
|
|
} catch (err) {
|
|
}
|
|
}
|
|
|
|
function initialiseHorizontalScroll()
|
|
{
|
|
if (isScrollableH) {
|
|
|
|
container.append(
|
|
$('<div class="jspHorizontalBar" />').append(
|
|
$('<div class="jspCap jspCapLeft" />'),
|
|
$('<div class="jspTrack" />').append(
|
|
$('<div class="jspDrag" />').append(
|
|
$('<div class="jspDragLeft" />'),
|
|
$('<div class="jspDragRight" />')
|
|
)
|
|
),
|
|
$('<div class="jspCap jspCapRight" />')
|
|
)
|
|
);
|
|
|
|
horizontalBar = container.find('>.jspHorizontalBar');
|
|
horizontalTrack = horizontalBar.find('>.jspTrack');
|
|
horizontalDrag = horizontalTrack.find('>.jspDrag');
|
|
|
|
if (settings.showArrows) {
|
|
arrowLeft = $('<a class="jspArrow jspArrowLeft" />').bind(
|
|
'mousedown.jsp', getArrowScroll(-1, 0)
|
|
).bind('click.jsp', nil);
|
|
arrowRight = $('<a class="jspArrow jspArrowRight" />').bind(
|
|
'mousedown.jsp', getArrowScroll(1, 0)
|
|
).bind('click.jsp', nil);
|
|
if (settings.arrowScrollOnHover) {
|
|
arrowLeft.bind('mouseover.jsp', getArrowScroll(-1, 0, arrowLeft));
|
|
arrowRight.bind('mouseover.jsp', getArrowScroll(1, 0, arrowRight));
|
|
}
|
|
appendArrows(horizontalTrack, settings.horizontalArrowPositions, arrowLeft, arrowRight);
|
|
}
|
|
|
|
horizontalDrag.hover(
|
|
function()
|
|
{
|
|
horizontalDrag.addClass('jspHover');
|
|
},
|
|
function()
|
|
{
|
|
horizontalDrag.removeClass('jspHover');
|
|
}
|
|
).bind(
|
|
'mousedown.jsp',
|
|
function(e)
|
|
{
|
|
// Stop IE from allowing text selection
|
|
$('html').bind('dragstart.jsp selectstart.jsp', nil);
|
|
|
|
horizontalDrag.addClass('jspActive');
|
|
|
|
var startX = e.pageX - horizontalDrag.position().left;
|
|
|
|
$('html').bind(
|
|
'mousemove.jsp',
|
|
function(e)
|
|
{
|
|
positionDragX(e.pageX - startX, false);
|
|
}
|
|
).bind('mouseup.jsp mouseleave.jsp', cancelDrag);
|
|
return false;
|
|
}
|
|
);
|
|
horizontalTrackWidth = container.innerWidth();
|
|
sizeHorizontalScrollbar();
|
|
}
|
|
}
|
|
|
|
function sizeHorizontalScrollbar()
|
|
{
|
|
container.find('>.jspHorizontalBar>.jspCap:visible,>.jspHorizontalBar>.jspArrow').each(
|
|
function()
|
|
{
|
|
horizontalTrackWidth -= $(this).outerWidth();
|
|
}
|
|
);
|
|
|
|
horizontalTrack.width(horizontalTrackWidth + 'px');
|
|
horizontalDragPosition = 0;
|
|
}
|
|
|
|
function resizeScrollbars()
|
|
{
|
|
if (isScrollableH && isScrollableV) {
|
|
var horizontalTrackHeight = horizontalTrack.outerHeight(),
|
|
verticalTrackWidth = verticalTrack.outerWidth();
|
|
verticalTrackHeight -= horizontalTrackHeight;
|
|
$(horizontalBar).find('>.jspCap:visible,>.jspArrow').each(
|
|
function()
|
|
{
|
|
horizontalTrackWidth += $(this).outerWidth();
|
|
}
|
|
);
|
|
horizontalTrackWidth -= verticalTrackWidth;
|
|
paneHeight -= verticalTrackWidth;
|
|
paneWidth -= horizontalTrackHeight;
|
|
horizontalTrack.parent().append(
|
|
$('<div class="jspCorner" />').css('width', horizontalTrackHeight + 'px')
|
|
);
|
|
sizeVerticalScrollbar();
|
|
sizeHorizontalScrollbar();
|
|
}
|
|
// reflow content
|
|
if (isScrollableH) {
|
|
pane.width((container.outerWidth() - originalPaddingTotalWidth) + 'px');
|
|
}
|
|
contentHeight = pane.outerHeight();
|
|
percentInViewV = contentHeight / paneHeight;
|
|
|
|
if (isScrollableH) {
|
|
horizontalDragWidth = Math.ceil(1 / percentInViewH * horizontalTrackWidth);
|
|
if (horizontalDragWidth > settings.horizontalDragMaxWidth) {
|
|
horizontalDragWidth = settings.horizontalDragMaxWidth;
|
|
} else if (horizontalDragWidth < settings.horizontalDragMinWidth) {
|
|
horizontalDragWidth = settings.horizontalDragMinWidth;
|
|
}
|
|
horizontalDrag.width(horizontalDragWidth + 'px');
|
|
dragMaxX = horizontalTrackWidth - horizontalDragWidth;
|
|
_positionDragX(horizontalDragPosition); // To update the state for the arrow buttons
|
|
}
|
|
if (isScrollableV) {
|
|
verticalDragHeight = Math.ceil(1 / percentInViewV * verticalTrackHeight);
|
|
if (verticalDragHeight > settings.verticalDragMaxHeight) {
|
|
verticalDragHeight = settings.verticalDragMaxHeight;
|
|
} else if (verticalDragHeight < settings.verticalDragMinHeight) {
|
|
verticalDragHeight = settings.verticalDragMinHeight;
|
|
}
|
|
verticalDrag.height(verticalDragHeight + 'px');
|
|
dragMaxY = verticalTrackHeight - verticalDragHeight;
|
|
_positionDragY(verticalDragPosition); // To update the state for the arrow buttons
|
|
}
|
|
}
|
|
|
|
function appendArrows(ele, p, a1, a2)
|
|
{
|
|
var p1 = "before", p2 = "after", aTemp;
|
|
|
|
// Sniff for mac... Is there a better way to determine whether the arrows would naturally appear
|
|
// at the top or the bottom of the bar?
|
|
if (p == "os") {
|
|
p = /Mac/.test(navigator.platform) ? "after" : "split";
|
|
}
|
|
if (p == p1) {
|
|
p2 = p;
|
|
} else if (p == p2) {
|
|
p1 = p;
|
|
aTemp = a1;
|
|
a1 = a2;
|
|
a2 = aTemp;
|
|
}
|
|
|
|
ele[p1](a1)[p2](a2);
|
|
}
|
|
|
|
function getArrowScroll(dirX, dirY, ele)
|
|
{
|
|
return function()
|
|
{
|
|
arrowScroll(dirX, dirY, this, ele);
|
|
this.blur();
|
|
return false;
|
|
};
|
|
}
|
|
|
|
function arrowScroll(dirX, dirY, arrow, ele)
|
|
{
|
|
arrow = $(arrow).addClass('jspActive');
|
|
|
|
var eve,
|
|
scrollTimeout,
|
|
isFirst = true,
|
|
doScroll = function()
|
|
{
|
|
if (dirX !== 0) {
|
|
jsp.scrollByX(dirX * settings.arrowButtonSpeed);
|
|
}
|
|
if (dirY !== 0) {
|
|
jsp.scrollByY(dirY * settings.arrowButtonSpeed);
|
|
}
|
|
scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.arrowRepeatFreq);
|
|
isFirst = false;
|
|
};
|
|
|
|
doScroll();
|
|
|
|
eve = ele ? 'mouseout.jsp' : 'mouseup.jsp';
|
|
ele = ele || $('html');
|
|
ele.bind(
|
|
eve,
|
|
function()
|
|
{
|
|
arrow.removeClass('jspActive');
|
|
scrollTimeout && clearTimeout(scrollTimeout);
|
|
scrollTimeout = null;
|
|
ele.unbind(eve);
|
|
}
|
|
);
|
|
}
|
|
|
|
function initClickOnTrack()
|
|
{
|
|
removeClickOnTrack();
|
|
if (isScrollableV) {
|
|
verticalTrack.bind(
|
|
'mousedown.jsp',
|
|
function(e)
|
|
{
|
|
if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) {
|
|
var clickedTrack = $(this),
|
|
offset = clickedTrack.offset(),
|
|
direction = e.pageY - offset.top - verticalDragPosition,
|
|
scrollTimeout,
|
|
isFirst = true,
|
|
doScroll = function()
|
|
{
|
|
var offset = clickedTrack.offset(),
|
|
pos = e.pageY - offset.top - verticalDragHeight / 2,
|
|
contentDragY = paneHeight * settings.scrollPagePercent,
|
|
dragY = dragMaxY * contentDragY / (contentHeight - paneHeight);
|
|
if (direction < 0) {
|
|
if (verticalDragPosition - dragY > pos) {
|
|
jsp.scrollByY(-contentDragY);
|
|
} else {
|
|
positionDragY(pos);
|
|
}
|
|
} else if (direction > 0) {
|
|
if (verticalDragPosition + dragY < pos) {
|
|
jsp.scrollByY(contentDragY);
|
|
} else {
|
|
positionDragY(pos);
|
|
}
|
|
} else {
|
|
cancelClick();
|
|
return;
|
|
}
|
|
scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq);
|
|
isFirst = false;
|
|
},
|
|
cancelClick = function()
|
|
{
|
|
scrollTimeout && clearTimeout(scrollTimeout);
|
|
scrollTimeout = null;
|
|
$(document).unbind('mouseup.jsp', cancelClick);
|
|
};
|
|
doScroll();
|
|
$(document).bind('mouseup.jsp', cancelClick);
|
|
return false;
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
if (isScrollableH) {
|
|
horizontalTrack.bind(
|
|
'mousedown.jsp',
|
|
function(e)
|
|
{
|
|
if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) {
|
|
var clickedTrack = $(this),
|
|
offset = clickedTrack.offset(),
|
|
direction = e.pageX - offset.left - horizontalDragPosition,
|
|
scrollTimeout,
|
|
isFirst = true,
|
|
doScroll = function()
|
|
{
|
|
var offset = clickedTrack.offset(),
|
|
pos = e.pageX - offset.left - horizontalDragWidth / 2,
|
|
contentDragX = paneWidth * settings.scrollPagePercent,
|
|
dragX = dragMaxX * contentDragX / (contentWidth - paneWidth);
|
|
if (direction < 0) {
|
|
if (horizontalDragPosition - dragX > pos) {
|
|
jsp.scrollByX(-contentDragX);
|
|
} else {
|
|
positionDragX(pos);
|
|
}
|
|
} else if (direction > 0) {
|
|
if (horizontalDragPosition + dragX < pos) {
|
|
jsp.scrollByX(contentDragX);
|
|
} else {
|
|
positionDragX(pos);
|
|
}
|
|
} else {
|
|
cancelClick();
|
|
return;
|
|
}
|
|
scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq);
|
|
isFirst = false;
|
|
},
|
|
cancelClick = function()
|
|
{
|
|
scrollTimeout && clearTimeout(scrollTimeout);
|
|
scrollTimeout = null;
|
|
$(document).unbind('mouseup.jsp', cancelClick);
|
|
};
|
|
doScroll();
|
|
$(document).bind('mouseup.jsp', cancelClick);
|
|
return false;
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
function removeClickOnTrack()
|
|
{
|
|
if (horizontalTrack) {
|
|
horizontalTrack.unbind('mousedown.jsp');
|
|
}
|
|
if (verticalTrack) {
|
|
verticalTrack.unbind('mousedown.jsp');
|
|
}
|
|
}
|
|
|
|
function cancelDrag()
|
|
{
|
|
$('html').unbind('dragstart.jsp selectstart.jsp mousemove.jsp mouseup.jsp mouseleave.jsp');
|
|
|
|
if (verticalDrag) {
|
|
verticalDrag.removeClass('jspActive');
|
|
}
|
|
if (horizontalDrag) {
|
|
horizontalDrag.removeClass('jspActive');
|
|
}
|
|
}
|
|
|
|
function positionDragY(destY, animate)
|
|
{
|
|
if (!isScrollableV) {
|
|
return;
|
|
}
|
|
if (destY < 0) {
|
|
destY = 0;
|
|
} else if (destY > dragMaxY) {
|
|
destY = dragMaxY;
|
|
}
|
|
|
|
// can't just check if(animate) because false is a valid value that could be passed in...
|
|
if (animate === undefined) {
|
|
animate = settings.animateScroll;
|
|
}
|
|
if (animate) {
|
|
jsp.animate(verticalDrag, 'top', destY, _positionDragY);
|
|
} else {
|
|
verticalDrag.css('top', destY);
|
|
_positionDragY(destY);
|
|
}
|
|
|
|
}
|
|
|
|
function _positionDragY(destY)
|
|
{
|
|
if (destY === undefined) {
|
|
destY = verticalDrag.position().top;
|
|
}
|
|
|
|
container.scrollTop(0);
|
|
verticalDragPosition = destY;
|
|
|
|
var isAtTop = verticalDragPosition === 0,
|
|
isAtBottom = verticalDragPosition == dragMaxY,
|
|
percentScrolled = destY/ dragMaxY,
|
|
destTop = -percentScrolled * (contentHeight - paneHeight);
|
|
|
|
if (wasAtTop != isAtTop || wasAtBottom != isAtBottom) {
|
|
wasAtTop = isAtTop;
|
|
wasAtBottom = isAtBottom;
|
|
elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]);
|
|
}
|
|
|
|
updateVerticalArrows(isAtTop, isAtBottom);
|
|
pane.css('top', destTop);
|
|
elem.trigger('jsp-scroll-y', [-destTop, isAtTop, isAtBottom]).trigger('scroll');
|
|
}
|
|
|
|
function positionDragX(destX, animate)
|
|
{
|
|
if (!isScrollableH) {
|
|
return;
|
|
}
|
|
if (destX < 0) {
|
|
destX = 0;
|
|
} else if (destX > dragMaxX) {
|
|
destX = dragMaxX;
|
|
}
|
|
|
|
if (animate === undefined) {
|
|
animate = settings.animateScroll;
|
|
}
|
|
if (animate) {
|
|
jsp.animate(horizontalDrag, 'left', destX, _positionDragX);
|
|
} else {
|
|
horizontalDrag.css('left', destX);
|
|
_positionDragX(destX);
|
|
}
|
|
}
|
|
|
|
function _positionDragX(destX)
|
|
{
|
|
if (destX === undefined) {
|
|
destX = horizontalDrag.position().left;
|
|
}
|
|
|
|
container.scrollTop(0);
|
|
horizontalDragPosition = destX;
|
|
|
|
var isAtLeft = horizontalDragPosition === 0,
|
|
isAtRight = horizontalDragPosition == dragMaxX,
|
|
percentScrolled = destX / dragMaxX,
|
|
destLeft = -percentScrolled * (contentWidth - paneWidth);
|
|
|
|
if (wasAtLeft != isAtLeft || wasAtRight != isAtRight) {
|
|
wasAtLeft = isAtLeft;
|
|
wasAtRight = isAtRight;
|
|
elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]);
|
|
}
|
|
|
|
updateHorizontalArrows(isAtLeft, isAtRight);
|
|
pane.css('left', destLeft);
|
|
elem.trigger('jsp-scroll-x', [-destLeft, isAtLeft, isAtRight]).trigger('scroll');
|
|
}
|
|
|
|
function updateVerticalArrows(isAtTop, isAtBottom)
|
|
{
|
|
if (settings.showArrows) {
|
|
arrowUp[isAtTop ? 'addClass' : 'removeClass']('jspDisabled');
|
|
arrowDown[isAtBottom ? 'addClass' : 'removeClass']('jspDisabled');
|
|
}
|
|
}
|
|
|
|
function updateHorizontalArrows(isAtLeft, isAtRight)
|
|
{
|
|
if (settings.showArrows) {
|
|
arrowLeft[isAtLeft ? 'addClass' : 'removeClass']('jspDisabled');
|
|
arrowRight[isAtRight ? 'addClass' : 'removeClass']('jspDisabled');
|
|
}
|
|
}
|
|
|
|
function scrollToY(destY, animate)
|
|
{
|
|
var percentScrolled = destY / (contentHeight - paneHeight);
|
|
positionDragY(percentScrolled * dragMaxY, animate);
|
|
}
|
|
|
|
function scrollToX(destX, animate)
|
|
{
|
|
var percentScrolled = destX / (contentWidth - paneWidth);
|
|
positionDragX(percentScrolled * dragMaxX, animate);
|
|
}
|
|
|
|
function scrollToElement(ele, stickToTop, animate)
|
|
{
|
|
var e, eleHeight, eleWidth, eleTop = 0, eleLeft = 0, viewportTop, viewportLeft, maxVisibleEleTop, maxVisibleEleLeft, destY, destX;
|
|
|
|
// Legal hash values aren't necessarily legal jQuery selectors so we need to catch any
|
|
// errors from the lookup...
|
|
try {
|
|
e = $(ele);
|
|
} catch (err) {
|
|
return;
|
|
}
|
|
eleHeight = e.outerHeight();
|
|
eleWidth= e.outerWidth();
|
|
|
|
container.scrollTop(0);
|
|
container.scrollLeft(0);
|
|
|
|
// loop through parents adding the offset top of any elements that are relatively positioned between
|
|
// the focused element and the jspPane so we can get the true distance from the top
|
|
// of the focused element to the top of the scrollpane...
|
|
while (!e.is('.jspPane')) {
|
|
eleTop += e.position().top;
|
|
eleLeft += e.position().left;
|
|
e = e.offsetParent();
|
|
if (/^body|html$/i.test(e[0].nodeName)) {
|
|
// we ended up too high in the document structure. Quit!
|
|
return;
|
|
}
|
|
}
|
|
|
|
viewportTop = contentPositionY();
|
|
maxVisibleEleTop = viewportTop + paneHeight;
|
|
if (eleTop < viewportTop || stickToTop) { // element is above viewport
|
|
destY = eleTop - settings.verticalGutter;
|
|
} else if (eleTop + eleHeight > maxVisibleEleTop) { // element is below viewport
|
|
destY = eleTop - paneHeight + eleHeight + settings.verticalGutter;
|
|
}
|
|
if (destY) {
|
|
scrollToY(destY, animate);
|
|
}
|
|
|
|
viewportLeft = contentPositionX();
|
|
maxVisibleEleLeft = viewportLeft + paneWidth;
|
|
if (eleLeft < viewportLeft || stickToTop) { // element is to the left of viewport
|
|
destX = eleLeft - settings.horizontalGutter;
|
|
} else if (eleLeft + eleWidth > maxVisibleEleLeft) { // element is to the right viewport
|
|
destX = eleLeft - paneWidth + eleWidth + settings.horizontalGutter;
|
|
}
|
|
if (destX) {
|
|
scrollToX(destX, animate);
|
|
}
|
|
|
|
}
|
|
|
|
function contentPositionX()
|
|
{
|
|
return -pane.position().left;
|
|
}
|
|
|
|
function contentPositionY()
|
|
{
|
|
return -pane.position().top;
|
|
}
|
|
|
|
function isCloseToBottom()
|
|
{
|
|
var scrollableHeight = contentHeight - paneHeight;
|
|
return (scrollableHeight > 20) && (scrollableHeight - contentPositionY() < 10);
|
|
}
|
|
|
|
function isCloseToRight()
|
|
{
|
|
var scrollableWidth = contentWidth - paneWidth;
|
|
return (scrollableWidth > 20) && (scrollableWidth - contentPositionX() < 10);
|
|
}
|
|
|
|
function initMousewheel()
|
|
{
|
|
container.unbind(mwEvent).bind(
|
|
mwEvent,
|
|
function (event, delta, deltaX, deltaY) {
|
|
var dX = horizontalDragPosition, dY = verticalDragPosition;
|
|
jsp.scrollBy(deltaX * settings.mouseWheelSpeed, -deltaY * settings.mouseWheelSpeed, false);
|
|
// return true if there was no movement so rest of screen can scroll
|
|
return dX == horizontalDragPosition && dY == verticalDragPosition;
|
|
}
|
|
);
|
|
}
|
|
|
|
function removeMousewheel()
|
|
{
|
|
container.unbind(mwEvent);
|
|
}
|
|
|
|
function nil()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function initFocusHandler()
|
|
{
|
|
pane.find(':input,a').unbind('focus.jsp').bind(
|
|
'focus.jsp',
|
|
function(e)
|
|
{
|
|
scrollToElement(e.target, false);
|
|
}
|
|
);
|
|
}
|
|
|
|
function removeFocusHandler()
|
|
{
|
|
pane.find(':input,a').unbind('focus.jsp');
|
|
}
|
|
|
|
function initKeyboardNav()
|
|
{
|
|
var keyDown, elementHasScrolled, validParents = [];
|
|
isScrollableH && validParents.push(horizontalBar[0]);
|
|
isScrollableV && validParents.push(verticalBar[0]);
|
|
|
|
// IE also focuses elements that don't have tabindex set.
|
|
pane.focus(
|
|
function()
|
|
{
|
|
elem.focus();
|
|
}
|
|
);
|
|
|
|
elem.attr('tabindex', 0)
|
|
.unbind('keydown.jsp keypress.jsp')
|
|
.bind(
|
|
'keydown.jsp',
|
|
function(e)
|
|
{
|
|
if (e.target !== this && !(validParents.length && $(e.target).closest(validParents).length)){
|
|
return;
|
|
}
|
|
var dX = horizontalDragPosition, dY = verticalDragPosition;
|
|
switch(e.keyCode) {
|
|
case 40: // down
|
|
case 38: // up
|
|
case 34: // page down
|
|
case 32: // space
|
|
case 33: // page up
|
|
case 39: // right
|
|
case 37: // left
|
|
keyDown = e.keyCode;
|
|
keyDownHandler();
|
|
break;
|
|
case 35: // end
|
|
scrollToY(contentHeight - paneHeight);
|
|
keyDown = null;
|
|
break;
|
|
case 36: // home
|
|
scrollToY(0);
|
|
keyDown = null;
|
|
break;
|
|
}
|
|
|
|
elementHasScrolled = e.keyCode == keyDown && dX != horizontalDragPosition || dY != verticalDragPosition;
|
|
return !elementHasScrolled;
|
|
}
|
|
).bind(
|
|
'keypress.jsp', // For FF/ OSX so that we can cancel the repeat key presses if the JSP scrolls...
|
|
function(e)
|
|
{
|
|
if (e.keyCode == keyDown) {
|
|
keyDownHandler();
|
|
}
|
|
return !elementHasScrolled;
|
|
}
|
|
);
|
|
|
|
if (settings.hideFocus) {
|
|
elem.css('outline', 'none');
|
|
if ('hideFocus' in container[0]){
|
|
elem.attr('hideFocus', true);
|
|
}
|
|
} else {
|
|
elem.css('outline', '');
|
|
if ('hideFocus' in container[0]){
|
|
elem.attr('hideFocus', false);
|
|
}
|
|
}
|
|
|
|
function keyDownHandler()
|
|
{
|
|
var dX = horizontalDragPosition, dY = verticalDragPosition;
|
|
switch(keyDown) {
|
|
case 40: // down
|
|
jsp.scrollByY(settings.keyboardSpeed, false);
|
|
break;
|
|
case 38: // up
|
|
jsp.scrollByY(-settings.keyboardSpeed, false);
|
|
break;
|
|
case 34: // page down
|
|
case 32: // space
|
|
jsp.scrollByY(paneHeight * settings.scrollPagePercent, false);
|
|
break;
|
|
case 33: // page up
|
|
jsp.scrollByY(-paneHeight * settings.scrollPagePercent, false);
|
|
break;
|
|
case 39: // right
|
|
jsp.scrollByX(settings.keyboardSpeed, false);
|
|
break;
|
|
case 37: // left
|
|
jsp.scrollByX(-settings.keyboardSpeed, false);
|
|
break;
|
|
}
|
|
|
|
elementHasScrolled = dX != horizontalDragPosition || dY != verticalDragPosition;
|
|
return elementHasScrolled;
|
|
}
|
|
}
|
|
|
|
function removeKeyboardNav()
|
|
{
|
|
elem.attr('tabindex', '-1')
|
|
.removeAttr('tabindex')
|
|
.unbind('keydown.jsp keypress.jsp');
|
|
}
|
|
|
|
function observeHash()
|
|
{
|
|
if (location.hash && location.hash.length > 1) {
|
|
var e,
|
|
retryInt,
|
|
hash = escape(location.hash.substr(1)) // hash must be escaped to prevent XSS
|
|
;
|
|
try {
|
|
e = $('#' + hash + ', a[name="' + hash + '"]');
|
|
} catch (err) {
|
|
return;
|
|
}
|
|
|
|
if (e.length && pane.find(hash)) {
|
|
// nasty workaround but it appears to take a little while before the hash has done its thing
|
|
// to the rendered page so we just wait until the container's scrollTop has been messed up.
|
|
if (container.scrollTop() === 0) {
|
|
retryInt = setInterval(
|
|
function()
|
|
{
|
|
if (container.scrollTop() > 0) {
|
|
scrollToElement(e, true);
|
|
$(document).scrollTop(container.position().top);
|
|
clearInterval(retryInt);
|
|
}
|
|
},
|
|
50
|
|
);
|
|
} else {
|
|
scrollToElement(e, true);
|
|
$(document).scrollTop(container.position().top);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function hijackInternalLinks()
|
|
{
|
|
// only register the link handler once
|
|
if ($(document.body).data('jspHijack')) {
|
|
return;
|
|
}
|
|
|
|
// remember that the handler was bound
|
|
$(document.body).data('jspHijack', true);
|
|
|
|
// use live handler to also capture newly created links
|
|
$(document.body).delegate('a[href*=#]', 'click', function(event) {
|
|
// does the link point to the same page?
|
|
// this also takes care of cases with a <base>-Tag or Links not starting with the hash #
|
|
// e.g. <a href="index.html#test"> when the current url already is index.html
|
|
var href = this.href.substr(0, this.href.indexOf('#')),
|
|
locationHref = location.href,
|
|
hash,
|
|
element,
|
|
container,
|
|
jsp,
|
|
scrollTop,
|
|
elementTop;
|
|
if (location.href.indexOf('#') !== -1) {
|
|
locationHref = location.href.substr(0, location.href.indexOf('#'));
|
|
}
|
|
if (href !== locationHref) {
|
|
// the link points to another page
|
|
return;
|
|
}
|
|
|
|
// check if jScrollPane should handle this click event
|
|
hash = escape(this.href.substr(this.href.indexOf('#') + 1));
|
|
|
|
// find the element on the page
|
|
element;
|
|
try {
|
|
element = $('#' + hash + ', a[name="' + hash + '"]');
|
|
} catch (e) {
|
|
// hash is not a valid jQuery identifier
|
|
return;
|
|
}
|
|
|
|
if (!element.length) {
|
|
// this link does not point to an element on this page
|
|
return;
|
|
}
|
|
|
|
container = element.closest('.jspScrollable');
|
|
jsp = container.data('jsp');
|
|
|
|
// jsp might be another jsp instance than the one, that bound this event
|
|
// remember: this event is only bound once for all instances.
|
|
jsp.scrollToElement(element, true);
|
|
|
|
if (container[0].scrollIntoView) {
|
|
// also scroll to the top of the container (if it is not visible)
|
|
scrollTop = $(window).scrollTop();
|
|
elementTop = element.offset().top;
|
|
if (elementTop < scrollTop || elementTop > scrollTop + $(window).height()) {
|
|
container[0].scrollIntoView();
|
|
}
|
|
}
|
|
|
|
// jsp handled this event, prevent the browser default (scrolling :P)
|
|
event.preventDefault();
|
|
});
|
|
}
|
|
|
|
// Init touch on iPad, iPhone, iPod, Android
|
|
function initTouch()
|
|
{
|
|
var startX,
|
|
startY,
|
|
touchStartX,
|
|
touchStartY,
|
|
moved,
|
|
moving = false;
|
|
|
|
container.unbind('touchstart.jsp touchmove.jsp touchend.jsp click.jsp-touchclick').bind(
|
|
'touchstart.jsp',
|
|
function(e)
|
|
{
|
|
var touch = e.originalEvent.touches[0];
|
|
startX = contentPositionX();
|
|
startY = contentPositionY();
|
|
touchStartX = touch.pageX;
|
|
touchStartY = touch.pageY;
|
|
moved = false;
|
|
moving = true;
|
|
}
|
|
).bind(
|
|
'touchmove.jsp',
|
|
function(ev)
|
|
{
|
|
if(!moving) {
|
|
return;
|
|
}
|
|
|
|
var touchPos = ev.originalEvent.touches[0],
|
|
dX = horizontalDragPosition, dY = verticalDragPosition;
|
|
|
|
jsp.scrollTo(startX + touchStartX - touchPos.pageX, startY + touchStartY - touchPos.pageY);
|
|
|
|
moved = moved || Math.abs(touchStartX - touchPos.pageX) > 5 || Math.abs(touchStartY - touchPos.pageY) > 5;
|
|
|
|
// return true if there was no movement so rest of screen can scroll
|
|
return dX == horizontalDragPosition && dY == verticalDragPosition;
|
|
}
|
|
).bind(
|
|
'touchend.jsp',
|
|
function(e)
|
|
{
|
|
moving = false;
|
|
/*if(moved) {
|
|
return false;
|
|
}*/
|
|
}
|
|
).bind(
|
|
'click.jsp-touchclick',
|
|
function(e)
|
|
{
|
|
if(moved) {
|
|
moved = false;
|
|
return false;
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
function destroy(){
|
|
var currentY = contentPositionY(),
|
|
currentX = contentPositionX();
|
|
elem.removeClass('jspScrollable').unbind('.jsp');
|
|
elem.replaceWith(originalElement.append(pane.children()));
|
|
originalElement.scrollTop(currentY);
|
|
originalElement.scrollLeft(currentX);
|
|
|
|
// clear reinitialize timer if active
|
|
if (reinitialiseInterval) {
|
|
clearInterval(reinitialiseInterval);
|
|
}
|
|
}
|
|
|
|
// Public API
|
|
$.extend(
|
|
jsp,
|
|
{
|
|
// Reinitialises the scroll pane (if it's internal dimensions have changed since the last time it
|
|
// was initialised). The settings object which is passed in will override any settings from the
|
|
// previous time it was initialised - if you don't pass any settings then the ones from the previous
|
|
// initialisation will be used.
|
|
reinitialise: function(s)
|
|
{
|
|
s = $.extend({}, settings, s);
|
|
initialise(s);
|
|
},
|
|
// Scrolls the specified element (a jQuery object, DOM node or jQuery selector string) into view so
|
|
// that it can be seen within the viewport. If stickToTop is true then the element will appear at
|
|
// the top of the viewport, if it is false then the viewport will scroll as little as possible to
|
|
// show the element. You can also specify if you want animation to occur. If you don't provide this
|
|
// argument then the animateScroll value from the settings object is used instead.
|
|
scrollToElement: function(ele, stickToTop, animate)
|
|
{
|
|
scrollToElement(ele, stickToTop, animate);
|
|
},
|
|
// Scrolls the pane so that the specified co-ordinates within the content are at the top left
|
|
// of the viewport. animate is optional and if not passed then the value of animateScroll from
|
|
// the settings object this jScrollPane was initialised with is used.
|
|
scrollTo: function(destX, destY, animate)
|
|
{
|
|
scrollToX(destX, animate);
|
|
scrollToY(destY, animate);
|
|
},
|
|
// Scrolls the pane so that the specified co-ordinate within the content is at the left of the
|
|
// viewport. animate is optional and if not passed then the value of animateScroll from the settings
|
|
// object this jScrollPane was initialised with is used.
|
|
scrollToX: function(destX, animate)
|
|
{
|
|
scrollToX(destX, animate);
|
|
},
|
|
// Scrolls the pane so that the specified co-ordinate within the content is at the top of the
|
|
// viewport. animate is optional and if not passed then the value of animateScroll from the settings
|
|
// object this jScrollPane was initialised with is used.
|
|
scrollToY: function(destY, animate)
|
|
{
|
|
scrollToY(destY, animate);
|
|
},
|
|
// Scrolls the pane to the specified percentage of its maximum horizontal scroll position. animate
|
|
// is optional and if not passed then the value of animateScroll from the settings object this
|
|
// jScrollPane was initialised with is used.
|
|
scrollToPercentX: function(destPercentX, animate)
|
|
{
|
|
scrollToX(destPercentX * (contentWidth - paneWidth), animate);
|
|
},
|
|
// Scrolls the pane to the specified percentage of its maximum vertical scroll position. animate
|
|
// is optional and if not passed then the value of animateScroll from the settings object this
|
|
// jScrollPane was initialised with is used.
|
|
scrollToPercentY: function(destPercentY, animate)
|
|
{
|
|
scrollToY(destPercentY * (contentHeight - paneHeight), animate);
|
|
},
|
|
// Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
|
|
// the value of animateScroll from the settings object this jScrollPane was initialised with is used.
|
|
scrollBy: function(deltaX, deltaY, animate)
|
|
{
|
|
jsp.scrollByX(deltaX, animate);
|
|
jsp.scrollByY(deltaY, animate);
|
|
},
|
|
// Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
|
|
// the value of animateScroll from the settings object this jScrollPane was initialised with is used.
|
|
scrollByX: function(deltaX, animate)
|
|
{
|
|
var destX = contentPositionX() + Math[deltaX<0 ? 'floor' : 'ceil'](deltaX),
|
|
percentScrolled = destX / (contentWidth - paneWidth);
|
|
positionDragX(percentScrolled * dragMaxX, animate);
|
|
},
|
|
// Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
|
|
// the value of animateScroll from the settings object this jScrollPane was initialised with is used.
|
|
scrollByY: function(deltaY, animate)
|
|
{
|
|
var destY = contentPositionY() + Math[deltaY<0 ? 'floor' : 'ceil'](deltaY),
|
|
percentScrolled = destY / (contentHeight - paneHeight);
|
|
positionDragY(percentScrolled * dragMaxY, animate);
|
|
},
|
|
// Positions the horizontal drag at the specified x position (and updates the viewport to reflect
|
|
// this). animate is optional and if not passed then the value of animateScroll from the settings
|
|
// object this jScrollPane was initialised with is used.
|
|
positionDragX: function(x, animate)
|
|
{
|
|
positionDragX(x, animate);
|
|
},
|
|
// Positions the vertical drag at the specified y position (and updates the viewport to reflect
|
|
// this). animate is optional and if not passed then the value of animateScroll from the settings
|
|
// object this jScrollPane was initialised with is used.
|
|
positionDragY: function(y, animate)
|
|
{
|
|
positionDragY(y, animate);
|
|
},
|
|
// This method is called when jScrollPane is trying to animate to a new position. You can override
|
|
// it if you want to provide advanced animation functionality. It is passed the following arguments:
|
|
// * ele - the element whose position is being animated
|
|
// * prop - the property that is being animated
|
|
// * value - the value it's being animated to
|
|
// * stepCallback - a function that you must execute each time you update the value of the property
|
|
// You can use the default implementation (below) as a starting point for your own implementation.
|
|
animate: function(ele, prop, value, stepCallback)
|
|
{
|
|
var params = {};
|
|
params[prop] = value;
|
|
ele.animate(
|
|
params,
|
|
{
|
|
'duration' : settings.animateDuration,
|
|
'easing' : settings.animateEase,
|
|
'queue' : false,
|
|
'step' : stepCallback
|
|
}
|
|
);
|
|
},
|
|
// Returns the current x position of the viewport with regards to the content pane.
|
|
getContentPositionX: function()
|
|
{
|
|
return contentPositionX();
|
|
},
|
|
// Returns the current y position of the viewport with regards to the content pane.
|
|
getContentPositionY: function()
|
|
{
|
|
return contentPositionY();
|
|
},
|
|
// Returns the width of the content within the scroll pane.
|
|
getContentWidth: function()
|
|
{
|
|
return contentWidth;
|
|
},
|
|
// Returns the height of the content within the scroll pane.
|
|
getContentHeight: function()
|
|
{
|
|
return contentHeight;
|
|
},
|
|
// Returns the horizontal position of the viewport within the pane content.
|
|
getPercentScrolledX: function()
|
|
{
|
|
return contentPositionX() / (contentWidth - paneWidth);
|
|
},
|
|
// Returns the vertical position of the viewport within the pane content.
|
|
getPercentScrolledY: function()
|
|
{
|
|
return contentPositionY() / (contentHeight - paneHeight);
|
|
},
|
|
// Returns whether or not this scrollpane has a horizontal scrollbar.
|
|
getIsScrollableH: function()
|
|
{
|
|
return isScrollableH;
|
|
},
|
|
// Returns whether or not this scrollpane has a vertical scrollbar.
|
|
getIsScrollableV: function()
|
|
{
|
|
return isScrollableV;
|
|
},
|
|
// Gets a reference to the content pane. It is important that you use this method if you want to
|
|
// edit the content of your jScrollPane as if you access the element directly then you may have some
|
|
// problems (as your original element has had additional elements for the scrollbars etc added into
|
|
// it).
|
|
getContentPane: function()
|
|
{
|
|
return pane;
|
|
},
|
|
// Scrolls this jScrollPane down as far as it can currently scroll. If animate isn't passed then the
|
|
// animateScroll value from settings is used instead.
|
|
scrollToBottom: function(animate)
|
|
{
|
|
positionDragY(dragMaxY, animate);
|
|
},
|
|
// Hijacks the links on the page which link to content inside the scrollpane. If you have changed
|
|
// the content of your page (e.g. via AJAX) and want to make sure any new anchor links to the
|
|
// contents of your scroll pane will work then call this function.
|
|
hijackInternalLinks: $.noop,
|
|
// Removes the jScrollPane and returns the page to the state it was in before jScrollPane was
|
|
// initialised.
|
|
destroy: function()
|
|
{
|
|
destroy();
|
|
}
|
|
}
|
|
);
|
|
|
|
initialise(s);
|
|
}
|
|
|
|
// Pluginifying code...
|
|
settings = $.extend({}, $.fn.jScrollPane.defaults, settings);
|
|
|
|
// Apply default speed
|
|
$.each(['mouseWheelSpeed', 'arrowButtonSpeed', 'trackClickSpeed', 'keyboardSpeed'], function() {
|
|
settings[this] = settings[this] || settings.speed;
|
|
});
|
|
|
|
return this.each(
|
|
function()
|
|
{
|
|
var elem = $(this), jspApi = elem.data('jsp');
|
|
if (jspApi) {
|
|
jspApi.reinitialise(settings);
|
|
} else {
|
|
$("script",elem).filter('[type="text/javascript"],:not([type])').remove();
|
|
jspApi = new JScrollPane(elem, settings);
|
|
elem.data('jsp', jspApi);
|
|
}
|
|
}
|
|
);
|
|
};
|
|
|
|
$.fn.jScrollPane.defaults = {
|
|
showArrows : false,
|
|
maintainPosition : true,
|
|
stickToBottom : false,
|
|
stickToRight : false,
|
|
clickOnTrack : true,
|
|
autoReinitialise : false,
|
|
autoReinitialiseDelay : 500,
|
|
verticalDragMinHeight : 0,
|
|
verticalDragMaxHeight : 99999,
|
|
horizontalDragMinWidth : 0,
|
|
horizontalDragMaxWidth : 99999,
|
|
contentWidth : undefined,
|
|
animateScroll : false,
|
|
animateDuration : 300,
|
|
animateEase : 'linear',
|
|
hijackInternalLinks : false,
|
|
verticalGutter : 4,
|
|
horizontalGutter : 4,
|
|
mouseWheelSpeed : 0,
|
|
arrowButtonSpeed : 0,
|
|
arrowRepeatFreq : 50,
|
|
arrowScrollOnHover : false,
|
|
trackClickSpeed : 0,
|
|
trackClickRepeatFreq : 70,
|
|
verticalArrowPositions : 'split',
|
|
horizontalArrowPositions : 'split',
|
|
enableKeyboardNavigation : true,
|
|
hideFocus : false,
|
|
keyboardSpeed : 0,
|
|
initialDelay : 300, // Delay before starting repeating
|
|
speed : 30, // Default speed when others falsey
|
|
scrollPagePercent : .8 // Percent of visible area scrolled when pageUp/Down or track area pressed
|
|
};
|
|
|
|
})(jQuery,this);
|