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