'use strict';

Object.defineProperty(exports, "__esModule", {
	value: true
});

var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray');

var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);

var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');

var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

var _utils = require('./utils.js');

var _Indicator = require('./Indicator.js');

var _Indicator2 = _interopRequireDefault(_Indicator);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var DEFAULT_OPTIONS = {
	// 默认位置
	startX: 0,
	startY: 0,
	scrollX: false,
	scrollY: true,
	/**
  * 主要在上下左右滚动都生效时使用，可以向任意方向滚动。
  */
	freeScroll: false,
	directionLockThreshold: 5,
	/**
  * 如：使用横轴滚动时，如想使用系统立轴滚动并在横轴上生效，请开启
  */
	eventPassthrough: '', // horizontal | vertical | ''
	/**
  * 是否启用click事件。建议关闭此选项并启用自定义的tap事件（options.tap）
  */
	click: false,
	tap: false,
	/**
  * 是否启用弹力动画效果，关掉可以加速
  */
	bounce: true,
	/**
  * 弹力动画持续的毫秒数	
  */
	bounceTime: 700,
	/**
  * 是否开启动量动画，关闭可以提升效率。
  */
	momentum: true,
	/**
  * 两次差值时间小于该值
  */
	momentumLimitTime: 300,
	/**
  * 移动超过规定像素启用滚动
  */
	momentumLimitDistance: 15,
	swipeTime: 2500,
	swipeBounceTime: 500,
	/**
  * 滚动动量减速越大越快，建议不大于0.01
  */
	deceleration: 0.001,
	flickLimitTime: 200,
	flickLimitDistance: 100,
	resizePolling: 60,
	/**
  * probeType：0  ？
  * probeType：1  滚动不繁忙的时候触发
  * probeType：2  滚动时每隔一定时间触发
  * probeType：3  每滚动一像素触发一次
  */
	probeType: 0,
	/**
  * 是否屏蔽默认事件
  */
	preventDefault: true,
	preventDefaultException: {
		tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/
	},
	/**
  * 是否启用硬件加速	
  */
	HWCompositing: true,
	/**
  * 是否使用CSS3的Transition属性，否则使用requestAnimationFram代替
  */
	useTransition: true,
	/**
  * 是否使用CSS3的Transform属性
  */
	useTransform: true,
	/**
  * 光标、触摸超出容器时，是否停止滚动
  */
	bindToWrapper: false,
	/**
  * 是否关闭鼠标事件探测。如知道运行在哪个平台，可以开启它来加速
  */
	disableMouse: _utils.hasTouch,
	/**
  * 是否关闭触摸事件探测。如知道运行在哪个平台，可以开启它来加速
  */
	disableTouch: !_utils.hasTouch,
	/**
  * for picker
  * wheel: {
  *   selectedIndex: 0,
  *   rotate: 25,
  *   adjustTime: 400
  * }
  */
	wheel: false,
	/**
  * for slide
  * snap: {
  *   loop: false,
  *   el: domEl,
  *   threshold: 0.1,
  *   stepX: 100,
  *   stepY: 100,
  *   listenFlick: true
  * }
  */
	snap: false,
	/**
  * for scrollbar
  * scrollbar: {
  *   fade: true
  * }
  */
	scrollbar: false,
	/**
  * for pull down and refresh
  * pullDownRefresh: {
  *   threshold: 50,
  *   stop: 20
  * }
  */
	pullDownRefresh: false,
	/**
  * for pull up and load
  * pullUpLoad: {
  *   threshold: 50
  * }
  */
	pullUpLoad: false
};

