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 _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