1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2013-2014 Chukong Technologies Inc.
  4 
  5  http://www.cocos2d-x.org
  6 
  7  Permission is hereby granted, free of charge, to any person obtaining a copy
  8  of this software and associated documentation files (the "Software"), to deal
  9  in the Software without restriction, including without limitation the rights
 10  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  copies of the Software, and to permit persons to whom the Software is
 12  furnished to do so, subject to the following conditions:
 13 
 14  The above copyright notice and this permission notice shall be included in
 15  all copies or substantial portions of the Software.
 16 
 17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23  THE SOFTWARE.
 24  ****************************************************************************/
 25 
 26 /**
 27  * ignore
 28  */
 29 cc.UIInterfaceOrientationLandscapeLeft = -90;
 30 
 31 cc.UIInterfaceOrientationLandscapeRight = 90;
 32 
 33 cc.UIInterfaceOrientationPortraitUpsideDown = 180;
 34 
 35 cc.UIInterfaceOrientationPortrait = 0;
 36 
 37 /**
 38  * <p>
 39  *  This class manages all events of input. include: touch, mouse, accelerometer, keyboard                                       <br/>
 40  * </p>
 41  * @namespace
 42  */
 43 cc.inputManager = /** @lends cc.inputManager# */{
 44     _mousePressed: false,
 45 
 46     _isRegisterEvent: false,
 47 
 48     _preTouchPoint: cc.p(0,0),
 49     _prevMousePoint: cc.p(0,0),
 50 
 51     _preTouchPool: [],
 52     _preTouchPoolPointer: 0,
 53 
 54     _touches: [],
 55     _touchesIntegerDict:{},
 56 
 57     _indexBitsUsed: 0,
 58     _maxTouches: 5,
 59 
 60     _accelEnabled: false,
 61     _accelInterval: 1/30,
 62     _accelMinus: 1,
 63     _accelCurTime: 0,
 64     _acceleration: null,
 65     _accelDeviceEvent: null,
 66 
 67     _getUnUsedIndex: function () {
 68         var temp = this._indexBitsUsed;
 69 
 70         for (var i = 0; i < this._maxTouches; i++) {
 71             if (!(temp & 0x00000001)) {
 72                 this._indexBitsUsed |= (1 << i);
 73                 return i;
 74             }
 75             temp >>= 1;
 76         }
 77 
 78         // all bits are used
 79         return -1;
 80     },
 81 
 82     _removeUsedIndexBit: function (index) {
 83         if (index < 0 || index >= this._maxTouches)
 84             return;
 85 
 86         var temp = 1 << index;
 87         temp = ~temp;
 88         this._indexBitsUsed &= temp;
 89     },
 90 
 91     _glView: null,
 92 
 93     handleTouchesBegin: function (touches) {
 94         var selTouch, index, curTouch, touchID, handleTouches = [], locTouchIntDict = this._touchesIntegerDict;
 95         for(var i = 0, len = touches.length; i< len; i ++){
 96             selTouch = touches[i];
 97             touchID = selTouch.getID();
 98             index = locTouchIntDict[touchID];
 99 
100             if(index == null){
101                 var unusedIndex = this._getUnUsedIndex();
102                 if (unusedIndex == -1) {
103                     cc.log(cc._LogInfos.inputManager_handleTouchesBegin, unusedIndex);
104                     continue;
105                 }
106                 curTouch = this._touches[unusedIndex] = selTouch;
107                 locTouchIntDict[touchID] = unusedIndex;
108                 handleTouches.push(curTouch);
109             }
110         }
111         if(handleTouches.length > 0){
112             this._glView._convertTouchesWithScale(handleTouches);
113             var touchEvent = new cc.EventTouch(handleTouches);
114             touchEvent._eventCode = cc.EventTouch.EventCode.BEGAN;
115             cc.eventManager.dispatchEvent(touchEvent);
116         }
117     },
118 
119     handleTouchesMove: function(touches){
120         var selTouch, index, touchID, handleTouches = [], locTouches = this._touches;
121         for(var i = 0, len = touches.length; i< len; i ++){
122             selTouch = touches[i];
123             touchID = selTouch.getID();
124             index = this._touchesIntegerDict[touchID];
125 
126             if(index == null){
127                 //cc.log("if the index doesn't exist, it is an error");
128                 continue;
129             }
130             if(locTouches[index]){
131                 locTouches[index]._setPoint(selTouch._point);
132                 locTouches[index]._setPrevPoint(selTouch._prevPoint);
133                 handleTouches.push(locTouches[index]);
134             }
135         }
136         if(handleTouches.length > 0){
137             this._glView._convertTouchesWithScale(handleTouches);
138             var touchEvent = new cc.EventTouch(handleTouches);
139             touchEvent._eventCode = cc.EventTouch.EventCode.MOVED;
140             cc.eventManager.dispatchEvent(touchEvent);
141         }
142     },
143 
144     handleTouchesEnd: function(touches){
145         var handleTouches = this.getSetOfTouchesEndOrCancel(touches);
146         if(handleTouches.length > 0) {
147             this._glView._convertTouchesWithScale(handleTouches);
148             var touchEvent = new cc.EventTouch(handleTouches);
149             touchEvent._eventCode = cc.EventTouch.EventCode.ENDED;
150             cc.eventManager.dispatchEvent(touchEvent);
151         }
152     },
153 
154     handleTouchesCancel: function(touches){
155         var handleTouches = this.getSetOfTouchesEndOrCancel(touches);
156         if(handleTouches.length > 0) {
157             this._glView._convertTouchesWithScale(handleTouches);
158             var touchEvent = new cc.EventTouch(handleTouches);
159             touchEvent._eventCode = cc.EventTouch.EventCode.CANCELLED;
160             cc.eventManager.dispatchEvent(touchEvent);
161         }
162     },
163 
164     getSetOfTouchesEndOrCancel: function(touches) {
165         var selTouch, index, touchID, handleTouches = [], locTouches = this._touches, locTouchesIntDict = this._touchesIntegerDict;
166         for(var i = 0, len = touches.length; i< len; i ++){
167             selTouch = touches[i];
168             touchID = selTouch.getID();
169             index = locTouchesIntDict[touchID];
170 
171             if(index == null){
172                 continue;  //cc.log("if the index doesn't exist, it is an error");
173             }
174             if(locTouches[index]){
175                 locTouches[index]._setPoint(selTouch._point);
176                 locTouches[index]._setPrevPoint(selTouch._prevPoint);         //TODO
177                 handleTouches.push(locTouches[index]);
178                 this._removeUsedIndexBit(index);
179                 delete locTouchesIntDict[touchID];
180             }
181         }
182         return handleTouches;
183     },
184 
185     getHTMLElementPosition: function (element) {
186         var docElem = document.documentElement;
187         var win = window;
188         var box = null;
189         if (typeof element.getBoundingClientRect === 'function') {
190             box = element.getBoundingClientRect();
191         } else {
192             if (element instanceof HTMLCanvasElement) {
193                 box = {
194                     left: 0,
195                     top: 0,
196                     width: element.width,
197                     height: element.height
198                 };
199             } else {
200                 box = {
201                     left: 0,
202                     top: 0,
203                     width: parseInt(element.style.width),
204                     height: parseInt(element.style.height)
205                 };
206             }
207         }
208         return {
209             left: box.left + win.pageXOffset - docElem.clientLeft,
210             top: box.top + win.pageYOffset - docElem.clientTop,
211             width: box.width,
212             height: box.height
213         };
214     },
215 
216     getPreTouch: function(touch){
217         var preTouch = null;
218         var locPreTouchPool = this._preTouchPool;
219         var id = touch.getId();
220         for (var i = locPreTouchPool.length - 1; i >= 0; i--) {
221             if (locPreTouchPool[i].getId() == id) {
222                 preTouch = locPreTouchPool[i];
223                 break;
224             }
225         }
226         if (!preTouch)
227             preTouch = touch;
228         return preTouch;
229     },
230 
231     setPreTouch: function(touch){
232         var find = false;
233         var locPreTouchPool = this._preTouchPool;
234         var id = touch.getId();
235         for (var i = locPreTouchPool.length - 1; i >= 0; i--) {
236             if (locPreTouchPool[i].getId() == id) {
237                 locPreTouchPool[i] = touch;
238                 find = true;
239                 break;
240             }
241         }
242         if (!find) {
243             if (locPreTouchPool.length <= 50) {
244                 locPreTouchPool.push(touch);
245             } else {
246                 locPreTouchPool[this._preTouchPoolPointer] = touch;
247                 this._preTouchPoolPointer = (this._preTouchPoolPointer + 1) % 50;
248             }
249         }
250     },
251 
252     getTouchByXY: function(tx, ty, pos){
253         var locPreTouch = this._preTouchPoint;
254         var location = this._glView.convertToLocationInView(tx, ty, pos);
255         var touch = new cc.Touch(location.x,  location.y);
256         touch._setPrevPoint(locPreTouch.x, locPreTouch.y);
257         locPreTouch.x = location.x;
258         locPreTouch.y = location.y;
259         return touch;
260     },
261 
262     getMouseEvent: function(location, pos, eventType){
263         var locPreMouse = this._prevMousePoint;
264         this._glView._convertMouseToLocationInView(location, pos);
265         var mouseEvent = new cc.EventMouse(eventType);
266         mouseEvent.setLocation(location.x, location.y);
267         mouseEvent._setPrevCursor(locPreMouse.x, locPreMouse.y);
268         locPreMouse.x = location.x;
269         locPreMouse.y = location.y;
270         return mouseEvent;
271     },
272 
273     getPointByEvent: function(event, pos){
274         if (event.pageX != null)  //not avalable in <= IE8
275             return {x: event.pageX, y: event.pageY};
276 
277         pos.left -= document.body.scrollLeft;
278         pos.top -= document.body.scrollTop;
279         return {x: event.clientX, y: event.clientY};
280     },
281 
282     getTouchesByEvent: function(event, pos){
283         var touchArr = [], locView = this._glView;
284         var touch_event, touch, preLocation;
285         var locPreTouch = this._preTouchPoint;
286 
287         var length = event.changedTouches.length;
288         for (var i = 0; i < length; i++) {
289             touch_event = event.changedTouches[i];
290             if (touch_event) {
291                 var location;
292                 if (cc.sys.BROWSER_TYPE_FIREFOX === cc.sys.browserType)
293                     location = locView.convertToLocationInView(touch_event.pageX, touch_event.pageY, pos);
294                 else
295                     location = locView.convertToLocationInView(touch_event.clientX, touch_event.clientY, pos);
296                 if (touch_event.identifier != null) {
297                     touch = new cc.Touch(location.x, location.y, touch_event.identifier);
298                     //use Touch Pool
299                     preLocation = this.getPreTouch(touch).getLocation();
300                     touch._setPrevPoint(preLocation.x, preLocation.y);
301                     this.setPreTouch(touch);
302                 } else {
303                     touch = new cc.Touch(location.x, location.y);
304                     touch._setPrevPoint(locPreTouch.x, locPreTouch.y);
305                 }
306                 locPreTouch.x = location.x;
307                 locPreTouch.y = location.y;
308                 touchArr.push(touch);
309             }
310         }
311         return touchArr;
312     },
313 
314     registerSystemEvent: function(element){
315         if(this._isRegisterEvent) return;
316 
317         var locView = this._glView = cc.view;
318         var selfPointer = this;
319         var supportMouse = ('mouse' in cc.sys.capabilities), supportTouches = ('touches' in cc.sys.capabilities);
320 
321         //register touch event
322         if (supportMouse) {
323             cc._addEventListener(window, 'mousedown', function () {
324                 selfPointer._mousePressed = true;
325             }, false);
326 
327             cc._addEventListener(window, 'mouseup', function (event) {
328                 var savePressed = selfPointer._mousePressed;
329                 selfPointer._mousePressed = false;
330 
331                 if(!savePressed)
332                     return;
333 
334                 var pos = selfPointer.getHTMLElementPosition(element);
335                 var location = selfPointer.getPointByEvent(event, pos);
336                 if (!cc.rectContainsPoint(new cc.Rect(pos.left, pos.top, pos.width, pos.height), location)){
337                     if(!supportTouches)
338                         selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]);
339 
340                     var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP);
341                     mouseEvent.setButton(event.button);
342                     cc.eventManager.dispatchEvent(mouseEvent);
343                 }
344             }, false);
345 
346             //register canvas mouse event
347             cc._addEventListener(element,"mousedown", function (event) {
348                 selfPointer._mousePressed = true;
349 
350                 var pos = selfPointer.getHTMLElementPosition(element);
351                 var location = selfPointer.getPointByEvent(event, pos);
352                 if(!supportTouches)
353                     selfPointer.handleTouchesBegin([selfPointer.getTouchByXY(location.x, location.y, pos)]);
354 
355                 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.DOWN);
356                 mouseEvent.setButton(event.button);
357                 cc.eventManager.dispatchEvent(mouseEvent);
358 
359                 event.stopPropagation();
360                 event.preventDefault();
361             }, false);
362 
363             cc._addEventListener(element, "mouseup", function (event) {
364                 selfPointer._mousePressed = false;
365 
366                 var pos = selfPointer.getHTMLElementPosition(element);
367                 var location = selfPointer.getPointByEvent(event, pos);
368 
369                 if(!supportTouches)
370                     selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]);
371 
372                 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP);
373                 mouseEvent.setButton(event.button);
374                 cc.eventManager.dispatchEvent(mouseEvent);
375 
376                 event.stopPropagation();
377                 event.preventDefault();
378             }, false);
379 
380             cc._addEventListener(element, "mousemove", function (event) {
381                 if(!selfPointer._mousePressed)
382                     return;
383 
384                 var pos = selfPointer.getHTMLElementPosition(element);
385                 var location = selfPointer.getPointByEvent(event, pos);
386 
387                 if(!supportTouches)
388                     selfPointer.handleTouchesMove([selfPointer.getTouchByXY(location.x, location.y, pos)]);
389 
390                 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.MOVE);
391                 mouseEvent.setButton(event.button);
392                 cc.eventManager.dispatchEvent(mouseEvent);
393 
394                 event.stopPropagation();
395                 event.preventDefault();
396             }, false);
397 
398             cc._addEventListener(element, "mousewheel", function (event) {
399                 var pos = selfPointer.getHTMLElementPosition(element);
400                 var location = selfPointer.getPointByEvent(event, pos);
401 
402                 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.SCROLL);
403                 mouseEvent.setButton(event.button);
404                 mouseEvent.setScrollData(0, event.wheelDelta);
405                 cc.eventManager.dispatchEvent(mouseEvent);
406 
407                 event.stopPropagation();
408                 event.preventDefault();
409             }, false);
410 
411             /* firefox fix */
412             cc._addEventListener(element, "DOMMouseScroll", function(event) {
413                 var pos = selfPointer.getHTMLElementPosition(element);
414                 var location = selfPointer.getPointByEvent(event, pos);
415 
416                 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.SCROLL);
417                 mouseEvent.setButton(event.button);
418                 mouseEvent.setScrollData(0, event.detail * -120);
419                 cc.eventManager.dispatchEvent(mouseEvent);
420 
421                 event.stopPropagation();
422                 event.preventDefault();
423             }, false);
424         }
425 
426         if(window.navigator.msPointerEnabled){
427             var _pointerEventsMap = {
428                 "MSPointerDown"     : selfPointer.handleTouchesBegin,
429                 "MSPointerMove"     : selfPointer.handleTouchesMove,
430                 "MSPointerUp"       : selfPointer.handleTouchesEnd,
431                 "MSPointerCancel"   : selfPointer.handleTouchesCancel
432             };
433 
434             for(var eventName in _pointerEventsMap){
435                 (function(_pointerEvent, _touchEvent){
436                     cc._addEventListener(element, _pointerEvent, function (event){
437                         var pos = selfPointer.getHTMLElementPosition(element);
438                         pos.left -= document.documentElement.scrollLeft;
439                         pos.top -= document.documentElement.scrollTop;
440 
441                         _touchEvent.call(selfPointer, [selfPointer.getTouchByXY(event.clientX, event.clientY, pos)]);
442                         event.stopPropagation();
443                         event.preventDefault();
444                     }, false);
445                 })(eventName, _pointerEventsMap[eventName]);
446             }
447         }
448 
449         if(supportTouches) {
450             //register canvas touch event
451             cc._addEventListener(element,"touchstart", function (event) {
452                 if (!event.changedTouches) return;
453 
454                 var pos = selfPointer.getHTMLElementPosition(element);
455                 pos.left -= document.body.scrollLeft;
456                 pos.top -= document.body.scrollTop;
457                 selfPointer.handleTouchesBegin(selfPointer.getTouchesByEvent(event, pos));
458                 event.stopPropagation();
459                 event.preventDefault();
460             }, false);
461 
462             cc._addEventListener(element, "touchmove", function (event) {
463                 if (!event.changedTouches) return;
464 
465                 var pos = selfPointer.getHTMLElementPosition(element);
466                 pos.left -= document.body.scrollLeft;
467                 pos.top -= document.body.scrollTop;
468                 selfPointer.handleTouchesMove(selfPointer.getTouchesByEvent(event, pos));
469                 event.stopPropagation();
470                 event.preventDefault();
471             }, false);
472 
473             cc._addEventListener(element, "touchend", function (event) {
474                 if (!event.changedTouches) return;
475 
476                 var pos = selfPointer.getHTMLElementPosition(element);
477                 pos.left -= document.body.scrollLeft;
478                 pos.top -= document.body.scrollTop;
479                 selfPointer.handleTouchesEnd(selfPointer.getTouchesByEvent(event, pos));
480                 event.stopPropagation();
481                 event.preventDefault();
482             }, false);
483 
484             cc._addEventListener(element, "touchcancel", function (event) {
485                 if (!event.changedTouches) return;
486 
487                 var pos = selfPointer.getHTMLElementPosition(element);
488                 pos.left -= document.body.scrollLeft;
489                 pos.top -= document.body.scrollTop;
490                 locView.handleTouchesCancel(selfPointer.getTouchesByEvent(event, pos));
491                 event.stopPropagation();
492                 event.preventDefault();
493             }, false);
494         }
495 
496         //register keyboard event
497         this._registerKeyboardEvent();
498 
499         //register Accelerometer event
500         this._registerAccelerometerEvent();
501 
502         this._isRegisterEvent = true;
503     },
504 
505     _registerKeyboardEvent: function(){},
506 
507     _registerAccelerometerEvent: function(){},
508 
509     update:function(dt){
510         if(this._accelCurTime > this._accelInterval){
511             this._accelCurTime -= this._accelInterval;
512             cc.eventManager.dispatchEvent(new cc.EventAcceleration(this._acceleration));
513         }
514         this._accelCurTime += dt;
515     }
516 };
517