perfect-scrollbar.common.js 35 KB

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