/* * TouchController.js * Keynote HTML Player * * Responsibility: Tungwei Cheng * Copyright (c) 2009-2013 Apple Inc. All rights reserved. */ // iPhone Event Name Constants: // ----------------------------- var kTouchStartEventName = "touchstart"; var kTouchMoveEventName = "touchmove"; var kTouchEndEventName = "touchend"; var kTouchCancelEventName = "touchcancel"; var kGestureStartEventName = "gesturestart"; var kGestureEndEventName = "gestureend"; // Class: TouchController // =============================================================== var kSwipeEvent = "TouchController:SwipeEvent"; var kTapEvent = "TouchController:TapeEvent"; var TouchController = Class.create({ initialize: function() { // observe touch events document.observe(kTouchStartEventName, this.handleTouchStartEvent.bind(this)); document.observe(kTouchMoveEventName, this.handleTouchMoveEvent.bind(this)); document.observe(kTouchEndEventName, this.handleTouchEndEvent.bind(this)); document.observe(kTouchCancelEventName, this.handleTouchCancelEvent.bind(this)); // observe gesture events document.observe(kGestureStartEventName, this.handleGestureStartEvent.bind(this)); document.observe(kGestureEndEventName, this.handleGestureEndEvent.bind(this)); this.swipeInProgress = false; this.swipeFingerCount = 0; this.swipeStartTime = 0; this.swipeStartX = 0; this.swipeStartY = 0; this.preventDefault = true; this.tapEventCallback = null; this.setTrackArea(0, 0, 0, 0); this.enableTouchTracking = true; }, setTouchTrackingEnabled: function(isEnabled) { this.enableTouchTracking = isEnabled; }, setTrackArea: function(left, top, width, height) { debugMessage(kDebugTouchController_SetTrackArea, "left: " + left + " top: " + top + " width: " + width + " height: " + height); this.trackAreaLeft = left; this.trackAreaTop = top; this.trackAreaRight = left + width; this.trackAreaBottom = top + height; }, registerTapEventCallback: function(tapEventCallback) { this.tapEventCallback = tapEventCallback; }, isTouchWithinTrackArea: function(touch) { debugMessage(kDebugTouchController_IsTouchWithinTrackArea, "checking..."); if (this.enableTouchTracking === false) { debugMessage(kDebugTouchController_IsTouchWithinTrackArea, "- nope, tracking is disabled"); return false; } if (touch.clientX < this.trackAreaLeft) { debugMessage(kDebugTouchController_IsTouchWithinTrackArea, "- nope, x < left"); return false; } if (touch.clientX > this.trackAreaRight) { debugMessage(kDebugTouchController_IsTouchWithinTrackArea, "- nope, x > right"); return false; } if (touch.clientY < this.trackAreaTop) { debugMessage(kDebugTouchController_IsTouchWithinTrackArea, "- nope, y < top"); return false; } if (touch.clientY > this.trackAreaBottom) { debugMessage(kDebugTouchController_IsTouchWithinTrackArea, "- nope, y > bottom"); return false; } debugMessage(kDebugTouchController_IsTouchWithinTrackArea, "- yes it is!"); return true; }, handleTouchStartEvent: function(event) { debugMessage(kDebugTouchController_HandleTouchStartEvent, "touch event has " + event.touches.length + " fingers..."); if (this.swipeInProgress === false) { debugMessage(kDebugTouchController_HandleTouchStartEvent, "- this is the first finger down event..."); var startTouch = event.touches[0]; if (this.isTouchWithinTrackArea(startTouch)) { debugMessage(kDebugTouchController_HandleTouchStartEvent, "- start tracking a swipt event..."); if (this.preventDefault) { event.preventDefault(); } this.swipeInProgress = true; this.swipeFingerCount = event.touches.length; this.swipeStartTime = new Date(); this.swipeStartX = startTouch.clientX; this.swipeStartY = startTouch.clientY; } else { debugMessage(kDebugTouchController_HandleTouchStartEvent, "- but it is outside of the track area"); } } else { debugMessage(kDebugTouchController_HandleTouchStartEvent, "- this is a subsequent finger down event. update finger count..."); if (event.touches.length > this.swipeFingerCount) { this.swipeFingerCount = event.touches.length; debugMessage(kDebugTouchController_HandleTouchStartEvent, "- this.swipeFingerCount:" + this.swipeFingerCount); } } }, handleTouchMoveEvent: function(event) { if (this.preventDefault) { event.preventDefault(); } debugMessage(kDebugTouchController_HandleTouchCancelEvent, ""); }, handleTouchEndEvent: function(event) { debugMessage(kDebugTouchController_HandleTouchEndEvent, "touch event has " + event.touches.length + " fingers..."); if (this.swipeInProgress) { if (this.preventDefault) { event.preventDefault(); } if (event.touches.length === 0) { debugMessage(kDebugTouchController_HandleTouchEndEvent, "- " + this.swipeFingerCount + " finger swipe is complete."); var endTouch = event.changedTouches[0]; var viewport = document.viewport.getDimensions(); var minSwipeDistance = viewport.width / 3.0; var maxVerticalMagnitude = viewport.height / 3.0; var maxHorizontalMagnitude = viewport.width / 3.0; var deltaX = endTouch.clientX - this.swipeStartX; var deltaY = endTouch.clientY - this.swipeStartY; var magnitudeX = Math.abs(deltaX); var magnitudeY = Math.abs(deltaY); var touchEndTime = new Date(); var elapsedTime = touchEndTime - this.swipeStartTime; var tapIsGood = false; var swipeIsGood = false; // First Check for a "tap" var kMaxTapTime = 400; var kMaxTapMagnitude = 20; if (elapsedTime < kMaxTapTime) { debugMessage(kDebugTouchController_HandleTouchEndEvent, "- elapsed time was short enough to be a tap, check its magnitude..."); if ((magnitudeX < kMaxTapMagnitude) && (magnitudeY < kMaxTapMagnitude)) { tapIsGood = true; } else { debugMessage(kDebugTouchController_HandleTouchEndEvent, "- magnitude time too big to be a tap, check if it's a swipe..."); } } else { debugMessage(kDebugTouchController_HandleTouchEndEvent, "- elapsed time too long to be a tap, check if it's a swipe..."); } if (elapsedTime > 800) { debugMessage(kDebugTouchController_HandleTouchEndEvent, "- elapsed time too long to be a swipe, ignoring..."); } else { if (magnitudeX > magnitudeY) { if (magnitudeY > maxVerticalMagnitude) { debugMessage(kDebugTouchController_HandleTouchEndEvent, "- vertical magnitude too high, ignoring..."); } else { swipeIsGood = true; } } else { if (magnitudeX > maxHorizontalMagnitude) { debugMessage(kDebugTouchController_HandleTouchEndEvent, "- horizontal magnitude too high, ignoring..."); } else { swipeIsGood = true; } } } if (tapIsGood) { debugMessage(kDebugTouchController_HandleTouchEndEvent, "- it's a " + this.swipeFingerCount + " finger tap"); if (this.tapEventCallback) { var tapEvent = {}; tapEvent.memo = {}; tapEvent.memo.fingers = this.swipeFingerCount; tapEvent.memo.pointX = endTouch.clientX; tapEvent.memo.pointY = endTouch.clientY; tapEvent.memo.target = event.target; debugMessage(kDebugTouchController_HandleTouchEndEvent, "- invoking callback with pointX: " + endTouch.clientX + " pointY: " + endTouch.clientY + "..."); this.tapEventCallback(tapEvent); debugMessage(kDebugTouchController_HandleTouchEndEvent, "- back from callback"); } else { debugMessage(kDebugTouchController_HandleTouchEndEvent, "- firing TapEvent..."); document.fire(kTapEvent, { fingers: this.swipeFingerCount, pointX: endTouch.clientX, pointY: endTouch.clientY }); } } else if (swipeIsGood) { var direction; if (magnitudeX > magnitudeY) { direction = (deltaX < 0 ? "left" : "right"); } else { direction = (deltaY < 0 ? "up" : "down"); } debugMessage(kDebugTouchController_HandleTouchEndEvent, "- it's a " + this.swipeFingerCount + " finger swipe in the " + direction + " direction"); document.fire(kSwipeEvent, { direction: direction, fingers: this.swipeFingerCount, swipeStartX: this.swipeStartX }); } this.swipeInProgress = false; this.swipeFingerCount = 0; } } else { debugMessage(kDebugTouchController_HandleTouchEndEvent, "- false alarm. swipe has already ended."); } }, handleTouchCancelEvent: function(event) { debugMessage(kDebugTouchController_HandleTouchCancelEvent, ""); this.swipeInProgress = false; }, handleGestureStartEvent: function(event) { debugMessage(kDebugTouchController_HandleGestureStartEvent, ""); if (this.preventDefault) { event.preventDefault(); } }, handleGestureEndEvent: function(event) { debugMessage(kDebugTouchController_HandleGestureEndEvent, ""); if (this.preventDefault) { event.preventDefault(); } } });