PageSourceSearch

https://www.tsuritama.com/js/jquery.jscrollpane.js

js tsuritama.com collected 2026-10-02 17:49:07 UTC 45,893 bytes, 1,390 lines download raw bytes

1/*!
2 * jScrollPane - v2.0.0beta11 - 2011-06-11
3 * http://jscrollpane.kelvinluck.com/
4 *
5 * Copyright (c) 2010 Kelvin Luck
6 * Dual licensed under the MIT and GPL licenses.
7 */
8
9// Script: jScrollPane - cross browser customisable scrollbars
10//
11// *Version: 2.0.0beta11, Last updated: 2011-06-11*
12//
13// Project Home - http://jscrollpane.kelvinluck.com/
14// GitHub       - http://github.com/vitch/jScrollPane
15// Source       - http://github.com/vitch/jScrollPane/raw/master/script/jquery.jscrollpane.js
16// (Minified)   - http://github.com/vitch/jScrollPane/raw/master/script/jquery.jscrollpane.min.js
17//
18// About: License
19//
20// Copyright (c) 2011 Kelvin Luck
21// Dual licensed under the MIT or GPL Version 2 licenses.
22// http://jscrollpane.kelvinluck.com/MIT-LICENSE.txt
23// http://jscrollpane.kelvinluck.com/GPL-LICENSE.txt
24//
25// About: Examples
26//
27// All examples and demos are available through the jScrollPane example site at:
28// http://jscrollpane.kelvinluck.com/
29//
30// About: Support and Testing
31//
32// This plugin is tested on the browsers below and has been found to work reliably on them. If you run
33// into a problem on one of the supported browsers then please visit the support section on the jScrollPane
34// website (http://jscrollpane.kelvinluck.com/) for more information on getting support. You are also
35// welcome to fork the project on GitHub if you can contribute a fix for a given issue. 
36//
37// jQuery Versions - tested in 1.4.2+ - reported to work in 1.3.x
38// Browsers Tested - Firefox 3.6.8, Safari 5, Opera 10.6, Chrome 5.0, IE 6, 7, 8
39//
40// About: Release History
41//
42// 2.0.0beta11 - (in progress) 
43// 2.0.0beta10 - (2011-04-17) cleaner required size calculation, improved keyboard support, stickToBottom/Left, other small fixes
44// 2.0.0beta9 - (2011-01-31) new API methods, bug fixes and correct keyboard support for FF/OSX
45// 2.0.0beta8 - (2011-01-29) touchscreen support, improved keyboard support
46// 2.0.0beta7 - (2011-01-23) scroll speed consistent (thanks Aivo Paas)
47// 2.0.0beta6 - (2010-12-07) scrollToElement horizontal support
48// 2.0.0beta5 - (2010-10-18) jQuery 1.4.3 support, various bug fixes
49// 2.0.0beta4 - (2010-09-17) clickOnTrack support, bug fixes
50// 2.0.0beta3 - (2010-08-27) Horizontal mousewheel, mwheelIntent, keyboard support, bug fixes
51// 2.0.0beta2 - (2010-08-21) Bug fixes
52// 2.0.0beta1 - (2010-08-17) Rewrite to follow modern best practices and enable horizontal scrolling, initially hidden
53//							 elements and dynamically sized elements.
54// 1.x - (2006-12-31 - 2010-07-31) Initial version, hosted at googlecode, deprecated
55
56(function($,window,undefined){
57
58	$.fn.jScrollPane = function(settings)
59	{
60		// JScrollPane "class" - public methods are available through $('selector').data('jsp')
61		function JScrollPane(elem, s)
62		{
63			var settings, jsp = this, pane, paneWidth, paneHeight, container, contentWidth, contentHeight,
64				percentInViewH, percentInViewV, isScrollableV, isScrollableH, verticalDrag, dragMaxY,
65				verticalDragPosition, horizontalDrag, dragMaxX, horizontalDragPosition,
66				verticalBar, verticalTrack, scrollbarWidth, verticalTrackHeight, verticalDragHeight, arrowUp, arrowDown,
67				horizontalBar, horizontalTrack, horizontalTrackWidth, horizontalDragWidth, arrowLeft, arrowRight,
68				reinitialiseInterval, originalPadding, originalPaddingTotalWidth, previousContentWidth,
69				wasAtTop = true, wasAtLeft = true, wasAtBottom = false, wasAtRight = false,
70				originalElement = elem.clone(false, false).empty(),
71				mwEvent = $.fn.mwheelIntent ? 'mwheelIntent.jsp' : 'mousewheel.jsp';
72
73			originalPadding = elem.css('paddingTop') + ' ' +
74								elem.css('paddingRight') + ' ' +
75								elem.css('paddingBottom') + ' ' +
76								elem.css('paddingLeft');
77			originalPaddingTotalWidth = (parseInt(elem.css('paddingLeft'), 10) || 0) +
78										(parseInt(elem.css('paddingRight'), 10) || 0);
79
80			function initialise(s)
81			{
82
83				var /*firstChild, lastChild, */isMaintainingPositon, lastContentX, lastContentY,
84						hasContainingSpaceChanged, originalScrollTop, originalScrollLeft,
85						maintainAtBottom = false, maintainAtRight = false;
86
87				settings = s;
88
89				if (pane === undefined) {
90					originalScrollTop = elem.scrollTop();
91					originalScrollLeft = elem.scrollLeft();
92
93					elem.css(
94						{
95							overflow: 'hidden',
96							padding: 0
97						}
98					);
99					// TODO: Deal with where width/ height is 0 as it probably means the element is hidden and we should
100					// come back to it later and check once it is unhidden...
101					paneWidth = elem.innerWidth() + originalPaddingTotalWidth;
102					paneHeight = elem.innerHeight();
103
104					elem.width(paneWidth);
105					
106					pane = $('<div class="jspPane" />').css('padding', originalPadding).append(elem.children());
107					container = $('<div class="jspContainer" />')
108						.css({
109							'width': paneWidth + 'px',
110							'height': paneHeight + 'px'
111						}
112					).append(pane).appendTo(elem);
113
114					/*
115					// Move any margins from the first and last children up to the container so they can still
116					// collapse with neighbouring elements as they would before jScrollPane 
117					firstChild = pane.find(':first-child');
118					lastChild = pane.find(':last-child');
119					elem.css(
120						{
121							'margin-top': firstChild.css('margin-top'),
122							'margin-bottom': lastChild.css('margin-bottom')
123						}
124					);
125					firstChild.css('margin-top', 0);
126					lastChild.css('margin-bottom', 0);
127					*/
128				} else {
129					elem.css('width', '');
130
131					maintainAtBottom = settings.stickToBottom && isCloseToBottom();
132					maintainAtRight  = settings.stickToRight  && isCloseToRight();
133
134					hasContainingSpaceChanged = elem.innerWidth() + originalPaddingTotalWidth != paneWidth || elem.outerHeight() != paneHeight;
135
136					if (hasContainingSpaceChanged) {
137						paneWidth = elem.innerWidth() + originalPaddingTotalWidth;
138						paneHeight = elem.innerHeight();
139						container.css({
140							width: paneWidth + 'px',
141							height: paneHeight + 'px'
142						});
143					}
144
145					// If nothing changed since last check...
146					if (!hasContainingSpaceChanged && previousContentWidth == contentWidth && pane.outerHeight() == contentHeight) {
147						elem.width(paneWidth);
148						return;
149					}
150					previousContentWidth = contentWidth;
151					
152					pane.css('width', '');
153					elem.width(paneWidth);
154
155					container.find('>.jspVerticalBar,>.jspHorizontalBar').remove().end();
156				}
157
158				pane.css('overflow', 'auto');
159				if (s.contentWidth) {
160					contentWidth = s.contentWidth;
161				} else {
162					contentWidth = pane[0].scrollWidth;
163				}
164				contentHeight = pane[0].scrollHeight;
165				pane.css('overflow', '');
166
167				percentInViewH = contentWidth / paneWidth;
168				percentInViewV = contentHeight / paneHeight;
169				isScrollableV = percentInViewV > 1;
170
171				isScrollableH = percentInViewH > 1;
172
173				//console.log(paneWidth, paneHeight, contentWidth, contentHeight, percentInViewH, percentInViewV, isScrollableH, isScrollableV);
174
175				if (!(isScrollableH || isScrollableV)) {
176					elem.removeClass('jspScrollable');
177					pane.css({
178						top: 0,
179						width: container.width() - originalPaddingTotalWidth
180					});
181					removeMousewheel();
182					removeFocusHandler();
183					removeKeyboardNav();
184					removeClickOnTrack();
185					unhijackInternalLinks();
186				} else {
187					elem.addClass('jspScrollable');
188
189					isMaintainingPositon = settings.maintainPosition && (verticalDragPosition || horizontalDragPosition);
190					if (isMaintainingPositon) {
191						lastContentX = contentPositionX();
192						lastContentY = contentPositionY();
193					}
194
195					initialiseVerticalScroll();
196					initialiseHorizontalScroll();
197					resizeScrollbars();
198
199					if (isMaintainingPositon) {
200						scrollToX(maintainAtRight  ? (contentWidth  - paneWidth ) : lastContentX, false);
201						scrollToY(maintainAtBottom ? (contentHeight - paneHeight) : lastContentY, false);
202					}
203
204					initFocusHandler();
205					initMousewheel();
206					initTouch();
207					
208					if (settings.enableKeyboardNavigation) {
209						initKeyboardNav();
210					}
211					if (settings.clickOnTrack) {
212						initClickOnTrack();
213					}
214					
215					observeHash();
216					if (settings.hijackInternalLinks) {
217						hijackInternalLinks();
218					}
219				}
220
221				if (settings.autoReinitialise && !reinitialiseInterval) {
222					reinitialiseInterval = setInterval(
223						function()
224						{
225							initialise(settings);
226						},
227						settings.autoReinitialiseDelay
228					);
229				} else if (!settings.autoReinitialise && reinitialiseInterval) {
230					clearInterval(reinitialiseInterval);
231				}
232
233				originalScrollTop && elem.scrollTop(0) && scrollToY(originalScrollTop, false);
234				originalScrollLeft && elem.scrollLeft(0) && scrollToX(originalScrollLeft, false);
235
236				elem.trigger('jsp-initialised', [isScrollableH || isScrollableV]);
237			}
238
239			function initialiseVerticalScroll()
240			{
241				if (isScrollableV) {
242
243					container.append(
244						$('<div class="jspVerticalBar" />').append(
245							$('<div class="jspCap jspCapTop" />'),
246							$('<div class="jspTrack" />').append(
247								$('<div class="jspDrag" />').append(
248									$('<div class="jspDragTop" />'),
249									$('<div class="jspDragBottom" />')
250								)
251							),
252							$('<div class="jspCap jspCapBottom" />')
253						)
254					);
255
256					verticalBar = container.find('>.jspVerticalBar');
257					verticalTrack = verticalBar.find('>.jspTrack');
258					verticalDrag = verticalTrack.find('>.jspDrag');
259
260					if (settings.showArrows) {
261						arrowUp = $('<a class="jspArrow jspArrowUp" />').bind(
262							'mousedown.jsp', getArrowScroll(0, -1)
263						).bind('click.jsp', nil);
264						arrowDown = $('<a class="jspArrow jspArrowDown" />').bind(
265							'mousedown.jsp', getArrowScroll(0, 1)
266						).bind('click.jsp', nil);
267						if (settings.arrowScrollOnHover) {
268							arrowUp.bind('mouseover.jsp', getArrowScroll(0, -1, arrowUp));
269							arrowDown.bind('mouseover.jsp', getArrowScroll(0, 1, arrowDown));
270						}
271
272						appendArrows(verticalTrack, settings.verticalArrowPositions, arrowUp, arrowDown);
273					}
274
275					verticalTrackHeight = paneHeight;
276					container.find('>.jspVerticalBar>.jspCap:visible,>.jspVerticalBar>.jspArrow').each(
277						function()
278						{
279							verticalTrackHeight -= $(this).outerHeight();
280						}
281					);
282
283
284					verticalDrag.hover(
285						function()
286						{
287							verticalDrag.addClass('jspHover');
288						},
289						function()
290						{
291							verticalDrag.removeClass('jspHover');
292						}
293					).bind(
294						'mousedown.jsp',
295						function(e)
296						{
297							// Stop IE from allowing text selection
298							$('html').bind('dragstart.jsp selectstart.jsp', nil);
299
300							verticalDrag.addClass('jspActive');
301
302							var startY = e.pageY - verticalDrag.position().top;
303
304							$('html').bind(
305								'mousemove.jsp',
306								function(e)
307								{
308									positionDragY(e.pageY - startY, false);
309								}
310							).bind('mouseup.jsp mouseleave.jsp', cancelDrag);
311							return false;
312						}
313					);
314					sizeVerticalScrollbar();
315				}
316			}
317
318			function sizeVerticalScrollbar()
319			{
320				verticalTrack.height(verticalTrackHeight + 'px');
321				verticalDragPosition = 0;
322				scrollbarWidth = settings.verticalGutter + verticalTrack.outerWidth();
323
324				// Make the pane thinner to allow for the vertical scrollbar
325				pane.width(paneWidth - scrollbarWidth - originalPaddingTotalWidth);
326
327				// Add margin to the left of the pane if scrollbars are on that side (to position
328				// the scrollbar on the left or right set it's left or right property in CSS)
329				try {
330					if (verticalBar.position().left === 0) {
331						pane.css('margin-left', scrollbarWidth + 'px');
332					}
333				} catch (err) {
334				}
335			}
336
337			function initialiseHorizontalScroll()
338			{
339				if (isScrollableH) {
340
341					container.append(
342						$('<div class="jspHorizontalBar" />').append(
343							$('<div class="jspCap jspCapLeft" />'),
344							$('<div class="jspTrack" />').append(
345								$('<div class="jspDrag" />').append(
346									$('<div class="jspDragLeft" />'),
347									$('<div class="jspDragRight" />')
348								)
349							),
350							$('<div class="jspCap jspCapRight" />')
351						)
352					);
353
354					horizontalBar = container.find('>.jspHorizontalBar');
355					horizontalTrack = horizontalBar.find('>.jspTrack');
356					horizontalDrag = horizontalTrack.find('>.jspDrag');
357
358					if (settings.showArrows) {
359						arrowLeft = $('<a class="jspArrow jspArrowLeft" />').bind(
360							'mousedown.jsp', getArrowScroll(-1, 0)
361						).bind('click.jsp', nil);
362						arrowRight = $('<a class="jspArrow jspArrowRight" />').bind(
363							'mousedown.jsp', getArrowScroll(1, 0)
364						).bind('click.jsp', nil);
365						if (settings.arrowScrollOnHover) {
366							arrowLeft.bind('mouseover.jsp', getArrowScroll(-1, 0, arrowLeft));
367							arrowRight.bind('mouseover.jsp', getArrowScroll(1, 0, arrowRight));
368						}
369						appendArrows(horizontalTrack, settings.horizontalArrowPositions, arrowLeft, arrowRight);
370					}
371
372					horizontalDrag.hover(
373						function()
374						{
375							horizontalDrag.addClass('jspHover');
376						},
377						function()
378						{
379							horizontalDrag.removeClass('jspHover');
380						}
381					).bind(
382						'mousedown.jsp',
383						function(e)
384						{
385							// Stop IE from allowing text selection
386							$('html').bind('dragstart.jsp selectstart.jsp', nil);
387
388							horizontalDrag.addClass('jspActive');
389
390							var startX = e.pageX - horizontalDrag.position().left;
391
392							$('html').bind(
393								'mousemove.jsp',
394								function(e)
395								{
396									positionDragX(e.pageX - startX, false);
397								}
398							).bind('mouseup.jsp mouseleave.jsp', cancelDrag);
399							return false;
400						}
401					);
402					horizontalTrackWidth = container.innerWidth();
403					sizeHorizontalScrollbar();
404				}
405			}
406
407			function sizeHorizontalScrollbar()
408			{
409				container.find('>.jspHorizontalBar>.jspCap:visible,>.jspHorizontalBar>.jspArrow').each(
410					function()
411					{
412						horizontalTrackWidth -= $(this).outerWidth();
413					}
414				);
415
416				horizontalTrack.width(horizontalTrackWidth + 'px');
417				horizontalDragPosition = 0;
418			}
419
420			function resizeScrollbars()
421			{
422				if (isScrollableH && isScrollableV) {
423					var horizontalTrackHeight = horizontalTrack.outerHeight(),
424						verticalTrackWidth = verticalTrack.outerWidth();
425					verticalTrackHeight -= horizontalTrackHeight;
426					$(horizontalBar).find('>.jspCap:visible,>.jspArrow').each(
427						function()
428						{
429							horizontalTrackWidth += $(this).outerWidth();
430						}
431					);
432					horizontalTrackWidth -= verticalTrackWidth;
433					paneHeight -= verticalTrackWidth;
434					paneWidth -= horizontalTrackHeight;
435					horizontalTrack.parent().append(
436						$('<div class="jspCorner" />').css('width', horizontalTrackHeight + 'px')
437					);
438					sizeVerticalScrollbar();
439					sizeHorizontalScrollbar();
440				}
441				// reflow content
442				if (isScrollableH) {
443					pane.width((container.outerWidth() - originalPaddingTotalWidth) + 'px');
444				}
445				contentHeight = pane.outerHeight();
446				percentInViewV = contentHeight / paneHeight;
447
448				if (isScrollableH) {
449					horizontalDragWidth = Math.ceil(1 / percentInViewH * horizontalTrackWidth);
450					if (horizontalDragWidth > settings.horizontalDragMaxWidth) {
451						horizontalDragWidth = settings.horizontalDragMaxWidth;
452					} else if (horizontalDragWidth < settings.horizontalDragMinWidth) {
453						horizontalDragWidth = settings.horizontalDragMinWidth;
454					}
455					horizontalDrag.width(horizontalDragWidth + 'px');
456					dragMaxX = horizontalTrackWidth - horizontalDragWidth;
457					_positionDragX(horizontalDragPosition); // To update the state for the arrow buttons
458				}
459				if (isScrollableV) {
460					verticalDragHeight = Math.ceil(1 / percentInViewV * (verticalTrackHeight-8));
461					if (verticalDragHeight > settings.verticalDragMaxHeight) {
462						verticalDragHeight = settings.verticalDragMaxHeight;
463					} else if (verticalDragHeight < settings.verticalDragMinHeight) {
464						verticalDragHeight = settings.verticalDragMinHeight;
465					}
466					verticalDrag.height(verticalDragHeight + 'px');
467					dragMaxY = verticalTrackHeight - verticalDragHeight;
468					_positionDragY(verticalDragPosition); // To update the state for the arrow buttons
469				}
470			}
471
472			function appendArrows(ele, p, a1, a2)
473			{
474				var p1 = "before", p2 = "after", aTemp;
475				
476				// Sniff for mac... Is there a better way to determine whether the arrows would naturally appear
477				// at the top or the bottom of the bar?
478				if (p == "os") {
479					p = /Mac/.test(navigator.platform) ? "after" : "split";
480				}
481				if (p == p1) {
482					p2 = p;
483				} else if (p == p2) {
484					p1 = p;
485					aTemp = a1;
486					a1 = a2;
487					a2 = aTemp;
488				}
489
490				ele[p1](a1)[p2](a2);
491			}
492
493			function getArrowScroll(dirX, dirY, ele)
494			{
495				return function()
496				{
497					arrowScroll(dirX, dirY, this, ele);
498					this.blur();
499					return false;
500				};
501			}
502
503			function arrowScroll(dirX, dirY, arrow, ele)
504			{
505				arrow = $(arrow).addClass('jspActive');
506
507				var eve,
508					scrollTimeout,
509					isFirst = true,
510					doScroll = function()
511					{
512						if (dirX !== 0) {
513							jsp.scrollByX(dirX * settings.arrowButtonSpeed);
514						}
515						if (dirY !== 0) {
516							jsp.scrollByY(dirY * settings.arrowButtonSpeed);
517						}
518						scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.arrowRepeatFreq);
519						isFirst = false;
520					};
521
522				doScroll();
523
524				eve = ele ? 'mouseout.jsp' : 'mouseup.jsp';
525				ele = ele || $('html');
526				ele.bind(
527					eve,
528					function()
529					{
530						arrow.removeClass('jspActive');
531						scrollTimeout && clearTimeout(scrollTimeout);
532						scrollTimeout = null;
533						ele.unbind(eve);
534					}
535				);
536			}
537
538			function initClickOnTrack()
539			{
540				removeClickOnTrack();
541				if (isScrollableV) {
542					verticalTrack.bind(
543						'mousedown.jsp',
544						function(e)
545						{
546							if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) {
547								var clickedTrack = $(this),
548									offset = clickedTrack.offset(),
549									direction = e.pageY - offset.top - verticalDragPosition,
550									scrollTimeout,
551									isFirst = true,
552									doScroll = function()
553									{
554										var offset = clickedTrack.offset(),
555											pos = e.pageY - offset.top - verticalDragHeight / 2,
556											contentDragY = paneHeight * settings.scrollPagePercent,
557											dragY = dragMaxY * contentDragY / (contentHeight - paneHeight);
558										if (direction < 0) {
559											if (verticalDragPosition - dragY > pos) {
560												jsp.scrollByY(-contentDragY);
561											} else {
562												positionDragY(pos);
563											}
564										} else if (direction > 0) {
565											if (verticalDragPosition + dragY < pos) {
566												jsp.scrollByY(contentDragY);
567											} else {
568												positionDragY(pos);
569											}
570										} else {
571											cancelClick();
572											return;
573										}
574										scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq);
575										isFirst = false;
576									},
577									cancelClick = function()
578									{
579										scrollTimeout && clearTimeout(scrollTimeout);
580										scrollTimeout = null;
581										$(document).unbind('mouseup.jsp', cancelClick);
582									};
583								doScroll();
584								$(document).bind('mouseup.jsp', cancelClick);
585								return false;
586							}
587						}
588					);
589				}
590				
591				if (isScrollableH) {
592					horizontalTrack.bind(
593						'mousedown.jsp',
594						function(e)
595						{
596							if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) {
597								var clickedTrack = $(this),
598									offset = clickedTrack.offset(),
599									direction = e.pageX - offset.left - horizontalDragPosition,
600									scrollTimeout,
601									isFirst = true,
602									doScroll = function()
603									{
604										var offset = clickedTrack.offset(),
605											pos = e.pageX - offset.left - horizontalDragWidth / 2,
606											contentDragX = paneWidth * settings.scrollPagePercent,
607											dragX = dragMaxX * contentDragX / (contentWidth - paneWidth);
608										if (direction < 0) {
609											if (horizontalDragPosition - dragX > pos) {
610												jsp.scrollByX(-contentDragX);
611											} else {
612												positionDragX(pos);
613											}
614										} else if (direction > 0) {
615											if (horizontalDragPosition + dragX < pos) {
616												jsp.scrollByX(contentDragX);
617											} else {
618												positionDragX(pos);
619											}
620										} else {
621											cancelClick();
622											return;
623										}
624										scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq);
625										isFirst = false;
626									},
627									cancelClick = function()
628									{
629										scrollTimeout && clearTimeout(scrollTimeout);
630										scrollTimeout = null;
631										$(document).unbind('mouseup.jsp', cancelClick);
632									};
633								doScroll();
634								$(document).bind('mouseup.jsp', cancelClick);
635								return false;
636							}
637						}
638					);
639				}
640			}
641
642			function removeClickOnTrack()
643			{
644				if (horizontalTrack) {
645					horizontalTrack.unbind('mousedown.jsp');
646				}
647				if (verticalTrack) {
648					verticalTrack.unbind('mousedown.jsp');
649				}
650			}
651
652			function cancelDrag()
653			{
654				$('html').unbind('dragstart.jsp selectstart.jsp mousemove.jsp mouseup.jsp mouseleave.jsp');
655
656				if (verticalDrag) {
657					verticalDrag.removeClass('jspActive');
658				}
659				if (horizontalDrag) {
660					horizontalDrag.removeClass('jspActive');
661				}
662			}
663
664			function positionDragY(destY, animate)
665			{
666				if (!isScrollableV) {
667					return;
668				}
669				if (destY < 0) {
670					destY = 0;
671				} else if (destY > dragMaxY) {
672					destY = dragMaxY;
673				}
674
675				// can't just check if(animate) because false is a valid value that could be passed in...
676				if (animate === undefined) {
677					animate = settings.animateScroll;
678				}
679				if (animate) {
680					jsp.animate(verticalDrag, 'top', destY,	_positionDragY);
681				} else {
682					verticalDrag.css('top', destY);
683					_positionDragY(destY);
684				}
685
686			}
687
688			function _positionDragY(destY)
689			{
690				if (destY === undefined) {
691					destY = verticalDrag.position().top;
692				}
693
694				container.scrollTop(0);
695				verticalDragPosition = destY;
696
697				var isAtTop = verticalDragPosition === 0,
698					isAtBottom = verticalDragPosition == dragMaxY,
699					percentScrolled = destY/ dragMaxY,
700					destTop = -percentScrolled * (contentHeight - paneHeight);
701
702				if (wasAtTop != isAtTop || wasAtBottom != isAtBottom) {
703					wasAtTop = isAtTop;
704					wasAtBottom = isAtBottom;
705					elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]);
706				}
707				
708				updateVerticalArrows(isAtTop, isAtBottom);
709				pane.css('top', destTop);
710				elem.trigger('jsp-scroll-y', [-destTop, isAtTop, isAtBottom]).trigger('scroll');
711			}
712
713			function positionDragX(destX, animate)
714			{
715				if (!isScrollableH) {
716					return;
717				}
718				if (destX < 0) {
719					destX = 0;
720				} else if (destX > dragMaxX) {
721					destX = dragMaxX;
722				}
723
724				if (animate === undefined) {
725					animate = settings.animateScroll;
726				}
727				if (animate) {
728					jsp.animate(horizontalDrag, 'left', destX,	_positionDragX);
729				} else {
730					horizontalDrag.css('left', destX);
731					_positionDragX(destX);
732				}
733			}
734
735			function _positionDragX(destX)
736			{
737				if (destX === undefined) {
738					destX = horizontalDrag.position().left;
739				}
740
741				container.scrollTop(0);
742				horizontalDragPosition = destX;
743
744				var isAtLeft = horizontalDragPosition === 0,
745					isAtRight = horizontalDragPosition == dragMaxX,
746					percentScrolled = destX / dragMaxX,
747					destLeft = -percentScrolled * (contentWidth - paneWidth);
748
749				if (wasAtLeft != isAtLeft || wasAtRight != isAtRight) {
750					wasAtLeft = isAtLeft;
751					wasAtRight = isAtRight;
752					elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]);
753				}
754				
755				updateHorizontalArrows(isAtLeft, isAtRight);
756				pane.css('left', destLeft);
757				elem.trigger('jsp-scroll-x', [-destLeft, isAtLeft, isAtRight]).trigger('scroll');
758			}
759
760			function updateVerticalArrows(isAtTop, isAtBottom)
761			{
762				if (settings.showArrows) {
763					arrowUp[isAtTop ? 'addClass' : 'removeClass']('jspDisabled');
764					arrowDown[isAtBottom ? 'addClass' : 'removeClass']('jspDisabled');
765				}
766			}
767
768			function updateHorizontalArrows(isAtLeft, isAtRight)
769			{
770				if (settings.showArrows) {
771					arrowLeft[isAtLeft ? 'addClass' : 'removeClass']('jspDisabled');
772					arrowRight[isAtRight ? 'addClass' : 'removeClass']('jspDisabled');
773				}
774			}
775
776			function scrollToY(destY, animate)
777			{
778				var percentScrolled = destY / (contentHeight - paneHeight);
779				positionDragY(percentScrolled * dragMaxY, animate);
780			}
781
782			function scrollToX(destX, animate)
783			{
784				var percentScrolled = destX / (contentWidth - paneWidth);
785				positionDragX(percentScrolled * dragMaxX, animate);
786			}
787
788			function scrollToElement(ele, stickToTop, animate)
789			{
790				var e, eleHeight, eleWidth, eleTop = 0, eleLeft = 0, viewportTop, viewportLeft, maxVisibleEleTop, maxVisibleEleLeft, destY, destX;
791
792				// Legal hash values aren't necessarily legal jQuery selectors so we need to catch any
793				// errors from the lookup...
794				try {
795					e = $(ele);
796				} catch (err) {
797					return;
798				}
799				eleHeight = e.outerHeight();
800				eleWidth= e.outerWidth();
801
802				container.scrollTop(0);
803				container.scrollLeft(0);
804				
805				// loop through parents adding the offset top of any elements that are relatively positioned between
806				// the focused element and the jspPane so we can get the true distance from the top
807				// of the focused element to the top of the scrollpane...
808				while (!e.is('.jspPane')) {
809					eleTop += e.position().top;
810					eleLeft += e.position().left;
811					e = e.offsetParent();
812					if (/^body|html$/i.test(e[0].nodeName)) {
813						// we ended up too high in the document structure. Quit!
814						return;
815					}
816				}
817
818				viewportTop = contentPositionY();
819				maxVisibleEleTop = viewportTop + paneHeight;
820				if (eleTop < viewportTop || stickToTop) { // element is above viewport
821					destY = eleTop - settings.verticalGutter;
822				} else if (eleTop + eleHeight > maxVisibleEleTop) { // element is below viewport
823					destY = eleTop - paneHeight + eleHeight + settings.verticalGutter;
824				}
825				if (destY) {
826					scrollToY(destY, animate);
827				}
828				
829				viewportLeft = contentPositionX();
830	            maxVisibleEleLeft = viewportLeft + paneWidth;
831	            if (eleLeft < viewportLeft || stickToTop) { // element is to the left of viewport
832	                destX = eleLeft - settings.horizontalGutter;
833	            } else if (eleLeft + eleWidth > maxVisibleEleLeft) { // element is to the right viewport
834	                destX = eleLeft - paneWidth + eleWidth + settings.horizontalGutter;
835	            }
836	            if (destX) {
837	                scrollToX(destX, animate);
838	            }
839
840			}
841
842			function contentPositionX()
843			{
844				return -pane.position().left;
845			}
846
847			function contentPositionY()
848			{
849				return -pane.position().top;
850			}
851
852			function isCloseToBottom()
853			{
854				var scrollableHeight = contentHeight - paneHeight;
855				return (scrollableHeight > 20) && (scrollableHeight - contentPositionY() < 10);
856			}
857
858			function isCloseToRight()
859			{
860				var scrollableWidth = contentWidth - paneWidth;
861				return (scrollableWidth > 20) && (scrollableWidth - contentPositionX() < 10);
862			}
863
864			function initMousewheel()
865			{
866				container.unbind(mwEvent).bind(
867					mwEvent,
868					function (event, delta, deltaX, deltaY) {
869						var dX = horizontalDragPosition, dY = verticalDragPosition;
870						jsp.scrollBy(deltaX * settings.mouseWheelSpeed, -deltaY * settings.mouseWheelSpeed, false);
871						// return true if there was no movement so rest of screen can scroll
872						return dX == horizontalDragPosition && dY == verticalDragPosition;
873					}
874				);
875			}
876
877			function removeMousewheel()
878			{
879				container.unbind(mwEvent);
880			}
881
882			function nil()
883			{
884				return false;
885			}
886
887			function initFocusHandler()
888			{
889				pane.find(':input,a').unbind('focus.jsp').bind(
890					'focus.jsp',
891					function(e)
892					{
893						scrollToElement(e.target, false);
894					}
895				);
896			}
897
898			function removeFocusHandler()
899			{
900				pane.find(':input,a').unbind('focus.jsp');
901			}
902			
903			function initKeyboardNav()
904			{
905				var keyDown, elementHasScrolled, validParents = [];
906				isScrollableH && validParents.push(horizontalBar[0]);
907				isScrollableV && validParents.push(verticalBar[0]);
908				
909				// IE also focuses elements that don't have tabindex set.
910				pane.focus(
911					function()
912					{
913						elem.focus();
914					}
915				);
916				
917				elem.attr('tabindex', 0)
918					.unbind('keydown.jsp keypress.jsp')
919					.bind(
920						'keydown.jsp',
921						function(e)
922						{
923							if (e.target !== this && !(validParents.length && $(e.target).closest(validParents).length)){
924								return;
925							}
926							var dX = horizontalDragPosition, dY = verticalDragPosition;
927							switch(e.keyCode) {
928								case 40: // down
929								case 38: // up
930								case 34: // page down
931								case 32: // space
932								case 33: // page up
933								case 39: // right
934								case 37: // left
935									keyDown = e.keyCode;
936									keyDownHandler();
937									break;
938								case 35: // end
939									scrollToY(contentHeight - paneHeight);
940									keyDown = null;
941									break;
942								case 36: // home
943									scrollToY(0);
944									keyDown = null;
945									break;
946							}
947
948							elementHasScrolled = e.keyCode == keyDown && dX != horizontalDragPosition || dY != verticalDragPosition;
949							return !elementHasScrolled;
950						}
951					).bind(
952						'keypress.jsp', // For FF/ OSX so that we can cancel the repeat key presses if the JSP scrolls...
953						function(e)
954						{
955							if (e.keyCode == keyDown) {
956								keyDownHandler();
957							}
958							return !elementHasScrolled;
959						}
960					);
961				
962				if (settings.hideFocus) {
963					elem.css('outline', 'none');
964					if ('hideFocus' in container[0]){
965						elem.attr('hideFocus', true);
966					}
967				} else {
968					elem.css('outline', '');
969					if ('hideFocus' in container[0]){
970						elem.attr('hideFocus', false);
971					}
972				}
973				
974				function keyDownHandler()
975				{
976					var dX = horizontalDragPosition, dY = verticalDragPosition;
977					switch(keyDown) {
978						case 40: // down
979							jsp.scrollByY(settings.keyboardSpeed, false);
980							break;
981						case 38: // up
982							jsp.scrollByY(-settings.keyboardSpeed, false);
983							break;
984						case 34: // page down
985						case 32: // space
986							jsp.scrollByY(paneHeight * settings.scrollPagePercent, false);
987							break;
988						case 33: // page up
989							jsp.scrollByY(-paneHeight * settings.scrollPagePercent, false);
990							break;
991						case 39: // right
992							jsp.scrollByX(settings.keyboardSpeed, false);
993							break;
994						case 37: // left
995							jsp.scrollByX(-settings.keyboardSpeed, false);
996							break;
997					}
998
999					elementHasScrolled = dX != horizontalDragPosition || dY != verticalDragPosition;
1000					return elementHasScrolled;
1001				}
1002			}
1003			
1004			function removeKeyboardNav()
1005			{
1006				elem.attr('tabindex', '-1')
1007					.removeAttr('tabindex')
1008					.unbind('keydown.jsp keypress.jsp');
1009			}
1010
1011			function observeHash()
1012			{
1013				if (location.hash && location.hash.length > 1) {
1014					var e,
1015						retryInt,
1016						hash = escape(location.hash) // hash must be escaped to prevent XSS
1017						;
1018					try {
1019						e = $(hash);
1020					} catch (err) {
1021						return;
1022					}
1023
1024					if (e.length && pane.find(hash)) {
1025						// nasty workaround but it appears to take a little while before the hash has done its thing
1026						// to the rendered page so we just wait until the container's scrollTop has been messed up.
1027						if (container.scrollTop() === 0) {
1028							retryInt = setInterval(
1029								function()
1030								{
1031									if (container.scrollTop() > 0) {
1032										scrollToElement(hash, true);
1033										$(document).scrollTop(container.position().top);
1034										clearInterval(retryInt);
1035									}
1036								},
1037								50
1038							);
1039						} else {
1040							scrollToElement(hash, true);
1041							$(document).scrollTop(container.position().top);
1042						}
1043					}
1044				}
1045			}
1046
1047			function unhijackInternalLinks()
1048			{
1049				$('a.jspHijack').unbind('click.jsp-hijack').removeClass('jspHijack');
1050			}
1051
1052			function hijackInternalLinks()
1053			{
1054				unhijackInternalLinks();
1055				$('a[href^=#]').addClass('jspHijack').bind(
1056					'click.jsp-hijack',
1057					function()
1058					{
1059						var uriParts = this.href.split('#'), hash;
1060						if (uriParts.length > 1) {
1061							hash = uriParts[1];
1062							if (hash.length > 0 && pane.find('#' + hash).length > 0) {
1063								scrollToElement('#' + hash, true);
1064								// Need to return false otherwise things mess up... Would be nice to maybe also scroll
1065								// the window to the top of the scrollpane?
1066								return false;
1067							}
1068						}
1069					}
1070				);
1071			}
1072			
1073			// Init touch on iPad, iPhone, iPod, Android
1074			function initTouch()
1075			{
1076				var startX,
1077					startY,
1078					touchStartX,
1079					touchStartY,
1080					moved,
1081					moving = false;
1082  
1083				container.unbind('touchstart.jsp touchmove.jsp touchend.jsp click.jsp-touchclick').bind(
1084					'touchstart.jsp',
1085					function(e)
1086					{
1087						var touch = e.originalEvent.touches[0];
1088						startX = contentPositionX();
1089						startY = contentPositionY();
1090						touchStartX = touch.pageX;
1091						touchStartY = touch.pageY;
1092						moved = false;
1093						moving = true;
1094					}
1095				).bind(
1096					'touchmove.jsp',
1097					function(ev)
1098					{
1099						if(!moving) {
1100							return;
1101						}
1102						
1103						var touchPos = ev.originalEvent.touches[0],
1104							dX = horizontalDragPosition, dY = verticalDragPosition;
1105						
1106						jsp.scrollTo(startX + touchStartX - touchPos.pageX, startY + touchStartY - touchPos.pageY);
1107						
1108						moved = moved || Math.abs(touchStartX - touchPos.pageX) > 5 || Math.abs(touchStartY - touchPos.pageY) > 5;
1109						
1110						// return true if there was no movement so rest of screen can scroll
1111						return dX == horizontalDragPosition && dY == verticalDragPosition;
1112					}
1113				).bind(
1114					'touchend.jsp',
1115					function(e)
1116					{
1117						moving = false;
1118						/*if(moved) {
1119							return false;
1120						}*/
1121					}
1122				).bind(
1123					'click.jsp-touchclick',
1124					function(e)
1125					{
1126						if(moved) {
1127							moved = false;
1128							return false;
1129						}
1130					}
1131				);
1132			}
1133			
1134			function destroy(){
1135				var currentY = contentPositionY(),
1136					currentX = contentPositionX();
1137				elem.removeClass('jspScrollable').unbind('.jsp');
1138				elem.replaceWith(originalElement.append(pane.children()));
1139				originalElement.scrollTop(currentY);
1140				originalElement.scrollLeft(currentX);
1141			}
1142
1143			// Public API
1144			$.extend(
1145				jsp,
1146				{
1147					// Reinitialises the scroll pane (if it's internal dimensions have changed since the last time it
1148					// was initialised). The settings object which is passed in will override any settings from the
1149					// previous time it was initialised - if you don't pass any settings then the ones from the previous
1150					// initialisation will be used.
1151					reinitialise: function(s)
1152					{
1153						s = $.extend({}, settings, s);
1154						initialise(s);
1155					},
1156					// Scrolls the specified element (a jQuery object, DOM node or jQuery selector string) into view so
1157					// that it can be seen within the viewport. If stickToTop is true then the element will appear at
1158					// the top of the viewport, if it is false then the viewport will scroll as little as possible to
1159					// show the element. You can also specify if you want animation to occur. If you don't provide this
1160					// argument then the animateScroll value from the settings object is used instead.
1161					scrollToElement: function(ele, stickToTop, animate)
1162					{
1163						scrollToElement(ele, stickToTop, animate);
1164					},
1165					// Scrolls the pane so that the specified co-ordinates within the content are at the top left
1166					// of the viewport. animate is optional and if not passed then the value of animateScroll from
1167					// the settings object this jScrollPane was initialised with is used.
1168					scrollTo: function(destX, destY, animate)
1169					{
1170						scrollToX(destX, animate);
1171						scrollToY(destY, animate);
1172					},
1173					// Scrolls the pane so that the specified co-ordinate within the content is at the left of the
1174					// viewport. animate is optional and if not passed then the value of animateScroll from the settings
1175					// object this jScrollPane was initialised with is used.
1176					scrollToX: function(destX, animate)
1177					{
1178						scrollToX(destX, animate);
1179					},
1180					// Scrolls the pane so that the specified co-ordinate within the content is at the top of the
1181					// viewport. animate is optional and if not passed then the value of animateScroll from the settings
1182					// object this jScrollPane was initialised with is used.
1183					scrollToY: function(destY, animate)
1184					{
1185						scrollToY(destY, animate);
1186					},
1187					// Scrolls the pane to the specified percentage of its maximum horizontal scroll position. animate
1188					// is optional and if not passed then the value of animateScroll from the settings object this
1189					// jScrollPane was initialised with is used.
1190					scrollToPercentX: function(destPercentX, animate)
1191					{
1192						scrollToX(destPercentX * (contentWidth - paneWidth), animate);
1193					},
1194					// Scrolls the pane to the specified percentage of its maximum vertical scroll position. animate
1195					// is optional and if not passed then the value of animateScroll from the settings object this
1196					// jScrollPane was initialised with is used.
1197					scrollToPercentY: function(destPercentY, animate)
1198					{
1199						scrollToY(destPercentY * (contentHeight - paneHeight), animate);
1200					},
1201					// Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
1202					// the value of animateScroll from the settings object this jScrollPane was initialised with is used.
1203					scrollBy: function(deltaX, deltaY, animate)
1204					{
1205						jsp.scrollByX(deltaX, animate);
1206						jsp.scrollByY(deltaY, animate);
1207					},
1208					// Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
1209					// the value of animateScroll from the settings object this jScrollPane was initialised with is used.
1210					scrollByX: function(deltaX, animate)
1211					{
1212						var destX = contentPositionX() + Math[deltaX<0 ? 'floor' : 'ceil'](deltaX),
1213							percentScrolled = destX / (contentWidth - paneWidth);
1214						positionDragX(percentScrolled * dragMaxX, animate);
1215					},
1216					// Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
1217					// the value of animateScroll from the settings object this jScrollPane was initialised with is used.
1218					scrollByY: function(deltaY, animate)
1219					{
1220						var destY = contentPositionY() + Math[deltaY<0 ? 'floor' : 'ceil'](deltaY),
1221							percentScrolled = destY / (contentHeight - paneHeight);
1222						positionDragY(percentScrolled * dragMaxY, animate);
1223					},
1224					// Positions the horizontal drag at the specified x position (and updates the viewport to reflect
1225					// this). animate is optional and if not passed then the value of animateScroll from the settings
1226					// object this jScrollPane was initialised with is used.
1227					positionDragX: function(x, animate)
1228					{
1229						positionDragX(x, animate);
1230					},
1231					// Positions the vertical drag at the specified y position (and updates the viewport to reflect
1232					// this). animate is optional and if not passed then the value of animateScroll from the settings
1233					// object this jScrollPane was initialised with is used.
1234					positionDragY: function(y, animate)
1235					{
1236						positionDragY(y, animate);
1237					},
1238					// This method is called when jScrollPane is trying to animate to a new position. You can override
1239					// it if you want to provide advanced animation functionality. It is passed the following arguments:
1240					//  * ele          - the element whose position is being animated
1241					//  * prop         - the property that is being animated
1242					//  * value        - the value it's being animated to
1243					//  * stepCallback - a function that you must execute each time you update the value of the property
1244					// You can use the default implementation (below) as a starting point for your own implementation.
1245					animate: function(ele, prop, value, stepCallback)
1246					{
1247						var params = {};
1248						params[prop] = value;
1249						ele.animate(
1250							params,
1251							{
1252								'duration'	: settings.animateDuration,
1253								'ease'		: settings.animateEase,
1254								'queue'		: false,
1255								'step'		: stepCallback
1256							}
1257						);
1258					},
1259					// Returns the current x position of the viewport with regards to the content pane.
1260					getContentPositionX: function()
1261					{
1262						return contentPositionX();
1263					},
1264					// Returns the current y position of the viewport with regards to the content pane.
1265					getContentPositionY: function()
1266					{
1267						return contentPositionY();
1268					},
1269					// Returns the width of the content within the scroll pane.
1270					getContentWidth: function()
1271					{
1272						return contentWidth;
1273					},
1274					// Returns the height of the content within the scroll pane.
1275					getContentHeight: function()
1276					{
1277						return contentHeight;
1278					},
1279					// Returns the horizontal position of the viewport within the pane content.
1280					getPercentScrolledX: function()
1281					{
1282						return contentPositionX() / (contentWidth - paneWidth);
1283					},
1284					// Returns the vertical position of the viewport within the pane content.
1285					getPercentScrolledY: function()
1286					{
1287						return contentPositionY() / (contentHeight - paneHeight);
1288					},
1289					// Returns whether or not this scrollpane has a horizontal scrollbar.
1290					getIsScrollableH: function()
1291					{
1292						return isScrollableH;
1293					},
1294					// Returns whether or not this scrollpane has a vertical scrollbar.
1295					getIsScrollableV: function()
1296					{
1297						return isScrollableV;
1298					},
1299					// Gets a reference to the content pane. It is important that you use this method if you want to
1300					// edit the content of your jScrollPane as if you access the element directly then you may have some
1301					// problems (as your original element has had additional elements for the scrollbars etc added into
1302					// it).
1303					getContentPane: function()
1304					{
1305						return pane;
1306					},
1307					// Scrolls this jScrollPane down as far as it can currently scroll. If animate isn't passed then the
1308					// animateScroll value from settings is used instead.
1309					scrollToBottom: function(animate)
1310					{
1311						positionDragY(dragMaxY, animate);
1312					},
1313					// Hijacks the links on the page which link to content inside the scrollpane. If you have changed
1314					// the content of your page (e.g. via AJAX) and want to make sure any new anchor links to the
1315					// contents of your scroll pane will work then call this function.
1316					hijackInternalLinks: function()
1317					{
1318						hijackInternalLinks();
1319					},
1320					// Removes the jScrollPane and returns the page to the state it was in before jScrollPane was
1321					// initialised.
1322					destroy: function()
1323					{
1324							destroy();
1325					}
1326				}
1327			);
1328			
1329			initialise(s);
1330		}
1331
1332		// Pluginifying code...
1333		settings = $.extend({}, $.fn.jScrollPane.defaults, settings);
1334		
1335		// Apply default speed
1336		$.each(['mouseWheelSpeed', 'arrowButtonSpeed', 'trackClickSpeed', 'keyboardSpeed'], function() {
1337			settings[this] = settings[this] || settings.speed;
1338		});
1339
1340		return this.each(
1341			function()
1342			{
1343				var elem = $(this), jspApi = elem.data('jsp');
1344				if (jspApi) {
1345					jspApi.reinitialise(settings);
1346				} else {
1347					jspApi = new JScrollPane(elem, settings);
1348					elem.data('jsp', jspApi);
1349				}
1350			}
1351		);
1352	};
1353
1354	$.fn.jScrollPane.defaults = {
1355		showArrows					: false,
1356		maintainPosition			: true,
1357		stickToBottom				: false,
1358		stickToRight				: false,
1359		clickOnTrack				: true,
1360		autoReinitialise			: false,
1361		autoReinitialiseDelay		: 500,
1362		verticalDragMinHeight		: 0,
1363		verticalDragMaxHeight		: 99999,
1364		horizontalDragMinWidth		: 0,
1365		horizontalDragMaxWidth		: 99999,
1366		contentWidth				: undefined,
1367		animateScroll				: false,
1368		animateDuration				: 300,
1369		animateEase					: 'linear',
1370		hijackInternalLinks			: false,
1371		verticalGutter				: 4,
1372		horizontalGutter			: 4,
1373		mouseWheelSpeed				: 0,
1374		arrowButtonSpeed			: 0,
1375		arrowRepeatFreq				: 50,
1376		arrowScrollOnHover			: false,
1377		trackClickSpeed				: 0,
1378		trackClickRepeatFreq		: 70,
1379		verticalArrowPositions		: 'split',
1380		horizontalArrowPositions	: 'split',
1381		enableKeyboardNavigation	: true,
1382		hideFocus					: false,
1383		keyboardSpeed				: 0,
1384		initialDelay                : 300,        // Delay before starting repeating
1385		speed						: 30,		// Default speed when others falsey
1386		scrollPagePercent			: .8		// Percent of visible area scrolled when pageUp/Down or track area pressed
1387	};
1388
1389})(jQuery,this);
1390

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.