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