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