jscrollpane-rails 2.1.1 → 2.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,28 +1,32 @@
1
1
  /*!
2
- * jScrollPane - v2.1.1 - 2018-01-12
2
+ * jScrollPane - v2.2.3-rc.1 - 2020-06-26
3
3
  * http://jscrollpane.kelvinluck.com/
4
4
  *
5
5
  * Copyright (c) 2014 Kelvin Luck
6
- * Copyright (c) 2017-2018 Tuukka Pasanen
6
+ * Copyright (c) 2017-2020 Tuukka Pasanen
7
7
  * Dual licensed under the MIT or GPL licenses.
8
+ *
9
+ * SPDX-License-Identifier: MIT
10
+ * SPDX-License-Identifier: GPL-2.0-or-later
8
11
  */
9
12
 
10
13
  // Script: jScrollPane - cross browser customisable scrollbars
11
14
  //
12
- // *Version: 2.1.1, Last updated: 2018-01-12*
13
- //
14
15
  // Project Home - http://jscrollpane.kelvinluck.com/
15
16
  // GitHub - http://github.com/vitch/jScrollPane
16
- // Source - http://github.com/vitch/jScrollPane/raw/master/script/jquery.jscrollpane.js
17
- // (Minified) - http://github.com/vitch/jScrollPane/raw/master/script/jquery.jscrollpane.min.js
17
+ // CND - https://cdnjs.com/libraries/jScrollPane
18
+ // Source - https://cdnjs.cloudflare.com/ajax/libs/jScrollPane/2.2.1/script/jquery.jscrollpane.min.js
19
+ // (Minified) - https://cdnjs.cloudflare.com/ajax/libs/jScrollPane/2.2.1/script/jquery.jscrollpane.js
20
+ // CSS - https://cdnjs.cloudflare.com/ajax/libs/jScrollPane/2.2.1/style/jquery.jscrollpane.css
21
+ // (Minified) - https://cdnjs.cloudflare.com/ajax/libs/jScrollPane/2.2.1/style/jquery.jscrollpane.min.css
18
22
  //
19
23
  // About: License
20
24
  //
21
25
  // Copyright (c) 2017 Kelvin Luck
22
- // Copyright (c) 2017-2018 Tuukka Pasanen
26
+ // Copyright (c) 2017-2020 Tuukka Pasanen
23
27
  // Dual licensed under the MIT or GPL Version 2 licenses.
24
- // http://jscrollpane.kelvinluck.com/MIT-LICENSE.txt
25
- // http://jscrollpane.kelvinluck.com/GPL-LICENSE.txt
28
+ // https://github.com/vitch/jScrollPane/blob/master/MIT-LICENSE.txt
29
+ // https://github.com/vitch/jScrollPane/blob/master/GPL-LICENSE.txt
26
30
  //
27
31
  // About: Examples
28
32
  //
@@ -42,1498 +46,1510 @@
42
46
  //
43
47
  // About: Release History
44
48
  //
45
- // 2.1.1 - (2018-01-12) As everyone stays silent then we just release! No changes from RC.1
46
- // 2.1.1-rc.1 - (2017-12-23) Started to slowly merge stuff (HO HO HO Merry Christmas!)
47
- // * Merged
48
- // - #349 - ScrollPane reinitialization should adapt to changed container size
49
- // - #335 Set drag bar width/height with .css instead of .width/.height
50
- // - #297 added two settings: always show HScroll and VScroll
51
- // * Bugs
52
- // - #8 Make it possible to tell a scrollbar to be "always on"
53
- // 2.1.0 - (2017-12-16) Update jQuery to version 3.x
54
- // 2.0.23 - (2016-01-28) Various
55
- // 2.0.22 - (2015-04-25) Resolve a memory leak due to an event handler that isn't cleaned up in destroy (thanks @timjnh)
56
- // 2.0.21 - (2015-02-24) Simplify UMD pattern: fixes browserify when loading jQuery outside of bundle
57
- // 2.0.20 - (2014-10-23) Adds AMD support (thanks @carlosrberto) and support for overflow-x/overflow-y (thanks @darimpulso)
58
- // 2.0.19 - (2013-11-16) Changes for more reliable scroll amount with latest mousewheel plugin (thanks @brandonaaron)
59
- // 2.0.18 - (2013-10-23) Fix for issue with gutters and scrollToElement (thanks @Dubiy)
60
- // 2.0.17 - (2013-08-17) Working correctly when box-sizing is set to border-box (thanks @pieht)
61
- // 2.0.16 - (2013-07-30) Resetting left position when scroll is removed. Fixes #189
62
- // 2.0.15 - (2013-07-29) Fixed issue with scrollToElement where the destX and destY are undefined.
63
- // 2.0.14 - (2013-05-01) Updated to most recent mouse wheel plugin (see #106) and related changes for sensible scroll speed
64
- // 2.0.13 - (2013-05-01) Switched to semver compatible version name
49
+ // 2.2.3-rc.1 - (2020-06-26) Fix Github Issue #376 and #377 with jQuery 3.5 and upcoming jQuery 4.0
50
+ // Small change with setting stickToBottom. Updated other scripts to
51
+ // jQuery 3.x.
52
+ // 2.2.2 - (2020-05-06) Just update NPM dependecies to remove vunerbilities
53
+ // 2.2.1 - (2018-09-27) No changed applied to release so same as RC1/2
54
+ // 2.2.1-rc.2 - (2018-06-14) Sucked NPM release have to make new Release.. this is 2018!
55
+ // 2.2.1-rc.1 - (2018-06-14) Fixed CSSLint warnings which can lead CSS problems in
56
+ // production! Please report a issue if this breaks something!
57
+ // * Merged:
58
+ // - #360 Register to globally available version of jQuery
59
+ // 2.2.0 - (2018-05-16) No changes to RC1
60
+ // 2.2.0-rc.1 - (2018-04-28) Merged resize sensor to find out size changes of screen and
61
+ // again little bit tuned this to support more npm goodies.
62
+ // * Merged:
63
+ // - #361 Event based reinitialising - Resize Sensor
64
+ // - #359 Use npm scripts and local dev dependencies to build the project
65
65
 
