PageSourceSearch

https://voenchast.ru/local/templates/voenchast/js/scrol/jquery.jscrollpane.js

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

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.