You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

jquery.jscrollpane.js 50 kB

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