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