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