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