//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package mx.effects { import flash.events.Event; import flash.events.EventDispatcher; import mx.core.FlexVersion; import mx.core.IFlexDisplayObject; import mx.core.mx_internal; import mx.effects.effectClasses.AddRemoveEffectTargetFilter; import mx.effects.effectClasses.HideShowEffectTargetFilter; import mx.effects.effectClasses.PropertyChanges; import mx.events.EffectEvent; import mx.managers.LayoutManager; import mx.styles.IStyleClient; import mx.utils.NameUtil; use namespace mx_internal; /** * Dispatched when one of the effect's instances finishes playing, * either when the instance finishes playing or when the effect * is interrupted by a call to the end() method. * * @eventType mx.events.EffectEvent.EFFECT_END * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="effectEnd", type="mx.events.EffectEvent")] /** * Dispatched when the effect has been stopped, * which only occurs when the effect is * interrupted by a call to the stop() method. * The EFFECT_END event is also dispatched to indicate that * the effect has ended. This extra event is sent first, as an * indicator to listeners that the effect did not reach its * end state. * * @eventType mx.events.EffectEvent.EFFECT_STOP * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="effectStop", type="mx.events.EffectEvent")] /** * Dispatched when the effect starts playing. * * @eventType mx.events.EffectEvent.EFFECT_START * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="effectStart", type="mx.events.EffectEvent")] /** * The Effect class is an abstract base class that defines the basic * functionality of all Flex effects. * The Effect class defines the base factory class for all effects. * The EffectInstance class defines the base class for all effect * instance subclasses. * *

You do not create an instance of the Effect class itself * in an application. * Instead, you create an instance of one of the subclasses, * such as Fade or WipeLeft.

* * @mxml * *

The Effect class defines the following properties, * which all of its subclasses inherit:

* *
 *  <mx:tagname
 *    Properties
 *    customFilter=""
 *    duration="500"
 *    filter=""
 *    hideFocusRing="false"
 *    perElementOffset="0"
 *    repeatCount="1"
 *    repeatDelay="0"
 *    startDelay="0"
 *    suspendBackgroundProcessing="false|true"
 *    target="effect target"
 *    targets="array of effect targets"
 *     
 *    Events
 *    effectEnd="No default"
 *    efectStart="No default"
 *  />
 *  
