1//////////////////////////////////////////////////////////////////////////////// 2// 3// ADOBE SYSTEMS INCORPORATED 4// Copyright 2003-2007 Adobe Systems Incorporated 5// All Rights Reserved. 6// 7// NOTICE: Adobe permits you to use, modify, and distribute this file 8// in accordance with the terms of the license agreement accompanying it. 9// 10//////////////////////////////////////////////////////////////////////////////// 11 12package mx.effects 13{ 14 15import flash.display.DisplayObject; 16import flash.display.DisplayObjectContainer; 17import flash.events.Event; 18import flash.events.EventDispatcher; 19import flash.events.FocusEvent; 20import flash.system.ApplicationDomain; 21import flash.utils.Dictionary; 22 23import mx.core.EventPriority; 24import mx.core.FlexGlobals; 25import mx.core.IDeferredInstantiationUIComponent; 26import mx.core.IFlexDisplayObject; 27import mx.core.IUIComponent; 28import mx.core.UIComponent; 29import mx.core.UIComponentCachePolicy; 30import mx.core.mx_internal; 31import mx.events.EffectEvent; 32import mx.events.FlexEvent; 33import mx.events.MoveEvent; 34import mx.events.ResizeEvent; 35import mx.managers.ISystemManager; 36import mx.managers.SystemManager; 37import mx.resources.IResourceManager; 38import mx.resources.ResourceManager; 39import mx.core.IVisualElementContainer; 40import mx.core.IVisualElement; 41 42use namespace mx_internal; 43 44[ResourceBundle("effects")] 45 46/** 47 * The EffectManager class listens for events, such as the <code>show</code> 48 * and <code>move</code> events, dispatched by objects in a Flex application. 49 * For each event, corresponding to an event trigger, it determines if 50 * there is an effect assigned to the object. 51 * If an effect is defined, it plays the effect. 52 * 53 * @langversion 3.0 54 * @playerversion Flash 9 55 * @playerversion AIR 1.1 56 * @productversion Flex 3 57 */ 58public class EffectManager extends EventDispatcher 59{ 60 include "../core/Version.as"; 61 62 //-------------------------------------------------------------------------- 63 // 64 // Class variables 65 // 66 //-------------------------------------------------------------------------- 67 68 /** 69 * @private 70 * Keeps track of all the triggered effects that are currently playing. 71 */ 72 mx_internal static var effectsPlaying:Array /* of EffectNode */ = []; 73 74 /** 75 * @private 76 * Map with event type as key and effectTrigger as value. 77 */ 78 private static var effectTriggersForEvent:Object = {}; 79 80 /** 81 * @private 82 * Map with effectTrigger as key and event type as value. 83 */ 84 private static var eventsForEffectTriggers:Object = {}; 85 86 /** 87 * @private 88 * Array containing miscellaneous info about effect targets. 89 * An element in the array is an Object with three fields: 90 * target - reference to the target 91 * bitmapEffectsCount - number of bitmap effects 92 * currently playing on the target 93 * vectorEffectsCount - number of vector effects 94 * currently playing on the target 95 */ 96 private static var targetsInfo:Array /* of Object */ = []; 97 98 /** 99 * @private 100 * Remember when suspendEventHandling() has been called 101 * without a matching resumeEventHandling(). 102 */ 103 private static var eventHandlingSuspendCount:Number = 0; 104 105 /** 106 * @private 107 * Weak backing storage for the lastEffectCreated instance. 108 */ 109 private static var weakKeys:Dictionary; 110 111 /** 112 * @private 113 * This internal property is currently only used by the ViewStack 114 * component in order to coordinate the hideEffect completion with 115 * ViewStack change logic. It would be best to find a better way 116 * to do this (localized to ViewStack) in the future however. 117 */ 118 mx_internal static function set lastEffectCreated(effect:Effect):void 119 { 120 // We just regenerate our dictionary instead of finding and deleting 121 // the previous key. Faster. 122 weakKeys = new Dictionary(true); 123 weakKeys[effect] = true; 124 } 125 126 /** 127 * @private 128 */ 129 mx_internal static function get lastEffectCreated():Effect 130 { 131 for (var item:* in weakKeys) 132 return item; 133 134 return null; 135 } 136 137 /** 138 * @private 139 * Storage for the resourceManager getter. 140 * This gets initialized on first access, 141 * not at static initialization time, in order to ensure 142 * that the Singleton registry has already been initialized. 143 */ 144 private static var _resourceManager:IResourceManager; 145 146 /** 147 * @private 148 * A reference to the object which manages 149 * all of the application's localized resources. 150 * This is a singleton instance which implements 151 * the IResourceManager interface. 152 */ 153 private static function get resourceManager():IResourceManager 154 { 155 if (!_resourceManager) 156 _resourceManager = ResourceManager.getInstance(); 157 158 return _resourceManager; 159 } 160 161 //-------------------------------------------------------------------------- 162 // 163 // Class methods 164 // 165 //-------------------------------------------------------------------------- 166 167 /** 168 * After this method is called, the EffectManager class ignores 169 * all events, and no effects are triggered, until a call to 170 * <code>resumeEventHandling()</code>. 171 * Used internally so that an effect that is updating the screen 172 * does not cause another effect to be triggered. 173 * 174 * @langversion 3.0 175 * @playerversion Flash 9 176 * @playerversion AIR 1.1 177 * @productversion Flex 3 178 */ 179 public static function suspendEventHandling():void 180 { 181 eventHandlingSuspendCount++; 182 } 183 184 /** 185 * Allows the EffectManager class to resume processing events 186 * after a call to the <code>suspendEventHandling()</code> method. 187 * Used internally in conjunction with the 188 * <code>suspendEventHandling()</code> method 189 * so that an effect that is updating the screen 190 * does not cause another effect to be triggered. 191 * 192 * @langversion 3.0 193 * @playerversion Flash 9 194 * @playerversion AIR 1.1 195 * @productversion Flex 3 196 */ 197 public static function resumeEventHandling():void 198 { 199 eventHandlingSuspendCount--; 200 } 201 202 /** 203 * Immediately ends any effects currently playing on a target. 204 * 205 * @param target The target component on which to end all effects. 206 * 207 * @langversion 3.0 208 * @playerversion Flash 9 209 * @playerversion AIR 1.1 210 * @productversion Flex 3 211 */ 212 public static function endEffectsForTarget(target:IUIComponent):void 213 { 214 // Iterate through the array backward, because calling end() 215 // may cause the element to be removed from the array. 216 var n:int = effectsPlaying.length; 217 for (var i:int = n - 1; i >= 0; i--) 218 { 219 var otherInst:EffectInstance = 220 effectsPlaying[i].instance; 221 if (otherInst.target == target) 222 otherInst.end(); 223 } 224 } 225 226 /** 227 * @private 228 */ 229 mx_internal static function setStyle(styleProp:String, target:*):void 230 { 231 // Anytime that any UIComponent's style is changed, 232 // check to see if the styleProp that's changing 233 // is an effect trigger (e.g., showEffect). 234 235 var eventName:String = eventsForEffectTriggers[styleProp]; 236 if (eventName != null && eventName != "") 237 { 238 target.addEventListener(eventName, 239 EffectManager.eventHandler, 240 false, EventPriority.EFFECT); 241 } 242 } 243 244 /** 245 * @private 246 * Internal function used to instantiate an effect 247 */ 248 mx_internal static function createEffectForType(target:Object, 249 type:String):Effect 250 { 251 var trigger:String = effectTriggersForEvent[type]; 252 253 if (trigger == "") 254 trigger = type + "Effect"; // For backwards compatibility 255 256 var value:Object = target.getStyle(trigger); 257 258 if (!value) 259 return null; 260 261 if (value is Class) 262 { 263 var cls:Class = Class(value); 264 return new cls(target); 265 } 266 267 // If we don't find the ID on the parent document, then just move on. 268 try 269 { 270 var effectObj:Effect; 271 if (value is String) 272 { 273 var doc:Object = target.parentDocument; 274 // The main Application doesn't have a parentDocument. 275 if (!doc) 276 doc = FlexGlobals.topLevelApplication; 277 effectObj = doc[value]; 278 } 279 else if (value is Effect) 280 { 281 effectObj = Effect(value); 282 } 283 284 if (effectObj) 285 { 286 effectObj.target = target; 287 return effectObj; 288 } 289 } 290 catch(e:Error) 291 { 292 } 293 294 var effectClass:Class; 295 if (target is UIComponent && target.moduleFactory) 296 { 297 // only UIComponents have moduleFactories 298 var appDomain:ApplicationDomain = 299 target.moduleFactory.info()["currentDomain"]; 300 if (appDomain.hasDefinition("mx.effects." + value)) 301 effectClass = Class(appDomain.getDefinition("mx.effects." + value)); 302 } 303 if (!effectClass) 304 effectClass = Class(target.systemManager.getDefinitionByName( 305 "mx.effects." + value)); 306 307 if (effectClass) 308 return new effectClass(target); 309 310 return null; 311 } 312 313 /** 314 * @private 315 * Internal function used while playing effects 316 */ 317 private static function animateSameProperty(a:Effect, b:Effect, 318 c:EffectInstance):Boolean 319 { 320 // This function returns true if "a" and "b" animate 321 // the same property of the same object. 322 323 if (a.target == c.target) 324 { 325 var aProps:Array = a.getAffectedProperties(); 326 var bProps:Array = b.getAffectedProperties(); 327 328 var n:int = aProps.length; 329 var m:int = bProps.length; 330 331 for (var i:int = 0; i < n; i++) 332 { 333 for (var j:int = 0; j < m; j++) 334 { 335 if (aProps[i] == bProps[j]) 336 return true; 337 } 338 } 339 } 340 341 return false; 342 } 343 344 /** 345 * @private 346 * Should be called by an effect instance before it starts playing, 347 * to suggest bitmap caching on the target. 348 * E.g. Fade calls this function in its play(). 349 */ 350 mx_internal static function startBitmapEffect(target:IUIComponent):void 351 { 352 cacheOrUncacheTargetAsBitmap(target, true, true); 353 } 354 355 /** 356 * @private 357 * Should be called by an effect instance after it has finished playing, 358 * to suggest that the cached bitmap for the target can be freed. 359 * E.g. Fade calls this function in its onTweenEnd(). 360 */ 361 mx_internal static function endBitmapEffect(target:IUIComponent):void 362 { 363 cacheOrUncacheTargetAsBitmap(target, false, true); 364 } 365 366 /** 367 * @private 368 * Should be called by an effect instance before it starts playing, to 369 * suggest that bitmap caching should be turned off on the target. 370 * E.g. Resize calls this function in its play(). 371 */ 372 mx_internal static function startVectorEffect(target:IUIComponent):void 373 { 374 cacheOrUncacheTargetAsBitmap(target, true, false); 375 } 376 377 /** 378 * @private 379 * Should be called by an effect instance after it has finished playing, 380 * to suggest that bitmap caching may be turned back on on the target. 381 * E.g. Resize calls this function in its onTweenEnd(). 382 */ 383 mx_internal static function endVectorEffect(target:IUIComponent):void 384 { 385 cacheOrUncacheTargetAsBitmap(target, false, false); 386 } 387 388 /** 389 * @private 390 * Cache or uncache the target as a bitmap depending on which effects are 391 * currently playing on the target. 392 * 393 * @param target The effect target. 394 * 395 * @param effectStart Whether this is the starting of the effect. 396 * false means it's the ending of the effect. 397 * 398 * @param bitmapEffect Whether this is a bitmap effect. 399 * false means it's a vector effect (like resize, zoom, etc.) 400 * that wants the target object to be uncached. 401 */ 402 private static function cacheOrUncacheTargetAsBitmap( 403 target:IUIComponent, 404 effectStart:Boolean = true, 405 bitmapEffect:Boolean = true):void 406 { 407 var n:int; 408 var i:int; 409 410 // Object containing information about the target. 411 var info:Object = null; 412 413 n = targetsInfo.length; 414 for (i = 0; i < n; i++) 415 { 416 if (targetsInfo[i].target == target) 417 { 418 info = targetsInfo[i]; 419 break; 420 } 421 } 422 423 // If no info object is available, create an object and push it 424 // into the array. 425 if (!info) 426 { 427 info = 428 { 429 target: target, 430 bitmapEffectsCount: 0, 431 vectorEffectsCount: 0 432 }; 433 434 targetsInfo.push(info); 435 } 436 437 if (effectStart) 438 { 439 if (bitmapEffect) 440 { 441 info.bitmapEffectsCount++; 442 443 // If no vector effects are currently playing, 444 // cache the target. 445 if (info.vectorEffectsCount == 0 && 446 target is IDeferredInstantiationUIComponent) 447 { 448 IDeferredInstantiationUIComponent(target).cacheHeuristic = true; 449 } 450 } 451 else 452 { 453 // If a vector effect started playing, forcibly uncache 454 // the target regardless of anything else. 455 if (info.vectorEffectsCount++ == 0 && 456 target is IDeferredInstantiationUIComponent && 457 IDeferredInstantiationUIComponent(target).cachePolicy == UIComponentCachePolicy.AUTO) 458 { 459 target.cacheAsBitmap = false; 460 } 461 } 462 } 463 else // effect end 464 { 465 if (bitmapEffect) 466 { 467 if (info.bitmapEffectsCount != 0) 468 info.bitmapEffectsCount--; 469 470 if (target is IDeferredInstantiationUIComponent) 471 IDeferredInstantiationUIComponent(target).cacheHeuristic = false; 472 } 473 else 474 { 475 if (info.vectorEffectsCount != 0) 476 { 477 // If no more vector effects are playing but bitmap 478 // effects are still playing, cache the target. 479 if (--info.vectorEffectsCount == 0 && 480 info.bitmapEffectsCount != 0) 481 { 482 // Crank up the counter. 483 n = info.bitmapEffectsCount; 484 for (i = 0; i < n; i++) 485 { 486 if (target is IDeferredInstantiationUIComponent) 487 IDeferredInstantiationUIComponent(target).cacheHeuristic = true; 488 } 489 } 490 } 491 } 492 493 if (info.bitmapEffectsCount == 0 && info.vectorEffectsCount == 0) 494 { 495 // No more effects are playing on this target, so discard the 496 // info object (should speed up lookups). 497 n = targetsInfo.length; 498 for (i = 0; i < n; i++) 499 { 500 if (targetsInfo[i].target == target) 501 { 502 targetsInfo.splice(i, 1); 503 break; 504 } 505 } 506 } 507 } 508 } 509 510 /** 511 * @private 512 * Called in code generated by MXML compiler. 513 */ 514 mx_internal static function registerEffectTrigger(name:String, 515 event:String):void 516 { 517 if (name != "") 518 { 519 if (event == "") 520 { 521 // For backwards compatibility. 522 var strLen:Number = name.length; 523 if (strLen > 6 && name.substring(strLen - 6) == "Effect") 524 event = name.substring(0, strLen - 6); 525 } 526 527 if (event != "") 528 { 529 effectTriggersForEvent[event] = name; 530 eventsForEffectTriggers[name] = event; 531 } 532 } 533 } 534 535 /** 536 * @private 537 */ 538 mx_internal static function getEventForEffectTrigger(effectTrigger:String):String 539 { 540 if (eventsForEffectTriggers) 541 { 542 try 543 { 544 return eventsForEffectTriggers[effectTrigger]; 545 } 546 catch(e:Error) 547 { 548 return ""; 549 } 550 } 551 552 return ""; 553 } 554 555 //-------------------------------------------------------------------------- 556 // 557 // Class event handlers 558 // 559 //-------------------------------------------------------------------------- 560 561 /** 562 * @private 563 */ 564 mx_internal static function eventHandler(eventObj:Event):void 565 { 566 // If this event fired because an effect is currently playing 567 // (in other words, if an effect was the source of this event), 568 // then don't listen to the effect. 569 if (!(eventObj.currentTarget is IFlexDisplayObject)) 570 return; 571 572 if (eventHandlingSuspendCount > 0) 573 return; 574 575 if (eventObj is FocusEvent && 576 (eventObj.type == FocusEvent.FOCUS_OUT || 577 eventObj.type == FocusEvent.FOCUS_IN)) 578 { 579 var focusEventObj:FocusEvent = FocusEvent(eventObj); 580 if (focusEventObj.relatedObject && 581 (focusEventObj.currentTarget.contains(focusEventObj.relatedObject) || 582 focusEventObj.currentTarget == focusEventObj.relatedObject)) 583 { 584 return; 585 } 586 } 587 588 // Only trigger the event for added and removed if the current target is the same as the target. 589 if ((eventObj.type == Event.ADDED || eventObj.type == Event.REMOVED) && eventObj.target != eventObj.currentTarget) 590 return; 591 592 if (eventObj.type == Event.REMOVED) 593 { 594 if (eventObj.target is UIComponent) 595 { 596 if (UIComponent(eventObj.target).initialized == false) 597 { 598 return; 599 } 600 else if (UIComponent(eventObj.target).isEffectStarted) 601 { 602 for (var i:int = 0; i < UIComponent(eventObj.target)._effectsStarted.length; i++) 603 { 604 // Don't allow removedEffect to trigger more than one effect at a time 605 if (UIComponent(eventObj.target)._effectsStarted[i].triggerEvent.type == Event.REMOVED) 606 return; 607 } 608 } 609 } 610 611 var targ:DisplayObject = eventObj.target as DisplayObject; 612 613 if (targ != null) 614 { 615 var parent:DisplayObjectContainer = targ.parent as DisplayObjectContainer; 616 617 if (parent != null) 618 { 619 var index:int = parent.getChildIndex(targ); 620 if (index >= 0) 621 { 622 if (targ is UIComponent) 623 { 624 // Since we get the "removed" event before the child is actually removed, 625 // we need to delay adding back the child. We must exit the current 626 // script block must exit before the child can be removed. 627 UIComponent(targ).callLater(removedEffectHandler, [targ, parent, index, eventObj]); 628 } 629 } 630 } 631 } 632 } 633 else 634 { 635 createAndPlayEffect(eventObj, eventObj.currentTarget); 636 } 637 } 638 639 /** 640 * @private 641 */ 642 private static function createAndPlayEffect(eventObj:Event, target:Object):void 643 { 644 645 var effectInst:Effect = createEffectForType(target, eventObj.type); 646 if (!effectInst) 647 return; 648 649 if (effectInst is Zoom && eventObj.type == MoveEvent.MOVE) 650 { 651 var message:String = resourceManager.getString( 652 "effects", "incorrectTrigger"); 653 throw new Error(message); 654 } 655 656 // If this is a "move" or "resize" event that was caused by 657 // the layout manager doing an initial layout, then don't 658 // play any effects. 659 // Ditto for "show" or "hide" effects triggered by ViewStack.doLayout. 660 if (target.initialized == false) 661 { 662 var type:String = eventObj.type; 663 if (type == MoveEvent.MOVE || 664 type == ResizeEvent.RESIZE || 665 type == FlexEvent.SHOW || 666 type == FlexEvent.HIDE || 667 type == Event.CHANGE) 668 { 669 effectInst = null; 670 return; 671 } 672 } 673 674 var n:int; 675 var i:int; 676 var m:int; 677 var j:int; 678 679 // Some components contain built-in tweens, which are not managed by 680 // the EffectManager. If one of those tweens is currently playing, 681 // and if it's animating a conflicting property, then don't play this 682 // tween. 683 if (effectInst.target is IUIComponent) 684 { 685 var tweeningProperties:Array = 686 IUIComponent(effectInst.target).tweeningProperties; 687 if (tweeningProperties && tweeningProperties.length > 0) 688 { 689 var effectProperties:Array = effectInst.getAffectedProperties(); 690 691 n = tweeningProperties.length; 692 m = effectProperties.length; 693 694 for (i = 0; i < n; i++) 695 { 696 for (j = 0; j < m; j++) 697 { 698 if (tweeningProperties[i] == effectProperties[j]) 699 { 700 effectInst = null; 701 return; 702 } 703 } 704 } 705 } 706 } 707 708 // At any given time, only one effect may be animating a given 709 // property of a given target object. If some other effect was 710 // previously animating the same properties of my target object, 711 // then finish the other effect before starting this new one. 712 // 713 if (effectInst.target is UIComponent && 714 UIComponent(effectInst.target).isEffectStarted) 715 { 716 var affectedProps:Array = effectInst.getAffectedProperties(); 717 for (i = 0; i < affectedProps.length; i++) 718 { 719 var runningInstances:Array = 720 effectInst.target.getEffectsForProperty(affectedProps[i]); 721 if (runningInstances.length > 0) 722 { 723 if (eventObj.type == ResizeEvent.RESIZE) 724 return; 725 726 for (j = 0; j < runningInstances.length; j++) 727 { 728 var otherInst:EffectInstance = runningInstances[j]; 729 if (eventObj.type == FlexEvent.SHOW && otherInst.hideOnEffectEnd) 730 { 731 otherInst.target.removeEventListener( 732 FlexEvent.SHOW, otherInst.eventHandler); 733 otherInst.hideOnEffectEnd = false; 734 735 } 736 737 /* 738 if (eventObj.type == MoveEvent.MOVE && 739 ((affectedProps[i] == "width") || 740 (affectedProps[i] == "height") || 741 (affectedProps[i] == "x") || 742 (affectedProps[i] == "y")) && 743 effectInst.target.getStyle("moveEffect") != undefined) 744 { 745 trace("EM Got Move and ignoring"); 746 return; 747 } 748 749 if (eventObj.type == ResizeEvent.RESIZE && 750 ((affectedProps[i] == "width") || 751 (affectedProps[i] == "height")) && 752 effectInst.target.getStyle("resizeEffect") != undefined) 753 { 754 return; 755 } 756 */ 757 758 otherInst.end(); 759 } 760 } 761 } 762 } 763 764 // Pass in event data for effect initialization 765 effectInst.triggerEvent = eventObj; 766 767 // Tell the effectInst that I'm the listener, so that my "onEffectEnd" 768 // method is called when the effect finishes playing. The 769 // onEffectEnd handler will remove this effect from the effectsPlaying 770 // array. 771 effectInst.addEventListener(EffectEvent.EFFECT_END, 772 EffectManager.effectEndHandler); 773 774 lastEffectCreated = effectInst; 775 776 var instances:Array = effectInst.play(); 777 n = instances.length; 778 for (i = 0; i < n; i++) 779 { 780 effectsPlaying.push( 781 new EffectNode(effectInst, instances[i])); 782 } 783 784 // Block all layout, responses from web services, and other background 785 // processing until the effect finishes executing. 786 if (effectInst.suspendBackgroundProcessing) 787 UIComponent.suspendBackgroundProcessing(); 788 } 789 790 /** 791 * @private 792 * Delayed function call when effect is triggered by "removed" event 793 */ 794 private static function removedEffectHandler(target:DisplayObject, parent:DisplayObjectContainer, index:int, eventObj:Event):void 795 { 796 suspendEventHandling(); 797 // Add the child back to the parent so the effect can play upon it 798 if (parent is IVisualElementContainer && target is IVisualElement) 799 (IVisualElementContainer(parent).addElementAt(target as IVisualElement, index)); 800 else 801 parent.addChildAt(target, index); 802 resumeEventHandling(); 803 // Use target because the player assigns the Stage to the currentTarget when we leave the scope of the event handler function 804 createAndPlayEffect(eventObj, target); 805 } 806 807 /** 808 * @private 809 * Internal function used while playing effects 810 */ 811 mx_internal static function effectEndHandler(event:EffectEvent):void 812 { 813 var effectInst:IEffectInstance = event.effectInstance; 814 // This function is called when an effect, which was started 815 // earlier by this effect manager, finishes playing. Remove 816 // this effect from the "effectPlaying" list 817 var n:int = effectsPlaying.length; 818 for (var i:int = n - 1; i >= 0; i--) 819 { 820 if (effectsPlaying[i].instance == effectInst) 821 { 822 effectsPlaying.splice(i, 1); 823 break; 824 } 825 } 826 827 // If the event that caused this effect was "hide", then the 828 // eventHandler() method set the object's visiblity to true. 829 // Now that the effect is finished playing, set visiblity to false. 830 if (Object(effectInst).hideOnEffectEnd == true) 831 { 832 effectInst.target.removeEventListener( 833 FlexEvent.SHOW, Object(effectInst).eventHandler); 834 effectInst.target.setVisible(false, true); 835 } 836 837 if (effectInst.triggerEvent && effectInst.triggerEvent.type == Event.REMOVED) 838 { 839 var targ:DisplayObject = effectInst.target as DisplayObject; 840 841 if (targ != null) 842 { 843 var parent:DisplayObjectContainer = targ.parent as DisplayObjectContainer; 844 845 if (parent != null) 846 { 847 // Since we added the child back to the parent when the effect began, 848 // we need to remove it once the effect has finished. 849 suspendEventHandling(); 850 if (parent is IVisualElementContainer && targ is IVisualElement) 851 (IVisualElementContainer(parent).removeElement(targ as IVisualElement)); 852 else 853 parent.removeChild(targ); 854 resumeEventHandling(); 855 } 856 } 857 } 858 859 // Resume the background processing that was suspended earlier 860 if (effectInst.suspendBackgroundProcessing) 861 UIComponent.resumeBackgroundProcessing(); 862 } 863 864 //-------------------------------------------------------------------------- 865 // 866 // Diagnostics 867 // 868 //-------------------------------------------------------------------------- 869 private static var effects:Dictionary = new Dictionary(true); 870 871 mx_internal static function effectStarted(effect:EffectInstance):void 872 { 873 effects[effect] = 1; 874 } 875 876 mx_internal static function effectFinished(effect:EffectInstance):void 877 { 878 delete effects[effect]; 879 } 880 881 mx_internal static function effectsInEffect():Boolean 882 { 883 for (var i:* in effects) 884 { 885 return true; 886 } 887 return false; 888 } 889} 890 891} 892 893//////////////////////////////////////////////////////////////////////////////// 894// 895// Helper class: EffectNode 896// 897//////////////////////////////////////////////////////////////////////////////// 898 899import mx.effects.Effect; 900import mx.effects.EffectInstance; 901 902/** 903 * @private 904 */ 905class EffectNode 906{ 907 //-------------------------------------------------------------------------- 908 // 909 // Constructor 910 // 911 //-------------------------------------------------------------------------- 912 913 /** 914 * Constructor. 915 * 916 * @langversion 3.0 917 * @playerversion Flash 9 918 * @playerversion AIR 1.1 919 * @productversion Flex 3 920 */ 921 public function EffectNode(factory:Effect, instance:EffectInstance) 922 { 923 super(); 924 925 this.factory = factory; 926 this.instance = instance; 927 } 928 929 //-------------------------------------------------------------------------- 930 // 931 // Properties 932 // 933 //-------------------------------------------------------------------------- 934 935 /** 936 * @private 937 */ 938 public var factory:Effect; 939 940 /** 941 * @private 942 */ 943 public var instance:EffectInstance; 944} 945