perfect-scrollbar.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356
  1. /*!
  2. * perfect-scrollbar v1.5.3
  3. * Copyright 2021 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, {passive: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, {passive: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.round(rect.width);
  283. i.containerHeight = Math.round(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. if (e.type.startsWith('touch') && e.changedTouches.length > 1) {
  495. e.preventDefault();
  496. }
  497. }
  498. function mouseUpHandler() {
  499. removeScrollingClass(i, y);
  500. i[scrollbarYRail].classList.remove(cls.state.clicking);
  501. i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler);
  502. }
  503. function bindMoves(e, touchMode) {
  504. startingScrollTop = element[scrollTop];
  505. if (touchMode && e.touches) {
  506. e[pageY] = e.touches[0].pageY;
  507. }
  508. startingMousePageY = e[pageY];
  509. scrollBy =
  510. (i[contentHeight] - i[containerHeight]) /
  511. (i[railYHeight] - i[scrollbarYHeight]);
  512. if (!touchMode) {
  513. i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler);
  514. i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler);
  515. e.preventDefault();
  516. } else {
  517. i.event.bind(i.ownerDocument, 'touchmove', mouseMoveHandler);
  518. }
  519. i[scrollbarYRail].classList.add(cls.state.clicking);
  520. e.stopPropagation();
  521. }
  522. i.event.bind(i[scrollbarY], 'mousedown', function (e) {
  523. bindMoves(e);
  524. });
  525. i.event.bind(i[scrollbarY], 'touchstart', function (e) {
  526. bindMoves(e, true);
  527. });
  528. }
  529. function keyboard(i) {
  530. var element = i.element;
  531. var elementHovered = function () { return matches(element, ':hover'); };
  532. var scrollbarFocused = function () { return matches(i.scrollbarX, ':focus') || matches(i.scrollbarY, ':focus'); };
  533. function shouldPreventDefault(deltaX, deltaY) {
  534. var scrollTop = Math.floor(element.scrollTop);
  535. if (deltaX === 0) {
  536. if (!i.scrollbarYActive) {
  537. return false;
  538. }
  539. if (
  540. (scrollTop === 0 && deltaY > 0) ||
  541. (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)
  542. ) {
  543. return !i.settings.wheelPropagation;
  544. }
  545. }
  546. var scrollLeft = element.scrollLeft;
  547. if (deltaY === 0) {
  548. if (!i.scrollbarXActive) {
  549. return false;
  550. }
  551. if (
  552. (scrollLeft === 0 && deltaX < 0) ||
  553. (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)
  554. ) {
  555. return !i.settings.wheelPropagation;
  556. }
  557. }
  558. return true;
  559. }
  560. i.event.bind(i.ownerDocument, 'keydown', function (e) {
  561. if (
  562. (e.isDefaultPrevented && e.isDefaultPrevented()) ||
  563. e.defaultPrevented
  564. ) {
  565. return;
  566. }
  567. if (!elementHovered() && !scrollbarFocused()) {
  568. return;
  569. }
  570. var activeElement = document.activeElement
  571. ? document.activeElement
  572. : i.ownerDocument.activeElement;
  573. if (activeElement) {
  574. if (activeElement.tagName === 'IFRAME') {
  575. activeElement = activeElement.contentDocument.activeElement;
  576. } else {
  577. // go deeper if element is a webcomponent
  578. while (activeElement.shadowRoot) {
  579. activeElement = activeElement.shadowRoot.activeElement;
  580. }
  581. }
  582. if (isEditable(activeElement)) {
  583. return;
  584. }
  585. }
  586. var deltaX = 0;
  587. var deltaY = 0;
  588. switch (e.which) {
  589. case 37: // left
  590. if (e.metaKey) {
  591. deltaX = -i.contentWidth;
  592. } else if (e.altKey) {
  593. deltaX = -i.containerWidth;
  594. } else {
  595. deltaX = -30;
  596. }
  597. break;
  598. case 38: // up
  599. if (e.metaKey) {
  600. deltaY = i.contentHeight;
  601. } else if (e.altKey) {
  602. deltaY = i.containerHeight;
  603. } else {
  604. deltaY = 30;
  605. }
  606. break;
  607. case 39: // right
  608. if (e.metaKey) {
  609. deltaX = i.contentWidth;
  610. } else if (e.altKey) {
  611. deltaX = i.containerWidth;
  612. } else {
  613. deltaX = 30;
  614. }
  615. break;
  616. case 40: // down
  617. if (e.metaKey) {
  618. deltaY = -i.contentHeight;
  619. } else if (e.altKey) {
  620. deltaY = -i.containerHeight;
  621. } else {
  622. deltaY = -30;
  623. }
  624. break;
  625. case 32: // space bar
  626. if (e.shiftKey) {
  627. deltaY = i.containerHeight;
  628. } else {
  629. deltaY = -i.containerHeight;
  630. }
  631. break;
  632. case 33: // page up
  633. deltaY = i.containerHeight;
  634. break;
  635. case 34: // page down
  636. deltaY = -i.containerHeight;
  637. break;
  638. case 36: // home
  639. deltaY = i.contentHeight;
  640. break;
  641. case 35: // end
  642. deltaY = -i.contentHeight;
  643. break;
  644. default:
  645. return;
  646. }
  647. if (i.settings.suppressScrollX && deltaX !== 0) {
  648. return;
  649. }
  650. if (i.settings.suppressScrollY && deltaY !== 0) {
  651. return;
  652. }
  653. element.scrollTop -= deltaY;
  654. element.scrollLeft += deltaX;
  655. updateGeometry(i);
  656. if (shouldPreventDefault(deltaX, deltaY)) {
  657. e.preventDefault();
  658. }
  659. });
  660. }
  661. function wheel(i) {
  662. var element = i.element;
  663. function shouldPreventDefault(deltaX, deltaY) {
  664. var roundedScrollTop = Math.floor(element.scrollTop);
  665. var isTop = element.scrollTop === 0;
  666. var isBottom =
  667. roundedScrollTop + element.offsetHeight === element.scrollHeight;
  668. var isLeft = element.scrollLeft === 0;
  669. var isRight =
  670. element.scrollLeft + element.offsetWidth === element.scrollWidth;
  671. var hitsBound;
  672. // pick axis with primary direction
  673. if (Math.abs(deltaY) > Math.abs(deltaX)) {
  674. hitsBound = isTop || isBottom;
  675. } else {
  676. hitsBound = isLeft || isRight;
  677. }
  678. return hitsBound ? !i.settings.wheelPropagation : true;
  679. }
  680. function getDeltaFromEvent(e) {
  681. var deltaX = e.deltaX;
  682. var deltaY = -1 * e.deltaY;
  683. if (typeof deltaX === 'undefined' || typeof deltaY === 'undefined') {
  684. // OS X Safari
  685. deltaX = (-1 * e.wheelDeltaX) / 6;
  686. deltaY = e.wheelDeltaY / 6;
  687. }
  688. if (e.deltaMode && e.deltaMode === 1) {
  689. // Firefox in deltaMode 1: Line scrolling
  690. deltaX *= 10;
  691. deltaY *= 10;
  692. }
  693. if (deltaX !== deltaX && deltaY !== deltaY /* NaN checks */) {
  694. // IE in some mouse drivers
  695. deltaX = 0;
  696. deltaY = e.wheelDelta;
  697. }
  698. if (e.shiftKey) {
  699. // reverse axis with shift key
  700. return [-deltaY, -deltaX];
  701. }
  702. return [deltaX, deltaY];
  703. }
  704. function shouldBeConsumedByChild(target, deltaX, deltaY) {
  705. // FIXME: this is a workaround for <select> issue in FF and IE #571
  706. if (!env.isWebKit && element.querySelector('select:focus')) {
  707. return true;
  708. }
  709. if (!element.contains(target)) {
  710. return false;
  711. }
  712. var cursor = target;
  713. while (cursor && cursor !== element) {
  714. if (cursor.classList.contains(cls.element.consuming)) {
  715. return true;
  716. }
  717. var style = get(cursor);
  718. // if deltaY && vertical scrollable
  719. if (deltaY && style.overflowY.match(/(scroll|auto)/)) {
  720. var maxScrollTop = cursor.scrollHeight - cursor.clientHeight;
  721. if (maxScrollTop > 0) {
  722. if (
  723. (cursor.scrollTop > 0 && deltaY < 0) ||
  724. (cursor.scrollTop < maxScrollTop && deltaY > 0)
  725. ) {
  726. return true;
  727. }
  728. }
  729. }
  730. // if deltaX && horizontal scrollable
  731. if (deltaX && style.overflowX.match(/(scroll|auto)/)) {
  732. var maxScrollLeft = cursor.scrollWidth - cursor.clientWidth;
  733. if (maxScrollLeft > 0) {
  734. if (
  735. (cursor.scrollLeft > 0 && deltaX < 0) ||
  736. (cursor.scrollLeft < maxScrollLeft && deltaX > 0)
  737. ) {
  738. return true;
  739. }
  740. }
  741. }
  742. cursor = cursor.parentNode;
  743. }
  744. return false;
  745. }
  746. function mousewheelHandler(e) {
  747. var ref = getDeltaFromEvent(e);
  748. var deltaX = ref[0];
  749. var deltaY = ref[1];
  750. if (shouldBeConsumedByChild(e.target, deltaX, deltaY)) {
  751. return;
  752. }
  753. var shouldPrevent = false;
  754. if (!i.settings.useBothWheelAxes) {
  755. // deltaX will only be used for horizontal scrolling and deltaY will
  756. // only be used for vertical scrolling - this is the default
  757. element.scrollTop -= deltaY * i.settings.wheelSpeed;
  758. element.scrollLeft += deltaX * i.settings.wheelSpeed;
  759. } else if (i.scrollbarYActive && !i.scrollbarXActive) {
  760. // only vertical scrollbar is active and useBothWheelAxes option is
  761. // active, so let's scroll vertical bar using both mouse wheel axes
  762. if (deltaY) {
  763. element.scrollTop -= deltaY * i.settings.wheelSpeed;
  764. } else {
  765. element.scrollTop += deltaX * i.settings.wheelSpeed;
  766. }
  767. shouldPrevent = true;
  768. } else if (i.scrollbarXActive && !i.scrollbarYActive) {
  769. // useBothWheelAxes and only horizontal bar is active, so use both
  770. // wheel axes for horizontal bar
  771. if (deltaX) {
  772. element.scrollLeft += deltaX * i.settings.wheelSpeed;
  773. } else {
  774. element.scrollLeft -= deltaY * i.settings.wheelSpeed;
  775. }
  776. shouldPrevent = true;
  777. }
  778. updateGeometry(i);
  779. shouldPrevent = shouldPrevent || shouldPreventDefault(deltaX, deltaY);
  780. if (shouldPrevent && !e.ctrlKey) {
  781. e.stopPropagation();
  782. e.preventDefault();
  783. }
  784. }
  785. if (typeof window.onwheel !== 'undefined') {
  786. i.event.bind(element, 'wheel', mousewheelHandler);
  787. } else if (typeof window.onmousewheel !== 'undefined') {
  788. i.event.bind(element, 'mousewheel', mousewheelHandler);
  789. }
  790. }
  791. function touch(i) {
  792. if (!env.supportsTouch && !env.supportsIePointer) {
  793. return;
  794. }
  795. var element = i.element;
  796. function shouldPrevent(deltaX, deltaY) {
  797. var scrollTop = Math.floor(element.scrollTop);
  798. var scrollLeft = element.scrollLeft;
  799. var magnitudeX = Math.abs(deltaX);
  800. var magnitudeY = Math.abs(deltaY);
  801. if (magnitudeY > magnitudeX) {
  802. // user is perhaps trying to swipe up/down the page
  803. if (
  804. (deltaY < 0 && scrollTop === i.contentHeight - i.containerHeight) ||
  805. (deltaY > 0 && scrollTop === 0)
  806. ) {
  807. // set prevent for mobile Chrome refresh
  808. return window.scrollY === 0 && deltaY > 0 && env.isChrome;
  809. }
  810. } else if (magnitudeX > magnitudeY) {
  811. // user is perhaps trying to swipe left/right across the page
  812. if (
  813. (deltaX < 0 && scrollLeft === i.contentWidth - i.containerWidth) ||
  814. (deltaX > 0 && scrollLeft === 0)
  815. ) {
  816. return true;
  817. }
  818. }
  819. return true;
  820. }
  821. function applyTouchMove(differenceX, differenceY) {
  822. element.scrollTop -= differenceY;
  823. element.scrollLeft -= differenceX;
  824. updateGeometry(i);
  825. }
  826. var startOffset = {};
  827. var startTime = 0;
  828. var speed = {};
  829. var easingLoop = null;
  830. function getTouch(e) {
  831. if (e.targetTouches) {
  832. return e.targetTouches[0];
  833. } else {
  834. // Maybe IE pointer
  835. return e;
  836. }
  837. }
  838. function shouldHandle(e) {
  839. if (e.pointerType && e.pointerType === 'pen' && e.buttons === 0) {
  840. return false;
  841. }
  842. if (e.targetTouches && e.targetTouches.length === 1) {
  843. return true;
  844. }
  845. if (
  846. e.pointerType &&
  847. e.pointerType !== 'mouse' &&
  848. e.pointerType !== e.MSPOINTER_TYPE_MOUSE
  849. ) {
  850. return true;
  851. }
  852. return false;
  853. }
  854. function touchStart(e) {
  855. if (!shouldHandle(e)) {
  856. return;
  857. }
  858. var touch = getTouch(e);
  859. startOffset.pageX = touch.pageX;
  860. startOffset.pageY = touch.pageY;
  861. startTime = new Date().getTime();
  862. if (easingLoop !== null) {
  863. clearInterval(easingLoop);
  864. }
  865. }
  866. function shouldBeConsumedByChild(target, deltaX, deltaY) {
  867. if (!element.contains(target)) {
  868. return false;
  869. }
  870. var cursor = target;
  871. while (cursor && cursor !== element) {
  872. if (cursor.classList.contains(cls.element.consuming)) {
  873. return true;
  874. }
  875. var style = get(cursor);
  876. // if deltaY && vertical scrollable
  877. if (deltaY && style.overflowY.match(/(scroll|auto)/)) {
  878. var maxScrollTop = cursor.scrollHeight - cursor.clientHeight;
  879. if (maxScrollTop > 0) {
  880. if (
  881. (cursor.scrollTop > 0 && deltaY < 0) ||
  882. (cursor.scrollTop < maxScrollTop && deltaY > 0)
  883. ) {
  884. return true;
  885. }
  886. }
  887. }
  888. // if deltaX && horizontal scrollable
  889. if (deltaX && style.overflowX.match(/(scroll|auto)/)) {
  890. var maxScrollLeft = cursor.scrollWidth - cursor.clientWidth;
  891. if (maxScrollLeft > 0) {
  892. if (
  893. (cursor.scrollLeft > 0 && deltaX < 0) ||
  894. (cursor.scrollLeft < maxScrollLeft && deltaX > 0)
  895. ) {
  896. return true;
  897. }
  898. }
  899. }
  900. cursor = cursor.parentNode;
  901. }
  902. return false;
  903. }
  904. function touchMove(e) {
  905. if (shouldHandle(e)) {
  906. var touch = getTouch(e);
  907. var currentOffset = { pageX: touch.pageX, pageY: touch.pageY };
  908. var differenceX = currentOffset.pageX - startOffset.pageX;
  909. var differenceY = currentOffset.pageY - startOffset.pageY;
  910. if (shouldBeConsumedByChild(e.target, differenceX, differenceY)) {
  911. return;
  912. }
  913. applyTouchMove(differenceX, differenceY);
  914. startOffset = currentOffset;
  915. var currentTime = new Date().getTime();
  916. var timeGap = currentTime - startTime;
  917. if (timeGap > 0) {
  918. speed.x = differenceX / timeGap;
  919. speed.y = differenceY / timeGap;
  920. startTime = currentTime;
  921. }
  922. if (shouldPrevent(differenceX, differenceY)) {
  923. e.preventDefault();
  924. }
  925. }
  926. }
  927. function touchEnd() {
  928. if (i.settings.swipeEasing) {
  929. clearInterval(easingLoop);
  930. easingLoop = setInterval(function() {
  931. if (i.isInitialized) {
  932. clearInterval(easingLoop);
  933. return;
  934. }
  935. if (!speed.x && !speed.y) {
  936. clearInterval(easingLoop);
  937. return;
  938. }
  939. if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) {
  940. clearInterval(easingLoop);
  941. return;
  942. }
  943. if (!i.element) {
  944. clearInterval(easingLoop);
  945. return;
  946. }
  947. applyTouchMove(speed.x * 30, speed.y * 30);
  948. speed.x *= 0.8;
  949. speed.y *= 0.8;
  950. }, 10);
  951. }
  952. }
  953. if (env.supportsTouch) {
  954. i.event.bind(element, 'touchstart', touchStart);
  955. i.event.bind(element, 'touchmove', touchMove);
  956. i.event.bind(element, 'touchend', touchEnd);
  957. } else if (env.supportsIePointer) {
  958. if (window.PointerEvent) {
  959. i.event.bind(element, 'pointerdown', touchStart);
  960. i.event.bind(element, 'pointermove', touchMove);
  961. i.event.bind(element, 'pointerup', touchEnd);
  962. } else if (window.MSPointerEvent) {
  963. i.event.bind(element, 'MSPointerDown', touchStart);
  964. i.event.bind(element, 'MSPointerMove', touchMove);
  965. i.event.bind(element, 'MSPointerUp', touchEnd);
  966. }
  967. }
  968. }
  969. var defaultSettings = function () { return ({
  970. handlers: ['click-rail', 'drag-thumb', 'keyboard', 'wheel', 'touch'],
  971. maxScrollbarLength: null,
  972. minScrollbarLength: null,
  973. scrollingThreshold: 1000,
  974. scrollXMarginOffset: 0,
  975. scrollYMarginOffset: 0,
  976. suppressScrollX: false,
  977. suppressScrollY: false,
  978. swipeEasing: true,
  979. useBothWheelAxes: false,
  980. wheelPropagation: true,
  981. wheelSpeed: 1,
  982. }); };
  983. var handlers = {
  984. 'click-rail': clickRail,
  985. 'drag-thumb': dragThumb,
  986. keyboard: keyboard,
  987. wheel: wheel,
  988. touch: touch,
  989. };
  990. var PerfectScrollbar = function PerfectScrollbar(element, userSettings) {
  991. var this$1 = this;
  992. if ( userSettings === void 0 ) userSettings = {};
  993. if (typeof element === 'string') {
  994. element = document.querySelector(element);
  995. }
  996. if (!element || !element.nodeName) {
  997. throw new Error('no element is specified to initialize PerfectScrollbar');
  998. }
  999. this.element = element;
  1000. element.classList.add(cls.main);
  1001. this.settings = defaultSettings();
  1002. for (var key in userSettings) {
  1003. this.settings[key] = userSettings[key];
  1004. }
  1005. this.containerWidth = null;
  1006. this.containerHeight = null;
  1007. this.contentWidth = null;
  1008. this.contentHeight = null;
  1009. var focus = function () { return element.classList.add(cls.state.focus); };
  1010. var blur = function () { return element.classList.remove(cls.state.focus); };
  1011. this.isRtl = get(element).direction === 'rtl';
  1012. if (this.isRtl === true) {
  1013. element.classList.add(cls.rtl);
  1014. }
  1015. this.isNegativeScroll = (function () {
  1016. var originalScrollLeft = element.scrollLeft;
  1017. var result = null;
  1018. element.scrollLeft = -1;
  1019. result = element.scrollLeft < 0;
  1020. element.scrollLeft = originalScrollLeft;
  1021. return result;
  1022. })();
  1023. this.negativeScrollAdjustment = this.isNegativeScroll
  1024. ? element.scrollWidth - element.clientWidth
  1025. : 0;
  1026. this.event = new EventManager();
  1027. this.ownerDocument = element.ownerDocument || document;
  1028. this.scrollbarXRail = div(cls.element.rail('x'));
  1029. element.appendChild(this.scrollbarXRail);
  1030. this.scrollbarX = div(cls.element.thumb('x'));
  1031. this.scrollbarXRail.appendChild(this.scrollbarX);
  1032. this.scrollbarX.setAttribute('tabindex', 0);
  1033. this.event.bind(this.scrollbarX, 'focus', focus);
  1034. this.event.bind(this.scrollbarX, 'blur', blur);
  1035. this.scrollbarXActive = null;
  1036. this.scrollbarXWidth = null;
  1037. this.scrollbarXLeft = null;
  1038. var railXStyle = get(this.scrollbarXRail);
  1039. this.scrollbarXBottom = parseInt(railXStyle.bottom, 10);
  1040. if (isNaN(this.scrollbarXBottom)) {
  1041. this.isScrollbarXUsingBottom = false;
  1042. this.scrollbarXTop = toInt(railXStyle.top);
  1043. } else {
  1044. this.isScrollbarXUsingBottom = true;
  1045. }
  1046. this.railBorderXWidth =
  1047. toInt(railXStyle.borderLeftWidth) + toInt(railXStyle.borderRightWidth);
  1048. // Set rail to display:block to calculate margins
  1049. set(this.scrollbarXRail, { display: 'block' });
  1050. this.railXMarginWidth =
  1051. toInt(railXStyle.marginLeft) + toInt(railXStyle.marginRight);
  1052. set(this.scrollbarXRail, { display: '' });
  1053. this.railXWidth = null;
  1054. this.railXRatio = null;
  1055. this.scrollbarYRail = div(cls.element.rail('y'));
  1056. element.appendChild(this.scrollbarYRail);
  1057. this.scrollbarY = div(cls.element.thumb('y'));
  1058. this.scrollbarYRail.appendChild(this.scrollbarY);
  1059. this.scrollbarY.setAttribute('tabindex', 0);
  1060. this.event.bind(this.scrollbarY, 'focus', focus);
  1061. this.event.bind(this.scrollbarY, 'blur', blur);
  1062. this.scrollbarYActive = null;
  1063. this.scrollbarYHeight = null;
  1064. this.scrollbarYTop = null;
  1065. var railYStyle = get(this.scrollbarYRail);
  1066. this.scrollbarYRight = parseInt(railYStyle.right, 10);
  1067. if (isNaN(this.scrollbarYRight)) {
  1068. this.isScrollbarYUsingRight = false;
  1069. this.scrollbarYLeft = toInt(railYStyle.left);
  1070. } else {
  1071. this.isScrollbarYUsingRight = true;
  1072. }
  1073. this.scrollbarYOuterWidth = this.isRtl ? outerWidth(this.scrollbarY) : null;
  1074. this.railBorderYWidth =
  1075. toInt(railYStyle.borderTopWidth) + toInt(railYStyle.borderBottomWidth);
  1076. set(this.scrollbarYRail, { display: 'block' });
  1077. this.railYMarginHeight =
  1078. toInt(railYStyle.marginTop) + toInt(railYStyle.marginBottom);
  1079. set(this.scrollbarYRail, { display: '' });
  1080. this.railYHeight = null;
  1081. this.railYRatio = null;
  1082. this.reach = {
  1083. x:
  1084. element.scrollLeft <= 0
  1085. ? 'start'
  1086. : element.scrollLeft >= this.contentWidth - this.containerWidth
  1087. ? 'end'
  1088. : null,
  1089. y:
  1090. element.scrollTop <= 0
  1091. ? 'start'
  1092. : element.scrollTop >= this.contentHeight - this.containerHeight
  1093. ? 'end'
  1094. : null,
  1095. };
  1096. this.isAlive = true;
  1097. this.settings.handlers.forEach(function (handlerName) { return handlers[handlerName](this$1); });
  1098. this.lastScrollTop = Math.floor(element.scrollTop); // for onScroll only
  1099. this.lastScrollLeft = element.scrollLeft; // for onScroll only
  1100. this.event.bind(this.element, 'scroll', function (e) { return this$1.onScroll(e); });
  1101. updateGeometry(this);
  1102. };
  1103. PerfectScrollbar.prototype.update = function update () {
  1104. if (!this.isAlive) {
  1105. return;
  1106. }
  1107. // Recalcuate negative scrollLeft adjustment
  1108. this.negativeScrollAdjustment = this.isNegativeScroll
  1109. ? this.element.scrollWidth - this.element.clientWidth
  1110. : 0;
  1111. // Recalculate rail margins
  1112. set(this.scrollbarXRail, { display: 'block' });
  1113. set(this.scrollbarYRail, { display: 'block' });
  1114. this.railXMarginWidth =
  1115. toInt(get(this.scrollbarXRail).marginLeft) +
  1116. toInt(get(this.scrollbarXRail).marginRight);
  1117. this.railYMarginHeight =
  1118. toInt(get(this.scrollbarYRail).marginTop) +
  1119. toInt(get(this.scrollbarYRail).marginBottom);
  1120. // Hide scrollbars not to affect scrollWidth and scrollHeight
  1121. set(this.scrollbarXRail, { display: 'none' });
  1122. set(this.scrollbarYRail, { display: 'none' });
  1123. updateGeometry(this);
  1124. processScrollDiff(this, 'top', 0, false, true);
  1125. processScrollDiff(this, 'left', 0, false, true);
  1126. set(this.scrollbarXRail, { display: '' });
  1127. set(this.scrollbarYRail, { display: '' });
  1128. };
  1129. PerfectScrollbar.prototype.onScroll = function onScroll (e) {
  1130. if (!this.isAlive) {
  1131. return;
  1132. }
  1133. updateGeometry(this);
  1134. processScrollDiff(this, 'top', this.element.scrollTop - this.lastScrollTop);
  1135. processScrollDiff(
  1136. this,
  1137. 'left',
  1138. this.element.scrollLeft - this.lastScrollLeft
  1139. );
  1140. this.lastScrollTop = Math.floor(this.element.scrollTop);
  1141. this.lastScrollLeft = this.element.scrollLeft;
  1142. };
  1143. PerfectScrollbar.prototype.destroy = function destroy () {
  1144. if (!this.isAlive) {
  1145. return;
  1146. }
  1147. this.event.unbindAll();
  1148. remove(this.scrollbarX);
  1149. remove(this.scrollbarY);
  1150. remove(this.scrollbarXRail);
  1151. remove(this.scrollbarYRail);
  1152. this.removePsClasses();
  1153. // unset elements
  1154. this.element = null;
  1155. this.scrollbarX = null;
  1156. this.scrollbarY = null;
  1157. this.scrollbarXRail = null;
  1158. this.scrollbarYRail = null;
  1159. this.isAlive = false;
  1160. };
  1161. PerfectScrollbar.prototype.removePsClasses = function removePsClasses () {
  1162. this.element.className = this.element.className
  1163. .split(' ')
  1164. .filter(function (name) { return !name.match(/^ps([-_].+|)$/); })
  1165. .join(' ');
  1166. };
  1167. return PerfectScrollbar;
  1168. })));