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