1 /**
  2  * The Render Engine
  3  * KeyboardInputComponent
  4  *
  5  * @fileoverview An extension of the input component to handle touch inputs from
  6  *               devices which support them.
  7  *
  8  * @author: Brett Fattori (brettf@renderengine.com)
  9  * @author: $Author: bfattori $
 10  * @version: $Revision: 1555 $
 11  *
 12  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 13  *
 14  * Permission is hereby granted, free of charge, to any person obtaining a copy
 15  * of this software and associated documentation files (the "Software"), to deal
 16  * in the Software without restriction, including without limitation the rights
 17  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 18  * copies of the Software, and to permit persons to whom the Software is
 19  * furnished to do so, subject to the following conditions:
 20  *
 21  * The above copyright notice and this permission notice shall be included in
 22  * all copies or substantial portions of the Software.
 23  *
 24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 29  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 30  * THE SOFTWARE.
 31  *
 32  */
 33 
 34 // The class this file defines and its required classes
 35 R.Engine.define({
 36     "class":"R.components.input.Touch",
 37     "requires":[
 38         "R.components.Input",
 39         "R.engine.Events",
 40         "R.struct.Touch"
 41     ]
 42 });
 43 
 44 /**
 45  * @class A component which responds to touch events and notifies
 46  * its {@link R.engine.GameObject} by triggering one of three events.  The <tt>R.engine.GameObject</tt>
 47  * should add event handlers for any one of:
 48  * <ul>
 49  * <li><tt>touchstart</tt> - A touch event started</li>
 50  * <li><tt>touchend</tt> - A touch event ended</li>
 51  * <li><tt>touchmove</tt> - A movement occurred after a touch event started</li>
 52  * </ul>
 53  * Each event handler will be passed a {@link R.struct.TouchInfo TouchInfo} object which
 54  * contains information about the event.  The <tt>touchmove</tt> event is also passed a boolean
 55  * flag indicating if the touch is within the world bounding box of the game object.
 56  * <p/>
 57  * <i>Note: The rendering context that the object is contained within needs to enable touch event
 58  * capturing with the {@link R.rendercontexts.AbstractRenderContext#captureTouch} method.</i>
 59  *
 60  * @param name {String} The unique name of the component.
 61  * @param [priority] {Number} The priority of the component among other input components.
 62  * @extends R.components.Input
 63  * @constructor
 64  * @description Create an instance of a touch input component.
 65  */
 66 R.components.input.Touch = function () {
 67     "use strict";
 68     return R.components.Input.extend(/** @scope R.components.input.Touch.prototype */{
 69 
 70         hasTouchMethods:null,
 71 
 72         /**
 73          * @private
 74          */
 75         constructor:function (name, passThru, priority) {
 76             this.base(name, priority);
 77         },
 78 
 79         /**
 80          * Destroy this instance and remove all references.
 81          */
 82         destroy:function () {
 83             if (this.getGameObject()) {
 84                 delete this.getGameObject().getObjectDataModel()[R.components.input.Touch.TOUCH_DATA_MODEL];
 85             }
 86             this.base();
 87         },
 88 
 89         /**
 90          * Deprecated in favor of {@link #setGameObject}
 91          * @deprecated
 92          */
 93         setHostObject:function (hostObj) {
 94             this.setGameObject(hostObj);
 95         },
 96 
 97         /**
 98          * Establishes the link between this component and its host object.
 99          * When you assign components to a host object, it will call this method
100          * so that each component can refer to its host object, the same way
101          * a host object can refer to a component with {@link R.engine.GameObject#getComponent}.
102          *
103          * @param gameObject {R.engine.GameObject} The object which hosts this component
104          */
105         setGameObject:function (gameObject) {
106             this.base(gameObject);
107 
108             // Set some flags we can check
109             var dataModel = gameObject.setObjectDataModel(R.components.input.Mouse.TOUCH_DATA_MODEL, {
110                 touchDown:false
111             });
112 
113             // Add event pass-thru for DOM objects
114             var el = gameObject.jQ();
115             if (el) {
116                 var tI = R.struct.TouchInfo.create();
117 
118                 // Wire up event handlers for the DOM element to mimic what is done for
119                 // canvas objects
120                 el.bind("touchmove", function (evt) {
121                     tI.lastPosition.set(tI.position);
122                     tI.position.set(evt.pageX, evt.pageY);
123                     gameObject.triggerEvent("touchmove", [tI]);
124                 });
125 
126                 el.bind("touchstart", function (evt) {
127                     tI.touches = tI.processTouches(evt);
128                     tI.button = R.engine.Events.MOUSE_LEFT_BUTTON;
129                     tI.downPosition.set(evt.pageX, evt.pageY);
130                     gameObject.triggerEvent("touchstart", [tI]);
131                 });
132 
133                 el.bind("touchend", function (evt) {
134                     tI.touches = tI.processTouches(evt);
135                     tI.button = R.engine.Events.MOUSE_NO_BUTTON;
136                     tI.dragVec.set(0, 0);
137                     gameObject.triggerEvent("touchend", [tI]);
138                 });
139             }
140 
141         },
142 
143         /**
144          * Perform the checks on the touch info object, and also perform
145          * intersection tests to be able to call touch events.
146          * @param renderContext {R.rendercontexts.AbstractRenderContext} The render context
147          * @param time {Number} The current world time
148          * @param dt {Number} The delta between the world time and the last time the world was updated
149          *          in milliseconds.
150          */
151         execute:function (renderContext, time, dt) {
152             // Objects may be in motion.  If so, we need to call the touch
153             // methods for just such a case.
154             var gameObject = this.getGameObject();
155 
156 
157             // Only objects without an element will use this.  For object WITH an element,
158             // this component will have intervened and wired up special handlers to fake
159             // the mouseInfo object.
160             if (!gameObject.getElement()) {
161                 var touchInfo = renderContext.getTouchInfo(),
162                     bBox = gameObject.getWorldBox(),
163                     touchOn = false,
164                     dataModel = gameObject.getObjectDataModel(R.components.input.Touch.TOUCH_DATA_MODEL);
165 
166                 if (touchInfo && bBox) {
167                     touchOn = R.math.Math2D.boxPointCollision(bBox, touchInfo.position);
168                 }
169 
170                 // Touched on object
171                 if (touchOn && (touchInfo.button != R.engine.Events.MOUSE_NO_BUTTON)) {
172                     dataModel.touchDown = true;
173                     gameObject.triggerEvent("touchstart", [touchInfo]);
174                 }
175 
176                 // Touch position changed
177                 if (dataModel.touchDown && !touchInfo.position.equals(touchInfo.lastPosition)) {
178                     gameObject.triggerEvent("touchmove", [touchInfo, touchOn]);
179                 }
180 
181                 // Touch ended (and object was touched)
182                 if (dataModel.touchDown && touchInfo.button == R.engine.Events.MOUSE_NO_BUTTON) {
183                     dataModel.touchDown = false;
184                     gameObject.triggerEvent("touchend", [touchInfo]);
185                 }
186             }
187         }
188 
189     }, /** @scope R.components.input.Touch.prototype */{
190         /**
191          * Get the class name of this object
192          *
193          * @return {String} "R.components.input.Touch"
194          */
195         getClassName:function () {
196             return "R.components.input.Touch";
197         },
198 
199         /**
200          * @private
201          */
202         TOUCH_DATA_MODEL:"TouchInputComponent"
203     });
204 };
205