////////////////////////////////////////////////////////////////////////////////
//
//  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 org.apache.flex.core
{
	import flash.system.ApplicationDomain;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;
	import flash.utils.getQualifiedSuperclassName;

	import org.apache.flex.events.EventDispatcher;
	import org.apache.flex.events.ValueChangeEvent;

    /**
     *  The SimpleCSSValuesImpl class implements a minimal set of
     *  CSS lookup rules that is sufficient for most applications.
     *  It does not support attribute selectors or descendant selectors
     *  or id selectors.  It will filter on a custom -flex-flash
     *  media query but not other media queries.  It can be
     *  replaced with other implementations that handle more complex
     *  selector lookups.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class SimpleCSSValuesImpl extends EventDispatcher implements IValuesImpl
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function SimpleCSSValuesImpl()
		{
			super();
		}

        private var mainClass:Object;

		private var conditionCombiners:Object;

        /**
         *  @copy org.apache.flex.core.IValuesImpl#init()
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function init(mainClass:Object):void
        {
			var styleClassName:String;
			var c:Class;
			if (!values)
			{
				values = {};
	            this.mainClass = mainClass;
	            var mainClassName:String = getQualifiedClassName(mainClass);
				styleClassName = "_" + mainClassName + "_Styles";
				c = ApplicationDomain.currentDomain.getDefinition(styleClassName) as Class;
			}
			else
			{
				c = mainClass.constructor as Class;
			}
            generateCSSStyleDeclarations(c["factoryFunctions"], c["data"]);
        }

        /**
         *  Process the encoded CSS data into data structures.  Usually not called
         *  directly by application developers.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function generateCSSStyleDeclarations(factoryFunctions:Object, arr:Array):void
        {
			if (factoryFunctions == null)
				return;
			if (arr == null)
				return;

            var declarationName:String = "";
            var segmentName:String = "";
            var n:int = arr.length;
            for (var i:int = 0; i < n; i++)
            {
                var className:int = arr[i];
                if (className == CSSClass.CSSSelector)
                {
                    var selectorName:String = arr[++i];
                    segmentName = selectorName + segmentName;
                    if (declarationName != "")
                        declarationName += " ";
                    declarationName += segmentName;
                    segmentName = "";
                }
                else if (className == CSSClass.CSSCondition)
                {
					if (!conditionCombiners)
					{
						conditionCombiners = {};
						conditionCombiners["class"] = ".";
						conditionCombiners["id"] = "#";
						conditionCombiners["pseudo"] = ':';
					}
					var conditionType:String = arr[++i];
					var conditionName:String = arr[++i];
					segmentName = segmentName + conditionCombiners[conditionType] + conditionName;
                }
                else if (className == CSSClass.CSSStyleDeclaration)
                {
                    var factoryName:int = arr[++i]; // defaultFactory or factory
                    var defaultFactory:Boolean = factoryName == CSSFactory.DefaultFactory;
                    /*
                    if (defaultFactory)
                    {
                        mergedStyle = styleManager.getMergedStyleDeclaration(declarationName);
                        style = new CSSStyleDeclaration(selector, styleManager, mergedStyle == null);
                    }
                    else
                    {
                        style = styleManager.getStyleDeclaration(declarationName);
                        if (!style)
                        {
                            style = new CSSStyleDeclaration(selector, styleManager, mergedStyle == null);
                            if (factoryName == CSSFactory.Override)
                                newSelectors.push(style);
                        }
                    }
                    */
                    var mq:String = null;
                    var o:Object;
                    if (i < n - 2)
                    {
                        // peek ahead to see if there is a media query
                        if (arr[i + 1] == CSSClass.CSSMediaQuery)
                        {
                            mq = arr[i + 2];
                            i += 2;
                            declarationName = mq + "_" + declarationName;
                        }
                    }
                    var finalName:String;
                    var valuesFunction:Function;
                    var valuesObject:Object;
                    if (defaultFactory)
                    {
                        valuesFunction = factoryFunctions[declarationName];
                        valuesObject = new valuesFunction();
                    }
                    else
                    {
                        valuesFunction = factoryFunctions[declarationName];
                        valuesObject = new valuesFunction();
                    }
                    if (isValidStaticMediaQuery(mq))
                    {
                        finalName = fixNames(declarationName, mq);
                        o = values[finalName];
                        if (o == null)
                            values[finalName] = valuesObject;
                        else
                        {
                            valuesFunction.prototype = o;
                            values[finalName] = new valuesFunction();
                        }
                    }
                    declarationName = "";
                }
            }

        }

        private function isValidStaticMediaQuery(mq:String):Boolean
        {
            if (mq == null)
                return true;

            if (mq == "-flex-flash")
                return true;

            // TODO: (aharui) other media query

            return false;
        }

        private function fixNames(s:String, mq:String):String
        {
            if (mq != null)
                s = s.substr(mq.length + 1); // 1 more for the hyphen

			if (s == "")
				return "*";

            var arr:Array = s.split(" ");
            var n:int = arr.length;
            for (var i:int = 0; i < n; i++)
            {
                var segmentName:String = arr[i];
				if (segmentName.charAt(0) == "#" || segmentName.charAt(0) == ".")
					continue;

                var c:int = segmentName.lastIndexOf(".");
                if (c > -1)	// it is 0 for class selectors
                {
                    segmentName = segmentName.substr(0, c) + "::" + segmentName.substr(c + 1);
                    arr[i] = segmentName;
                }
            }
            return arr.join(" ");
        }

        /**
         *  The map of values.  The format is not documented and it is not recommended
         *  to manipulate this structure directly.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public var values:Object;

        /**
         *  @copy org.apache.flex.core.IValuesImpl#getValue()
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function getValue(thisObject:Object, valueName:String, state:String = null, attrs:Object = null):*
		{
            var c:int = valueName.indexOf("-");
            while (c != -1)
            {
                valueName = valueName.substr(0, c) +
                    valueName.charAt(c + 1).toUpperCase() +
                    valueName.substr(c + 2);
                c = valueName.indexOf("-");
            }

            var value:*;
			var o:Object;
			var className:String;
			var selectorName:String;

			if ("className" in thisObject)
			{
				className = thisObject.className;
				if (state)
				{
					selectorName = className + ":" + state;
					o = values["." + selectorName];
					if (o)
					{
						value = o[valueName];
						if (value !== undefined)
							return value;
					}
				}

				o = values["." + className];
				if (o)
				{
					value = o[valueName];
					if (value !== undefined)
						return value;
				}
			}

			className = getQualifiedClassName(thisObject);
            var thisInstance:Object = thisObject;
			while (className != "Object")
			{
				if (state)
				{
					selectorName = className + ":" + state;
					o = values[selectorName];
					if (o)
					{
						value = o[valueName];
						if (value !== undefined)
							return value;
					}
				}

	            o = values[className];
	            if (o)
	            {
	                value = o[valueName];
	                if (value !== undefined)
	                    return value;
	            }
				className = getQualifiedSuperclassName(thisInstance);
				thisInstance = getDefinitionByName(className);
			}

            if (inheritingStyles[valueName] != null &&
                thisObject is IChild)
            {
                var parentObject:Object = IChild(thisObject).parent;
                if (parentObject)
                    return getValue(parentObject, valueName, state, attrs);
            }

            o = values["global"];
            if (o)
            {
    			value = o[valueName];
    			if (value !== undefined)
    				return value;
            }
			o = values["*"];
			if(o)
			{
				return o[valueName];
			}
			return null;
		}

        /**
         *  A method that stores a value to be shared with other objects.
         *  It is global, not per instance.  Fancier implementations
         *  may store shared values per-instance.
         *
         *  @param thisObject An object associated with this value.  Thiis
         *                parameter is ignored.
         *  @param valueName The name or key of the value being stored.
         *  @param The value to be stored.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function setValue(thisObject:Object, valueName:String, value:*):void
		{
            var c:int = valueName.indexOf("-");
            while (c != -1)
            {
                valueName = valueName.substr(0, c) +
                    valueName.charAt(c + 1).toUpperCase() +
                    valueName..substr(c + 2);
                c = valueName.indexOf("-");
            }
			var oldValue:Object = values[valueName];
			if (oldValue != value)
			{
				values[valueName] = value;
				dispatchEvent(new ValueChangeEvent(ValueChangeEvent.VALUE_CHANGE, false, false, oldValue, value));
			}
		}

		/**
		 *  @copy org.apache.flex.core.IValuesImpl#newInstance()
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function newInstance(thisObject:Object, valueName:String, state:String = null, attrs:Object = null):*
		{
			var c:Class = getValue(thisObject, valueName, state, attrs);
			if (c)
				return new c();
			return null;
		}

        /**
         *  @copy org.apache.flex.core.IValuesImpl#getInstance()
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function getInstance(valueName:String):Object
        {
            var o:Object = values["global"];
            if (o is Class)
            {
                o[valueName] = new o[valueName]();
                if (o[valueName] is IDocument)
                    o[valueName].setDocument(mainClass);
            }
            return o[valueName];
        }

        /**
         *  @copy org.apache.flex.core.IValuesImpl#convertColor()
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function convertColor(value:Object):uint
        {
            if (!(value is String))
                return uint(value);

            var stringValue:String = value as String;
            if (stringValue.charAt(0) == '#')
                return uint(stringValue.substr(1));
            return uint(stringValue);
        }

        /**
         *  A map of inheriting styles
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public static var inheritingStyles:Object = {
            "color" : 1,
            "fontFamily" : 1,
            "fontSize" : 1,
            "fontStyle" : 1
        }

	}
}

class CSSClass
{
    public static const CSSSelector:int = 0;
    public static const CSSCondition:int = 1;
    public static const CSSStyleDeclaration:int = 2;
    public static const CSSMediaQuery:int = 3;
}

class CSSFactory
{
    public static const DefaultFactory:int = 0;
    public static const Factory:int = 1;
    public static const Override:int = 2;
}

class CSSDataType
{
    public static const Native:int = 0;
    public static const Definition:int = 1;
}

////////////////////////////////////////////////////////////////////////////////
//
//  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 org.apache.flex.core
{
	import flash.system.ApplicationDomain;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;
	import flash.utils.getQualifiedSuperclassName;

	import org.apache.flex.events.EventDispatcher;
	import org.apache.flex.events.ValueChangeEvent;

    /**
     *  The SimpleCSSValuesImpl class implements a minimal set of
     *  CSS lookup rules that is sufficient for most applications.
     *  It does not support attribute selectors or descendant selectors
     *  or id selectors.  It will filter on a custom -flex-flash
     *  media query but not other media queries.  It can be
     *  replaced with other implementations that handle more complex
     *  selector lookups.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class SimpleCSSValuesImpl extends EventDispatcher implements IValuesImpl
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function SimpleCSSValuesImpl()
		{
			super();
		}

        private var mainClass:Object;

		private var conditionCombiners:Object;

        /**
         *  @copy org.apache.flex.core.IValuesImpl#init()
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function init(mainClass:Object):void
        {
			var styleClassName:String;
			var c:Class;
			if (!values)
			{
				values = {};
	            this.mainClass = mainClass;
	            var mainClassName:String = getQualifiedClassName(mainClass);
				styleClassName = "_" + mainClassName + "_Styles";
				c = ApplicationDomain.currentDomain.getDefinition(styleClassName) as Class;
			}
			else
			{
				c = mainClass.constructor as Class;
			}
            generateCSSStyleDeclarations(c["factoryFunctions"], c["data"]);
        }

        /**
         *  Process the encoded CSS data into data structures.  Usually not called
         *  directly by application developers.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function generateCSSStyleDeclarations(factoryFunctions:Object, arr:Array):void
        {
			if (factoryFunctions == null)
				return;
			if (arr == null)
				return;

            var declarationName:String = "";
            var segmentName:String = "";
            var n:int = arr.length;
            for (var i:int = 0; i < n; i++)
            {
                var className:int = arr[i];
                if (className == CSSClass.CSSSelector)
                {
                    var selectorName:String = arr[++i];
                    segmentName = selectorName + segmentName;
                    if (declarationName != "")
                        declarationName += " ";
                    declarationName += segmentName;
                    segmentName = "";
                }
                else if (className == CSSClass.CSSCondition)
                {
					if (!conditionCombiners)
					{
						conditionCombiners = {};
						conditionCombiners["class"] = ".";
						conditionCombiners["id"] = "#";
						conditionCombiners["pseudo"] = ':';
					}
					var conditionType:String = arr[++i];
					var conditionName:String = arr[++i];
					segmentName = segmentName + conditionCombiners[conditionType] + conditionName;
                }
                else if (className == CSSClass.CSSStyleDeclaration)
                {
                    var factoryName:int = arr[++i]; // defaultFactory or factory
                    var defaultFactory:Boolean = factoryName == CSSFactory.DefaultFactory;
                    /*
                    if (defaultFactory)
                    {
                        mergedStyle = styleManager.getMergedStyleDeclaration(declarationName);
                        style = new CSSStyleDeclaration(selector, styleManager, mergedStyle == null);
                    }
                    else
                    {
                        style = styleManager.getStyleDeclaration(declarationName);
                        if (!style)
                        {
                            style = new CSSStyleDeclaration(selector, styleManager, mergedStyle == null);
                            if (factoryName == CSSFactory.Override)
                                newSelectors.push(style);
                        }
                    }
                    */
                    var mq:String = null;
                    var o:Object;
                    if (i < n - 2)
                    {
                        // peek ahead to see if there is a media query
                        if (arr[i + 1] == CSSClass.CSSMediaQuery)
                        {
                            mq = arr[i + 2];
                            i += 2;
                            declarationName = mq + "_" + declarationName;
                        }
                    }
                    var finalName:String;
                    var valuesFunction:Function;
                    var valuesObject:Object;
                    if (defaultFactory)
                    {
                        valuesFunction = factoryFunctions[declarationName];
                        valuesObject = new valuesFunction();
                    }
                    else
                    {
                        valuesFunction = factoryFunctions[declarationName];
                        valuesObject = new valuesFunction();
                    }
                    if (isValidStaticMediaQuery(mq))
                    {
                        finalName = fixNames(declarationName, mq);
                        o = values[finalName];
                        if (o == null)
                            values[finalName] = valuesObject;
                        else
                        {
                            valuesFunction.prototype = o;
                            values[finalName] = new valuesFunction();
                        }
                    }
                    declarationName = "";
                }
            }

        }

        private function isValidStaticMediaQuery(mq:String):Boolean
        {
            if (mq == null)
                return true;

            if (mq == "-flex-flash")
                return true;

            // TODO: (aharui) other media query

            return false;
        }

        private function fixNames(s:String, mq:String):String
        {
            if (mq != null)
                s = s.substr(mq.length + 1); // 1 more for the hyphen

			if (s == "")
				return "*";

            var arr:Array = s.split(" ");
            var n:int = arr.length;
            for (var i:int = 0; i < n; i++)
            {
                var segmentName:String = arr[i];
				if (segmentName.charAt(0) == "#" || segmentName.charAt(0) == ".")
					continue;

                var c:int = segmentName.lastIndexOf(".");
                if (c > -1)	// it is 0 for class selectors
                {
                    segmentName = segmentName.substr(0, c) + "::" + segmentName.substr(c + 1);
                    arr[i] = segmentName;
                }
            }
            return arr.join(" ");
        }

        /**
         *  The map of values.  The format is not documented and it is not recommended
         *  to manipulate this structure directly.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public var values:Object;

        /**
         *  @copy org.apache.flex.core.IValuesImpl#getValue()
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function getValue(thisObject:Object, valueName:String, state:String = null, attrs:Object = null):*
		{
            var c:int = valueName.indexOf("-");
            while (c != -1)
            {
                valueName = valueName.substr(0, c) +
                    valueName.charAt(c + 1).toUpperCase() +
                    valueName.substr(c + 2);
                c = valueName.indexOf("-");
            }

            var value:*;
			var o:Object;
			var className:String;
			var selectorName:String;

			if (thisObject is IStyleableObject)
			{
                var styleable:IStyleableObject = IStyleableObject(thisObject);
                if (styleable.style != null)
                {
                    try {
                        value = styleable.style[valueName];
                    }
                    catch (e:Error) {
                        value = undefined;
                    }
                    if (value == "inherit")
                        return getInheritingValue(thisObject, valueName, state, attrs);
                    if (value !== undefined)
                        return value;
                }
				className = styleable.className;
				if (state)
				{
					selectorName = className + ":" + state;
					o = values["." + selectorName];
					if (o)
					{
						value = o[valueName];
                        if (value == "inherit")
                            return getInheritingValue(thisObject, valueName, state, attrs);
						if (value !== undefined)
							return value;
					}
				}

				o = values["." + className];
				if (o)
				{
					value = o[valueName];
                    if (value == "inherit")
                        return getInheritingValue(thisObject, valueName, state, attrs);
					if (value !== undefined)
						return value;
				}
			}

			className = getQualifiedClassName(thisObject);
            var thisInstance:Object = thisObject;
			while (className != "Object")
			{
				if (state)
				{
					selectorName = className + ":" + state;
					o = values[selectorName];
					if (o)
					{
						value = o[valueName];
                        if (value == "inherit")
                            return getInheritingValue(thisObject, valueName, state, attrs);
						if (value !== undefined)
							return value;
					}
				}

	            o = values[className];
	            if (o)
	            {
	                value = o[valueName];
                    if (value == "inherit")
                        return getInheritingValue(thisObject, valueName, state, attrs);
	                if (value !== undefined)
	                    return value;
	            }
				className = getQualifiedSuperclassName(thisInstance);
				thisInstance = getDefinitionByName(className);
			}

            if (inheritingStyles[valueName] != null &&
                thisObject is IChild)
            {
                var parentObject:Object = IChild(thisObject).parent;
                if (parentObject)
                    return getValue(parentObject, valueName, state, attrs);
            }

            o = values["global"];
            if (o)
            {
    			value = o[valueName];
    			if (value !== undefined)
    				return value;
            }
			o = values["*"];
			if(o)
			{
				return o[valueName];
			}
			return undefined;
		}

        private function getInheritingValue(thisObject:Object, valueName:String, state:String = null, attrs:Object = null):*
        {
            var value:*;
            if (thisObject is IChild)
            {
                var parentObject:Object = IChild(thisObject).parent;
                if (parentObject)
                {
                    value = getValue(parentObject, valueName, state, attrs);
                    if (value == "inherit" || value === undefined)
                        return getInheritingValue(parentObject, valueName, state, attrs);
                    if (value !== undefined)
                        return value;
                }
            }
            return "inherit";
        }

        /**
         *  A method that stores a value to be shared with other objects.
         *  It is global, not per instance.  Fancier implementations
         *  may store shared values per-instance.
         *
         *  @param thisObject An object associated with this value.  Thiis
         *                parameter is ignored.
         *  @param valueName The name or key of the value being stored.
         *  @param The value to be stored.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function setValue(thisObject:Object, valueName:String, value:*):void
		{
            var c:int = valueName.indexOf("-");
            while (c != -1)
            {
                valueName = valueName.substr(0, c) +
                    valueName.charAt(c + 1).toUpperCase() +
                    valueName..substr(c + 2);
                c = valueName.indexOf("-");
            }
			var oldValue:Object = values[valueName];
			if (oldValue != value)
			{
				values[valueName] = value;
				dispatchEvent(new ValueChangeEvent(ValueChangeEvent.VALUE_CHANGE, false, false, oldValue, value));
			}
		}

		/**
		 *  @copy org.apache.flex.core.IValuesImpl#newInstance()
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function newInstance(thisObject:Object, valueName:String, state:String = null, attrs:Object = null):*
		{
			var c:Class = getValue(thisObject, valueName, state, attrs);
			if (c)
				return new c();
			return null;
		}

        /**
         *  @copy org.apache.flex.core.IValuesImpl#getInstance()
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function getInstance(valueName:String):Object
        {
            var o:Object = values["global"];
            if (o is Class)
            {
                o[valueName] = new o[valueName]();
                if (o[valueName] is IDocument)
                    o[valueName].setDocument(mainClass);
            }
            return o[valueName];
        }

        /**
         *  @copy org.apache.flex.core.IValuesImpl#convertColor()
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function convertColor(value:Object):uint
        {
            if (!(value is String))
                return uint(value);

            var stringValue:String = value as String;
            if (stringValue.charAt(0) == '#')
                return uint(stringValue.substr(1));
            return uint(stringValue);
        }

        /**
         *  A map of inheriting styles
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public static var inheritingStyles:Object = {
            "color" : 1,
            "fontFamily" : 1,
            "fontSize" : 1,
            "fontStyle" : 1
        }

	}
}

class CSSClass
{
    public static const CSSSelector:int = 0;
    public static const CSSCondition:int = 1;
    public static const CSSStyleDeclaration:int = 2;
    public static const CSSMediaQuery:int = 3;
}

class CSSFactory
{
    public static const DefaultFactory:int = 0;
    public static const Factory:int = 1;
    public static const Override:int = 2;
}

class CSSDataType
{
    public static const Native:int = 0;
    public static const Definition:int = 1;
}

support inline styles

support inline styles


package com.mangui.HLS.streaming {

    import com.mangui.HLS.*;
    import com.mangui.HLS.muxing.*;
    import com.mangui.HLS.streaming.*;
    import com.mangui.HLS.parsing.*;
    import com.mangui.HLS.utils.*;

    import flash.media.*;
    import flash.net.*;
    import flash.utils.*;

    /** Class that keeps the buffer filled. **/
    public class Buffer {
        /** Reference to the framework controller. **/
        private var _hls:HLS;
        /** The buffer with video tags. **/
        private var _buffer:Vector.<Tag>;
        /** The fragment loader. **/
        private var _loader:FragmentLoader;
        /** Store that a fragment load is in progress. **/
        private var _loading:Boolean;
        /** means that last fragment of a VOD playlist has been loaded */
        private var _reached_vod_end:Boolean;
        /** Interval for checking buffer and position. **/
        private var _interval:Number;
        /** Next loading fragment sequence number. **/
        /** The start position of the stream. **/
        public var PlaybackStartPosition:Number = 0;
         /** start playback position in second, retrieved from first fragment **/
        private var _playback_start_position:Number;
        /** playback start PTS. **/
        private var _playback_start_pts:Number;
        /** playlist start PTS when playback started. **/
        private var _playlist_start_pts:Number;
        /** Current play position (relative position from beginning of sliding window) **/
        private var _playback_current_position:Number;
        /** buffer last PTS. **/
        private var _buffer_last_pts:Number;
         /** next buffer time. **/
        private var _buffer_next_time:Number;
         /** previous buffer time. **/
        private var _last_buffer:Number;
        /** Current playback state. **/
        private var _state:String;
        /** Netstream instance used for playing the stream. **/
        private var _stream:NetStream;
        /** The last tag that was appended to the buffer. **/
        private var _buffer_current_index:Number;
        /** soundtransform object. **/
        private var _transform:SoundTransform;

        private var _was_playing:Boolean = false;
        /** Create the buffer. **/
        public function Buffer(hls:HLS, loader:FragmentLoader, stream:NetStream):void {
            _hls = hls;
            _loader = loader;
            _stream = stream;
            _stream.inBufferSeek = true;
            _hls.addEventListener(HLSEvent.MANIFEST,_manifestHandler);
            _transform = new SoundTransform();
            _transform.volume = 0.9;
            _setState(HLSStates.IDLE);
        };

        /** Check the bufferlength. **/
        private function _checkBuffer():void {
          //Log.txt("checkBuffer");
            var buffer:Number = 0;
            // Calculate the buffer and position.
            if(_buffer.length) {
               buffer = (_buffer_last_pts - _playback_start_pts)/1000 - _stream.time;
               /** Current play time (time since beginning of playback) **/
               var playback_current_time:Number = (Math.round(_stream.time*100 + _playback_start_position*100)/100);
               var current_playlist_start_pts:Number = _loader.getPlayListStartPTS();
               var play_position:Number;
               if(current_playlist_start_pts ==Number.NEGATIVE_INFINITY) {
                  play_position = 0;
               } else {
                  play_position = playback_current_time -(current_playlist_start_pts-_playlist_start_pts)/1000;
                }
               if(play_position != _playback_current_position || buffer !=_last_buffer) {
                  if (play_position <0) {
                     play_position = 0;
                  }
                  _playback_current_position = play_position;
                  _last_buffer = buffer;
                  _hls.dispatchEvent(new HLSEvent(HLSEvent.MEDIA_TIME,{ position:_playback_current_position, buffer:buffer, duration:_loader.getPlayListDuration()}));
               }
            }

            // Load new tags from fragment.
            if(_reached_vod_end == false && buffer < _loader.getBufferLength() && ((!_loading) || _loader.needReload() == true)) {
               var loadstatus:Number = _loader.loadfragment(_buffer_next_time,_buffer_last_pts,buffer,_loaderCallback,(_buffer.length == 0));
               if (loadstatus == 0) {
                  // good, new fragment being loaded
                  _loading = true;
               } else  if (loadstatus < 0) {
                  /* it means PTS requested is smaller than playlist start PTS.
                     it could happen on live playlist :
                     - if bandwidth available is lower than lowest quality needed bandwidth
                     - after long pause
                     seek to offset 0 to force a restart of the playback session  */
                  Log.txt("long pause on live stream or bad network quality");
                  seek(0);
                  return;
               } else if(loadstatus > 0) {
                  _loading = false;
                  //seqnum not available in playlist
                  if (_hls.getType() == HLSTypes.VOD) {
                     // if VOD playlist, it means we reached the end, on live playlist do nothing and wait ...
                     _reached_vod_end = true;
                  }
               }
            }
            if((_state == HLSStates.PLAYING) ||
               (_state == HLSStates.BUFFERING && buffer > _loader.getSegmentAverageDuration()))
             {
                //Log.txt("appending data into NetStream");
                while(_buffer_current_index < _buffer.length && _stream.bufferLength < 2*_loader.getSegmentAverageDuration()) {
                    try {
                        _stream.appendBytes(_buffer[_buffer_current_index].data);
                    } catch (error:Error) {
                        _errorHandler(new Error(_buffer[_buffer_current_index].type+": "+ error.message));
                    }
                    // Last tag done? Then append sequence end.
                    if (_reached_vod_end ==true && _buffer_current_index == _buffer.length - 1) {
                        _stream.appendBytesAction(NetStreamAppendBytesAction.END_SEQUENCE);
                        _stream.appendBytes(new ByteArray());
                    }
                    _buffer_current_index++;
                }
            }
            // Set playback state and complete.
            if(_stream.bufferLength < 3) {
                if(_stream.bufferLength == 0 && _reached_vod_end ==true) {
                    clearInterval(_interval);
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.COMPLETE));
                    _setState(HLSStates.IDLE);
                } else if(_state == HLSStates.PLAYING) {
                    !_reached_vod_end && _setState(HLSStates.BUFFERING);
                }
            } else if (_state == HLSStates.BUFFERING) {

                if(_was_playing)
                _setState(HLSStates.PLAYING);
                else
                    pause();
            }
        };

        /** Dispatch an error to the controller. **/
        private function _errorHandler(error:Error):void {
            _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR,error.toString()));
        };

        /** Return the current playback state. **/
        public function getPosition():Number {
            return _playback_current_position;
        };

        /** Return the current playback state. **/
        public function getState():String {
            return _state;
        };

        /** Add a fragment to the buffer. **/
        private function _loaderCallback(tags:Vector.<Tag>,min_pts:Number,max_pts:Number, hasDiscontinuity:Boolean, start_offset:Number):void {
            // flush already injected Tags and restart index from 0
            _buffer = _buffer.slice(_buffer_current_index);
            _buffer_current_index = 0;
            var seek_pts:Number = min_pts + (PlaybackStartPosition-start_offset)*1000;
            if (_playback_start_pts == Number.NEGATIVE_INFINITY) {
               _playback_start_pts = seek_pts;
               _playlist_start_pts = _loader.getPlayListStartPTS();
               _playback_start_position = (_playback_start_pts-_playlist_start_pts)/1000;
            }
            _buffer_last_pts = max_pts;
            tags.sort(_sortTagsbyDTS);
            for each (var t:Tag in tags) {
                _filterTag(t,seek_pts) && _buffer.push(t);
            }
            _buffer_next_time=_playback_start_position+(_buffer_last_pts-_playback_start_pts)/1000;
            Log.txt("_loaderCallback,_buffer_next_time:"+ _buffer_next_time);
            _loading = false;
        };

        /** Start streaming on manifest load. **/
        private function _manifestHandler(event:HLSEvent):void {
            if(_state == HLSStates.IDLE) {
                _stream.close();
                _stream.play(null);
                _stream.soundTransform = _transform;
                _stream.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
                _stream.appendBytes(FLV.getHeader());
                seek(PlaybackStartPosition);
            }
        };

        /** Pause playback. **/
        public function pause():void {
            if(_state == HLSStates.PLAYING || _state == HLSStates.BUFFERING) {
                clearInterval(_interval);
                _setState(HLSStates.PAUSED);
                _stream.pause();
            }
        };

        /** Resume playback. **/
        public function resume():void {
            if(_state == HLSStates.PAUSED) {
                clearInterval(_interval);
                _setState(HLSStates.PLAYING);
                _stream.resume();
                _interval = setInterval(_checkBuffer,100);
            }
        };

        /** Change playback state. **/
        private function _setState(state:String):void {
            if(state != _state) {
                _state = state;
                _hls.dispatchEvent(new HLSEvent(HLSEvent.STATE,_state));
            }
        };

        /** Sort the buffer by tag. **/
        private function _sortTagsbyDTS(x:Tag,y:Tag):Number {
            if(x.dts < y.dts) {
                return -1;
            } else if (x.dts > y.dts) {
                return 1;
            } else {
                if(x.type == Tag.AVC_HEADER || x.type == Tag.AAC_HEADER) {
                    return -1;
                } else if (y.type == Tag.AVC_HEADER || y.type == Tag.AAC_HEADER) {
                    return 1;
                } else {
                    if(x.type == Tag.AVC_NALU) {
                        return -1;
                    } else if (y.type == Tag.AVC_NALU) {
                        return 1;
                    } else {
                        return 0;
                    }
                }
            }
        };

        /**
         *
         * Filter tag by type and pts for accurate seeking.
         *
         * @param tag
         * @param pts Destination pts
         * @return
         */
        private function _filterTag(tag:Tag,pts:Number = 0):Boolean{
            if(tag.type == Tag.AAC_HEADER || tag.type == Tag.AVC_HEADER || tag.type == Tag.AVC_NALU){
              if(tag.pts < pts)
               tag.pts = tag.dts = pts;
              return true;
            }
            return tag.pts >= pts;
        }

        /** Start playing data in the buffer. **/
        public function seek(position:Number):void {
               _buffer = new Vector.<Tag>();
               _loader.clearLoader();
               _loading = false;
               _buffer_current_index = 0;
                PlaybackStartPosition = position;
               _stream.seek(0);
               _stream.appendBytesAction(NetStreamAppendBytesAction.RESET_SEEK);
               _reached_vod_end = false;
               _playback_start_pts = Number.NEGATIVE_INFINITY;
               _buffer_next_time = position;
               _buffer_last_pts = 0;
               _last_buffer = 0;
               _was_playing = (_state == HLSStates.PLAYING) || (_state == HLSStates.IDLE);
               _setState(HLSStates.BUFFERING);
               clearInterval(_interval);
               _interval = setInterval(_checkBuffer,100);
        };

        /** Stop playback. **/
        public function stop():void {
            if(_stream) {
                _stream.pause();
            }
            _loading = false;
            clearInterval(_interval);
            _setState(HLSStates.IDLE);
        };

        /** Change the volume (set in the NetStream). **/
        public function volume(percent:Number):void {
            _transform.volume = percent/100;
            if(_stream) {
                _stream.soundTransform = _transform;
            }
        };

    }

}

