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