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