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