text: import scrollbarWidth from 'scrollbarwidth';
  import throttle from 'lodash.throttle';
  import debounce from 'lodash.debounce';
  import memoize from 'lodash.memoize';
  import ResizeObserver from 'resize-observer-polyfill';
  import canUseDOM from 'can-use-dom';
  
  export default class SimpleBar {
    constructor(element, options) {
      this.el = element;
      this.flashTimeout;
      this.resizeWrapperEl;
      this.contentEl;
      this.offsetEl;
      this.maskEl;
      this.globalObserver;
      this.mutationObserver;
      this.resizeObserver;
      this.scrollbarWidth;
      this.minScrollbarWidth = 20;
      this.options = { ...SimpleBar.defaultOptions, ...options };
      this.classNames = { ...SimpleBar.defaultOptions.classNames, ...this.options.classNames };
      this.isRtl;
      this.axis = {
        x: {
          scrollOffsetAttr: 'scrollLeft',
          sizeAttr: 'width',
          scrollSizeAttr: 'scrollWidth',
          offsetAttr: 'left',
          overflowAttr: 'overflowX',
          dragOffset: 0,
          isOverflowing: true,
          isVisible: false,
          forceVisible: false,
          track: {},
          scrollbar: {}
        },
        y: {
          scrollOffsetAttr: 'scrollTop',
          sizeAttr: 'height',
          scrollSizeAttr: 'scrollHeight',
          offsetAttr: 'top',
          overflowAttr: 'overflowY',
          dragOffset: 0,
          isOverflowing: true,
          isVisible: false,
          forceVisible: false,
          track: {},
          scrollbar: {}
        }
      };
  
      this.recalculate = throttle(this.recalculate.bind(this), 64);
      this.onMouseMove = throttle(this.onMouseMove.bind(this), 64);
      this.hideScrollbars = debounce(
        this.hideScrollbars.bind(this),
        this.options.timeout
      );
      this.onWindowResize = debounce(this.onWindowResize.bind(this), 64, {
        leading: true
      });
  
      SimpleBar.getRtlHelpers = memoize(SimpleBar.getRtlHelpers);
  
      // getContentElement is deprecated
      this.getContentElement = this.getScrollElement;
  
      this.init();
    }
  
    /**
     * Static properties
     */
  
    /**
     * Helper to fix browsers inconsistency on RTL:
     *  - Firefox inverts the scrollbar initial position
     *  - IE11 inverts both scrollbar position and scrolling offset
     * Directly inspired by @KingSora's OverlayScrollbars https://github.com/KingSora/OverlayScrollbars/blob/master/js/OverlayScrollbars.js#L1634
     */
    static getRtlHelpers() {
      const dummyDiv = document.createElement('div');
      dummyDiv.innerHTML =
        '<div class="hs-dummy-scrollbar-size"><div style="height: 200%; width: 200%; margin: 10px 0;"></div></div>';
      const scrollbarDummyEl = dummyDiv.firstElementChild;
      document.body.appendChild(scrollbarDummyEl);
      const dummyContainerChild = scrollbarDummyEl.firstElementChild;
      scrollbarDummyEl.scrollLeft = 0;
      const dummyContainerOffset = SimpleBar.getOffset(scrollbarDummyEl);
      const dummyContainerChildOffset = SimpleBar.getOffset(dummyContainerChild);
      scrollbarDummyEl.scrollLeft = 999;
      const dummyContainerScrollOffsetAfterScroll = SimpleBar.getOffset(
        dummyContainerChild
      );
  
      return {
        // determines if the scrolling is responding with negative values
        isRtlScrollingInverted:
          dummyContainerOffset.left !== dummyContainerChildOffset.left &&
          dummyContainerChildOffset.left -
            dummyContainerScrollOffsetAfterScroll.left !==
            0,
        // determines if the origin scrollbar position is inverted or not (positioned on left or right)
        isRtlScrollbarInverted:
          dummyContainerOffset.left !== dummyContainerChildOffset.left
      };
    }
  
    static defaultOptions = {
      autoHide: true,
      forceVisible: false,
      classNames: {
        resizeWrapper: 'simplebar-resize-wrapper',
        content: 'simplebar-content',
        offset: 'simplebar-offset',
        mask: 'simplebar-mask',
        wrapper: 'simplebar-wrapper',
        placeholder: 'simplebar-placeholder',
        scrollbar: 'simplebar-scrollbar',
        track: 'simplebar-track',
        heightAutoObserverWrapperEl: 'simplebar-height-auto-observer-wrapper',
        heightAutoObserverEl: 'simplebar-height-auto-observer',
        visible: 'simplebar-visible',
        horizontal: 'simplebar-horizontal',
        vertical: 'simplebar-vertical',
        hover: 'simplebar-hover',
        dragging: 'simplebar-dragging',
      },
      scrollbarMinSize: 25,
      scrollbarMaxSize: 0,
      timeout: 1000
    };
  
    static initHtmlApi() {
      this.initDOMLoadedElements = this.initDOMLoadedElements.bind(this);
  
      // MutationObserver is IE11+
      if (typeof MutationObserver !== 'undefined') {
        // Mutation observer to observe dynamically added elements
        this.globalObserver = new MutationObserver(mutations => {
          mutations.forEach(mutation => {
            Array.from(mutation.addedNodes).forEach(addedNode => {
              if (addedNode.nodeType === 1) {
                if (addedNode.hasAttribute('data-simplebar')) {
                  !addedNode.SimpleBar &&
                    new SimpleBar(addedNode, SimpleBar.getElOptions(addedNode));
                } else {
                  Array.from(
                    addedNode.querySelectorAll('[data-simplebar]')
                  ).forEach(el => {
                    !el.SimpleBar &&
                      new SimpleBar(el, SimpleBar.getElOptions(el));
                  });
                }
              }
            });
  
            Array.from(mutation.removedNodes).forEach(removedNode => {
              if (removedNode.nodeType === 1) {
                if (removedNode.hasAttribute('data-simplebar')) {
                  removedNode.SimpleBar && removedNode.SimpleBar.unMount();
                } else {
                  Array.from(
                    removedNode.querySelectorAll('[data-simplebar]')
                  ).forEach(el => {
                    el.SimpleBar && el.SimpleBar.unMount();
                  });
                }
              }
            });
          });
        });
  
        this.globalObserver.observe(document, { childList: true, subtree: true });
      }
  
      // Taken from jQuery `ready` function
      // Instantiate elements already present on the page
      if (
        document.readyState === 'complete' ||
        (document.readyState !== 'loading' && !document.documentElement.doScroll)
      ) {
        // Handle it asynchronously to allow scripts the opportunity to delay init
        window.setTimeout(this.initDOMLoadedElements);
      } else {
        document.addEventListener('DOMContentLoaded', this.initDOMLoadedElements);
        window.addEventListener('load', this.initDOMLoadedElements);
      }
    }
  
    // Helper function to retrieve options from element attributes
    static getElOptions(el) {
      const options = Array.from(el.attributes).reduce((acc, attribute) => {
        const option = attribute.name.match(/data-simplebar-(.+)/);
        if (option) {
          const key = option[1].replace(/\W+(.)/g, (x, chr) => chr.toUpperCase());
          switch (attribute.value) {
            case 'true':
              acc[key] = true;
              break;
            case 'false':
              acc[key] = false;
              break;
            case undefined:
              acc[key] = true;
              break;
            default:
              acc[key] = attribute.value;
          }
        }
        return acc;
      }, {});
      return options;
    }
  
    static removeObserver() {
      this.globalObserver.disconnect();
    }
  
    static initDOMLoadedElements() {
      document.removeEventListener(
        'DOMContentLoaded',
        this.initDOMLoadedElements
      );
      window.removeEventListener('load', this.initDOMLoadedElements);
  
      Array.from(document.querySelectorAll('[data-simplebar]')).forEach(el => {
        if (!el.SimpleBar) new SimpleBar(el, SimpleBar.getElOptions(el));
      });
    }
  
    static getOffset(el) {
      const rect = el.getBoundingClientRect();
  
      return {
        top:
          rect.top + (window.pageYOffset || document.documentElement.scrollTop),
        left:
          rect.left + (window.pageXOffset || document.documentElement.scrollLeft)
      };
    }
  
    init() {
      // Save a reference to the instance, so we know this DOM node has already been instancied
      this.el.SimpleBar = this;
  
      // We stop here on server-side
      if (canUseDOM) {
        this.initDOM();
  
        this.scrollbarWidth = scrollbarWidth();
  
        this.recalculate();
  
        this.initListeners();
      }
    }
  
    initDOM() {
      // make sure this element doesn't have the elements yet
      if (
        Array.from(this.el.children).filter(child =>
          child.classList.contains(this.classNames.wrapper)
        ).length
      ) {
        // assume that element has his DOM already initiated
        this.wrapperEl = this.el.querySelector(`.${this.classNames.wrapper}`);
        this.contentEl = this.el.querySelector(`.${this.classNames.content}`);
        this.offsetEl = this.el.querySelector(`.${this.classNames.offset}`);
        this.maskEl = this.el.querySelector(`.${this.classNames.mask}`);
        this.resizeWrapperEl = this.el.querySelector(
          `.${this.classNames.resizeWrapper}`
        );
        this.placeholderEl = this.el.querySelector(
          `.${this.classNames.placeholder}`
        );
        this.heightAutoObserverWrapperEl = this.el.querySelector(
          `.${this.classNames.heightAutoObserverWrapperEl}`
        );
        this.heightAutoObserverEl = this.el.querySelector(
          `.${this.classNames.heightAutoObserverEl}`
        );
        this.axis.x.track.el = this.el.querySelector(
          `.${this.classNames.track}.${this.classNames.horizontal}`
        );
        this.axis.y.track.el = this.el.querySelector(
          `.${this.classNames.track}.${this.classNames.vertical}`
        );
      } else {
        // Prepare DOM
        this.wrapperEl = document.createElement('div');
        this.contentEl = document.createElement('div');
        this.offsetEl = document.createElement('div');
        this.maskEl = document.createElement('div');
        this.resizeWrapperEl = document.createElement('div');
        this.placeholderEl = document.createElement('div');
        this.heightAutoObserverWrapperEl = document.createElement('div');
        this.heightAutoObserverEl = document.createElement('div');
  
        this.wrapperEl.classList.add(this.classNames.wrapper);
        this.contentEl.classList.add(this.classNames.content);
        this.offsetEl.classList.add(this.classNames.offset);
        this.maskEl.classList.add(this.classNames.mask);
        this.resizeWrapperEl.classList.add(this.classNames.resizeWrapper);
        this.placeholderEl.classList.add(this.classNames.placeholder);
        this.heightAutoObserverWrapperEl.classList.add(
          this.classNames.heightAutoObserverWrapperEl
        );
        this.heightAutoObserverEl.classList.add(
          this.classNames.heightAutoObserverEl
        );
  
        while (this.el.firstChild) {
          this.resizeWrapperEl.appendChild(this.el.firstChild);
        }
  
        this.contentEl.appendChild(this.resizeWrapperEl);
        this.offsetEl.appendChild(this.contentEl);
        this.maskEl.appendChild(this.offsetEl);
        this.heightAutoObserverWrapperEl.appendChild(this.heightAutoObserverEl);
        this.wrapperEl.appendChild(this.heightAutoObserverWrapperEl);
        this.wrapperEl.appendChild(this.maskEl);
        this.wrapperEl.appendChild(this.placeholderEl);
        this.el.appendChild(this.wrapperEl);
      }
  
      if (!this.axis.x.track.el || !this.axis.y.track.el) {
        const track = document.createElement('div');
        const scrollbar = document.createElement('div');
  
        track.classList.add(this.classNames.track);
        scrollbar.classList.add(this.classNames.scrollbar);
  
        track.appendChild(scrollbar);
  
        this.axis.x.track.el = track.cloneNode(true);
        this.axis.x.track.el.classList.add(this.classNames.horizontal);
  
        this.axis.y.track.el = track.cloneNode(true);
        this.axis.y.track.el.classList.add(this.classNames.vertical);
  
        this.el.appendChild(this.axis.x.track.el);
        this.el.appendChild(this.axis.y.track.el);
      }
  
      this.axis.x.scrollbar.el = this.axis.x.track.el.querySelector(
        `.${this.classNames.scrollbar}`
      );
      this.axis.y.scrollbar.el = this.axis.y.track.el.querySelector(
        `.${this.classNames.scrollbar}`
      );
  
      if (!this.options.autoHide) {
        this.axis.x.scrollbar.el.classList.add(this.classNames.visible);
        this.axis.y.scrollbar.el.classList.add(this.classNames.visible);
      }
  
      this.el.setAttribute('data-simplebar', 'init');
    }
  
    initListeners() {
      // Event listeners
      if (this.options.autoHide) {
        this.el.addEventListener('mouseenter', this.onMouseEnter);
      }
  
      [
        'mousedown',
        'click',
        'dblclick',
        'touchstart',
        'touchend',
        'touchmove'
      ].forEach(e => {
        this.el.addEventListener(e, this.onPointerEvent, true);
      });
      this.el.addEventListener('mousemove', this.onMouseMove);
      this.el.addEventListener('mouseleave', this.onMouseLeave);
  
      this.contentEl.addEventListener('scroll', this.onScroll);
  
      // Browser zoom triggers a window resize
      window.addEventListener('resize', this.onWindowResize);
  
      // MutationObserver is IE11+
      if (typeof MutationObserver !== 'undefined') {
        // create an observer instance
        this.mutationObserver = new MutationObserver(mutations => {
          mutations.forEach(mutation => {
            if (
              mutation.target === this.contentEl ||
              this.isChildNode(mutation.target) ||
              mutation.addedNodes.length ||
              mutation.removedNodes.length
            ) {
              this.recalculate();
            }
          });
        });
  
        this.mutationObserver.observe(this.el, { attributes: true });
        this.mutationObserver.observe(this.contentEl, {
          attributes: true,
          childList: true,
          characterData: true,
          subtree: true
        });
      }
  
      this.resizeObserver = new ResizeObserver(this.recalculate);
      this.resizeObserver.observe(this.el);
      this.resizeObserver.observe(this.resizeWrapperEl);
    }
  
    recalculate() {
      const isHeightAuto = this.heightAutoObserverEl.offsetHeight <= 1;
      const isWidthAuto = this.heightAutoObserverEl.offsetWidth <= 1;
  
      this.elStyles = window.getComputedStyle(this.el);
  
      this.isRtl = this.elStyles.direction === 'rtl';
  
      this.contentEl.style.padding = `${this.elStyles.paddingTop} ${
        this.elStyles.paddingRight
      } ${this.elStyles.paddingBottom} ${this.elStyles.paddingLeft}`;
      this.contentEl.style.height = isHeightAuto ? 'auto' : '100%';
  
      this.placeholderEl.style.width = isWidthAuto
        ? `${this.contentEl.scrollWidth}px`
        : 'auto';
      this.placeholderEl.style.height = `${this.contentEl.scrollHeight}px`;
  
      this.wrapperEl.style.margin = `-${this.elStyles.paddingTop} -${
        this.elStyles.paddingRight
      } -${this.elStyles.paddingBottom} -${this.elStyles.paddingLeft}`;
  
      this.axis.x.track.rect = this.axis.x.track.el.getBoundingClientRect();
      this.axis.y.track.rect = this.axis.y.track.el.getBoundingClientRect();
  
      // Set isOverflowing to false if scrollbar is not necessary (content is shorter than offset)
      this.axis.x.isOverflowing =
        (this.scrollbarWidth
          ? this.contentEl.scrollWidth
          : this.contentEl.scrollWidth - this.minScrollbarWidth) >
        Math.ceil(this.axis.x.track.rect.width);
      this.axis.y.isOverflowing =
        (this.scrollbarWidth
          ? this.contentEl.scrollHeight
          : this.contentEl.scrollHeight - this.minScrollbarWidth) >
        Math.ceil(this.axis.y.track.rect.height);
  
      // Set isOverflowing to false if user explicitely set hidden overflow
      this.axis.x.isOverflowing =
        this.elStyles.overflowX === 'hidden' ? false : this.axis.x.isOverflowing;
      this.axis.y.isOverflowing =
        this.elStyles.overflowY === 'hidden' ? false : this.axis.y.isOverflowing;
  
      this.axis.x.forceVisible = this.options.forceVisible === "x" || this.options.forceVisible === true;
      this.axis.y.forceVisible = this.options.forceVisible === "y" || this.options.forceVisible === true;
  
      this.axis.x.scrollbar.size = this.getScrollbarSize('x');
      this.axis.y.scrollbar.size = this.getScrollbarSize('y');
  
      this.axis.x.scrollbar.el.style.width = `${this.axis.x.scrollbar.size}px`;
      this.axis.y.scrollbar.el.style.height = `${this.axis.y.scrollbar.size}px`;
  
      this.positionScrollbar('x');
      this.positionScrollbar('y');
  
      this.toggleTrackVisibility('x');
      this.toggleTrackVisibility('y');
  
      this.hideNativeScrollbar();
    }
  
    /**
     * Calculate scrollbar size
     */
    getScrollbarSize(axis = 'y') {
      const contentSize = this.scrollbarWidth
        ? this.contentEl[this.axis[axis].scrollSizeAttr]
        : this.contentEl[this.axis[axis].scrollSizeAttr] - this.minScrollbarWidth;
      const trackSize = this.axis[axis].track.rect[this.axis[axis].sizeAttr];
      let scrollbarSize;
  
      if (!this.axis[axis].isOverflowing) {
        return;
      }
  
      let scrollbarRatio = trackSize / contentSize;
  
      // Calculate new height/position of drag handle.
      scrollbarSize = Math.max(
        ~~(scrollbarRatio * trackSize),
        this.options.scrollbarMinSize
      );
  
      if (this.options.scrollbarMaxSize) {
        scrollbarSize = Math.min(scrollbarSize, this.options.scrollbarMaxSize);
      }
  
      return scrollbarSize;
    }
  
    positionScrollbar(axis = 'y') {
      const contentSize = this.contentEl[this.axis[axis].scrollSizeAttr];
      const trackSize = this.axis[axis].track.rect[this.axis[axis].sizeAttr];
      const hostSize = parseInt(this.elStyles[this.axis[axis].sizeAttr], 10);
      const scrollbar = this.axis[axis].scrollbar;
  
      let scrollOffset = this.contentEl[this.axis[axis].scrollOffsetAttr];
      scrollOffset =
        axis === 'x' &&
        this.isRtl &&
        SimpleBar.getRtlHelpers().isRtlScrollingInverted
          ? -scrollOffset
          : scrollOffset;
      let scrollPourcent = scrollOffset / (contentSize - hostSize);
  
      let handleOffset = ~~((trackSize - scrollbar.size) * scrollPourcent);
      handleOffset =
        axis === 'x' &&
        this.isRtl &&
        SimpleBar.getRtlHelpers().isRtlScrollbarInverted
          ? handleOffset + (trackSize - scrollbar.size)
          : handleOffset;
  
      scrollbar.el.style.transform =
        axis === 'x'
          ? `translate3d(${handleOffset}px, 0, 0)`
          : `translate3d(0, ${handleOffset}px, 0)`;
    }
  
    toggleTrackVisibility(axis = 'y') {
      const track = this.axis[axis].track.el;
      const scrollbar = this.axis[axis].scrollbar.el;
  
      if (this.axis[axis].isOverflowing || this.axis[axis].forceVisible) {
        track.style.visibility = 'visible';
        this.contentEl.style[this.axis[axis].overflowAttr] = 'scroll';
      } else {
        track.style.visibility = 'hidden';
        this.contentEl.style[this.axis[axis].overflowAttr] = 'hidden';
      }
  
      // Even if forceVisible is enabled, scrollbar itself should be hidden
      if (this.axis[axis].isOverflowing) {
        scrollbar.style.visibility = 'visible';
      } else {
        scrollbar.style.visibility = 'hidden';
      }
    }
  
    hideNativeScrollbar() {
      this.offsetEl.style[this.isRtl ? 'left' : 'right'] =
        this.axis.y.isOverflowing || this.axis.y.forceVisible
          ? `-${this.scrollbarWidth || this.minScrollbarWidth}px`
          : 0;
      this.offsetEl.style.bottom =
        this.axis.x.isOverflowing || this.axis.x.forceVisible
          ? `-${this.scrollbarWidth || this.minScrollbarWidth}px`
          : 0;
  
      // If floating scrollbar
      if (!this.scrollbarWidth) {
        const paddingDirection = [this.isRtl ? 'paddingLeft' : 'paddingRight'];
        this.contentEl.style[paddingDirection] =
          this.axis.y.isOverflowing || this.axis.y.forceVisible
            ? `calc(${this.elStyles[paddingDirection]} + ${
                this.minScrollbarWidth
              }px)`
            : this.elStyles[paddingDirection];
        this.contentEl.style.paddingBottom =
          this.axis.x.isOverflowing || this.axis.x.forceVisible
            ? `calc(${this.elStyles.paddingBottom} + ${this.minScrollbarWidth}px)`
            : this.elStyles.paddingBottom;
      }
    }
  
    /**
     * On scroll event handling
     */
    onScroll = () => {
      if (!this.scrollXTicking) {
        window.requestAnimationFrame(this.scrollX);
        this.scrollXTicking = true;
      }
  
      if (!this.scrollYTicking) {
        window.requestAnimationFrame(this.scrollY);
        this.scrollYTicking = true;
      }
    };
  
    scrollX = () => {
      if (this.axis.x.isOverflowing) {
        this.showScrollbar('x');
        this.positionScrollbar('x');
      }
  
      this.scrollXTicking = false;
    };
  
    scrollY = () => {
      if (this.axis.y.isOverflowing) {
        this.showScrollbar('y');
        this.positionScrollbar('y');
      }
  
      this.scrollYTicking = false;
    };
  
    onMouseEnter = () => {
      this.showScrollbar('x');
      this.showScrollbar('y');
    };
  
    onMouseMove = e => {
      this.mouseX = e.clientX;
      this.mouseY = e.clientY;
  
      if (this.axis.x.isOverflowing || this.axis.x.forceVisible) {
        this.onMouseMoveForAxis('x');
      }
  
      if (this.axis.y.isOverflowing || this.axis.y.forceVisible) {
        this.onMouseMoveForAxis('y');
      }
    };
  
    onMouseMoveForAxis(axis = 'y') {
      this.axis[axis].track.rect = this.axis[
        axis
      ].track.el.getBoundingClientRect();
      this.axis[axis].scrollbar.rect = this.axis[
        axis
      ].scrollbar.el.getBoundingClientRect();
  
      const isWithinScrollbarBoundsX = this.isWithinBounds(
        this.axis[axis].scrollbar.rect
      );
  
      if (isWithinScrollbarBoundsX) {
        this.axis[axis].scrollbar.el.classList.add(this.classNames.hover);
      } else {
        this.axis[axis].scrollbar.el.classList.remove(this.classNames.hover);
      }
  
      if (this.isWithinBounds(this.axis[axis].track.rect)) {
        this.showScrollbar(axis);
        this.axis[axis].track.el.classList.add(this.classNames.hover);
      } else {
        this.axis[axis].track.el.classList.remove(this.classNames.hover);
      }
    }
  
    onMouseLeave = () => {
      this.onMouseMove.cancel();
  
      if (this.axis.x.isOverflowing || this.axis.x.forceVisible) {
        this.onMouseLeaveForAxis('x');
      }
  
      if (this.axis.y.isOverflowing || this.axis.y.forceVisible) {
        this.onMouseLeaveForAxis('y');
      }
  
      this.mouseX = -1;
      this.mouseY = -1;
    };
  
    onMouseLeaveForAxis(axis = 'y') {
      this.axis[axis].track.el.classList.remove(this.classNames.hover);
      this.axis[axis].scrollbar.el.classList.remove(this.classNames.hover);
    }
  
    onWindowResize = () => {
      // Recalculate scrollbarWidth in case it's a zoom
      this.scrollbarWidth = scrollbarWidth();
  
      this.hideNativeScrollbar();
    };
  
    /**
     * Show scrollbar
     */
    showScrollbar(axis = 'y') {
      let scrollbar = this.axis[axis].scrollbar.el;
  
      if (!this.axis[axis].isVisible) {
        scrollbar.classList.add(this.classNames.visible);
        this.axis[axis].isVisible = true;
      }
  
      if (this.options.autoHide) {
        this.hideScrollbars();
      }
    }
  
    /**
     * Hide Scrollbar
     */
    hideScrollbars = () => {
      this.axis.x.track.rect = this.axis.x.track.el.getBoundingClientRect();
      this.axis.y.track.rect = this.axis.y.track.el.getBoundingClientRect();
  
      if (!this.isWithinBounds(this.axis.y.track.rect)) {
        this.axis.y.scrollbar.el.classList.remove(this.classNames.visible);
        this.axis.y.isVisible = false;
      }
  
      if (!this.isWithinBounds(this.axis.x.track.rect)) {
        this.axis.x.scrollbar.el.classList.remove(this.classNames.visible);
        this.axis.x.isVisible = false;
      }
    };
  
    onPointerEvent = e => {
      let isWithinBoundsY, isWithinBoundsX;
      this.axis.x.scrollbar.rect = this.axis.x.scrollbar.el.getBoundingClientRect();
      this.axis.y.scrollbar.rect = this.axis.y.scrollbar.el.getBoundingClientRect();
  
      if (this.axis.x.isOverflowing || this.axis.x.forceVisible) {
        isWithinBoundsX = this.isWithinBounds(this.axis.x.scrollbar.rect);
      }
  
      if (this.axis.y.isOverflowing || this.axis.y.forceVisible) {
        isWithinBoundsY = this.isWithinBounds(this.axis.y.scrollbar.rect);
      }
  
      // If any pointer event is called on the scrollbar
      if (isWithinBoundsY || isWithinBoundsX) {
        // Preventing the event's default action stops text being
        // selectable during the drag.
        e.preventDefault();
        // Prevent event leaking
        e.stopPropagation();
  
        if (e.type === 'mousedown') {
          if (isWithinBoundsY) {
            this.onDragStart(e, 'y');
          }
  
          if (isWithinBoundsX) {
            this.onDragStart(e, 'x');
          }
        }
      }
    };
  
    /**
     * on scrollbar handle drag movement starts
     */
    onDragStart(e, axis = 'y') {
      const scrollbar = this.axis[axis].scrollbar.el;
  
      // Measure how far the user's mouse is from the top of the scrollbar drag handle.
      const eventOffset = axis === 'y' ? e.pageY : e.pageX;
      this.axis[axis].dragOffset =
        eventOffset -
        scrollbar.getBoundingClientRect()[this.axis[axis].offsetAttr];
      this.draggedAxis = axis;
  
      this.el.classList.add(this.classNames.dragging);
  
      document.addEventListener('mousemove', this.drag);
      document.addEventListener('mouseup', this.onEndDrag);
    }
  
    /**
     * Drag scrollbar handle
     */
    drag = e => {
      let eventOffset;
      let track = this.axis[this.draggedAxis].track;
      const trackSize = track.rect[this.axis[this.draggedAxis].sizeAttr];
      const scrollbar = this.axis[this.draggedAxis].scrollbar;
  
      e.preventDefault();
      e.stopPropagation();
  
      if (this.draggedAxis === 'y') {
        eventOffset = e.pageY;
      } else {
        eventOffset = e.pageX;
      }
  
      // Calculate how far the user's mouse is from the top/left of the scrollbar (minus the dragOffset).
      let dragPos =
        eventOffset -
        track.rect[this.axis[this.draggedAxis].offsetAttr] -
        this.axis[this.draggedAxis].dragOffset;
      // Convert the mouse position into a percentage of the scrollbar height/width.
      let dragPerc = dragPos / track.rect[this.axis[this.draggedAxis].sizeAttr];
  
      // Scroll the content by the same percentage.
      let scrollPos =
        dragPerc * this.contentEl[this.axis[this.draggedAxis].scrollSizeAttr];
  
      // Fix browsers inconsistency on RTL
      if (this.draggedAxis === 'x') {
        scrollPos =
          this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollbarInverted
            ? scrollPos - (trackSize + scrollbar.size)
            : scrollPos;
        scrollPos =
          this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollingInverted
            ? -scrollPos
            : scrollPos;
      }
  
      this.contentEl[this.axis[this.draggedAxis].scrollOffsetAttr] = scrollPos;
    };
  
    /**
     * End scroll handle drag
     */
    onEndDrag = e => {
      e.preventDefault();
      e.stopPropagation();
  
      this.el.classList.remove(this.classNames.dragging);
  
      document.removeEventListener('mousemove', this.drag);
      document.removeEventListener('mouseup', this.onEndDrag);
    };
  
    /**
     * Getter for original scrolling element
     */
    getScrollElement() {
      return this.contentEl;
    }
  
    removeListeners() {
      // Event listeners
      if (this.options.autoHide) {
        this.el.removeEventListener('mouseenter', this.onMouseEnter);
      }
  
      [
        'mousedown',
        'click',
        'dblclick',
        'touchstart',
        'touchend',
        'touchmove'
      ].forEach(e => {
        this.el.removeEventListener(e, this.onPointerEvent);
      });
  
      this.el.removeEventListener('mousemove', this.onMouseMove);
      this.el.removeEventListener('mouseleave', this.onMouseLeave);
  
      this.contentEl.removeEventListener('scroll', this.onScroll);
      window.removeEventListener('resize', this.onWindowResize);
  
      this.mutationObserver && this.mutationObserver.disconnect();
      this.resizeObserver.disconnect();
  
      // Cancel all debounced functions
      this.recalculate.cancel();
      this.onMouseMove.cancel();
      this.hideScrollbars.cancel();
      this.onWindowResize.cancel();
    }
  
    /**
     * UnMount mutation observer and delete SimpleBar instance from DOM element
     */
    unMount() {
      this.removeListeners();
      this.el.SimpleBar = null;
    }
  
    /**
     * Recursively walks up the parent nodes looking for this.el
     */
    isChildNode(el) {
      if (el === null) return false;
      if (el === this.el) return true;
  
      return this.isChildNode(el.parentNode);
    }
  
    /**
     * Check if mouse is within bounds
     */
    isWithinBounds(bbox) {
      return (
        this.mouseX >= bbox.left &&
        this.mouseX <= bbox.left + bbox.width &&
        this.mouseY >= bbox.top &&
        this.mouseY <= bbox.top + bbox.height
      );
    }
  }
  
  /**
   * HTML API
   * Called only in a browser env.
   */
  if (canUseDOM) {
    SimpleBar.initHtmlApi();
  }
  