/*!
 * TouchEmulator
 * https://github.com/hammerjs/touchemulator
 *
 * This is a small helper that converts mouse events into W3C touch events,
 * so desktop browsers can exercise touch-based UIs (swipe/drag).
 *
 * NOTE: Included for development/PC-frame preview usage.
 * License: MIT (see upstream repository).
 */
;(function (window, document) {
	'use strict'

	var TOUCH_TIMEOUT = 200
	var TOUCH_MOVE_RADIUS = 16
	var SCROLL_SPEED = 0.85

	var eventTarget = null
	var touchId = 0
	var isMultiTouch = false
	var touches = []
	var targetTouches = []
	var changedTouches = []
	var lastTouch = null
	var startX = 0
	var startY = 0
	var lastX = 0
	var lastY = 0
	var moved = false
	var isScrolling = false
	var touchTimer = null
	var lockedScroller = null
	var pendingScrollDX = 0
	var pendingScrollDY = 0
	var rafId = 0

	function hasTouchSupport() {
		return 'ontouchstart' in window || (window.DocumentTouch && document instanceof window.DocumentTouch)
	}

	function isInTouchMode() {
		return isMultiTouch || (!hasTouchSupport() && lastTouch)
	}

	function now() {
		return Date.now ? Date.now() : +new Date()
	}

	function createTouch(touchTarget, identifier, pageX, pageY) {
		var clientX = pageX - window.pageXOffset
		var clientY = pageY - window.pageYOffset
		return {
			identifier: identifier,
			target: touchTarget,
			screenX: pageX,
			screenY: pageY,
			clientX: clientX,
			clientY: clientY,
			pageX: pageX,
			pageY: pageY
		}
	}

	function createTouchList() {
		return touches.slice()
	}

	function dispatchTouchEvent(type, target, pageX, pageY) {
		if (!target) return true

		changedTouches.length = 0
		targetTouches.length = 0

		var touch = createTouch(target, touchId, pageX, pageY)
		changedTouches.push(touch)

		for (var i = 0; i < touches.length; i++) {
			if (touches[i].target === target) targetTouches.push(touches[i])
		}

		var evt
		try {
			evt = new Event(type, { bubbles: true, cancelable: true })
		} catch (e) {
			evt = document.createEvent('Event')
			evt.initEvent(type, true, true)
		}

		evt.touches = createTouchList()
		evt.targetTouches = targetTouches.slice()
		evt.changedTouches = changedTouches.slice()
		evt.altKey = false
		evt.metaKey = false
		evt.ctrlKey = false
		evt.shiftKey = isMultiTouch

		return target.dispatchEvent(evt)
	}

	function getScrollableAncestor(el, allowHorizontal) {
		var cur = el
		while (cur && cur !== document.body && cur !== document.documentElement) {
			try {
				var style = window.getComputedStyle(cur)
				var oy = style.overflowY
				var ox = style.overflowX
				var canScrollY = (oy === 'auto' || oy === 'scroll') && cur.scrollHeight > cur.clientHeight + 1
				var canScrollX =
					allowHorizontal && (ox === 'auto' || ox === 'scroll') && cur.scrollWidth > cur.clientWidth + 1
				if (canScrollY || canScrollX) return cur
			} catch (e) {}
			cur = cur.parentElement
		}
		return document.scrollingElement || document.documentElement
	}

	function flushScroll() {
		rafId = 0
		if (!lockedScroller) {
			pendingScrollDX = 0
			pendingScrollDY = 0
			return
		}

		var dx = pendingScrollDX
		var dy = pendingScrollDY
		pendingScrollDX = 0
		pendingScrollDY = 0

		try {
			lockedScroller.scrollLeft += dx
			lockedScroller.scrollTop += dy
		} catch (e) {
			try {
				window.scrollBy(dx, dy)
			} catch (e2) {}
		}
	}

	function queueScrollFromDrag(dx, dy) {
		pendingScrollDX += -dx * SCROLL_SPEED
		pendingScrollDY += -dy * SCROLL_SPEED
		if (!rafId) rafId = window.requestAnimationFrame(flushScroll)
	}

	function suppressClickOnce(ev) {
		if (!moved) return
		ev.preventDefault()
		ev.stopPropagation()
	}

	function onMouseDown(ev) {
		// ignore right click
		if (ev.button === 2) return

		eventTarget = ev.target
		startX = ev.pageX
		startY = ev.pageY
		lastX = ev.pageX
		lastY = ev.pageY
		moved = false
		isScrolling = false
		lockedScroller = null
		pendingScrollDX = 0
		pendingScrollDY = 0
		if (rafId) {
			window.cancelAnimationFrame(rafId)
			rafId = 0
		}

		// short-circuit: if recently fired touch, ignore mouse
		if (lastTouch && now() - lastTouch < TOUCH_TIMEOUT) return

		touches.length = 0
		touches.push(createTouch(eventTarget, touchId, ev.pageX, ev.pageY))
		dispatchTouchEvent('touchstart', eventTarget, ev.pageX, ev.pageY)

		document.addEventListener('mousemove', onMouseMove, true)
		document.addEventListener('mouseup', onMouseUp, true)
		document.addEventListener('click', suppressClickOnce, true)
	}

	function onMouseMove(ev) {
		if (!eventTarget) return

		var dx = ev.pageX - lastX
		var dy = ev.pageY - lastY
		var totalDX = ev.pageX - startX
		var totalDY = ev.pageY - startY
		var dist = Math.sqrt(totalDX * totalDX + totalDY * totalDY)

		if (!isScrolling && dist > TOUCH_MOVE_RADIUS) {
			isScrolling = true
			moved = true
			lockedScroller = getScrollableAncestor(eventTarget, true)
			try {
				ev.preventDefault()
			} catch (e) {}
		}

		touches[0] = createTouch(eventTarget, touchId, ev.pageX, ev.pageY)
		var notPrevented = dispatchTouchEvent('touchmove', eventTarget, ev.pageX, ev.pageY)
		if (isScrolling && notPrevented) {
			queueScrollFromDrag(dx, dy)
		}

		lastX = ev.pageX
		lastY = ev.pageY
	}

	function onMouseUp(ev) {
		if (!eventTarget) return

		touches.length = 0
		dispatchTouchEvent('touchend', eventTarget, ev.pageX, ev.pageY)

		eventTarget = null
		lastTouch = now()

		document.removeEventListener('mousemove', onMouseMove, true)
		document.removeEventListener('mouseup', onMouseUp, true)
		setTimeout(function () {
			document.removeEventListener('click', suppressClickOnce, true)
		}, 0)
		lockedScroller = null
	}

	function onTouchStart() {
		lastTouch = now()
	}

	function install() {
		if (touchTimer) return

		// prevent emulating touch when real touch exists (mobile)
		document.addEventListener('touchstart', onTouchStart, true)
		document.addEventListener('mousedown', onMouseDown, true)

		// Basic createTouch polyfills used by some libs
		if (!document.createTouch) {
			document.createTouch = function (view, target, identifier, pageX, pageY, screenX, screenY, clientX, clientY) {
				return createTouch(target, identifier, pageX, pageY)
			}
		}
		if (!document.createTouchList) {
			document.createTouchList = function () {
				return createTouchList()
			}
		}
	}

	window.TouchEmulator = function () {
		install()
		return {
			isInTouchMode: isInTouchMode
		}
	}
})(window, document)

