jscrollpane-rails 2.0.23 → 2.1.1

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