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