66
66
  (function (factory) {
67
- if ( typeof define === 'function' && define.amd ) {
68
- // AMD. Register as an anonymous module.
69
- define(['jquery'], factory);
70
- } else if (typeof exports === 'object') {
71
- // Node/CommonJS style for Browserify
72
- module.exports = factory(require('jquery'));
73
- } else {
74
- // Browser globals
75
- factory(jQuery);
76
- }
77
- }(function($){
78
-
79
- $.fn.jScrollPane = function(settings)
80
- {
81
- // JScrollPane "class" - public methods are available through $('selector').data('jsp')
82
- function JScrollPane(elem, s)
83
- {
84
- var settings, jsp = this, pane, paneWidth, paneHeight, container, contentWidth, contentHeight,
85
- percentInViewH, percentInViewV, isScrollableV, isScrollableH, verticalDrag, dragMaxY,
86
- verticalDragPosition, horizontalDrag, dragMaxX, horizontalDragPosition,
87
- verticalBar, verticalTrack, scrollbarWidth, verticalTrackHeight, verticalDragHeight, arrowUp, arrowDown,
88
- horizontalBar, horizontalTrack, horizontalTrackWidth, horizontalDragWidth, arrowLeft, arrowRight,
89
- reinitialiseInterval, originalPadding, originalPaddingTotalWidth, previousContentWidth,
90
- wasAtTop = true, wasAtLeft = true, wasAtBottom = false, wasAtRight = false,
91
- originalElement = elem.clone(false, false).empty(),
92
- mwEvent = $.fn.mwheelIntent ? 'mwheelIntent.jsp' : 'mousewheel.jsp';
93
-
94
- if (elem.css('box-sizing') === 'border-box') {
95
- originalPadding = 0;
96
- originalPaddingTotalWidth = 0;
97
- } else {
98
- originalPadding = elem.css('paddingTop') + ' ' +
99
- elem.css('paddingRight') + ' ' +
100
- elem.css('paddingBottom') + ' ' +
101
- elem.css('paddingLeft');
102
- originalPaddingTotalWidth = (parseInt(elem.css('paddingLeft'), 10) || 0) +
103
- (parseInt(elem.css('paddingRight'), 10) || 0);
104
- }
105
-
106
- function initialise(s)
107
- {
108
-
109
- var /*firstChild, lastChild, */isMaintainingPositon, lastContentX, lastContentY,
110
- hasContainingSpaceChanged, originalScrollTop, originalScrollLeft,
111
- newPaneWidth, newPaneHeight, maintainAtBottom = false, maintainAtRight = false;
112
-
113
- settings = s;
114
-
115
- if (pane === undefined) {
116
- originalScrollTop = elem.scrollTop();
117
- originalScrollLeft = elem.scrollLeft();
118
-
119
- elem.css(
120
- {
121
- overflow: 'hidden',
122
- padding: 0
123
- }
124
- );
125
- // TODO: Deal with where width/ height is 0 as it probably means the element is hidden and we should
126
- // come back to it later and check once it is unhidden...
127
- paneWidth = elem.innerWidth() + originalPaddingTotalWidth;
128
- paneHeight = elem.innerHeight();
129
-
130
- elem.width(paneWidth);
131
-
132
- pane = $('<div class="jspPane" />').css('padding', originalPadding).append(elem.children());
133
- container = $('<div class="jspContainer" />')
134
- .css({
135
- 'width': paneWidth + 'px',
136
- 'height': paneHeight + 'px'
137
- }
138
- ).append(pane).appendTo(elem);
139
-
140
- /*
141
- // Move any margins from the first and last children up to the container so they can still
142
- // collapse with neighbouring elements as they would before jScrollPane
143
- firstChild = pane.find(':first-child');
144
- lastChild = pane.find(':last-child');
145
- elem.css(
146
- {
147
- 'margin-top': firstChild.css('margin-top'),
148
- 'margin-bottom': lastChild.css('margin-bottom')
67
+ if (typeof define === 'function' && define.amd) {
68
+ // AMD. Register as an anonymous module.
69
+ define(['jquery'], factory);
70
+ } else if (typeof exports === 'object') {
71
+ // Node/CommonJS style for Browserify
72
+ module.exports = factory(jQuery || require('jquery'));
73
+ } else {
74
+ // Browser globals
75
+ factory(jQuery);
76
+ }
77
+ })(function ($) {
78
+ $.fn.jScrollPane = function (settings) {
79
+ // JScrollPane "class" - public methods are available through $('selector').data('jsp')
80
+ function JScrollPane(elem, s) {
81
+ var settings,
82
+ jsp = this,
83
+ pane,
84
+ paneWidth,
85
+ paneHeight,
86
+ container,
87
+ contentWidth,
88
+ contentHeight,
89
+ percentInViewH,
90
+ percentInViewV,
91
+ isScrollableV,
92
+ isScrollableH,
93
+ verticalDrag,
94
+ dragMaxY,
95
+ verticalDragPosition,
96
+ horizontalDrag,
97
+ dragMaxX,
98
+ horizontalDragPosition,
99
+ verticalBar,
100
+ verticalTrack,
101
+ scrollbarWidth,
102
+ verticalTrackHeight,
103
+ verticalDragHeight,
104
+ arrowUp,
105
+ arrowDown,
106
+ horizontalBar,
107
+ horizontalTrack,
108
+ horizontalTrackWidth,
109
+ horizontalDragWidth,
110
+ arrowLeft,
111
+ arrowRight,
112
+ reinitialiseInterval,
113
+ originalPadding,
114
+ originalPaddingTotalWidth,
115
+ previousContentWidth,
116
+ wasAtTop = true,
117
+ wasAtLeft = true,
118
+ wasAtBottom = false,
119
+ wasAtRight = false,
120
+ originalElement = elem.clone(false, false).empty(),
121
+ resizeEventsAdded = false,
122
+ mwEvent = $.fn.mwheelIntent ? 'mwheelIntent.jsp' : 'mousewheel.jsp';
123
+
124
+ var reinitialiseFn = function () {
125
+ // if size has changed then reinitialise
126
+ if (settings.resizeSensorDelay > 0) {
127
+ setTimeout(function () {
128
+ initialise(settings);
129
+ }, settings.resizeSensorDelay);
130
+ } else {
131
+ initialise(settings);
132
+ }
133
+ };
134
+
135
+ if (elem.css('box-sizing') === 'border-box') {
136
+ originalPadding = 0;
137
+ originalPaddingTotalWidth = 0;
138
+ } else {
139
+ originalPadding =
140
+ elem.css('paddingTop') +
141
+ ' ' +
142
+ elem.css('paddingRight') +
143
+ ' ' +
144
+ elem.css('paddingBottom') +
145
+ ' ' +
146
+ elem.css('paddingLeft');
147
+ originalPaddingTotalWidth = (parseInt(elem.css('paddingLeft'), 10) || 0) + (parseInt(elem.css('paddingRight'), 10) || 0);
149
148
  }
150
- );
151
- firstChild.css('margin-top', 0);
152
- lastChild.css('margin-bottom', 0);
153
- */
154
- } else {
155
- elem.css('width', '');
156
-
157
- // To measure the required dimensions accurately, temporarily override the CSS positioning
158
- // of the container and pane.
159
- container.css({width: 'auto', height: 'auto'});
160
- pane.css('position', 'static');
161
-
162
- newPaneWidth = elem.innerWidth() + originalPaddingTotalWidth;
163
- newPaneHeight = elem.innerHeight();
164
- console.log('newPaneHeight = ' + newPaneHeight);
165
- pane.css('position', 'absolute');
166
-
167
- maintainAtBottom = settings.stickToBottom && isCloseToBottom();
168
- maintainAtRight = settings.stickToRight && isCloseToRight();
169
-
170
- hasContainingSpaceChanged = newPaneWidth !== paneWidth || newPaneHeight !== paneHeight;
171
-
172
- paneWidth = newPaneWidth;
173
- paneHeight = newPaneHeight;
174
- container.css({width: paneWidth, height: paneHeight});
175
-
176
- // If nothing changed since last check...
177
- if (!hasContainingSpaceChanged && previousContentWidth == contentWidth && pane.outerHeight() == contentHeight) {
178
- elem.width(paneWidth);
179
- return;
180
- }
181
- previousContentWidth = contentWidth;
182
-
183
- pane.css('width', '');
184
- elem.width(paneWidth);
185
-
186
- container.find('>.jspVerticalBar,>.jspHorizontalBar').remove().end();
187
- }
188
149
 
189
- pane.css('overflow', 'auto');
190
- if (s.contentWidth) {
191
- contentWidth = s.contentWidth;
192
- } else {
193
- contentWidth = pane[0].scrollWidth;
194
- }
195
- contentHeight = pane[0].scrollHeight;
196
- pane.css('overflow', '');
197
-
198
- percentInViewH = contentWidth / paneWidth;
199
- percentInViewV = contentHeight / paneHeight;
200
- isScrollableV = percentInViewV > 1 || settings.alwaysShowVScroll;
201
- isScrollableH = percentInViewH > 1 || settings.alwaysShowHScroll;
202
-
203
- //console.log(paneWidth, paneHeight, contentWidth, contentHeight, percentInViewH, percentInViewV, isScrollableH, isScrollableV);
204
-
205
- if (!(isScrollableH || isScrollableV)) {
206
- elem.removeClass('jspScrollable');
207
- pane.css({
208
- top: 0,
209
- left: 0,
210
- width: container.width() - originalPaddingTotalWidth
211
- });
212
- removeMousewheel();
213
- removeFocusHandler();
214
- removeKeyboardNav();
215
- removeClickOnTrack();
216
- } else {
217
- elem.addClass('jspScrollable');
218
-
219
- isMaintainingPositon = settings.maintainPosition && (verticalDragPosition || horizontalDragPosition);
220
- if (isMaintainingPositon) {
221
- lastContentX = contentPositionX();
222
- lastContentY = contentPositionY();
223
- }
224
-
225
- initialiseVerticalScroll();
226
- initialiseHorizontalScroll();
227
- resizeScrollbars();
228
-
229
- if (isMaintainingPositon) {
230
- scrollToX(maintainAtRight ? (contentWidth - paneWidth ) : lastContentX, false);
231
- scrollToY(maintainAtBottom ? (contentHeight - paneHeight) : lastContentY, false);
232
- }
233
-
234
- initFocusHandler();
235
- initMousewheel();
236
- initTouch();
237
-
238
- if (settings.enableKeyboardNavigation) {
239
- initKeyboardNav();
240
- }
241
- if (settings.clickOnTrack) {
242
- initClickOnTrack();
243
- }
244
-
245
- observeHash();
246
- if (settings.hijackInternalLinks) {
247
- hijackInternalLinks();
248
- }
249
- }
150
+ function initialise(s) {
151
+ var /*firstChild, lastChild, */ isMaintainingPositon,
152
+ lastContentX,
153
+ lastContentY,
154
+ hasContainingSpaceChanged,
155
+ originalScrollTop,
156
+ originalScrollLeft,
157
+ newPaneWidth,
158
+ newPaneHeight,
159
+ maintainAtBottom = false,
160
+ maintainAtRight = false;
161
+
162
+ settings = s;
163
+ lastContentX = 0;
164
+ lastContentY = 0;
165
+
166
+ if (pane === undefined) {
167
+ originalScrollTop = elem.scrollTop();
168
+ originalScrollLeft = elem.scrollLeft();
169
+
170
+ elem.css({
171
+ overflow: 'hidden',
172
+ padding: '0',
173
+ });
174
+ // TODO: Deal with where width/ height is 0 as it probably means the element is hidden and we should
175
+ // come back to it later and check once it is unhidden...
176
+ paneWidth = elem.innerWidth() + originalPaddingTotalWidth;
177
+ paneHeight = elem.innerHeight();
178
+
179
+ elem.width(paneWidth);
180
+
181
+ pane = $('<div class="jspPane"></div>').css('padding', originalPadding).append(elem.children());
182
+ container = $('<div class="jspContainer"></div>')
183
+ .css({
184
+ width: paneWidth + 'px',
185
+ height: paneHeight + 'px',
186
+ })
187
+ .append(pane)
188
+ .appendTo(elem);
189
+
190
+ /*
191
+ // Move any margins from the first and last children up to the container so they can still
192
+ // collapse with neighbouring elements as they would before jScrollPane
193
+ firstChild = pane.find(':first-child');
194
+ lastChild = pane.find(':last-child');
195
+ elem.css(
196
+ {
197
+ 'margin-top': firstChild.css('margin-top'),
198
+ 'margin-bottom': lastChild.css('margin-bottom')
199
+ }
200
+ );
201
+ firstChild.css('margin-top', 0);
202
+ lastChild.css('margin-bottom', 0);
203
+ */
204
+ } else {
205
+ elem.css('width', '');
206
+
207
+ // To measure the required dimensions accurately, temporarily override the CSS positioning
208
+ // of the container and pane.
209
+ container.css({ width: 'auto', height: 'auto' });
210
+ pane.css('position', 'static');
211
+
212
+ newPaneWidth = elem.innerWidth() + originalPaddingTotalWidth;
213
+ newPaneHeight = elem.innerHeight();
214
+ pane.css('position', 'absolute');
215
+
216
+ maintainAtBottom = settings.stickToBottom && isCloseToBottom();
217
+ maintainAtRight = settings.stickToRight && isCloseToRight();
218
+
219
+ hasContainingSpaceChanged = newPaneWidth !== paneWidth || newPaneHeight !== paneHeight;
220
+
221
+ paneWidth = newPaneWidth;
222
+ paneHeight = newPaneHeight;
223
+ container.css({ width: paneWidth + 'px', height: paneHeight + 'px' });
224
+
225
+ // If nothing changed since last check...
226
+ if (!hasContainingSpaceChanged && previousContentWidth == contentWidth && pane.outerHeight() == contentHeight) {
227
+ elem.width(paneWidth);
228
+ return;
229
+ }
230
+ previousContentWidth = contentWidth;
250
231
 
251
- if (settings.autoReinitialise && !reinitialiseInterval) {
252
- reinitialiseInterval = setInterval(
253
- function()
254
- {
255
- initialise(settings);
256
- },
257
- settings.autoReinitialiseDelay
258
- );
259
- } else if (!settings.autoReinitialise && reinitialiseInterval) {
260
- clearInterval(reinitialiseInterval);
261
- }
232
+ pane.css('width', '');
233
+ elem.width(paneWidth);
262
234
 
263
- if(originalScrollTop && elem.scrollTop(0)) {
264
- scrollToY(originalScrollTop, false);
265
- }
235
+ container.find('>.jspVerticalBar,>.jspHorizontalBar').remove().end();
236
+ }
266
237
 
267
- if(originalScrollLeft && elem.scrollLeft(0)) {
268
- scrollToX(originalScrollLeft, false);
269
- }
238
+ pane.css('overflow', 'auto');
239
+ if (s.contentWidth) {
240
+ contentWidth = s.contentWidth;
241
+ } else {
242
+ contentWidth = pane[0].scrollWidth;
243
+ }
244
+ contentHeight = pane[0].scrollHeight;
245
+ pane.css('overflow', '');
246
+
247
+ percentInViewH = contentWidth / paneWidth;
248
+ percentInViewV = contentHeight / paneHeight;
249
+ isScrollableV = percentInViewV > 1 || settings.alwaysShowVScroll;
250
+ isScrollableH = percentInViewH > 1 || settings.alwaysShowHScroll;
251
+
252
+ if (!(isScrollableH || isScrollableV)) {
253
+ elem.removeClass('jspScrollable');
254
+ pane.css({
255
+ top: '0',
256
+ left: '0',
257
+ width: container.width() - originalPaddingTotalWidth,
258
+ });
259
+ removeMousewheel();
260
+ removeFocusHandler();
261
+ removeKeyboardNav();
262
+ removeClickOnTrack();
263
+ } else {
264
+ elem.addClass('jspScrollable');
265
+
266
+ isMaintainingPositon = settings.maintainPosition && (verticalDragPosition || horizontalDragPosition);
267
+
268
+ if (isMaintainingPositon) {
269
+ lastContentX = contentPositionX();
270
+ lastContentY = contentPositionY();
271
+ }
272
+
273
+ initialiseVerticalScroll();
274
+ initialiseHorizontalScroll();
275
+ resizeScrollbars();
276
+
277
+ if (settings.stickToBottom || settings.stickToRight) {
278
+ scrollToX(maintainAtRight ? contentWidth - paneWidth : lastContentX, false);
279
+ scrollToY(maintainAtBottom ? contentHeight - paneHeight : lastContentY, false);
280
+ }
281
+
282
+ initFocusHandler();
283
+ initMousewheel();
284
+ initTouch();
285
+
286
+ if (settings.enableKeyboardNavigation) {
287
+ initKeyboardNav();
288
+ }
289
+ if (settings.clickOnTrack) {
290
+ initClickOnTrack();
291
+ }
292
+
293
+ observeHash();
294
+ if (settings.hijackInternalLinks) {
295
+ hijackInternalLinks();
296
+ }
297
+ }
298
+
299
+ if (!settings.resizeSensor && settings.autoReinitialise && !reinitialiseInterval) {
300
+ reinitialiseInterval = setInterval(function () {
301
+ initialise(settings);
302
+ }, settings.autoReinitialiseDelay);
303
+ } else if (!settings.resizeSensor && !settings.autoReinitialise && reinitialiseInterval) {
304
+ clearInterval(reinitialiseInterval);
305
+ }
306
+
307
+ if (settings.resizeSensor && !resizeEventsAdded) {
308
+ // detect size change in content
309
+ detectSizeChanges(pane, reinitialiseFn);
310
+
311
+ // detect size changes of scroll element
312
+ detectSizeChanges(elem, reinitialiseFn);
313
+
314
+ // detect size changes of container
315
+ detectSizeChanges(elem.parent(), reinitialiseFn);
270
316
 
271
- elem.trigger('jsp-initialised', [isScrollableH || isScrollableV]);
272
- }
273
-
274
- function initialiseVerticalScroll()
275
- {
276
- if (isScrollableV) {
277
-
278
- container.append(
279
- $('<div class="jspVerticalBar" />').append(
280
- $('<div class="jspCap jspCapTop" />'),
281
- $('<div class="jspTrack" />').append(
282
- $('<div class="jspDrag" />').append(
283
- $('<div class="jspDragTop" />'),
284
- $('<div class="jspDragBottom" />')
285
- )
286
- ),
287
- $('<div class="jspCap jspCapBottom" />')
288
- )
289
- );
290
-
291
- verticalBar = container.find('>.jspVerticalBar');
292
- verticalTrack = verticalBar.find('>.jspTrack');
293
- verticalDrag = verticalTrack.find('>.jspDrag');
294
-
295
- if (settings.showArrows) {
296
- arrowUp = $('<a class="jspArrow jspArrowUp" />').on(
297
- 'mousedown.jsp', getArrowScroll(0, -1)
298
- ).on('click.jsp', nil);
299
- arrowDown = $('<a class="jspArrow jspArrowDown" />').on(
300
- 'mousedown.jsp', getArrowScroll(0, 1)
301
- ).on('click.jsp', nil);
302
- if (settings.arrowScrollOnHover) {
303
- arrowUp.on('mouseover.jsp', getArrowScroll(0, -1, arrowUp));
304
- arrowDown.on('mouseover.jsp', getArrowScroll(0, 1, arrowDown));
317
+ // add a reinit on window resize also for safety
318
+ window.addEventListener('resize', reinitialiseFn);
319
+
320
+ resizeEventsAdded = true;
321
+ }
322
+
323
+ if (originalScrollTop && elem.scrollTop(0)) {
324
+ scrollToY(originalScrollTop, false);
325
+ }
326
+
327
+ if (originalScrollLeft && elem.scrollLeft(0)) {
328
+ scrollToX(originalScrollLeft, false);
329
+ }
330
+
331
+ elem.trigger('jsp-initialised', [isScrollableH || isScrollableV]);
305
332
  }
306
333
 
307
- appendArrows(verticalTrack, settings.verticalArrowPositions, arrowUp, arrowDown);
308
- }
309
-
310
- verticalTrackHeight = paneHeight;
311
- container.find('>.jspVerticalBar>.jspCap:visible,>.jspVerticalBar>.jspArrow').each(
312
- function()
313
- {
314
- verticalTrackHeight -= $(this).outerHeight();
315
- }
316
- );
317
-
318
-
319
- verticalDrag.on(
320
- "mouseenter",
321
- function()
322
- {
323
- verticalDrag.addClass('jspHover');
324
- }
325
- ).on(
326
- "mouseleave",
327
- function()
328
- {
329
- verticalDrag.removeClass('jspHover');
330
- }
331
- ).on(
332
- 'mousedown.jsp',
333
- function(e)
334
- {
335
- // Stop IE from allowing text selection
336
- $('html').on('dragstart.jsp selectstart.jsp', nil);
337
-
338
- verticalDrag.addClass('jspActive');
339
-
340
- var startY = e.pageY - verticalDrag.position().top;
341
-
342
- $('html').on(
343
- 'mousemove.jsp',
344
- function(e)
345
- {
346
- positionDragY(e.pageY - startY, false);
334
+ function detectSizeChanges(element, callback) {
335
+ // create resize event elements - based on resize sensor: https://github.com/flowkey/resize-sensor/
336
+ var resizeWidth, resizeHeight;
337
+ var resizeElement = document.createElement('div');
338
+ var resizeGrowElement = document.createElement('div');
339
+ var resizeGrowChildElement = document.createElement('div');
340
+ var resizeShrinkElement = document.createElement('div');
341
+ var resizeShrinkChildElement = document.createElement('div');
342
+
343
+ // add necessary styling
344
+ resizeElement.style.cssText =
345
+ 'position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;';
346
+ resizeGrowElement.style.cssText =
347
+ 'position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;';
348
+ resizeShrinkElement.style.cssText =
349
+ 'position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;';
350
+
351
+ resizeGrowChildElement.style.cssText = 'position: absolute; left: 0; top: 0;';
352
+ resizeShrinkChildElement.style.cssText = 'position: absolute; left: 0; top: 0; width: 200%; height: 200%;';
353
+
354
+ // Create a function to programmatically update sizes
355
+ var updateSizes = function () {
356
+ resizeGrowChildElement.style.width = resizeGrowElement.offsetWidth + 10 + 'px';
357
+ resizeGrowChildElement.style.height = resizeGrowElement.offsetHeight + 10 + 'px';
358
+
359
+ resizeGrowElement.scrollLeft = resizeGrowElement.scrollWidth;
360
+ resizeGrowElement.scrollTop = resizeGrowElement.scrollHeight;
361
+
362
+ resizeShrinkElement.scrollLeft = resizeShrinkElement.scrollWidth;
363
+ resizeShrinkElement.scrollTop = resizeShrinkElement.scrollHeight;
364
+
365
+ resizeWidth = element.width();
366
+ resizeHeight = element.height();
367
+ };
368
+
369
+ // create functions to call when content grows
370
+ var onGrow = function () {
371
+ // check to see if the content has change size
372
+ if (element.width() > resizeWidth || element.height() > resizeHeight) {
373
+ // if size has changed then reinitialise
374
+ callback.apply(this, []);
347
375
  }
348
- ).on('mouseup.jsp mouseleave.jsp', cancelDrag);
349
- return false;
350
- }
351
- );
352
- sizeVerticalScrollbar();
353
- }
354
- }
355
-
356
- function sizeVerticalScrollbar()
357
- {
358
- verticalTrack.height(verticalTrackHeight + 'px');
359
- verticalDragPosition = 0;
360
- scrollbarWidth = settings.verticalGutter + verticalTrack.outerWidth();
361
-
362
- // Make the pane thinner to allow for the vertical scrollbar
363
- pane.width(paneWidth - scrollbarWidth - originalPaddingTotalWidth);
364
-
365
- // Add margin to the left of the pane if scrollbars are on that side (to position
366
- // the scrollbar on the left or right set it's left or right property in CSS)
367
- try {
368
- if (verticalBar.position().left === 0) {
369
- pane.css('margin-left', scrollbarWidth + 'px');
370
- }
371
- } catch (err) {
372
- }
373
- }
374
-
375
- function initialiseHorizontalScroll()
376
- {
377
- if (isScrollableH) {
378
-
379
- container.append(
380
- $('<div class="jspHorizontalBar" />').append(
381
- $('<div class="jspCap jspCapLeft" />'),
382
- $('<div class="jspTrack" />').append(
383
- $('<div class="jspDrag" />').append(
384
- $('<div class="jspDragLeft" />'),
385
- $('<div class="jspDragRight" />')
386
- )
387
- ),
388
- $('<div class="jspCap jspCapRight" />')
389
- )
390
- );
391
-
392
- horizontalBar = container.find('>.jspHorizontalBar');
393
- horizontalTrack = horizontalBar.find('>.jspTrack');
394
- horizontalDrag = horizontalTrack.find('>.jspDrag');
395
-
396
- if (settings.showArrows) {
397
- arrowLeft = $('<a class="jspArrow jspArrowLeft" />').on(
398
- 'mousedown.jsp', getArrowScroll(-1, 0)
399
- ).on('click.jsp', nil);
400
- arrowRight = $('<a class="jspArrow jspArrowRight" />').on(
401
- 'mousedown.jsp', getArrowScroll(1, 0)
402
- ).on('click.jsp', nil);
403
- if (settings.arrowScrollOnHover) {
404
- arrowLeft.on('mouseover.jsp', getArrowScroll(-1, 0, arrowLeft));
405
- arrowRight.on('mouseover.jsp', getArrowScroll(1, 0, arrowRight));
376
+ // after reinitialising update sizes
377
+ updateSizes();
378
+ };
379
+
380
+ // create functions to call when content shrinks
381
+ var onShrink = function () {
382
+ // check to see if the content has change size
383
+ if (element.width() < resizeWidth || element.height() < resizeHeight) {
384
+ // if size has changed then reinitialise
385
+ callback.apply(this, []);
386
+ }
387
+ // after reinitialising update sizes
388
+ updateSizes();
389
+ };
390
+
391
+ // bind to scroll events
392
+ resizeGrowElement.addEventListener('scroll', onGrow.bind(this));
393
+ resizeShrinkElement.addEventListener('scroll', onShrink.bind(this));
394
+
395
+ // nest elements before adding to pane
396
+ resizeGrowElement.appendChild(resizeGrowChildElement);
397
+ resizeShrinkElement.appendChild(resizeShrinkChildElement);
398
+
399
+ resizeElement.appendChild(resizeGrowElement);
400
+ resizeElement.appendChild(resizeShrinkElement);
401
+
402
+ element.append(resizeElement);
403
+
404
+ // ensure parent element is not statically positioned
405
+ if (window.getComputedStyle(element[0], null).getPropertyValue('position') === 'static') {
406
+ element[0].style.position = 'relative';
407
+ }
408
+
409
+ // update sizes initially
410
+ updateSizes();
406
411
  }
407
- appendArrows(horizontalTrack, settings.horizontalArrowPositions, arrowLeft, arrowRight);
408
- }
409
-
410
- horizontalDrag.on(
411
- "mouseenter",
412
- function()
413
- {
414
- horizontalDrag.addClass('jspHover');
415
- }
416
- ).on(
417
- "mouseleave",
418
- function()
419
- {
420
- horizontalDrag.removeClass('jspHover');
421
- }
422
- ).on(
423
- 'mousedown.jsp',
424
- function(e)
425
- {
426
- // Stop IE from allowing text selection
427
- $('html').on('dragstart.jsp selectstart.jsp', nil);
428
-
429
- horizontalDrag.addClass('jspActive');
430
-
431
- var startX = e.pageX - horizontalDrag.position().left;
432
-
433
- $('html').on(
434
- 'mousemove.jsp',
435
- function(e)
436
- {
437
- positionDragX(e.pageX - startX, false);
412
+
413
+ function initialiseVerticalScroll() {
414
+ if (isScrollableV) {
415
+ container.append(
416
+ $('<div class="jspVerticalBar"></div>').append(
417
+ $('<div class="jspCap jspCapTop"></div>'),
418
+ $('<div class="jspTrack"></div>').append(
419
+ $('<div class="jspDrag"></div>').append(
420
+ $('<div class="jspDragTop"></div>'),
421
+ $('<div class="jspDragBottom"></div>'),
422
+ ),
423
+ ),
424
+ $('<div class="jspCap jspCapBottom"></div>'),
425
+ ),
426
+ );
427
+
428
+ verticalBar = container.find('>.jspVerticalBar');
429
+ verticalTrack = verticalBar.find('>.jspTrack');
430
+ verticalDrag = verticalTrack.find('>.jspDrag');
431
+
432
+ if (settings.showArrows) {
433
+ arrowUp = $('<a class="jspArrow jspArrowUp"></a>').on('mousedown.jsp', getArrowScroll(0, -1)).on('click.jsp', nil);
434
+ arrowDown = $('<a class="jspArrow jspArrowDown"></a>')
435
+ .on('mousedown.jsp', getArrowScroll(0, 1))
436
+ .on('click.jsp', nil);
437
+ if (settings.arrowScrollOnHover) {
438
+ arrowUp.on('mouseover.jsp', getArrowScroll(0, -1, arrowUp));
439
+ arrowDown.on('mouseover.jsp', getArrowScroll(0, 1, arrowDown));
440
+ }
441
+
442
+ appendArrows(verticalTrack, settings.verticalArrowPositions, arrowUp, arrowDown);
438
443
  }
439
- ).on('mouseup.jsp mouseleave.jsp', cancelDrag);
440
- return false;
441
- }
442
- );
443
- horizontalTrackWidth = container.innerWidth();
444
- sizeHorizontalScrollbar();
445
- }
446
- }
447
-
448
- function sizeHorizontalScrollbar()
449
- {
450
- container.find('>.jspHorizontalBar>.jspCap:visible,>.jspHorizontalBar>.jspArrow').each(
451
- function()
452
- {
453
- horizontalTrackWidth -= $(this).outerWidth();
444
+
445
+ verticalTrackHeight = paneHeight;
446
+ container.find('>.jspVerticalBar>.jspCap:visible,>.jspVerticalBar>.jspArrow').each(function () {
447
+ verticalTrackHeight -= $(this).outerHeight();
448
+ });
449
+
450
+ verticalDrag
451
+ .on('mouseenter', function () {
452
+ verticalDrag.addClass('jspHover');
453
+ })
454
+ .on('mouseleave', function () {
455
+ verticalDrag.removeClass('jspHover');
456
+ })
457
+ .on('mousedown.jsp', function (e) {
458
+ // Stop IE from allowing text selection
459
+ $('html').on('dragstart.jsp selectstart.jsp', nil);
460
+
461
+ verticalDrag.addClass('jspActive');
462
+
463
+ var startY = e.pageY - verticalDrag.position().top;
464
+
465
+ $('html')
466
+ .on('mousemove.jsp', function (e) {
467
+ positionDragY(e.pageY - startY, false);
468
+ })
469
+ .on('mouseup.jsp mouseleave.jsp', cancelDrag);
470
+ return false;
471
+ });
472
+ sizeVerticalScrollbar();
473
+ }
454
474
  }
455
- );
456
-
457
- horizontalTrack.width(horizontalTrackWidth + 'px');
458
- horizontalDragPosition = 0;
459
- }
460
-
461
- function resizeScrollbars()
462
- {
463
- if (isScrollableH && isScrollableV) {
464
- var horizontalTrackHeight = horizontalTrack.outerHeight(),
465
- verticalTrackWidth = verticalTrack.outerWidth();
466
- verticalTrackHeight -= horizontalTrackHeight;
467
- $(horizontalBar).find('>.jspCap:visible,>.jspArrow').each(
468
- function()
469
- {
470
- horizontalTrackWidth += $(this).outerWidth();
471
- }
472
- );
473
- horizontalTrackWidth -= verticalTrackWidth;
474
- paneHeight -= verticalTrackWidth;
475
- paneWidth -= horizontalTrackHeight;
476
- horizontalTrack.parent().append(
477
- $('<div class="jspCorner" />').css('width', horizontalTrackHeight + 'px')
478
- );
479
- sizeVerticalScrollbar();
480
- sizeHorizontalScrollbar();
481
- }
482
- // reflow content
483
- if (isScrollableH) {
484
- pane.width((container.outerWidth() - originalPaddingTotalWidth) + 'px');
485
- }
486
- contentHeight = pane.outerHeight();
487
- percentInViewV = contentHeight / paneHeight;
488
-
489
- if (isScrollableH) {
490
- horizontalDragWidth = Math.ceil(1 / percentInViewH * horizontalTrackWidth);
491
- if (horizontalDragWidth > settings.horizontalDragMaxWidth) {
492
- horizontalDragWidth = settings.horizontalDragMaxWidth;
493
- } else if (horizontalDragWidth < settings.horizontalDragMinWidth) {
494
- horizontalDragWidth = settings.horizontalDragMinWidth;
495
- }
496
- horizontalDrag.css('width', horizontalDragWidth + 'px');
497
- dragMaxX = horizontalTrackWidth - horizontalDragWidth;
498
- _positionDragX(horizontalDragPosition); // To update the state for the arrow buttons
499
- }
500
- if (isScrollableV) {
501
- verticalDragHeight = Math.ceil(1 / percentInViewV * verticalTrackHeight);
502
- if (verticalDragHeight > settings.verticalDragMaxHeight) {
503
- verticalDragHeight = settings.verticalDragMaxHeight;
504
- } else if (verticalDragHeight < settings.verticalDragMinHeight) {
505
- verticalDragHeight = settings.verticalDragMinHeight;
506
- }
507
- verticalDrag.css('height', verticalDragHeight + 'px');
508
- dragMaxY = verticalTrackHeight - verticalDragHeight;
509
- _positionDragY(verticalDragPosition); // To update the state for the arrow buttons
510
- }
511
- }
512
475
 
513
- function appendArrows(ele, p, a1, a2)
514
- {
515
- var p1 = "before", p2 = "after", aTemp;
476
+ function sizeVerticalScrollbar() {
477
+ verticalTrack.height(verticalTrackHeight + 'px');
478
+ verticalDragPosition = 0;
479
+ scrollbarWidth = settings.verticalGutter + verticalTrack.outerWidth();
516
480
 
517
- // Sniff for mac... Is there a better way to determine whether the arrows would naturally appear
518
- // at the top or the bottom of the bar?
519
- if (p == "os") {
520
- p = /Mac/.test(navigator.platform) ? "after" : "split";
521
- }
522
- if (p == p1) {
523
- p2 = p;
524
- } else if (p == p2) {
525
- p1 = p;
526
- aTemp = a1;
527
- a1 = a2;
528
- a2 = aTemp;
529
- }
481
+ // Make the pane thinner to allow for the vertical scrollbar
482
+ pane.width(paneWidth - scrollbarWidth - originalPaddingTotalWidth);
530
483
 
531
- ele[p1](a1)[p2](a2);
532
- }
533
-
534
- function getArrowScroll(dirX, dirY, ele)
535
- {
536
- return function()
537
- {
538
- arrowScroll(dirX, dirY, this, ele);
539
- this.blur();
540
- return false;
541
- };
542
- }
543
-
544
- function arrowScroll(dirX, dirY, arrow, ele)
545
- {
546
- arrow = $(arrow).addClass('jspActive');
547
-
548
- var eve,
549
- scrollTimeout,
550
- isFirst = true,
551
- doScroll = function()
552
- {
553
- if (dirX !== 0) {
554
- jsp.scrollByX(dirX * settings.arrowButtonSpeed);
555
- }
556
- if (dirY !== 0) {
557
- jsp.scrollByY(dirY * settings.arrowButtonSpeed);
558
- }
559
- scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.arrowRepeatFreq);
560
- isFirst = false;
561
- };
484
+ // Add margin to the left of the pane if scrollbars are on that side (to position
485
+ // the scrollbar on the left or right set it's left or right property in CSS)
486
+ try {
487
+ if (verticalBar.position().left === 0) {
488
+ pane.css('margin-left', scrollbarWidth + 'px');
489
+ }
490
+ } catch (err) {}
491
+ }
492
+
493
+ function initialiseHorizontalScroll() {
494
+ if (isScrollableH) {
495
+ container.append(
496
+ $('<div class="jspHorizontalBar"></div>').append(
497
+ $('<div class="jspCap jspCapLeft"></div>'),
498
+ $('<div class="jspTrack"></div>').append(
499
+ $('<div class="jspDrag"></div>').append(
500
+ $('<div class="jspDragLeft"></div>'),
501
+ $('<div class="jspDragRight"></div>'),
502
+ ),
503
+ ),
504
+ $('<div class="jspCap jspCapRight"></div>'),
505
+ ),
506
+ );
507
+
508
+ horizontalBar = container.find('>.jspHorizontalBar');
509
+ horizontalTrack = horizontalBar.find('>.jspTrack');
510
+ horizontalDrag = horizontalTrack.find('>.jspDrag');
511
+
512
+ if (settings.showArrows) {
513
+ arrowLeft = $('<a class="jspArrow jspArrowLeft"></a>')
514
+ .on('mousedown.jsp', getArrowScroll(-1, 0))
515
+ .on('click.jsp', nil);
516
+ arrowRight = $('<a class="jspArrow jspArrowRight"></a>')
517
+ .on('mousedown.jsp', getArrowScroll(1, 0))
518
+ .on('click.jsp', nil);
519
+ if (settings.arrowScrollOnHover) {
520
+ arrowLeft.on('mouseover.jsp', getArrowScroll(-1, 0, arrowLeft));
521
+ arrowRight.on('mouseover.jsp', getArrowScroll(1, 0, arrowRight));
522
+ }
523
+ appendArrows(horizontalTrack, settings.horizontalArrowPositions, arrowLeft, arrowRight);
524
+ }
562
525
 
563
- doScroll();
564
-
565
- eve = ele ? 'mouseout.jsp' : 'mouseup.jsp';
566
- ele = ele || $('html');
567
- ele.on(
568
- eve,
569
- function()
570
- {
571
- arrow.removeClass('jspActive');
572
- if(scrollTimeout) {
573
- clearTimeout(scrollTimeout);
574
- }
575
- scrollTimeout = null;
576
- ele.off(eve);
526
+ horizontalDrag
527
+ .on('mouseenter', function () {
528
+ horizontalDrag.addClass('jspHover');
529
+ })
530
+ .on('mouseleave', function () {
531
+ horizontalDrag.removeClass('jspHover');
532
+ })
533
+ .on('mousedown.jsp', function (e) {
534
+ // Stop IE from allowing text selection
535
+ $('html').on('dragstart.jsp selectstart.jsp', nil);
536
+
537
+ horizontalDrag.addClass('jspActive');
538
+
539
+ var startX = e.pageX - horizontalDrag.position().left;
540
+
541
+ $('html')
542
+ .on('mousemove.jsp', function (e) {
543
+ positionDragX(e.pageX - startX, false);
544
+ })
545
+ .on('mouseup.jsp mouseleave.jsp', cancelDrag);
546
+ return false;
547
+ });
548
+ horizontalTrackWidth = container.innerWidth();
549
+ sizeHorizontalScrollbar();
550
+ }
577
551
  }
578
- );
579
- }
580
-
581
- function initClickOnTrack()
582
- {
583
- removeClickOnTrack();
584
- if (isScrollableV) {
585
- verticalTrack.on(
586
- 'mousedown.jsp',
587
- function(e)
588
- {
589
- if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) {
590
- var clickedTrack = $(this),
591
- offset = clickedTrack.offset(),
592
- direction = e.pageY - offset.top - verticalDragPosition,
593
- scrollTimeout,
594
- isFirst = true,
595
- doScroll = function()
596
- {
597
- var offset = clickedTrack.offset(),
598
- pos = e.pageY - offset.top - verticalDragHeight / 2,
599
- contentDragY = paneHeight * settings.scrollPagePercent,
600
- dragY = dragMaxY * contentDragY / (contentHeight - paneHeight);
601
- if (direction < 0) {
602
- if (verticalDragPosition - dragY > pos) {
603
- jsp.scrollByY(-contentDragY);
604
- } else {
605
- positionDragY(pos);
606
- }
607
- } else if (direction > 0) {
608
- if (verticalDragPosition + dragY < pos) {
609
- jsp.scrollByY(contentDragY);
610
- } else {
611
- positionDragY(pos);
612
- }
613
- } else {
614
- cancelClick();
615
- return;
552
+
553
+ function sizeHorizontalScrollbar() {
554
+ container.find('>.jspHorizontalBar>.jspCap:visible,>.jspHorizontalBar>.jspArrow').each(function () {
555
+ horizontalTrackWidth -= $(this).outerWidth();
556
+ });
557
+
558
+ horizontalTrack.width(horizontalTrackWidth + 'px');
559
+ horizontalDragPosition = 0;
560
+ }
561
+
562
+ function resizeScrollbars() {
563
+ if (isScrollableH && isScrollableV) {
564
+ var horizontalTrackHeight = horizontalTrack.outerHeight(),
565
+ verticalTrackWidth = verticalTrack.outerWidth();
566
+ verticalTrackHeight -= horizontalTrackHeight;
567
+ $(horizontalBar)
568
+ .find('>.jspCap:visible,>.jspArrow')
569
+ .each(function () {
570
+ horizontalTrackWidth += $(this).outerWidth();
571
+ });
572
+ horizontalTrackWidth -= verticalTrackWidth;
573
+ paneHeight -= verticalTrackWidth;
574
+ paneWidth -= horizontalTrackHeight;
575
+ horizontalTrack.parent().append($('<div class="jspCorner"></div>').css('width', horizontalTrackHeight + 'px'));
576
+ sizeVerticalScrollbar();
577
+ sizeHorizontalScrollbar();
578
+ }
579
+ // reflow content
580
+ if (isScrollableH) {
581
+ pane.width(container.outerWidth() - originalPaddingTotalWidth + 'px');
582
+ }
583
+ contentHeight = pane.outerHeight();
584
+ percentInViewV = contentHeight / paneHeight;
585
+
586
+ if (isScrollableH) {
587
+ horizontalDragWidth = Math.ceil((1 / percentInViewH) * horizontalTrackWidth);
588
+ if (horizontalDragWidth > settings.horizontalDragMaxWidth) {
589
+ horizontalDragWidth = settings.horizontalDragMaxWidth;
590
+ } else if (horizontalDragWidth < settings.horizontalDragMinWidth) {
591
+ horizontalDragWidth = settings.horizontalDragMinWidth;
592
+ }
593
+ horizontalDrag.css('width', horizontalDragWidth + 'px');
594
+ dragMaxX = horizontalTrackWidth - horizontalDragWidth;
595
+ _positionDragX(horizontalDragPosition); // To update the state for the arrow buttons
596
+ }
597
+ if (isScrollableV) {
598
+ verticalDragHeight = Math.ceil((1 / percentInViewV) * verticalTrackHeight);
599
+ if (verticalDragHeight > settings.verticalDragMaxHeight) {
600
+ verticalDragHeight = settings.verticalDragMaxHeight;
601
+ } else if (verticalDragHeight < settings.verticalDragMinHeight) {
602
+ verticalDragHeight = settings.verticalDragMinHeight;
603
+ }
604
+ verticalDrag.css('height', verticalDragHeight + 'px');
605
+ dragMaxY = verticalTrackHeight - verticalDragHeight;
606
+ _positionDragY(verticalDragPosition); // To update the state for the arrow buttons
607
+ }
608
+ }
609
+
610
+ function appendArrows(ele, p, a1, a2) {
611
+ var p1 = 'before',
612
+ p2 = 'after',
613
+ aTemp;
614
+
615
+ // Sniff for mac... Is there a better way to determine whether the arrows would naturally appear
616
+ // at the top or the bottom of the bar?
617
+ if (p == 'os') {
618
+ p = /Mac/.test(navigator.platform) ? 'after' : 'split';
619
+ }
620
+ if (p == p1) {
621
+ p2 = p;
622
+ } else if (p == p2) {
623
+ p1 = p;
624
+ aTemp = a1;
625
+ a1 = a2;
626
+ a2 = aTemp;
627
+ }
628
+
629
+ ele[p1](a1)[p2](a2);
630
+ }
631
+
632
+ function getArrowScroll(dirX, dirY, ele) {
633
+ return function () {
634
+ arrowScroll(dirX, dirY, this, ele);
635
+ this.blur();
636
+ return false;
637
+ };
638
+ }
639
+
640
+ function arrowScroll(dirX, dirY, arrow, ele) {
641
+ arrow = $(arrow).addClass('jspActive');
642
+
643
+ var eve,
644
+ scrollTimeout,
645
+ isFirst = true,
646
+ doScroll = function () {
647
+ if (dirX !== 0) {
648
+ jsp.scrollByX(dirX * settings.arrowButtonSpeed);
649
+ }
650
+ if (dirY !== 0) {
651
+ jsp.scrollByY(dirY * settings.arrowButtonSpeed);
616
652
  }
617
- scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq);
653
+ scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.arrowRepeatFreq);
618
654
  isFirst = false;
619
- },
620
- cancelClick = function()
621
- {
622
- if(scrollTimeout) {
623
- clearTimeout(scrollTimeout);
655
+ };
656
+
657
+ doScroll();
658
+
659
+ eve = ele ? 'mouseout.jsp' : 'mouseup.jsp';
660
+ ele = ele || $('html');
661
+ ele.on(eve, function () {
662
+ arrow.removeClass('jspActive');
663
+ if (scrollTimeout) {
664
+ clearTimeout(scrollTimeout);
665
+ }
666
+ scrollTimeout = null;
667
+ ele.off(eve);
668
+ });
669
+ }
670
+
671
+ function initClickOnTrack() {
672
+ removeClickOnTrack();
673
+ if (isScrollableV) {
674
+ verticalTrack.on('mousedown.jsp', function (e) {
675
+ if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) {
676
+ var clickedTrack = $(this),
677
+ offset = clickedTrack.offset(),
678
+ direction = e.pageY - offset.top - verticalDragPosition,
679
+ scrollTimeout,
680
+ isFirst = true,
681
+ doScroll = function () {
682
+ var offset = clickedTrack.offset(),
683
+ pos = e.pageY - offset.top - verticalDragHeight / 2,
684
+ contentDragY = paneHeight * settings.scrollPagePercent,
685
+ dragY = (dragMaxY * contentDragY) / (contentHeight - paneHeight);
686
+ if (direction < 0) {
687
+ if (verticalDragPosition - dragY > pos) {
688
+ jsp.scrollByY(-contentDragY);
689
+ } else {
690
+ positionDragY(pos);
691
+ }
692
+ } else if (direction > 0) {
693
+ if (verticalDragPosition + dragY < pos) {
694
+ jsp.scrollByY(contentDragY);
695
+ } else {
696
+ positionDragY(pos);
697
+ }
698
+ } else {
699
+ cancelClick();
700
+ return;
701
+ }
702
+ scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq);
703
+ isFirst = false;
704
+ },
705
+ cancelClick = function () {
706
+ if (scrollTimeout) {
707
+ clearTimeout(scrollTimeout);
708
+ }
709
+ scrollTimeout = null;
710
+ $(document).off('mouseup.jsp', cancelClick);
711
+ };
712
+ doScroll();
713
+ $(document).on('mouseup.jsp', cancelClick);
714
+ return false;
624
715
  }
625
- scrollTimeout = null;
626
- $(document).off('mouseup.jsp', cancelClick);
627
- };
628
- doScroll();
629
- $(document).on('mouseup.jsp', cancelClick);
630
- return false;
716
+ });
631
717
  }
632
- }
633
- );
634
- }
635
718
 
636
- if (isScrollableH) {
637
- horizontalTrack.on(
638
- 'mousedown.jsp',
639
- function(e)
640
- {
641
- if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) {
642
- var clickedTrack = $(this),
643
- offset = clickedTrack.offset(),
644
- direction = e.pageX - offset.left - horizontalDragPosition,
645
- scrollTimeout,
646
- isFirst = true,
647
- doScroll = function()
648
- {
649
- var offset = clickedTrack.offset(),
650
- pos = e.pageX - offset.left - horizontalDragWidth / 2,
651
- contentDragX = paneWidth * settings.scrollPagePercent,
652
- dragX = dragMaxX * contentDragX / (contentWidth - paneWidth);
653
- if (direction < 0) {
654
- if (horizontalDragPosition - dragX > pos) {
655
- jsp.scrollByX(-contentDragX);
656
- } else {
657
- positionDragX(pos);
658
- }
659
- } else if (direction > 0) {
660
- if (horizontalDragPosition + dragX < pos) {
661
- jsp.scrollByX(contentDragX);
662
- } else {
663
- positionDragX(pos);
664
- }
665
- } else {
666
- cancelClick();
667
- return;
668
- }
669
- scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq);
670
- isFirst = false;
671
- },
672
- cancelClick = function()
673
- {
674
- if(scrollTimeout) {
675
- clearTimeout(scrollTimeout);
719
+ if (isScrollableH) {
720
+ horizontalTrack.on('mousedown.jsp', function (e) {
721
+ if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) {
722
+ var clickedTrack = $(this),
723
+ offset = clickedTrack.offset(),
724
+ direction = e.pageX - offset.left - horizontalDragPosition,
725
+ scrollTimeout,
726
+ isFirst = true,
727
+ doScroll = function () {
728
+ var offset = clickedTrack.offset(),
729
+ pos = e.pageX - offset.left - horizontalDragWidth / 2,
730
+ contentDragX = paneWidth * settings.scrollPagePercent,
731
+ dragX = (dragMaxX * contentDragX) / (contentWidth - paneWidth);
732
+ if (direction < 0) {
733
+ if (horizontalDragPosition - dragX > pos) {
734
+ jsp.scrollByX(-contentDragX);
735
+ } else {
736
+ positionDragX(pos);
737
+ }
738
+ } else if (direction > 0) {
739
+ if (horizontalDragPosition + dragX < pos) {
740
+ jsp.scrollByX(contentDragX);
741
+ } else {
742
+ positionDragX(pos);
743
+ }
744
+ } else {
745
+ cancelClick();
746
+ return;
747
+ }
748
+ scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq);
749
+ isFirst = false;
750
+ },
751
+ cancelClick = function () {
752
+ if (scrollTimeout) {
753
+ clearTimeout(scrollTimeout);
754
+ }
755
+ scrollTimeout = null;
756
+ $(document).off('mouseup.jsp', cancelClick);
757
+ };
758
+ doScroll();
759
+ $(document).on('mouseup.jsp', cancelClick);
760
+ return false;
676
761
  }
677
- scrollTimeout = null;
678
- $(document).off('mouseup.jsp', cancelClick);
679
- };
680
- doScroll();
681
- $(document).on('mouseup.jsp', cancelClick);
682
- return false;
762
+ });
683
763
  }
684
- }
685
- );
686
- }
687
- }
764
+ }
688
765
 
689
- function removeClickOnTrack()
690
- {
691
- if (horizontalTrack) {
692
- horizontalTrack.off('mousedown.jsp');
693
- }
694
- if (verticalTrack) {
695
- verticalTrack.off('mousedown.jsp');
696
- }
697
- }
766
+ function removeClickOnTrack() {
767
+ if (horizontalTrack) {
768
+ horizontalTrack.off('mousedown.jsp');
769
+ }
770
+ if (verticalTrack) {
771
+ verticalTrack.off('mousedown.jsp');
772
+ }
773
+ }
698
774
 
699
- function cancelDrag()
700
- {
701
- $('html').off('dragstart.jsp selectstart.jsp mousemove.jsp mouseup.jsp mouseleave.jsp');
775
+ function cancelDrag() {
776
+ $('html').off('dragstart.jsp selectstart.jsp mousemove.jsp mouseup.jsp mouseleave.jsp');
702
777
 
703
- if (verticalDrag) {
704
- verticalDrag.removeClass('jspActive');
705
- }
706
- if (horizontalDrag) {
707
- horizontalDrag.removeClass('jspActive');
708
- }
709
- }
778
+ if (verticalDrag) {
779
+ verticalDrag.removeClass('jspActive');
780
+ }
781
+ if (horizontalDrag) {
782
+ horizontalDrag.removeClass('jspActive');
783
+ }
784
+ }
710
785
 
711
- function positionDragY(destY, animate)
712
- {
713
- if (!isScrollableV) {
714
- return;
715
- }
716
- if (destY < 0) {
717
- destY = 0;
718
- } else if (destY > dragMaxY) {
719
- destY = dragMaxY;
720
- }
786
+ function positionDragY(destY, animate) {
787
+ if (!isScrollableV) {
788
+ return;
789
+ }
790
+ if (destY < 0) {
791
+ destY = 0;
792
+ } else if (destY > dragMaxY) {
793
+ destY = dragMaxY;
794
+ }
721
795
 
722
- // allow for devs to prevent the JSP from being scrolled
723
- var willScrollYEvent = new $.Event("jsp-will-scroll-y");
724
- elem.trigger(willScrollYEvent, [destY]);
796
+ // allow for devs to prevent the JSP from being scrolled
797
+ var willScrollYEvent = new $.Event('jsp-will-scroll-y');
798
+ elem.trigger(willScrollYEvent, [destY]);
725
799
 
726
- if (willScrollYEvent.isDefaultPrevented()) {
727
- return;
728
- }
800
+ if (willScrollYEvent.isDefaultPrevented()) {
801
+ return;
802
+ }
729
803
 
730
- var tmpVerticalDragPosition = destY || 0;
804
+ var tmpVerticalDragPosition = destY || 0;
731
805
 
732
- var isAtTop = tmpVerticalDragPosition === 0,
733
- isAtBottom = tmpVerticalDragPosition == dragMaxY,
734
- percentScrolled = destY/ dragMaxY,
735
- destTop = -percentScrolled * (contentHeight - paneHeight);
806
+ var isAtTop = tmpVerticalDragPosition === 0,
807
+ isAtBottom = tmpVerticalDragPosition == dragMaxY,
808
+ percentScrolled = destY / dragMaxY,
809
+ destTop = -percentScrolled * (contentHeight - paneHeight);
736
810
 
737
- // can't just check if(animate) because false is a valid value that could be passed in...
738
- if (animate === undefined) {
739
- animate = settings.animateScroll;
740
- }
741
- if (animate) {
742
- jsp.animate(verticalDrag, 'top', destY, _positionDragY, function() {
743
- elem.trigger('jsp-user-scroll-y', [-destTop, isAtTop, isAtBottom]);
744
- });
745
- } else {
746
- verticalDrag.css('top', destY);
747
- _positionDragY(destY);
748
- elem.trigger('jsp-user-scroll-y', [-destTop, isAtTop, isAtBottom]);
749
- }
811
+ // can't just check if(animate) because false is a valid value that could be passed in...
812
+ if (animate === undefined) {
813
+ animate = settings.animateScroll;
814
+ }
815
+ if (animate) {
816
+ jsp.animate(verticalDrag, 'top', destY, _positionDragY, function () {
817
+ elem.trigger('jsp-user-scroll-y', [-destTop, isAtTop, isAtBottom]);
818
+ });
819
+ } else {
820
+ verticalDrag.css('top', destY + 'px');
821
+ _positionDragY(destY);
822
+ elem.trigger('jsp-user-scroll-y', [-destTop, isAtTop, isAtBottom]);
823
+ }
824
+ }
750
825
 
751
- }
826
+ function _positionDragY(destY) {
827
+ if (destY === undefined) {
828
+ destY = verticalDrag.position().top;
829
+ }
752
830
 
753
- function _positionDragY(destY)
754
- {
755
- if (destY === undefined) {
756
- destY = verticalDrag.position().top;
757
- }
831
+ container.scrollTop(0);
832
+ verticalDragPosition = destY || 0;
758
833
 
759
- container.scrollTop(0);
760
- verticalDragPosition = destY || 0;
834
+ var isAtTop = verticalDragPosition === 0,
835
+ isAtBottom = verticalDragPosition == dragMaxY,
836
+ percentScrolled = destY / dragMaxY,
837
+ destTop = -percentScrolled * (contentHeight - paneHeight);
761
838
 
762
- var isAtTop = verticalDragPosition === 0,
763
- isAtBottom = verticalDragPosition == dragMaxY,
764
- percentScrolled = destY/ dragMaxY,
765
- destTop = -percentScrolled * (contentHeight - paneHeight);
839
+ if (wasAtTop != isAtTop || wasAtBottom != isAtBottom) {
840
+ wasAtTop = isAtTop;
841
+ wasAtBottom = isAtBottom;
842
+ elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]);
843
+ }
766
844
 
767
- if (wasAtTop != isAtTop || wasAtBottom != isAtBottom) {
768
- wasAtTop = isAtTop;
769
- wasAtBottom = isAtBottom;
770
- elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]);
771
- }
845
+ updateVerticalArrows(isAtTop, isAtBottom);
846
+ pane.css('top', destTop + 'px');
847
+ elem.trigger('jsp-scroll-y', [-destTop, isAtTop, isAtBottom]).trigger('scroll');
848
+ }
772
849
 
773
- updateVerticalArrows(isAtTop, isAtBottom);
774
- pane.css('top', destTop);
775
- elem.trigger('jsp-scroll-y', [-destTop, isAtTop, isAtBottom]).trigger('scroll');
776
- }
850
+ function positionDragX(destX, animate) {
851
+ if (!isScrollableH) {
852
+ return;
853
+ }
854
+ if (destX < 0) {
855
+ destX = 0;
856
+ } else if (destX > dragMaxX) {
857
+ destX = dragMaxX;
858
+ }
777
859
 
778
- function positionDragX(destX, animate)
779
- {
780
- if (!isScrollableH) {
781
- return;
782
- }
783
- if (destX < 0) {
784
- destX = 0;
785
- } else if (destX > dragMaxX) {
786
- destX = dragMaxX;
787
- }
860
+ // allow for devs to prevent the JSP from being scrolled
861
+ var willScrollXEvent = new $.Event('jsp-will-scroll-x');
862
+ elem.trigger(willScrollXEvent, [destX]);
788
863
 
864
+ if (willScrollXEvent.isDefaultPrevented()) {
865
+ return;
866
+ }
789
867
 
790
- // allow for devs to prevent the JSP from being scrolled
791
- var willScrollXEvent = new $.Event("jsp-will-scroll-x");
792
- elem.trigger(willScrollXEvent, [destX]);
868
+ var tmpHorizontalDragPosition = destX || 0;
793
869
 
794
- if (willScrollXEvent.isDefaultPrevented()) {
795
- return;
796
- }
870
+ var isAtLeft = tmpHorizontalDragPosition === 0,
871
+ isAtRight = tmpHorizontalDragPosition == dragMaxX,
872
+ percentScrolled = destX / dragMaxX,
873
+ destLeft = -percentScrolled * (contentWidth - paneWidth);
797
874
 
798
- var tmpHorizontalDragPosition = destX ||0;
875
+ if (animate === undefined) {
876
+ animate = settings.animateScroll;
877
+ }
878
+ if (animate) {
879
+ jsp.animate(horizontalDrag, 'left', destX, _positionDragX, function () {
880
+ elem.trigger('jsp-user-scroll-x', [-destLeft, isAtLeft, isAtRight]);
881
+ });
882
+ } else {
883
+ horizontalDrag.css('left', destX + 'px');
884
+ _positionDragX(destX);
885
+ elem.trigger('jsp-user-scroll-x', [-destLeft, isAtLeft, isAtRight]);
886
+ }
887
+ }
799
888
 
800
- var isAtLeft = tmpHorizontalDragPosition === 0,
801
- isAtRight = tmpHorizontalDragPosition == dragMaxX,
802
- percentScrolled = destX / dragMaxX,
803
- destLeft = -percentScrolled * (contentWidth - paneWidth);
889
+ function _positionDragX(destX) {
890
+ if (destX === undefined) {
891
+ destX = horizontalDrag.position().left;
892
+ }
804
893
 
805
- if (animate === undefined) {
806
- animate = settings.animateScroll;
807
- }
808
- if (animate) {
809
- jsp.animate(horizontalDrag, 'left', destX, _positionDragX, function() {
810
- elem.trigger('jsp-user-scroll-x', [-destLeft, isAtLeft, isAtRight]);
811
- });
812
- } else {
813
- horizontalDrag.css('left', destX);
814
- _positionDragX(destX);
815
- elem.trigger('jsp-user-scroll-x', [-destLeft, isAtLeft, isAtRight]);
816
- }
817
- }
894
+ container.scrollTop(0);
895
+ horizontalDragPosition = destX || 0;
818
896
 
819
- function _positionDragX(destX)
820
- {
821
- if (destX === undefined) {
822
- destX = horizontalDrag.position().left;
823
- }
897
+ var isAtLeft = horizontalDragPosition === 0,
898
+ isAtRight = horizontalDragPosition == dragMaxX,
899
+ percentScrolled = destX / dragMaxX,
900
+ destLeft = -percentScrolled * (contentWidth - paneWidth);
824
901
 
825
- container.scrollTop(0);
826
- horizontalDragPosition = destX ||0;
902
+ if (wasAtLeft != isAtLeft || wasAtRight != isAtRight) {
903
+ wasAtLeft = isAtLeft;
904
+ wasAtRight = isAtRight;
905
+ elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]);
906
+ }
827
907
 
828
- var isAtLeft = horizontalDragPosition === 0,
829
- isAtRight = horizontalDragPosition == dragMaxX,
830
- percentScrolled = destX / dragMaxX,
831
- destLeft = -percentScrolled * (contentWidth - paneWidth);
908
+ updateHorizontalArrows(isAtLeft, isAtRight);
909
+ pane.css('left', destLeft + 'px');
910
+ elem.trigger('jsp-scroll-x', [-destLeft, isAtLeft, isAtRight]).trigger('scroll');
911
+ }
832
912
 
833
- if (wasAtLeft != isAtLeft || wasAtRight != isAtRight) {
834
- wasAtLeft = isAtLeft;
835
- wasAtRight = isAtRight;
836
- elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]);
837
- }
913
+ function updateVerticalArrows(isAtTop, isAtBottom) {
914
+ if (settings.showArrows) {
915
+ arrowUp[isAtTop ? 'addClass' : 'removeClass']('jspDisabled');
916
+ arrowDown[isAtBottom ? 'addClass' : 'removeClass']('jspDisabled');
917
+ }
918
+ }
838
919
 
