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