1 /****************************************************************************
  2  Copyright (c) 2011-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                 if(selfPointer._mousePressed)
392                     mouseEvent.setButton(event.button);
393                 else
394                     mouseEvent.setButton(null);
395                 cc.eventManager.dispatchEvent(mouseEvent);
396 
397                 event.stopPropagation();
398                 event.preventDefault();
399             }, false);
400 
401             cc._addEventListener(element, "mousewheel", function (event) {
402                 var pos = selfPointer.getHTMLElementPosition(element);
403                 var location = selfPointer.getPointByEvent(event, pos);
404 
405                 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.SCROLL);
406                 mouseEvent.setButton(event.button);
407                 mouseEvent.setScrollData(0, event.wheelDelta);
408                 cc.eventManager.dispatchEvent(mouseEvent);
409 
410                 event.stopPropagation();
411                 event.preventDefault();
412             }, false);
413 
414             /* firefox fix */
415             cc._addEventListener(element, "DOMMouseScroll", function(event) {
416                 var pos = selfPointer.getHTMLElementPosition(element);
417                 var location = selfPointer.getPointByEvent(event, pos);
418 
419                 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.SCROLL);
420                 mouseEvent.setButton(event.button);
421                 mouseEvent.setScrollData(0, event.detail * -120);
422                 cc.eventManager.dispatchEvent(mouseEvent);
423 
424                 event.stopPropagation();
425                 event.preventDefault();
426             }, false);
427         }
428 
429         if(window.navigator.msPointerEnabled){
430             var _pointerEventsMap = {
431                 "MSPointerDown"     : selfPointer.handleTouchesBegin,
432                 "MSPointerMove"     : selfPointer.handleTouchesMove,
433                 "MSPointerUp"       : selfPointer.handleTouchesEnd,
434                 "MSPointerCancel"   : selfPointer.handleTouchesCancel
435             };
436 
437             for(var eventName in _pointerEventsMap){
438                 (function(_pointerEvent, _touchEvent){
439                     cc._addEventListener(element, _pointerEvent, function (event){
440                         var pos = selfPointer.getHTMLElementPosition(element);
441                         pos.left -= document.documentElement.scrollLeft;
442                         pos.top -= document.documentElement.scrollTop;
443 
444                         _touchEvent.call(selfPointer, [selfPointer.getTouchByXY(event.clientX, event.clientY, pos)]);
445                         event.stopPropagation();
446                         event.preventDefault();
447                     }, false);
448                 })(eventName, _pointerEventsMap[eventName]);
449             }
450         }
451 
452         if(supportTouches) {
453             //register canvas touch event
454             cc._addEventListener(element,"touchstart", function (event) {
455                 if (!event.changedTouches) return;
456 
457                 var pos = selfPointer.getHTMLElementPosition(element);
458                 pos.left -= document.body.scrollLeft;
459                 pos.top -= document.body.scrollTop;
460                 selfPointer.handleTouchesBegin(selfPointer.getTouchesByEvent(event, pos));
461                 event.stopPropagation();
462                 event.preventDefault();
463             }, false);
464 
465             cc._addEventListener(element, "touchmove", function (event) {
466                 if (!event.changedTouches) return;
467 
468                 var pos = selfPointer.getHTMLElementPosition(element);
469                 pos.left -= document.body.scrollLeft;
470                 pos.top -= document.body.scrollTop;
471                 selfPointer.handleTouchesMove(selfPointer.getTouchesByEvent(event, pos));
472                 event.stopPropagation();
473                 event.preventDefault();
474             }, false);
475 
476             cc._addEventListener(element, "touchend", function (event) {
477                 if (!event.changedTouches) return;
478 
479                 var pos = selfPointer.getHTMLElementPosition(element);
480                 pos.left -= document.body.scrollLeft;
481                 pos.top -= document.body.scrollTop;
482                 selfPointer.handleTouchesEnd(selfPointer.getTouchesByEvent(event, pos));
483                 event.stopPropagation();
484                 event.preventDefault();
485             }, false);
486 
487             cc._addEventListener(element, "touchcancel", function (event) {
488                 if (!event.changedTouches) return;
489 
490                 var pos = selfPointer.getHTMLElementPosition(element);
491                 pos.left -= document.body.scrollLeft;
492                 pos.top -= document.body.scrollTop;
493                 locView.handleTouchesCancel(selfPointer.getTouchesByEvent(event, pos));
494                 event.stopPropagation();
495                 event.preventDefault();
496             }, false);
497         }
498 
499         //register keyboard event
500         this._registerKeyboardEvent();
501 
502         //register Accelerometer event
503         this._registerAccelerometerEvent();
504 
505         this._isRegisterEvent = true;
506     },
507 
508     _registerKeyboardEvent: function(){},
509 
510     _registerAccelerometerEvent: function(){},
511 
512     update:function(dt){
513         if(this._accelCurTime > this._accelInterval){
514             this._accelCurTime -= this._accelInterval;
515             cc.eventManager.dispatchEvent(new cc.EventAcceleration(this._acceleration));
516         }
517         this._accelCurTime += dt;
518     }
519 };
520