package com.mangui.HLS.streaming {

    import com.mangui.HLS.*;
    import com.mangui.HLS.muxing.*;
    import com.mangui.HLS.streaming.*;
    import com.mangui.HLS.parsing.*;
    import com.mangui.HLS.utils.*;

    import flash.media.*;
    import flash.net.*;
    import flash.utils.*;

    /** Class that keeps the buffer filled. **/
    public class Buffer {
        /** Reference to the framework controller. **/
        private var _hls:HLS;
        /** The buffer with video tags. **/
        private var _buffer:Vector.<Tag>;
        /** The fragment loader. **/
        private var _loader:FragmentLoader;
        /** Store that a fragment load is in progress. **/
        private var _loading:Boolean;
        /** means that last fragment of a VOD playlist has been loaded */
        private var _reached_vod_end:Boolean;
        /** Interval for checking buffer and position. **/
        private var _interval:Number;
        /** Next loading fragment sequence number. **/
        /** The start position of the stream. **/
        public var PlaybackStartPosition:Number = 0;
         /** start playback position in second, retrieved from first fragment **/
        private var _playback_start_position:Number;
        /** playback start PTS. **/
        private var _playback_start_pts:Number;
        /** playlist start PTS when playback started. **/
        private var _playlist_start_pts:Number;
        /** Current play position (relative position from beginning of sliding window) **/
        private var _playback_current_position:Number;
        /** buffer last PTS. **/
        private var _buffer_last_pts:Number;
         /** next buffer time. **/
        private var _buffer_next_time:Number;
         /** previous buffer time. **/
        private var _last_buffer:Number;
        /** Current playback state. **/
        private var _state:String;
        /** Netstream instance used for playing the stream. **/
        private var _stream:NetStream;
        /** The last tag that was appended to the buffer. **/
        private var _buffer_current_index:Number;
        /** soundtransform object. **/
        private var _transform:SoundTransform;

        private var _was_playing:Boolean = false;
        /** Create the buffer. **/
        public function Buffer(hls:HLS, loader:FragmentLoader, stream:NetStream):void {
            _hls = hls;
            _loader = loader;
            _stream = stream;
            _stream.inBufferSeek = true;
            _hls.addEventListener(HLSEvent.MANIFEST,_manifestHandler);
            _transform = new SoundTransform();
            _transform.volume = 0.9;
            _setState(HLSStates.IDLE);
        };

        /** Check the bufferlength. **/
        private function _checkBuffer():void {
          //Log.txt("checkBuffer");
            var buffer:Number = 0;
            // Calculate the buffer and position.
            if(_buffer.length) {
               buffer = (_buffer_last_pts - _playback_start_pts)/1000 - _stream.time;
               /** Current play time (time since beginning of playback) **/
               var playback_current_time:Number = (Math.round(_stream.time*100 + _playback_start_position*100)/100);
               var current_playlist_start_pts:Number = _loader.getPlayListStartPTS();
               var play_position:Number;
               if(current_playlist_start_pts ==Number.NEGATIVE_INFINITY) {
                  play_position = 0;
               } else {
                  play_position = playback_current_time -(current_playlist_start_pts-_playlist_start_pts)/1000;
                }
               if(play_position != _playback_current_position || buffer !=_last_buffer) {
                  if (play_position <0) {
                     play_position = 0;
                  }
                  _playback_current_position = play_position;
                  _last_buffer = buffer;
                  _hls.dispatchEvent(new HLSEvent(HLSEvent.MEDIA_TIME,{ position:_playback_current_position, buffer:buffer, duration:_loader.getPlayListDuration()}));
               }
            }

            // Load new tags from fragment.
            if(_reached_vod_end == false && buffer < _loader.getBufferLength() && ((!_loading) || _loader.needReload() == true)) {
               var loadstatus:Number = _loader.loadfragment(_buffer_next_time,_buffer_last_pts,buffer,_loaderCallback,(_buffer.length == 0));
               if (loadstatus == 0) {
                  // good, new fragment being loaded
                  _loading = true;
               } else  if (loadstatus < 0) {
                  /* it means PTS requested is smaller than playlist start PTS.
                     it could happen on live playlist :
                     - if bandwidth available is lower than lowest quality needed bandwidth
                     - after long pause
                     seek to offset 0 to force a restart of the playback session  */
                  Log.txt("long pause on live stream or bad network quality");
                  seek(0);
                  return;
               } else if(loadstatus > 0) {
                  _loading = false;
                  //seqnum not available in playlist
                  if (_hls.getType() == HLSTypes.VOD) {
                     // if VOD playlist, it means we reached the end, on live playlist do nothing and wait ...
                     _reached_vod_end = true;
                  }
               }
            }
            if((_state == HLSStates.PLAYING) ||
               (_state == HLSStates.BUFFERING && (buffer > _loader.getSegmentAverageDuration() || _reached_vod_end)))
             {
                //Log.txt("appending data into NetStream");
                while(_buffer_current_index < _buffer.length && _stream.bufferLength < 2*_loader.getSegmentAverageDuration()) {
                    try {
                        _stream.appendBytes(_buffer[_buffer_current_index].data);
                    } catch (error:Error) {
                        _errorHandler(new Error(_buffer[_buffer_current_index].type+": "+ error.message));
                    }
                    // Last tag done? Then append sequence end.
                    if (_reached_vod_end ==true && _buffer_current_index == _buffer.length - 1) {
                        _stream.appendBytesAction(NetStreamAppendBytesAction.END_SEQUENCE);
                        _stream.appendBytes(new ByteArray());
                    }
                    _buffer_current_index++;
                }
            }
            // Set playback state and complete.
            if(_stream.bufferLength < 3) {
                if(_stream.bufferLength == 0 && _reached_vod_end ==true) {
                    clearInterval(_interval);
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.COMPLETE));
                    _setState(HLSStates.IDLE);
                } else if(_state == HLSStates.PLAYING) {
                    !_reached_vod_end && _setState(HLSStates.BUFFERING);
                }
            } else if (_state == HLSStates.BUFFERING) {

                if(_was_playing)
                _setState(HLSStates.PLAYING);
                else
                    pause();
            }
        };

        /** Dispatch an error to the controller. **/
        private function _errorHandler(error:Error):void {
            _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR,error.toString()));
        };

        /** Return the current playback state. **/
        public function getPosition():Number {
            return _playback_current_position;
        };

        /** Return the current playback state. **/
        public function getState():String {
            return _state;
        };

        /** Add a fragment to the buffer. **/
        private function _loaderCallback(tags:Vector.<Tag>,min_pts:Number,max_pts:Number, hasDiscontinuity:Boolean, start_offset:Number):void {
            // flush already injected Tags and restart index from 0
            _buffer = _buffer.slice(_buffer_current_index);
            _buffer_current_index = 0;
            var seek_pts:Number = min_pts + (PlaybackStartPosition-start_offset)*1000;
            if (_playback_start_pts == Number.NEGATIVE_INFINITY) {
               _playback_start_pts = seek_pts;
               _playlist_start_pts = _loader.getPlayListStartPTS();
               _playback_start_position = (_playback_start_pts-_playlist_start_pts)/1000;
            }
            _buffer_last_pts = max_pts;
            tags.sort(_sortTagsbyDTS);
            for each (var t:Tag in tags) {
                _filterTag(t,seek_pts) && _buffer.push(t);
            }
            _buffer_next_time=_playback_start_position+(_buffer_last_pts-_playback_start_pts)/1000;
            Log.txt("_loaderCallback,_buffer_next_time:"+ _buffer_next_time);
            _loading = false;
        };

        /** Start streaming on manifest load. **/
        private function _manifestHandler(event:HLSEvent):void {
            if(_state == HLSStates.IDLE) {
                _stream.close();
                _stream.play(null);
                _stream.soundTransform = _transform;
                _stream.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
                _stream.appendBytes(FLV.getHeader());
                seek(PlaybackStartPosition);
            }
        };

        /** Pause playback. **/
        public function pause():void {
            if(_state == HLSStates.PLAYING || _state == HLSStates.BUFFERING) {
                clearInterval(_interval);
                _stream.pause();
                _setState(HLSStates.PAUSED);
            }
        };

        /** Resume playback. **/
        public function resume():void {
            if(_state == HLSStates.PAUSED) {
                clearInterval(_interval);
                _stream.resume();
                _interval = setInterval(_checkBuffer,100);
                _setState(HLSStates.PLAYING);
            }
        };

        /** Change playback state. **/
        private function _setState(state:String):void {
            if(state != _state) {
                _state = state;
                _hls.dispatchEvent(new HLSEvent(HLSEvent.STATE,_state));
            }
        };

        /** Sort the buffer by tag. **/
        private function _sortTagsbyDTS(x:Tag,y:Tag):Number {
            if(x.dts < y.dts) {
                return -1;
            } else if (x.dts > y.dts) {
                return 1;
            } else {
                if(x.type == Tag.AVC_HEADER || x.type == Tag.AAC_HEADER) {
                    return -1;
                } else if (y.type == Tag.AVC_HEADER || y.type == Tag.AAC_HEADER) {
                    return 1;
                } else {
                    if(x.type == Tag.AVC_NALU) {
                        return -1;
                    } else if (y.type == Tag.AVC_NALU) {
                        return 1;
                    } else {
                        return 0;
                    }
                }
            }
        };

        /**
         *
         * Filter tag by type and pts for accurate seeking.
         *
         * @param tag
         * @param pts Destination pts
         * @return
         */
        private function _filterTag(tag:Tag,pts:Number = 0):Boolean{
            if(tag.type == Tag.AAC_HEADER || tag.type == Tag.AVC_HEADER || tag.type == Tag.AVC_NALU){
              if(tag.pts < pts)
               tag.pts = tag.dts = pts;
              return true;
            }
            return tag.pts >= pts;
        }

        /** Start playing data in the buffer. **/
        public function seek(position:Number):void {
               _buffer = new Vector.<Tag>();
               _loader.clearLoader();
               _loading = false;
               _buffer_current_index = 0;
                PlaybackStartPosition = position;
               _stream.seek(0);
               _stream.appendBytesAction(NetStreamAppendBytesAction.RESET_SEEK);
               _reached_vod_end = false;
               _playback_start_pts = Number.NEGATIVE_INFINITY;
               _buffer_next_time = position;
               _buffer_last_pts = 0;
               _last_buffer = 0;
               _was_playing = (_state == HLSStates.PLAYING) || (_state == HLSStates.IDLE);
               _setState(HLSStates.BUFFERING);
               clearInterval(_interval);
               _interval = setInterval(_checkBuffer,100);
        };

        /** Stop playback. **/
        public function stop():void {
            if(_stream) {
                _stream.pause();
            }
            _loading = false;
            clearInterval(_interval);
            _setState(HLSStates.IDLE);
        };

        /** Change the volume (set in the NetStream). **/
        public function volume(percent:Number):void {
            _transform.volume = percent/100;
            if(_stream) {
                _stream.soundTransform = _transform;
            }
        };

    }

}

