You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by ah...@apache.org on 2012/05/03 00:45:08 UTC

svn commit: r1333232 [15/34] - in /incubator/flex/trunk: ./ frameworks/tests/ frameworks/tests/basicTests/ frameworks/tests/basicTests/dmv/ frameworks/tests/basicTests/dmv/scripts/ frameworks/tests/basicTests/dmv/views/ frameworks/tests/basicTests/fxg/...

Added: incubator/flex/trunk/mustella/as3/src/mustella/EffectTesting.as
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/EffectTesting.as?rev=1333232&view=auto
==============================================================================
--- incubator/flex/trunk/mustella/as3/src/mustella/EffectTesting.as (added)
+++ incubator/flex/trunk/mustella/as3/src/mustella/EffectTesting.as Wed May  2 22:44:38 2012
@@ -0,0 +1,648 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 
+{
+    import flash.events.Event;
+    import flash.events.TimerEvent;
+    import flash.utils.Timer;
+    import flash.utils.getQualifiedClassName;
+    
+    import mx.collections.ArrayCollection;
+    import mx.core.IVisualElement;
+    import mx.core.IVisualElementContainer;
+    import mx.core.UIComponent;
+    import mx.effects.CompositeEffect;
+    import mx.effects.Effect;
+    import mx.events.EffectEvent;
+    import mx.geom.TransformOffsets;
+    import mx.states.Transition;
+    
+    import spark.primitives.supportClasses.GraphicElement;
+    
+    /**
+    * 
+    * This class provides some APIs that can be useful for writing Mustella effects
+    * and transitions tests.  This will be instrumental in the Catalyst matrix tests.
+    * 
+    * It might be useful to think about building a set of TestStep classes that wrap 
+    * some of this functionality.
+    * 
+    * @author Steven Shongrunden (stshongr@adobe.com)
+    * 
+    */
+    public class EffectTesting
+    {
+        // whether to seek the effect on effectStart (default: false)
+        public static var requestedSeek:Boolean = false;
+        
+        // what time to seek to in an effect (default: NaN - seek to the end of the effect)
+        public static var requestedSeekTime:Number = NaN;
+        
+        // the current effect being played
+        [Bindable] public static var currentEffect:Effect;
+        
+        // the document that ready events will be dispatched from and transitions will be pulled from
+        private static var rootDocument:UIComponent;
+        
+        // the character used to separate the elements in the expected values string
+        private static var elementSeparator:String = "|";
+        
+        // the character used to separate the values in the expected values string
+        private static var propertySeparator:String = ",";
+        
+        // keep track of the details of the latest comparison to ease further investigation
+        private static var lastResult:ArrayCollection;
+        
+        /**
+        * Sets up for an effect test.  This allows you to seek to a specific time in an effect.
+        * 
+        * Call this method after the ResetComponent in your (non-transition) effects test.
+        */
+        public static function setupEffectTest(document:Object, effect:Effect):String {
+            
+            // reset the test properties
+            resetProperties(document);
+            
+            // null check the effect
+            if (effect == null)
+                throw new Error("ERROR: You must provide a non-null effect to test.");
+            
+            // set the current effect
+            currentEffect = effect;
+            
+            // handle the effectEnd event
+            currentEffect.removeEventListener(EffectEvent.EFFECT_END, handleEffectEnd);
+            currentEffect.addEventListener(EffectEvent.EFFECT_END, handleEffectEnd);
+            
+            // dispatches a setupComplete event and returns setupComplete String so you can
+            // use this with either an AssertMethodValue or RunCode in Mustella
+            rootDocument.dispatchEvent(new Event('setupComplete'));
+            return "setupComplete";
+        }
+        
+        /**
+         * Sets up for a transitions test.  This allows you to seek to a specific time in a transition.
+         * 
+         * It parses all of the transitions in a document and sets up event listeners in a way that allows
+         * seeking to a specific time in the transition.
+         * 
+         * Call this method after the ResetComponent in your transitions test.
+         */
+        public static function setupTransitionTest(document:Object):String {
+            
+            resetProperties(document);
+            
+            var transitions:Array = rootDocument.transitions;
+            
+            // don't manage any listeners if there aren't any transitions
+            if (transitions == null)
+                throw new Error("ERROR: document has no transitions");
+
+            // add event listeners to each transition
+            for each (var t:Transition in transitions){
+                
+                // remove the effectStart event listener and add it again so we don't pile them up
+                t.effect.removeEventListener(EffectEvent.EFFECT_START, handleEffectStart);
+                t.effect.addEventListener(EffectEvent.EFFECT_START, handleEffectStart);
+                
+                // remove the effectEnd event listener and add it again so we don't pile them up
+                t.effect.removeEventListener(EffectEvent.EFFECT_END, handleEffectEnd);
+                t.effect.addEventListener(EffectEvent.EFFECT_END, handleEffectEnd);
+            }
+            
+            // dispatches a setupComplete event and returns setupComplete String so you can
+            // use this with either an AssertMethodValue or RunCode in Mustella
+            rootDocument.dispatchEvent(new Event('setupComplete'));
+            return "setupComplete";
+        }
+        
+        /**
+        * Called by the setup methods to reset the properties in this class
+        */
+        private static function resetProperties(document:Object):void {
+            
+            // null checks
+            if (document == null)
+                throw new Error("ERROR: You must provide a non-null document.");
+            
+            if (!(document is UIComponent))
+                throw new Error("ERROR: document must be a UIComponent");
+            
+            // reset the rootDocument
+            rootDocument = document as UIComponent;
+            
+            // reset the seek information
+            requestedSeek = false;
+            requestedSeekTime = NaN;
+        }
+        
+        /**
+        * Called on effect start, kicks off the seek behavior if it is requested.
+        */
+        private static function handleEffectStart(event:EffectEvent):void {
+            trace('effect start');
+            
+            currentEffect = event.target as Effect;
+            
+            // seek if it was requested
+            if (requestedSeek){
+            
+                // wait roughly a frame then pause the effect before seeking
+                var timer:Timer = new Timer(0);
+                timer.repeatCount = 1;
+                timer.addEventListener(TimerEvent.TIMER, function(e:Event):void{ seekCurrentEffect(); });
+                timer.start();
+            }
+        }
+        
+        /**
+        * Pauses then seeks to the position in the current effect. Fires an event when that is done.
+        */
+        public static function seekCurrentEffect():void {
+            var seekTime:Number = requestedSeekTime;
+            var c:CompositeEffect = currentEffect as CompositeEffect;
+            
+            // seek to the end if a specific seek time was not requested
+            if (isNaN(seekTime)){
+                // set the seekTime to the end of the effect
+                if (c){
+                    // if its a Parallel/Sequence then use the compositeDuration that also handle startDelay
+                    seekTime = c.compositeDuration;
+                } else {
+                    // just a plain effect so use startDelay + duration
+                    seekTime = currentEffect.startDelay + currentEffect.duration;
+                }
+            }
+            
+            trace('effect seek to ' + seekTime);
+            
+            // pause then seek
+            currentEffect.pause();
+            currentEffect.playheadTime = seekTime;
+            
+            // dispatch a ready event on the document
+            rootDocument.dispatchEvent(new Event("seekAssertionReady"));
+        }
+        
+        /**
+        * TODO: The inclusion of this method in the API is not fully baked.
+        * This method's name/signature/existance could change in the future
+        * when it is properly implemented.
+        */
+        public static function seekCurrentEffectTo(time:Number):void {
+            currentEffect.playheadTime = time;
+            rootDocument.dispatchEvent(new Event("seekAssertionReady"));
+        }
+        
+        /**
+         * TODO: The inclusion of this method in the API is not fully baked.
+         * This method's name/signature/existance could change in the future
+         * when it is properly implemented.
+         */
+        public static function getCurrentEffectDuration():Number {
+            var c:CompositeEffect = currentEffect as CompositeEffect;
+        
+            if (c){
+                // if its a Parallel/Sequence then use the compositeDuration that also handle startDelay
+                return c.compositeDuration;
+            } else {
+                // just a plain effect so use startDelay + duration
+                return currentEffect.startDelay + currentEffect.duration;
+            }
+        }
+        
+        /**
+        * Resumes the current effect
+        */
+        public static function resumeCurrentEffect():void {
+            trace("effect resume");
+            currentEffect.resume();
+        }
+        
+        /**
+        * Fires an event after the effectEnd event that signifies an assertion is now valid.
+        * 
+        * In a transition this gets called after the state values have been slammed in. 
+        */
+        private static function handleEffectEnd(e:EffectEvent):void {
+            trace('effect end');
+            
+            // dispatch a ready event on the document
+            rootDocument.dispatchEvent(new Event("endAssertionReady"));
+        }
+        
+        /**
+        * Given a root element it compares a set of properties across that element and any of its ancestors.
+        *
+        * Sample usage:
+        * 
+        * assertPropertySet(test1, 'width, height', '70,22|10,10', 0)
+        *   outputs: 'FAIL: test1.width: expected 70 +/- 0, but received 100') 
+        * 
+        * @param rootContainer - the root element to inspect
+        * @param propertyNameString - a string deliminated with a character that lists the properties to inspect
+        * @param expectedValuesString - a string deliminiated with a character that lists the values to expect
+        * @param tolerance - the amount of difference between actual and expected is allowed before failure
+        * @param depth - how deep to recurse in the rootContainer
+        * 
+        * @return - a string of either "PASS" or "FAIL: ..." with a failure message  
+        */
+        public static function assertPropertySet(rootContainer:IVisualElement, propertyNamesString:String, 
+                                                expectedValuesString:String, tolerance:Number = 0, depth:int = -1):String {
+            return checkPropertySet(rootContainer, false, propertyNamesString, expectedValuesString, tolerance, depth);
+        }
+        
+        /**
+        * Given a root element it compares a set of properties across that element and any of its ancestors
+        * using the postLayoutTransformOffsets object of those elements.
+        * 
+        * Sample usage:
+        * 
+        * assertPostLayoutPropertySet(test1, 'rotationX, rotationY', '45,45|0,0', 0)
+        *   outputs: 'FAIL: test1.rotationX: expected 45 +/- 0, but received 0')
+        * 
+        * Use null as an expected value if postLayoutTransformOffsets is null for example:
+        *  -  properties: 'rotationX,rotationY'
+        *  -  expected string: 'null,null|null,null'
+        * 
+        * @param rootContainer - the root element to inspect
+        * @param propertyNameString - a string deliminated with a character that lists the properties to inspect
+        * @param expectedValuesString - a string deliminiated with a character that lists the values to expect
+        * @param tolerance - the amount of difference between actual and expected is allowed before failure
+        * @param depth - how deep to recurse in the rootContainer
+        * 
+        * @return - a string of either "PASS" or "FAIL: ..." with a failure message
+        */
+        public static function assertPostLayoutPropertySet(rootContainer:IVisualElement, propertyNamesString:String, 
+                                                          expectedValuesString:String, tolerance:Number = 0, depth:int = -1):String {
+            return checkPropertySet(rootContainer, true, propertyNamesString, expectedValuesString, tolerance, depth);
+        }
+        
+        /**
+        * Workhorse method that is exposed via the two public assert methods.
+        * 
+        * Given a root element it compares a set of properties across that element and any of its ancestors
+        * 
+        * @param rootContainer - the root element to inspect
+        * @param postLayout - whether to look at the postLayoutTransformOffsets object of an element
+        * @param propertyNameString - a string deliminated with a character that lists the properties to inspect
+        * @param expectedValuesString - a string deliminiated with a character that lists the values to expect
+        * @param tolerance - the amount of difference between actual and expected is allowed before failure
+        * @param depth - how deep to recurse in the rootContainer
+        *  
+        * @return - a string of either "PASS" or "FAIL: ..." with a failure message
+        * 
+        */
+        private static function checkPropertySet(rootContainer:IVisualElement, postLayout:Boolean, propertyNamesString:String, 
+                                    expectedValuesString:String, tolerance:Number = 0, depth:int = -1):String {
+            
+            // reset the result of the last comparison
+            // add to this collection at any point a comparison happens
+            lastResult = new ArrayCollection();
+            
+            // get the list of elements to inspect properties of 
+            var elementsToInspect:Array = getElementsToInspect(rootContainer, depth);
+            
+            // get the list of properties to inspect on each element
+            var propertyNames:Array = getPropertyNames(propertyNamesString);
+            
+            // split up the expectedValue string into values for each element
+            var expectedElementValues:Array = expectedValuesString.split(elementSeparator);
+            
+            // string that represents the reason for fail
+            var failString:String = "";
+            
+            if (elementsToInspect.length != expectedElementValues.length){
+                // this will also catch existance failures, for example if an
+                // element is supposed to be included or excluded from a state
+                failString = "FAIL: number of elements (" + elementsToInspect.length + ") != number of expected elements (" + expectedElementValues.length + ")";
+                logResult(failString);
+                return failString;
+            }
+            
+            // Go through each of the elements recursively in the rootContainer
+            for (var i:int = 0; i < elementsToInspect.length; i++){
+                var element:IVisualElement = elementsToInspect[i];
+                var expectedPropertyValues:Array = expectedElementValues[i].split(propertySeparator);
+                
+                // check for a malformed expected string
+                if (propertyNames.length != expectedPropertyValues.length){
+                    failString = "FAIL: number of properties != number of expected values for " + getElementId(element);
+                    logResult(failString);
+                    return failString;
+                }   
+                
+                // log that we are checking this property
+                logResult(getElementId(element));
+                
+                // check each property value 
+                for (var j:int = 0; j < propertyNames.length; j++){
+
+                    var propertyName:String = propertyNames[j];
+                    var e:* = expectedPropertyValues[j];
+                    var a:*;
+                    
+                    // First need to decide whether to grab the property values from 
+                    // the element or its postLayoutTransformOffsets
+                    if (postLayout){
+                        if (element.postLayoutTransformOffsets){
+                            a = element.postLayoutTransformOffsets[propertyName];
+                        } else {
+                            a = null;
+                        }
+                    } else {
+                        a = element[propertyName];                    
+                    }
+                    
+                    // prepare the log object for this property
+                    var logItem:Object = new Object();
+                    logItem.target = getElementId(element);
+                    logItem.propertyName = propertyName;
+                    logItem.actual = a;
+                    logItem.expected = e;
+                    logItem.tolerance = tolerance;
+                    logItem.postLayout = postLayout;
+                    logItem.depth = "TODO"; // TODO: one day might want to keep track of the depth of this item
+                    logItem.result = "Unknown";
+                    
+                    //
+                    // String comparison
+                    //
+                    
+                    // First just check if expected == actual via a simple string comparison.
+                    // If so then move on to the next propertyName, otherwise investigate further
+                    // via null and number comparisons.
+                    if (String(e) == String(a)){
+                        // this property passed
+                        
+                        // log the pass
+                        logResult("PASS", logItem);
+                        
+                        continue;
+                    }
+                    
+                    //
+                    // Null comparison
+                    //
+                    
+                    // expected == actual == null so this is fine, continue to next propertyName
+                    if (e == 'null' && a == null){
+
+                        // log the pass
+                        logResult("PASS", logItem);
+                        
+                        continue;
+                    }
+                    
+                    // expected or actual is null, but not both (because of above) so fail
+                    if (e == 'null' || a == null){
+                        failString = "FAIL: " + describeFailureLocation(element, propertyName, postLayout) + ": " + a + ", but expected " + e; 
+                        
+                        // log the fail
+                        logResult(failString, logItem);
+                        
+                        return failString;
+                    }
+                    
+                    //
+                    // Number comparison
+                    //
+                    
+                    // This approach assumes that it's ok treating undefined and NaN the same.
+                    // This is because Number(undefined) gets turned into NaN, if this is a limitation
+                    // might have to revisit this in the future.  
+                    var expectedValue:Number = Number(e);
+                    var actualValue:Number = Number(a);
+                    
+                    //
+                    // NaN comparison
+                    //
+                    
+                    // expected == actual == NaN, so this is fine, continue to next propertyName
+                    if (isNaN(actualValue) && isNaN(expectedValue)){
+
+                        // log the pass
+                        logResult("PASS", logItem);
+                        
+                        continue;
+                    }
+                    
+                    // expected or actual is NaN, but not both (because of above) so fail
+                    if (isNaN(actualValue) || isNaN(expectedValue)){
+                        failString = "FAIL: " + describeFailureLocation(element, propertyName, postLayout) + ": expected " + 
+                            expectedValue + ' plus or minus ' + tolerance + ", but received " + actualValue;
+                        
+                        // log the fail
+                        logResult(failString, logItem);
+                        
+                        return failString;
+                    }
+                    
+                    //
+                    // Number tolerance comparison
+                    //
+                    
+                    // expected differs from actual by more than the tolerance so fail
+                    if (Math.abs(actualValue - expectedValue) > tolerance){
+                        failString = "FAIL: " + describeFailureLocation(element, propertyName, postLayout) + ": expected " + 
+                            expectedValue + ' plus or minus ' + tolerance + ", but received " + actualValue;
+                        
+                        // log the fail
+                        logResult(failString, logItem);
+                        
+                        return failString;
+                    }
+                    
+                    // at this point the property passed
+                    
+                    // log the pass
+                    logResult("PASS", logItem);
+                }
+                // at this point the element passed, no need to log here
+            }
+            
+            return "PASS";
+        }
+        
+        /**
+        * Adds a result to the log.
+        * 
+        * @param result - a simple string to add to the log
+        * @param details - an object that if not null is added to the log after setting details.result equal to the first parameter
+        */
+        private static function logResult(result:String, details:Object = null):void {
+            if (details != null){
+                details.result = result;
+                lastResult.addItem(details);
+            } else {
+                lastResult.addItem(result);
+            }
+        }
+        
+        /**
+        * Returns the log of the last assertion result
+        */
+        public static function getLastResult():ArrayCollection {
+            return lastResult;
+        }
+        
+        /**
+        * Generates a string that describes what property of what element has failed.
+        * 
+        * ex: 
+        *   target.width
+        *   target.postLayoutTransformOffsets.width
+        */
+        private static function describeFailureLocation(element:IVisualElement, propertyName:String, postLayout:Boolean):String{
+            var output:String = "";
+            
+            output += getElementId(element);
+            
+            if (postLayout)
+                output += ".postLayoutTransformOffsets";
+            
+            output += "." + propertyName;
+            
+            return output;
+        }
+        
+        /**
+        * Given a root element and a string of property names this returns the formatted string of 
+        * each property value against that element and all descendants in a format that the assertion
+        * methods require.
+        * 
+        * @param rootContainer
+        * @param propertyNamesString - ex: 'width, height, alpha'
+        * @param postLayout - set to true if you want to access the properties of the postLayoutTransformOffsets
+        * @param requestedDepth - the depth to recurse (-1 by default for full recursion)
+        * 
+        * @return string
+        */
+        public static function generatePropertySet(rootContainer:IVisualElement, propertyNamesString:String, postLayout:Boolean = false, requestedDepth:int = -1):String {
+            // get the list of elements to inspect properties of 
+            var elementsToInspect:Array = getElementsToInspect(rootContainer, requestedDepth);
+            var propertyNames:Array = getPropertyNames(propertyNamesString);
+            var output:String = "";
+            
+            // for each element
+            for (var i:int = 0; i < elementsToInspect.length; i++){
+                var e:IVisualElement = elementsToInspect[i];
+                
+                // for each property
+                for (var j:int = 0; j < propertyNames.length; j++){
+                    // the property name
+                    var propertyName:String = propertyNames[j];
+                    
+                    // concatenate the value
+                    if (postLayout){
+                        if (e.postLayoutTransformOffsets){
+                            // access the value via the transform offsets
+                            output += e.postLayoutTransformOffsets[propertyName];
+                        } else {
+                            // the transform offsets are null
+                            output += "null";
+                        }
+                    } else {
+                        // access the value directly
+                        output += e[propertyName];
+                    }
+                    
+                    // concatenate the value separator
+                    if (j < propertyNames.length - 1)
+                        output += ",";
+                }
+                
+                // concatenate the element separator
+                if (i < elementsToInspect.length - 1)
+                    output += elementSeparator;
+            }
+            
+            return output;
+        }
+        
+        /**
+        * Returns an array of property names parsed from a comma separated string with 
+        * spaces removed. 
+        */
+        private static function getPropertyNames(s:String):Array {
+            // strip spaces
+            while (s.indexOf(" ") != -1){
+                s = s.replace(' ','');
+            }
+            
+            return s.split(propertySeparator);
+        }
+
+        /**
+        * Returns the id of an element, if one is not defined then it returns the class name
+        */
+        private static function getElementId(element:IVisualElement):String {
+            var s:String = String(Object(element).id);
+
+            return (s != "null") ? s : flash.utils.getQualifiedClassName(element).split("::")[1];
+        }
+        
+        /**
+        * Returns an array of all elements in a root element. If the element is not a
+        * container then it just returns an array of that element.
+        */
+        public static function getElementsToInspect(root:IVisualElement, requestedDepth:int):Array {
+            var output:Array = new Array();
+            
+            if (root is IVisualElementContainer){
+                // if its a container then recursively get all the elements to requestedDepth 
+                output = getDescendants(root as IVisualElementContainer, requestedDepth);
+            } else {
+                // just return the element
+                output.push(root);
+            }
+            
+            return output;
+        }
+        
+        /**
+        * Recursively generates an array of all elements in a given container (including itself) to a requested depth
+        */
+        private static function getDescendants(rootContainer:IVisualElementContainer, requestedDepth:int, depth:int = 0):Array{
+            var output:Array = new Array();
+            
+            // push the container element
+            output.push(rootContainer);
+            
+            // return if we've gone past the requested depth (and a requestedDepth of not -1)
+            if (requestedDepth != -1 && (depth >= requestedDepth)){
+                return output;
+            }
+            
+            for (var i:int = 0; i < rootContainer.numElements; i++){
+                var e:IVisualElement = rootContainer.getElementAt(i);
+                if (e is IVisualElementContainer){
+                    // recursively get the elements of the container
+                    output = output.concat(getDescendants(e as IVisualElementContainer, requestedDepth, depth+1));
+                } else {
+                    // push the non-container element
+                    output.push(e);
+                }
+            }
+
+            return output;
+        }
+        
+    }
+}
\ No newline at end of file

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/EffectTesting.as
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/flex/trunk/mustella/as3/src/mustella/EnableRemoteImageDiff.as
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/EnableRemoteImageDiff.as?rev=1333232&view=auto
==============================================================================
--- incubator/flex/trunk/mustella/as3/src/mustella/EnableRemoteImageDiff.as (added)
+++ incubator/flex/trunk/mustella/as3/src/mustella/EnableRemoteImageDiff.as Wed May  2 22:44:38 2012
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 {
+
+import flash.display.DisplayObject;
+import flash.net.*;
+import flash.events.Event;
+
+[Mixin]
+/**
+ *  A "marker" class that causes test scripts to write out
+ *  bitmaps to the urls instead of reading and comparing
+ *  so that baselines/reference-points can be created for
+ *  future comparing.
+ */
+public class EnableRemoteImageDiff
+{
+
+	/**
+	 *  Mixin callback that gets everything ready to go.
+	 *  The UnitTester waits for an event before starting
+	 */
+	public static function init(root:DisplayObject):void
+	{
+		CompareBitmap.useRemoteDiffer = true;
+	}
+
+
+}
+}

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/EnableRemoteImageDiff.as
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/flex/trunk/mustella/as3/src/mustella/EventSnifferRemote.as
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/EventSnifferRemote.as?rev=1333232&view=auto
==============================================================================
--- incubator/flex/trunk/mustella/as3/src/mustella/EventSnifferRemote.as (added)
+++ incubator/flex/trunk/mustella/as3/src/mustella/EventSnifferRemote.as Wed May  2 22:44:38 2012
@@ -0,0 +1,154 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 
+{
+import flash.display.DisplayObject;
+import flash.events.Event;
+import flash.events.FocusEvent;
+import flash.events.KeyboardEvent;
+import flash.events.MouseEvent;
+import flash.events.StatusEvent;
+import flash.net.LocalConnection;
+
+import mx.core.FlexGlobals;
+import mx.core.IFlexDisplayObject;
+import mx.core.IMXMLObject;
+import mx.core.UIComponent;
+import mx.core.mx_internal;
+use namespace mx_internal;
+
+[Mixin]
+/**
+ *  Traps most events and records them to SnifferRemoteClient.swf
+ */
+public class EventSnifferRemote
+{
+	public function EventSnifferRemote()
+	{
+	}
+
+	public static function init(root:Object):void
+	{
+		document = root;
+		if (document)
+			document.addEventListener("applicationComplete", initHandler);
+			
+  		connection = new LocalConnection();
+		connection.allowDomain("*");
+  		connection.addEventListener(StatusEvent.STATUS, statusHandler);
+  		
+		commandconnection = new LocalConnection();
+		commandconnection.allowDomain("*");
+		commandconnection.client = EventSnifferRemote;
+		commandconnection.connect("_EventSnifferCommands");
+	}
+
+    // Turn on only if the SnifferRemoteClient app's checkbox
+    // for this item is checked.
+	private static function initHandler(event:Event):void
+	{
+        connection.send("_EventSniffer", "toggleSniffersEnabled");
+	}
+
+	private static function statusHandler(event:Event):void
+	{
+	}
+
+    /**
+     *  @private
+	 *  The document containing a reference to this object
+     */
+    private static var document:Object;
+
+    /**
+     *  @private
+	 *  The local connection to the remote client
+     */
+    private static var connection:LocalConnection;
+    private static var commandconnection:LocalConnection;
+
+	public static function enableSniffer():void
+	{
+        //trace("EventSnifferRemote enabled");
+
+		// hook UIComponent so we can see all events
+		UIComponent.dispatchEventHook = dispatchEventHook;
+		document.stage.addEventListener(MouseEvent.CLICK, uberListener, true);
+		document.stage.addEventListener(MouseEvent.DOUBLE_CLICK, uberListener, true);
+		document.stage.addEventListener(MouseEvent.MOUSE_DOWN, uberListener, true);
+		document.stage.addEventListener(MouseEvent.MOUSE_MOVE, uberListener, true);
+		document.stage.addEventListener(MouseEvent.MOUSE_OUT, uberListener, true);
+		document.stage.addEventListener(MouseEvent.MOUSE_OVER, uberListener, true);
+		document.stage.addEventListener(MouseEvent.MOUSE_UP, uberListener, true);
+		document.stage.addEventListener(MouseEvent.MOUSE_WHEEL, uberListener, true);
+		document.stage.addEventListener(MouseEvent.ROLL_OUT, uberListener, true);
+		document.stage.addEventListener(MouseEvent.ROLL_OVER, uberListener, true);
+		document.stage.addEventListener(KeyboardEvent.KEY_UP, uberListener, true);
+		document.stage.addEventListener(KeyboardEvent.KEY_DOWN, uberListener, true);
+		document.stage.addEventListener(FocusEvent.FOCUS_IN, uberListener, true);
+		document.stage.addEventListener(FocusEvent.FOCUS_OUT, uberListener, true);
+	}
+
+	public static function disableSniffer():void
+	{
+        //trace("EventSnifferRemote disabled");
+        
+		// unhook UIComponent so we can see all events
+		UIComponent.dispatchEventHook = null;
+		document.stage.removeEventListener(MouseEvent.CLICK, uberListener, true);
+		document.stage.removeEventListener(MouseEvent.DOUBLE_CLICK, uberListener, true);
+		document.stage.removeEventListener(MouseEvent.MOUSE_DOWN, uberListener, true);
+		document.stage.removeEventListener(MouseEvent.MOUSE_MOVE, uberListener, true);
+		document.stage.removeEventListener(MouseEvent.MOUSE_OUT, uberListener, true);
+		document.stage.removeEventListener(MouseEvent.MOUSE_OVER, uberListener, true);
+		document.stage.removeEventListener(MouseEvent.MOUSE_UP, uberListener, true);
+		document.stage.removeEventListener(MouseEvent.MOUSE_WHEEL, uberListener, true);
+		document.stage.removeEventListener(MouseEvent.ROLL_OUT, uberListener, true);
+		document.stage.removeEventListener(MouseEvent.ROLL_OVER, uberListener, true);
+		document.stage.removeEventListener(KeyboardEvent.KEY_UP, uberListener, true);
+		document.stage.removeEventListener(KeyboardEvent.KEY_DOWN, uberListener, true);
+		document.stage.removeEventListener(FocusEvent.FOCUS_IN, uberListener, true);
+		document.stage.removeEventListener(FocusEvent.FOCUS_OUT, uberListener, true);
+	}
+
+	private static function uberListener(event:Event):void
+	{
+		dispatchEventHook(event, DisplayObject(event.target));
+	}
+
+	private static function dispatchEventHook(event:Event, target:DisplayObject):void
+	{
+	    
+		// connection.send("_EventSniffer", "appendLog", "Event", target.toString(), event.type, event.toString());
+		
+		// This is the appendLog method in SnifferRemoteClient which will accept this:
+	    // public function appendLog(dataSource:String = null, target:String=null, eventName:String = null, event:String = null):void
+
+	    var info:Object = new Object();
+	    
+	    info.dataSource = "Event";
+	    info.target = target.toString();
+	    info.eventName = event.type;
+	    info.event = event.toString();
+	    
+	    connection.send("_EventSniffer", "appendLog", info);
+	}
+}
+
+}

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/EventSnifferRemote.as
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/flex/trunk/mustella/as3/src/mustella/ExcludeFileLocation.as
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/ExcludeFileLocation.as?rev=1333232&view=auto
==============================================================================
--- incubator/flex/trunk/mustella/as3/src/mustella/ExcludeFileLocation.as (added)
+++ incubator/flex/trunk/mustella/as3/src/mustella/ExcludeFileLocation.as Wed May  2 22:44:38 2012
@@ -0,0 +1,224 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 {
+
+import flash.display.DisplayObject;
+import flash.net.URLLoader;
+import flash.net.URLRequest;
+import flash.events.Event;
+import flash.system.Capabilities;
+
+[Mixin]
+/**
+ *  Location of a hash table of tests not to run. 
+ *  This mixin should find the excludes written to one of two locations.
+ *  Browsers runs are picky about where they find the excludes, so, we serve them 
+ *  up via http (in a sibling location to the tests). 
+ *  The file is one test per line of the form ScriptName$TestID
+ *  The location of the file is assumed to be c:/temp on windows, 
+ *  or /tmp on Unix. 
+ */
+public class ExcludeFileLocation
+{
+
+	private static var loader:URLLoader;
+
+	public static var url:String;
+	public static var mustellaDir:String;
+
+	public static var frontURL:String;
+	public static var domain:String;
+
+	public static var excludeFile:String;
+
+	/**
+	 * tell UnitTester it should wait for this load to complete
+	 */
+	UnitTester.waitForExcludes = true;
+
+
+	public static var _root:DisplayObject;
+
+
+	public static var triedBrowser:Boolean = false;
+	public static var triedNormal:Boolean  = false;
+
+	// we try the http spot, in case this is a browser run
+	public static function init(root:DisplayObject):void
+	{
+
+		trace ("Hello from excludes at: " + new Date());
+
+
+		var os:String = Capabilities.os;
+
+
+		if (os.indexOf ("Windows") != -1) 
+		{
+			excludeFile = "ExcludeListWin.txt";
+		} else if (os.indexOf ("Mac") != -1) 
+		{
+			excludeFile = "ExcludeListMac.txt";
+		} else if (os.indexOf ("Linux") != -1) 
+		{
+			excludeFile = "ExcludeListLinux.txt";
+		} else 
+		{		
+			trace ("Excludes: bad: we didn't see an OS we liked: " + os);
+			excludeFile = "ExcludeListWin.txt";
+		}
+
+
+		_root = root;
+
+		url = root.loaderInfo.url;
+
+		mustellaDir = url.substring (0, url.indexOf ("mustella/tests")+14);
+
+		frontURL = url.substring (0, url.indexOf (":"));
+
+		domain = url.substring (url.indexOf (":")+3);
+		domain = domain.substring (0, domain.indexOf ("/"));
+
+		if (Capabilities.playerType == "PlugIn" || Capabilities.playerType == "ActiveX")
+		{ 
+			loadTryBrowser();
+		} else
+		{
+			loadTryNormal();
+
+		} 
+
+	}
+
+
+
+	public static function loadTryBrowser():void
+	{
+		trace ("excludes loadTryBrowser at: " + new Date().toString());
+
+		triedBrowser = true;
+
+		var currentOS:String = Capabilities.os;
+
+		var useFile:String = "http://localhost/" + excludeFile;
+
+		trace ("try excludes from here: " + useFile);
+
+		var req:URLRequest = new URLRequest(useFile);
+		loader = new URLLoader();
+		loader.addEventListener("complete", completeHandler);
+		loader.addEventListener("securityError", errorHandler);
+		loader.addEventListener("ioError", errorHandler);
+		loader.load(req);
+
+	}
+
+	/// if it worked, all good
+	private static function completeHandler(event:Event):void
+	{
+		trace ("Excludes: from web server");
+		postProcessData(event);
+	}
+
+
+	/**
+	 *  A non-browser will get its results from the /tmp location. 
+ 	 *  It should be a failure to get here; we try the browser case first
+	 *  because it throws a nasty seciry
+	 *  
+	 */
+	public static function errorHandler(event:Event):void
+	{ 
+	
+		trace ("Exclude: in the web read error handler");
+		if (!triedNormal) 
+		{
+			loadTryNormal();	
+
+		}
+
+	}
+	public static function loadTryNormal():void
+	{
+
+		triedNormal = true;
+
+		trace ("excludes loadTryFile " + new Date().toString());
+
+		var currentOS:String = Capabilities.os;
+
+		var useFile:String;
+
+		useFile = mustellaDir +"/" + excludeFile;
+
+		trace ("Exclude: try load from: " + useFile);
+
+		var req:URLRequest = new URLRequest(useFile);
+		loader = new URLLoader();
+		loader.addEventListener("complete", completeHandler2);
+		loader.addEventListener("ioError", errorHandler2);
+		loader.addEventListener("securityError", errorHandler2);
+		loader.load(req);
+
+	}
+
+	private static function errorHandler2(event:Event):void
+	{
+		trace ("Exclude: error in the exclude file load " +event);	
+		if (!triedBrowser) 
+		{
+			loadTryBrowser();
+		}
+
+	}
+
+
+	private static function completeHandler2(event:Event):void
+	{
+		trace ("Excludes: Reading from file system at "+mustellaDir );
+		postProcessData(event);
+	}
+	private static function postProcessData(event:Event):void
+	{
+		var data:String = loader.data;
+		// DOS end of line
+		var delimiter:RegExp = new RegExp("\r\n", "g");
+		data = data.replace(delimiter, ",");
+		// Unix end of line
+		delimiter = new RegExp("\n", "g");
+		data = data.replace(delimiter, ",");
+
+		UnitTester.excludeList = new Object();
+		var items:Array = data.split(",");
+		var n:int = items.length;
+		for (var i:int = 0; i < n; i++)
+		{
+			var s:String = items[i];
+			if (s.length)
+				UnitTester.excludeList[s] = 1;
+
+		}
+		
+		UnitTester.waitForExcludes = false;
+		UnitTester.pre_startEventHandler(event);
+	}
+}
+
+}

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/ExcludeFileLocation.as
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/flex/trunk/mustella/as3/src/mustella/ExcludeFileLocationApollo.as
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/ExcludeFileLocationApollo.as?rev=1333232&view=auto
==============================================================================
--- incubator/flex/trunk/mustella/as3/src/mustella/ExcludeFileLocationApollo.as (added)
+++ incubator/flex/trunk/mustella/as3/src/mustella/ExcludeFileLocationApollo.as Wed May  2 22:44:38 2012
@@ -0,0 +1,285 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 {
+
+import flash.display.DisplayObject;
+import flash.net.URLLoader;
+import flash.net.URLRequest;
+import flash.events.Event;
+import flash.system.Capabilities;
+import flash.filesystem.File;
+
+[Mixin]
+/**
+ *  Location of a hash table of tests not to run. 
+ *  This mixin should find the excludes written to one of two locations.
+ *  Browsers runs are picky about where they find the excludes, so, we serve them 
+ *  up via http (in a sibling location to the tests). 
+ *  The file is one test per line of the form ScriptName$TestID
+ *  The location of the file is assumed to be c:/temp on windows, 
+ *  or /tmp on Unix. 
+ */
+public class ExcludeFileLocationApollo
+{
+
+	private static var loader:URLLoader;
+
+	private static var url:String;
+	private static var mustellaDir:String;
+
+	private static var frontURL:String;
+	private static var domain:String;
+
+	public static var excludeFile:String = "ExcludeList.txt";	
+
+	/**
+	 * tell UnitTester it should wait for this load to complete
+	 */
+	UnitTester.waitForExcludes = true;
+
+
+	public static var _root:DisplayObject;
+
+
+	public static var triedBrowser:Boolean = false;
+	public static var triedNormal:Boolean  = false;
+
+	// we try the http spot, in case this is a browser run
+	public static function init(root:DisplayObject):void
+	{
+
+		trace ("Hello from excludes at: " + new Date());
+
+		var os:String = Capabilities.os;
+
+		// This seems to be a timing issue which pops up on some machines.
+		if( UnitTester.cv == null ){
+			UnitTester.cv = new ConditionalValue();
+		}
+
+		if( (UnitTester.cv.os != null) && (UnitTester.cv.os.toLowerCase() == DeviceNames.ANDROID.toLowerCase()) ){
+			mustellaDir = "..";
+			excludeFile = UnitTester.excludeFile;
+			trace("Doing Android style exclude.  Checking in " + mustellaDir + "/" + excludeFile);
+		}else if( (UnitTester.cv.os != null) && (UnitTester.cv.os.toLowerCase() == DeviceNames.IOS.toLowerCase()) ){
+			mustellaDir = File.documentsDirectory.url;
+			excludeFile = UnitTester.excludeFile;
+		}else if (os.indexOf ("QNX") > -1) {
+			mustellaDir = "..";
+			excludeFile = UnitTester.excludeFile;
+			trace("Doing QNX style exclude.  Checking in " + mustellaDir + "/" + excludeFile);
+		}
+		else{
+
+			if (os.indexOf ("Windows") != -1) 
+			{
+				excludeFile = "ExcludeListWin.txt";
+			} else if (os.indexOf ("Mac") != -1) 
+			{
+				excludeFile = "ExcludeListMac.txt";
+			} else if (os.indexOf ("Linux") != -1) 
+			{
+				excludeFile = "ExcludeListLinux.txt";
+			} else 
+			{		
+				trace ("Excludes: bad: we didn't see an OS we liked: " + os);
+				excludeFile = "ExcludeListWin.txt";
+			}
+
+			_root = root;
+
+			url = root.loaderInfo.url;
+
+			if (url.indexOf("app:")!=-1) 
+			{
+				ApolloFilePath.init (root);
+							url = encodeURI2(ApolloFilePath.apolloAdjust(url));
+				trace ("Adjusting path for AIR to: " + url);
+						   // url = adjustPath(url);
+			}
+
+			mustellaDir = url.substring (0, url.indexOf ("mustella/tests")+14);
+			frontURL = url.substring (0, url.indexOf (":"));
+			domain = url.substring (url.indexOf (":")+3);		
+			domain = domain.substring (0, domain.indexOf ("/"));
+		}
+		
+		loadTryNormal();
+	}
+
+	private static function apolloAdjust(url:String):String
+	{
+
+		var swf:String = _root.loaderInfo.url;
+		var f:File = new File (swf);
+		// clean it up: 
+		var myPattern:RegExp = /\\/g; 
+		var path:String = f.nativePath;
+		path = path.replace (myPattern, "/");
+		path = path.replace (":", "|");
+		// yank off the swfs directory, which we're in
+		path = path.substr (0, path.lastIndexOf ("/")-1);
+		path = path.substr (0, path.lastIndexOf ("/"));
+
+		if (url.indexOf ("../")==0)
+			url = url.substring (2);
+
+		if (url.indexOf ("/..")==0)
+		{
+			url = url.substring (3);
+			path = path.substr (0, path.lastIndexOf ("/"));
+		}
+
+		/// create the final url
+		path = path + url;
+
+		return "file:///" + path;
+	}
+
+	public static var adjustPath:Function = function(url:String):String { return url; };
+
+
+
+        private static function encodeURI2(s:String):String
+        {
+                var pos:int = s.lastIndexOf("/");
+                if (pos != -1)
+                {
+                        var fragment:String = s.substring(pos + 1);
+                        s = s.substring(0, pos + 1);
+                        fragment= encodeURIComponent(fragment);
+                        s = s + fragment;
+                }
+                return s;
+        }
+
+
+	public static function loadTryBrowser():void
+	{
+		trace ("excludes loadTryBrowser at: " + new Date().toString());
+
+		triedBrowser = true;
+
+		var currentOS:String = Capabilities.os;
+
+		var useFile:String = "http://localhost/" + excludeFile;
+
+		trace ("try excludes from here: " + useFile);
+
+		var req:URLRequest = new URLRequest(useFile);
+		loader = new URLLoader();
+		loader.addEventListener("complete", completeHandler);
+		loader.addEventListener("securityError", errorHandler);
+		loader.addEventListener("ioError", errorHandler);
+		loader.load(req);
+
+	}
+
+	/// if it worked, all good
+	private static function completeHandler(event:Event):void
+	{
+		trace ("Excludes: from web server");
+		postProcessData(event);
+	}
+
+
+	/**
+	 *  A non-browser will get its results from the /tmp location. 
+ 	 *  It should be a failure to get here; we try the browser case first
+	 *  because it throws a nasty seciry
+	 *  
+	 */
+	public static function errorHandler(event:Event):void
+	{ 
+	
+		trace ("Exclude: in the web read error handler");
+		if (!triedNormal) 
+		{
+			loadTryNormal();	
+
+		}
+
+	}
+	public static function loadTryNormal():void
+	{
+
+		triedNormal = true;
+
+		trace ("excludes loadTryFile " + new Date().toString());
+
+		var currentOS:String = Capabilities.os;
+
+		var useFile:String;
+
+		useFile = mustellaDir +"/" + excludeFile;
+
+		trace ("Exclude: try load from: " + useFile);
+
+		var req:URLRequest = new URLRequest(useFile);
+		loader = new URLLoader();
+		loader.addEventListener("complete", completeHandler2);
+		loader.addEventListener("ioError", errorHandler2);
+		loader.addEventListener("securityError", errorHandler2);
+		loader.load(req);
+
+	}
+
+	private static function errorHandler2(event:Event):void
+	{
+		trace ("Exclude: error in the exclude file load " +event);	
+		if (!triedBrowser) 
+		{
+			loadTryBrowser();
+		}
+
+	}
+
+
+	private static function completeHandler2(event:Event):void
+	{
+		trace ("Excludes: Reading from file system at "+mustellaDir );
+		postProcessData(event);
+	}
+	private static function postProcessData(event:Event):void
+	{
+		var data:String = loader.data;
+		// DOS end of line
+		var delimiter:RegExp = new RegExp("\r\n", "g");
+		data = data.replace(delimiter, ",");
+		// Unix end of line
+		delimiter = new RegExp("\n", "g");
+		data = data.replace(delimiter, ",");
+
+		UnitTester.excludeList = new Object();
+		var items:Array = data.split(",");
+		var n:int = items.length;
+		for (var i:int = 0; i < n; i++)
+		{
+			var s:String = items[i];
+			if (s.length)
+				UnitTester.excludeList[s] = 1;
+
+		}
+		
+		UnitTester.waitForExcludes = false;
+		UnitTester.pre_startEventHandler(event);
+	}
+}
+
+}

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/ExcludeFileLocationApollo.as
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/flex/trunk/mustella/as3/src/mustella/ExcludeList.as
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/ExcludeList.as?rev=1333232&view=auto
==============================================================================
--- incubator/flex/trunk/mustella/as3/src/mustella/ExcludeList.as (added)
+++ incubator/flex/trunk/mustella/as3/src/mustella/ExcludeList.as Wed May  2 22:44:38 2012
@@ -0,0 +1,44 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 {
+
+import flash.display.DisplayObject;
+
+[Mixin]
+/**
+ *  A hash table of tests not to run.
+ */
+public class ExcludeList 
+{
+
+	/**
+	 *  Mixin callback that gets everything ready to go.
+	 *  Table is of form: ScriptName$TestID: 1,
+	 */
+	public static function init(root:DisplayObject):void
+	{
+		UnitTester.excludeList = {
+			CBTester$myButtonTest1: 1,
+			CBTester$myTest2: 1
+		};
+	}
+
+}
+
+}

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/ExcludeList.as
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/flex/trunk/mustella/as3/src/mustella/ExcludeList.txt
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/ExcludeList.txt?rev=1333232&view=auto
==============================================================================
--- incubator/flex/trunk/mustella/as3/src/mustella/ExcludeList.txt (added)
+++ incubator/flex/trunk/mustella/as3/src/mustella/ExcludeList.txt Wed May  2 22:44:38 2012
@@ -0,0 +1 @@
+CBTester$myButtonTest1
\ No newline at end of file

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/ExcludeList.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/flex/trunk/mustella/as3/src/mustella/ExcludeListTextFile.as
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/ExcludeListTextFile.as?rev=1333232&view=auto
==============================================================================
--- incubator/flex/trunk/mustella/as3/src/mustella/ExcludeListTextFile.as (added)
+++ incubator/flex/trunk/mustella/as3/src/mustella/ExcludeListTextFile.as Wed May  2 22:44:38 2012
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 {
+
+import flash.display.DisplayObject;
+import flash.net.URLLoader;
+import flash.net.URLRequest;
+import flash.events.Event;
+
+[Mixin]
+/**
+ *  A hash table of tests not to run, read from ExcludeList.txt
+ *  The file is one test per line of the form ScriptName$TestID
+ */
+public class ExcludeListTextFile
+{
+
+	private static var loader:URLLoader;
+
+	/**
+	 *  Mixin callback that gets everything ready to go.
+	 *  Table is of form: ScriptName$TestID: 1,
+	 */
+	public static function init(root:DisplayObject):void
+	{
+		var req:URLRequest = new URLRequest("ExcludeList.txt");
+		loader = new URLLoader();
+		loader.addEventListener("complete", completeHandler);
+		loader.load(req);
+
+	}
+
+	private static function completeHandler(event:Event):void
+	{
+		var data:String = loader.data;
+		// DOS end of line
+		var delimiter:RegExp = new RegExp("\r\n", "g");
+		data = data.replace(delimiter, ",");
+		// Unix end of line
+		delimiter = new RegExp("\n", "g");
+		data = data.replace(delimiter, ",");
+
+		UnitTester.excludeList = new Object();
+		var items:Array = data.split(",");
+		var n:int = items.length;
+		for (var i:int = 0; i < n; i++)
+		{
+			var s:String = items[i];
+			if (s.length)
+				UnitTester.excludeList[s] = 1;
+		}
+	}
+}
+
+}

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/ExcludeListTextFile.as
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/flex/trunk/mustella/as3/src/mustella/ExitWhenDone.as
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/ExitWhenDone.as?rev=1333232&view=auto
==============================================================================
--- incubator/flex/trunk/mustella/as3/src/mustella/ExitWhenDone.as (added)
+++ incubator/flex/trunk/mustella/as3/src/mustella/ExitWhenDone.as Wed May  2 22:44:38 2012
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 {
+
+import flash.display.DisplayObject;
+import flash.net.*;
+import flash.events.Event;
+
+[Mixin]
+/**
+ *  A "marker" class that causes test scripts to write out
+ *  bitmaps to the urls instead of reading and comparing
+ *  so that baselines/reference-points can be created for
+ *  future comparing.
+ */
+public class ExitWhenDone
+{
+
+	/**
+	 *  Mixin callback that gets everything ready to go.
+	 *  The UnitTester waits for an event before starting
+	 */
+	public static function init(root:DisplayObject):void
+	{
+		UnitTester.exitWhenDone = true;
+	}
+
+
+}
+}

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/ExitWhenDone.as
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR-app.xml
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR-app.xml?rev=1333232&view=auto
==============================================================================
--- incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR-app.xml (added)
+++ incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR-app.xml Wed May  2 22:44:38 2012
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!--
+
+  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.
+
+-->
+<application xmlns="http://ns.adobe.com/air/application/1.5">
+
+<!-- Adobe AIR Application Descriptor File Template.
+
+	Specifies parameters for identifying, installing, and launching AIR applications.
+
+	xmlns - The Adobe AIR namespace: http://ns.adobe.com/air/application/1.5
+			The last segment of the namespace specifies the version 
+			of the AIR runtime required for this application to run.
+			
+	minimumPatchLevel - The minimum patch level of the AIR runtime required to run 
+			the application. Optional.
+-->
+
+	<!-- The application identifier string, unique to this application. Required. -->
+	<id>ImageDiff</id>
+
+	<!-- Used as the filename for the application. Required. -->
+	<filename>ImageDiff</filename>
+
+	<!-- The name that is displayed in the AIR application installer. 
+	     May have multiple values for each language. See samples or xsd schema file. Optional. -->
+	<name>ImageDiff</name>
+
+	<!-- An application version designator (such as "v1", "2.5", or "Alpha 1"). Required. -->
+	<version>v2</version>
+
+	<!-- Description, displayed in the AIR application installer.
+	     May have multiple values for each language. See samples or xsd schema file. Optional. -->
+	<!-- <description></description> -->
+
+	<!-- Copyright information. Optional -->
+	<!-- <copyright></copyright> -->
+
+	<!-- Settings for the application's initial window. Required. -->
+	<initialWindow>
+		<!-- The main SWF or HTML file of the application. Required. -->
+		<!-- Note: In Flex Builder, the SWF reference is set automatically. -->
+		<content>ImageDiffAIR.swf</content>
+		
+		<!-- The title of the main window. Optional. -->
+		<!-- <title></title> -->
+
+		<!-- The type of system chrome to use (either "standard" or "none"). Optional. Default standard. -->
+		<!-- <systemChrome></systemChrome> -->
+
+		<!-- Whether the window is transparent. Only applicable when systemChrome is none. Optional. Default false. -->
+		<!-- <transparent></transparent> -->
+
+		<!-- Whether the window is initially visible. Optional. Default false. -->
+		<!-- <visible></visible> -->
+
+		<!-- Whether the user can minimize the window. Optional. Default true. -->
+		<!-- <minimizable></minimizable> -->
+
+		<!-- Whether the user can maximize the window. Optional. Default true. -->
+		<!-- <maximizable></maximizable> -->
+
+		<!-- Whether the user can resize the window. Optional. Default true. -->
+		<!-- <resizable></resizable> -->
+
+		<!-- The window's initial width. Optional. -->
+		<!-- <width></width> -->
+
+		<!-- The window's initial height. Optional. -->
+		<!-- <height></height> -->
+
+		<!-- The window's initial x position. Optional. -->
+		<!-- <x></x> -->
+
+		<!-- The window's initial y position. Optional. -->
+		<!-- <y></y> -->
+
+		<!-- The window's minimum size, specified as a width/height pair, such as "400 200". Optional. -->
+		<!-- <minSize></minSize> -->
+
+		<!-- The window's initial maximum size, specified as a width/height pair, such as "1600 1200". Optional. -->
+		<!-- <maxSize></maxSize> -->
+	</initialWindow>
+
+	<!-- The subpath of the standard default installation location to use. Optional. -->
+	<!-- <installFolder></installFolder> -->
+
+	<!-- The subpath of the Programs menu to use. (Ignored on operating systems without a Programs menu.) Optional. -->
+	<!-- <programMenuFolder></programMenuFolder> -->
+
+	<!-- The icon the system uses for the application. For at least one resolution,
+		 specify the path to a PNG file included in the AIR package. Optional. -->
+	<!-- <icon>
+		<image16x16></image16x16>
+		<image32x32></image32x32>
+		<image48x48></image48x48>
+		<image128x128></image128x128>
+	</icon> -->
+
+	<!-- Whether the application handles the update when a user double-clicks an update version
+	of the AIR file (true), or the default AIR application installer handles the update (false).
+	Optional. Default false. -->
+	<!-- <customUpdateUI></customUpdateUI> -->
+	
+	<!-- Whether the application can be launched when the user clicks a link in a web browser.
+	Optional. Default false. -->
+	<!-- <allowBrowserInvocation></allowBrowserInvocation> -->
+
+	<!-- Listing of file types for which the application can register. Optional. -->
+	<!-- <fileTypes> -->
+
+		<!-- Defines one file type. Optional. -->
+		<!-- <fileType> -->
+
+			<!-- The name that the system displays for the registered file type. Required. -->
+			<!-- <name></name> -->
+
+			<!-- The extension to register. Required. -->
+			<!-- <extension></extension> -->
+			
+			<!-- The description of the file type. Optional. -->
+			<!-- <description></description> -->
+			
+			<!-- The MIME content type. -->
+			<!-- <contentType></contentType> -->
+			
+			<!-- The icon to display for the file type. Optional. -->
+			<!-- <icon>
+				<image16x16></image16x16>
+				<image32x32></image32x32>
+				<image48x48></image48x48>
+				<image128x128></image128x128>
+			</icon> -->
+			
+		<!-- </fileType> -->
+	<!-- </fileTypes> -->
+
+</application>
\ No newline at end of file

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR-app.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR.air
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR.air?rev=1333232&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR.air
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR.mxml
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR.mxml?rev=1333232&view=auto
==============================================================================
--- incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR.mxml (added)
+++ incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR.mxml Wed May  2 22:44:38 2012
@@ -0,0 +1,509 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" initialize="initLC()" creationComplete="doCreationComplete()">
+<mx:Script>
+	<![CDATA[
+	    import flash.geom.Rectangle;
+		import mx.controls.Label;
+		import mx.core.UIComponent;
+
+		private var results:UIComponent;
+        private var image1Loaded:Boolean;
+        private var image2Loaded:Boolean;
+		private var digits:Array = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
+
+		private function initLC():void
+		{
+			for (var i:int = 0; i < 16; i++)
+			{
+				for (var j:int = 0; j < 16; j++)
+				{
+					byteTable[digits[i] + digits[j]] = i * 16 + j;
+				}
+			}
+		}
+
+        private function doCreationComplete():void{
+            nativeWindow.bounds = new Rectangle(0, 0, flash.system.Capabilities.screenResolutionX, flash.system.Capabilities.screenResolutionY);
+        }
+
+		private function statusHandler(event:Event):void
+		{
+		}
+
+		private var sbd:BitmapData;
+		private var sba:ByteArray;
+		public function startScreenData(w:int, h:int, length:int):void
+		{
+			sba = new ByteArray();
+			sbd = new BitmapData(w, h);
+		}
+		public function addScreenData(s:String):void
+		{
+			toByteArray(sba, s);
+		}
+
+		private var bbd:BitmapData;
+		private var bba:ByteArray;
+		public function startBaseData(w:int, h:int, length:int):void
+		{
+			bba = new ByteArray();
+			bbd = new BitmapData(w, h);
+		}
+		public function addBaseData(s:String):void
+		{
+			toByteArray(bba, s);
+		}
+
+		public function compareBitmaps():void
+		{
+			var bm1:Bitmap = new Bitmap();
+			var bm2:Bitmap = new Bitmap();
+			sba.position = 0;
+			bba.position = 0;
+			sbd.setPixels(sbd.rect, sba);
+			bbd.setPixels(bbd.rect, bba);
+			bm1.bitmapData = sbd;
+			bm2.bitmapData = bbd;
+			image1.load(bm1);
+			image2.load(bm2);
+			image1.scaleX = 1;
+			image1.scaleY = 1;
+			image2.scaleX = 1;
+			image2.scaleY = 1;
+			canvas1.width = sbd.width;
+			canvas1.height = sbd.height;
+			canvas2.width = bbd.width;
+			canvas2.height = bbd.height;
+
+			doCompare();
+		}
+
+		private var byteTable:Object = new Object();
+
+		private function toByteArray(ba:ByteArray, s:String):void
+		{
+			var arr:Array = s.split(",");
+
+			var n:int = arr.length;
+			for (var i:int = 0; i < n; i++)
+			{
+				var b:String = arr[i];
+				var byte:int = byteTable[b];
+				ba.writeByte(byte);
+			}
+		}
+
+		public function doCompare():void
+		{
+			var bm1:BitmapData = new BitmapData(image1.content.width, image1.content.height);
+			bm1.draw(image1.content, new Matrix());
+
+			var bm2:BitmapData = new BitmapData(image2.content.width, image2.content.height);
+			bm2.draw(image2.content, new Matrix());
+
+			if (results)
+				canvas3.removeChild(results);
+
+
+			var cmp:Object = bm1.compare(bm2);
+			var label:Label;
+
+			if (cmp == 0)
+			{
+				results = label = new Label();
+				label.text = "Same";
+				label.validateNow();
+				canvas3.addChild(results);
+				canvas3.width = results.width;
+				canvas3.height = results.height;
+			}
+			else if (cmp == -3)
+			{
+				results = label = new Label();
+				label.text = "Widths are Different: " + image1.content.width + " vs " + image2.content.width;
+				label.validateNow();
+				canvas3.addChild(results);
+				canvas3.width = results.width;
+				canvas3.height = results.height;
+			}
+			else if (cmp == -4)
+			{
+				results = label = new Label();
+				label.text = "Heights are Different: " + image1.content.height + " vs " + image2.content.height;
+				label.validateNow();
+				canvas3.addChild(results);
+				canvas3.width = results.width;
+				canvas3.height = results.height;
+			}
+			else
+			{
+				results = new UIComponent();
+				var bm:Bitmap = new Bitmap();
+				results.addChild(bm);
+				bm.bitmapData = BitmapData(cmp);
+				results.width = image1.content.width;
+				results.height = image1.content.height;
+				canvas3.addChild(results);
+				canvas3.width = results.width;
+				canvas3.height = results.height;
+				results.graphics.clear();
+				results.graphics.beginFill(bg.selectedColor);
+				results.graphics.drawRect(0, 0, results.width, results.height);
+				results.graphics.endFill();
+			}
+
+		}
+
+        // Show previous image.
+		private function prevHandler():void
+		{
+		    if(--currentIndex < 0){
+		        currentIndex = selectedImagesArray.length - 1;
+		    }
+
+            initiateLoading();
+		}
+
+        // Show next image.
+		private function nextHandler():void
+		{
+		    if(++currentIndex == selectedImagesArray.length){
+		        currentIndex = 0;
+		    }
+
+            initiateLoading();
+		}
+
+        // Start loading images.  We continue with handleImage(1 or 2)Complete
+        private function initiateLoading():void{
+			image1Loaded = false;
+			image2Loaded = false;
+
+			image1.source = selectedImagesArray[currentIndex].url;
+			image2.source = selectedImagesArray[currentIndex].url.substring(0, selectedImagesArray[currentIndex].url.length - 8);
+
+			progressLabel.text = (currentIndex+1) + " of " + selectedImagesArray.length;
+        }
+
+        private function handleImage1Complete():void{
+		    image1Loaded = true;
+		    sizeCanvas1();
+		    
+		    if(image1Loaded && image2Loaded){
+                doCompare();
+		    }        
+        }
+
+        private function handleImage2Complete():void{
+		    image2Loaded = true;
+		    sizeCanvas2();
+		    
+		    if(image1Loaded && image2Loaded){
+                doCompare();
+		    }        
+        }
+		
+		private function bgChanged():void
+		{
+			if (results)
+			{
+				results.graphics.clear();
+				results.graphics.beginFill(bg.selectedColor);
+				results.graphics.drawRect(0, 0, results.width, results.height);
+				results.graphics.endFill();
+			}
+		}
+
+		private function getPixel(target:UIComponent, x:int, y:int, lbl:Label, bg:UIComponent):void
+		{
+			var pt:Point = new Point(x, y);
+			var screenBits:BitmapData = new BitmapData(target.width, target.height);
+			screenBits.draw(target, new Matrix(1, 0, 0, 1, 0, 0));
+
+			var clr:uint = screenBits.getPixel(pt.x, pt.y);
+			var s:String = clr.toString(16);
+			while (s.length < 6)
+			{
+				s = "0" + s;
+			}
+			lbl.text = s.toUpperCase();
+			bg.graphics.beginFill(clr);
+			bg.graphics.drawRect(0, 0, bg.width, bg.height);
+			bg.graphics.endFill();
+		}
+
+		private function pixelTracking():void
+		{
+			if (cb.selected)
+				systemManager.addEventListener("mouseMove", pixelTracker);
+			else
+				systemManager.removeEventListener("mouseMove", pixelTracker);
+		}
+
+		private function pixelTracker(event:MouseEvent):void
+		{
+			if (image1.contains(event.target as DisplayObject))
+			{
+				updatePixels(image1, event.localX, event.localY, "1");
+			}
+			else if (image2.contains(event.target as DisplayObject))
+			{
+				updatePixels(image2, event.localX, event.localY, "2");
+			}
+			else if (results && results.contains(event.target as DisplayObject))
+			{
+				updatePixels(results, event.localX, event.localY, "3");
+			}
+		}
+
+		private function updatePixels(target:UIComponent, x:Number, y:Number, ui:String):void
+		{
+			var nsx:NumericStepper = this["img" + ui + "x"];
+			var nsy:NumericStepper = this["img" + ui + "y"];
+			nsx.value = x;
+			nsy.value = y;
+			var lbl:Label = this["pixel" + ui];
+			var bg:UIComponent = this["bg" + ui];
+			getPixel(target, x, y, lbl, bg);
+		}
+
+		private function sizeCanvas1():void
+		{
+			image1.scaleX = 1;
+			image1.scaleY = 1;
+			canvas1.width = image1.content.width;
+			canvas1.height = image1.content.height;
+		}
+
+		private function sizeCanvas2():void
+		{
+			image2.scaleX = 1;
+			image2.scaleY = 1;
+			canvas2.width = image2.content.width;
+			canvas2.height = image2.content.height;
+		}
+
+		private function zoomit():void
+		{
+			image1.scaleY = image1.scaleX = zoom.value;
+			image1.validateNow();
+			image2.scaleY = image2.scaleX = zoom.value;
+			image2.validateNow();
+			results.scaleY = results.scaleX = zoom.value;
+			results.validateNow();
+			if (canvas1.width < 100)
+			{
+				canvas1.width = Math.min(image1.width, 100);
+				canvas1.height= Math.min(image1.height, 100);
+				canvas2.width = Math.min(image2.width, 100);
+				canvas2.height= Math.min(image2.height, 100);
+				canvas3.width = Math.min(results.width, 100);
+				canvas3.height= Math.min(results.height, 100);
+			}
+		}
+	]]>
+	</mx:Script>
+	<mx:Script>
+		<![CDATA[
+			import flash.filesystem.File;
+			
+			[Bindable]
+			private var selectedImagesArray:Array = [];
+			
+			[Bindable]
+			private var currentIndex:int = -1;
+			
+			private function getImages():void
+			{
+				if (!fs.selectedItem)
+					return;
+				
+				var dir:File = File(fs.selectedItem.isDirectory ? fs.selectedItem : fs.selectedItem.parent);
+				
+				var filesArray:Array = [];
+				
+				getFilesFromDir(dir, filesArray);
+				
+				dg.dataProvider = filesArray;
+				
+				accord.selectedIndex = 1;
+			}
+			
+			private function getImages2():void
+			{
+				if (!dg.selectedItems)
+					return;
+					
+				selectedImagesArray = dg.selectedItems;
+				currentIndex = -1;
+				nextHandler();
+				accord.selectedIndex = 2;
+			}
+			
+			private function deleteImages2():void
+			{
+				if (!dg.selectedItems)
+					return;
+				
+				for (var i:int=0; i<dg.selectedItems.length; i++) {
+					var file:File = File(dg.selectedItems[i]);
+					file.deleteFileAsync();
+				}
+				accord.selectedIndex = 0;
+			}
+			
+			private function getFilesFromDir(dir:File, filesArray:Array):void
+			{
+				var files:Array = dir.getDirectoryListing();
+				
+				for(var i:int=0; i<files.length; i++)
+				{
+					var file:File = File(files[i]);
+					if (file.isDirectory)
+					{
+						getFilesFromDir(file, filesArray);
+					}
+					else
+					{
+						if (file.url.indexOf(".png.bad.png") != -1)
+						{
+							filesArray.push(file);
+						}
+					}
+				}
+			}
+			
+			private function getFileName(item:Object,blah:*=null):String
+			{
+				var file:File = File(item);
+				return getFileNameFromString(file.url);
+			}
+			
+			private function getFileNameFromString(item:String):String
+			{
+				if(item == null)
+					return "";
+				var index:Number = item.indexOf("mustella");
+				if (index == -1)
+					return item;
+					
+				return item.substr(index + 14);
+			}
+		]]>
+	</mx:Script>
+	<mx:Accordion id="accord" width="100%" height="100%" creationPolicy="all">
+		<mx:VBox label="Select Directory" width="100%" height="100%" verticalAlign="center" horizontalAlign="center" backgroundColor="0x008888">
+		
+            <!-- To do: load a local XML file for people to personalize.
+            
+            <mx:RadioButtonGroup id="rbgTestDirs" itemClick="fs.directory = new File(rbgTestDirs.selectedValue as String)" />
+			<mx:HBox>
+                <mx:RadioButton groupName="rbgTestDirs" label="3.x Tests" value="/Users/rv/source/depot/flex/branches/3.x/qa/sdk/testsuites/mustella/tests" />
+			</mx:HBox>
+			<mx:HBox>
+                <mx:RadioButton groupName="rbgTestDirs" label="DataGrid" value="/Users/rv/source/depot/flex/qa/sdk/testsuites/mustella/tests/components/DataGrid" />
+                <mx:RadioButton groupName="rbgTestDirs" label="DataGridColumn" value="/Users/rv/source/depot/flex/qa/sdk/testsuites/mustella/tests/components/DataGridColumn" />
+                <mx:RadioButton groupName="rbgTestDirs" label="TileLayout" value="/Users/rv/source/depot/flex/qa/sdk/testsuites/mustella/tests/gumbo/layout/TileLayout" />
+                <mx:RadioButton groupName="rbgTestDirs" label="ShaderFilter" value="/Users/rv/source/depot/flex/qa/sdk/testsuites/mustella/tests/gumbo/filters/ShaderFilter" />
+                <mx:RadioButton groupName="rbgTestDirs" label="States" value="/Users/rv/source/depot/flex/qa/sdk/testsuites/mustella/tests/States" />
+                <mx:RadioButton groupName="rbgTestDirs" label="Spark WA" value="/Users/rv/source/depot/flex/qa/sdk/testsuites/mustella/tests/apollo/spark/components/WindowedApplication" />
+                <mx:RadioButton groupName="rbgTestDirs" label="Tests" value="/Users/rv/source/depot/flex/qa/sdk/testsuites/mustella/tests" />
+			</mx:HBox>
+            -->		
+
+			<mx:FileSystemTree id="fs"  width="100%" height="100%" />
+			<mx:Button label="Find Images" click="getImages()" />
+			<mx:Label text="{fs.selectedItem.url}" />
+		</mx:VBox>
+		<mx:VBox label="Select Images" width="100%" height="100%" verticalAlign="center" horizontalAlign="center" backgroundColor="0x008888">
+			<mx:DataGrid id="dg" width="100%" height="100%" allowMultipleSelection="true">
+				<mx:columns>
+					<mx:DataGridColumn headerText="File" labelFunction="getFileName" />
+				</mx:columns>
+			</mx:DataGrid>
+			<mx:Button click="getImages2()" label="Compare Selected Images" />
+			<mx:Button click="deleteImages2()" label="Delete Selected Images" />
+		</mx:VBox>
+		<mx:VBox label="Compare Images" width="100%" height="100%" verticalAlign="center" horizontalAlign="center" backgroundColor="0x008888">
+			<mx:HBox>
+				<mx:Button id="prevButton" click="prevHandler()" label="&lt; Prev Image"/>
+				<mx:Label id="progressLabel" />
+				<mx:Button id="nextButton" click="nextHandler()" label="Next Image &gt;"/>
+			</mx:HBox>
+			<mx:HBox>
+				<mx:VBox>
+					<mx:VBox>
+						<mx:Canvas id="canvas1" minHeight="0" minWidth="0" >
+							<mx:Image id="image1" complete="handleImage1Complete()" />
+						</mx:Canvas>
+						<mx:Label text="{getFileNameFromString(String(image1.source))}" />
+					</mx:VBox>
+					<mx:HBox>
+						<mx:Label text="x" />
+						<mx:NumericStepper id="img1x" width="60" maximum="4000" />
+						<mx:Label text="y" />
+						<mx:NumericStepper id="img1y" width="60" maximum="4000" />
+						<mx:Button label="get pixel" click="getPixel(image1, img1x.value, img1y.value, pixel1, bg1)" />
+						<mx:Label id="pixel1" />
+						<mx:UIComponent id="bg1" width="16" height="16" />
+					</mx:HBox>
+				</mx:VBox>
+				<mx:Spacer width="50" />
+				<mx:VBox>
+					<mx:VBox>
+						<mx:Canvas id="canvas2" minHeight="0" minWidth="0" >
+							<mx:Image id="image2" complete="handleImage2Complete()" />
+						</mx:Canvas>
+						<mx:Label text="{getFileNameFromString(String(image2.source))}" />
+					</mx:VBox>
+					<mx:HBox>
+						<mx:Label text="x" />
+						<mx:NumericStepper id="img2x" width="60" maximum="4000" />
+						<mx:Label text="y" />
+						<mx:NumericStepper id="img2y" width="60" maximum="4000" />
+						<mx:Button label="get pixel" click="getPixel(image2, img2x.value, img2y.value, pixel2, bg2)" />
+						<mx:Label id="pixel2" />
+						<mx:UIComponent id="bg2" width="16" height="16" />
+					</mx:HBox>
+				</mx:VBox>
+			</mx:HBox>
+			<mx:HBox>
+				<mx:CheckBox id="cb" label="Pixel Reading" click="pixelTracking()" />
+				<mx:Label text="Zoom" />
+				<mx:NumericStepper id="zoom" minimum="1" change="zoomit()" />
+				<mx:Label text="background" />
+				<mx:ColorPicker id="bg" change="bgChanged();" />
+			</mx:HBox>
+			<mx:Canvas id="canvas4" minHeight="0" minWidth="0" />
+			<mx:Canvas id="canvas3" minHeight="0" minWidth="0" />
+			<mx:HBox>
+				<mx:Label text="x" />
+				<mx:NumericStepper id="img3x" width="60" maximum="4000" />
+				<mx:Label text="y" />
+				<mx:NumericStepper id="img3y" width="60" maximum="4000" />
+				<mx:Button label="get pixel" click="getPixel(results, img3x.value, img3y.value, pixel3, bg3)" />
+				<mx:Label id="pixel3" />
+				<mx:UIComponent id="bg3" width="16" height="16" />
+			</mx:HBox>
+		</mx:VBox>
+	</mx:Accordion>
+	
+</mx:WindowedApplication>

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR.mxml
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR.swf
URL: http://svn.apache.org/viewvc/incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR.swf?rev=1333232&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/flex/trunk/mustella/as3/src/mustella/ImageDiffAIR.swf
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream