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