839
- updateHorizontalArrows(isAtLeft, isAtRight);
840
- pane.css('left', destLeft);
841
- elem.trigger('jsp-scroll-x', [-destLeft, isAtLeft, isAtRight]).trigger('scroll');
842
- }
920
+ function updateHorizontalArrows(isAtLeft, isAtRight) {
921
+ if (settings.showArrows) {
922
+ arrowLeft[isAtLeft ? 'addClass' : 'removeClass']('jspDisabled');
923
+ arrowRight[isAtRight ? 'addClass' : 'removeClass']('jspDisabled');
924
+ }
925
+ }
843
926
 
844
- function updateVerticalArrows(isAtTop, isAtBottom)
845
- {
846
- if (settings.showArrows) {
847
- arrowUp[isAtTop ? 'addClass' : 'removeClass']('jspDisabled');
848
- arrowDown[isAtBottom ? 'addClass' : 'removeClass']('jspDisabled');
849
- }
850
- }
927
+ function scrollToY(destY, animate) {
928
+ var percentScrolled = destY / (contentHeight - paneHeight);
929
+ positionDragY(percentScrolled * dragMaxY, animate);
930
+ }
851
931
 
852
- function updateHorizontalArrows(isAtLeft, isAtRight)
853
- {
854
- if (settings.showArrows) {
855
- arrowLeft[isAtLeft ? 'addClass' : 'removeClass']('jspDisabled');
856
- arrowRight[isAtRight ? 'addClass' : 'removeClass']('jspDisabled');
857
- }
858
- }
859
-
860
- function scrollToY(destY, animate)
861
- {
862
- var percentScrolled = destY / (contentHeight - paneHeight);
863
- positionDragY(percentScrolled * dragMaxY, animate);
864
- }
865
-
866
- function scrollToX(destX, animate)
867
- {
868
- var percentScrolled = destX / (contentWidth - paneWidth);
869
- positionDragX(percentScrolled * dragMaxX, animate);
870
- }
871
-
872
- function scrollToElement(ele, stickToTop, animate)
873
- {
874
- var e, eleHeight, eleWidth, eleTop = 0, eleLeft = 0, viewportTop, viewportLeft, maxVisibleEleTop, maxVisibleEleLeft, destY, destX;
875
-
876
- // Legal hash values aren't necessarily legal jQuery selectors so we need to catch any
877
- // errors from the lookup...
878
- try {
879
- e = $(ele);
880
- } catch (err) {
881
- return;
882
- }
883
- eleHeight = e.outerHeight();
884
- eleWidth= e.outerWidth();
885
-
886
- container.scrollTop(0);
887
- container.scrollLeft(0);
888
-
889
- // loop through parents adding the offset top of any elements that are relatively positioned between
890
- // the focused element and the jspPane so we can get the true distance from the top
891
- // of the focused element to the top of the scrollpane...
892
- while (!e.is('.jspPane')) {
893
- eleTop += e.position().top;
894
- eleLeft += e.position().left;
895
- e = e.offsetParent();
896
- if (/^body|html$/i.test(e[0].nodeName)) {
897
- // we ended up too high in the document structure. Quit!
898
- return;
899
- }
900
- }
932
+ function scrollToX(destX, animate) {
933
+ var percentScrolled = destX / (contentWidth - paneWidth);
934
+ positionDragX(percentScrolled * dragMaxX, animate);
935
+ }
901
936
 
902
- viewportTop = contentPositionY();
903
- maxVisibleEleTop = viewportTop + paneHeight;
904
- if (eleTop < viewportTop || stickToTop) { // element is above viewport
905
- destY = eleTop - settings.horizontalGutter;
906
- } else if (eleTop + eleHeight > maxVisibleEleTop) { // element is below viewport
907
- destY = eleTop - paneHeight + eleHeight + settings.horizontalGutter;
908
- }
909
- if (!isNaN(destY)) {
910
- scrollToY(destY, animate);
911
- }
937
+ function scrollToElement(ele, stickToTop, animate) {
938
+ var e,
939
+ eleHeight,
940
+ eleWidth,
941
+ eleTop = 0,
942
+ eleLeft = 0,
943
+ viewportTop,
944
+ viewportLeft,
945
+ maxVisibleEleTop,
946
+ maxVisibleEleLeft,
947
+ destY,
948
+ destX;
949
+
950
+ // Legal hash values aren't necessarily legal jQuery selectors so we need to catch any
951
+ // errors from the lookup...
952
+ try {
953
+ e = $(ele);
954
+ } catch (err) {
955
+ return;
956
+ }
957
+ eleHeight = e.outerHeight();
958
+ eleWidth = e.outerWidth();
959
+
960
+ container.scrollTop(0);
961
+ container.scrollLeft(0);
962
+
963
+ // loop through parents adding the offset top of any elements that are relatively positioned between
964
+ // the focused element and the jspPane so we can get the true distance from the top
965
+ // of the focused element to the top of the scrollpane...
966
+ while (!e.is('.jspPane')) {
967
+ eleTop += e.position().top;
968
+ eleLeft += e.position().left;
969
+ e = e.offsetParent();
970
+ if (/^body|html$/i.test(e[0].nodeName)) {
971
+ // we ended up too high in the document structure. Quit!
972
+ return;
973
+ }
974
+ }
912
975
 
913
- viewportLeft = contentPositionX();
914
- maxVisibleEleLeft = viewportLeft + paneWidth;
915
- if (eleLeft < viewportLeft || stickToTop) { // element is to the left of viewport
916
- destX = eleLeft - settings.horizontalGutter;
917
- } else if (eleLeft + eleWidth > maxVisibleEleLeft) { // element is to the right viewport
918
- destX = eleLeft - paneWidth + eleWidth + settings.horizontalGutter;
919
- }
920
- if (!isNaN(destX)) {
921
- scrollToX(destX, animate);
922
- }
976
+ viewportTop = contentPositionY();
977
+ maxVisibleEleTop = viewportTop + paneHeight;
978
+ if (eleTop < viewportTop || stickToTop) {
979
+ // element is above viewport
980
+ destY = eleTop - settings.horizontalGutter;
981
+ } else if (eleTop + eleHeight > maxVisibleEleTop) {
982
+ // element is below viewport
983
+ destY = eleTop - paneHeight + eleHeight + settings.horizontalGutter;
984
+ }
985
+ if (!isNaN(destY)) {
986
+ scrollToY(destY, animate);
987
+ }
923
988
 
924
- }
925
-
926
- function contentPositionX()
927
- {
928
- return -pane.position().left;
929
- }
930
-
931
- function contentPositionY()
932
- {
933
- return -pane.position().top;
934
- }
935
-
936
- function isCloseToBottom()
937
- {
938
- var scrollableHeight = contentHeight - paneHeight;
939
- return (scrollableHeight > 20) && (scrollableHeight - contentPositionY() < 10);
940
- }
941
-
942
- function isCloseToRight()
943
- {
944
- var scrollableWidth = contentWidth - paneWidth;
945
- return (scrollableWidth > 20) && (scrollableWidth - contentPositionX() < 10);
946
- }
947
-
948
- function initMousewheel()
949
- {
950
- container.off(mwEvent).on(
951
- mwEvent,
952
- function (event, delta, deltaX, deltaY) {
953
-
954
- if (!horizontalDragPosition) horizontalDragPosition = 0;
955
- if (!verticalDragPosition) verticalDragPosition = 0;
956
-
957
- var dX = horizontalDragPosition, dY = verticalDragPosition, factor = event.deltaFactor || settings.mouseWheelSpeed;
958
- jsp.scrollBy(deltaX * factor, -deltaY * factor, false);
959
- // return true if there was no movement so rest of screen can scroll
960
- return dX == horizontalDragPosition && dY == verticalDragPosition;
989
+ viewportLeft = contentPositionX();
990
+ maxVisibleEleLeft = viewportLeft + paneWidth;
991
+ if (eleLeft < viewportLeft || stickToTop) {
992
+ // element is to the left of viewport
993
+ destX = eleLeft - settings.horizontalGutter;
994
+ } else if (eleLeft + eleWidth > maxVisibleEleLeft) {
995
+ // element is to the right viewport
996
+ destX = eleLeft - paneWidth + eleWidth + settings.horizontalGutter;
997
+ }
998
+ if (!isNaN(destX)) {
999
+ scrollToX(destX, animate);
1000
+ }
961
1001
  }
962
- );
963
- }
964
-
965
- function removeMousewheel()
966
- {
967
- container.off(mwEvent);
968
- }
969
-
970
- function nil()
971
- {
972
- return false;
973
- }
974
-
975
- function initFocusHandler()
976
- {
977
- pane.find(':input,a').off('focus.jsp').on(
978
- 'focus.jsp',
979
- function(e)
980
- {
981
- scrollToElement(e.target, false);
1002
+
1003
+ function contentPositionX() {
1004
+ return -pane.position().left;
982
1005
  }
983
- );
984
- }
985
-
986
- function removeFocusHandler()
987
- {
988
- pane.find(':input,a').off('focus.jsp');
989
- }
990
-
991
- function initKeyboardNav()
992
- {
993
- var keyDown, elementHasScrolled, validParents = [];
994
- if(isScrollableH) {
995
- validParents.push(horizontalBar[0]);
996
- }
997
1006
 
998
- if(isScrollableV) {
999
- validParents.push(verticalBar[0]);
1000
- }
1007
+ function contentPositionY() {
1008
+ return -pane.position().top;
1009
+ }
1010
+
1011
+ function isCloseToBottom() {
1012
+ var scrollableHeight = contentHeight - paneHeight;
1001
1013
 
1002
- // IE also focuses elements that don't have tabindex set.
1003
- pane.on(
1004
- 'focus.jsp',
1005
- function()
1006
- {
1007
- elem.focus();
1014
+ if (settings.maintainPosition == true) {
1015
+ return scrollableHeight >= 20 && scrollableHeight - contentPositionY() < 10;
1016
+ }
1017
+
1018
+ return true;
1008
1019
  }
1009
- );
1010
-
1011
- elem.attr('tabindex', 0)
1012
- .off('keydown.jsp keypress.jsp')
1013
- .on(
1014
- 'keydown.jsp',
1015
- function(e)
1016
- {
1017
- if (e.target !== this && !(validParents.length && $(e.target).closest(validParents).length)){
1018
- return;
1019
- }
1020
- var dX = horizontalDragPosition, dY = verticalDragPosition;
1021
- switch(e.keyCode) {
1022
- case 40: // down
1023
- case 38: // up
1024
- case 34: // page down
1025
- case 32: // space
1026
- case 33: // page up
1027
- case 39: // right
1028
- case 37: // left
1029
- keyDown = e.keyCode;
1030
- keyDownHandler();
1031
- break;
1032
- case 35: // end
1033
- scrollToY(contentHeight - paneHeight);
1034
- keyDown = null;
1035
- break;
1036
- case 36: // home
1037
- scrollToY(0);
1038
- keyDown = null;
1039
- break;
1040
- }
1041
-
1042
- elementHasScrolled = e.keyCode == keyDown && dX != horizontalDragPosition || dY != verticalDragPosition;
1043
- return !elementHasScrolled;
1020
+
1021
+ function isCloseToRight() {
1022
+ var scrollableWidth = contentWidth - paneWidth;
1023
+
1024
+ if (settings.maintainPosition == true) {
1025
+ return scrollableWidth >= 20 && scrollableWidth - contentPositionX() < 10;
1044
1026
  }
1045
- ).on(
1046
- 'keypress.jsp', // For FF/ OSX so that we can cancel the repeat key presses if the JSP scrolls...
1047
- function(e)
1048
- {
1049
- if (e.keyCode == keyDown) {
1050
- keyDownHandler();
1051
- }
1052
- // If the keypress is not related to the area, ignore it. Fixes problem with inputs inside scrolled area. Copied from line 955.
1053
- if (e.target !== this && !(validParents.length && $(e.target).closest(validParents).length)){
1054
- return;
1055
- }
1056
- return !elementHasScrolled;
1027
+
1028
+ return true;
1057
1029
  }
1058
- );
1059
-
1060
- if (settings.hideFocus) {
1061
- elem.css('outline', 'none');
1062
- if ('hideFocus' in container[0]){
1063
- elem.attr('hideFocus', true);
1064
- }
1065
- } else {
1066
- elem.css('outline', '');
1067
- if ('hideFocus' in container[0]){
1068
- elem.attr('hideFocus', false);
1069
- }
1070
- }
1071
1030
 
1072
- function keyDownHandler()
1073
- {
1074
- var dX = horizontalDragPosition, dY = verticalDragPosition;
1075
- switch(keyDown) {
1076
- case 40: // down
1077
- jsp.scrollByY(settings.keyboardSpeed, false);
1078
- break;
1079
- case 38: // up
1080
- jsp.scrollByY(-settings.keyboardSpeed, false);
1081
- break;
1082
- case 34: // page down
1083
- case 32: // space
1084
- jsp.scrollByY(paneHeight * settings.scrollPagePercent, false);
1085
- break;
1086
- case 33: // page up
1087
- jsp.scrollByY(-paneHeight * settings.scrollPagePercent, false);
1088
- break;
1089
- case 39: // right
1090
- jsp.scrollByX(settings.keyboardSpeed, false);
1091
- break;
1092
- case 37: // left
1093
- jsp.scrollByX(-settings.keyboardSpeed, false);
1094
- break;
1095
- }
1096
-
1097
- elementHasScrolled = dX != horizontalDragPosition || dY != verticalDragPosition;
1098
- return elementHasScrolled;
1099
- }
1100
- }
1101
-
1102
- function removeKeyboardNav()
1103
- {
1104
- elem.attr('tabindex', '-1')
1105
- .removeAttr('tabindex')
1106
- .off('keydown.jsp keypress.jsp');
1107
-
1108
- pane.off('.jsp');
1109
- }
1110
-
1111
- function observeHash()
1112
- {
1113
- if (location.hash && location.hash.length > 1) {
1114
- var e,
1115
- retryInt,
1116
- hash = escape(location.hash.substr(1)) // hash must be escaped to prevent XSS
1117
- ;
1118
- try {
1119
- e = $('#' + hash + ', a[name="' + hash + '"]');
1120
- } catch (err) {
1121
- return;
1122
- }
1123
-
1124
- if (e.length && pane.find(hash)) {
1125
- // nasty workaround but it appears to take a little while before the hash has done its thing
1126
- // to the rendered page so we just wait until the container's scrollTop has been messed up.
1127
- if (container.scrollTop() === 0) {
1128
- retryInt = setInterval(
1129
- function()
1130
- {
1131
- if (container.scrollTop() > 0) {
1132
- scrollToElement(e, true);
1133
- $(document).scrollTop(container.position().top);
1134
- clearInterval(retryInt);
1135
- }
1136
- },
1137
- 50
1138
- );
1139
- } else {
1140
- scrollToElement(e, true);
1141
- $(document).scrollTop(container.position().top);
1031
+ function initMousewheel() {
1032
+ container.off(mwEvent).on(mwEvent, function (event, delta, deltaX, deltaY) {
1033
+ if (!horizontalDragPosition) horizontalDragPosition = 0;
1034
+ if (!verticalDragPosition) verticalDragPosition = 0;
1035
+
1036
+ var dX = horizontalDragPosition,
1037
+ dY = verticalDragPosition,
1038
+ factor = event.deltaFactor || settings.mouseWheelSpeed;
1039
+ jsp.scrollBy(deltaX * factor, -deltaY * factor, false);
1040
+ // return true if there was no movement so rest of screen can scroll
1041
+ return dX == horizontalDragPosition && dY == verticalDragPosition;
1042
+ });
1142
1043
  }
1143
- }
1144
- }
1145
- }
1146
1044
 
1147
- function hijackInternalLinks()
1148
- {
1149
- // only register the link handler once
1150
- if ($(document.body).data('jspHijack')) {
1151
- return;
1152
- }
1045
+ function removeMousewheel() {
1046
+ container.off(mwEvent);
1047
+ }
1153
1048
 
1154
- // remember that the handler was bound
1155
- $(document.body).data('jspHijack', true);
1156
-
1157
- // use live handler to also capture newly created links
1158
- $(document.body).delegate('a[href*="#"]', 'click', function(event) {
1159
- // does the link point to the same page?
1160
- // this also takes care of cases with a <base>-Tag or Links not starting with the hash #
1161
- // e.g. <a href="index.html#test"> when the current url already is index.html
1162
- var href = this.href.substr(0, this.href.indexOf('#')),
1163
- locationHref = location.href,
1164
- hash,
1165
- element,
1166
- container,
1167
- jsp,
1168
- scrollTop,
1169
- elementTop;
1170
- if (location.href.indexOf('#') !== -1) {
1171
- locationHref = location.href.substr(0, location.href.indexOf('#'));
1172
- }
1173
- if (href !== locationHref) {
1174
- // the link points to another page
1175
- return;
1176
- }
1177
-
1178
- // check if jScrollPane should handle this click event
1179
- hash = escape(this.href.substr(this.href.indexOf('#') + 1));
1180
-
1181
- // find the element on the page
1182
- try {
1183
- element = $('#' + hash + ', a[name="' + hash + '"]');
1184
- } catch (e) {
1185
- // hash is not a valid jQuery identifier
1186
- return;
1187
- }
1188
-
1189
- if (!element.length) {
1190
- // this link does not point to an element on this page
1191
- return;
1192
- }
1193
-
1194
- container = element.closest('.jspScrollable');
1195
- jsp = container.data('jsp');
1196
-
1197
- // jsp might be another jsp instance than the one, that bound this event
1198
- // remember: this event is only bound once for all instances.
1199
- jsp.scrollToElement(element, true);
1200
-
1201
- if (container[0].scrollIntoView) {
1202
- // also scroll to the top of the container (if it is not visible)
1203
- scrollTop = $(window).scrollTop();
1204
- elementTop = element.offset().top;
1205
- if (elementTop < scrollTop || elementTop > scrollTop + $(window).height()) {
1206
- container[0].scrollIntoView();
1049
+ function nil() {
1050
+ return false;
1207
1051
  }
1208
- }
1209
1052
 
1210
- // jsp handled this event, prevent the browser default (scrolling :P)
1211
- event.preventDefault();
1212
- });
1213
- }
1214
-
1215
- // Init touch on iPad, iPhone, iPod, Android
1216
- function initTouch()
1217
- {
1218
- var startX,
1219
- startY,
1220
- touchStartX,
1221
- touchStartY,
1222
- moved,
1223
- moving = false;
1224
-
1225
- container.off('touchstart.jsp touchmove.jsp touchend.jsp click.jsp-touchclick').on(
1226
- 'touchstart.jsp',
1227
- function(e)
1228
- {
1229
- var touch = e.originalEvent.touches[0];
1230
- startX = contentPositionX();
1231
- startY = contentPositionY();
1232
- touchStartX = touch.pageX;
1233
- touchStartY = touch.pageY;
1234
- moved = false;
1235
- moving = true;
1053
+ function initFocusHandler() {
1054
+ pane.find(':input,a')
1055
+ .off('focus.jsp')
1056
+ .on('focus.jsp', function (e) {
1057
+ scrollToElement(e.target, false);
1058
+ });
1236
1059
  }
1237
- ).on(
1238
- 'touchmove.jsp',
1239
- function(ev)
1240
- {
1241
- if(!moving) {
1242
- return;
1243
- }
1244
1060
 
1245
- var touchPos = ev.originalEvent.touches[0],
1246
- dX = horizontalDragPosition, dY = verticalDragPosition;
1061
+ function removeFocusHandler() {
1062
+ pane.find(':input,a').off('focus.jsp');
1063
+ }
1064
+
1065
+ function initKeyboardNav() {
1066
+ var keyDown,
1067
+ elementHasScrolled,
1068
+ validParents = [];
1069
+ if (isScrollableH) {
1070
+ validParents.push(horizontalBar[0]);
1071
+ }
1072
+
1073
+ if (isScrollableV) {
1074
+ validParents.push(verticalBar[0]);
1075
+ }
1076
+
1077
+ // IE also focuses elements that don't have tabindex set.
1078
+ pane.on('focus.jsp', function () {
1079
+ elem.focus();
1080
+ });
1247
1081
 
1248
- jsp.scrollTo(startX + touchStartX - touchPos.pageX, startY + touchStartY - touchPos.pageY);
1082
+ elem.attr('tabindex', 0)
1083
+ .off('keydown.jsp keypress.jsp')
1084
+ .on('keydown.jsp', function (e) {
1085
+ if (e.target !== this && !(validParents.length && $(e.target).closest(validParents).length)) {
1086
+ return;
1087
+ }
1088
+ var dX = horizontalDragPosition,
1089
+ dY = verticalDragPosition;
1090
+ switch (e.keyCode) {
1091
+ case 40: // down
1092
+ case 38: // up
1093
+ case 34: // page down
1094
+ case 32: // space
1095
+ case 33: // page up
1096
+ case 39: // right
1097
+ case 37: // left
1098
+ keyDown = e.keyCode;
1099
+ keyDownHandler();
1100
+ break;
1101
+ case 35: // end
1102
+ scrollToY(contentHeight - paneHeight);
1103
+ keyDown = null;
1104
+ break;
1105
+ case 36: // home
1106
+ scrollToY(0);
1107
+ keyDown = null;
1108
+ break;
1109
+ }
1249
1110
 
1250
- moved = moved || Math.abs(touchStartX - touchPos.pageX) > 5 || Math.abs(touchStartY - touchPos.pageY) > 5;
1111
+ elementHasScrolled = (e.keyCode == keyDown && dX != horizontalDragPosition) || dY != verticalDragPosition;
1112
+ return !elementHasScrolled;
1113
+ })
1114
+ .on(
1115
+ 'keypress.jsp', // For FF/ OSX so that we can cancel the repeat key presses if the JSP scrolls...
1116
+ function (e) {
1117
+ if (e.keyCode == keyDown) {
1118
+ keyDownHandler();
1119
+ }
1120
+ // If the keypress is not related to the area, ignore it. Fixes problem with inputs inside scrolled area. Copied from line 955.
1121
+ if (e.target !== this && !(validParents.length && $(e.target).closest(validParents).length)) {
1122
+ return;
1123
+ }
1124
+ return !elementHasScrolled;
1125
+ },
1126
+ );
1127
+
1128
+ if (settings.hideFocus) {
1129
+ elem.css('outline', 'none');
1130
+ if ('hideFocus' in container[0]) {
1131
+ elem.attr('hideFocus', true);
1132
+ }
1133
+ } else {
1134
+ elem.css('outline', '');
1135
+ if ('hideFocus' in container[0]) {
1136
+ elem.attr('hideFocus', false);
1137
+ }
1138
+ }
1251
1139
 
1252
- // return true if there was no movement so rest of screen can scroll
1253
- return dX == horizontalDragPosition && dY == verticalDragPosition;
1140
+ function keyDownHandler() {
1141
+ var dX = horizontalDragPosition,
1142
+ dY = verticalDragPosition;
1143
+ switch (keyDown) {
1144
+ case 40: // down
1145
+ jsp.scrollByY(settings.keyboardSpeed, false);
1146
+ break;
1147
+ case 38: // up
1148
+ jsp.scrollByY(-settings.keyboardSpeed, false);
1149
+ break;
1150
+ case 34: // page down
1151
+ case 32: // space
1152
+ jsp.scrollByY(paneHeight * settings.scrollPagePercent, false);
1153
+ break;
1154
+ case 33: // page up
1155
+ jsp.scrollByY(-paneHeight * settings.scrollPagePercent, false);
1156
+ break;
1157
+ case 39: // right
1158
+ jsp.scrollByX(settings.keyboardSpeed, false);
1159
+ break;
1160
+ case 37: // left
1161
+ jsp.scrollByX(-settings.keyboardSpeed, false);
1162
+ break;
1163
+ }
1164
+
1165
+ elementHasScrolled = dX != horizontalDragPosition || dY != verticalDragPosition;
1166
+ return elementHasScrolled;
1167
+ }
1254
1168
  }
1255
- ).on(
1256
- 'touchend.jsp',
1257
- function(e)
1258
- {
1259
- moving = false;
1260
- /*if(moved) {
1261
- return false;
1262
- }*/
1169
+
1170
+ function removeKeyboardNav() {
1171
+ elem.attr('tabindex', '-1').removeAttr('tabindex').off('keydown.jsp keypress.jsp');
1172
+
1173
+ pane.off('.jsp');
1263
1174
  }
1264
- ).on(
1265
- 'click.jsp-touchclick',
1266
- function(e)
1267
- {
1268
- if(moved) {
1269
- moved = false;
1270
- return false;
1271
- }
1175
+
1176
+ function observeHash() {
1177
+ if (location.hash && location.hash.length > 1) {
1178
+ var e,
1179
+ retryInt,
1180
+ hash = escape(location.hash.substr(1)); // hash must be escaped to prevent XSS
1181
+ try {
1182
+ e = $('#' + hash + ', a[name="' + hash + '"]');
1183
+ } catch (err) {
1184
+ return;
1185
+ }
1186
+
1187
+ if (e.length && pane.find(hash)) {
1188
+ // nasty workaround but it appears to take a little while before the hash has done its thing
1189
+ // to the rendered page so we just wait until the container's scrollTop has been messed up.
1190
+ if (container.scrollTop() === 0) {
1191
+ retryInt = setInterval(function () {
1192
+ if (container.scrollTop() > 0) {
1193
+ scrollToElement(e, true);
1194
+ $(document).scrollTop(container.position().top);
1195
+ clearInterval(retryInt);
1196
+ }
1197
+ }, 50);
1198
+ } else {
1199
+ scrollToElement(e, true);
1200
+ $(document).scrollTop(container.position().top);
1201
+ }
1202
+ }
1203
+ }
1272
1204
  }
1273
- );
1274
- }
1275
-
1276
- function destroy(){
1277
- var currentY = contentPositionY(),
1278
- currentX = contentPositionX();
1279
- elem.removeClass('jspScrollable').off('.jsp');
1280
- pane.off('.jsp');
1281
- elem.replaceWith(originalElement.append(pane.children()));
1282
- originalElement.scrollTop(currentY);
1283
- originalElement.scrollLeft(currentX);
1284
-
1285
- // clear reinitialize timer if active
1286
- if (reinitialiseInterval) {
1287
- clearInterval(reinitialiseInterval);
1288
- }
1289
- }
1290
-
1291
- // Public API
1292
- $.extend(
1293
- jsp,
1294
- {
1295
- // Reinitialises the scroll pane (if it's internal dimensions have changed since the last time it
1296
- // was initialised). The settings object which is passed in will override any settings from the
1297
- // previous time it was initialised - if you don't pass any settings then the ones from the previous
1298
- // initialisation will be used.
1299
- reinitialise: function(s)
1300
- {
1301
- s = $.extend({}, settings, s);
1302
- initialise(s);
1303
- },
1304
- // Scrolls the specified element (a jQuery object, DOM node or jQuery selector string) into view so
1305
- // that it can be seen within the viewport. If stickToTop is true then the element will appear at
1306
- // the top of the viewport, if it is false then the viewport will scroll as little as possible to
1307
- // show the element. You can also specify if you want animation to occur. If you don't provide this
1308
- // argument then the animateScroll value from the settings object is used instead.
1309
- scrollToElement: function(ele, stickToTop, animate)
1310
- {
1311
- scrollToElement(ele, stickToTop, animate);
1312
- },
1313
- // Scrolls the pane so that the specified co-ordinates within the content are at the top left
1314
- // of the viewport. animate is optional and if not passed then the value of animateScroll from
1315
- // the settings object this jScrollPane was initialised with is used.
1316
- scrollTo: function(destX, destY, animate)
1317
- {
1318
- scrollToX(destX, animate);
1319
- scrollToY(destY, animate);
1320
- },
1321
- // Scrolls the pane so that the specified co-ordinate within the content is at the left of the
1322
- // viewport. animate is optional and if not passed then the value of animateScroll from the settings
1323
- // object this jScrollPane was initialised with is used.
1324
- scrollToX: function(destX, animate)
1325
- {
1326
- scrollToX(destX, animate);
1327
- },
1328
- // Scrolls the pane so that the specified co-ordinate within the content is at the top of the
1329
- // viewport. animate is optional and if not passed then the value of animateScroll from the settings
1330
- // object this jScrollPane was initialised with is used.
1331
- scrollToY: function(destY, animate)
1332
- {
1333
- scrollToY(destY, animate);
1334
- },
1335
- // Scrolls the pane to the specified percentage of its maximum horizontal scroll position. animate
1336
- // is optional and if not passed then the value of animateScroll from the settings object this
1337
- // jScrollPane was initialised with is used.
1338
- scrollToPercentX: function(destPercentX, animate)
1339
- {
1340
- scrollToX(destPercentX * (contentWidth - paneWidth), animate);
1341
- },
1342
- // Scrolls the pane to the specified percentage of its maximum vertical scroll position. animate
1343
- // is optional and if not passed then the value of animateScroll from the settings object this
1344
- // jScrollPane was initialised with is used.
1345
- scrollToPercentY: function(destPercentY, animate)
1346
- {
1347
- scrollToY(destPercentY * (contentHeight - paneHeight), animate);
1348
- },
1349
- // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
1350
- // the value of animateScroll from the settings object this jScrollPane was initialised with is used.
1351
- scrollBy: function(deltaX, deltaY, animate)
1352
- {
1353
- jsp.scrollByX(deltaX, animate);
1354
- jsp.scrollByY(deltaY, animate);
1355
- },
1356
- // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
1357
- // the value of animateScroll from the settings object this jScrollPane was initialised with is used.
1358
- scrollByX: function(deltaX, animate)
1359
- {
1360
- var destX = contentPositionX() + Math[deltaX<0 ? 'floor' : 'ceil'](deltaX),
1361
- percentScrolled = destX / (contentWidth - paneWidth);
1362
- positionDragX(percentScrolled * dragMaxX, animate);
1363
- },
1364
- // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
1365
- // the value of animateScroll from the settings object this jScrollPane was initialised with is used.
1366
- scrollByY: function(deltaY, animate)
1367
- {
1368
- var destY = contentPositionY() + Math[deltaY<0 ? 'floor' : 'ceil'](deltaY),
1369
- percentScrolled = destY / (contentHeight - paneHeight);
1370
- positionDragY(percentScrolled * dragMaxY, animate);
1371
- },
1372
- // Positions the horizontal drag at the specified x position (and updates the viewport to reflect
1373
- // this). animate is optional and if not passed then the value of animateScroll from the settings
1374
- // object this jScrollPane was initialised with is used.
1375
- positionDragX: function(x, animate)
1376
- {
1377
- positionDragX(x, animate);
1378
- },
1379
- // Positions the vertical drag at the specified y position (and updates the viewport to reflect
1380
- // this). animate is optional and if not passed then the value of animateScroll from the settings
1381
- // object this jScrollPane was initialised with is used.
1382
- positionDragY: function(y, animate)
1383
- {
1384
- positionDragY(y, animate);
1385
- },
1386
- // This method is called when jScrollPane is trying to animate to a new position. You can override
1387
- // it if you want to provide advanced animation functionality. It is passed the following arguments:
1388
- // * ele - the element whose position is being animated
1389
- // * prop - the property that is being animated
1390
- // * value - the value it's being animated to
1391
- // * stepCallback - a function that you must execute each time you update the value of the property
1392
- // * completeCallback - a function that will be executed after the animation had finished
1393
- // You can use the default implementation (below) as a starting point for your own implementation.
1394
- animate: function(ele, prop, value, stepCallback, completeCallback)
1395
- {
1396
- var params = {};
1397
- params[prop] = value;
1398
- ele.animate(
1399
- params,
1400
- {
1401
- 'duration' : settings.animateDuration,
1402
- 'easing' : settings.animateEase,
1403
- 'queue' : false,
1404
- 'step' : stepCallback,
1405
- 'complete' : completeCallback
1406
- }
1407
- );
1408
- },
1409
- // Returns the current x position of the viewport with regards to the content pane.
1410
- getContentPositionX: function()
1411
- {
1412
- return contentPositionX();
1413
- },
1414
- // Returns the current y position of the viewport with regards to the content pane.
1415
- getContentPositionY: function()
1416
- {
1417
- return contentPositionY();
1418
- },
1419
- // Returns the width of the content within the scroll pane.
1420
- getContentWidth: function()
1421
- {
1422
- return contentWidth;
1423
- },
1424
- // Returns the height of the content within the scroll pane.
1425
- getContentHeight: function()
1426
- {
1427
- return contentHeight;
1428
- },
1429
- // Returns the horizontal position of the viewport within the pane content.
1430
- getPercentScrolledX: function()
1431
- {
1432
- return contentPositionX() / (contentWidth - paneWidth);
1433
- },
1434
- // Returns the vertical position of the viewport within the pane content.
1435
- getPercentScrolledY: function()
1436
- {
1437
- return contentPositionY() / (contentHeight - paneHeight);
1438
- },
1439
- // Returns whether or not this scrollpane has a horizontal scrollbar.
1440
- getIsScrollableH: function()
1441
- {
1442
- return isScrollableH;
1443
- },
1444
- // Returns whether or not this scrollpane has a vertical scrollbar.
1445
- getIsScrollableV: function()
1446
- {
1447
- return isScrollableV;
1448
- },
1449
- // Gets a reference to the content pane. It is important that you use this method if you want to
1450
- // edit the content of your jScrollPane as if you access the element directly then you may have some
1451
- // problems (as your original element has had additional elements for the scrollbars etc added into
1452
- // it).
1453
- getContentPane: function()
1454
- {
1455
- return pane;
1456
- },
1457
- // Scrolls this jScrollPane down as far as it can currently scroll. If animate isn't passed then the
1458
- // animateScroll value from settings is used instead.
1459
- scrollToBottom: function(animate)
1460
- {
1461
- positionDragY(dragMaxY, animate);
1462
- },
1463
- // Hijacks the links on the page which link to content inside the scrollpane. If you have changed
1464
- // the content of your page (e.g. via AJAX) and want to make sure any new anchor links to the
1465
- // contents of your scroll pane will work then call this function.
1466
- hijackInternalLinks: $.noop,
1467
- // Removes the jScrollPane and returns the page to the state it was in before jScrollPane was
1468
- // initialised.
1469
- destroy: function()
1470
- {
1471
- destroy();
1205
+
1206
+ function hijackInternalLinks() {
1207
+ // only register the link handler once
1208
+ if ($(document.body).data('jspHijack')) {
1209
+ return;
1210
+ }
1211
+
1212
+ // remember that the handler was bound
1213
+ $(document.body).data('jspHijack', true);
1214
+
1215
+ // use live handler to also capture newly created links
1216
+ $(document.body).delegate('a[href*="#"]', 'click', function (event) {
1217
+ // does the link point to the same page?
1218
+ // this also takes care of cases with a <base>-Tag or Links not starting with the hash #
1219
+ // e.g. <a href="index.html#test"> when the current url already is index.html
1220
+ var href = this.href.substr(0, this.href.indexOf('#')),
1221
+ locationHref = location.href,
1222
+ hash,
1223
+ element,
1224
+ container,
1225
+ jsp,
1226
+ scrollTop,
1227
+ elementTop;
1228
+ if (location.href.indexOf('#') !== -1) {
1229
+ locationHref = location.href.substr(0, location.href.indexOf('#'));
1230
+ }
1231
+ if (href !== locationHref) {
1232
+ // the link points to another page
1233
+ return;
1234
+ }
1235
+
1236
+ // check if jScrollPane should handle this click event
1237
+ hash = escape(this.href.substr(this.href.indexOf('#') + 1));
1238
+
1239
+ // find the element on the page
1240
+ try {
1241
+ element = $('#' + hash + ', a[name="' + hash + '"]');
1242
+ } catch (e) {
1243
+ // hash is not a valid jQuery identifier
1244
+ return;
1245
+ }
1246
+
1247
+ if (!element.length) {
1248
+ // this link does not point to an element on this page
1249
+ return;
1250
+ }
1251
+
1252
+ container = element.closest('.jspScrollable');
1253
+ jsp = container.data('jsp');
1254
+
1255
+ // jsp might be another jsp instance than the one, that bound this event
1256
+ // remember: this event is only bound once for all instances.
1257
+ jsp.scrollToElement(element, true);
1258
+
1259
+ if (container[0].scrollIntoView) {
1260
+ // also scroll to the top of the container (if it is not visible)
1261
+ scrollTop = $(window).scrollTop();
1262
+ elementTop = element.offset().top;
1263
+ if (elementTop < scrollTop || elementTop > scrollTop + $(window).height()) {
1264
+ container[0].scrollIntoView();
1265
+ }
1266
+ }
1267
+
1268
+ // jsp handled this event, prevent the browser default (scrolling :P)
1269
+ event.preventDefault();
1270
+ });
1472
1271
  }
1473
- }
1474
- );
1475
1272
 
1476
- initialise(s);
1477
- }
1273
+ // Init touch on iPad, iPhone, iPod, Android
1274
+ function initTouch() {
1275
+ var startX,
1276
+ startY,
1277
+ touchStartX,
1278
+ touchStartY,
1279
+ moved,
1280
+ moving = false;
1281
+
1282
+ container
1283
+ .off('touchstart.jsp touchmove.jsp touchend.jsp click.jsp-touchclick')
1284
+ .on('touchstart.jsp', function (e) {
1285
+ var touch = e.originalEvent.touches[0];
1286
+ startX = contentPositionX();
1287
+ startY = contentPositionY();
1288
+ touchStartX = touch.pageX;
1289
+ touchStartY = touch.pageY;
1290
+ moved = false;
1291
+ moving = true;
1292
+ })
1293
+ .on('touchmove.jsp', function (ev) {
1294
+ if (!moving) {
1295
+ return;
1296
+ }
1297
+
1298
+ var touchPos = ev.originalEvent.touches[0],
1299
+ dX = horizontalDragPosition,
1300
+ dY = verticalDragPosition;
1301
+
1302
+ jsp.scrollTo(startX + touchStartX - touchPos.pageX, startY + touchStartY - touchPos.pageY);
1303
+
1304
+ moved = moved || Math.abs(touchStartX - touchPos.pageX) > 5 || Math.abs(touchStartY - touchPos.pageY) > 5;
1305
+
1306
+ // return true if there was no movement so rest of screen can scroll
1307
+ return dX == horizontalDragPosition && dY == verticalDragPosition;
1308
+ })
1309
+ .on('touchend.jsp', function (e) {
1310
+ moving = false;
1311
+ /*if(moved) {
1312
+ return false;
1313
+ }*/
1314
+ })
1315
+ .on('click.jsp-touchclick', function (e) {
1316
+ if (moved) {
1317
+ moved = false;
1318
+ return false;
1319
+ }
1320
+ });
1321
+ }
1322
+
1323
+ function destroy() {
1324
+ var currentY = contentPositionY(),
1325
+ currentX = contentPositionX();
1326
+ elem.removeClass('jspScrollable').off('.jsp');
1327
+ pane.off('.jsp');
1328
+ elem.replaceWith(originalElement.append(pane.children()));
1329
+ originalElement.scrollTop(currentY);
1330
+ originalElement.scrollLeft(currentX);
1331
+
1332
+ // clear reinitialize timer if active
1333
+ if (reinitialiseInterval) {
1334
+ clearInterval(reinitialiseInterval);
1335
+ }
1336
+ }
1478
1337
 
1479
- // Pluginifying code...
1480
- settings = $.extend({}, $.fn.jScrollPane.defaults, settings);
1481
-
1482
- // Apply default speed
1483
- $.each(['arrowButtonSpeed', 'trackClickSpeed', 'keyboardSpeed'], function() {
1484
- settings[this] = settings[this] || settings.speed;
1485
- });
1486
-
1487
- return this.each(
1488
- function()
1489
- {
1490
- var elem = $(this), jspApi = elem.data('jsp');
1491
- if (jspApi) {
1492
- jspApi.reinitialise(settings);
1493
- } else {
1494
- $("script",elem).filter('[type="text/javascript"],:not([type])').remove();
1495
- jspApi = new JScrollPane(elem, settings);
1496
- elem.data('jsp', jspApi);
1497
- }
1338
+ // Public API
1339
+ $.extend(jsp, {
1340
+ // Reinitialises the scroll pane (if it's internal dimensions have changed since the last time it
1341
+ // was initialised). The settings object which is passed in will override any settings from the
1342
+ // previous time it was initialised - if you don't pass any settings then the ones from the previous
1343
+ // initialisation will be used.
1344
+ reinitialise: function (s) {
1345
+ s = $.extend({}, settings, s);
1346
+ initialise(s);
1347
+ },
1348
+ // Scrolls the specified element (a jQuery object, DOM node or jQuery selector string) into view so
1349
+ // that it can be seen within the viewport. If stickToTop is true then the element will appear at
1350
+ // the top of the viewport, if it is false then the viewport will scroll as little as possible to
1351
+ // show the element. You can also specify if you want animation to occur. If you don't provide this
1352
+ // argument then the animateScroll value from the settings object is used instead.
1353
+ scrollToElement: function (ele, stickToTop, animate) {
1354
+ scrollToElement(ele, stickToTop, animate);
1355
+ },
1356
+ // Scrolls the pane so that the specified co-ordinates within the content are at the top left
1357
+ // of the viewport. animate is optional and if not passed then the value of animateScroll from
1358
+ // the settings object this jScrollPane was initialised with is used.
1359
+ scrollTo: function (destX, destY, animate) {
1360
+ scrollToX(destX, animate);
1361
+ scrollToY(destY, animate);
1362
+ },
1363
+ // Scrolls the pane so that the specified co-ordinate within the content is at the left of the
1364
+ // viewport. animate is optional and if not passed then the value of animateScroll from the settings
1365
+ // object this jScrollPane was initialised with is used.
1366
+ scrollToX: function (destX, animate) {
1367
+ scrollToX(destX, animate);
1368
+ },
1369
+ // Scrolls the pane so that the specified co-ordinate within the content is at the top of the
1370
+ // viewport. animate is optional and if not passed then the value of animateScroll from the settings
1371
+ // object this jScrollPane was initialised with is used.
1372
+ scrollToY: function (destY, animate) {
1373
+ scrollToY(destY, animate);
1374
+ },
1375
+ // Scrolls the pane to the specified percentage of its maximum horizontal scroll position. animate
1376
+ // is optional and if not passed then the value of animateScroll from the settings object this
1377
+ // jScrollPane was initialised with is used.
1378
+ scrollToPercentX: function (destPercentX, animate) {
1379
+ scrollToX(destPercentX * (contentWidth - paneWidth), animate);
1380
+ },
1381
+ // Scrolls the pane to the specified percentage of its maximum vertical scroll position. animate
1382
+ // is optional and if not passed then the value of animateScroll from the settings object this
1383
+ // jScrollPane was initialised with is used.
1384
+ scrollToPercentY: function (destPercentY, animate) {
1385
+ scrollToY(destPercentY * (contentHeight - paneHeight), animate);
1386
+ },
1387
+ // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
1388
+ // the value of animateScroll from the settings object this jScrollPane was initialised with is used.
1389
+ scrollBy: function (deltaX, deltaY, animate) {
1390
+ jsp.scrollByX(deltaX, animate);
1391
+ jsp.scrollByY(deltaY, animate);
1392
+ },
1393
+ // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
1394
+ // the value of animateScroll from the settings object this jScrollPane was initialised with is used.
1395
+ scrollByX: function (deltaX, animate) {
1396
+ var destX = contentPositionX() + Math[deltaX < 0 ? 'floor' : 'ceil'](deltaX),
1397
+ percentScrolled = destX / (contentWidth - paneWidth);
1398
+ positionDragX(percentScrolled * dragMaxX, animate);
1399
+ },
1400
+ // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
1401
+ // the value of animateScroll from the settings object this jScrollPane was initialised with is used.
1402
+ scrollByY: function (deltaY, animate) {
1403
+ var destY = contentPositionY() + Math[deltaY < 0 ? 'floor' : 'ceil'](deltaY),
1404
+ percentScrolled = destY / (contentHeight - paneHeight);
1405
+ positionDragY(percentScrolled * dragMaxY, animate);
1406
+ },
1407
+ // Positions the horizontal drag at the specified x position (and updates the viewport to reflect
1408
+ // this). animate is optional and if not passed then the value of animateScroll from the settings
1409
+ // object this jScrollPane was initialised with is used.
1410
+ positionDragX: function (x, animate) {
1411
+ positionDragX(x, animate);
1412
+ },
1413
+ // Positions the vertical drag at the specified y position (and updates the viewport to reflect
1414
+ // this). animate is optional and if not passed then the value of animateScroll from the settings
1415
+ // object this jScrollPane was initialised with is used.
1416
+ positionDragY: function (y, animate) {
1417
+ positionDragY(y, animate);
1418
+ },
1419
+ // This method is called when jScrollPane is trying to animate to a new position. You can override
1420
+ // it if you want to provide advanced animation functionality. It is passed the following arguments:
1421
+ // * ele - the element whose position is being animated
1422
+ // * prop - the property that is being animated
1423
+ // * value - the value it's being animated to
1424
+ // * stepCallback - a function that you must execute each time you update the value of the property
1425
+ // * completeCallback - a function that will be executed after the animation had finished
1426
+ // You can use the default implementation (below) as a starting point for your own implementation.
1427
+ animate: function (ele, prop, value, stepCallback, completeCallback) {
1428
+ var params = {};
1429
+ params[prop] = value;
1430
+ ele.animate(params, {
1431
+ duration: settings.animateDuration,
1432
+ easing: settings.animateEase,
1433
+ queue: false,
1434
+ step: stepCallback,
1435
+ complete: completeCallback,
1436
+ });
1437
+ },
1438
+ // Returns the current x position of the viewport with regards to the content pane.
1439
+ getContentPositionX: function () {
1440
+ return contentPositionX();
1441
+ },
1442
+ // Returns the current y position of the viewport with regards to the content pane.
1443
+ getContentPositionY: function () {
1444
+ return contentPositionY();
1445
+ },
1446
+ // Returns the width of the content within the scroll pane.
1447
+ getContentWidth: function () {
1448
+ return contentWidth;
1449
+ },
1450
+ // Returns the height of the content within the scroll pane.
1451
+ getContentHeight: function () {
1452
+ return contentHeight;
1453
+ },
1454
+ // Returns the horizontal position of the viewport within the pane content.
1455
+ getPercentScrolledX: function () {
1456
+ return contentPositionX() / (contentWidth - paneWidth);
1457
+ },
1458
+ // Returns the vertical position of the viewport within the pane content.
1459
+ getPercentScrolledY: function () {
1460
+ return contentPositionY() / (contentHeight - paneHeight);
1461
+ },
1462
+ // Returns whether or not this scrollpane has a horizontal scrollbar.
1463
+ getIsScrollableH: function () {
1464
+ return isScrollableH;
1465
+ },
1466
+ // Returns whether or not this scrollpane has a vertical scrollbar.
1467
+ getIsScrollableV: function () {
1468
+ return isScrollableV;
1469
+ },
1470
+ // Gets a reference to the content pane. It is important that you use this method if you want to
1471
+ // edit the content of your jScrollPane as if you access the element directly then you may have some
1472
+ // problems (as your original element has had additional elements for the scrollbars etc added into
1473
+ // it).
1474
+ getContentPane: function () {
1475
+ return pane;
1476
+ },
1477
+ // Scrolls this jScrollPane down as far as it can currently scroll. If animate isn't passed then the
1478
+ // animateScroll value from settings is used instead.
1479
+ scrollToBottom: function (animate) {
1480
+ positionDragY(dragMaxY, animate);
1481
+ },
1482
+ // Hijacks the links on the page which link to content inside the scrollpane. If you have changed
1483
+ // the content of your page (e.g. via AJAX) and want to make sure any new anchor links to the
1484
+ // contents of your scroll pane will work then call this function.
1485
+ hijackInternalLinks: $.noop,
1486
+ // Removes the jScrollPane and returns the page to the state it was in before jScrollPane was
1487
+ // initialised.
1488
+ destroy: function () {
1489
+ destroy();
1490
+ },
1491
+ });
1492
+
1493
+ initialise(s);
1498
1494
  }
1499
- );
1500
- };
1501
-
1502
- $.fn.jScrollPane.defaults = {
1503
- showArrows : false,
1504
- maintainPosition : true,
1505
- stickToBottom : false,
1506
- stickToRight : false,
1507
- clickOnTrack : true,
1508
- autoReinitialise : false,
1509
- autoReinitialiseDelay : 500,
1510
- verticalDragMinHeight : 0,
1511
- verticalDragMaxHeight : 99999,
1512
- horizontalDragMinWidth : 0,
1513
- horizontalDragMaxWidth : 99999,
1514
- contentWidth : undefined,
1515
- animateScroll : false,
1516
- animateDuration : 300,
1517
- animateEase : 'linear',
1518
- hijackInternalLinks : false,
1519
- verticalGutter : 4,
1520
- horizontalGutter : 4,
1521
- mouseWheelSpeed : 3,
1522
- arrowButtonSpeed : 0,
1523
- arrowRepeatFreq : 50,
1524
- arrowScrollOnHover : false,
1525
- trackClickSpeed : 0,
1526
- trackClickRepeatFreq : 70,
1527
- verticalArrowPositions : 'split',
1528
- horizontalArrowPositions : 'split',
1529
- enableKeyboardNavigation : true,
1530
- hideFocus : false,
1531
- keyboardSpeed : 0,
1532
- initialDelay : 300, // Delay before starting repeating
1533
- speed : 30, // Default speed when others falsey
1534
- scrollPagePercent : 0.8, // Percent of visible area scrolled when pageUp/Down or track area pressed
1535
- alwaysShowVScroll : false,
1536
- alwaysShowHScroll : false,
1537
- };
1538
-
1539
- }));
1495
+
1496
+ // Pluginifying code...
1497
+ settings = $.extend({}, $.fn.jScrollPane.defaults, settings);
1498
+
1499
+ // Apply default speed
1500
+ $.each(['arrowButtonSpeed', 'trackClickSpeed', 'keyboardSpeed'], function () {
1501
+ settings[this] = settings[this] || settings.speed;
1502
+ });
1503
+
1504
+ return this.each(function () {
1505
+ var elem = $(this),
1506
+ jspApi = elem.data('jsp');
1507
+ if (jspApi) {
1508
+ jspApi.reinitialise(settings);
1509
+ } else {
1510
+ $('script', elem).filter('[type="text/javascript"],:not([type])').remove();
1511
+ jspApi = new JScrollPane(elem, settings);
1512
+ elem.data('jsp', jspApi);
1513
+ }
1514
+ });
1515
+ };
1516
+
1517
+ $.fn.jScrollPane.defaults = {
1518
+ showArrows: false,
1519
+ maintainPosition: true,
1520
+ stickToBottom: false,
1521
+ stickToRight: false,
1522
+ clickOnTrack: true,
1523
+ autoReinitialise: false,
1524
+ autoReinitialiseDelay: 500,
1525
+ verticalDragMinHeight: 0,
1526
+ verticalDragMaxHeight: 99999,
1527
+ horizontalDragMinWidth: 0,
1528
+ horizontalDragMaxWidth: 99999,
1529
+ contentWidth: undefined,
1530
+ animateScroll: false,
1531
+ animateDuration: 300,
1532
+ animateEase: 'linear',
1533
+ hijackInternalLinks: false,
1534
+ verticalGutter: 4,
1535
+ horizontalGutter: 4,
1536
+ mouseWheelSpeed: 3,
1537
+ arrowButtonSpeed: 0,
1538
+ arrowRepeatFreq: 50,
1539
+ arrowScrollOnHover: false,
1540
+ trackClickSpeed: 0,
1541
+ trackClickRepeatFreq: 70,
1542
+ verticalArrowPositions: 'split',
1543
+ horizontalArrowPositions: 'split',
1544
+ enableKeyboardNavigation: true,
1545
+ hideFocus: false,
1546
+ keyboardSpeed: 0,
1547
+ initialDelay: 300, // Delay before starting repeating
1548
+ speed: 30, // Default speed when others falsey
1549
+ scrollPagePercent: 0.8, // Percent of visible area scrolled when pageUp/Down or track area pressed
1550
+ alwaysShowVScroll: false,
1551
+ alwaysShowHScroll: false,
1552
+ resizeSensor: false,
1553
+ resizeSensorDelay: 0,
1554
+ };
1555
+ });