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