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