put state update after stream action in resume/pause, also deal with last fragment duration that can be smaller than average duration.

State update event must occur after real state update (otherwise, e.g. if I want prevent hls from pausing I can't do that: I receive event, call resume, but than _stream.pause() is executed.


////////////////////////////////////////////////////////////////////////////////
//
//  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 org.apache.flex.html.beads
{
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.SimpleButton;

    import org.apache.flex.core.BeadViewBase;
	import org.apache.flex.core.IStrand;
    import org.apache.flex.core.IBeadView;
    import org.apache.flex.events.Event;

    /**
     *  The DownArrowButtonView class is the view for
     *  the down arrow button in a ScrollBar and other controls.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class DownArrowButtonView extends BeadViewBase implements IBeadView
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function DownArrowButtonView()
		{
			upView = new Shape();
			downView = new Shape();
			overView = new Shape();

			drawView(upView.graphics, 0xf8f8f8);
			drawView(downView.graphics, 0xd8d8d8);
			drawView(overView.graphics, 0xe8e8e8);
		}

		private function drawView(g:Graphics, bgColor:uint):void
		{
			g.lineStyle(1, 0x808080);
			g.beginFill(bgColor);
			g.drawRoundRect(0, 0, ScrollBarView.FullSize, ScrollBarView.FullSize, ScrollBarView.ThirdSize);
			g.endFill();
			g.lineStyle(0);
			g.beginFill(0);
			g.moveTo(ScrollBarView.QuarterSize, ScrollBarView.QuarterSize);
			g.lineTo(ScrollBarView.ThreeQuarterSize, ScrollBarView.QuarterSize);
			g.lineTo(ScrollBarView.HalfSize, ScrollBarView.ThreeQuarterSize);
			g.lineTo(ScrollBarView.QuarterSize, ScrollBarView.QuarterSize);
			g.endFill();
		}

		private var shape:Shape;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		override public function set strand(value:IStrand):void
		{
			super.strand = value;
			shape = new Shape();
			shape.graphics.beginFill(0xCCCCCC);
			shape.graphics.drawRect(0, 0, ScrollBarView.FullSize, ScrollBarView.FullSize);
			shape.graphics.endFill();
			SimpleButton(value).upState = upView;
			SimpleButton(value).downState = downView;
			SimpleButton(value).overState = overView;
			SimpleButton(value).hitTestState = shape;

            SimpleButton(_strand).addEventListener("widthChanged",sizeChangeHandler);
            SimpleButton(_strand).addEventListener("heightChanged",sizeChangeHandler);
        }

		private var upView:Shape;
		private var downView:Shape;
		private var overView:Shape;

        private function sizeChangeHandler(event:Event):void
        {
            SimpleButton(_strand).scaleX = SimpleButton(_strand).width / ScrollBarView.FullSize;
            SimpleButton(_strand).scaleY = SimpleButton(_strand).height / ScrollBarView.FullSize;
        }
	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  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 org.apache.flex.html.beads
{
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.SimpleButton;

    import org.apache.flex.core.BeadViewBase;
	import org.apache.flex.core.IStrand;
    import org.apache.flex.core.IBeadView;
    import org.apache.flex.events.Event;

    /**
     *  The DownArrowButtonView class is the view for
     *  the down arrow button in a ScrollBar and other controls.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class DownArrowButtonView extends BeadViewBase implements IBeadView
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function DownArrowButtonView()
		{
			upView = new Shape();
			downView = new Shape();
			overView = new Shape();

			drawView(upView.graphics, 0xf8f8f8);
			drawView(downView.graphics, 0xd8d8d8);
			drawView(overView.graphics, 0xe8e8e8);
		}

		private function drawView(g:Graphics, bgColor:uint):void
		{
			g.lineStyle(1);
			g.beginFill(bgColor);
			g.drawRoundRect(0, 0, ScrollBarView.FullSize, ScrollBarView.FullSize, ScrollBarView.ThirdSize);
			g.endFill();
			g.lineStyle(0);
			g.beginFill(0);
			g.moveTo(ScrollBarView.QuarterSize, ScrollBarView.QuarterSize);
			g.lineTo(ScrollBarView.ThreeQuarterSize, ScrollBarView.QuarterSize);
			g.lineTo(ScrollBarView.HalfSize, ScrollBarView.ThreeQuarterSize);
			g.lineTo(ScrollBarView.QuarterSize, ScrollBarView.QuarterSize);
			g.endFill();
		}

		private var shape:Shape;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		override public function set strand(value:IStrand):void
		{
			super.strand = value;
			shape = new Shape();
			shape.graphics.beginFill(0xCCCCCC);
			shape.graphics.drawRect(0, 0, ScrollBarView.FullSize, ScrollBarView.FullSize);
			shape.graphics.endFill();
			SimpleButton(value).upState = upView;
			SimpleButton(value).downState = downView;
			SimpleButton(value).overState = overView;
			SimpleButton(value).hitTestState = shape;

            SimpleButton(_strand).addEventListener("widthChanged",sizeChangeHandler);
            SimpleButton(_strand).addEventListener("heightChanged",sizeChangeHandler);
        }

		private var upView:Shape;
		private var downView:Shape;
		private var overView:Shape;

        private function sizeChangeHandler(event:Event):void
        {
            SimpleButton(_strand).scaleX = SimpleButton(_strand).width / ScrollBarView.FullSize;
            SimpleButton(_strand).scaleY = SimpleButton(_strand).height / ScrollBarView.FullSize;
        }
	}
}

use black like up button

use black like up button


package aerys.minko.type.clone
{
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.controller.AnimationController;
	import aerys.minko.scene.controller.TransformController;
	import aerys.minko.scene.controller.camera.CameraController;
	import aerys.minko.scene.controller.light.DirectionalLightController;
	import aerys.minko.scene.controller.light.LightController;
	import aerys.minko.scene.controller.light.PointLightController;
	import aerys.minko.scene.controller.light.SpotLightController;
	import aerys.minko.scene.controller.mesh.MeshController;
	import aerys.minko.scene.controller.mesh.MeshVisibilityController;
	import aerys.minko.scene.controller.mesh.skinning.SkinningController;

	public final class CloneOptions
	{
		private var _clonedControllerTypes		: Vector.<Class>;
		private var _ignoredControllerTypes		: Vector.<Class>;
		private var _reassignedControllerTypes	: Vector.<Class>;
		private var _defaultControllerAction	: uint;

		public function get defaultControllerAction() : uint
		{
			return _defaultControllerAction;
		}
		public function set defaultControllerAction(v : uint) : void
		{
			if (v != ControllerCloneAction.CLONE
				&& v != ControllerCloneAction.IGNORE
				&& v != ControllerCloneAction.REASSIGN)
				throw new Error('Unknown action type.');

			_defaultControllerAction = v;
		}

		public function CloneOptions()
		{
			initialize();
		}

		private function initialize() : void
		{
			_clonedControllerTypes = new <Class>[];
			_ignoredControllerTypes = new <Class>[];
			_reassignedControllerTypes = new <Class>[];
			_defaultControllerAction = ControllerCloneAction.REASSIGN;
		}

		public static function get defaultOptions() : CloneOptions
		{
			var cloneOptions : CloneOptions = new CloneOptions();

			cloneOptions._clonedControllerTypes.push(
				AnimationController,
				SkinningController
			);
			cloneOptions._ignoredControllerTypes.push(
				MeshController,
				MeshVisibilityController,
				CameraController,
				TransformController,
                LightController,
                DirectionalLightController,
                SpotLightController,
                PointLightController,
			);
			cloneOptions._defaultControllerAction = ControllerCloneAction.REASSIGN;

			return cloneOptions;
		}

		public static function get cloneAllOptions() : CloneOptions
		{
			var cloneOptions : CloneOptions = new CloneOptions();

			cloneOptions._ignoredControllerTypes.push(
				MeshController,
				MeshVisibilityController,
				CameraController,
				TransformController,
                LightController,
                DirectionalLightController,
                SpotLightController,
                PointLightController
			);
			cloneOptions._defaultControllerAction = ControllerCloneAction.CLONE;

			return cloneOptions;
		}

		public function addControllerAction(controllerClass : Class, action : uint) : CloneOptions
		{
			switch (action)
			{
				case ControllerCloneAction.CLONE:
					_clonedControllerTypes.push(controllerClass);
					break;

				case ControllerCloneAction.IGNORE:
					_ignoredControllerTypes.push(controllerClass);
					break;

				case ControllerCloneAction.REASSIGN:
					_reassignedControllerTypes.push(controllerClass);
					break;

				default:
					throw new Error('Unknown action type.');
			}

            return this;
		}

		public function removeControllerAction(controllerClass : Class) : void
		{
			throw new Error('Implement me.');
		}

		public function getActionForController(controller : AbstractController) : uint
		{
			var numControllersToClone		: uint = _clonedControllerTypes.length;
			var numControllersToIgnore		: uint = _ignoredControllerTypes.length;
			var numControllersToReassign	: uint = _reassignedControllerTypes.length;
			var controllerId				: uint;

			for (controllerId = 0; controllerId < numControllersToClone; ++controllerId)
				if (controller is _clonedControllerTypes[controllerId])
					return ControllerCloneAction.CLONE;

			for (controllerId = 0; controllerId < numControllersToIgnore; ++controllerId)
				if (controller is _ignoredControllerTypes[controllerId])
					return ControllerCloneAction.IGNORE;

			for (controllerId = 0; controllerId < numControllersToReassign; ++controllerId)
				if (controller is _reassignedControllerTypes[controllerId])
					return ControllerCloneAction.REASSIGN;

			return _defaultControllerAction;
		}

	}
}

package aerys.minko.type.clone
{
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.controller.AnimationController;
	import aerys.minko.scene.controller.TransformController;
	import aerys.minko.scene.controller.camera.CameraController;
	import aerys.minko.scene.controller.light.DirectionalLightController;
	import aerys.minko.scene.controller.light.LightController;
	import aerys.minko.scene.controller.light.PointLightController;
	import aerys.minko.scene.controller.light.SpotLightController;
	import aerys.minko.scene.controller.mesh.MeshController;
	import aerys.minko.scene.controller.mesh.MeshVisibilityController;
	import aerys.minko.scene.controller.mesh.skinning.SkinningController;

	public final class CloneOptions
	{
		private var _clonedControllerTypes		: Vector.<Class>;
		private var _ignoredControllerTypes		: Vector.<Class>;
		private var _reassignedControllerTypes	: Vector.<Class>;
		private var _defaultControllerAction	: uint;

		public function get defaultControllerAction() : uint
		{
			return _defaultControllerAction;
		}
		public function set defaultControllerAction(v : uint) : void
		{
			if (v != ControllerCloneAction.CLONE
				&& v != ControllerCloneAction.IGNORE
				&& v != ControllerCloneAction.REASSIGN)
				throw new Error('Unknown action type.');

			_defaultControllerAction = v;
		}

		public function CloneOptions()
		{
			initialize();
		}

		private function initialize() : void
		{
			_clonedControllerTypes = new <Class>[];
			_ignoredControllerTypes = new <Class>[];
			_reassignedControllerTypes = new <Class>[];
			_defaultControllerAction = ControllerCloneAction.REASSIGN;
		}

		public static function get defaultOptions() : CloneOptions
		{
			var cloneOptions : CloneOptions = new CloneOptions();

			cloneOptions._clonedControllerTypes.push(
				AnimationController,
				SkinningController
			);
			cloneOptions._ignoredControllerTypes.push(
				MeshController,
				MeshVisibilityController,
				CameraController,
				TransformController,
                LightController,
                DirectionalLightController,
                SpotLightController,
                PointLightController
			);
			cloneOptions._defaultControllerAction = ControllerCloneAction.REASSIGN;

			return cloneOptions;
		}

		public static function get cloneAllOptions() : CloneOptions
		{
			var cloneOptions : CloneOptions = new CloneOptions();

			cloneOptions._ignoredControllerTypes.push(
				MeshController,
				MeshVisibilityController,
				CameraController,
				TransformController,
                LightController,
                DirectionalLightController,
                SpotLightController,
                PointLightController
			);
			cloneOptions._defaultControllerAction = ControllerCloneAction.CLONE;

			return cloneOptions;
		}

		public function addControllerAction(controllerClass : Class, action : uint) : CloneOptions
		{
			switch (action)
			{
				case ControllerCloneAction.CLONE:
					_clonedControllerTypes.push(controllerClass);
					break;

				case ControllerCloneAction.IGNORE:
					_ignoredControllerTypes.push(controllerClass);
					break;

				case ControllerCloneAction.REASSIGN:
					_reassignedControllerTypes.push(controllerClass);
					break;

				default:
					throw new Error('Unknown action type.');
			}

            return this;
		}

		public function removeControllerAction(controllerClass : Class) : void
		{
			throw new Error('Implement me.');
		}

		public function getActionForController(controller : AbstractController) : uint
		{
			var numControllersToClone		: uint = _clonedControllerTypes.length;
			var numControllersToIgnore		: uint = _ignoredControllerTypes.length;
			var numControllersToReassign	: uint = _reassignedControllerTypes.length;
			var controllerId				: uint;

			for (controllerId = 0; controllerId < numControllersToClone; ++controllerId)
				if (controller is _clonedControllerTypes[controllerId])
					return ControllerCloneAction.CLONE;

			for (controllerId = 0; controllerId < numControllersToIgnore; ++controllerId)
				if (controller is _ignoredControllerTypes[controllerId])
					return ControllerCloneAction.IGNORE;

			for (controllerId = 0; controllerId < numControllersToReassign; ++controllerId)
				if (controller is _reassignedControllerTypes[controllerId])
					return ControllerCloneAction.REASSIGN;

			return _defaultControllerAction;
		}

	}
}

fix typo

fix typo


package aerys.minko.type.math
{
	public class HLSAMatrix4x4 extends Matrix4x4
	{
		private static const TMP_NUMBERS	: Vector.<Number> 	= new Vector.<Number>(16, true);
		private static const TMP_MATRIX		: Matrix4x4			= new Matrix4x4();
		private static const TMP_VECTOR4	: Vector4			= new Vector4();
		private static const RWGT			: Number 			= 0.3086;
		private static const GWGT			: Number 			= 0.6094;
		private static const BWGT			: Number 			= 0.0820;
		private static const SQRT2			: Number			= Math.sqrt(2.0);
		private static const SQRT3			: Number			= Math.sqrt(3.0);
		private static const INV_SQRT2		: Number			= 1.0 / SQRT2;

		private var _hue				: Number		= 0;
		private var _saturation			: Number		= 1;
		private var _luminance			: Number		= 1;
		private var _alpha				: Number		= 1;
		private var _hueMatrix			: Matrix4x4		= new Matrix4x4();
		private var _saturationMatrix	: Matrix4x4		= new Matrix4x4();
		private var _luminanceMatrix	: Matrix4x4		= new Matrix4x4();
		private var _luminancePlane 	: Vector4		= new Vector4(RWGT, GWGT, BWGT, 0);

		/**
		 * Alpha multiplier, from 0 to 1, default is 1.
		 */
		public function get alpha() : Number
		{
			return _alpha
		}
		public function set alpha(value : Number) : void
		{
			_alpha = value;

			updateDiffuseColorMatrix();
		}

		/**
		 * Hue correction, from -2PI to 2PI, default is 0.
		 */
		public function get hue() : Number
		{
			return _hue;
		}
		public function set hue(value : Number) : void
		{
			_hue = value;

			// First we make an identity matrix
			_hueMatrix.identity();

			// Rotate the grey vector into positive Z
			TMP_MATRIX.initialize(
				1,	0, 			0, 			0,
				0, 	INV_SQRT2, 	INV_SQRT2, 	0,
				0, 	-INV_SQRT2, INV_SQRT2, 	0,
				0, 	0, 			0, 			1
			);
			_hueMatrix.append(TMP_MATRIX);

			var mag : Number	= SQRT3;
			var yrs	: Number 	= -1.0 / mag;
			var yrc	: Number 	= SQRT2 / mag;

			TMP_MATRIX.initialize(
				yrc,	0,	-yrs,	0,
				0,		1,	0,		0,
				yrs,	0,	yrc,	0,
				0,		0,	0,		1
			);
			_hueMatrix.append(TMP_MATRIX);

			// Shear the space to make the luminance plane horizontal
			var l	: Vector4 	= _hueMatrix.transformVector(_luminancePlane, TMP_VECTOR4);
			var zsx	: Number 	= l.x / l.z;
			var zsy	: Number 	= l.y / l.z;

			TMP_MATRIX.initialize(
				1,	0,	zsx,	0,
				0,	1,	zsy,	0,
				0,	0,	1,		0,
				0,	0,	0,		1
			);
			_hueMatrix.append(TMP_MATRIX);

			// Rotate the hue
			var zrs	: Number = Math.sin(value);
			var zrc	: Number = Math.cos(value);

			TMP_MATRIX.initialize(
				zrc,	zrs,	0,	0,
				-zrs,	zrc,	0,	0,
				0,		0,		1,	0,
				0,		0,		0,	1
			);
			_hueMatrix.append(TMP_MATRIX);

			// Unshear the space to put the luminance plane back
			TMP_MATRIX.initialize(
				1,	0,	-zsx,	0,
				0,	1,	-zsy,	0,
				0,	0,	1,		0,
				0,	0,	0,		1
			);
			_hueMatrix.append(TMP_MATRIX);

			// Rotate the grey vector back into place
			TMP_MATRIX.initialize(
				yrc,	0,	yrs,	0,
				0,		1,	0,		0,
				-yrs,	0,	yrc,	0,
				0,		0,	0,		1
			);
			_hueMatrix.append(TMP_MATRIX);
			TMP_MATRIX.initialize(
				1,	0,			0,			0,
				0,	INV_SQRT2,	-INV_SQRT2,	0,
				0,	INV_SQRT2,	INV_SQRT2,	0,
				0,	0,			0,			1
			);
			_hueMatrix.append(TMP_MATRIX);

			updateDiffuseColorMatrix();
		}

		/**
		 * Saturation multiplier, default is 1.
		 */
		public function get saturation() : Number
		{
			return _saturation;
		}
		public function set saturation(s : Number):void
		{
			_saturation = s;

			var s1	: Number = 1.0 - s;
			var a	: Number = s1 * RWGT + s;
			var b	: Number = s1 * RWGT;
			var c	: Number = s1 * RWGT;
			var d	: Number = s1 * GWGT;
			var e	: Number = s1 * GWGT + s;
			var f	: Number = s1 * GWGT;
			var g	: Number = s1 * BWGT;
			var h	: Number = s1 * BWGT;
			var i	: Number = s1 * BWGT + s;

			_saturationMatrix.initialize(
				a, b, c, 0,
				d, e, f, 0,
				g, h, i, 0,
				0, 0, 0, 1
			);

			updateDiffuseColorMatrix();
		}

		/**
		 * Luminance multiplier, default is 1.
		 */
		public function get luminance() : Number
		{
			return _luminance;
		}
		public function set luminance(l : Number) : void
		{
			_luminance = l;
			_luminanceMatrix
				.lock()
				.identity()
				.appendUniformScale(l)
				.unlock();

			updateDiffuseColorMatrix();
		}

		public function HLSAMatrix4x4(hue : Number, luminance : Number, saturation : Number, alpha : Number)
		{
			super();

			this.hue = hue;
			this.luminance = luminance;
			this.saturation = saturation;
			this.alpha = alpha;

			updateDiffuseColorMatrix();
		}

		private function updateDiffuseColorMatrix() : void
		{
			lock();
			identity();
			append(_hueMatrix);
			append(_saturationMatrix);
			append(_luminanceMatrix);
			appendTranslation(0, 0, _alpha);
			unlock();
		}
	}
}

