Subversion Repositories SmartDukaan

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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