text: /*! iScroll v5.2.0-snapshot ~ (c) 2008-2017 Matteo Spinelli ~ http://cubiq.org/license */
  (function (window, document, Math) {
      var rAF = window.requestAnimationFrame ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame ||
          window.oRequestAnimationFrame ||
          window.msRequestAnimationFrame ||
          function (callback) {
              window.setTimeout(callback, 1000 / 60);
          };
  
      var utils = (function () {
          var me = {};
  
          var _elementStyle = document.createElement('div').style;
          var _vendor = (function () {
              var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
                  transform,
                  i = 0,
                  l = vendors.length;
  
              for (; i < l; i++) {
                  transform = vendors[i] + 'ransform';
                  if (transform in _elementStyle) return vendors[i].substr(0, vendors[i].length - 1);
              }
  
              return false;
          })();
  
          function _prefixStyle(style) {
              if (_vendor === false) return false;
              if (_vendor === '') return style;
              return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
          }
  
          me.getTime = Date.now || function getTime() {
              return new Date().getTime();
          };
  
          me.extend = function (target, obj) {
              for (var i in obj) {
                  target[i] = obj[i];
              }
          };
  
          me.addEvent = function (el, type, fn, capture) {
              el.addEventListener(type, fn, !!capture);
          };
  
          me.removeEvent = function (el, type, fn, capture) {
              el.removeEventListener(type, fn, !!capture);
          };
  
          me.prefixPointerEvent = function (pointerEvent) {
              return window.MSPointerEvent ?
                  'MSPointer' + pointerEvent.charAt(7).toUpperCase() + pointerEvent.substr(8) :
                  pointerEvent;
          };
  
          me.momentum = function (current, start, time, lowerMargin, wrapperSize, deceleration) {
              var distance = current - start,
                  speed = Math.abs(distance) / time,
                  destination,
                  duration;
  
              deceleration = deceleration === undefined ? 0.0006 : deceleration;
  
              destination = current + (speed * speed) / (2 * deceleration) * (distance < 0 ? -1 : 1);
              duration = speed / deceleration;
  
              if (destination < lowerMargin) {
                  destination = wrapperSize ? lowerMargin - (wrapperSize / 2.5 * (speed / 8)) : lowerMargin;
                  distance = Math.abs(destination - current);
                  duration = distance / speed;
              } else if (destination > 0) {
                  destination = wrapperSize ? wrapperSize / 2.5 * (speed / 8) : 0;
                  distance = Math.abs(current) + destination;
                  duration = distance / speed;
              }
  
              return {
                  destination: Math.round(destination),
                  duration: duration
              };
          };
  
          var _transform = _prefixStyle('transform');
  
          me.extend(me, {
              hasTransform: _transform !== false,
              hasPerspective: _prefixStyle('perspective') in _elementStyle,
              hasTouch: 'ontouchstart' in window,
              hasPointer: !!(window.PointerEvent || window.MSPointerEvent), // IE10 is prefixed
              hasTransition: _prefixStyle('transition') in _elementStyle
          });
  
          /*
          This should find all Android browsers lower than build 535.19 (both stock browser and webview)
          - galaxy S2 is ok
          - 2.3.6 : `AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1`
          - 4.0.4 : `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
         - galaxy S3 is badAndroid (stock brower, webview)
           `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
         - galaxy S4 is badAndroid (stock brower, webview)
           `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
         - galaxy S5 is OK
           `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
         - galaxy S6 is OK
           `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
        */
          me.isBadAndroid = (function () {
              var appVersion = window.navigator.appVersion;
              // Android browser is not a chrome browser.
              if (/Android/.test(appVersion) && !(/Chrome\/\d/.test(appVersion))) {
                  var safariVersion = appVersion.match(/Safari\/(\d+.\d)/);
                  if (safariVersion && typeof safariVersion === "object" && safariVersion.length >= 2) {
                      return parseFloat(safariVersion[1]) < 535.19;
                  } else {
                      return true;
                  }
              } else {
                  return false;
              }
          })();
  
          me.extend(me.style = {}, {
              transform: _transform,
              transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
              transitionDuration: _prefixStyle('transitionDuration'),
              transitionDelay: _prefixStyle('transitionDelay'),
              transformOrigin: _prefixStyle('transformOrigin'),
              touchAction: _prefixStyle('touchAction')
          });
  
          me.hasClass = function (e, c) {
              var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
              return re.test(e.className);
          };
  
          me.addClass = function (e, c) {
              if (me.hasClass(e, c)) {
                  return;
              }
  
              var newclass = e.className.split(' ');
              newclass.push(c);
              e.className = newclass.join(' ');
          };
  
          me.removeClass = function (e, c) {
              if (!me.hasClass(e, c)) {
                  return;
              }
  
              var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g');
              e.className = e.className.replace(re, ' ');
          };
  
          me.offset = function (el) {
              var left = -el.offsetLeft,
                  top = -el.offsetTop;
  
              // jshint -W084
              while (el = el.offsetParent) {
                  left -= el.offsetLeft;
                  top -= el.offsetTop;
              }
              // jshint +W084
  
              return {
                  left: left,
                  top: top
              };
          };
  
          me.preventDefaultException = function (el, exceptions) {
              for (var i in exceptions) {
                  if (exceptions[i].test(el[i])) {
                      return true;
                  }
              }
  
              return false;
          };
  
          me.extend(me.eventType = {}, {
              touchstart: 1,
              touchmove: 1,
              touchend: 1,
  
              mousedown: 2,
              mousemove: 2,
              mouseup: 2,
  
              pointerdown: 3,
              pointermove: 3,
              pointerup: 3,
  
              MSPointerDown: 3,
              MSPointerMove: 3,
              MSPointerUp: 3
          });
  
          me.extend(me.ease = {}, {
              quadratic: {
                  style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
                  fn: function (k) {
                      return k * (2 - k);
                  }
              },
              circular: {
                  style: 'cubic-bezier(0.1, 0.57, 0.1, 1)',	// Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)
                  fn: function (k) {
                      return Math.sqrt(1 - (--k * k));
                  }
              },
              back: {
                  style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
                  fn: function (k) {
                      var b = 4;
                      return (k = k - 1) * k * ((b + 1) * k + b) + 1;
                  }
              },
              bounce: {
                  style: '',
                  fn: function (k) {
                      if ((k /= 1) < (1 / 2.75)) {
                          return 7.5625 * k * k;
                      } else if (k < (2 / 2.75)) {
                          return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;
                      } else if (k < (2.5 / 2.75)) {
                          return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;
                      } else {
                          return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;
                      }
                  }
              },
              elastic: {
                  style: '',
                  fn: function (k) {
                      var f = 0.22,
                          e = 0.4;
  
                      if (k === 0) {
                          return 0;
                      }
                      if (k == 1) {
                          return 1;
                      }
  
                      return (e * Math.pow(2, -10 * k) * Math.sin((k - f / 4) * (2 * Math.PI) / f) + 1);
                  }
              }
          });
  
          me.tap = function (e, eventName) {
              var ev = document.createEvent('Event');
              ev.initEvent(eventName, true, true);
              ev.pageX = e.pageX;
              ev.pageY = e.pageY;
              e.target.dispatchEvent(ev);
          };
  
          me.click = function (e) {
              var target = e.target,
                  ev;
  
              if (!(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName)) {
                  // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
                  // initMouseEvent is deprecated.
                  ev = document.createEvent(window.MouseEvent ? 'MouseEvents' : 'Event');
                  ev.initEvent('click', true, true);
                  ev.view = e.view || window;
                  ev.detail = 1;
                  ev.screenX = target.screenX || 0;
                  ev.screenY = target.screenY || 0;
                  ev.clientX = target.clientX || 0;
                  ev.clientY = target.clientY || 0;
                  ev.ctrlKey = !!e.ctrlKey;
                  ev.altKey = !!e.altKey;
                  ev.shiftKey = !!e.shiftKey;
                  ev.metaKey = !!e.metaKey;
                  ev.button = 0;
                  ev.relatedTarget = null;
                  ev._constructed = true;
                  target.dispatchEvent(ev);
              }
          };
  
          me.getTouchAction = function (eventPassthrough, addPinch) {
              var touchAction = 'none';
              if (eventPassthrough === 'vertical') {
                  touchAction = 'pan-y';
              } else if (eventPassthrough === 'horizontal') {
                  touchAction = 'pan-x';
              }
              if (addPinch && touchAction != 'none') {
                  // add pinch-zoom support if the browser supports it, but if not (eg. Chrome <55) do nothing
                  touchAction += ' pinch-zoom';
              }
              return touchAction;
          };
  
          me.getRect = function (el) {
              if (el instanceof SVGElement) {
                  var rect = el.getBoundingClientRect();
                  return {
                      top: rect.top,
                      left: rect.left,
                      width: rect.width,
                      height: rect.height
                  };
              } else {
                  return {
                      top: el.offsetTop,
                      left: el.offsetLeft,
                      width: el.offsetWidth,
                      height: el.offsetHeight
                  };
              }
          };
  
          return me;
      })();
  
      function IScroll(el, options) {
          this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
          this.scroller = this.wrapper.children[0];
          this.scrollerStyle = this.scroller.style;		// cache style for better performance
  
          this.options = {
  
  // INSERT POINT: OPTIONS
              disablePointer: !utils.hasPointer,
              disableTouch: utils.hasPointer || !utils.hasTouch,
              disableMouse: utils.hasPointer || utils.hasTouch,
              startX: 0,
              startY: 0,
              scrollY: true,
              directionLockThreshold: 5,
              momentum: true,
  
              bounce: true,
              bounceTime: 600,
              bounceEasing: '',
  
              preventDefault: true,
              preventDefaultException: {tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/},
  
              HWCompositing: true,
              useTransition: true,
              useTransform: true,
              bindToWrapper: typeof window.onmousedown === "undefined"
          };
  
          for (var i in options) {
              this.options[i] = options[i];
          }
  
          // Normalize options
          this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : '';
  
          this.options.useTransition = utils.hasTransition && this.options.useTransition;
          this.options.useTransform = utils.hasTransform && this.options.useTransform;
  
          this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough;
          this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;
  
          // If you want eventPassthrough I have to lock one of the axes
          this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY;
          this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX;
  
          // With eventPassthrough we also need lockDirection mechanism
          this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
          this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;
  
          this.options.bounceEasing = typeof this.options.bounceEasing == 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;
  
          this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;
  
          if (this.options.tap === true) {
              this.options.tap = 'tap';
          }
  
          // https://github.com/cubiq/iscroll/issues/1029
          if (!this.options.useTransition && !this.options.useTransform) {
              if (!(/relative|absolute/i).test(this.scrollerStyle.position)) {
                  this.scrollerStyle.position = "relative";
              }
          }
  
  // INSERT POINT: NORMALIZATION
  
          // Some defaults
          this.x = 0;
          this.y = 0;
          this.directionX = 0;
          this.directionY = 0;
          this._events = {};
  
  // INSERT POINT: DEFAULTS
  
          this._init();
          this.refresh();
  
          this.scrollTo(this.options.startX, this.options.startY);
          this.enable();
      }
  
      IScroll.prototype = {
          version: '5.2.0-snapshot',
  
          _init: function () {
              this._initEvents();
  
  // INSERT POINT: _init
  
          },
  
          destroy: function () {
              this._initEvents(true);
              clearTimeout(this.resizeTimeout);
              this.resizeTimeout = null;
              this._execEvent('destroy');
          },
  
          _transitionEnd: function (e) {
              if (e.target != this.scroller || !this.isInTransition) {
                  return;
              }
  
              this._transitionTime();
              if (!this.resetPosition(this.options.bounceTime)) {
                  this.isInTransition = false;
                  this._execEvent('scrollEnd');
              }
          },
  
          _start: function (e) {
              // React to left mouse button only
              if (utils.eventType[e.type] != 1) {
                  // for button property
                  // http://unixpapa.com/js/mouse.html
                  var button;
                  if (!e.which) {
                      /* IE case */
                      button = (e.button < 2) ? 0 :
                          ((e.button == 4) ? 1 : 2);
                  } else {
                      /* All others */
                      button = e.button;
                  }
                  if (button !== 0) {
                      return;
                  }
              }
  
              if (!this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated)) {
                  return;
              }
  
              if (this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException)) {
                  e.preventDefault();
              }
  
              var point = e.touches ? e.touches[0] : e,
                  pos;
  
              this.initiated = utils.eventType[e.type];
              this.moved = false;
              this.distX = 0;
              this.distY = 0;
              this.directionX = 0;
              this.directionY = 0;
              this.directionLocked = 0;
  
              this.startTime = utils.getTime();
  
              if (this.options.useTransition && this.isInTransition) {
                  this._transitionTime();
                  this.isInTransition = false;
                  pos = this.getComputedPosition();
                  this._translate(Math.round(pos.x), Math.round(pos.y));
                  this._execEvent('scrollEnd');
              } else if (!this.options.useTransition && this.isAnimating) {
                  this.isAnimating = false;
                  this._execEvent('scrollEnd');
              }
  
              this.startX = this.x;
              this.startY = this.y;
              this.absStartX = this.x;
              this.absStartY = this.y;
              this.pointX = point.pageX;
              this.pointY = point.pageY;
  
              this._execEvent('beforeScrollStart');
          },
  
          _move: function (e) {
              if (!this.enabled || utils.eventType[e.type] !== this.initiated) {
                  return;
              }
  
              if (this.options.preventDefault) {	// increases performance on Android? TODO: check!
                  e.preventDefault();
              }
  
              var point = e.touches ? e.touches[0] : e,
                  deltaX = point.pageX - this.pointX,
                  deltaY = point.pageY - this.pointY,
                  timestamp = utils.getTime(),
                  newX, newY,
                  absDistX, absDistY;
  
              this.pointX = point.pageX;
              this.pointY = point.pageY;
  
              this.distX += deltaX;
              this.distY += deltaY;
              absDistX = Math.abs(this.distX);
              absDistY = Math.abs(this.distY);
  
              // We need to move at least 10 pixels for the scrolling to initiate
              if (timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10)) {
                  return;
              }
  
              // If you are scrolling in one direction lock the other
              if (!this.directionLocked && !this.options.freeScroll) {
                  if (absDistX > absDistY + this.options.directionLockThreshold) {
                      this.directionLocked = 'h';		// lock horizontally
                  } else if (absDistY >= absDistX + this.options.directionLockThreshold) {
                      this.directionLocked = 'v';		// lock vertically
                  } else {
                      this.directionLocked = 'n';		// no lock
                  }
              }
  
              if (this.directionLocked == 'h') {
                  if (this.options.eventPassthrough == 'vertical') {
                      e.preventDefault();
                  } else if (this.options.eventPassthrough == 'horizontal') {
                      this.initiated = false;
                      return;
                  }
  
                  deltaY = 0;
              } else if (this.directionLocked == 'v') {
                  if (this.options.eventPassthrough == 'horizontal') {
                      e.preventDefault();
                  } else if (this.options.eventPassthrough == 'vertical') {
                      this.initiated = false;
                      return;
                  }
  
                  deltaX = 0;
              }
  
              deltaX = this.hasHorizontalScroll ? deltaX : 0;
              deltaY = this.hasVerticalScroll ? deltaY : 0;
  
              newX = this.x + deltaX;
              newY = this.y + deltaY;
  
              // Slow down if outside of the boundaries
              if (newX > 0 || newX < this.maxScrollX) {
                  newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
              }
              if (newY > 0 || newY < this.maxScrollY) {
                  newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
              }
  
              this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
              this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
  
              if (!this.moved) {
                  this._execEvent('scrollStart');
              }
  
              this.moved = true;
  
              this._translate(newX, newY);
  
              /* REPLACE START: _move */
  
              if (timestamp - this.startTime > 300) {
                  this.startTime = timestamp;
                  this.startX = this.x;
                  this.startY = this.y;
              }
  
              /* REPLACE END: _move */
  
          },
  
          _end: function (e) {
              if (!this.enabled || utils.eventType[e.type] !== this.initiated) {
                  return;
              }
  
              if (this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException)) {
                  e.preventDefault();
              }
  
              var point = e.changedTouches ? e.changedTouches[0] : e,
                  momentumX,
                  momentumY,
                  duration = utils.getTime() - this.startTime,
                  newX = Math.round(this.x),
                  newY = Math.round(this.y),
                  distanceX = Math.abs(newX - this.startX),
                  distanceY = Math.abs(newY - this.startY),
                  time = 0,
                  easing = '';
  
              this.isInTransition = 0;
              this.initiated = 0;
              this.endTime = utils.getTime();
  
              // reset if we are outside of the boundaries
              if (this.resetPosition(this.options.bounceTime)) {
                  return;
              }
  
              this.scrollTo(newX, newY);	// ensures that the last position is rounded
  
              // we scrolled less than 10 pixels
              if (!this.moved) {
                  if (this.options.tap) {
                      utils.tap(e, this.options.tap);
                  }
  
                  if (this.options.click) {
                      utils.click(e);
                  }
  
                  this._execEvent('scrollCancel');
                  return;
              }
  
              if (this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100) {
                  this._execEvent('flick');
                  return;
              }
  
              // start momentum animation if needed
              if (this.options.momentum && duration < 300) {
                  momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options.deceleration) : {
                      destination: newX,
                      duration: 0
                  };
                  momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options.deceleration) : {
                      destination: newY,
                      duration: 0
                  };
                  newX = momentumX.destination;
                  newY = momentumY.destination;
                  time = Math.max(momentumX.duration, momentumY.duration);
                  this.isInTransition = 1;
              }
  
  // INSERT POINT: _end
  
              if (newX != this.x || newY != this.y) {
                  // change easing function when scroller goes out of the boundaries
                  if (newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY) {
                      easing = utils.ease.quadratic;
                  }
  
                  this.scrollTo(newX, newY, time, easing);
                  return;
              }
  
              this._execEvent('scrollEnd');
          },
  
          _resize: function () {
              var that = this;
  
              clearTimeout(this.resizeTimeout);
  
              this.resizeTimeout = setTimeout(function () {
                  that.refresh();
              }, this.options.resizePolling);
          },
  
          resetPosition: function (time) {
              var x = this.x,
                  y = this.y;
  
              time = time || 0;
  
              if (!this.hasHorizontalScroll || this.x > 0) {
                  x = 0;
              } else if (this.x < this.maxScrollX) {
                  x = this.maxScrollX;
              }
  
              if (!this.hasVerticalScroll || this.y > 0) {
                  y = 0;
              } else if (this.y < this.maxScrollY) {
                  y = this.maxScrollY;
              }
  
              if (x == this.x && y == this.y) {
                  return false;
              }
  
              this.scrollTo(x, y, time, this.options.bounceEasing);
  
              return true;
          },
  
          disable: function () {
              this.enabled = false;
          },
  
          enable: function () {
              this.enabled = true;
          },
  
          refresh: function () {
              utils.getRect(this.wrapper);		// Force reflow
  
              this.wrapperWidth = this.wrapper.clientWidth;
              this.wrapperHeight = this.wrapper.clientHeight;
  
              var rect = utils.getRect(this.scroller);
              /* REPLACE START: refresh */
  
              this.scrollerWidth = rect.width;
              this.scrollerHeight = rect.height;
  
              this.maxScrollX = this.wrapperWidth - this.scrollerWidth;
              this.maxScrollY = this.wrapperHeight - this.scrollerHeight;
  
              /* REPLACE END: refresh */
  
              this.hasHorizontalScroll = this.options.scrollX && this.maxScrollX < 0;
              this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < 0;
  
              if (!this.hasHorizontalScroll) {
                  this.maxScrollX = 0;
                  this.scrollerWidth = this.wrapperWidth;
              }
  
              if (!this.hasVerticalScroll) {
                  this.maxScrollY = 0;
                  this.scrollerHeight = this.wrapperHeight;
              }
  
              this.endTime = 0;
              this.directionX = 0;
              this.directionY = 0;
  
              if (utils.hasPointer && !this.options.disablePointer) {
                  // The wrapper should have `touchAction` property for using pointerEvent.
                  this.wrapper.style[utils.style.touchAction] = utils.getTouchAction(this.options.eventPassthrough, true);
  
                  // case. not support 'pinch-zoom'
                  // https://github.com/cubiq/iscroll/issues/1118#issuecomment-270057583
                  if (!this.wrapper.style[utils.style.touchAction]) {
                      this.wrapper.style[utils.style.touchAction] = utils.getTouchAction(this.options.eventPassthrough, false);
                  }
              }
              this.wrapperOffset = utils.offset(this.wrapper);
  
              this._execEvent('refresh');
  
              this.resetPosition();
  
  // INSERT POINT: _refresh
  
          },
  
          on: function (type, fn) {
              if (!this._events[type]) {
                  this._events[type] = [];
              }
  
              this._events[type].push(fn);
          },
  
          off: function (type, fn) {
              if (!this._events[type]) {
                  return;
              }
  
              var index = this._events[type].indexOf(fn);
  
              if (index > -1) {
                  this._events[type].splice(index, 1);
              }
          },
  
          _execEvent: function (type) {
              if (!this._events[type]) {
                  return;
              }
  
              var i = 0,
                  l = this._events[type].length;
  
              if (!l) {
                  return;
              }
  
              for (; i < l; i++) {
                  this._events[type][i].apply(this, [].slice.call(arguments, 1));
              }
          },
  
          scrollBy: function (x, y, time, easing) {
              x = this.x + x;
              y = this.y + y;
              time = time || 0;
  
              this.scrollTo(x, y, time, easing);
          },
  
          scrollTo: function (x, y, time, easing) {
              easing = easing || utils.ease.circular;
  
              this.isInTransition = this.options.useTransition && time > 0;
              var transitionType = this.options.useTransition && easing.style;
              if (!time || transitionType) {
                  if (transitionType) {
                      this._transitionTimingFunction(easing.style);
                      this._transitionTime(time);
                  }
                  this._translate(x, y);
              } else {
                  this._animate(x, y, time, easing.fn);
              }
          },
  
          scrollToElement: function (el, time, offsetX, offsetY, easing) {
              el = el.nodeType ? el : this.scroller.querySelector(el);
  
              if (!el) {
                  return;
              }
  
              var pos = utils.offset(el);
  
              pos.left -= this.wrapperOffset.left;
              pos.top -= this.wrapperOffset.top;
  
              // if offsetX/Y are true we center the element to the screen
              var elRect = utils.getRect(el);
              var wrapperRect = utils.getRect(this.wrapper);
              if (offsetX === true) {
                  offsetX = Math.round(elRect.width / 2 - wrapperRect.width / 2);
              }
              if (offsetY === true) {
                  offsetY = Math.round(elRect.height / 2 - wrapperRect.height / 2);
              }
  
              pos.left -= offsetX || 0;
              pos.top -= offsetY || 0;
  
              pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
              pos.top = pos.top > 0 ? 0 : pos.top < this.maxScrollY ? this.maxScrollY : pos.top;
  
              time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x - pos.left), Math.abs(this.y - pos.top)) : time;
  
              this.scrollTo(pos.left, pos.top, time, easing);
          },
  
          _transitionTime: function (time) {
              if (!this.options.useTransition) {
                  return;
              }
              time = time || 0;
              var durationProp = utils.style.transitionDuration;
              if (!durationProp) {
                  return;
              }
  
              this.scrollerStyle[durationProp] = time + 'ms';
  
              if (!time && utils.isBadAndroid) {
                  this.scrollerStyle[durationProp] = '0.0001ms';
                  // remove 0.0001ms
                  var self = this;
                  rAF(function () {
                      if (self.scrollerStyle[durationProp] === '0.0001ms') {
                          self.scrollerStyle[durationProp] = '0s';
                      }
                  });
              }
  
  // INSERT POINT: _transitionTime
  
          },
  
          _transitionTimingFunction: function (easing) {
              this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
  
  // INSERT POINT: _transitionTimingFunction
  
          },
  
          _translate: function (x, y) {
              if (this.options.useTransform) {
  
                  /* REPLACE START: _translate */
  
                  this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
  
                  /* REPLACE END: _translate */
  
              } else {
                  x = Math.round(x);
                  y = Math.round(y);
                  this.scrollerStyle.left = x + 'px';
                  this.scrollerStyle.top = y + 'px';
              }
  
              this.x = x;
              this.y = y;
  
  // INSERT POINT: _translate
  
          },
  
          _initEvents: function (remove) {
              var eventType = remove ? utils.removeEvent : utils.addEvent,
                  target = this.options.bindToWrapper ? this.wrapper : window;
  
              eventType(window, 'orientationchange', this);
              eventType(window, 'resize', this);
  
              if (this.options.click) {
                  eventType(this.wrapper, 'click', this, true);
              }
  
              if (!this.options.disableMouse) {
                  eventType(this.wrapper, 'mousedown', this);
                  eventType(target, 'mousemove', this);
                  eventType(target, 'mousecancel', this);
                  eventType(target, 'mouseup', this);
              }
  
              if (utils.hasPointer && !this.options.disablePointer) {
                  eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
                  eventType(target, utils.prefixPointerEvent('pointermove'), this);
                  eventType(target, utils.prefixPointerEvent('pointercancel'), this);
                  eventType(target, utils.prefixPointerEvent('pointerup'), this);
              }
  
              if (utils.hasTouch && !this.options.disableTouch) {
                  eventType(this.wrapper, 'touchstart', this);
                  eventType(target, 'touchmove', this);
                  eventType(target, 'touchcancel', this);
                  eventType(target, 'touchend', this);
              }
  
              eventType(this.scroller, 'transitionend', this);
              eventType(this.scroller, 'webkitTransitionEnd', this);
              eventType(this.scroller, 'oTransitionEnd', this);
              eventType(this.scroller, 'MSTransitionEnd', this);
          },
  
          getComputedPosition: function () {
              var matrix = window.getComputedStyle(this.scroller, null),
                  x, y;
  
              if (this.options.useTransform) {
                  matrix = matrix[utils.style.transform].split(')')[0].split(', ');
                  x = +(matrix[12] || matrix[4]);
                  y = +(matrix[13] || matrix[5]);
              } else {
                  x = +matrix.left.replace(/[^-\d.]/g, '');
                  y = +matrix.top.replace(/[^-\d.]/g, '');
              }
  
              return {x: x, y: y};
          },
          _animate: function (destX, destY, duration, easingFn) {
              var that = this,
                  startX = this.x,
                  startY = this.y,
                  startTime = utils.getTime(),
                  destTime = startTime + duration;
  
              function step() {
                  var now = utils.getTime(),
                      newX, newY,
                      easing;
  
                  if (now >= destTime) {
                      that.isAnimating = false;
                      that._translate(destX, destY);
  
                      if (!that.resetPosition(that.options.bounceTime)) {
                          that._execEvent('scrollEnd');
                      }
  
                      return;
                  }
  
                  now = (now - startTime) / duration;
                  easing = easingFn(now);
                  newX = (destX - startX) * easing + startX;
                  newY = (destY - startY) * easing + startY;
                  that._translate(newX, newY);
  
                  if (that.isAnimating) {
                      rAF(step);
                  }
              }
  
              this.isAnimating = true;
              step();
          },
          handleEvent: function (e) {
              switch (e.type) {
                  case 'touchstart':
                  case 'pointerdown':
                  case 'MSPointerDown':
                  case 'mousedown':
                      this._start(e);
                      break;
                  case 'touchmove':
                  case 'pointermove':
                  case 'MSPointerMove':
                  case 'mousemove':
                      this._move(e);
                      break;
                  case 'touchend':
                  case 'pointerup':
                  case 'MSPointerUp':
                  case 'mouseup':
                  case 'touchcancel':
                  case 'pointercancel':
                  case 'MSPointerCancel':
                  case 'mousecancel':
                      this._end(e);
                      break;
                  case 'orientationchange':
                  case 'resize':
                      this._resize();
                      break;
                  case 'transitionend':
                  case 'webkitTransitionEnd':
                  case 'oTransitionEnd':
                  case 'MSTransitionEnd':
                      this._transitionEnd(e);
                      break;
                  case 'wheel':
                  case 'DOMMouseScroll':
                  case 'mousewheel':
                      this._wheel(e);
                      break;
                  case 'keydown':
                      this._key(e);
                      break;
                  case 'click':
                      if (this.enabled && !e._constructed) {
                          e.preventDefault();
                          e.stopPropagation();
                      }
                      break;
              }
          }
      };
      IScroll.utils = utils;
  
      if (typeof module != 'undefined' && module.exports) {
          module.exports = IScroll;
      } else if (typeof define == 'function' && define.amd) {
          define(function () {
              return IScroll;
          });
      } else {
          window.IScroll = IScroll;
      }
  
  })(window, document, Math);
  