package aerys.minko.type.math
{
	public class HLSAMatrix4x4 extends Matrix4x4
	{
		private static const TMP_NUMBERS	: Vector.<Number> 	= new Vector.<Number>(16, true);
		private static const TMP_MATRIX		: Matrix4x4			= new Matrix4x4();
		private static const TMP_VECTOR4	: Vector4			= new Vector4();
		private static const RWGT			: Number 			= 0.3086;
		private static const GWGT			: Number 			= 0.6094;
		private static const BWGT			: Number 			= 0.0820;
		private static const SQRT2			: Number			= Math.sqrt(2.0);
		private static const SQRT3			: Number			= Math.sqrt(3.0);
		private static const INV_SQRT2		: Number			= 1. / SQRT2;

		private var _hue				: Number		= 0;
		private var _saturation			: Number		= 1;
		private var _luminance			: Number		= 1;
		private var _alpha				: Number		= 1;
		private var _hueMatrix			: Matrix4x4		= new Matrix4x4();
		private var _saturationMatrix	: Matrix4x4		= new Matrix4x4();
		private var _luminanceMatrix	: Matrix4x4		= new Matrix4x4();
		private var _luminancePlane 	: Vector4		= new Vector4(RWGT, GWGT, BWGT, 0);

		/**
		 * Alpha multiplier, from 0 to 1, default is 1.
		 */
		public function get alpha() : Number
		{
			return _alpha
		}
		public function set alpha(value : Number) : void
		{
			_alpha = value;

			updateDiffuseColorMatrix();
		}

		/**
		 * Hue correction, from -2PI to 2PI, default is 0.
		 */
		public function get hue() : Number
		{
			return _hue;
		}
		public function set hue(value : Number) : void
		{
			_hue = value;

			// First we make an identity matrix
			_hueMatrix.identity();

			// Rotate the grey vector into positive Z
			TMP_MATRIX.initialize(
				1,	0, 			0, 			0,
				0, 	INV_SQRT2, 	INV_SQRT2, 	0,
				0, 	-INV_SQRT2, INV_SQRT2, 	0,
				0, 	0, 			0, 			1
			);
			_hueMatrix.append(TMP_MATRIX);

			var mag : Number	= SQRT3;
			var yrs	: Number 	= -1.0 / mag;
			var yrc	: Number 	= SQRT2 / mag;

			TMP_MATRIX.initialize(
				yrc,	0,	-yrs,	0,
				0,		1,	0,		0,
				yrs,	0,	yrc,	0,
				0,		0,	0,		1
			);
			_hueMatrix.append(TMP_MATRIX);

			// Shear the space to make the luminance plane horizontal
			var l	: Vector4 	= _hueMatrix.transformVector(_luminancePlane, TMP_VECTOR4);
			var zsx	: Number 	= l.x / l.z;
			var zsy	: Number 	= l.y / l.z;

			TMP_MATRIX.initialize(
				1,	0,	zsx,	0,
				0,	1,	zsy,	0,
				0,	0,	1,		0,
				0,	0,	0,		1
			);
			_hueMatrix.append(TMP_MATRIX);

			// Rotate the hue
			var zrs	: Number = Math.sin(value);
			var zrc	: Number = Math.cos(value);

			TMP_MATRIX.initialize(
				zrc,	zrs,	0,	0,
				-zrs,	zrc,	0,	0,
				0,		0,		1,	0,
				0,		0,		0,	1
			);
			_hueMatrix.append(TMP_MATRIX);

			// Unshear the space to put the luminance plane back
			TMP_MATRIX.initialize(
				1,	0,	-zsx,	0,
				0,	1,	-zsy,	0,
				0,	0,	1,		0,
				0,	0,	0,		1
			);
			_hueMatrix.append(TMP_MATRIX);

			// Rotate the grey vector back into place
			TMP_MATRIX.initialize(
				yrc,	0,	yrs,	0,
				0,		1,	0,		0,
				-yrs,	0,	yrc,	0,
				0,		0,	0,		1
			);
			_hueMatrix.append(TMP_MATRIX);
			TMP_MATRIX.initialize(
				1,	0,			0,			0,
				0,	INV_SQRT2,	-INV_SQRT2,	0,
				0,	INV_SQRT2,	INV_SQRT2,	0,
				0,	0,			0,			1
			);
			_hueMatrix.append(TMP_MATRIX);

			updateDiffuseColorMatrix();
		}

		/**
		 * Saturation multiplier, default is 1.
		 */
		public function get saturation() : Number
		{
			return _saturation;
		}
		public function set saturation(s : Number):void
		{
			_saturation = s;

			var s1	: Number = 1.0 - s;
			var a	: Number = s1 * RWGT + s;
			var b	: Number = s1 * RWGT;
			var c	: Number = s1 * RWGT;
			var d	: Number = s1 * GWGT;
			var e	: Number = s1 * GWGT + s;
			var f	: Number = s1 * GWGT;
			var g	: Number = s1 * BWGT;
			var h	: Number = s1 * BWGT;
			var i	: Number = s1 * BWGT + s;

			_saturationMatrix.initialize(
				a, b, c, 0,
				d, e, f, 0,
				g, h, i, 0,
				0, 0, 0, 1
			);

			updateDiffuseColorMatrix();
		}

		/**
		 * Luminance multiplier, default is 1.
		 */
		public function get luminance() : Number
		{
			return _luminance;
		}
		public function set luminance(l : Number) : void
		{
			_luminance = l;
			_luminanceMatrix
				.lock()
				.identity()
				.appendUniformScale(l)
				.unlock();

			updateDiffuseColorMatrix();
		}

		public function HLSAMatrix4x4(hue 			: Number	= 0.,
									  luminance 	: Number	= 1.,
									  saturation 	: Number	= 1.,
									  alpha 		: Number	= 1.)
		{
			super();

			this.hue = hue;
			this.luminance = luminance;
			this.saturation = saturation;
			this.alpha = alpha;

			updateDiffuseColorMatrix();
		}

		private function updateDiffuseColorMatrix() : void
		{
			lock();
			identity();
			append(_hueMatrix);
			append(_saturationMatrix);
			append(_luminanceMatrix);
			setColumn(3, new Vector4(0, 0, 0, _alpha));
			unlock();
		}

		override public function clone() : Matrix4x4
		{
			return new HLSAMatrix4x4(_hue, _luminance, _saturation, _alpha);
		}
	}
}

