Return-Path: X-Original-To: apmail-geode-commits-archive@minotaur.apache.org Delivered-To: apmail-geode-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 4A34718440 for ; Fri, 22 Jan 2016 23:26:27 +0000 (UTC) Received: (qmail 73625 invoked by uid 500); 22 Jan 2016 23:26:27 -0000 Delivered-To: apmail-geode-commits-archive@geode.apache.org Received: (qmail 73587 invoked by uid 500); 22 Jan 2016 23:26:27 -0000 Mailing-List: contact commits-help@geode.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@geode.incubator.apache.org Delivered-To: mailing list commits@geode.incubator.apache.org Received: (qmail 73574 invoked by uid 99); 22 Jan 2016 23:26:27 -0000 Received: from Unknown (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 22 Jan 2016 23:26:27 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id 72FFFC05FF for ; Fri, 22 Jan 2016 23:26:26 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 0.979 X-Spam-Level: X-Spam-Status: No, score=0.979 tagged_above=-999 required=6.31 tests=[KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.001] autolearn=disabled Received: from mx1-eu-west.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id 5e5YPvx2nDGz for ; Fri, 22 Jan 2016 23:26:15 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-eu-west.apache.org (ASF Mail Server at mx1-eu-west.apache.org) with SMTP id D934831B0E for ; Fri, 22 Jan 2016 23:25:46 +0000 (UTC) Received: (qmail 67980 invoked by uid 99); 22 Jan 2016 23:25:43 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 22 Jan 2016 23:25:43 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 11510E3901; Fri, 22 Jan 2016 23:25:43 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: upthewaterspout@apache.org To: commits@geode.incubator.apache.org Date: Fri, 22 Jan 2016 23:25:55 -0000 Message-Id: In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [14/51] [abbrv] incubator-geode git commit: GEODE-12: update copyright headers http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ec004fc1/gemfire-pulse/src/main/webapp/scripts/lib/jquery.jscrollpane.js ---------------------------------------------------------------------- diff --git a/gemfire-pulse/src/main/webapp/scripts/lib/jquery.jscrollpane.js b/gemfire-pulse/src/main/webapp/scripts/lib/jquery.jscrollpane.js index b43a61c..74d4563 100644 --- a/gemfire-pulse/src/main/webapp/scripts/lib/jquery.jscrollpane.js +++ b/gemfire-pulse/src/main/webapp/scripts/lib/jquery.jscrollpane.js @@ -1,1340 +1,1359 @@ -(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 = $('
').css('padding', originalPadding).append(elem.children()); - container = $('
') - .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(); - unhijackInternalLinks(); - } 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( - $('
').append( - $('
'), - $('
').append( - $('
').append( - $('
'), - $('
') - ) - ), - $('
') - ) - ); - - verticalBar = container.find('>.jspVerticalBar'); - verticalTrack = verticalBar.find('>.jspTrack'); - verticalDrag = verticalTrack.find('>.jspDrag'); - - if (settings.showArrows) { - arrowUp = $('').bind( - 'mousedown.jsp', getArrowScroll(0, -1) - ).bind('click.jsp', nil); - arrowDown = $('').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( - $('
').append( - $('
'), - $('
').append( - $('
').append( - $('
'), - $('
') - ) - ), - $('
') - ) - ); - - horizontalBar = container.find('>.jspHorizontalBar'); - horizontalTrack = horizontalBar.find('>.jspTrack'); - horizontalDrag = horizontalTrack.find('>.jspDrag'); - - if (settings.showArrows) { - arrowLeft = $('').bind( - 'mousedown.jsp', getArrowScroll(-1, 0) - ).bind('click.jsp', nil); - arrowRight = $('').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( - $('
').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) // hash must be escaped to prevent XSS - ; - try { - e = $(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(hash, true); - $(document).scrollTop(container.position().top); - clearInterval(retryInt); - } - }, - 50 - ); - } else { - scrollToElement(hash, true); - $(document).scrollTop(container.position().top); - } - } - } - } - - function unhijackInternalLinks() - { - $('a.jspHijack').unbind('click.jsp-hijack').removeClass('jspHijack'); - } - - function hijackInternalLinks() - { - unhijackInternalLinks(); - $('a[href^=#]').addClass('jspHijack').bind( - 'click.jsp-hijack', - function() - { - var uriParts = this.href.split('#'), hash; - if (uriParts.length > 1) { - hash = uriParts[1]; - if (hash.length > 0 && pane.find('#' + hash).length > 0) { - scrollToElement('#' + hash, true); - // Need to return false otherwise things mess up... Would be nice to maybe also scroll - // the window to the top of the scrollpane? - return false; - } - } - } - ); - } - - // 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: function() - { - hijackInternalLinks(); - }, - // 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 { - 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); - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +(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 = $('
').css('padding', originalPadding).append(elem.children()); + container = $('
') + .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(); + unhijackInternalLinks(); + } 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( + $('
').append( + $('
'), + $('
').append( + $('
').append( + $('
'), + $('
') + ) + ), + $('
') + ) + ); + + verticalBar = container.find('>.jspVerticalBar'); + verticalTrack = verticalBar.find('>.jspTrack'); + verticalDrag = verticalTrack.find('>.jspDrag'); + + if (settings.showArrows) { + arrowUp = $('').bind( + 'mousedown.jsp', getArrowScroll(0, -1) + ).bind('click.jsp', nil); + arrowDown = $('').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( + $('
').append( + $('
'), + $('
').append( + $('
').append( + $('
'), + $('
') + ) + ), + $('
') + ) + ); + + horizontalBar = container.find('>.jspHorizontalBar'); + horizontalTrack = horizontalBar.find('>.jspTrack'); + horizontalDrag = horizontalTrack.find('>.jspDrag'); + + if (settings.showArrows) { + arrowLeft = $('').bind( + 'mousedown.jsp', getArrowScroll(-1, 0) + ).bind('click.jsp', nil); + arrowRight = $('').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( + $('
').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) // hash must be escaped to prevent XSS + ; + try { + e = $(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(hash, true); + $(document).scrollTop(container.position().top); + clearInterval(retryInt); + } + }, + 50 + ); + } else { + scrollToElement(hash, true); + $(document).scrollTop(container.position().top); + } + } + } + } + + function unhijackInternalLinks() + { + $('a.jspHijack').unbind('click.jsp-hijack').removeClass('jspHijack'); + } + + function hijackInternalLinks() + { + unhijackInternalLinks(); + $('a[href^=#]').addClass('jspHijack').bind( + 'click.jsp-hijack', + function() + { + var uriParts = this.href.split('#'), hash; + if (uriParts.length > 1) { + hash = uriParts[1]; + if (hash.length > 0 && pane.find('#' + hash).length > 0) { + scrollToElement('#' + hash, true); + // Need to return false otherwise things mess up... Would be nice to maybe also scroll + // the window to the top of the scrollpane? + return false; + } + } + } + ); + } + + // 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: function() + { + hijackInternalLinks(); + }, + // 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 { + 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