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