fix HLSAMatrix4x4.updateDiffuseColorMatrix() to use Matrix4x4.setColumn() instead of Matrix4x4.appendTranslation() to handle alpha and override HLSAMatrix4x4.clone() to return an HLSAMatrix4x4 instance


////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008-2013 Esri. All Rights Reserved.
//
// Licensed 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 com.esri.builder.controllers.supportClasses
{

import com.esri.builder.model.WidgetType;
import com.esri.builder.model.WidgetTypeRegistryModel;
import com.esri.builder.supportClasses.FileUtil;
import com.esri.builder.supportClasses.LogUtil;
import com.esri.builder.views.BuilderAlert;

import flash.events.Event;
import flash.events.EventDispatcher;
import flash.filesystem.File;
import flash.utils.Dictionary;

import mx.logging.ILogger;
import mx.logging.Log;
import mx.resources.ResourceManager;

public class StartupWidgetTypeLoader extends EventDispatcher
{
    private static const LOG:ILogger = LogUtil.createLogger(StartupWidgetTypeLoader);

    private var widgetTypeArr:Array = [];
    private var pendingLoaders:Array;

    public function loadWidgetTypes():void
    {
        if (Log.isInfo())
        {
            LOG.info('Loading modules...');
        }

        var moduleFiles:Array = getModuleFiles(WellKnownDirectories.getInstance().bundledModules)
            .concat(getModuleFiles(WellKnownDirectories.getInstance().customModules));

        var swfFileAndXMLFileMaps:Array = mapFilenameToModuleSWFAndXMLFiles(moduleFiles);

        var processedFileNames:Array = [];
        var loaders:Array = [];

        for each (var moduleFile:File in moduleFiles)
        {
            var fileName:String = FileUtil.getFileName(moduleFile);

            if (Log.isDebug())
            {
                LOG.debug('loading module {0}', fileName);
            }

            if (processedFileNames.indexOf(fileName) == -1)
            {
                processedFileNames.push(fileName);
                loaders.push(new WidgetTypeLoader(swfFileAndXMLFileMaps[0][fileName],
                                                  swfFileAndXMLFileMaps[1][fileName]));
            }
        }

        pendingLoaders = loaders;
        for each (var loader:WidgetTypeLoader in loaders)
        {
            loader.addEventListener(WidgetTypeLoaderEvent.LOAD_COMPLETE, loader_loadCompleteHandler);
            loader.addEventListener(WidgetTypeLoaderEvent.LOAD_ERROR, loader_loadErrorHandler);
            loader.load();
        }

        checkIfWidgetTypeLoadersComplete();
    }

    private function mapFilenameToModuleSWFAndXMLFiles(moduleFiles:Array):Array
    {
        var uniqueFileNameToSWF:Dictionary = new Dictionary();
        var uniqueFileNameToXML:Dictionary = new Dictionary();

        for each (var file:File in moduleFiles)
        {
            if (file.extension == "swf")
            {
                uniqueFileNameToSWF[FileUtil.getFileName(file)] = file;
            }
            else if (file.extension == "xml")
            {
                uniqueFileNameToXML[FileUtil.getFileName(file)] = file;
            }
        }

        return [ uniqueFileNameToSWF, uniqueFileNameToXML ];
    }

    private function getModuleFiles(directory:File):Array
    {
        const moduleFileName:RegExp = /^.*Module\.(swf|xml)$/;
        return FileUtil.findMatchingFiles(directory, moduleFileName);
    }

    private function checkIfWidgetTypeLoadersComplete():void
    {
        if (pendingLoaders.length === 0)
        {
            sortAndAssignWidgetTypes();
        }
    }

    private function markModuleAsLoaded(loader:WidgetTypeLoader):void
    {
        var loaderIndex:int = pendingLoaders.indexOf(loader);
        if (loaderIndex > -1)
        {
            pendingLoaders.splice(loaderIndex, 1);
        }
    }

    private function sortAndAssignWidgetTypes():void
    {
        if (Log.isInfo())
        {
            LOG.info('All modules resolved');
        }

        for each (var widgetType:WidgetType in widgetTypeArr)
        {
            if (widgetType.isManaged)
            {
                WidgetTypeRegistryModel.getInstance().widgetTypeRegistry.addWidgetType(widgetType);
            }
            else
            {
                WidgetTypeRegistryModel.getInstance().layoutWidgetTypeRegistry.addWidgetType(widgetType);
            }
        }

        WidgetTypeRegistryModel.getInstance().widgetTypeRegistry.sort();
        WidgetTypeRegistryModel.getInstance().layoutWidgetTypeRegistry.sort();

        dispatchEvent(new Event(Event.COMPLETE));
    }

    private function loader_loadCompleteHandler(event:WidgetTypeLoaderEvent):void
    {
        var loader:WidgetTypeLoader = event.currentTarget as WidgetTypeLoader;
        loader.removeEventListener(WidgetTypeLoaderEvent.LOAD_COMPLETE, loader_loadCompleteHandler);
        loader.removeEventListener(WidgetTypeLoaderEvent.LOAD_ERROR, loader_loadErrorHandler);

        var widgetType:WidgetType = event.widgetType;

        if (Log.isDebug())
        {
            LOG.debug('Module {0} is resolved', widgetType.name);
        }

        widgetTypeArr.push(widgetType);
        markModuleAsLoaded(loader);
        checkIfWidgetTypeLoadersComplete();
    }

    private function loader_loadErrorHandler(event:WidgetTypeLoaderEvent):void
    {
        var loader:WidgetTypeLoader = event.currentTarget as WidgetTypeLoader;
        loader.removeEventListener(WidgetTypeLoaderEvent.LOAD_COMPLETE, loader_loadCompleteHandler);
        loader.removeEventListener(WidgetTypeLoaderEvent.LOAD_ERROR, loader_loadErrorHandler);

        var errorMessage:String = ResourceManager.getInstance().getString('BuilderStrings',
                                                                          'importWidgetProcess.couldNotLoadCustomWidgets',
                                                                          [ loader.name ]);
        if (Log.isDebug())
        {
            LOG.debug(errorMessage);
        }

        BuilderAlert.show(errorMessage,
                          ResourceManager.getInstance().getString('BuilderStrings', 'error'));
        markModuleAsLoaded(loader);
        checkIfWidgetTypeLoadersComplete();
    }
}
}