var Core = function () {
	function Core(el) {
		var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
		(0, _classCallCheck3.default)(this, Core);

		// 容器
		this.wrapper = typeof el === 'string' ? document.querySelector(el) : el;
		// 操作区域节点
		this.scroller = this.wrapper.children[0];
		// 操作区域默认样式 CSSStyleDeclaration
		this.scrollerStyle = this.scroller.style;
		// 设置默认属性
		this.setOptions(opts);

		// 自定义事件
		this.events = {};
		// 当前位置
		this.x = 0;
		this.y = 0;
		// 上一次的滚动方向(-1 下/右, 0 保持原状, 1 上/左)
		this.directionX = 0;
		this.directionY = 0;
		// 添加事件
		this.addDOMEvents();

		// 扩展
		this.initExtFeatures();

		// 刷新scroll
		this.refresh();

		if (!this.options.snap) {
			this.scrollTo(this.options.startX, this.options.startY);
		}
		// 启用
		this.enable();
	}
	// - start init prototype 


	(0, _createClass3.default)(Core, [{
		key: 'enable',
		value: function enable() {
			this.enabled = true;
		}
	}, {
		key: 'disable',
		value: function disable() {
			this.enabled = false;
		}
		/**
   * @private
   * 设置属性值
   */

	}, {
		key: 'setOptions',
		value: function setOptions(opts) {
			this.options = Object.assign({}, DEFAULT_OPTIONS, opts);
			// 是否启用硬件加速
			this.translateZ = this.options.HWCompositing && _utils.hasPerspective ? ' translateZ(0)' : '';
			// 是否使用CSS3的Transition属性，否则使用requestAnimationFram代替
			this.options.useTransition = this.options.useTransition && _utils.hasTransition;
			// 是否使用CSS3的Transform属性
			this.options.useTransform = this.options.useTransform && _utils.hasTransform;

			this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;

			// 使用横轴滚动时，如想使用系统立轴滚动并在横轴上生效，请开启。
			this.options.scrollX = this.options.eventPassthrough === 'horizontal' ? false : this.options.scrollX;
			this.options.scrollY = this.options.eventPassthrough === 'vertical' ? false : this.options.scrollY;

			// 主要在上下左右滚动都生效时使用，可以向任意方向滚动。
			this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
			this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;

			if (this.options.tap === true) {
				this.options.tap = 'tap';
			}
		}
		/**
   * @private
   * 添加事件
   */

	}, {
		key: 'addDOMEvents',
		value: function addDOMEvents() {
			this.operateDOMEvents(_utils.addEvent);
		}
		/**
   * @private
   * 移除事件
   */

	}, {
		key: 'removeDOMEvents',
		value: function removeDOMEvents() {
			this.operateDOMEvents(_utils.removeEvent);
		}
		/**
   * @private
   * 事件操作
   */

	}, {
		key: 'operateDOMEvents',
		value: function operateDOMEvents(eventOperation) {
			var target = this.options.bindToWrapper ? this.wrapper : window;
			eventOperation(window, 'orientationchange', this);
			eventOperation(window, 'resize', this);

			if (this.options.click) {
				eventOperation(this.wrapper, 'click', this, true);
			}

			if (!this.options.disableMouse) {
				eventOperation(this.wrapper, 'mousedown', this);
				eventOperation(target, 'mousemove', this);
				eventOperation(target, 'mousecancel', this);
				eventOperation(target, 'mouseup', this);
			}

			if (_utils.hasTouch && !this.options.disableTouch) {
				eventOperation(this.wrapper, 'touchstart', this);
				eventOperation(target, 'touchmove', this);
				eventOperation(target, 'touchcancel', this);
				eventOperation(target, 'touchend', this);
			}

			eventOperation(this.scroller, _utils.style.transitionEnd, this);
		}
	}, {
		key: 'handleEvent',
		value: function handleEvent(e) {
			switch (e.type) {
				case 'touchstart':
				case 'mousedown':
					this.start(e);
					break;
				case 'touchmove':
				case 'mousemove':
					this.move(e);
					break;
				case 'touchend':
				case 'mouseup':
				case 'touchcancel':
				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 'click':
					if (this.enabled && !e._constructed) {
						if (!(0, _utils.preventDefaultException)(e.target, this.options.preventDefaultException)) {
							e.preventDefault();
						}
						e.stopPropagation();
					}
					break;
			}
		}
		/**
   * @private
   */

	}, {
		key: 'initExtFeatures',
		value: function initExtFeatures() {
			if (this.options.snap) {
				this.initSnap();
			}
			if (this.options.scrollbar) {
				this.initScrollbar();
			}
			if (this.options.pullUpLoad) {
				this.initPullUp();
			}
			if (this.options.pullDownRefresh) {
				this.initPullDown();
			}
		}
		/**
   * @public
   * 刷新scroll
   */

	}, {
		key: 'refresh',
		value: function refresh() {
			// 容器宽高
			var wrapperRect = (0, _utils.getRect)(this.wrapper);
			this.wrapperWidth = wrapperRect.width;
			this.wrapperHeight = wrapperRect.height;
			// 容器内 内容宽高
			var scrollerRect = (0, _utils.getRect)(this.scroller);
			this.scrollerWidth = scrollerRect.width;
			this.scrollerHeight = scrollerRect.height;

			var wheel = this.options.wheel;
			if (wheel) {
				this.items = this.scroller.children;
				this.options.itemHeight = this.itemHeight = this.items.length ? this.scrollerHeight / this.items.length : 0;
				if (this.selectedIndex === undefined) {
					this.selectedIndex = wheel.selectedIndex;
				}
				this.options.startY = -this.selectedIndex * this.itemHeight;
				this.maxScrollX = 0;
				this.maxScrollY = -this.itemHeight * (this.items.length - 1);
			} else {
				// 极限距离，相当于scrollLeft/scrollTop最大值 负值
				this.maxScrollX = this.wrapperWidth - this.scrollerWidth;
				this.maxScrollY = this.wrapperHeight - this.scrollerHeight;
			}
			// 是否有横向滚动条
			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;
			// 计算容器所在位置偏移值{left: Number?, top: Number?}负值
			this.wrapperOffset = (0, _utils.offset)(this.wrapper);

			this.trigger('refresh');

			this.resetPosition();
		}
		// - end init prototype 

		// - start event prototype 

	}, {
		key: 'on',
		value: function on(type, fn) {
			var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this;

			if (!this.events[type]) {
				this.events[type] = [];
			}

			this.events[type].push([fn, context]);
		}
	}, {
		key: 'once',
		value: function once(type, fn) {
			var _this = this,
			    _arguments = arguments;

			var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this;

			var fired = false;

			var magic = function magic() {
				_this.off(type, magic);

				if (!fired) {
					fired = true;
					fn.apply(context, _arguments);
				}
			};
			// 将参数中的回调函数挂载在magic对象的fn属性上,为了执行off方法的时候，暴露对应的函数方法
			magic.fn = fn;

			this.on(type, magic);
		}
	}, {
		key: 'off',
		value: function off(type, fn) {
			var events = this.events[type];
			if (!events) {
				return;
			}

			var count = events.length;
			while (count--) {
				// 移除通过on或者once绑定的回调函数
				if (events[count][0] === fn || events[count][0] && events[count][0].fn === fn) {
					events[count][0] = undefined;
				}
			}
		}
	}, {
		key: 'trigger',
		value: function trigger(type) {
			var events = this.events[type];
			if (!events) {
				return;
			}

			var len = events.length;
			var eventsCopy = [].concat((0, _toConsumableArray3.default)(events));
			for (var i = 0; i < len; i++) {
				var event = eventsCopy[i];

				var _event = (0, _slicedToArray3.default)(event, 2),
				    fn = _event[0],
				    context = _event[1];

				if (fn) {
					fn.apply(context, [].slice.call(arguments, 1));
				}
			}
		}
		// - end event prototype 
		// - start core prototype
		/**
   * @private
   */

	}, {
		key: 'start',
		value: function start(e) {
			var __eventType = _utils.eventType[e.type];
			// 是否是触摸事件
			if (__eventType !== _utils.TOUCH_EVENT) {
				// PC事件
				if (e.button !== 0) {
					// 0 左键，1 滚轮按下，2 右键
					return;
				}
			}
			if (!this.enabled || this.destroyed || this.initiated && this.initiated !== __eventType) {
				// 未启用，已销毁，事件类型改变
				return;
			}
			this.initiated = __eventType;

			if (this.options.preventDefault && !(0, _utils.preventDefaultException)(e.target, this.options.preventDefaultException)) {
				// 去除默认事件
				e.preventDefault();
			}
			// init
			this.moved = false;
			this.distX = 0;
			this.distY = 0;
			this.directionX = 0;
			this.directionY = 0;
			this.movingDirectionX = 0;
			this.movingDirectionY = 0;
			this.directionLocked = 0;
			// 过渡时间： 0
			this.transitionTime();
			// 开始时间
			this.startTime = (0, _utils.getNow)();

			if (this.options.wheel) {
				this.target = e.target;
			}

			this.stop();
			// 单个手指
			var point = e.touches ? e.touches[0] : e;
			// 开始的位置， this.x 当前位置
			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.trigger('beforeScrollStart');
		}
		/**
   * @private
   */

	}, {
		key: 'move',
		value: function move(e) {
			if (!this.enabled || this.destroyed || _utils.eventType[e.type] !== this.initiated) {
				// 未启用，已销毁，事件类型改变
				return;
			}

			if (this.options.preventDefault) {
				// 去除默认事件， 可阻止滚动
				e.preventDefault();
			}
			// 单个手指
			var point = e.touches ? e.touches[0] : e;
			// 距离上一次move相对移动位置
			var deltaX = point.pageX - this.pointX;
			var deltaY = point.pageY - this.pointY;
			this.pointX = point.pageX;
			this.pointY = point.pageY;
			// 从start开始move后的位置
			this.distX += deltaX;
			this.distY += deltaY;

			var absDistX = Math.abs(this.distX);
			var absDistY = Math.abs(this.distY);

			var timestamp = (0, _utils.getNow)();

			if (timestamp - this.endTime > this.options.momentumLimitTime && absDistY < this.options.momentumLimitDistance && absDistX < this.options.momentumLimitDistance) {
				// 时间差大于规定值（move过程this.endTime = 0），移动超过规定像素启用滚动
				return;
			}

			// 如果不是自由滚动，将滚动锁定在一个方向上
			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;
			this.movingDirectionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
			this.movingDirectionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
			// 新的坐标位置
			var newX = this.x + deltaX;
			var newY = this.y + deltaY;

			// 超出横轴临界值
			if (newX > 0 || newX < this.maxScrollX) {
				if (this.options.bounce) {
					newX = this.x + deltaX / 3;
				} else {
					newX = newX > 0 ? 0 : this.maxScrollX;
				}
			}
			// 超出立轴临界值
			if (newY > 0 || newY < this.maxScrollY) {
				if (this.options.bounce) {
					newY = this.y + deltaY / 3;
				} else {
					newY = newY > 0 ? 0 : this.maxScrollY;
				}
			}

			if (!this.moved) {
				this.moved = true;
				// 开始滚动
				this.trigger('scrollStart');
			}

			this.translate(newX, newY);

			if (timestamp - this.startTime > this.options.momentumLimitTime) {
				this.startTime = timestamp;
				this.startX = this.x;
				this.startY = this.y;

				if (this.options.probeType === 1) {
					this.trigger('scroll', {
						x: this.x,
						y: this.y
					});
				}
			}

			if (this.options.probeType > 1) {
				this.trigger('scroll', {
					x: this.x,
					y: this.y
				});
			}

			var scrollLeft = document.documentElement.scrollLeft || window.pageXOffset || document.body.scrollLeft;
			var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;

			var pX = this.pointX - scrollLeft;
			var pY = this.pointY - scrollTop;

			if (pX > document.documentElement.clientWidth - this.options.momentumLimitDistance || pX < this.options.momentumLimitDistance || pY < this.options.momentumLimitDistance || pY > document.documentElement.clientHeight - this.options.momentumLimitDistance) {
				this.end(e);
			}
		}
		/**
   * @private
   */

	}, {
		key: 'end',
		value: function end(e) {
			if (!this.enabled || this.destroyed || _utils.eventType[e.type] !== this.initiated) {
				// 未启用，已销毁，事件类型改变
				return;
			}
			this.initiated = false;

			if (this.options.preventDefault && !(0, _utils.preventDefaultException)(e.target, this.options.preventDefaultException)) {
				e.preventDefault();
			}

			this.trigger('touchEnd', {
				x: this.x,
				y: this.y
			});

			// 下拉刷新
			if (this.options.pullDownRefresh && this.checkPullDown()) {
				return;
			}

			// reset位置，如果出去边界位置
			if (this.resetPosition(this.options.bounceTime, _utils.ease.bounce)) {
				return;
			}
			this.isInTransition = false;
			// 四舍五入
			var newX = Math.round(this.x);
			var newY = Math.round(this.y);

			// 滚动超过 15 px
			if (!this.moved) {
				if (this.options.wheel) {
					if (this.target && this.target.className === 'wheel-scroll') {
						var index = Math.abs(Math.round(newY / this.itemHeight));
						var _offset = Math.round((this.pointY + (0, _utils.offset)(this.target).top - this.itemHeight / 2) / this.itemHeight);
						this.target = this.items[index + _offset];
					}
					this.scrollToElement(this.target, this.options.wheel.adjustTime || 400, true, true, _utils.ease.swipe);
				} else {
					if (this.options.tap) {
						(0, _utils.tap)(e, this.options.tap);
					}

					if (this.options.click) {
						(0, _utils.click)(e);
					}
				}
				// 初始化滚动后又取消
				this.trigger('scrollCancel');
				return;
			}

			this.scrollTo(newX, newY);

			var deltaX = newX - this.absStartX;
			var deltaY = newY - this.absStartY;
			this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
			this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;

			this.endTime = (0, _utils.getNow)();

			var duration = this.endTime - this.startTime;
			var absDistX = Math.abs(newX - this.startX);
			var absDistY = Math.abs(newY - this.startY);

			// 轻击屏幕
			if (this.events.flick && duration < this.options.flickLimitTime && absDistX < this.options.flickLimitDistance && absDistY < this.options.flickLimitDistance) {
				this.trigger('flick');
				return;
			}

			var time = 0;
			// 是否开启动量动画，关闭可以提升效率。
			if (this.options.momentum && duration < this.options.momentumLimitTime && (absDistY > this.options.momentumLimitDistance || absDistX > this.options.momentumLimitDistance)) {
				var momentumX = this.hasHorizontalScroll ? (0, _utils.momentum)(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options) : { destination: newX, duration: 0 };
				var momentumY = this.hasVerticalScroll ? (0, _utils.momentum)(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options) : { destination: newY, duration: 0 };
				newX = momentumX.destination;
				newY = momentumY.destination;
				time = Math.max(momentumX.duration, momentumY.duration);
				this.isInTransition = 1;
			} else {
				if (this.options.wheel) {
					newY = Math.round(newY / this.itemHeight) * this.itemHeight;
					time = this.options.wheel.adjustTime || 400;
				}
			}

			var easing = _utils.ease.swipe;
			if (this.options.snap) {
				var snap = this.nearestSnap(newX, newY);
				this.currentPage = snap;
				time = this.options.snapSpeed || Math.max(Math.max(Math.min(Math.abs(newX - snap.x), 1000), Math.min(Math.abs(newY - snap.y), 1000)), 300);
				newX = snap.x;
				newY = snap.y;

				this.directionX = 0;
				this.directionY = 0;
				easing = _utils.ease.bounce;
			}

			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.swipeBounce;
				}
				this.scrollTo(newX, newY, time, easing);
				return;
			}

			if (this.options.wheel) {
				this.selectedIndex = Math.abs(this.y / this.itemHeight) | 0;
			}
			// 滚动结束
			this.trigger('scrollEnd', {
				x: this.x,
				y: this.y
			});
		}
		/**
   * @private
   */

	}, {
		key: 'resize',
		value: function resize() {
			var _this2 = this;

			if (!this.enabled) {
				return;
			}

			clearTimeout(this.resizeTimeout);
			this.resizeTimeout = setTimeout(function () {
				_this2.refresh();
			}, this.options.resizePolling);
		}
		/**
   * @private
   */

	}, {
		key: 'startProbe',
		value: function startProbe() {
			var _this3 = this;

			(0, _utils.cancelAnimationFrame)(this.probeTimer);
			this.probeTimer = (0, _utils.requestAnimationFrame)(probe);
			var probe = function probe() {
				if (!_this3.isInTransition) {
					return;
				}
				var pos = _this3.getComputedPosition();
				_this3.trigger('scroll', pos);
				_this3.probeTimer = (0, _utils.requestAnimationFrame)(probe);
			};
		}
		/**
   * @private
   */

	}, {
		key: 'transitionTime',
		value: function transitionTime() {
			var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;

			this.scrollerStyle[_utils.style.transitionDuration] = time + 'ms';

			if (this.options.wheel) {
				for (var i = 0; i < this.items.length; i++) {
					this.items[i].style[_utils.style.transitionDuration] = time + 'ms';
				}
			}

			if (this.indicators) {
				for (var _i = 0; _i < this.indicators.length; _i++) {
					this.indicators[_i].transitionTime(time);
				}
			}
		}
		/**
   * @private
   */

	}, {
		key: 'transitionTimingFunction',
		value: function transitionTimingFunction(easing) {
			this.scrollerStyle[_utils.style.transitionTimingFunction] = easing;

			if (this.options.wheel) {
				for (var i = 0; i < this.items.length; i++) {
					this.items[i].style[_utils.style.transitionTimingFunction] = easing;
				}
			}

			if (this.indicators) {
				for (var _i2 = 0; _i2 < this.indicators.length; _i2++) {
					this.indicators[_i2].transitionTimingFunction(easing);
				}
			}
		}
		/**
   * @private
   */

	}, {
		key: 'transitionEnd',
		value: function transitionEnd(e) {
			if (e.target !== this.scroller || !this.isInTransition) {
				return;
			}

			this.transitionTime();
			if (!this.pulling && !this.resetPosition(this.options.bounceTime, _utils.ease.bounce)) {
				this.isInTransition = false;
				this.trigger('scrollEnd', {
					x: this.x,
					y: this.y
				});
			}
		}
		/**
   * @private
   */

	}, {
		key: 'translate',
		value: function translate(x, y) {
			if (this.options.useTransform) {
				this.scrollerStyle[_utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
			} else {
				x = Math.round(x);
				y = Math.round(y);
				this.scrollerStyle.left = x + 'px';
				this.scrollerStyle.top = y + 'px';
			}

			if (this.options.wheel) {
				var _options$wheel$rotate = this.options.wheel.rotate,
				    rotate = _options$wheel$rotate === undefined ? 25 : _options$wheel$rotate;

				for (var i = 0; i < this.items.length; i++) {
					var deg = rotate * (y / this.itemHeight + i);
					this.items[i].style[_utils.style.transform] = 'rotateX(' + deg + 'deg)';
				}
			}

			this.x = x;
			this.y = y;

			if (this.indicators) {
				for (var _i3 = 0; _i3 < this.indicators.length; _i3++) {
					this.indicators[_i3].updatePosition();
				}
			}
		}
		/**
   * @private
   */

	}, {
		key: 'animate',
		value: function animate(destX, destY, duration, easingFn) {
			var _this4 = this;

			var me = this;
			var startX = this.x;
			var startY = this.y;
			var startTime = (0, _utils.getNow)();
			var destTime = startTime + duration;

			var step = function step() {
				var now = (0, _utils.getNow)();

				if (now >= destTime) {
					_this4.isAnimating = false;
					_this4.translate(destX, destY);

					if (!_this4.pulling && !_this4.resetPosition(_this4.options.bounceTime)) {
						_this4.trigger('scrollEnd', {
							x: _this4.x,
							y: _this4.y
						});
					}
					return;
				}
				now = (now - startTime) / duration;
				var easing = easingFn(now);
				var newX = (destX - startX) * easing + startX;
				var newY = (destY - startY) * easing + startY;

				_this4.translate(newX, newY);

				if (_this4.isAnimating) {
					_this4.animateTimer = (0, _utils.requestAnimationFrame)(step);
				}

				if (_this4.options.probeType === 3) {
					_this4.trigger('scroll', {
						x: _this4.x,
						y: _this4.y
					});
				}
			};

			this.isAnimating = true;
			(0, _utils.cancelAnimationFrame)(this.animateTimer);
			step();
		}
		/**
   * @public
   * 滚动到相对于当前位置的某处其余同上
   */

	}, {
		key: 'scrollBy',
		value: function scrollBy(x, y) {
			var time = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
			var easing = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _utils.ease.bounce;

			x = this.x + x;
			y = this.y + y;

			this.scrollTo(x, y, time, easing);
		}
		/**
   * @public
   * 滚动到：x，y
   */

	}, {
		key: 'scrollTo',
		value: function scrollTo(x, y) {
			var time = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
			var easing = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _utils.ease.bounce;

			this.isInTransition = this.options.useTransition && time > 0 && (x !== this.x || y !== this.y);

			if (!time || this.options.useTransition) {
				this.transitionTimingFunction(easing.style);
				this.transitionTime(time);
				this.translate(x, y);

				if (time && this.options.probeType === 3) {
					this.startProbe();
				}

				if (this.options.wheel) {
					if (y > 0) {
						this.selectedIndex = 0;
					} else if (y < this.maxScrollY) {
						this.selectedIndex = this.items.length - 1;
					} else {
						this.selectedIndex = Math.abs(y / this.itemHeight) | 0;
					}
				}
			} else {
				this.animate(x, y, time, easing.fn);
			}
		}
		/**
   * @public
   * 滚动到某个元素。el为必须的参数offsetX/offsetY：相对于el元素的位移。设为true即为屏幕中心
   */

	}, {
		key: 'scrollToElement',
		value: function scrollToElement(el, time, offsetX, offsetY, easing) {
			if (!el) {
				return;
			}
			el = el.nodeType ? el : this.scroller.querySelector(el);

			if (this.options.wheel && el.className !== 'wheel-item') {
				return;
			}

			var pos = (0, _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
			if (offsetX === true) {
				offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2);
			}
			if (offsetY === true) {
				offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 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;

			if (this.options.wheel) {
				pos.top = Math.round(pos.top / this.itemHeight) * this.itemHeight;
			}

			this.scrollTo(pos.left, pos.top, time, easing);
		}
		/**
   * @public
   * 重定位
   */

	}, {
		key: 'resetPosition',
		value: function resetPosition() {
			var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
			var easeing = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _utils.ease.bounce;

			var x = this.x;
			// 左右临界值判断
			if (!this.hasHorizontalScroll || x > 0) {
				// 无横向滚动条，或者左方超出，需回弹
				x = 0;
			} else if (x < this.maxScrollX) {
				// 右方超出，需回弹
				x = this.maxScrollX;
			}
			// 上下临界值判断
			var y = this.y;
			if (!this.hasVerticalScroll || y > 0) {
				// 无立向滚动条，或者上方超出，需回弹
				y = 0;
			} else if (y < this.maxScrollY) {
				// 下方超出，需回弹
				y = this.maxScrollY;
			}
			// 是否处于滚动区域
			if (x === this.x && y === this.y) {
				// 处于滚动区域，不做处理
				return false;
			}
			// 滚动到指定地方
			this.scrollTo(x, y, time, easeing);

			return true;
		}
		/**
   * @public
   */

	}, {
		key: 'getComputedPosition',
		value: function getComputedPosition() {
			var matrix = window.getComputedStyle(this.scroller, null);
			var x = void 0;
			var y = void 0;

			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
			};
		}
		/**
   * @public
   */

	}, {
		key: 'stop',
		value: function stop() {
			if (this.options.useTransition && this.isInTransition) {
				this.isInTransition = false;
				var pos = this.getComputedPosition();
				this.translate(pos.x, pos.y);
				if (this.options.wheel) {
					this.target = this.items[Math.round(-pos.y / this.itemHeight)];
				} else {
					this.trigger('scrollEnd', {
						x: this.x,
						y: this.y
					});
				}
			} else if (!this.options.useTransition && this.isAnimating) {
				this.isAnimating = false;
				this.trigger('scrollEnd', {
					x: this.x,
					y: this.y
				});
			}
		}
		/**
   * @public
   * 销毁资源
   */

	}, {
		key: 'destroy',
		value: function destroy() {
			this.removeDOMEvents();
			// remove custom events
			this.events = {};

			if (this.options.scrollbar) {
				this.removeScrollBars();
			}

			this.destroyed = true;
			this.trigger('destroy');
		}
		// - end core prototype
		// - start snap prototype
		/**
   * @private
   */

	}, {
		key: 'initSnap',
		value: function initSnap() {
			var _this5 = this;

			this.currentPage = {};
			var snap = this.options.snap;

			if (snap.loop) {
				var children = this.scroller.children;
				if (children.length > 0) {
					prepend(children[children.length - 1].cloneNode(true), this.scroller);
					this.scroller.appendChild(children[1].cloneNode(true));
				}
			}

			var el = snap.el;
			if (typeof el === 'string') {
				el = this.scroller.querySelectorAll(el);
			}

			this.on('refresh', function () {
				_this5.pages = [];

				if (!_this5.wrapperWidth || !_this5.wrapperHeight || !_this5.scrollerWidth || !_this5.scrollerHeight) {
					return;
				}

				var stepX = snap.stepX || _this5.wrapperWidth;
				var stepY = snap.stepY || _this5.wrapperHeight;

				var x = 0;
				var y = void 0;
				var cx = void 0;
				var cy = void 0;
				var i = 0;
				var l = void 0;
				var m = 0;
				var n = void 0;
				var rect = void 0;
				if (!el) {
					cx = Math.round(stepX / 2);
					cy = Math.round(stepY / 2);

					while (x > -_this5.scrollerWidth) {
						_this5.pages[i] = [];
						l = 0;
						y = 0;

						while (y > -_this5.scrollerHeight) {
							_this5.pages[i][l] = {
								x: Math.max(x, _this5.maxScrollX),
								y: Math.max(y, _this5.maxScrollY),
								width: stepX,
								height: stepY,
								cx: x - cx,
								cy: y - cy
							};

							y -= stepY;
							l++;
						}

						x -= stepX;
						i++;
					}
				} else {
					l = el.length;
					n = -1;

					for (; i < l; i++) {
						rect = (0, _utils.getRect)(el[i]);
						if (i === 0 || rect.left <= (0, _utils.getRect)(el[i - 1]).left) {
							m = 0;
							n++;
						}

						if (!_this5.pages[m]) {
							_this5.pages[m] = [];
						}

						x = Math.max(-rect.left, _this5.maxScrollX);
						y = Math.max(-rect.top, _this5.maxScrollY);
						cx = x - Math.round(rect.width / 2);
						cy = y - Math.round(rect.height / 2);

						_this5.pages[m][n] = {
							x: x,
							y: y,
							width: rect.width,
							height: rect.height,
							cx: cx,
							cy: cy
						};

						if (x > _this5.maxScrollX) {
							m++;
						}
					}
				}

				var initPage = snap.loop ? 1 : 0;
				_this5.goToPage(_this5.currentPage.pageX || initPage, _this5.currentPage.pageY || 0, 0);

				var snapThreshold = snap.threshold;
				if (snapThreshold % 1 === 0) {
					_this5.snapThresholdX = snapThreshold;
					_this5.snapThresholdY = snapThreshold;
				} else {
					_this5.snapThresholdX = Math.round(_this5.pages[_this5.currentPage.pageX][_this5.currentPage.pageY].width * snapThreshold);
					_this5.snapThresholdY = Math.round(_this5.pages[_this5.currentPage.pageX][_this5.currentPage.pageY].height * snapThreshold);
				}
			});

			this.on('scrollEnd', function () {
				if (snap.loop) {
					if (_this5.currentPage.pageX === 0) {
						_this5.goToPage(_this5.pages.length - 2, _this5.currentPage.pageY, 0);
					}
					if (_this5.currentPage.pageX === _this5.pages.length - 1) {
						_this5.goToPage(1, _this5.currentPage.pageY, 0);
					}
				}
			});

			if (snap.listenFlick !== false) {
				this.on('flick', function () {
					var time = snap.speed || Math.max(Math.max(Math.min(Math.abs(_this5.x - _this5.startX), 1000), Math.min(Math.abs(_this5.y - _this5.startY), 1000)), 300);

					_this5.goToPage(_this5.currentPage.pageX + _this5.directionX, _this5.currentPage.pageY + _this5.directionY, time);
				});
			}
		}
		/**
   * @private
   */

	}, {
		key: 'nearestSnap',
		value: function nearestSnap(x, y) {
			if (!this.pages.length) {
				return { x: 0, y: 0, pageX: 0, pageY: 0 };
			}

			var i = 0;

			if (Math.abs(x - this.absStartX) <= this.snapThresholdX && Math.abs(y - this.absStartY) <= this.snapThresholdY) {
				return this.currentPage;
			}

			if (x > 0) {
				x = 0;
			} else if (x < this.maxScrollX) {
				x = this.maxScrollX;
			}

			if (y > 0) {
				y = 0;
			} else if (y < this.maxScrollY) {
				y = this.maxScrollY;
			}

			var l = this.pages.length;
			for (; i < l; i++) {
				if (x >= this.pages[i][0].cx) {
					x = this.pages[i][0].x;
					break;
				}
			}

			l = this.pages[i].length;

			var m = 0;
			for (; m < l; m++) {
				if (y >= this.pages[0][m].cy) {
					y = this.pages[0][m].y;
					break;
				}
			}

			if (i === this.currentPage.pageX) {
				i += this.directionX;

				if (i < 0) {
					i = 0;
				} else if (i >= this.pages.length) {
					i = this.pages.length - 1;
				}

				x = this.pages[i][0].x;
			}

			if (m === this.currentPage.pageY) {
				m += this.directionY;

				if (m < 0) {
					m = 0;
				} else if (m >= this.pages[0].length) {
					m = this.pages[0].length - 1;
				}

				y = this.pages[0][m].y;
			}

			return {
				x: x,
				y: y,
				pageX: i,
				pageY: m
			};
		}
		/**
   * @public
   */

	}, {
		key: 'goToPage',
		value: function goToPage(x, y, time) {
			var easing = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _utils.ease.bounce;

			var snap = this.options.snap;
			if (x >= this.pages.length) {
				x = this.pages.length - 1;
			} else if (x < 0) {
				x = 0;
			}

			if (y >= this.pages[x].length) {
				y = this.pages[x].length - 1;
			} else if (y < 0) {
				y = 0;
			}

			var posX = this.pages[x][y].x;
			var posY = this.pages[x][y].y;

			time = time === undefined ? snap.speed || Math.max(Math.max(Math.min(Math.abs(posX - this.x), 1000), Math.min(Math.abs(posY - this.y), 1000)), 300) : time;

			this.currentPage = {
				x: posX,
				y: posY,
				pageX: x,
				pageY: y
			};
			this.scrollTo(posX, posY, time, easing);
		}
		/**
   * @public
   */

	}, {
		key: 'next',
		value: function next(time, easing) {
			var x = this.currentPage.pageX;
			var y = this.currentPage.pageY;

			x++;
			if (x >= this.pages.length && this.hasVerticalScroll) {
				x = 0;
				y++;
			}

			this.goToPage(x, y, time, easing);
		}
		/**
   * @public
   */

	}, {
		key: 'prev',
		value: function prev(time, easing) {
			var x = this.currentPage.pageX;
			var y = this.currentPage.pageY;

			x--;
			if (x < 0 && this.hasVerticalScroll) {
				x = 0;
				y--;
			}

			this.goToPage(x, y, time, easing);
		}
		/**
   * @public
   */

	}, {
		key: 'getCurrentPage',
		value: function getCurrentPage() {
			return this.options.snap && this.currentPage;
		}
		// - end snap prototype
		// - start scrollBar prototype
		/**
   * @private
   */

	}, {
		key: 'initScrollbar',
		value: function initScrollbar() {
			var _this6 = this;

			var _options$scrollbar$fa = this.options.scrollbar.fade,
			    fade = _options$scrollbar$fa === undefined ? true : _options$scrollbar$fa;

			this.indicators = [];
			var indicator = void 0;

			if (this.options.scrollX) {
				indicator = {
					el: (0, _utils.createScrollbar)('horizontal'),
					direction: 'horizontal',
					fade: fade
				};
				this.insertScrollBar(indicator.el);

				this.indicators.push(new _Indicator2.default(this, indicator));
			}

			if (this.options.scrollY) {
				indicator = {
					el: (0, _utils.createScrollbar)('vertical'),
					direction: 'vertical',
					fade: fade
				};
				this.insertScrollBar(indicator.el);
				this.indicators.push(new _Indicator2.default(this, indicator));
			}

			this.on('refresh', function () {
				for (var i = 0; i < _this6.indicators.length; i++) {
					_this6.indicators[i].refresh();
				}
			});

			if (fade) {
				this.on('scrollEnd', function () {
					for (var i = 0; i < _this6.indicators.length; i++) {
						_this6.indicators[i].fade();
					}
				});

				this.on('scrollCancel', function () {
					for (var i = 0; i < _this6.indicators.length; i++) {
						_this6.indicators[i].fade();
					}
				});

				this.on('scrollStart', function () {
					for (var i = 0; i < _this6.indicators.length; i++) {
						_this6.indicators[i].fade(true);
					}
				});

				this.on('beforeScrollStart', function () {
					for (var i = 0; i < _this6.indicators.length; i++) {
						_this6.indicators[i].fade(true, true);
					}
				});
			}
		}
		/**
   * @private
   */

	}, {
		key: 'insertScrollBar',
		value: function insertScrollBar(scrollbar) {
			this.wrapper.appendChild(scrollbar);
		}
		/**
   * @private
   */

	}, {
		key: 'removeScrollBars',
		value: function removeScrollBars() {
			for (var i = 0; i < this.indicators.length; i++) {
				var indicator = this.indicators[i];
				indicator.remove();
			}
		}
		// - end scrollBar prototype
		// - start pullUp prototype
		/**
   * @private
   */

	}, {
		key: 'initPullUp',
		value: function initPullUp() {
			this.options.probeType = 3;

			this.pullupWatching = false;
			this.watchPullUp();
		}
		/**
   * @private
   */

	}, {
		key: 'watchPullUp',
		value: function watchPullUp() {
			var _this7 = this;

			if (this.pullupWatching) {
				return;
			}
			this.pullupWatching = true;
			var _options$pullUpLoad$t = this.options.pullUpLoad.threshold,
			    threshold = _options$pullUpLoad$t === undefined ? 0 : _options$pullUpLoad$t;

			var checkToEnd = function checkToEnd(pos) {
				if (_this7.movingDirectionY === 1 && pos.y <= _this7.maxScrollY + threshold) {
					_this7.trigger('pullingUp');
					_this7.pullupWatching = false;
					_this7.off('scroll', checkToEnd);
				}
			};

			this.on('scroll', checkToEnd);
		}
		/**
   * @public
   */

	}, {
		key: 'finishPullUp',
		value: function finishPullUp() {
			var _this8 = this;

			if (this.isInTransition) {
				this.once('scrollEnd', function () {
					_this8.watchPullUp();
				});
			} else {
				this.watchPullUp();
			}
		}
		// - end pullUp prototype
		// - start pullDown prototype
		/**
   * @private
   */

	}, {
		key: 'initPullDown',
		value: function initPullDown() {
			this.options.probeType = 3;
		}
		/**
   * @private
   */

	}, {
		key: 'checkPullDown',
		value: function checkPullDown() {
			var _options$pullDownRefr = this.options.pullDownRefresh,
			    _options$pullDownRefr2 = _options$pullDownRefr.threshold,
			    threshold = _options$pullDownRefr2 === undefined ? 90 : _options$pullDownRefr2,
			    _options$pullDownRefr3 = _options$pullDownRefr.stop,
			    stop = _options$pullDownRefr3 === undefined ? 40 : _options$pullDownRefr3;

			if (this.y > threshold && !this.pulling) {
				this.pulling = true;
				this.trigger('pullingDown');
				this.scrollTo(this.x, stop, this.options.bounceTime, _utils.ease.bounce);
			}

			return this.pulling;
		}
		/**
   * @public
   */

	}, {
		key: 'finishPullDown',
		value: function finishPullDown() {
			this.pulling = false;
			this.resetPosition(this.options.bounceTime, _utils.ease.bounce);
		}
		// - end pullDown prototype
		// - start wheel prototype
		/**
   * @public
   */

	}, {
		key: 'wheelTo',
		value: function wheelTo(index) {
			if (this.options.wheel) {
				this.y = -index * this.itemHeight;
				this.scrollTo(0, this.y);
			}
		}
		/**
   * @public
   */

	}, {
		key: 'getSelectedIndex',
		value: function getSelectedIndex() {
			return this.options.wheel && this.selectedIndex;
		}
		// - end wheel prototype

	}]);
	return Core;
}();

exports.default = Core;