jquery.jscrollpane.js 45 KB

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