////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008-2013 Esri. All Rights Reserved.
//
// Licensed 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 com.esri.builder.controllers.supportClasses
{

import com.esri.builder.model.WidgetType;
import com.esri.builder.model.WidgetTypeRegistryModel;
import com.esri.builder.supportClasses.FileUtil;
import com.esri.builder.supportClasses.LogUtil;
import com.esri.builder.views.BuilderAlert;

import flash.events.Event;
import flash.events.EventDispatcher;
import flash.filesystem.File;
import flash.utils.Dictionary;

import mx.logging.ILogger;
import mx.logging.Log;
import mx.resources.ResourceManager;

public class StartupWidgetTypeLoader extends EventDispatcher
{
    private static const LOG:ILogger = LogUtil.createLogger(StartupWidgetTypeLoader);

    private var widgetTypeArr:Array = [];
    private var pendingLoaders:Array;

    public function loadWidgetTypes():void
    {
        if (Log.isInfo())
        {
            LOG.info('Loading modules...');
        }

        var moduleFiles:Array = getModuleFiles(WellKnownDirectories.getInstance().bundledModules)
            .concat(getModuleFiles(WellKnownDirectories.getInstance().customModules));

        var swfFileAndXMLFileMaps:Array = mapFilenameToModuleSWFAndXMLFiles(moduleFiles);

        var processedFileNames:Array = [];
        var loaders:Array = [];

        for each (var moduleFile:File in moduleFiles)
        {
            var fileName:String = FileUtil.getFileName(moduleFile);

            if (Log.isDebug())
            {
                LOG.debug('loading module {0}', fileName);
            }

            if (processedFileNames.indexOf(fileName) == -1)
            {
                processedFileNames.push(fileName);
                loaders.push(new WidgetTypeLoader(swfFileAndXMLFileMaps[0][fileName],
                                                  swfFileAndXMLFileMaps[1][fileName]));
            }
        }

        pendingLoaders = loaders.concat();
        for each (var loader:WidgetTypeLoader in loaders)
        {
            loader.addEventListener(WidgetTypeLoaderEvent.LOAD_COMPLETE, loader_loadCompleteHandler);
            loader.addEventListener(WidgetTypeLoaderEvent.LOAD_ERROR, loader_loadErrorHandler);
            loader.load();
        }

        checkIfWidgetTypeLoadersComplete();
    }

    private function mapFilenameToModuleSWFAndXMLFiles(moduleFiles:Array):Array
    {
        var uniqueFileNameToSWF:Dictionary = new Dictionary();
        var uniqueFileNameToXML:Dictionary = new Dictionary();

        for each (var file:File in moduleFiles)
        {
            if (file.extension == "swf")
            {
                uniqueFileNameToSWF[FileUtil.getFileName(file)] = file;
            }
            else if (file.extension == "xml")
            {
                uniqueFileNameToXML[FileUtil.getFileName(file)] = file;
            }
        }

        return [ uniqueFileNameToSWF, uniqueFileNameToXML ];
    }

    private function getModuleFiles(directory:File):Array
    {
        const moduleFileName:RegExp = /^.*Module\.(swf|xml)$/;
        return FileUtil.findMatchingFiles(directory, moduleFileName);
    }

    private function checkIfWidgetTypeLoadersComplete():void
    {
        if (pendingLoaders.length === 0)
        {
            sortAndAssignWidgetTypes();
        }
    }

    private function markModuleAsLoaded(loader:WidgetTypeLoader):void
    {
        var loaderIndex:int = pendingLoaders.indexOf(loader);
        if (loaderIndex > -1)
        {
            pendingLoaders.splice(loaderIndex, 1);
        }
    }

    private function sortAndAssignWidgetTypes():void
    {
        if (Log.isInfo())
        {
            LOG.info('All modules resolved');
        }

        for each (var widgetType:WidgetType in widgetTypeArr)
        {
            if (widgetType.isManaged)
            {
                WidgetTypeRegistryModel.getInstance().widgetTypeRegistry.addWidgetType(widgetType);
            }
            else
            {
                WidgetTypeRegistryModel.getInstance().layoutWidgetTypeRegistry.addWidgetType(widgetType);
            }
        }

        WidgetTypeRegistryModel.getInstance().widgetTypeRegistry.sort();
        WidgetTypeRegistryModel.getInstance().layoutWidgetTypeRegistry.sort();

        dispatchEvent(new Event(Event.COMPLETE));
    }

    private function loader_loadCompleteHandler(event:WidgetTypeLoaderEvent):void
    {
        var loader:WidgetTypeLoader = event.currentTarget as WidgetTypeLoader;
        loader.removeEventListener(WidgetTypeLoaderEvent.LOAD_COMPLETE, loader_loadCompleteHandler);
        loader.removeEventListener(WidgetTypeLoaderEvent.LOAD_ERROR, loader_loadErrorHandler);

        var widgetType:WidgetType = event.widgetType;

        if (Log.isDebug())
        {
            LOG.debug('Module {0} is resolved', widgetType.name);
        }

        widgetTypeArr.push(widgetType);
        markModuleAsLoaded(loader);
        checkIfWidgetTypeLoadersComplete();
    }

    private function loader_loadErrorHandler(event:WidgetTypeLoaderEvent):void
    {
        var loader:WidgetTypeLoader = event.currentTarget as WidgetTypeLoader;
        loader.removeEventListener(WidgetTypeLoaderEvent.LOAD_COMPLETE, loader_loadCompleteHandler);
        loader.removeEventListener(WidgetTypeLoaderEvent.LOAD_ERROR, loader_loadErrorHandler);

        var errorMessage:String = ResourceManager.getInstance().getString('BuilderStrings',
                                                                          'importWidgetProcess.couldNotLoadCustomWidgets',
                                                                          [ loader.name ]);
        if (Log.isDebug())
        {
            LOG.debug(errorMessage);
        }

        BuilderAlert.show(errorMessage,
                          ResourceManager.getInstance().getString('BuilderStrings', 'error'));
        markModuleAsLoaded(loader);
        checkIfWidgetTypeLoadersComplete();
    }
}
}

Store pending widget type loaders separately to avoid messing load order.