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             box = {
236                 left: 0,
237                 top: 0,
238                 width: parseInt(element.style.width),
239                 height: parseInt(element.style.height)
240             };
241         }
242         return {
243             left: box.left + win.pageXOffset - docElem.clientLeft,
244             top: box.top + win.pageYOffset - docElem.clientTop,
245             width: box.width,
246             height: box.height
247         };
248     },
249 
250     /**
251      * @function
252      * @param {cc.Touch} touch
253      * @return {cc.Touch}
254      */
255     getPreTouch: function(touch){
256         var preTouch = null;
257         var locPreTouchPool = this._preTouchPool;
258         var id = touch.getID();
259         for (var i = locPreTouchPool.length - 1; i >= 0; i--) {
260             if (locPreTouchPool[i].getID() === id) {
261                 preTouch = locPreTouchPool[i];
262                 break;
263             }
264         }
265         if (!preTouch)
266             preTouch = touch;
267         return preTouch;
268     },
269 
270     /**
271      * @function
272      * @param {cc.Touch} touch
273      */
274     setPreTouch: function(touch){
275         var find = false;
276         var locPreTouchPool = this._preTouchPool;
277         var id = touch.getID();
278         for (var i = locPreTouchPool.length - 1; i >= 0; i--) {
279             if (locPreTouchPool[i].getID() === id) {
280                 locPreTouchPool[i] = touch;
281                 find = true;
282                 break;
283             }
284         }
285         if (!find) {
286             if (locPreTouchPool.length <= 50) {
287                 locPreTouchPool.push(touch);
288             } else {
289                 locPreTouchPool[this._preTouchPoolPointer] = touch;
290                 this._preTouchPoolPointer = (this._preTouchPoolPointer + 1) % 50;
291             }
292         }
293     },
294 
295     /**
296      * @function
297      * @param {Number} tx
298      * @param {Number} ty
299      * @param {cc.Point} pos
300      * @return {cc.Touch}
301      */
302     getTouchByXY: function(tx, ty, pos){
303         var locPreTouch = this._preTouchPoint;
304         var location = this._glView.convertToLocationInView(tx, ty, pos);
305         var touch = new cc.Touch(location.x,  location.y);
306         touch._setPrevPoint(locPreTouch.x, locPreTouch.y);
307         locPreTouch.x = location.x;
308         locPreTouch.y = location.y;
309         return touch;
310     },
311 
312     /**
313      * @function
314      * @param {cc.Point} location
315      * @param {cc.Point} pos
316      * @param {Number} eventType
317      * @returns {cc.EventMouse}
318      */
319     getMouseEvent: function(location, pos, eventType){
320         var locPreMouse = this._prevMousePoint;
321         this._glView._convertMouseToLocationInView(location, pos);
322         var mouseEvent = new cc.EventMouse(eventType);
323         mouseEvent.setLocation(location.x, location.y);
324         mouseEvent._setPrevCursor(locPreMouse.x, locPreMouse.y);
325         locPreMouse.x = location.x;
326         locPreMouse.y = location.y;
327         return mouseEvent;
328     },
329 
330     /**
331      * @function
332      * @param {Touch} event
333      * @param {cc.Point} pos
334      * @return {cc.Point}
335      */
336     getPointByEvent: function(event, pos){
337         if (event.pageX != null)  //not available in <= IE8
338             return {x: event.pageX, y: event.pageY};
339 
340         pos.left -= document.body.scrollLeft;
341         pos.top -= document.body.scrollTop;
342         return {x: event.clientX, y: event.clientY};
343     },
344 
345     /**
346      * @function
347      * @param {Touch} event
348      * @param {cc.Point} pos
349      * @returns {Array}
350      */
351     getTouchesByEvent: function(event, pos){
352         var touchArr = [], locView = this._glView;
353         var touch_event, touch, preLocation;
354         var locPreTouch = this._preTouchPoint;
355 
356         var length = event.changedTouches.length;
357         for (var i = 0; i < length; i++) {
358             touch_event = event.changedTouches[i];
359             if (touch_event) {
360                 var location;
361                 if (cc.sys.BROWSER_TYPE_FIREFOX === cc.sys.browserType)
362                     location = locView.convertToLocationInView(touch_event.pageX, touch_event.pageY, pos);
363                 else
364                     location = locView.convertToLocationInView(touch_event.clientX, touch_event.clientY, pos);
365                 if (touch_event.identifier != null) {
366                     touch = new cc.Touch(location.x, location.y, touch_event.identifier);
367                     //use Touch Pool
368                     preLocation = this.getPreTouch(touch).getLocation();
369                     touch._setPrevPoint(preLocation.x, preLocation.y);
370                     this.setPreTouch(touch);
371                 } else {
372                     touch = new cc.Touch(location.x, location.y);
373                     touch._setPrevPoint(locPreTouch.x, locPreTouch.y);
374                 }
375                 locPreTouch.x = location.x;
376                 locPreTouch.y = location.y;
377                 touchArr.push(touch);
378             }
379         }
380         return touchArr;
381     },
382 
383     /**
384      * @function
385      * @param {HTMLElement} element
386      */
387     registerSystemEvent: function(element){
388         if(this._isRegisterEvent) return;
389 
390         var locView = this._glView = cc.view;
391         var selfPointer = this;
392         var supportMouse = ('mouse' in cc.sys.capabilities), supportTouches = ('touches' in cc.sys.capabilities);
393 
394         //HACK
395         //  - At the same time to trigger the ontouch event and onmouse event
396         //  - The function will execute 2 times
397         //The known browser:
398         //  liebiao
399         //  miui
400         //  WECHAT
401         var prohibition = false;
402         if( cc.sys.isMobile)
403             prohibition = true;
404 
405         //register touch event
406         if (supportMouse) {
407             window.addEventListener('mousedown', function () {
408                 selfPointer._mousePressed = true;
409             }, false);
410 
411             window.addEventListener('mouseup', function (event) {
412                 if(prohibition) return;
413                 var savePressed = selfPointer._mousePressed;
414                 selfPointer._mousePressed = false;
415 
416                 if(!savePressed)
417                     return;
418 
419                 var pos = selfPointer.getHTMLElementPosition(element);
420                 var location = selfPointer.getPointByEvent(event, pos);
421                 if (!cc.rectContainsPoint(new cc.Rect(pos.left, pos.top, pos.width, pos.height), location)){
422                     selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]);
423 
424                     var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP);
425                     mouseEvent.setButton(event.button);
426                     cc.eventManager.dispatchEvent(mouseEvent);
427                 }
428             }, false);
429 
430             //register canvas mouse event
431             element.addEventListener("mousedown", function (event) {
432                 if(prohibition) return;
433                 selfPointer._mousePressed = true;
434 
435                 var pos = selfPointer.getHTMLElementPosition(element);
436                 var location = selfPointer.getPointByEvent(event, pos);
437 
438                 selfPointer.handleTouchesBegin([selfPointer.getTouchByXY(location.x, location.y, pos)]);
439 
440                 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.DOWN);
441                 mouseEvent.setButton(event.button);
442                 cc.eventManager.dispatchEvent(mouseEvent);
443 
444                 event.stopPropagation();
445                 event.preventDefault();
446                 element.focus();
447             }, false);
448 
449             element.addEventListener("mouseup", function (event) {
450                 if(prohibition) return;
451                 selfPointer._mousePressed = false;
452 
453                 var pos = selfPointer.getHTMLElementPosition(element);
454                 var location = selfPointer.getPointByEvent(event, pos);
455 
456                 selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]);
457 
458                 var mouseEvent = selfPointer.getMouseEvent(location,pos,cc.EventMouse.UP);
459                 mouseEvent.setButton(event.button);
460                 cc.eventManager.dispatchEvent(mouseEvent);
461 
462                 event.stopPropagation();
463                 event.preventDefault();
464             }, false);
465 
466             element.addEventListener("mousemove", function (event) {
467                 if(prohibition) return;
468 
469                 var pos = selfPointer.getHTMLElementPosition(element);
470                 var location = selfPointer.getPointByEvent(event, pos);
471 
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             element.addEventListener("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             element.addEventListener("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                     element.addEventListener(_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             element.addEventListener("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             element.addEventListener("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             element.addEventListener("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             element.addEventListener("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                 selfPointer.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