perfect-scrollbar.js 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349
  1. /*!
  2. * perfect-scrollbar v1.5.0
  3. * Copyright 2020 Hyunje Jun, MDBootstrap and Contributors
  4. * Licensed under MIT
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  8. typeof define === 'function' && define.amd ? define(factory) :
  9. (global = global || self, global.PerfectScrollbar = factory());
  10. }(this, (function () { 'use strict';
  11. function get(element) {
  12. return getComputedStyle(element);
  13. }
  14. function set(element, obj) {
  15. for (var key in obj) {
  16. var val = obj[key];
  17. if (typeof val === 'number') {
  18. val = val + "px";
  19. }
  20. element.style[key] = val;
  21. }
  22. return element;
  23. }
  24. function div(className) {
  25. var div = document.createElement('div');
  26. div.className = className;
  27. return div;
  28. }
  29. var elMatches =
  30. typeof Element !== 'undefined' &&
  31. (Element.prototype.matches ||
  32. Element.prototype.webkitMatchesSelector ||
  33. Element.prototype.mozMatchesSelector ||
  34. Element.prototype.msMatchesSelector);
  35. function matches(element, query) {
  36. if (!elMatches) {
  37. throw new Error('No element matching method supported');
  38. }
  39. return elMatches.call(element, query);
  40. }
  41. function remove(element) {
  42. if (element.remove) {
  43. element.remove();
  44. } else {
  45. if (element.parentNode) {
  46. element.parentNode.removeChild(element);
  47. }
  48. }
  49. }
  50. function queryChildren(element, selector) {
  51. return Array.prototype.filter.call(element.children, function (child) { return matches(child, selector); }
  52. );
  53. }
  54. var cls = {
  55. main: 'ps',
  56. rtl: 'ps__rtl',
  57. element: {
  58. thumb: function (x) { return ("ps__thumb-" + x); },
  59. rail: function (x) { return ("ps__rail-" + x); },
  60. consuming: 'ps__child--consume',
  61. },
  62. state: {
  63. focus: 'ps--focus',
  64. clicking: 'ps--clicking',
  65. active: function (x) { return ("ps--active-" + x); },
  66. scrolling: function (x) { return ("ps--scrolling-" + x); },
  67. },
  68. };
  69. /*
  70. * Helper methods
  71. */
  72. var scrollingClassTimeout = { x: null, y: null };
  73. function addScrollingClass(i, x) {
  74. var classList = i.element.classList;
  75. var className = cls.state.scrolling(x);
  76. if (classList.contains(className)) {
  77. clearTimeout(scrollingClassTimeout[x]);
  78. } else {
  79. classList.add(className);
  80. }
  81. }
  82. function removeScrollingClass(i, x) {
  83. scrollingClassTimeout[x] = setTimeout(
  84. function () { return i.isAlive && i.element.classList.remove(cls.state.scrolling(x)); },
  85. i.settings.scrollingThreshold
  86. );
  87. }
  88. function setScrollingClassInstantly(i, x) {
  89. addScrollingClass(i, x);
  90. removeScrollingClass(i, x);
  91. }
  92. var EventElement = function EventElement(element) {
  93. this.element = element;
  94. this.handlers = {};
  95. };
  96. var prototypeAccessors = { isEmpty: { configurable: true } };
  97. EventElement.prototype.bind = function bind (eventName, handler) {
  98. if (typeof this.handlers[eventName] === 'undefined') {
  99. this.handlers[eventName] = [];
  100. }
  101. this.handlers[eventName].push(handler);
  102. this.element.addEventListener(eventName, handler, false);
  103. };
  104. EventElement.prototype.unbind = function unbind (eventName, target) {
  105. var this$1 = this;
  106. this.handlers[eventName] = this.handlers[eventName].filter(function (handler) {
  107. if (target && handler !== target) {
  108. return true;
  109. }
  110. this$1.element.removeEventListener(eventName, handler, false);
  111. return false;
  112. });
  113. };
  114. EventElement.prototype.unbindAll = function unbindAll () {
  115. for (var name in this.handlers) {
  116. this.unbind(name);
  117. }
  118. };
  119. prototypeAccessors.isEmpty.get = function () {
  120. var this$1 = this;
  121. return Object.keys(this.handlers).every(
  122. function (key) { return this$1.handlers[key].length === 0; }
  123. );
  124. };
  125. Object.defineProperties( EventElement.prototype, prototypeAccessors );
  126. var EventManager = function EventManager() {
  127. this.eventElements = [];
  128. };
  129. EventManager.prototype.eventElement = function eventElement (element) {
  130. var ee = this.eventElements.filter(function (ee) { return ee.element === element; })[0];
  131. if (!ee) {
  132. ee = new EventElement(element);
  133. this.eventElements.push(ee);
  134. }
  135. return ee;
  136. };
  137. EventManager.prototype.bind = function bind (element, eventName, handler) {
  138. this.eventElement(element).bind(eventName, handler);
  139. };
  140. EventManager.prototype.unbind = function unbind (element, eventName, handler) {
  141. var ee = this.eventElement(element);
  142. ee.unbind(eventName, handler);
  143. if (ee.isEmpty) {
  144. // remove
  145. this.eventElements.splice(this.eventElements.indexOf(ee), 1);
  146. }
  147. };
  148. EventManager.prototype.unbindAll = function unbindAll () {
  149. this.eventElements.forEach(function (e) { return e.unbindAll(); });
  150. this.eventElements = [];
  151. };
  152. EventManager.prototype.once = function once (element, eventName, handler) {
  153. var ee = this.eventElement(element);
  154. var onceHandler = function (evt) {
  155. ee.unbind(eventName, onceHandler);
  156. handler(evt);
  157. };
  158. ee.bind(eventName, onceHandler);
  159. };
  160. function createEvent(name) {
  161. if (typeof window.CustomEvent === 'function') {
  162. return new CustomEvent(name);
  163. } else {
  164. var evt = document.createEvent('CustomEvent');
  165. evt.initCustomEvent(name, false, false, undefined);
  166. return evt;
  167. }
  168. }
  169. function processScrollDiff(
  170. i,
  171. axis,
  172. diff,
  173. useScrollingClass,
  174. forceFireReachEvent
  175. ) {
  176. if ( useScrollingClass === void 0 ) useScrollingClass = true;
  177. if ( forceFireReachEvent === void 0 ) forceFireReachEvent = false;
  178. var fields;
  179. if (axis === 'top') {
  180. fields = [
  181. 'contentHeight',
  182. 'containerHeight',
  183. 'scrollTop',
  184. 'y',
  185. 'up',
  186. 'down' ];
  187. } else if (axis === 'left') {
  188. fields = [
  189. 'contentWidth',
  190. 'containerWidth',
  191. 'scrollLeft',
  192. 'x',
  193. 'left',
  194. 'right' ];
  195. } else {
  196. throw new Error('A proper axis should be provided');
  197. }
  198. processScrollDiff$1(i, diff, fields, useScrollingClass, forceFireReachEvent);
  199. }
  200. function processScrollDiff$1(
  201. i,
  202. diff,
  203. ref,
  204. useScrollingClass,
  205. forceFireReachEvent
  206. ) {
  207. var contentHeight = ref[0];
  208. var containerHeight = ref[1];
  209. var scrollTop = ref[2];
  210. var y = ref[3];
  211. var up = ref[4];
  212. var down = ref[5];
  213. if ( useScrollingClass === void 0 ) useScrollingClass = true;
  214. if ( forceFireReachEvent === void 0 ) forceFireReachEvent = false;
  215. var element = i.element;
  216. // reset reach
  217. i.reach[y] = null;
  218. // 1 for subpixel rounding
  219. if (element[scrollTop] < 1) {
  220. i.reach[y] = 'start';
  221. }
  222. // 1 for subpixel rounding
  223. if (element[scrollTop] > i[contentHeight] - i[containerHeight] - 1) {
  224. i.reach[y] = 'end';
  225. }
  226. if (diff) {
  227. element.dispatchEvent(createEvent(("ps-scroll-" + y)));
  228. if (diff < 0) {
  229. element.dispatchEvent(createEvent(("ps-scroll-" + up)));
  230. } else if (diff > 0) {
  231. element.dispatchEvent(createEvent(("ps-scroll-" + down)));
  232. }
  233. if (useScrollingClass) {
  234. setScrollingClassInstantly(i, y);
  235. }
  236. }
  237. if (i.reach[y] && (diff || forceFireReachEvent)) {
  238. element.dispatchEvent(createEvent(("ps-" + y + "-reach-" + (i.reach[y]))));
  239. }
  240. }
  241. function toInt(x) {
  242. return parseInt(x, 10) || 0;
  243. }
  244. function isEditable(el) {
  245. return (
  246. matches(el, 'input,[contenteditable]') ||
  247. matches(el, 'select,[contenteditable]') ||
  248. matches(el, 'textarea,[contenteditable]') ||
  249. matches(el, 'button,[contenteditable]')
  250. );
  251. }
  252. function outerWidth(element) {
  253. var styles = get(element);
  254. return (
  255. toInt(styles.width) +
  256. toInt(styles.paddingLeft) +
  257. toInt(styles.paddingRight) +
  258. toInt(styles.borderLeftWidth) +
  259. toInt(styles.borderRightWidth)
  260. );
  261. }
  262. var env = {
  263. isWebKit:
  264. typeof document !== 'undefined' &&
  265. 'WebkitAppearance' in document.documentElement.style,
  266. supportsTouch:
  267. typeof window !== 'undefined' &&
  268. ('ontouchstart' in window ||
  269. ('maxTouchPoints' in window.navigator &&
  270. window.navigator.maxTouchPoints > 0) ||
  271. (window.DocumentTouch && document instanceof window.DocumentTouch)),
  272. supportsIePointer:
  273. typeof navigator !== 'undefined' && navigator.msMaxTouchPoints,
  274. isChrome:
  275. typeof navigator !== 'undefined' &&
  276. /Chrome/i.test(navigator && navigator.userAgent),
  277. };
  278. function updateGeometry(i) {
  279. var element = i.element;
  280. var roundedScrollTop = Math.floor(element.scrollTop);
  281. var rect = element.getBoundingClientRect();
  282. i.containerWidth = Math.ceil(rect.width);
  283. i.containerHeight = Math.ceil(rect.height);
  284. i.contentWidth = element.scrollWidth;
  285. i.contentHeight = element.scrollHeight;
  286. if (!element.contains(i.scrollbarXRail)) {
  287. // clean up and append
  288. queryChildren(element, cls.element.rail('x')).forEach(function (el) { return remove(el); }
  289. );
  290. element.appendChild(i.scrollbarXRail);
  291. }
  292. if (!element.contains(i.scrollbarYRail)) {
  293. // clean up and append
  294. queryChildren(element, cls.element.rail('y')).forEach(function (el) { return remove(el); }
  295. );
  296. element.appendChild(i.scrollbarYRail);
  297. }
  298. if (
  299. !i.settings.suppressScrollX &&
  300. i.containerWidth + i.settings.scrollXMarginOffset < i.contentWidth
  301. ) {
  302. i.scrollbarXActive = true;
  303. i.railXWidth = i.containerWidth - i.railXMarginWidth;
  304. i.railXRatio = i.containerWidth / i.railXWidth;
  305. i.scrollbarXWidth = getThumbSize(
  306. i,
  307. toInt((i.railXWidth * i.containerWidth) / i.contentWidth)
  308. );
  309. i.scrollbarXLeft = toInt(
  310. ((i.negativeScrollAdjustment + element.scrollLeft) *
  311. (i.railXWidth - i.scrollbarXWidth)) /
  312. (i.contentWidth - i.containerWidth)
  313. );
  314. } else {
  315. i.scrollbarXActive = false;
  316. }
  317. if (
  318. !i.settings.suppressScrollY &&
  319. i.containerHeight + i.settings.scrollYMarginOffset < i.contentHeight
  320. ) {
  321. i.scrollbarYActive = true;
  322. i.railYHeight = i.containerHeight - i.railYMarginHeight;
  323. i.railYRatio = i.containerHeight / i.railYHeight;
  324. i.scrollbarYHeight = getThumbSize(
  325. i,
  326. toInt((i.railYHeight * i.containerHeight) / i.contentHeight)
  327. );
  328. i.scrollbarYTop = toInt(
  329. (roundedScrollTop * (i.railYHeight - i.scrollbarYHeight)) /
  330. (i.contentHeight - i.containerHeight)
  331. );
  332. } else {
  333. i.scrollbarYActive = false;
  334. }
  335. if (i.scrollbarXLeft >= i.railXWidth - i.scrollbarXWidth) {
  336. i.scrollbarXLeft = i.railXWidth - i.scrollbarXWidth;
  337. }
  338. if (i.scrollbarYTop >= i.railYHeight - i.scrollbarYHeight) {
  339. i.scrollbarYTop = i.railYHeight - i.scrollbarYHeight;
  340. }
  341. updateCss(element, i);
  342. if (i.scrollbarXActive) {
  343. element.classList.add(cls.state.active('x'));
  344. } else {
  345. element.classList.remove(cls.state.active('x'));
  346. i.scrollbarXWidth = 0;
  347. i.scrollbarXLeft = 0;
  348. element.scrollLeft = i.isRtl === true ? i.contentWidth : 0;
  349. }
  350. if (i.scrollbarYActive) {
  351. element.classList.add(cls.state.active('y'));
  352. } else {
  353. element.classList.remove(cls.state.active('y'));
  354. i.scrollbarYHeight = 0;
  355. i.scrollbarYTop = 0;
  356. element.scrollTop = 0;
  357. }
  358. }
  359. function getThumbSize(i, thumbSize) {
  360. if (i.settings.minScrollbarLength) {
  361. thumbSize = Math.max(thumbSize, i.settings.minScrollbarLength);
  362. }
  363. if (i.settings.maxScrollbarLength) {
  364. thumbSize = Math.min(thumbSize, i.settings.maxScrollbarLength);
  365. }
  366. return thumbSize;
  367. }
  368. function updateCss(element, i) {
  369. var xRailOffset = { width: i.railXWidth };
  370. var roundedScrollTop = Math.floor(element.scrollTop);
  371. if (i.isRtl) {
  372. xRailOffset.left =
  373. i.negativeScrollAdjustment +
  374. element.scrollLeft +
  375. i.containerWidth -
  376. i.contentWidth;
  377. } else {
  378. xRailOffset.left = element.scrollLeft;
  379. }
  380. if (i.isScrollbarXUsingBottom) {
  381. xRailOffset.bottom = i.scrollbarXBottom - roundedScrollTop;
  382. } else {
  383. xRailOffset.top = i.scrollbarXTop + roundedScrollTop;
  384. }
  385. set(i.scrollbarXRail, xRailOffset);
  386. var yRailOffset = { top: roundedScrollTop, height: i.railYHeight };
  387. if (i.isScrollbarYUsingRight) {
  388. if (i.isRtl) {
  389. yRailOffset.right =
  390. i.contentWidth -
  391. (i.negativeScrollAdjustment + element.scrollLeft) -
  392. i.scrollbarYRight -
  393. i.scrollbarYOuterWidth -
  394. 9;
  395. } else {
  396. yRailOffset.right = i.scrollbarYRight - element.scrollLeft;
  397. }
  398. } else {
  399. if (i.isRtl) {
  400. yRailOffset.left =
  401. i.negativeScrollAdjustment +
  402. element.scrollLeft +
  403. i.containerWidth * 2 -
  404. i.contentWidth -
  405. i.scrollbarYLeft -
  406. i.scrollbarYOuterWidth;
  407. } else {
  408. yRailOffset.left = i.scrollbarYLeft + element.scrollLeft;
  409. }
  410. }
  411. set(i.scrollbarYRail, yRailOffset);
  412. set(i.scrollbarX, {
  413. left: i.scrollbarXLeft,
  414. width: i.scrollbarXWidth - i.railBorderXWidth,
  415. });
  416. set(i.scrollbarY, {
  417. top: i.scrollbarYTop,
  418. height: i.scrollbarYHeight - i.railBorderYWidth,
  419. });
  420. }
  421. function clickRail(i) {
  422. var element = i.element;
  423. i.event.bind(i.scrollbarY, 'mousedown', function (e) { return e.stopPropagation(); });
  424. i.event.bind(i.scrollbarYRail, 'mousedown', function (e) {
  425. var positionTop =
  426. e.pageY -
  427. window.pageYOffset -
  428. i.scrollbarYRail.getBoundingClientRect().top;
  429. var direction = positionTop > i.scrollbarYTop ? 1 : -1;
  430. i.element.scrollTop += direction * i.containerHeight;
  431. updateGeometry(i);
  432. e.stopPropagation();
  433. });
  434. i.event.bind(i.scrollbarX, 'mousedown', function (e) { return e.stopPropagation(); });
  435. i.event.bind(i.scrollbarXRail, 'mousedown', function (e) {
  436. var positionLeft =
  437. e.pageX -
  438. window.pageXOffset -
  439. i.scrollbarXRail.getBoundingClientRect().left;
  440. var direction = positionLeft > i.scrollbarXLeft ? 1 : -1;
  441. i.element.scrollLeft += direction * i.containerWidth;
  442. updateGeometry(i);
  443. e.stopPropagation();
  444. });
  445. }
  446. function dragThumb(i) {
  447. bindMouseScrollHandler(i, [
  448. 'containerWidth',
  449. 'contentWidth',
  450. 'pageX',
  451. 'railXWidth',
  452. 'scrollbarX',
  453. 'scrollbarXWidth',
  454. 'scrollLeft',
  455. 'x',
  456. 'scrollbarXRail' ]);
  457. bindMouseScrollHandler(i, [
  458. 'containerHeight',
  459. 'contentHeight',
  460. 'pageY',
  461. 'railYHeight',
  462. 'scrollbarY',
  463. 'scrollbarYHeight',
  464. 'scrollTop',
  465. 'y',
  466. 'scrollbarYRail' ]);
  467. }
  468. function bindMouseScrollHandler(
  469. i,
  470. ref
  471. ) {
  472. var containerHeight = ref[0];
  473. var contentHeight = ref[1];
  474. var pageY = ref[2];
  475. var railYHeight = ref[3];
  476. var scrollbarY = ref[4];
  477. var scrollbarYHeight = ref[5];
  478. var scrollTop = ref[6];
  479. var y = ref[7];
  480. var scrollbarYRail = ref[8];
  481. var element = i.element;
  482. var startingScrollTop = null;
  483. var startingMousePageY = null;
  484. var scrollBy = null;
  485. function mouseMoveHandler(e) {
  486. if (e.touches && e.touches[0]) {
  487. e[pageY] = e.touches[0].pageY;
  488. }
  489. element[scrollTop] =
  490. startingScrollTop + scrollBy * (e[pageY] - startingMousePageY);
  491. addScrollingClass(i, y);
  492. updateGeometry(i);
  493. e.stopPropagation();
  494. e.preventDefault();
  495. }
  496. function mouseUpHandler() {
  497. removeScrollingClass(i, y);
  498. i[scrollbarYRail].classList.remove(cls.state.clicking);
  499. i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler);
  500. }
  501. function bindMoves(e, touchMode) {
  502. startingScrollTop = element[scrollTop];
  503. if (touchMode && e.touches) {
  504. e[pageY] = e.touches[0].pageY;
  505. }
  506. startingMousePageY = e[pageY];
  507. scrollBy =
  508. (i[contentHeight] - i[containerHeight]) /
  509. (i[railYHeight] - i[scrollbarYHeight]);
  510. if (!touchMode) {
  511. i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler);
  512. i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler);
  513. e.preventDefault();
  514. } else {
  515. i.event.bind(i.ownerDocument, 'touchmove', mouseMoveHandler);
  516. }
  517. i[scrollbarYRail].classList.add(cls.state.clicking);
  518. e.stopPropagation();
  519. }
  520. i.event.bind(i[scrollbarY], 'mousedown', function (e) {
  521. bindMoves(e);
  522. });
  523. i.event.bind(i[scrollbarY], 'touchstart', function (e) {
  524. bindMoves(e, true);
  525. });
  526. }
  527. function keyboard(i) {
  528. var element = i.element;
  529. var elementHovered = function () { return matches(element, ':hover'); };
  530. var scrollbarFocused = function () { return matches(i.scrollbarX, ':focus') || matches(i.scrollbarY, ':focus'); };
  531. function shouldPreventDefault(deltaX, deltaY) {
  532. var scrollTop = Math.floor(element.scrollTop);
  533. if (deltaX === 0) {
  534. if (!i.scrollbarYActive) {
  535. return false;
  536. }
  537. if (
  538. (scrollTop === 0 && deltaY > 0) ||
  539. (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)
  540. ) {
  541. return !i.settings.wheelPropagation;
  542. }
  543. }
  544. var scrollLeft = element.scrollLeft;
  545. if (deltaY === 0) {
  546. if (!i.scrollbarXActive) {
  547. return false;
  548. }
  549. if (
  550. (scrollLeft === 0 && deltaX < 0) ||
  551. (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)
  552. ) {
  553. return !i.settings.wheelPropagation;
  554. }
  555. }
  556. return true;
  557. }
  558. i.event.bind(i.ownerDocument, 'keydown', function (e) {
  559. if (
  560. (e.isDefaultPrevented && e.isDefaultPrevented()) ||
  561. e.defaultPrevented
  562. ) {
  563. return;
  564. }
  565. if (!elementHovered() && !scrollbarFocused()) {
  566. return;
  567. }
  568. var activeElement = document.activeElement
  569. ? document.activeElement
  570. : i.ownerDocument.activeElement;
  571. if (activeElement) {
  572. if (activeElement.tagName === 'IFRAME') {
  573. activeElement = activeElement.contentDocument.activeElement;
  574. } else {
  575. // go deeper if element is a webcomponent
  576. while (activeElement.shadowRoot) {
  577. activeElement = activeElement.shadowRoot.activeElement;
  578. }
  579. }
  580. if (isEditable(activeElement)) {
  581. return;
  582. }
  583. }
  584. var deltaX = 0;
  585. var deltaY = 0;
  586. switch (e.which) {
  587. case 37: // left
  588. if (e.metaKey) {
  589. deltaX = -i.contentWidth;
  590. } else if (e.altKey) {
  591. deltaX = -i.containerWidth;
  592. } else {
  593. deltaX = -30;
  594. }
  595. break;
  596. case 38: // up
  597. if (e.metaKey) {
  598. deltaY = i.contentHeight;
  599. } else if (e.altKey) {
  600. deltaY = i.containerHeight;
  601. } else {
  602. deltaY = 30;
  603. }
  604. break;
  605. case 39: // right
  606. if (e.metaKey) {
  607. deltaX = i.contentWidth;
  608. } else if (e.altKey) {
  609. deltaX = i.containerWidth;
  610. } else {
  611. deltaX = 30;
  612. }
  613. break;
  614. case 40: // down
  615. if (e.metaKey) {
  616. deltaY = -i.contentHeight;
  617. } else if (e.altKey) {
  618. deltaY = -i.containerHeight;
  619. } else {
  620. deltaY = -30;
  621. }
  622. break;
  623. case 32: // space bar
  624. if (e.shiftKey) {
  625. deltaY = i.containerHeight;
  626. } else {
  627. deltaY = -i.containerHeight;
  628. }
  629. break;
  630. case 33: // page up
  631. deltaY = i.containerHeight;
  632. break;
  633. case 34: // page down
  634. deltaY = -i.containerHeight;
  635. break;
  636. case 36: // home
  637. deltaY = i.contentHeight;
  638. break;
  639. case 35: // end
  640. deltaY = -i.contentHeight;
  641. break;
  642. default:
  643. return;
  644. }
  645. if (i.settings.suppressScrollX && deltaX !== 0) {
  646. return;
  647. }
  648. if (i.settings.suppressScrollY && deltaY !== 0) {
  649. return;
  650. }
  651. element.scrollTop -= deltaY;
  652. element.scrollLeft += deltaX;
  653. updateGeometry(i);
  654. if (shouldPreventDefault(deltaX, deltaY)) {
  655. e.preventDefault();
  656. }
  657. });
  658. }
  659. function wheel(i) {
  660. var element = i.element;
  661. function shouldPreventDefault(deltaX, deltaY) {
  662. var roundedScrollTop = Math.floor(element.scrollTop);
  663. var isTop = element.scrollTop === 0;
  664. var isBottom =
  665. roundedScrollTop + element.offsetHeight === element.scrollHeight;
  666. var isLeft = element.scrollLeft === 0;
  667. var isRight =
  668. element.scrollLeft + element.offsetWidth === element.scrollWidth;
  669. var hitsBound;
  670. // pick axis with primary direction
  671. if (Math.abs(deltaY) > Math.abs(deltaX)) {
  672. hitsBound = isTop || isBottom;
  673. } else {
  674. hitsBound = isLeft || isRight;
  675. }
  676. return hitsBound ? !i.settings.wheelPropagation : true;
  677. }
  678. function getDeltaFromEvent(e) {
  679. var deltaX = e.deltaX;
  680. var deltaY = -1 * e.deltaY;
  681. if (typeof deltaX === 'undefined' || typeof deltaY === 'undefined') {
  682. // OS X Safari
  683. deltaX = (-1 * e.wheelDeltaX) / 6;
  684. deltaY = e.wheelDeltaY / 6;
  685. }
  686. if (e.deltaMode && e.deltaMode === 1) {
  687. // Firefox in deltaMode 1: Line scrolling
  688. deltaX *= 10;
  689. deltaY *= 10;
  690. }
  691. if (deltaX !== deltaX && deltaY !== deltaY /* NaN checks */) {
  692. // IE in some mouse drivers
  693. deltaX = 0;
  694. deltaY = e.wheelDelta;
  695. }
  696. if (e.shiftKey) {
  697. // reverse axis with shift key
  698. return [-deltaY, -deltaX];
  699. }
  700. return [deltaX, deltaY];
  701. }
  702. function shouldBeConsumedByChild(target, deltaX, deltaY) {
  703. // FIXME: this is a workaround for <select> issue in FF and IE #571
  704. if (!env.isWebKit && element.querySelector('select:focus')) {
  705. return true;
  706. }
  707. if (!element.contains(target)) {
  708. return false;
  709. }
  710. var cursor = target;
  711. while (cursor && cursor !== element) {
  712. if (cursor.classList.contains(cls.element.consuming)) {
  713. return true;
  714. }
  715. var style = get(cursor);
  716. // if deltaY && vertical scrollable
  717. if (deltaY && style.overflowY.match(/(scroll|auto)/)) {
  718. var maxScrollTop = cursor.scrollHeight - cursor.clientHeight;
  719. if (maxScrollTop > 0) {
  720. if (
  721. (cursor.scrollTop > 0 && deltaY < 0) ||
  722. (cursor.scrollTop < maxScrollTop && deltaY > 0)
  723. ) {
  724. return true;
  725. }
  726. }
  727. }
  728. // if deltaX && horizontal scrollable
  729. if (deltaX && style.overflowX.match(/(scroll|auto)/)) {
  730. var maxScrollLeft = cursor.scrollWidth - cursor.clientWidth;
  731. if (maxScrollLeft > 0) {
  732. if (
  733. (cursor.scrollLeft > 0 && deltaX < 0) ||
  734. (cursor.scrollLeft < maxScrollLeft && deltaX > 0)
  735. ) {
  736. return true;
  737. }
  738. }
  739. }
  740. cursor = cursor.parentNode;
  741. }
  742. return false;
  743. }
  744. function mousewheelHandler(e) {
  745. var ref = getDeltaFromEvent(e);
  746. var deltaX = ref[0];
  747. var deltaY = ref[1];
  748. if (shouldBeConsumedByChild(e.target, deltaX, deltaY)) {
  749. return;
  750. }
  751. var shouldPrevent = false;
  752. if (!i.settings.useBothWheelAxes) {
  753. // deltaX will only be used for horizontal scrolling and deltaY will
  754. // only be used for vertical scrolling - this is the default
  755. element.scrollTop -= deltaY * i.settings.wheelSpeed;
  756. element.scrollLeft += deltaX * i.settings.wheelSpeed;
  757. } else if (i.scrollbarYActive && !i.scrollbarXActive) {
  758. // only vertical scrollbar is active and useBothWheelAxes option is
  759. // active, so let's scroll vertical bar using both mouse wheel axes
  760. if (deltaY) {
  761. element.scrollTop -= deltaY * i.settings.wheelSpeed;
  762. } else {
  763. element.scrollTop += deltaX * i.settings.wheelSpeed;
  764. }
  765. shouldPrevent = true;
  766. } else if (i.scrollbarXActive && !i.scrollbarYActive) {
  767. // useBothWheelAxes and only horizontal bar is active, so use both
  768. // wheel axes for horizontal bar
  769. if (deltaX) {
  770. element.scrollLeft += deltaX * i.settings.wheelSpeed;
  771. } else {
  772. element.scrollLeft -= deltaY * i.settings.wheelSpeed;
  773. }
  774. shouldPrevent = true;
  775. }
  776. updateGeometry(i);
  777. shouldPrevent = shouldPrevent || shouldPreventDefault(deltaX, deltaY);
  778. if (shouldPrevent && !e.ctrlKey) {
  779. e.stopPropagation();
  780. e.preventDefault();
  781. }
  782. }
  783. if (typeof window.onwheel !== 'undefined') {
  784. i.event.bind(element, 'wheel', mousewheelHandler);
  785. } else if (typeof window.onmousewheel !== 'undefined') {
  786. i.event.bind(element, 'mousewheel', mousewheelHandler);
  787. }
  788. }
  789. function touch(i) {
  790. if (!env.supportsTouch && !env.supportsIePointer) {
  791. return;
  792. }
  793. var element = i.element;
  794. function shouldPrevent(deltaX, deltaY) {
  795. var scrollTop = Math.floor(element.scrollTop);
  796. var scrollLeft = element.scrollLeft;
  797. var magnitudeX = Math.abs(deltaX);
  798. var magnitudeY = Math.abs(deltaY);
  799. if (magnitudeY > magnitudeX) {
  800. // user is perhaps trying to swipe up/down the page
  801. if (
  802. (deltaY < 0 && scrollTop === i.contentHeight - i.containerHeight) ||
  803. (deltaY > 0 && scrollTop === 0)
  804. ) {
  805. // set prevent for mobile Chrome refresh
  806. return window.scrollY === 0 && deltaY > 0 && env.isChrome;
  807. }
  808. } else if (magnitudeX > magnitudeY) {
  809. // user is perhaps trying to swipe left/right across the page
  810. if (
  811. (deltaX < 0 && scrollLeft === i.contentWidth - i.containerWidth) ||
  812. (deltaX > 0 && scrollLeft === 0)
  813. ) {
  814. return true;
  815. }
  816. }
  817. return true;
  818. }
  819. function applyTouchMove(differenceX, differenceY) {
  820. element.scrollTop -= differenceY;
  821. element.scrollLeft -= differenceX;
  822. updateGeometry(i);
  823. }
  824. var startOffset = {};
  825. var startTime = 0;
  826. var speed = {};
  827. var easingLoop = null;
  828. function getTouch(e) {
  829. if (e.targetTouches) {
  830. return e.targetTouches[0];
  831. } else {
  832. // Maybe IE pointer
  833. return e;
  834. }
  835. }
  836. function shouldHandle(e) {
  837. if (e.pointerType && e.pointerType === 'pen' && e.buttons === 0) {
  838. return false;
  839. }
  840. if (e.targetTouches && e.targetTouches.length === 1) {
  841. return true;
  842. }
  843. if (
  844. e.pointerType &&
  845. e.pointerType !== 'mouse' &&
  846. e.pointerType !== e.MSPOINTER_TYPE_MOUSE
  847. ) {
  848. return true;
  849. }
  850. return false;
  851. }
  852. function touchStart(e) {
  853. if (!shouldHandle(e)) {
  854. return;
  855. }
  856. var touch = getTouch(e);
  857. startOffset.pageX = touch.pageX;
  858. startOffset.pageY = touch.pageY;
  859. startTime = new Date().getTime();
  860. if (easingLoop !== null) {
  861. clearInterval(easingLoop);
  862. }
  863. }
  864. function shouldBeConsumedByChild(target, deltaX, deltaY) {
  865. if (!element.contains(target)) {
  866. return false;
  867. }
  868. var cursor = target;
  869. while (cursor && cursor !== element) {
  870. if (cursor.classList.contains(cls.element.consuming)) {
  871. return true;
  872. }
  873. var style = get(cursor);
  874. // if deltaY && vertical scrollable
  875. if (deltaY && style.overflowY.match(/(scroll|auto)/)) {
  876. var maxScrollTop = cursor.scrollHeight - cursor.clientHeight;
  877. if (maxScrollTop > 0) {
  878. if (
  879. (cursor.scrollTop > 0 && deltaY < 0) ||
  880. (cursor.scrollTop < maxScrollTop && deltaY > 0)
  881. ) {
  882. return true;
  883. }
  884. }
  885. }
  886. // if deltaX && horizontal scrollable
  887. if (deltaX && style.overflowX.match(/(scroll|auto)/)) {
  888. var maxScrollLeft = cursor.scrollWidth - cursor.clientWidth;
  889. if (maxScrollLeft > 0) {
  890. if (
  891. (cursor.scrollLeft > 0 && deltaX < 0) ||
  892. (cursor.scrollLeft < maxScrollLeft && deltaX > 0)
  893. ) {
  894. return true;
  895. }
  896. }
  897. }
  898. cursor = cursor.parentNode;
  899. }
  900. return false;
  901. }
  902. function touchMove(e) {
  903. if (shouldHandle(e)) {
  904. var touch = getTouch(e);
  905. var currentOffset = { pageX: touch.pageX, pageY: touch.pageY };
  906. var differenceX = currentOffset.pageX - startOffset.pageX;
  907. var differenceY = currentOffset.pageY - startOffset.pageY;
  908. if (shouldBeConsumedByChild(e.target, differenceX, differenceY)) {
  909. return;
  910. }
  911. applyTouchMove(differenceX, differenceY);
  912. startOffset = currentOffset;
  913. var currentTime = new Date().getTime();
  914. var timeGap = currentTime - startTime;
  915. if (timeGap > 0) {
  916. speed.x = differenceX / timeGap;
  917. speed.y = differenceY / timeGap;
  918. startTime = currentTime;
  919. }
  920. if (shouldPrevent(differenceX, differenceY)) {
  921. e.preventDefault();
  922. }
  923. }
  924. }
  925. function touchEnd() {
  926. if (i.settings.swipeEasing) {
  927. clearInterval(easingLoop);
  928. easingLoop = setInterval(function() {
  929. if (i.isInitialized) {
  930. clearInterval(easingLoop);
  931. return;
  932. }
  933. if (!speed.x && !speed.y) {
  934. clearInterval(easingLoop);
  935. return;
  936. }
  937. if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) {
  938. clearInterval(easingLoop);
  939. return;
  940. }
  941. applyTouchMove(speed.x * 30, speed.y * 30);
  942. speed.x *= 0.8;
  943. speed.y *= 0.8;
  944. }, 10);
  945. }
  946. }
  947. if (env.supportsTouch) {
  948. i.event.bind(element, 'touchstart', touchStart);
  949. i.event.bind(element, 'touchmove', touchMove);
  950. i.event.bind(element, 'touchend', touchEnd);
  951. } else if (env.supportsIePointer) {
  952. if (window.PointerEvent) {
  953. i.event.bind(element, 'pointerdown', touchStart);
  954. i.event.bind(element, 'pointermove', touchMove);
  955. i.event.bind(element, 'pointerup', touchEnd);
  956. } else if (window.MSPointerEvent) {
  957. i.event.bind(element, 'MSPointerDown', touchStart);
  958. i.event.bind(element, 'MSPointerMove', touchMove);
  959. i.event.bind(element, 'MSPointerUp', touchEnd);
  960. }
  961. }
  962. }
  963. var defaultSettings = function () { return ({
  964. handlers: ['click-rail', 'drag-thumb', 'keyboard', 'wheel', 'touch'],
  965. maxScrollbarLength: null,
  966. minScrollbarLength: null,
  967. scrollingThreshold: 1000,
  968. scrollXMarginOffset: 0,
  969. scrollYMarginOffset: 0,
  970. suppressScrollX: false,
  971. suppressScrollY: false,
  972. swipeEasing: true,
  973. useBothWheelAxes: false,
  974. wheelPropagation: true,
  975. wheelSpeed: 1,
  976. }); };
  977. var handlers = {
  978. 'click-rail': clickRail,
  979. 'drag-thumb': dragThumb,
  980. keyboard: keyboard,
  981. wheel: wheel,
  982. touch: touch,
  983. };
  984. var PerfectScrollbar = function PerfectScrollbar(element, userSettings) {
  985. var this$1 = this;
  986. if ( userSettings === void 0 ) userSettings = {};
  987. if (typeof element === 'string') {
  988. element = document.querySelector(element);
  989. }
  990. if (!element || !element.nodeName) {
  991. throw new Error('no element is specified to initialize PerfectScrollbar');
  992. }
  993. this.element = element;
  994. element.classList.add(cls.main);
  995. this.settings = defaultSettings();
  996. for (var key in userSettings) {
  997. this.settings[key] = userSettings[key];
  998. }
  999. this.containerWidth = null;
  1000. this.containerHeight = null;
  1001. this.contentWidth = null;
  1002. this.contentHeight = null;
  1003. var focus = function () { return element.classList.add(cls.state.focus); };
  1004. var blur = function () { return element.classList.remove(cls.state.focus); };
  1005. this.isRtl = get(element).direction === 'rtl';
  1006. if (this.isRtl === true) {
  1007. element.classList.add(cls.rtl);
  1008. }
  1009. this.isNegativeScroll = (function () {
  1010. var originalScrollLeft = element.scrollLeft;
  1011. var result = null;
  1012. element.scrollLeft = -1;
  1013. result = element.scrollLeft < 0;
  1014. element.scrollLeft = originalScrollLeft;
  1015. return result;
  1016. })();
  1017. this.negativeScrollAdjustment = this.isNegativeScroll
  1018. ? element.scrollWidth - element.clientWidth
  1019. : 0;
  1020. this.event = new EventManager();
  1021. this.ownerDocument = element.ownerDocument || document;
  1022. this.scrollbarXRail = div(cls.element.rail('x'));
  1023. element.appendChild(this.scrollbarXRail);
  1024. this.scrollbarX = div(cls.element.thumb('x'));
  1025. this.scrollbarXRail.appendChild(this.scrollbarX);
  1026. this.scrollbarX.setAttribute('tabindex', 0);
  1027. this.event.bind(this.scrollbarX, 'focus', focus);
  1028. this.event.bind(this.scrollbarX, 'blur', blur);
  1029. this.scrollbarXActive = null;
  1030. this.scrollbarXWidth = null;
  1031. this.scrollbarXLeft = null;
  1032. var railXStyle = get(this.scrollbarXRail);
  1033. this.scrollbarXBottom = parseInt(railXStyle.bottom, 10);
  1034. if (isNaN(this.scrollbarXBottom)) {
  1035. this.isScrollbarXUsingBottom = false;
  1036. this.scrollbarXTop = toInt(railXStyle.top);
  1037. } else {
  1038. this.isScrollbarXUsingBottom = true;
  1039. }
  1040. this.railBorderXWidth =
  1041. toInt(railXStyle.borderLeftWidth) + toInt(railXStyle.borderRightWidth);
  1042. // Set rail to display:block to calculate margins
  1043. set(this.scrollbarXRail, { display: 'block' });
  1044. this.railXMarginWidth =
  1045. toInt(railXStyle.marginLeft) + toInt(railXStyle.marginRight);
  1046. set(this.scrollbarXRail, { display: '' });
  1047. this.railXWidth = null;
  1048. this.railXRatio = null;
  1049. this.scrollbarYRail = div(cls.element.rail('y'));
  1050. element.appendChild(this.scrollbarYRail);
  1051. this.scrollbarY = div(cls.element.thumb('y'));
  1052. this.scrollbarYRail.appendChild(this.scrollbarY);
  1053. this.scrollbarY.setAttribute('tabindex', 0);
  1054. this.event.bind(this.scrollbarY, 'focus', focus);
  1055. this.event.bind(this.scrollbarY, 'blur', blur);
  1056. this.scrollbarYActive = null;
  1057. this.scrollbarYHeight = null;
  1058. this.scrollbarYTop = null;
  1059. var railYStyle = get(this.scrollbarYRail);
  1060. this.scrollbarYRight = parseInt(railYStyle.right, 10);
  1061. if (isNaN(this.scrollbarYRight)) {
  1062. this.isScrollbarYUsingRight = false;
  1063. this.scrollbarYLeft = toInt(railYStyle.left);
  1064. } else {
  1065. this.isScrollbarYUsingRight = true;
  1066. }
  1067. this.scrollbarYOuterWidth = this.isRtl ? outerWidth(this.scrollbarY) : null;
  1068. this.railBorderYWidth =
  1069. toInt(railYStyle.borderTopWidth) + toInt(railYStyle.borderBottomWidth);
  1070. set(this.scrollbarYRail, { display: 'block' });
  1071. this.railYMarginHeight =
  1072. toInt(railYStyle.marginTop) + toInt(railYStyle.marginBottom);
  1073. set(this.scrollbarYRail, { display: '' });
  1074. this.railYHeight = null;
  1075. this.railYRatio = null;
  1076. this.reach = {
  1077. x:
  1078. element.scrollLeft <= 0
  1079. ? 'start'
  1080. : element.scrollLeft >= this.contentWidth - this.containerWidth
  1081. ? 'end'
  1082. : null,
  1083. y:
  1084. element.scrollTop <= 0
  1085. ? 'start'
  1086. : element.scrollTop >= this.contentHeight - this.containerHeight
  1087. ? 'end'
  1088. : null,
  1089. };
  1090. this.isAlive = true;
  1091. this.settings.handlers.forEach(function (handlerName) { return handlers[handlerName](this$1); });
  1092. this.lastScrollTop = Math.floor(element.scrollTop); // for onScroll only
  1093. this.lastScrollLeft = element.scrollLeft; // for onScroll only
  1094. this.event.bind(this.element, 'scroll', function (e) { return this$1.onScroll(e); });
  1095. updateGeometry(this);
  1096. };
  1097. PerfectScrollbar.prototype.update = function update () {
  1098. if (!this.isAlive) {
  1099. return;
  1100. }
  1101. // Recalcuate negative scrollLeft adjustment
  1102. this.negativeScrollAdjustment = this.isNegativeScroll
  1103. ? this.element.scrollWidth - this.element.clientWidth
  1104. : 0;
  1105. // Recalculate rail margins
  1106. set(this.scrollbarXRail, { display: 'block' });
  1107. set(this.scrollbarYRail, { display: 'block' });
  1108. this.railXMarginWidth =
  1109. toInt(get(this.scrollbarXRail).marginLeft) +
  1110. toInt(get(this.scrollbarXRail).marginRight);
  1111. this.railYMarginHeight =
  1112. toInt(get(this.scrollbarYRail).marginTop) +
  1113. toInt(get(this.scrollbarYRail).marginBottom);
  1114. // Hide scrollbars not to affect scrollWidth and scrollHeight
  1115. set(this.scrollbarXRail, { display: 'none' });
  1116. set(this.scrollbarYRail, { display: 'none' });
  1117. updateGeometry(this);
  1118. processScrollDiff(this, 'top', 0, false, true);
  1119. processScrollDiff(this, 'left', 0, false, true);
  1120. set(this.scrollbarXRail, { display: '' });
  1121. set(this.scrollbarYRail, { display: '' });
  1122. };
  1123. PerfectScrollbar.prototype.onScroll = function onScroll (e) {
  1124. if (!this.isAlive) {
  1125. return;
  1126. }
  1127. updateGeometry(this);
  1128. processScrollDiff(this, 'top', this.element.scrollTop - this.lastScrollTop);
  1129. processScrollDiff(
  1130. this,
  1131. 'left',
  1132. this.element.scrollLeft - this.lastScrollLeft
  1133. );
  1134. this.lastScrollTop = Math.floor(this.element.scrollTop);
  1135. this.lastScrollLeft = this.element.scrollLeft;
  1136. };
  1137. PerfectScrollbar.prototype.destroy = function destroy () {
  1138. if (!this.isAlive) {
  1139. return;
  1140. }
  1141. this.event.unbindAll();
  1142. remove(this.scrollbarX);
  1143. remove(this.scrollbarY);
  1144. remove(this.scrollbarXRail);
  1145. remove(this.scrollbarYRail);
  1146. this.removePsClasses();
  1147. // unset elements
  1148. this.element = null;
  1149. this.scrollbarX = null;
  1150. this.scrollbarY = null;
  1151. this.scrollbarXRail = null;
  1152. this.scrollbarYRail = null;
  1153. this.isAlive = false;
  1154. };
  1155. PerfectScrollbar.prototype.removePsClasses = function removePsClasses () {
  1156. this.element.className = this.element.className
  1157. .split(' ')
  1158. .filter(function (name) { return !name.match(/^ps([-_].+|)$/); })
  1159. .join(' ');
  1160. };
  1161. return PerfectScrollbar;
  1162. })));
  1163. //# sourceMappingURL=perfect-scrollbar.js.map