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