* * @see mx.effects.EffectInstance * * @includeExample examples/SimpleEffectExample.mxml * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class Effect extends EventDispatcher implements IEffect { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class methods // //-------------------------------------------------------------------------- /** * @private */ private static function mergeArrays(a1:Array, a2:Array):Array { if (a2) { for (var i2:int = 0; i2 < a2.length; i2++) { var addIt:Boolean = true; for (var i1:int = 0; i1 < a1.length; i1++) { if (a1[i1] == a2[i2]) { addIt = false; break; } } if (addIt) a1.push(a2[i2]); } } return a1; } /** * @private */ private static function stripUnchangedValues(propChanges:Array):Array { // Go through and remove any before/after values that are the same. for (var i:int = 0; i < propChanges.length; i++) { if (propChanges[i].stripUnchangedValues == false) continue; for (var prop:Object in propChanges[i].start) { if ((propChanges[i].start[prop] == propChanges[i].end[prop]) || (typeof(propChanges[i].start[prop]) == "number" && typeof(propChanges[i].end[prop])== "number" && isNaN(propChanges[i].start[prop]) && isNaN(propChanges[i].end[prop]))) { delete propChanges[i].start[prop]; delete propChanges[i].end[prop]; } } } return propChanges; } //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * *

Starting an effect is usually a three-step process:

* * * * @param target The Object to animate with this effect. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function Effect(target:Object = null) { super(); this.target = target; } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- // Transitions will force the values animated by this effect to get set to // their final values, as specified in the end state, when the effect finishes. // This flag is set to true when the effect is being played by a transition, // but applying the values is still gated by the value of the // applyTransitionEndProperties flag, which is set on a per-Effect basis. mx_internal var applyEndValuesWhenDone:Boolean = false; // This property tracks whether the effect is playing in interruption mode. // Normally, effects pick up starting values (when not provided explicitly) // from the current values of the target objects. But when playing in interruption // mode, the effect should get its starting values from the propertyChanges // array instead. private var _transitionInterruption:Boolean = false; mx_internal function get transitionInterruption():Boolean { return _transitionInterruption; } mx_internal function set transitionInterruption(value:Boolean):void { _transitionInterruption = value; } // This is new behavior for the Flex4 effects; previously, we would not // set the end-state values automatically. Because of this switch, the // default value is hinged on a compatibility check, so that applications // can choose to have the older behavior instead. /** * This flag controls whether the effect, when run in a transition, * automatically applies the property values according to the end * state, as opposed to leaving values as set by the effect itself. * * @default true * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var applyTransitionEndProperties:Boolean = (FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_0) ? false : true; /** * @private */ private var _instances:Array /* of EffectInstance */ = []; /** * @private */ private var _callValidateNow:Boolean = false; /** * @private */ private var isPaused:Boolean = false; /** * @private */ mx_internal var filterObject:EffectTargetFilter; /** * @private * Used in applyValueToTarget() */ mx_internal var applyActualDimensions:Boolean = true; /** * @private * Holds the init object passed in by the Transition. */ mx_internal var propertyChangesArray:Array; /** * @private * Track the property set in the play() function */ mx_internal var playReversed:Boolean; private var effectStopped:Boolean; /** * @private * Pointer back to the CompositeEffect that created this instance. * Value is null if we are not the child of a CompositeEffect */ mx_internal var parentCompositeEffect:Effect; //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // className //---------------------------------- /** * @copy mx.effects.IEffect#className * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get className():String { return NameUtil.getUnqualifiedClassName(this); } //---------------------------------- // customFilter //---------------------------------- /** * @private * Storage for the customFilter property. */ private var _customFilter:EffectTargetFilter; /** * @copy mx.effects.IEffect#customFilter * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get customFilter():EffectTargetFilter { return _customFilter; } /** * @private */ public function set customFilter(value:EffectTargetFilter):void { _customFilter = value; filterObject = value; } //---------------------------------- // duration //---------------------------------- /** * @private * Storage for the duration property. */ private var _duration:Number = 500; /** * @private */ mx_internal var durationExplicitlySet:Boolean = false; [Inspectable(category="General", defaultValue="500", minValue="0.0")] /** * @copy mx.effects.IEffect#duration * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get duration():Number { if (!durationExplicitlySet && parentCompositeEffect) { return parentCompositeEffect.duration; } else { return _duration; } } /** * @private */ public function set duration(value:Number):void { durationExplicitlySet = true; _duration = value; } //---------------------------------- // effectTargetHost //---------------------------------- /** * @private * Storage for the effectTargetHost property. */ private var _effectTargetHost:IEffectTargetHost; /** * @copy mx.effects.IEffect#effectTargetHost * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get effectTargetHost():IEffectTargetHost { return _effectTargetHost; } /** * @private */ public function set effectTargetHost(value:IEffectTargetHost):void { _effectTargetHost = value; } //---------------------------------- // endValuesCaptured //---------------------------------- /** * A flag containing true if the end values * of an effect have already been determined, * or false if they should be acquired from the * current properties of the effect targets when the effect runs. * This property is required by data effects because the sequence * of setting up the data effects, such as DefaultListEffect * and DefaultTileListEffect, is more complicated than for * normal effects. * * @default false * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var endValuesCaptured:Boolean = false; //---------------------------------- // filter //---------------------------------- /** * @private * Storage for the filter property. */ private var _filter:String; [Inspectable(category="General", enumeration="add,remove,show,hide,move,resize,addItem,removeItem,replacedItem,replacementItem,none", defaultValue="none")] /** * @copy mx.effects.IEffect#filter * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get filter():String { return _filter; } /** * @private */ public function set filter(value:String):void { if (!customFilter) { _filter = value; switch (value) { case "add": case "remove": { filterObject = new AddRemoveEffectTargetFilter(); AddRemoveEffectTargetFilter(filterObject).add = (value == "add"); break; } case "hide": case "show": { filterObject = new HideShowEffectTargetFilter(); HideShowEffectTargetFilter(filterObject).show = (value == "show"); break; } case "move": { filterObject = new EffectTargetFilter(); filterObject.filterProperties = [ "x", "y" ]; break; } case "resize": { filterObject = new EffectTargetFilter(); filterObject.filterProperties = [ "width", "height" ]; break; } case "addItem": { filterObject = new EffectTargetFilter(); filterObject.requiredSemantics = {added:true}; break; } case "removeItem": { filterObject = new EffectTargetFilter(); filterObject.requiredSemantics = {removed:true}; break; } case "replacedItem": { filterObject = new EffectTargetFilter(); filterObject.requiredSemantics = {replaced:true}; break; } case "replacementItem": { filterObject = new EffectTargetFilter(); filterObject.requiredSemantics = {replacement:true}; break; } default: { filterObject = null; break; } } } } //---------------------------------- // hideFocusRing //---------------------------------- /** * @private * Storage for the hideFocusRing property. */ private var _hideFocusRing:Boolean = false; /** * @copy mx.effects.IEffect#hideFocusRing * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get hideFocusRing():Boolean { return _hideFocusRing; } /** * @private */ public function set hideFocusRing(value:Boolean):void { _hideFocusRing = value; } //---------------------------------- // instanceClass //---------------------------------- /** * An object of type Class that specifies the effect * instance class class for this effect class. * *

All subclasses of the Effect class must set this property * in their constructor.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var instanceClass:Class = IEffectInstance; //---------------------------------- // isPlaying //---------------------------------- /** * @copy mx.effects.IEffect#isPlaying * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get isPlaying():Boolean { return _instances && _instances.length > 0; } //---------------------------------- // perElementOffset //---------------------------------- /** * @private * Storage for the perElementOffset property. */ private var _perElementOffset:Number = 0; [Inspectable(defaultValue="0", category="General", verbose="0", minValue="0.0")] /** * @copy mx.effects.IEffect#perElementOffset * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get perElementOffset():Number { return _perElementOffset; } /** * @private */ public function set perElementOffset(value:Number):void { _perElementOffset = value; } //---------------------------------- // relevantProperties //---------------------------------- /** * @private * Storage for the relevantProperties property. */ private var _relevantProperties:Array /* of String */; /** * @copy mx.effects.IEffect#relevantProperties * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get relevantProperties():Array /* of String */ { if (_relevantProperties) return _relevantProperties; else return getAffectedProperties(); } /** * @private */ public function set relevantProperties(value:Array /* of String */):void { _relevantProperties = value; } //---------------------------------- // relevantStyles //---------------------------------- /** * @private * Storage for the relevantStyles property. */ private var _relevantStyles:Array /* of String */ = []; /** * @copy mx.effects.IEffect#relevantStyles * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get relevantStyles():Array /* of String */ { return _relevantStyles; } /** * @private */ public function set relevantStyles(value:Array /* of String */):void { _relevantStyles = value; } //---------------------------------- // repeatCount //---------------------------------- [Inspectable(category="General", defaultValue="1", minValue="0")] /** * Number of times to repeat the effect. * Possible values are any integer greater than or equal to 0. * A value of 1 means to play the effect once. * A value of 0 means to play the effect indefinitely * until stopped by a call to the end() method. * * @default 1 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var repeatCount:int = 1; //---------------------------------- // repeatDelay //---------------------------------- [Inspectable(category="General", defaultValue="0", minValue="0")] /** * Amount of time, in milliseconds, to wait before repeating the effect. * Possible values are any integer greater than or equal to 0. * * @default 0 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var repeatDelay:int = 0; //---------------------------------- // startDelay //---------------------------------- [Inspectable(category="General", defaultValue="0", minValue="0")] /** * Amount of time, in milliseconds, to wait before starting the effect. * Possible values are any int greater than or equal to 0. * If the effect is repeated by using the repeatCount * property, the startDelay is only applied * to the first time the effect is played. * * @default 0 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var startDelay:int = 0; //---------------------------------- // suspendBackgroundProcessing //---------------------------------- /** * If true, blocks all background processing * while the effect is playing. * Background processing includes measurement, layout, and * processing responses that have arrived from the server. * The default value is false. * *

You are encouraged to set this property to * true in most cases, because it improves * the performance of the application. * However, the property should be set to false * if either of the following is true:

* * * @default false * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var suspendBackgroundProcessing:Boolean = false; //---------------------------------- // target //---------------------------------- /** * @copy mx.effects.IEffect#target * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get target():Object { if (_targets.length > 0) return _targets[0]; else return null; } /** * @private */ public function set target(value:Object):void { _targets.splice(0); if (value) _targets[0] = value; } //---------------------------------- // targets //---------------------------------- /** * @private * Storage for the targets property. */ private var _targets:Array = []; [Inspectable(arrayType="Object")] [ArrayElementType("Object")] /** * @copy mx.effects.IEffect#targets * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get targets():Array { return _targets; } /** * @private */ public function set targets(value:Array):void { // Strip out null values. // Binding will trigger again when the null targets are created. var n:int = value.length; for (var i:int = n - 1; i >= 0; i--) { if (value[i] == null) value.splice(i,1); } _targets = value; } //---------------------------------- // triggerEvent //---------------------------------- /** * @private * Storage for the triggerEvent property. */ private var _triggerEvent:Event; /** * @copy mx.effects.IEffect#triggerEvent * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get triggerEvent():Event { return _triggerEvent; } /** * @private */ public function set triggerEvent(value:Event):void { _triggerEvent = value; } //---------------------------------- // playheadTime //---------------------------------- /** * @private * Backing storage for the playheadTime property. Note that this * value is just a backup, used if the effect is not currently running. * A running effect will query its effect instance for the value * instead. */ private var _playheadTime:Number = 0; [Inspectable(minValue="0.0")] /** * Current time position of the effect. * This property has a value between 0 and the total duration, * which includes the Effect's startDelay, * repeatCount, and repeatDelay. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get playheadTime():Number { for (var i:int = 0; i < _instances.length; i++) { if (_instances[i]) return IEffectInstance(_instances[i]).playheadTime; } // Effect isn't running: return the cached value return _playheadTime; } public function set playheadTime(value:Number):void { // If the effect is not yet playing, it should still be possible // to seek into it. playing and then pausing it provides that // capability // TODO (chaase): Need better overall mechanism to seek into a // non-playing effect. The internals of seeking in Animation // are complicated and don't end up giving us the behavior we // want, especially for successive seeks. var started:Boolean = false; if (_instances.length == 0) { play(); started = true; } for (var i:int = 0; i < _instances.length; i++) { if (_instances[i]) EffectInstance(_instances[i]).playheadTime = value; } if (started) pause(); _playheadTime = value; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @copy mx.effects.IEffect#getAffectedProperties() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function getAffectedProperties():Array /* of String */ { // Every subclass should override this method. return []; } /** * @copy mx.effects.IEffect#createInstances() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function createInstances(targets:Array = null):Array /* of EffectInstance */ { if (!targets) targets = this.targets; var newInstances:Array = []; // Multiple target support var n:int = targets.length; var offsetDelay:Number = 0; for (var i:int = 0; i < n; i++) { var newInstance:IEffectInstance = createInstance(targets[i]); if (newInstance) { newInstance.startDelay += offsetDelay; offsetDelay += perElementOffset; newInstances.push(newInstance); } } triggerEvent = null; return newInstances; } /** * @copy mx.effects.IEffect#createInstance() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function createInstance(target:Object = null):IEffectInstance { if (!target) target = this.target; var newInstance:IEffectInstance = null; var props:PropertyChanges = null; var create:Boolean = true; var setPropsArray:Boolean = false; if (propertyChangesArray) { setPropsArray = true; create = filterInstance(propertyChangesArray, target); } if (create) { newInstance = IEffectInstance(new instanceClass(target)); initInstance(newInstance); if (setPropsArray) { var n:int = propertyChangesArray.length; for (var i:int = 0; i < n; i++) { if (propertyChangesArray[i].target == target) { newInstance.propertyChanges = propertyChangesArray[i]; } } } EventDispatcher(newInstance).addEventListener(EffectEvent.EFFECT_START, effectStartHandler); EventDispatcher(newInstance).addEventListener(EffectEvent.EFFECT_STOP, effectStopHandler); EventDispatcher(newInstance).addEventListener(EffectEvent.EFFECT_END, effectEndHandler); _instances.push(newInstance); if (triggerEvent) newInstance.initEffect(triggerEvent); } return newInstance; } /** * Copies properties of the effect to the effect instance. * *

Flex calls this method from the Effect.createInstance() * method; you do not have to call it yourself.

* *

When you create a custom effect, override this method to * copy properties from the Effect class to the effect instance class. * In your override, call super.initInstance().

* * @param EffectInstance The effect instance to initialize. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function initInstance(instance:IEffectInstance):void { instance.duration = duration; Object(instance).durationExplicitlySet = durationExplicitlySet; instance.effect = this; instance.effectTargetHost = effectTargetHost; instance.hideFocusRing = hideFocusRing; instance.repeatCount = repeatCount; instance.repeatDelay = repeatDelay; instance.startDelay = startDelay; instance.suspendBackgroundProcessing = suspendBackgroundProcessing; } /** * @copy mx.effects.IEffect#deleteInstance() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function deleteInstance(instance:IEffectInstance):void { EventDispatcher(instance).removeEventListener( EffectEvent.EFFECT_START, effectStartHandler); EventDispatcher(instance).removeEventListener( EffectEvent.EFFECT_STOP, effectStopHandler); EventDispatcher(instance).removeEventListener( EffectEvent.EFFECT_END, effectEndHandler); var n:int = _instances.length; for (var i:int = 0; i < n; i++) { if (_instances[i] === instance) _instances.splice(i, 1); } } /** * @copy mx.effects.IEffect#play() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function play(targets:Array = null, playReversedFromEnd:Boolean = false): Array /* of EffectInstance */ { effectStopped = false; isPaused = false; playReversed = playReversedFromEnd; // If we have a propertyChangesArray, capture the current values // if they haven't been captured already, strip out any unchanged // values, then apply the start values. if (targets == null && propertyChangesArray != null) { if (_callValidateNow) LayoutManager.getInstance().validateNow(); if (!endValuesCaptured) propertyChangesArray = captureValues(propertyChangesArray, false); propertyChangesArray = stripUnchangedValues(propertyChangesArray); applyStartValues(propertyChangesArray, this.targets); if (playReversedFromEnd) { for (var j:int = 0; j < propertyChangesArray.length; ++j) { var tmpStart:Object = propertyChangesArray[j].start; propertyChangesArray[j].start = propertyChangesArray[j].end; propertyChangesArray[j].end = tmpStart; } } // Revalidate after applying the start values, to get everything // back the way it should be before starting the animation LayoutManager.getInstance().validateNow(); applyEndValuesWhenDone = true; } var newInstances:Array = createInstances(targets); var n:int = newInstances.length; for (var i:int = 0; i < n; i++) { var newInstance:IEffectInstance = IEffectInstance(newInstances[i]); Object(newInstance).playReversed = playReversedFromEnd; newInstance.startEffect(); } return newInstances; } /** * @copy mx.effects.IEffect#pause() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function pause():void { if (isPlaying && !isPaused) { isPaused = true; var n:int = _instances.length; for (var i:int = 0; i < n; i++) { IEffectInstance(_instances[i]).pause(); } } } /** * @copy mx.effects.IEffect#stop() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function stop():void { var n:int = _instances.length - 1; for (var i:int = n; i >= 0; i--) { var instance:IEffectInstance = IEffectInstance(_instances[i]); if (instance) instance.stop(); } } /** * @copy mx.effects.IEffect#resume() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function resume():void { if (isPlaying && isPaused) { isPaused = false; var n:int = _instances.length; for (var i:int = 0; i < n; i++) { IEffectInstance(_instances[i]).resume(); } } } /** * @copy mx.effects.IEffect#reverse() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function reverse():void { if (isPlaying) { var n:int = _instances.length; for (var i:int = 0; i < n; i++) { IEffectInstance(_instances[i]).reverse(); } } } /** * @copy mx.effects.IEffect#end() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function end(effectInstance:IEffectInstance = null):void { if (effectInstance) { effectInstance.end(); } else { var n:int = _instances.length; for (var i:int = n - 1; i >= 0; i--) { var instance:IEffectInstance = IEffectInstance(_instances[i]); if (instance) instance.end(); } } } /** * Determines the logic for filtering out an effect instance. * The CompositeEffect class overrides this method. * * @param propChanges The properties modified by the effect. * * @param targ The effect target. * * @return Returns true if the effect instance should play. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function filterInstance(propChanges:Array, target:Object):Boolean { if (filterObject) return filterObject.filterInstance(propChanges, effectTargetHost, target); return true; } /** * @copy mx.effects.IEffect#captureStartValues() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function captureStartValues():void { if (targets.length > 0) { // Reset the PropertyChanges array by passing in 'null' propertyChangesArray = captureValues(null, true); _callValidateNow = true; } endValuesCaptured = false; } /** * @copy mx.effects.IEffect#captureMoreStartValues() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function captureMoreStartValues(targets:Array):void { if (targets.length > 0) { // make temporary PropertyChangesArray var additionalPropertyChangesArray:Array = captureValues(null, true); propertyChangesArray = (propertyChangesArray != null) ? propertyChangesArray.concat(additionalPropertyChangesArray) : additionalPropertyChangesArray; } } /** * @copy mx.effects.IEffect#captureEndValues() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function captureEndValues():void { // captureValues will create propertyChangesArray if it does not // yet exist propertyChangesArray = captureValues(propertyChangesArray, false); endValuesCaptured = true; } /** * @private * Used internally to grab the values of the relevant properties */ mx_internal function captureValues(propChanges:Array, setStartValues:Boolean, targetsToCapture:Array = null):Array { var n:int; var i:int; if (!propChanges) { propChanges = []; // Create a new PropertyChanges object for the sum of all targets. n = targets.length; for (i = 0; i < n; i++) propChanges.push(new PropertyChanges(targets[i])); } // Merge Effect.filterProperties and filterObject.filterProperties var effectProps:Array = !filterObject ? relevantProperties : mergeArrays(relevantProperties, filterObject.filterProperties); var valueMap:Object; var target:Object; var m:int; var j:int; // For each target, grab the property's value // and put it into the propChanges Array. // Walk the targets. if (effectProps && effectProps.length > 0) { n = propChanges.length; for (i = 0; i < n; i++) { target = propChanges[i].target; if (targetsToCapture == null || targetsToCapture.length == 0 || targetsToCapture.indexOf(target) >= 0) { valueMap = setStartValues ? propChanges[i].start : propChanges[i].end; // Walk the properties in the target m = effectProps.length; for (j = 0; j < m; j++) { // Don't clobber values already set if (valueMap[effectProps[j]] === undefined) { valueMap[effectProps[j]] = getValueFromTarget(target,effectProps[j]); } } } } } var styles:Array = !filterObject ? relevantStyles : mergeArrays(relevantStyles, filterObject.filterStyles); if (styles && styles.length > 0) { n = propChanges.length; for (i = 0; i < n; i++) { target = propChanges[i].target; if (targetsToCapture == null || targetsToCapture.length == 0 || targetsToCapture.indexOf(target) >= 0) { if (!(target is IStyleClient)) continue; valueMap = setStartValues ? propChanges[i].start : propChanges[i].end; // Walk the properties in the target. m = styles.length; for (j = 0; j < m; j++) { // Don't clobber values set by relevantProperties if (valueMap[styles[j]] === undefined) { var value:* = target.getStyle(styles[j]); valueMap[styles[j]] = value; } } } } } return propChanges; } /** * Called by the captureStartValues() method to get the value * of a property from the target. * This function should only be called internally * by the effects framework. * The default behavior is to simply return target[property]. * Effect developers can override this function * if you need a different behavior. * * @param target The effect target. * * @param property The target property. * * @return The value of the target property. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function getValueFromTarget(target:Object, property:String):* { if (property in target) return target[property]; return undefined; } /** * @private * Applies the start values found in the array of PropertyChanges * to the relevant targets. */ mx_internal function applyStartValues(propChanges:Array, targets:Array):void { var effectProps:Array = relevantProperties; var n:int = propChanges.length; for (var i:int = 0; i < n; i++) { var m:int; var j:int; var target:Object = propChanges[i].target; var apply:Boolean = false; m = targets.length; for (j = 0; j < m; j++) { if (targets[j] == target) { apply = filterInstance(propChanges, target); break; } } if (apply) { // Walk the properties in the target m = effectProps.length; for (j = 0; j < m; j++) { var propName:String = effectProps[j]; var startVal:* = propChanges[i].start[propName]; var endVal:* = propChanges[i].end[propName]; if (propName in propChanges[i].start && endVal != startVal && (!(startVal is Number) || !(isNaN(endVal) && isNaN(startVal)))) { applyValueToTarget(target, effectProps[j], propChanges[i].start[effectProps[j]], propChanges[i].start); } } // Walk the styles in the target m = relevantStyles.length; for (j = 0; j < m; j++) { var styleName:String = relevantStyles[j]; var startStyle:* = propChanges[i].start[styleName]; var endStyle:* = propChanges[i].end[styleName]; if (styleName in propChanges[i].start && endStyle != startStyle && (!(startStyle is Number) || !(isNaN(endStyle) && isNaN(startStyle))) && target is IStyleClient) { if (propChanges[i].end[relevantStyles[j]] !== undefined) target.setStyle(relevantStyles[j], propChanges[i].start[relevantStyles[j]]); else target.clearStyle(relevantStyles[j]); } } } } } /** * @private * Applies the start values found in the array of PropertyChanges * to the relevant targets. */ mx_internal function applyEndValues(propChanges:Array, targets:Array):void { // For now, only new Flex4 effects will apply end values when transitions // are over, to preserve the previous behavior of Flex3 effects if (!applyTransitionEndProperties) return; var effectProps:Array = relevantProperties; var n:int = propChanges.length; for (var i:int = 0; i < n; i++) { var m:int; var j:int; var target:Object = propChanges[i].target; var apply:Boolean = false; m = targets.length; for (j = 0; j < m; j++) { if (targets[j] == target) { apply = filterInstance(propChanges, target); break; } } if (apply) { // Walk the properties in the target m = effectProps.length; for (j = 0; j < m; j++) { var propName:String = effectProps[j]; var startVal:* = propChanges[i].start[propName]; var endVal:* = propChanges[i].end[propName]; if (propName in propChanges[i].end && (!(endVal is Number) || !(isNaN(endVal) && isNaN(startVal)))) { applyValueToTarget(target, propName, propChanges[i].end[propName], propChanges[i].end); } } // Walk the styles in the target m = relevantStyles.length; for (j = 0; j < m; j++) { var styleName:String = relevantStyles[j]; var startStyle:* = propChanges[i].start[styleName]; var endStyle:* = propChanges[i].end[styleName]; if (styleName in propChanges[i].end && (!(endStyle is Number) || !(isNaN(endStyle) && isNaN(startStyle))) && target is IStyleClient) { if (propChanges[i].end[styleName] !== undefined) target.setStyle(styleName, propChanges[i].end[styleName]); else target.clearStyle(styleName); } } } } } /** * Used internally by the Effect infrastructure. * If captureStartValues() has been called, * then when Flex calls the play() method, it uses this function * to set the targets back to the starting state. * The default behavior is to take the value captured * using the getValueFromTarget() method * and set it directly on the target's property. For example:
     *  
     *  target[property] = value;
* *

Only override this method if you need to apply * the captured values in a different way. * Note that style properties of a target are set * using a different mechanism. * Use the relevantStyles property to specify * which style properties to capture and apply.

* * @param target The effect target. * * @param property The target property. * * @param value The value of the property. * * @param props Array of Objects, where each Array element contains a * start and end Object * for the properties that the effect is monitoring. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function applyValueToTarget(target:Object, property:String, value:*, props:Object):void { if (property in target) { // The "property in target" test only tells if the property exists // in the target, but does not distinguish between read-only and // read-write properties. Put a try/catch around the setter and // ignore any errors. try { if (applyActualDimensions && target is IFlexDisplayObject && property == "height") { target.setActualSize(target.width,value); } else if (applyActualDimensions && target is IFlexDisplayObject && property == "width") { target.setActualSize(value,target.height); } else { target[property] = value; } } catch(e:Error) { // Ignore errors } } } //-------------------------------------------------------------------------- // // Event handlers // //-------------------------------------------------------------------------- /** * This method is called when the effect instance starts playing. * If you override this method, ensure that you call the super method. * * @param event An event object of type EffectEvent. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function effectStartHandler(event:EffectEvent):void { dispatchEvent(event); } /** * Called when an effect instance has stopped by a call * to the stop() method. * If you override this method, ensure that you call the super method. * * @param event An event object of type EffectEvent. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function effectStopHandler(event:EffectEvent):void { dispatchEvent(event); // We use this event to determine whether we should set final // state values in the ensuing endHandler() call effectStopped = true; } /** * Called when an effect instance has finished playing. * If you override this method, ensure that you call the super method. * * @param event An event object of type EffectEvent. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function effectEndHandler(event:EffectEvent):void { if (playReversed && propertyChangesArray != null) { for (var j:int = 0; j < propertyChangesArray.length; ++j) { var tmpStart:Object = propertyChangesArray[j].start; propertyChangesArray[j].start = propertyChangesArray[j].end; propertyChangesArray[j].end = tmpStart; } } var lastTime:Boolean = !_instances || _instances.length == 1; // Transitions should set the end values when done if (applyEndValuesWhenDone && !effectStopped && lastTime) applyEndValues(propertyChangesArray, targets); var instance:IEffectInstance = IEffectInstance(event.effectInstance); deleteInstance(instance); dispatchEvent(event); if (lastTime) { propertyChangesArray = null; applyEndValuesWhenDone = false; } } } }