perfect-scrollbar.esm.js 34 KB

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