1 /****************************************************************************
  2  Copyright (c) 2011-2012 cocos2d-x.org
  3  Copyright (c) 2013-2015 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._EventListenerVector = cc.Class.extend({
 30     _fixedListeners: null,
 31     _sceneGraphListeners: null,
 32     gt0Index: 0,
 33 
 34     ctor: function () {
 35         this._fixedListeners = [];
 36         this._sceneGraphListeners = [];
 37     },
 38 
 39     size: function () {
 40         return this._fixedListeners.length + this._sceneGraphListeners.length;
 41     },
 42 
 43     empty: function () {
 44         return (this._fixedListeners.length === 0) && (this._sceneGraphListeners.length === 0);
 45     },
 46 
 47     push: function (listener) {
 48         if (listener._getFixedPriority() === 0)
 49             this._sceneGraphListeners.push(listener);
 50         else
 51             this._fixedListeners.push(listener);
 52     },
 53 
 54     clearSceneGraphListeners: function () {
 55         this._sceneGraphListeners.length = 0;
 56     },
 57 
 58     clearFixedListeners: function () {
 59         this._fixedListeners.length = 0;
 60     },
 61 
 62     clear: function () {
 63         this._sceneGraphListeners.length = 0;
 64         this._fixedListeners.length = 0;
 65     },
 66 
 67     getFixedPriorityListeners: function () {
 68         return this._fixedListeners;
 69     },
 70 
 71     getSceneGraphPriorityListeners: function () {
 72         return this._sceneGraphListeners;
 73     }
 74 });
 75 
 76 cc.__getListenerID = function (event) {
 77     var eventType = cc.Event, getType = event.getType();
 78     if(getType === eventType.ACCELERATION)
 79         return cc._EventListenerAcceleration.LISTENER_ID;
 80     if(getType === eventType.CUSTOM)
 81         return event.getEventName();
 82     if(getType === eventType.KEYBOARD)
 83         return cc._EventListenerKeyboard.LISTENER_ID;
 84     if(getType === eventType.MOUSE)
 85         return cc._EventListenerMouse.LISTENER_ID;
 86     if(getType === eventType.FOCUS)
 87         return cc._EventListenerFocus.LISTENER_ID;
 88     if(getType === eventType.TOUCH){
 89         // Touch listener is very special, it contains two kinds of listeners, EventListenerTouchOneByOne and EventListenerTouchAllAtOnce.
 90         // return UNKNOWN instead.
 91         cc.log(cc._LogInfos.__getListenerID);
 92     }
 93     return "";
 94 };
 95 
 96 /**
 97  * <p>
 98  *  cc.eventManager is a singleton object which manages event listener subscriptions and event dispatching. <br/>
 99  *                                                                                                              <br/>
100  *  The EventListener list is managed in such way so that event listeners can be added and removed          <br/>
101  *  while events are being dispatched.
102  * </p>
103  * @class
104  * @name cc.eventManager
105  */
106 cc.eventManager = /** @lends cc.eventManager# */{
107     //Priority dirty flag
108     DIRTY_NONE:0,
109     DIRTY_FIXED_PRIORITY:1 <<0,
110     DIRTY_SCENE_GRAPH_PRIORITY : 1<< 1,
111     DIRTY_ALL: 3,
112 
113     _listenersMap: {},
114     _priorityDirtyFlagMap: {},
115     _nodeListenersMap: {},
116     _nodePriorityMap: {},
117     _globalZOrderNodeMap: {},
118     _toAddedListeners: [],
119     _toRemovedListeners: [],
120     _dirtyNodes: [],
121     _inDispatch: 0,
122     _isEnabled: false,
123     _nodePriorityIndex: 0,
124 
125     _internalCustomListenerIDs:[cc.game.EVENT_HIDE, cc.game.EVENT_SHOW],
126 
127     _setDirtyForNode: function (node) {
128         // Mark the node dirty only when there is an event listener associated with it.
129         if (this._nodeListenersMap[node.__instanceId] != null)
130             this._dirtyNodes.push(node);
131         var _children = node.getChildren();
132         for(var i = 0, len = _children.length; i < len; i++)
133             this._setDirtyForNode(_children[i]);
134     },
135 
136     /**
137      * Pauses all listeners which are associated the specified target.
138      * @param {cc.Node} node
139      * @param {Boolean} [recursive=false]
140      */
141     pauseTarget: function (node, recursive) {
142         var listeners = this._nodeListenersMap[node.__instanceId], i, len;
143         if (listeners) {
144             for ( i = 0, len = listeners.length; i < len; i++)
145                 listeners[i]._setPaused(true);
146         }
147         if (recursive === true) {
148             var locChildren = node.getChildren();
149             for ( i = 0, len = locChildren.length; i< len; i++)
150                 this.pauseTarget(locChildren[i], true);
151         }
152     },
153 
154     /**
155      * Resumes all listeners which are associated the specified target.
156      * @param {cc.Node} node
157      * @param {Boolean} [recursive=false]
158      */
159     resumeTarget: function (node, recursive) {
160         var listeners = this._nodeListenersMap[node.__instanceId], i, len;
161         if (listeners){
162             for ( i = 0, len = listeners.length; i < len; i++)
163                 listeners[i]._setPaused(false);
164         }
165         this._setDirtyForNode(node);
166         if (recursive === true) {
167             var locChildren = node.getChildren();
168             for ( i = 0, len = locChildren.length; i< len; i++)
169                 this.resumeTarget(locChildren[i], true);
170         }
171     },
172 
173     _addListener: function (listener) {
174         if (this._inDispatch === 0)
175             this._forceAddEventListener(listener);
176         else
177             this._toAddedListeners.push(listener);
178     },
179 
180     _forceAddEventListener: function (listener) {
181         var listenerID = listener._getListenerID();
182         var listeners = this._listenersMap[listenerID];
183         if (!listeners) {
184             listeners = new cc._EventListenerVector();
185             this._listenersMap[listenerID] = listeners;
186         }
187         listeners.push(listener);
188 
189         if (listener._getFixedPriority() === 0) {
190             this._setDirty(listenerID, this.DIRTY_SCENE_GRAPH_PRIORITY);
191 
192             var node = listener._getSceneGraphPriority();
193             if (node === null)
194                 cc.log(cc._LogInfos.eventManager__forceAddEventListener);
195 
196             this._associateNodeAndEventListener(node, listener);
197             if (node.isRunning())
198                 this.resumeTarget(node);
199         } else
200             this._setDirty(listenerID, this.DIRTY_FIXED_PRIORITY);
201     },
202 
203     _getListeners: function (listenerID) {
204         return this._listenersMap[listenerID];
205     },
206 
207     _updateDirtyFlagForSceneGraph: function () {
208         if (this._dirtyNodes.length === 0)
209             return;
210 
211         var locDirtyNodes = this._dirtyNodes, selListeners, selListener, locNodeListenersMap = this._nodeListenersMap;
212         for (var i = 0, len = locDirtyNodes.length; i < len; i++) {
213             selListeners = locNodeListenersMap[locDirtyNodes[i].__instanceId];
214             if (selListeners) {
215                 for (var j = 0, listenersLen = selListeners.length; j < listenersLen; j++) {
216                     selListener = selListeners[j];
217                     if (selListener)
218                         this._setDirty(selListener._getListenerID(), this.DIRTY_SCENE_GRAPH_PRIORITY);
219                 }
220             }
221         }
222         this._dirtyNodes.length = 0;
223     },
224 
225     _removeAllListenersInVector: function (listenerVector) {
226         if (!listenerVector)
227             return;
228         var selListener;
229         for (var i = 0; i < listenerVector.length;) {
230             selListener = listenerVector[i];
231             selListener._setRegistered(false);
232             if (selListener._getSceneGraphPriority() != null){
233                 this._dissociateNodeAndEventListener(selListener._getSceneGraphPriority(), selListener);
234                 selListener._setSceneGraphPriority(null);   // NULL out the node pointer so we don't have any dangling pointers to destroyed nodes.
235             }
236 
237             if (this._inDispatch === 0)
238                 cc.arrayRemoveObject(listenerVector, selListener);
239             else
240                 ++i;
241         }
242     },
243 
244     _removeListenersForListenerID: function (listenerID) {
245         var listeners = this._listenersMap[listenerID], i;
246         if (listeners) {
247             var fixedPriorityListeners = listeners.getFixedPriorityListeners();
248             var sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners();
249 
250             this._removeAllListenersInVector(sceneGraphPriorityListeners);
251             this._removeAllListenersInVector(fixedPriorityListeners);
252 
253             // Remove the dirty flag according the 'listenerID'.
254             // No need to check whether the dispatcher is dispatching event.
255             delete this._priorityDirtyFlagMap[listenerID];
256 
257             if (!this._inDispatch) {
258                 listeners.clear();
259                 delete this._listenersMap[listenerID];
260             }
261         }
262 
263         var locToAddedListeners = this._toAddedListeners, listener;
264         for (i = 0; i < locToAddedListeners.length;) {
265             listener = locToAddedListeners[i];
266             if (listener && listener._getListenerID() === listenerID)
267                 cc.arrayRemoveObject(locToAddedListeners, listener);
268             else
269                 ++i;
270         }
271     },
272 
273     _sortEventListeners: function (listenerID) {
274         var dirtyFlag = this.DIRTY_NONE,  locFlagMap = this._priorityDirtyFlagMap;
275         if (locFlagMap[listenerID])
276             dirtyFlag = locFlagMap[listenerID];
277 
278         if (dirtyFlag !== this.DIRTY_NONE) {
279             // Clear the dirty flag first, if `rootNode` is null, then set its dirty flag of scene graph priority
280             locFlagMap[listenerID] = this.DIRTY_NONE;
281 
282             if (dirtyFlag & this.DIRTY_FIXED_PRIORITY)
283                 this._sortListenersOfFixedPriority(listenerID);
284 
285             if (dirtyFlag & this.DIRTY_SCENE_GRAPH_PRIORITY){
286                 var rootNode = cc.director.getRunningScene();
287                 if(rootNode)
288                     this._sortListenersOfSceneGraphPriority(listenerID, rootNode);
289                 else
290                     locFlagMap[listenerID] = this.DIRTY_SCENE_GRAPH_PRIORITY;
291             }
292         }
293     },
294 
295     _sortListenersOfSceneGraphPriority: function (listenerID, rootNode) {
296         var listeners = this._getListeners(listenerID);
297         if (!listeners)
298             return;
299 
300         var sceneGraphListener = listeners.getSceneGraphPriorityListeners();
301         if(!sceneGraphListener || sceneGraphListener.length === 0)
302             return;
303 
304         // Reset priority index
305         this._nodePriorityIndex = 0;
306         this._nodePriorityMap = {};
307 
308         this._visitTarget(rootNode, true);
309 
310         // After sort: priority < 0, > 0
311         listeners.getSceneGraphPriorityListeners().sort(this._sortEventListenersOfSceneGraphPriorityDes);
312     },
313 
314     _sortEventListenersOfSceneGraphPriorityDes : function(l1, l2){
315         var locNodePriorityMap = cc.eventManager._nodePriorityMap, node1 = l1._getSceneGraphPriority(),
316             node2 = l2._getSceneGraphPriority();
317         if( !l2 || !node2 || !locNodePriorityMap[node2.__instanceId] )
318             return -1;
319         else if( !l1 || !node1 || !locNodePriorityMap[node1.__instanceId] )
320             return 1;
321         return locNodePriorityMap[l2._getSceneGraphPriority().__instanceId] - locNodePriorityMap[l1._getSceneGraphPriority().__instanceId];
322     },
323 
324     _sortListenersOfFixedPriority: function (listenerID) {
325         var listeners = this._listenersMap[listenerID];
326         if (!listeners)
327             return;
328 
329         var fixedListeners = listeners.getFixedPriorityListeners();
330         if(!fixedListeners || fixedListeners.length === 0)
331             return;
332         // After sort: priority < 0, > 0
333         fixedListeners.sort(this._sortListenersOfFixedPriorityAsc);
334 
335         // FIXME: Should use binary search
336         var index = 0;
337         for (var len = fixedListeners.length; index < len;) {
338             if (fixedListeners[index]._getFixedPriority() >= 0)
339                 break;
340             ++index;
341         }
342         listeners.gt0Index = index;
343     },
344 
345     _sortListenersOfFixedPriorityAsc: function (l1, l2) {
346         return l1._getFixedPriority() - l2._getFixedPriority();
347     },
348 
349     _onUpdateListeners: function (listenerID) {
350         var listeners = this._listenersMap[listenerID];
351         if (!listeners)
352             return;
353 
354         var fixedPriorityListeners = listeners.getFixedPriorityListeners();
355         var sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners();
356         var i, selListener, idx, toRemovedListeners = this._toRemovedListeners;
357 
358         if (sceneGraphPriorityListeners) {
359             for (i = 0; i < sceneGraphPriorityListeners.length;) {
360                 selListener = sceneGraphPriorityListeners[i];
361                 if (!selListener._isRegistered()) {
362                     cc.arrayRemoveObject(sceneGraphPriorityListeners, selListener);
363                     // if item in toRemove list, remove it from the list
364                     idx = toRemovedListeners.indexOf(selListener);
365                     if(idx !== -1)
366                         toRemovedListeners.splice(idx, 1);
367                 } else
368                     ++i;
369             }
370         }
371 
372         if (fixedPriorityListeners) {
373             for (i = 0; i < fixedPriorityListeners.length;) {
374                 selListener = fixedPriorityListeners[i];
375                 if (!selListener._isRegistered()) {
376                     cc.arrayRemoveObject(fixedPriorityListeners, selListener);
377                     // if item in toRemove list, remove it from the list
378                     idx = toRemovedListeners.indexOf(selListener);
379                     if(idx !== -1)
380                         toRemovedListeners.splice(idx, 1);
381                 } else
382                     ++i;
383             }
384         }
385 
386         if (sceneGraphPriorityListeners && sceneGraphPriorityListeners.length === 0)
387             listeners.clearSceneGraphListeners();
388 
389         if (fixedPriorityListeners && fixedPriorityListeners.length === 0)
390             listeners.clearFixedListeners();
391     },
392 
393     _updateListeners: function (event) {
394         var locInDispatch = this._inDispatch;
395         cc.assert(locInDispatch > 0, cc._LogInfos.EventManager__updateListeners);
396 
397         if(locInDispatch > 1)
398             return;
399 
400         if (event.getType() === cc.Event.TOUCH) {
401             this._onUpdateListeners(cc._EventListenerTouchOneByOne.LISTENER_ID);
402             this._onUpdateListeners(cc._EventListenerTouchAllAtOnce.LISTENER_ID);
403         } else
404             this._onUpdateListeners(cc.__getListenerID(event));
405 
406         cc.assert(locInDispatch === 1, cc._LogInfos.EventManager__updateListeners_2);
407         var locListenersMap = this._listenersMap, locPriorityDirtyFlagMap = this._priorityDirtyFlagMap;
408         for (var selKey in locListenersMap) {
409             if (locListenersMap[selKey].empty()) {
410                 delete locPriorityDirtyFlagMap[selKey];
411                 delete locListenersMap[selKey];
412             }
413         }
414 
415         var locToAddedListeners = this._toAddedListeners;
416         if (locToAddedListeners.length !== 0) {
417             for (var i = 0, len = locToAddedListeners.length; i < len; i++)
418                 this._forceAddEventListener(locToAddedListeners[i]);
419             this._toAddedListeners.length = 0;
420         }
421         if(this._toRemovedListeners.length !== 0)
422             this._cleanToRemovedListeners();
423     },
424 
425     //Remove all listeners in _toRemoveListeners list and cleanup
426     _cleanToRemovedListeners: function(){
427         var toRemovedListeners = this._toRemovedListeners;
428         for(var i = 0; i< toRemovedListeners.length; i++){
429             var selListener = toRemovedListeners[i];
430             var listeners = this._listenersMap[selListener._getListenerID()];
431             if(!listeners)
432                 continue;
433 
434             var idx, fixedPriorityListeners = listeners.getFixedPriorityListeners(),
435                 sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners();
436 
437             if(sceneGraphPriorityListeners){
438                 idx = sceneGraphPriorityListeners.indexOf(selListener);
439                 if (idx !== -1) {
440                     sceneGraphPriorityListeners.splice(idx, 1);
441                 }
442             }
443             if(fixedPriorityListeners){
444                 idx = fixedPriorityListeners.indexOf(selListener);
445                 if (idx !== -1) {
446                     fixedPriorityListeners.splice(idx, 1);
447                 }
448             }
449         }
450         toRemovedListeners.length = 0;
451     },
452 
453     _onTouchEventCallback: function(listener, argsObj){
454         // Skip if the listener was removed.
455         if (!listener._isRegistered)
456             return false;
457 
458         var event = argsObj.event, selTouch = argsObj.selTouch;
459         event._setCurrentTarget(listener._node);
460 
461         var isClaimed = false, removedIdx;
462         var getCode = event.getEventCode(), eventCode = cc.EventTouch.EventCode;
463         if (getCode === eventCode.BEGAN) {
464             if (listener.onTouchBegan) {
465                 isClaimed = listener.onTouchBegan(selTouch, event);
466                 if (isClaimed && listener._registered)
467                     listener._claimedTouches.push(selTouch);
468             }
469         } else if (listener._claimedTouches.length > 0
470             && ((removedIdx = listener._claimedTouches.indexOf(selTouch)) !== -1)) {
471             isClaimed = true;
472             if(getCode === eventCode.MOVED && listener.onTouchMoved){
473                 listener.onTouchMoved(selTouch, event);
474             } else if(getCode === eventCode.ENDED){
475                 if (listener.onTouchEnded)
476                     listener.onTouchEnded(selTouch, event);
477                 if (listener._registered)
478                     listener._claimedTouches.splice(removedIdx, 1);
479             } else if(getCode === eventCode.CANCELLED){
480                 if (listener.onTouchCancelled)
481                     listener.onTouchCancelled(selTouch, event);
482                 if (listener._registered)
483                     listener._claimedTouches.splice(removedIdx, 1);
484             }
485         }
486 
487         // If the event was stopped, return directly.
488         if (event.isStopped()) {
489             cc.eventManager._updateListeners(event);
490             return true;
491         }
492 
493         if (isClaimed && listener._registered && listener.swallowTouches) {
494             if (argsObj.needsMutableSet)
495                 argsObj.touches.splice(selTouch, 1);
496             return true;
497         }
498         return false;
499     },
500 
501     _dispatchTouchEvent: function (event) {
502         this._sortEventListeners(cc._EventListenerTouchOneByOne.LISTENER_ID);
503         this._sortEventListeners(cc._EventListenerTouchAllAtOnce.LISTENER_ID);
504 
505         var oneByOneListeners = this._getListeners(cc._EventListenerTouchOneByOne.LISTENER_ID);
506         var allAtOnceListeners = this._getListeners(cc._EventListenerTouchAllAtOnce.LISTENER_ID);
507 
508         // If there aren't any touch listeners, return directly.
509         if (null === oneByOneListeners && null === allAtOnceListeners)
510             return;
511 
512         var originalTouches = event.getTouches(), mutableTouches = cc.copyArray(originalTouches);
513         var oneByOneArgsObj = {event: event, needsMutableSet: (oneByOneListeners && allAtOnceListeners), touches: mutableTouches, selTouch: null};
514 
515         //
516         // process the target handlers 1st
517         //
518         if (oneByOneListeners) {
519             for (var i = 0; i < originalTouches.length; i++) {
520                 oneByOneArgsObj.selTouch = originalTouches[i];
521                 this._dispatchEventToListeners(oneByOneListeners, this._onTouchEventCallback, oneByOneArgsObj);
522                 if (event.isStopped())
523                     return;
524             }
525         }
526 
527         //
528         // process standard handlers 2nd
529         //
530         if (allAtOnceListeners && mutableTouches.length > 0) {
531             this._dispatchEventToListeners(allAtOnceListeners, this._onTouchesEventCallback, {event: event, touches: mutableTouches});
532             if (event.isStopped())
533                 return;
534         }
535         this._updateListeners(event);
536     },
537 
538     _onTouchesEventCallback: function (listener, callbackParams) {
539         // Skip if the listener was removed.
540         if (!listener._registered)
541             return false;
542 
543         var eventCode = cc.EventTouch.EventCode, event = callbackParams.event, touches = callbackParams.touches, getCode = event.getEventCode();
544         event._setCurrentTarget(listener._node);
545         if(getCode === eventCode.BEGAN && listener.onTouchesBegan)
546             listener.onTouchesBegan(touches, event);
547         else if(getCode === eventCode.MOVED && listener.onTouchesMoved)
548             listener.onTouchesMoved(touches, event);
549         else if(getCode === eventCode.ENDED && listener.onTouchesEnded)
550             listener.onTouchesEnded(touches, event);
551         else if(getCode === eventCode.CANCELLED && listener.onTouchesCancelled)
552             listener.onTouchesCancelled(touches, event);
553 
554         // If the event was stopped, return directly.
555         if (event.isStopped()) {
556             cc.eventManager._updateListeners(event);
557             return true;
558         }
559         return false;
560     },
561 
562     _associateNodeAndEventListener: function (node, listener) {
563         var listeners = this._nodeListenersMap[node.__instanceId];
564         if (!listeners) {
565             listeners = [];
566             this._nodeListenersMap[node.__instanceId] = listeners;
567         }
568         listeners.push(listener);
569     },
570 
571     _dissociateNodeAndEventListener: function (node, listener) {
572         var listeners = this._nodeListenersMap[node.__instanceId];
573         if (listeners) {
574             cc.arrayRemoveObject(listeners, listener);
575             if (listeners.length === 0)
576                 delete this._nodeListenersMap[node.__instanceId];
577         }
578     },
579 
580     _dispatchEventToListeners: function (listeners, onEvent, eventOrArgs) {
581         var shouldStopPropagation = false;
582         var fixedPriorityListeners = listeners.getFixedPriorityListeners();
583         var sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners();
584 
585         var i = 0, j, selListener;
586         if (fixedPriorityListeners) {  // priority < 0
587             if (fixedPriorityListeners.length !== 0) {
588                 for (; i < listeners.gt0Index; ++i) {
589                     selListener = fixedPriorityListeners[i];
590                     if (selListener.isEnabled() && !selListener._isPaused() && selListener._isRegistered() && onEvent(selListener, eventOrArgs)) {
591                         shouldStopPropagation = true;
592                         break;
593                     }
594                 }
595             }
596         }
597 
598         if (sceneGraphPriorityListeners && !shouldStopPropagation) {    // priority == 0, scene graph priority
599             for (j = 0; j < sceneGraphPriorityListeners.length; j++) {
600                 selListener = sceneGraphPriorityListeners[j];
601                 if (selListener.isEnabled() && !selListener._isPaused() && selListener._isRegistered() && onEvent(selListener, eventOrArgs)) {
602                     shouldStopPropagation = true;
603                     break;
604                 }
605             }
606         }
607 
608         if (fixedPriorityListeners && !shouldStopPropagation) {    // priority > 0
609             for (; i < fixedPriorityListeners.length; ++i) {
610                 selListener = fixedPriorityListeners[i];
611                 if (selListener.isEnabled() && !selListener._isPaused() && selListener._isRegistered() && onEvent(selListener, eventOrArgs)) {
612                     shouldStopPropagation = true;
613                     break;
614                 }
615             }
616         }
617     },
618 
619     _setDirty: function (listenerID, flag) {
620         var locDirtyFlagMap = this._priorityDirtyFlagMap;
621         if (locDirtyFlagMap[listenerID] == null)
622             locDirtyFlagMap[listenerID] = flag;
623         else
624             locDirtyFlagMap[listenerID] = flag | locDirtyFlagMap[listenerID];
625     },
626 
627     _visitTarget: function (node, isRootNode) {
628         var children = node.getChildren(), i = 0;
629         var childrenCount = children.length, locGlobalZOrderNodeMap = this._globalZOrderNodeMap, locNodeListenersMap = this._nodeListenersMap;
630 
631         if (childrenCount > 0) {
632             var child;
633             // visit children zOrder < 0
634             for (; i < childrenCount; i++) {
635                 child = children[i];
636                 if (child && child.getLocalZOrder() < 0)
637                     this._visitTarget(child, false);
638                 else
639                     break;
640             }
641 
642             if (locNodeListenersMap[node.__instanceId] != null) {
643                 if (!locGlobalZOrderNodeMap[node.getGlobalZOrder()])
644                     locGlobalZOrderNodeMap[node.getGlobalZOrder()] = [];
645                 locGlobalZOrderNodeMap[node.getGlobalZOrder()].push(node.__instanceId);
646             }
647 
648             for (; i < childrenCount; i++) {
649                 child = children[i];
650                 if (child)
651                     this._visitTarget(child, false);
652             }
653         } else {
654             if (locNodeListenersMap[node.__instanceId] != null) {
655                 if (!locGlobalZOrderNodeMap[node.getGlobalZOrder()])
656                     locGlobalZOrderNodeMap[node.getGlobalZOrder()] = [];
657                 locGlobalZOrderNodeMap[node.getGlobalZOrder()].push(node.__instanceId);
658             }
659         }
660 
661         if (isRootNode) {
662             var globalZOrders = [];
663             for (var selKey in locGlobalZOrderNodeMap)
664                 globalZOrders.push(selKey);
665 
666             globalZOrders.sort(this._sortNumberAsc);
667 
668             var zOrdersLen = globalZOrders.length, selZOrders, j, locNodePriorityMap = this._nodePriorityMap;
669             for (i = 0; i < zOrdersLen; i++) {
670                 selZOrders = locGlobalZOrderNodeMap[globalZOrders[i]];
671                 for (j = 0; j < selZOrders.length; j++)
672                     locNodePriorityMap[selZOrders[j]] = ++this._nodePriorityIndex;
673             }
674             this._globalZOrderNodeMap = {};
675         }
676     },
677 
678     _sortNumberAsc : function (a, b) {
679         return a - b;
680     },
681 
682     /**
683      * <p>
684      * Adds a event listener for a specified event.                                                                                                            <br/>
685      * if the parameter "nodeOrPriority" is a node, it means to add a event listener for a specified event with the priority of scene graph.                   <br/>
686      * if the parameter "nodeOrPriority" is a Number, it means to add a event listener for a specified event with the fixed priority.                          <br/>
687      * </p>
688      * @param {cc.EventListener|Object} listener The listener of a specified event or a object of some event parameters.
689      * @param {cc.Node|Number} nodeOrPriority The priority of the listener is based on the draw order of this node or fixedPriority The fixed priority of the listener.
690      * @note  The priority of scene graph will be fixed value 0. So the order of listener item in the vector will be ' <0, scene graph (0 priority), >0'.
691      *         A lower priority will be called before the ones that have a higher value. 0 priority is forbidden for fixed priority since it's used for scene graph based priority.
692      *         The listener must be a cc.EventListener object when adding a fixed priority listener, because we can't remove a fixed priority listener without the listener handler,
693      *         except calls removeAllListeners().
694      * @return {cc.EventListener} Return the listener. Needed in order to remove the event from the dispatcher.
695      */
696     addListener: function (listener, nodeOrPriority) {
697         cc.assert(listener && nodeOrPriority, cc._LogInfos.eventManager_addListener_2);
698         if(!(listener instanceof cc.EventListener)){
699             cc.assert(!cc.isNumber(nodeOrPriority), cc._LogInfos.eventManager_addListener_3);
700             listener = cc.EventListener.create(listener);
701         } else {
702             if(listener._isRegistered()){
703                 cc.log(cc._LogInfos.eventManager_addListener_4);
704                 return;
705             }
706         }
707 
708         if (!listener.checkAvailable())
709             return;
710 
711         if (cc.isNumber(nodeOrPriority)) {
712             if (nodeOrPriority === 0) {
713                 cc.log(cc._LogInfos.eventManager_addListener);
714                 return;
715             }
716 
717             listener._setSceneGraphPriority(null);
718             listener._setFixedPriority(nodeOrPriority);
719             listener._setRegistered(true);
720             listener._setPaused(false);
721             this._addListener(listener);
722         } else {
723             listener._setSceneGraphPriority(nodeOrPriority);
724             listener._setFixedPriority(0);
725             listener._setRegistered(true);
726             this._addListener(listener);
727         }
728 
729         return listener;
730     },
731 
732     /**
733      * Adds a Custom event listener. It will use a fixed priority of 1.
734      * @param {string} eventName
735      * @param {function} callback
736      * @return {cc.EventListener} the generated event. Needed in order to remove the event from the dispatcher
737      */
738     addCustomListener: function (eventName, callback) {
739         var listener = new cc._EventListenerCustom(eventName, callback);
740         this.addListener(listener, 1);
741         return listener;
742     },
743 
744     /**
745      * Remove a listener
746      * @param {cc.EventListener} listener an event listener or a registered node target
747      */
748     removeListener: function (listener) {
749         if (listener == null)
750             return;
751 
752         var isFound, locListener = this._listenersMap;
753         for (var selKey in locListener) {
754             var listeners = locListener[selKey];
755             var fixedPriorityListeners = listeners.getFixedPriorityListeners(), sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners();
756 
757             isFound = this._removeListenerInVector(sceneGraphPriorityListeners, listener);
758             if (isFound){
759                 // fixed #4160: Dirty flag need to be updated after listeners were removed.
760                this._setDirty(listener._getListenerID(), this.DIRTY_SCENE_GRAPH_PRIORITY);
761             }else{
762                 isFound = this._removeListenerInVector(fixedPriorityListeners, listener);
763                 if (isFound)
764                     this._setDirty(listener._getListenerID(), this.DIRTY_FIXED_PRIORITY);
765             }
766 
767             if (listeners.empty()) {
768                 delete this._priorityDirtyFlagMap[listener._getListenerID()];
769                 delete locListener[selKey];
770             }
771 
772             if (isFound)
773                 break;
774         }
775 
776         if (!isFound) {
777             var locToAddedListeners = this._toAddedListeners;
778             for (var i = 0, len = locToAddedListeners.length; i < len; i++) {
779                 var selListener = locToAddedListeners[i];
780                 if (selListener === listener) {
781                     cc.arrayRemoveObject(locToAddedListeners, selListener);
782                     selListener._setRegistered(false);
783                     break;
784                 }
785             }
786         }
787     },
788 
789     _removeListenerInCallback: function(listeners, callback){
790         if (listeners == null)
791             return false;
792 
793         for (var i = 0, len = listeners.length; i < len; i++) {
794             var selListener = listeners[i];
795             if (selListener._onCustomEvent === callback || selListener._onEvent === callback) {
796                 selListener._setRegistered(false);
797                 if (selListener._getSceneGraphPriority() != null){
798                     this._dissociateNodeAndEventListener(selListener._getSceneGraphPriority(), selListener);
799                     selListener._setSceneGraphPriority(null);         // NULL out the node pointer so we don't have any dangling pointers to destroyed nodes.
800                 }
801 
802                 if (this._inDispatch === 0)
803                     cc.arrayRemoveObject(listeners, selListener);
804                 return true;
805             }
806         }
807         return false;
808     },
809 
810     _removeListenerInVector : function(listeners, listener){
811         if (listeners == null)
812             return false;
813 
814         for (var i = 0, len = listeners.length; i < len; i++) {
815             var selListener = listeners[i];
816             if (selListener === listener) {
817                 selListener._setRegistered(false);
818                 if (selListener._getSceneGraphPriority() != null){
819                     this._dissociateNodeAndEventListener(selListener._getSceneGraphPriority(), selListener);
820                     selListener._setSceneGraphPriority(null);         // NULL out the node pointer so we don't have any dangling pointers to destroyed nodes.
821                 }
822 
823                 if (this._inDispatch === 0)
824                     cc.arrayRemoveObject(listeners, selListener);
825                 else
826                     this._toRemovedListeners.push(selListener);
827                 return true;
828             }
829         }
830         return false;
831     },
832 
833     /**
834      * Removes all listeners with the same event listener type or removes all listeners of a node
835      * @param {Number|cc.Node} listenerType listenerType or a node
836      * @param {Boolean} [recursive=false]
837      */
838     removeListeners: function (listenerType, recursive) {
839         var _t = this;
840         if (listenerType instanceof cc.Node) {
841             // Ensure the node is removed from these immediately also.
842             // Don't want any dangling pointers or the possibility of dealing with deleted objects..
843             delete _t._nodePriorityMap[listenerType.__instanceId];
844             cc.arrayRemoveObject(_t._dirtyNodes, listenerType);
845             var listeners = _t._nodeListenersMap[listenerType.__instanceId], i;
846             if (listeners) {
847                 var listenersCopy = cc.copyArray(listeners);
848                 for (i = 0; i < listenersCopy.length; i++)
849                     _t.removeListener(listenersCopy[i]);
850                 listenersCopy.length = 0;
851             }
852 
853             // Bug fix: ensure there are no references to the node in the list of listeners to be added.
854             // If we find any listeners associated with the destroyed node in this list then remove them.
855             // This is to catch the scenario where the node gets destroyed before it's listener
856             // is added into the event dispatcher fully. This could happen if a node registers a listener
857             // and gets destroyed while we are dispatching an event (touch etc.)
858             var locToAddedListeners = _t._toAddedListeners;
859             for (i = 0; i < locToAddedListeners.length; ) {
860                 var listener = locToAddedListeners[i];
861                 if (listener._getSceneGraphPriority() === listenerType) {
862                     listener._setSceneGraphPriority(null);                      // Ensure no dangling ptr to the target node.
863                     listener._setRegistered(false);
864                     locToAddedListeners.splice(i, 1);
865                 } else
866                     ++i;
867             }
868 
869             if (recursive === true) {
870                 var locChildren = listenerType.getChildren(), len;
871                 for (i = 0, len = locChildren.length; i< len; i++)
872                     _t.removeListeners(locChildren[i], true);
873             }
874         } else {
875             if (listenerType === cc.EventListener.TOUCH_ONE_BY_ONE)
876                 _t._removeListenersForListenerID(cc._EventListenerTouchOneByOne.LISTENER_ID);
877             else if (listenerType === cc.EventListener.TOUCH_ALL_AT_ONCE)
878                 _t._removeListenersForListenerID(cc._EventListenerTouchAllAtOnce.LISTENER_ID);
879             else if (listenerType === cc.EventListener.MOUSE)
880                 _t._removeListenersForListenerID(cc._EventListenerMouse.LISTENER_ID);
881             else if (listenerType === cc.EventListener.ACCELERATION)
882                 _t._removeListenersForListenerID(cc._EventListenerAcceleration.LISTENER_ID);
883             else if (listenerType === cc.EventListener.KEYBOARD)
884                 _t._removeListenersForListenerID(cc._EventListenerKeyboard.LISTENER_ID);
885             else
886                 cc.log(cc._LogInfos.eventManager_removeListeners);
887         }
888     },
889 
890     /**
891      * Removes all custom listeners with the same event name
892      * @param {string} customEventName
893      */
894     removeCustomListeners: function (customEventName) {
895         this._removeListenersForListenerID(customEventName);
896     },
897 
898     /**
899      * Removes all listeners
900      */
901     removeAllListeners: function () {
902         var locListeners = this._listenersMap, locInternalCustomEventIDs = this._internalCustomListenerIDs;
903         for (var selKey in locListeners){
904             if(locInternalCustomEventIDs.indexOf(selKey) === -1)
905                 this._removeListenersForListenerID(selKey);
906         }
907     },
908 
909     /**
910      * Sets listener's priority with fixed value.
911      * @param {cc.EventListener} listener
912      * @param {Number} fixedPriority
913      */
914     setPriority: function (listener, fixedPriority) {
915         if (listener == null)
916             return;
917 
918         var locListeners = this._listenersMap;
919         for (var selKey in locListeners) {
920             var selListeners = locListeners[selKey];
921             var fixedPriorityListeners = selListeners.getFixedPriorityListeners();
922             if (fixedPriorityListeners) {
923                 var found = fixedPriorityListeners.indexOf(listener);
924                 if (found !== -1) {
925                     if(listener._getSceneGraphPriority() != null)
926                         cc.log(cc._LogInfos.eventManager_setPriority);
927                     if (listener._getFixedPriority() !== fixedPriority) {
928                         listener._setFixedPriority(fixedPriority);
929                         this._setDirty(listener._getListenerID(), this.DIRTY_FIXED_PRIORITY);
930                     }
931                     return;
932                 }
933             }
934         }
935     },
936 
937     /**
938      * Whether to enable dispatching events
939      * @param {boolean} enabled
940      */
941     setEnabled: function (enabled) {
942         this._isEnabled = enabled;
943     },
944 
945     /**
946      * Checks whether dispatching events is enabled
947      * @returns {boolean}
948      */
949     isEnabled: function () {
950         return this._isEnabled;
951     },
952 
953     /**
954      * Dispatches the event, also removes all EventListeners marked for deletion from the event dispatcher list.
955      * @param {cc.Event} event
956      */
957     dispatchEvent: function (event) {
958         if (!this._isEnabled)
959             return;
960 
961         this._updateDirtyFlagForSceneGraph();
962         this._inDispatch++;
963         if(!event || !event.getType)
964             throw new Error("event is undefined");
965         if (event.getType() === cc.Event.TOUCH) {
966             this._dispatchTouchEvent(event);
967             this._inDispatch--;
968             return;
969         }
970 
971         var listenerID = cc.__getListenerID(event);
972         this._sortEventListeners(listenerID);
973         var selListeners = this._listenersMap[listenerID];
974         if (selListeners != null)
975             this._dispatchEventToListeners(selListeners, this._onListenerCallback, event);
976 
977         this._updateListeners(event);
978         this._inDispatch--;
979     },
980 
981     _onListenerCallback: function(listener, event){
982         event._setCurrentTarget(listener._getSceneGraphPriority());
983         listener._onEvent(event);
984         return event.isStopped();
985     },
986 
987     /**
988      * Dispatches a Custom Event with a event name an optional user data
989      * @param {string} eventName
990      * @param {*} optionalUserData
991      */
992     dispatchCustomEvent: function (eventName, optionalUserData) {
993         var ev = new cc.EventCustom(eventName);
994         ev.setUserData(optionalUserData);
995         this.dispatchEvent(ev);
996     }
997 };
998