Transferred from the Adobe Open Source Subversion Repository

Revision: 8
Author:   open-source-svn-admin@adobe.com
Date:     2007-12-15T01:49:31.087529Z

Log Message:
-----------
The beginning of the open-source Flex SDK.

Populated the flex/sdk/trunk directory with an open-source subset of files from //depot/flex/sdk/...@191097 in the Faramir Perforce server. This changelist is a bit past the 3.0rc0 build.

The Ant scripts will not successfully build yet. They need to be adapted to the open-source subset.

Modified Paths:
--------------
2532 paths were modified

git-svn-id: 7146a936b37d36e51ea0d66f8acbe0b8281bc7b7@1274591 13f79535-47bb-0310-9956-ffa450edef68

--- frameworks/projects/rpc/src/mx/rpc/xml/SchemaMarshaller.as
+++ frameworks/projects/rpc/src/mx/rpc/xml/SchemaMarshaller.as
@@ -0,0 +1,1636 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  ADOBE SYSTEMS INCORPORATED
+//  Copyright 2006-2007 Adobe Systems Incorporated
+//  All Rights Reserved.
+//
+//  NOTICE: Adobe permits you to use, modify, and distribute this file
+//  in accordance with the terms of the license agreement accompanying it.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package mx.rpc.xml
+{
+
+import flash.utils.ByteArray;
+import flash.utils.Dictionary;
+
+import mx.utils.Base64Encoder;
+import mx.utils.Base64Decoder;
+import mx.utils.HexEncoder;
+import mx.utils.HexDecoder;
+import mx.utils.StringUtil;
+
+[ExcludeClass]
+
+/**
+ * FIXME: Derivations and restrictions need to be considered in addition
+ * to base schema types
+ *
+ * @private
+ */
+public class SchemaMarshaller //implements IXMLTypeMarshaller
+{
+    public function SchemaMarshaller(constants:SchemaConstants, datatypes:SchemaDatatypes)
+    {
+        super();
+        this.constants = constants;
+        this.datatypes = datatypes;
+
+        registerMarshallers();
+    }
+
+
+    //--------------------------------------------------------------------------
+    //
+    // Properties
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * Determines whether this marshaller will throw errors for input that
+     * violates the specified format or restrictions for the associated type.
+     * Type errors are still thrown for unexpected input types.
+     */
+    public function get validating():Boolean
+    {
+        return _validating;
+    }
+
+    public function set validating(value:Boolean):void
+    {
+        _validating = value;
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    // Methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * This function converts an ActionScript value to a String for XML
+     * simple content based on a built-in XML Schema type. If a type is not
+     * provided, the anyType is assumed.
+     */
+    public function marshall(value:*, type:QName = null, restriction:XML = null):*
+    {
+        if (type == null)
+            type = datatypes.anyTypeQName;
+
+        var marshaller:Function = marshallers[type.localName];
+        var result:*;
+
+        if (marshaller != null)
+            result = marshaller(value, type, restriction);
+        else
+            throw new TypeError("Cannot marshall type '" + type + "' to simple content.");
+
+        return result;
+    }
+
+    /**
+     * This function converts XML simple content (formatted based on a built-in
+     * XML Schema type) to an ActionScript value. If a type is not provided,
+     * the anyType is assumed.
+     */
+    public function unmarshall(value:*, type:QName = null, restriction:XML = null):*
+    {
+        // First, get the simple content as a String
+        var rawValue:String;
+
+        if (value is XML)
+        {
+            var xml:XML = value as XML;
+            if (xml.nodeKind() == "element")
+            {
+                var nilAttribute:String = xml.attribute(constants.nilQName).toString();
+                if (nilAttribute == "true")
+                    rawValue = null;
+                else
+                    rawValue = xml.text().toString();
+            }
+            else
+            {
+                rawValue = xml.toString();
+            }
+        }
+        else if (value is XMLList)
+        {
+            var list:XMLList = value as XMLList;
+            rawValue = list.text().toString();
+        }
+        else if (value != null)
+        {
+            rawValue = value.toString();
+        }
+
+        if (type == null)
+            type = datatypes.anyTypeQName;
+
+        var unmarshaller:Function = unmarshallers[type.localName];
+        if (unmarshaller != null)
+            var value:* = unmarshaller(rawValue, type, restriction);
+        else
+            throw new TypeError("Cannot unmarshall type '" + type + "' from XML.");
+
+        return value;
+    }
+
+    /**
+     * In the case of XML Schema ur-types such as anyType and
+     * anySimpleType we try to guess what the equivalent XML Schema
+     * simple datatype should be based on the ActionScript type. As a last
+     * resort, the string datatype is used.
+     */
+    public function marshallAny(value:*, type:QName = null, restriction:XML = null):*
+    {
+        if (value === undefined)
+            return undefined;
+        else if (value == null)
+            return null;
+
+        var localName:String = guessSimpleType(value);
+
+        if (type != null)
+            type = new QName(type.uri, localName);
+        else
+            type = new QName(constants.xsdURI, localName);
+
+        var marshaller:Function = marshallers[type.localName];
+        if (marshaller != null)
+        {
+            return marshaller(value, type);
+        }
+        else
+        {
+            throw new TypeError("Cannot marshall type '" + type + "' to simple content.");
+        }
+    }
+
+    public function marshallBase64Binary(value:*, type:QName = null, restriction:XML = null):String
+    {
+        var ba:ByteArray = value as ByteArray;
+        var result:String;
+        if (ba != null)
+        {
+            var encoder:Base64Encoder = new Base64Encoder();
+            encoder.insertNewLines = false;
+            encoder.encodeBytes(ba);
+            result = encoder.drain();
+        }
+        else
+        {
+            return null;
+        }
+        return result;
+    }
+
+    /**
+     * The boolean schema type allows the string values 'true' or
+     * '1' for true values and 'false' or '0' for false values. This
+     * marshaller, by default, represents values using 'true' or false.
+     * If a String value of '1' or '0' is passed, however, it is preserved.
+     */
+    public function marshallBoolean(value:*, type:QName = null, restriction:XML = null):String
+    {
+        var result:String;
+
+        if (value != null)
+        {
+            if (value is Boolean)
+            {
+                result = (value as Boolean) ? "true" : "false";
+            }
+            else if (value is Number)
+            {
+                result = (value == 1) ? "true" : "false";
+            }
+            else if (value is Object)
+            {
+                var s:String = Object(value).toString();
+                if (s == "true" || s == "false" || s == "1" || s == "0")
+                    result = s;
+                else
+                    throw new Error("String '" + value + "' is not a Boolean.");
+            }
+        }
+
+        return result;
+    }
+
+    public function marshallDatetime(value:*, type:QName = null, restriction:XML = null):String
+    {
+        var result:String;
+        var date:Date;
+
+        if (value is Number)
+        {
+            date = new Date();
+            date.time = value as Number;
+            value = date;
+        }
+
+        if (value is Date)
+        {
+            var n:Number;
+            date = value as Date;
+            result = "";
+
+            if (type == datatypes.dateTimeQName
+                || type == datatypes.dateQName)
+            {
+                // Year
+                result = result.concat(date.getUTCFullYear(), "-");
+
+                // Month
+                n = date.getUTCMonth() + 1; // ActionScript UTC month starts from 0
+                if (n < 10)
+                    result = result.concat("0");
+                result = result.concat(n, "-");
+
+                // Day
+                n = date.getUTCDate();
+                if (n < 10)
+                    result = result.concat("0");
+                result = result.concat(n);
+            }
+
+            if (type == datatypes.dateTimeQName)
+            {
+                result = result.concat("T");
+            }
+
+            // Time
+            if (type == datatypes.dateTimeQName
+                || type == datatypes.timeQName)
+            {
+                n = date.getUTCHours();
+                if (n < 10)
+                    result = result.concat("0");
+                result = result.concat(n, ":");
+
+                n = date.getUTCMinutes();
+                if (n < 10)
+                    result = result.concat("0");
+                result = result.concat(n, ":");
+
+                n = date.getUTCSeconds();
+                if (n < 10)
+                    result = result.concat("0");
+                result = result.concat(n);
+
+                // Milliseconds are optional so we write them if > 0
+                n = date.getUTCMilliseconds();
+                if (n > 0)
+                {
+                    result = result.concat(".");
+                    if (n < 10)
+                        result = result.concat("00");
+                    else if (n < 100)
+                        result = result.concat("0");
+                    result = result.concat(n);
+                }
+            }
+
+            // Always send dates, times and dateTimes in UTC from the player.
+            result = result.concat("Z");
+        }
+        else if (value is String)
+        {
+            result = value as String;
+        }
+
+        return result;
+    }
+
+    /**
+     * FIXME: Handle precision and exponent restrictions.
+     */
+    public function marshallDecimal(value:*, type:QName = null, restriction:XML = null):String
+    {
+        var result:String;
+        var number:Number;
+        if (value is Number)
+        {
+            number = value as Number;
+        }
+        else
+        {
+            var str:String = whitespaceCollapse(value);
+            number = Number(str);
+        }
+
+        // Check for Infinity, -Infinity and NaN as these are not valid
+        // values for the XML Schema decimal type.
+        result = specialNumber(number);
+
+        if (result != null)
+        {
+            throw new Error("Invalid decimal value '" + value + "'.");
+        }
+        else
+        {
+            // We have a normal number
+            result = number.toString();
+        }
+        return result;
+    }
+
+    /**
+     * FIXME: Handle precision and exponent restrictions.
+     */
+    public function marshallDouble(value:*, type:QName = null, restriction:XML = null):String
+    {
+        var result:String;
+        var number:Number = Number(value);
+
+        // Check for Infinity, -Infinity and NaN
+        result = specialNumber(number);
+
+        if (result == null)
+            result = number.toString();
+
+        return result;
+    }
+
+    public function marshallDuration(value:*, type:QName = null, restriction:XML = null):String
+    {
+        return whitespaceCollapse(value);
+    }
+
+    /**
+     * FIXME: Handle precision and exponent restrictions.
+     */
+    public function marshallFloat(value:*, type:QName = null, restriction:XML = null):String
+    {
+        var result:String;
+
+        // First, check that we have a suitable input for Number
+        var number:Number;
+        if (value is Number)
+        {
+            number = value as Number;
+        }
+        else
+        {
+            var str:String = whitespaceCollapse(value);
+            number = Number(str);
+        }
+
+        // Check for Infinity, -Infinity and NaN
+        result = specialNumber(number);
+
+        if (result == null)
+        {
+            if (validating)
+            {
+                if (number > 0)
+                {
+                    if (number > FLOAT_MAX_VALUE)
+                        throw new RangeError("The value '" + value + "' is too large for a single-precision floating point value.");
+                    else if (number < FLOAT_MIN_VALUE)
+                        throw new RangeError("The value '" + value + "' is too small for a single-precision floating point value.");
+                }
+                else
+                {
+                    if (-number > FLOAT_MAX_VALUE)
+                        throw new RangeError("The absolute value of '" + value + "' is too large for a single-precision floating point value.");
+                    else if (-number < FLOAT_MIN_VALUE)
+                        throw new RangeError("The absolute value of '" + value + "' is too small for a single-precision floating point value.");
+                }
+            }
+            result = number.toString();
+        }
+
+        return result;
+    }
+
+    public function marshallGregorian(value:*, type:QName = null, restriction:XML = null):String
+    {
+        var date:Date;
+        var n:Number;
+
+        if (value is Date)
+            date = value as Date;
+        else if (value is Number)
+            n = value as Number;
+        else
+            value = whitespaceCollapse(value);
+
+        var result:String = "";
+        var hyphen:int;
+
+        // Year
+        if (type == datatypes.gYearMonthQName
+            || type == datatypes.gYearQName)
+        {
+            var year:String;
+
+            if (value is Date)
+            {
+                n = date.getUTCFullYear();
+            }
+            else if (value is String)
+            {
+                year = value as String;
+                // We may have CCYY, -CCYY, CCYY-MM or -CCYY-MM
+                hyphen = year.indexOf("-", 1);
+                if (hyphen > 0)
+                    year = year.substring(0, hyphen);
+                n = parseInt(year);
+            }
+
+            // Check for NaN or 0000 as these are not valid years.
+            if (isNaN(n) || n == 0)
+            {
+                if (validating)
+                {
+                    throw new Error("Invalid year supplied for type " + type.localName + " in value " + value);
+                }
+                else
+                {
+                    // Abort trying to process this value
+                    return whitespaceCollapse(value);
+                }
+            }
+            else
+            {
+                // Get a String representation of the year, though this value
+                // is potentially unbounded so we use Number.toFixed instead
+                // of int().
+                year = n.toFixed(0);
+
+                // Pad early years with leading 0s up to 000x but
+                // we keep in mind that negative years such as -894 are valid.
+                if (year.indexOf("-") == 0)
+                {
+                    while (year.length < 5)
+                        year = year.substring(0, 1) + "0" + year.substring(1);
+                }
+                else
+                {
+                    while (year.length < 4)
+                        year = "0" + year;
+                }
+                result = result.concat(year);
+            }
+
+            if (type != datatypes.gYearQName)
+                result = result.concat("-");
+        }
+
+        // Month
+        if (type == datatypes.gYearMonthQName
+            || type == datatypes.gMonthDayQName
+            || type == datatypes.gMonthQName)
+        {
+            if (type != datatypes.gYearMonthQName)
+                result = result.concat("--");
+
+            var month:String;
+
+            if (value is Date)
+                n = date.getUTCMonth() + 1; // ActionScript UTC month starts at 0
+            else
+            {
+                month = value.toString();
+                if (type == datatypes.gMonthDayQName)
+                {
+                    // We must have --MM-DD
+                    hyphen = month.indexOf("--", 0);
+                    if (hyphen == 0)
+                        month = month.substring(2, 4);
+                    else if (validating)
+                        throw new Error("Invalid month supplied for " + type.localName + " in value " + value + ". The format must be '--MM-DD'.");
+                    else
+                    {
+                        // Abort trying to process this value
+                        return whitespaceCollapse(value);
+                    }
+                }
+                else if (type == datatypes.gYearMonthQName)
+                {
+                    hyphen = month.indexOf("-", 1);
+                    // We must have CCYY-MM or -CCYY-MM
+                    if (hyphen > 0)
+                        month = month.substring(hyphen + 1, hyphen + 3);
+                    else if (validating)
+                        throw new Error("Invalid month supplied for " + type.localName + " in value " + value + ". The format must be '--CCYY-MM'.");
+                    else
+                    {
+                        // Abort trying to process this value
+                        return whitespaceCollapse(value);
+                    }
+                }
+                else
+                {
+                    // We may have --MM-- (but we allow just MM too)
+                    hyphen = month.indexOf("--", 0);
+                    if (hyphen > 0)
+                        month = month.substring(2, 4);
+                }
+                n = parseInt(month);
+            }
+
+            // Check for NaN or values not in the range of 1 to 12
+            // as these are not valid months
+            if (isNaN(n) || n <= 0 || n > 12)
+            {
+                if (validating)
+                    throw new Error("Invalid month supplied for type " + type.localName + " in value " + value);
+                else
+                {
+                    // Abort trying to process this value
+                    return whitespaceCollapse(value);
+                }
+            }
+            else
+            {
+                n = int(n);
+                if (n < 10)
+                    result = result.concat("0");
+                result = result.concat(n);
+            }
+
+            if (type == datatypes.gMonthDayQName)
+            {
+                result = result.concat("-");
+            }
+        }
+
+        // Day
+        if (type == datatypes.gMonthDayQName
+            || type == datatypes.gDayQName)
+        {
+            if (type == datatypes.gDayQName)
+                result = result.concat("---");
+
+            if (value is Date)
+                n = date.getUTCDate();
+            else if (value is String)
+            {
+                var day:String = value as String;
+                if (type == datatypes.gMonthDayQName)
+                {
+                    //We must have --MM-DD
+                    hyphen = day.indexOf("--", 0);
+                    if (hyphen == 0)
+                        day = day.substring(5, 7);
+                    else if (validating)
+                        throw new Error("Invalid day supplied for gMonthDay in value " + value + ". The format must be '--MM-DD'.");
+                    else
+                    {
+                        // Abort trying to process this value
+                        return whitespaceCollapse(value);
+                    }
+                }
+                else
+                {
+                    //We might have ---DD (but we allow just DD too)
+                    hyphen = day.indexOf("---", 0);
+                    if (hyphen == 0)
+                        day = day.substring(3, 5);
+                }
+
+                n = parseInt(day);
+            }
+
+            // Check for NaN or values not in the range of 1 to 31
+            // as these are not valid days
+            if (isNaN(n) || n <= 0 || n > 31)
+            {
+                if (validating)
+                    throw new Error("Invalid day supplied for type " + type.localName + " in value " + value);
+                else
+                    // Abort trying to process this value
+                    return whitespaceCollapse(value);
+            }
+            else
+            {
+                n = int(n);
+                if (n < 10)
+                    result = result.concat("0");
+                result = result.concat(n);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * The schema type hexBinary represents arbitrary hex-encoded binary data.
+     * Each binary octet is encoded as a character tuple consisting of two
+     * hexadecimal digits (which is treated case insensitively although
+     * capital letters A-F are always used on encoding). These tuples are
+     * added to a String to serialize the binary data.
+     */
+    public function marshallHexBinary(value:*, type:QName = null, restriction:XML = null):String
+    {
+        var ba:ByteArray = value as ByteArray;
+        var valueString:String;
+        if (ba != null)
+        {
+            var encoder:HexEncoder = new HexEncoder();
+            encoder.encode(ba);
+            valueString = encoder.drain();
+        }
+        else
+        {
+            throw new Error("Cannot marshall value as hex binary.");
+        }
+        return valueString;
+    }
+
+    /**
+     * The schema type integer is dervied from the decimal type via restrictions
+     * by fixing the value of fractionDigits to be 0 and disallowing the
+     * trailing decimal point. The schema types long, int, short, byte are
+     * derived from integer by restricting the maxInclusive and minInclusive
+     * properties. Other types such as nonPositiveInteger, negativeInteger,
+     * nonNegativeInteger, positiveInteger, unsignedLong, unsignedInt,
+     * unsignedShort and unsignedByte are also dervied from integer through
+     * similar restrictions.
+     *
+     * This method first calls parses the value as a Number. It
+     * then uses Math.floor() on the number to remove any fraction
+     * digits and then checks that the result is within the specified
+     * min and max for the type. Note that decimal
+     * values are not rounded. This method handles integers longer than 32-bit
+     * so ActionScript int or uint types are not used internally.
+     */
+    public function marshallInteger(value:*, type:QName = null, restriction:XML = null):String
+    {
+        var result:String;
+
+        var number:Number;
+        if (value is Number)
+            number = value as Number
+        else
+            number = Number(whitespaceCollapse(value));
+
+        var min:Number = -Number.MAX_VALUE;
+        var max:Number = Number.MAX_VALUE;
+
+        if (type == datatypes.longQName)
+        {
+            min = LONG_MIN_VALUE
+            max = LONG_MAX_VALUE;
+        }
+        else if (type == datatypes.intQName)
+        {
+            min = int.MIN_VALUE;
+            max = int.MAX_VALUE;
+        }
+        else if (type == datatypes.shortQName)
+        {
+            min = SHORT_MIN_VALUE
+            max = SHORT_MAX_VALUE;
+        }
+        else if (type == datatypes.byteQName)
+        {
+            min = BYTE_MIN_VALUE
+            max = BYTE_MAX_VALUE;
+        }
+        else if (type == datatypes.unsignedLongQName)
+        {
+            min = 0;
+            max = ULONG_MAX_VALUE;
+        }
+        else if (type == datatypes.unsignedIntQName)
+        {
+            min = 0;
+            max = uint.MAX_VALUE;
+        }
+        else if (type == datatypes.unsignedShortQName)
+        {
+            min = 0;
+            max = USHORT_MAX_VALUE;
+        }
+        else if (type == datatypes.unsignedByteQName)
+        {
+            min = 0;
+            max = UBYTE_MAX_VALUE;
+        }
+        else if (type == datatypes.positiveIntegerQName)
+        {
+            min = 1;
+            max = Number.MAX_VALUE;
+        }
+        else if (type == datatypes.nonNegativeIntegerQName)
+        {
+            min = 0;
+            max = Number.MAX_VALUE;
+        }
+        else if (type == datatypes.negativeIntegerQName)
+        {
+            min = -Number.MAX_VALUE;
+            max = -1;
+        }
+        else if (type == datatypes.nonPositiveIntegerQName)
+        {
+            min = -Number.MAX_VALUE;
+            max = 0;
+        }
+        else if (type == datatypes.integerQName)
+        {
+            min = -Number.MAX_VALUE;
+            max = Number.MAX_VALUE;
+        }
+
+        var integer:Number = Math.floor(number);
+
+        result = specialNumber(integer);
+        if (result != null)
+        {
+            // Throw an error for NaN, Inf and -Inf even if validating is false.
+            throw new Error(result + " is not an allowed value for " + type.localName);
+        }
+
+        if (integer >= min)
+        {
+            if (integer > max)
+            {
+                if (validating)
+                {
+                    throw new RangeError("The value '" + value + "' is too large for " + type.localName);
+                }
+                else
+                {
+                    // As a last resort, we just go with the original input
+                    return whitespaceCollapse(value);
+                }
+            }
+        }
+        else
+        {
+            if (validating)
+            {
+                throw new RangeError("The value '" + value + "' is too small for " + type.localName);
+            }
+            else
+            {
+                // As a last resort, we just go with the original input
+                return whitespaceCollapse(value);
+            }
+        }
+
+        result = integer.toString();
+        return result;
+    }
+
+    public function marshallString(value:*, type:QName = null, restriction:XML = null):String
+    {
+        if (value != null && value is Object)
+        {
+            return Object(value).toString();
+        }
+
+        return null;
+    }
+
+    public function unmarshallAny(value:*, type:QName = null, restriction:XML = null):*
+    {
+        if (value === undefined)
+            return undefined;
+        else if (value == null)
+            return null;
+
+        var result:*;
+        var unmarshaller:Function;
+
+        var s:String = whitespaceCollapse(value);
+
+        if (s == "")
+        {
+            unmarshaller = unmarshallers[datatypes.stringQName.localName];
+        }
+        else if (isNaN(Number(s))
+            || (s.charAt(0) == '0')
+            || ((s.charAt(0) == '-') && (s.charAt(1) == '0'))
+            || s.charAt(s.length - 1) == 'E')
+        {
+            var lowerS:String = s.toLowerCase();
+            if (lowerS == "true" || lowerS == "false")
+            {
+                unmarshaller = unmarshallers[datatypes.booleanQName.localName];
+            }
+            else
+            {
+                unmarshaller = unmarshallers[datatypes.stringQName.localName];
+            }
+        }
+        else
+        {
+            unmarshaller = unmarshallers[datatypes.doubleQName.localName];
+        }
+
+        result = unmarshaller(value, type, restriction);
+        return result;
+    }
+
+    public function unmarshallBase64Binary(value:*, type:QName = null, restriction:XML = null):ByteArray
+    {
+        if (value == null)
+            return null;
+
+        var data:String = whitespaceCollapse(value);
+        var decoder:Base64Decoder = new Base64Decoder();
+        decoder.decode(data);
+        return decoder.drain();
+    }
+
+    public function unmarshallBoolean(value:*, type:QName = null, restriction:XML = null):Boolean
+    {
+        if (value == null)
+            return false;
+
+        var bool:String = whitespaceCollapse(value).toLowerCase();
+        if (bool == "true" || bool == "1")
+            return true;
+
+        return false;
+    }
+
+    public function unmarshallDate(value:*, type:QName = null, restriction:XML = null):Object
+    {
+        if (value == null)
+            return null;
+
+        var date:Date;
+        var index:int;
+        var datePart:String = whitespaceCollapse(value);
+
+        if (datePart != null)
+        {
+            // dateLexicalRep ::= yearFrag '-' monthFrag '-' dayFrag timezoneFrag?
+            // yearFrag ::= '-'? (([1-9] digit digit digit+)) | ('0' digit digit digit))
+            index = datePart.indexOf("-", 1);
+            var year:uint = uint(datePart.substring(0, index++));
+            // monthFrag ::= ('0' [1-9]) | ('1' [0-2])
+            var month:uint = uint(datePart.substring(index, index + 2));
+            index += 3;
+            // dayFrag ::= (0 [1-9]) | ([12] digit) | ('3' [01])
+            var day:uint = uint(datePart.substring(index, index + 2));
+            index += 2;
+            // timezoneFrag ::= 'Z' | (('+' | '-') ('0' digit | '1' [0-4]) ':' minuteFrag)
+            if (datePart.charAt(index) == "Z") // UTC.
+            {
+                date = new Date(Date.UTC(year, month - 1, day));
+            }
+            else if (datePart.length > 10) // UTC offset.
+            {
+                // Start at UTC.
+                date = new Date(Date.UTC(year, month - 1, day));
+                // (('+' | '-') ('0' digit | '1' [0-4]) ':' minuteFrag)
+                var offsetDirection:String = datePart.charAt(index++);
+                var hours:int = int(datePart.substring(index, index + 2));
+                index += 3;
+                // minuteFrag ::= [0-5] digit
+                var minutes:int = int(datePart.substring(index, index + 2));
+                // Done with parse; apply offset.
+                var millis:Number = (hours * 3600000) + (minutes * 60000);
+                if (offsetDirection == "+")
+                {
+                    date.time -= millis;
+                }
+                else // "-"
+                {
+                    date.time += millis;
+                }
+            }
+            else // Treat as local time.
+            {
+                date = new Date(year, month - 1, day);
+            }
+        }
+
+        return date;
+    }
+
+    /**
+     * Handles dateTime and time types.
+     */
+    public function unmarshallDatetime(value:*, type:QName = null, restriction:XML = null):Object
+    {
+        if (value == null)
+            return null;
+
+        var date:Date;
+        var rawValue:String = whitespaceCollapse(value);
+        var datePart:String;
+        var timePart:String;
+        var utc:Boolean;
+        var offset:Boolean;
+        var index:int;
+
+        var sep:int = rawValue.indexOf("T");
+        if (sep != -1)
+        {
+            datePart = rawValue.substring(0, sep);
+            timePart = rawValue.substring(sep + 1);
+        }
+        else // It's a time (no date part).
+        {
+            timePart = rawValue;
+        }
+
+        // Parse the time first to get the timezone/offset. E.g. 24:00:00.000
+        // timeLexicalRep ::= ((hourFrag ':' minuteFrag ':' secondFrag) | endOfDayFrag) timezoneFrag?
+        // hourFrag ::= ([01] digit) | ('2' [0-3])
+        var hours:int = int(timePart.substring(0, 2));
+        // minuteFrag ::= [0-5] digit
+        var minutes:int = int(timePart.substring(3, 5));
+        // secondFrag ::= ([0-5] digit) ('.' digit+)?
+        var millisStart:int = timePart.indexOf(".", 6);
+        var seconds:int = int(timePart.substring(6, 8));
+        // Handle millis and timezone next.
+        // timezoneFrag ::= 'Z' | (('+' | '-') ('0' digit | '1' [0-4]) ':' minuteFrag)
+        var tzIndex:int = timePart.indexOf("Z", 8);
+        var offsetDirection:int;
+        var offsetMillis:Number;
+        if (tzIndex == -1)
+        {
+            if ((tzIndex = timePart.indexOf("+", 8)) != -1)
+                offsetDirection = 1; // Positive.
+            else if ((tzIndex = timePart.indexOf("-", 8)) != -1)
+                offsetDirection = -1; // Negative.
+
+            if (tzIndex != -1)
+            {
+                index = tzIndex + 1;
+                var offsetHours:int = int(timePart.substring(index, index + 2));
+                index += 3;
+                var offsetMinutes:int = int(timePart.substring(index, index + 2));
+                offsetMillis = (offsetHours * 3600000) + (offsetMinutes * 60000);
+                utc = true;
+                offset = true;
+            }
+        }
+        else
+        {
+            utc = true;
+        }
+        var millis:int = 0;
+        if (millisStart != -1)
+        {
+            if (utc)
+                millis = int(timePart.substring(millisStart + 1, tzIndex));
+            else
+                millis = int(timePart.substring(millisStart + 1));
+        }
+
+        // Now parse the datePart if it exists.
+        if (datePart != null)
+        {
+            // dateLexicalRep ::= yearFrag '-' monthFrag '-' dayFrag timezoneFrag?
+            // yearFrag ::= '-'? (([1-9] digit digit digit+)) | ('0' digit digit digit))
+            index = datePart.indexOf("-", 1);
+            var year:uint = uint(datePart.substring(0, index++));
+            // monthFrag ::= ('0' [1-9]) | ('1' [0-2])
+            var month:uint = uint(datePart.substring(index, index + 2));
+            index += 3;
+            // dayFrag ::= (0 [1-9]) | ([12] digit) | ('3' [01])
+            var day:uint = uint(datePart.substring(index, index + 2));
+
+            if (utc)
+                date = new Date(Date.UTC(year, month - 1, day));
+            else
+                date = new Date(year, month - 1, day);
+
+        }
+        else
+        {
+            if (utc)
+            {
+                var now:Date = new Date();
+                date = new Date(Date.UTC(now.fullYearUTC, now.monthUTC, now.dateUTC));
+            }
+            else
+            {
+                date = new Date();
+            }
+        }
+        // Apply the time part.
+        if (utc)
+        {
+            date.setUTCHours(hours, minutes, seconds, millis);
+            if (offset)
+            {
+                if (offsetDirection > 0)
+                    date.time -= offsetMillis;
+                else
+                    date.time += offsetMillis;
+            }
+        }
+        else
+        {
+            date.setHours(hours, minutes, seconds, millis);
+        }
+
+        return date;
+    }
+
+    public function unmarshallDecimal(value:*, type:QName = null, restriction:XML = null):Number
+    {
+        return unmarshallDouble(value, type);
+    }
+
+    public function unmarshallDouble(value:*, type:QName = null, restriction:XML = null):Number
+    {
+        var result:Number;
+
+        if (value != null)
+        {
+            var s:String = whitespaceCollapse(value);
+            if (s == "INF")
+            {
+                result = Number.POSITIVE_INFINITY;
+            }
+            else if (s == "-INF")
+            {
+                result = Number.NEGATIVE_INFINITY;
+            }
+            else
+            {
+                result = Number(s);
+            }
+        }
+
+        return result;
+    }
+
+    public function unmarshallDuration(value:*, type:QName = null, restriction:XML = null):*
+    {
+        return whitespaceCollapse(value);
+    }
+
+    public function unmarshallFloat(value:*, type:QName = null, restriction:XML = null):Number
+    {
+        return unmarshallDouble(value, type, restriction);
+    }
+
+    // FIXME: Should we always return String for all gregorian types?
+    public function unmarshallGregorian(value:*, type:QName = null, restriction:XML = null):*
+    {
+        var rawValue:String = whitespaceCollapse(value);
+
+        var result:* = rawValue;
+
+        // FIXME: Consider timezone, if provided, for gregorian date info
+        if (type == datatypes.gYearQName)
+        {
+            result = uint(rawValue.substring(0, 4));
+        }
+        /*
+        // FIXME: Should we leave YearMonth and MonthDay as String?
+        else if (type == datatypes.gYearMonthQName)
+        {
+            result = rawValue.substring(0, 7);
+        }
+        else if (type == datatypes.gMonthDayQName)
+        {
+            result = rawValue.substring(2, 5);
+        }
+        */
+        else if (type == datatypes.gMonthQName)
+        {
+            result = uint(rawValue.substring(2, 4));
+        }
+        else if (type == datatypes.gDayQName)
+        {
+            result = uint(rawValue.substring(3, 5));
+        }
+
+        return result;
+    }
+
+    public function unmarshallHexBinary(value:*, type:QName = null, restriction:XML = null):ByteArray
+    {
+        if (value != null)
+        {
+            var data:String = whitespaceCollapse(value);
+            var decoder:HexDecoder = new HexDecoder();
+            decoder.decode(data);
+            return decoder.drain();
+        }
+
+        return null;
+    }
+
+    public function unmarshallInteger(value:*, type:QName = null, restriction:XML = null):Number
+    {
+        return parseInt(value);
+    }
+
+    public function unmarshallString(value:*, type:QName = null, restriction:XML = null):String
+    {
+        var result:String;
+        if (value != null)
+        {
+            result = value.toString();
+        }
+        return result;
+    }
+
+    public static function guessSimpleType(value:*):*
+    {
+        var localName:String = "string";
+        if (value is uint)
+        {
+            localName = "unsignedInt";
+        }
+        else if (value is int)
+        {
+            localName = "int";
+        }
+        else if (value is Number)
+        {
+            localName = "double";
+        }
+        else if (value is Boolean)
+        {
+            localName = "boolean";
+        }
+        else if (value is String)
+        {
+            localName = "string";
+        }
+        else if (value is ByteArray)
+        {
+            if (byteArrayAsBase64Binary)
+                localName = "base64Binary";
+            else
+                localName = "hexBinary";
+        }
+        else if (value is Date)
+        {
+            localName = "dateTime";
+        }
+
+        return localName;
+    }
+
+    private static function specialNumber(value:Number):String
+    {
+        if (value == Number.NEGATIVE_INFINITY)
+            return "-INF";
+        else if (value == Number.POSITIVE_INFINITY)
+            return "INF";
+        else if (isNaN(value))
+            return "NaN";
+        else
+            return null;
+    }
+
+    /**
+     * For simple types with the whitespace restriction collapse
+     * all occurrences of #x9 (tab), #xA (line feed) and #xD (carriage return)
+     * are replaced with #x20 (space), then consecutive spaces are collapsed
+     * to a single space, then finally the leading and trailing spaces are
+     * trimmed.
+     */
+    private static function whitespaceCollapse(value:*):String
+    {
+        if (value == null)
+            return null;
+
+        var s:String = value.toString();
+        s = s.replace(whitespaceCollapsePattern, " ");
+        s = s.replace(whitespaceTrimPattern, "");
+        return s;
+    }
+
+    /**
+     * For simple types with the whitespace restriction replace
+     * all occurrences of #x9 (tab), #xA (line feed) and #xD (carriage return)
+     * are replaced with #x20 (space).
+     */
+    private static function whitespaceReplace(value:*):String
+    {
+        if (value == null)
+            return null;
+
+        var s:String = value.toString();
+        s = s.replace(whitespaceReplacePattern, " ");
+        return s;
+    }
+
+    private function registerMarshallers():void
+    {
+        marshallers = {};
+        marshallers[datatypes.booleanQName.localName] = marshallBoolean;
+
+        // Special built-in schema datatypes
+        marshallers[datatypes.anyTypeQName.localName] = marshallAny;
+        marshallers[datatypes.anySimpleTypeQName.localName] = marshallAny;
+        marshallers[datatypes.anyAtomicTypeQName.localName] = marshallAny;
+
+        // Primitive datatypes
+        marshallers[datatypes.stringQName.localName] = marshallString;
+        marshallers[datatypes.booleanQName.localName] = marshallBoolean;
+        marshallers[datatypes.decimalQName.localName] = marshallDecimal;
+        marshallers[datatypes.precisionDecimal.localName] = marshallDecimal;
+        marshallers[datatypes.floatQName.localName] = marshallFloat;
+        marshallers[datatypes.doubleQName.localName] = marshallDouble;
+        marshallers[datatypes.durationQName.localName] = marshallDuration;
+        marshallers[datatypes.dateTimeQName.localName] = marshallDatetime;
+        marshallers[datatypes.timeQName.localName] = marshallDatetime;
+        marshallers[datatypes.dateQName.localName] = marshallDatetime;
+        marshallers[datatypes.gYearMonthQName.localName] = marshallGregorian;
+        marshallers[datatypes.gYearQName.localName] = marshallGregorian;
+        marshallers[datatypes.gMonthDayQName.localName] = marshallGregorian;
+        marshallers[datatypes.gDayQName.localName] = marshallGregorian;
+        marshallers[datatypes.gMonthQName.localName] = marshallGregorian;
+        marshallers[datatypes.hexBinaryQName.localName] = marshallHexBinary;
+        marshallers[datatypes.base64BinaryQName.localName] = marshallBase64Binary;
+        marshallers[datatypes.anyURIQName.localName] = marshallString;
+        marshallers[datatypes.QNameQName.localName] = marshallString;
+        marshallers[datatypes.NOTATIONQName.localName] = marshallString;
+
+        // Other built-in datatypes
+        marshallers[datatypes.normalizedStringQName.localName] = marshallString;
+        marshallers[datatypes.tokenQName.localName] = marshallString;
+        marshallers[datatypes.languageQName.localName] = marshallString;
+        marshallers[datatypes.NMTOKENQName.localName] = marshallString;
+        marshallers[datatypes.NMTOKENSQName.localName] = marshallString;
+        marshallers[datatypes.NameQName.localName] = marshallString;
+        marshallers[datatypes.NCNameQName.localName] = marshallString;
+        marshallers[datatypes.IDQName.localName] = marshallString;
+        marshallers[datatypes.IDREF.localName] = marshallString;
+        marshallers[datatypes.IDREFS.localName] = marshallString;
+        marshallers[datatypes.ENTITY.localName] = marshallString;
+        marshallers[datatypes.ENTITIES.localName] = marshallString;
+        marshallers[datatypes.integerQName.localName] = marshallInteger;
+        marshallers[datatypes.nonPositiveIntegerQName.localName] = marshallInteger;
+        marshallers[datatypes.negativeIntegerQName.localName] = marshallInteger;
+        marshallers[datatypes.longQName.localName] = marshallInteger;
+        marshallers[datatypes.intQName.localName] = marshallInteger;
+        marshallers[datatypes.shortQName.localName] = marshallInteger;
+        marshallers[datatypes.byteQName.localName] = marshallInteger;
+        marshallers[datatypes.nonNegativeIntegerQName.localName] = marshallInteger;
+        marshallers[datatypes.unsignedLongQName.localName] = marshallInteger;
+        marshallers[datatypes.unsignedIntQName.localName] = marshallInteger;
+        marshallers[datatypes.unsignedShortQName.localName] = marshallInteger;
+        marshallers[datatypes.unsignedByteQName.localName] = marshallInteger;
+        marshallers[datatypes.positiveIntegerQName.localName] = marshallInteger;
+        marshallers[datatypes.yearMonthDurationQName.localName] = marshallDatetime;
+        marshallers[datatypes.dayTimeDurationQName.localName] = marshallDatetime;
+
+        // 1999
+        if (datatypes.timeInstantQName != null)
+            marshallers[datatypes.timeInstantQName.localName] = marshallDatetime;
+
+
+        unmarshallers = {};
+        unmarshallers[datatypes.booleanQName.localName] = unmarshallBoolean;
+
+        // Special built-in schema datatypes
+        unmarshallers[datatypes.anyTypeQName.localName] = unmarshallAny;
+        unmarshallers[datatypes.anySimpleTypeQName.localName] = unmarshallAny;
+        unmarshallers[datatypes.anyAtomicTypeQName.localName] = unmarshallAny;
+
+        // Primitive datatypes
+        unmarshallers[datatypes.stringQName.localName] = unmarshallString;
+        unmarshallers[datatypes.booleanQName.localName] = unmarshallBoolean;
+        unmarshallers[datatypes.decimalQName.localName] = unmarshallDecimal;
+        unmarshallers[datatypes.precisionDecimal.localName] = unmarshallDecimal;
+        unmarshallers[datatypes.floatQName.localName] = unmarshallFloat;
+        unmarshallers[datatypes.doubleQName.localName] = unmarshallDouble;
+        unmarshallers[datatypes.durationQName.localName] = unmarshallDuration;
+        unmarshallers[datatypes.dateTimeQName.localName] = unmarshallDatetime;
+        unmarshallers[datatypes.timeQName.localName] = unmarshallDatetime;
+        unmarshallers[datatypes.dateQName.localName] = unmarshallDate;
+        unmarshallers[datatypes.gYearMonthQName.localName] = unmarshallGregorian;
+        unmarshallers[datatypes.gYearQName.localName] = unmarshallGregorian;
+        unmarshallers[datatypes.gMonthDayQName.localName] = unmarshallGregorian;
+        unmarshallers[datatypes.gDayQName.localName] = unmarshallGregorian;
+        unmarshallers[datatypes.gMonthQName.localName] = unmarshallGregorian;
+        unmarshallers[datatypes.hexBinaryQName.localName] = unmarshallHexBinary;
+        unmarshallers[datatypes.base64BinaryQName.localName] = unmarshallBase64Binary;
+        unmarshallers[datatypes.anyURIQName.localName] = unmarshallString;
+        unmarshallers[datatypes.QNameQName.localName] = unmarshallString;
+        unmarshallers[datatypes.NOTATIONQName.localName] = unmarshallString;
+
+        // Other built-in datatypes
+        unmarshallers[datatypes.normalizedStringQName.localName] = unmarshallString;
+        unmarshallers[datatypes.tokenQName.localName] = unmarshallString;
+        unmarshallers[datatypes.languageQName.localName] = unmarshallString;
+        unmarshallers[datatypes.NMTOKENQName.localName] = unmarshallString;
+        unmarshallers[datatypes.NMTOKENSQName.localName] = unmarshallString;
+        unmarshallers[datatypes.NameQName.localName] = unmarshallString;
+        unmarshallers[datatypes.NCNameQName.localName] = unmarshallString;
+        unmarshallers[datatypes.IDQName.localName] = unmarshallString;
+        unmarshallers[datatypes.IDREF.localName] = unmarshallString;
+        unmarshallers[datatypes.IDREFS.localName] = unmarshallString;
+        unmarshallers[datatypes.ENTITY.localName] = unmarshallString;
+        unmarshallers[datatypes.ENTITIES.localName] = unmarshallString;
+        unmarshallers[datatypes.integerQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.nonPositiveIntegerQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.negativeIntegerQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.longQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.intQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.shortQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.byteQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.nonNegativeIntegerQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.unsignedLongQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.unsignedIntQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.unsignedShortQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.unsignedByteQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.positiveIntegerQName.localName] = unmarshallInteger;
+        unmarshallers[datatypes.yearMonthDurationQName.localName] = unmarshallDatetime;
+        unmarshallers[datatypes.dayTimeDurationQName.localName] = unmarshallDatetime;
+
+        // 1999
+        if (datatypes.timeInstantQName != null)
+            unmarshallers[datatypes.timeInstantQName.localName] = unmarshallDatetime;
+    }
+
+    /**
+     * A Boolean flag to determines whether ActionScript ByteArrays should be
+     * serialized as base64Binary or hexBinary when specific XML Schema type
+     * information has not been provided. The default is true meaning
+     * base64Binary.
+     *
+     * @see flash.utils.ByteArray
+     */
+    public static var byteArrayAsBase64Binary:Boolean = true;
+
+    /**
+     * A RegEx pattern to help replace all whitespace characters in the content
+     * of certain simple types with #x20 (space) characters. The XML Schema
+     * specification defines whitespace as #x9 (tab), #xA (line feed) and
+     * #xD (carriage return).
+     */
+    public static var whitespaceReplacePattern:RegExp = new RegExp("[\t\r\n]", "g");
+
+    /**
+     * A RegEx pattern to help collapse all consecutive whitespace characters in
+     * the content of certain simple types to a single #x20 (space) character.
+     * The XML Schema specification defines whitespace as #x9 (tab),
+     * #xA (line feed) and #xD (carriage return).
+     */
+    public static var whitespaceCollapsePattern:RegExp = new RegExp("[ \t\r\n]+", "g");
+
+    /**
+     * A RegEx pattern to help trim all leading and trailing spaces in the
+     * content of certain simple types. For whitespace collapse,
+     * this RegEx is executed after the whitespaceCollapsePattern RegEx has
+     * been executed.
+     */
+    public static var whitespaceTrimPattern:RegExp = new RegExp("^[ ]+|[ ]+$", "g");
+
+    public static const FLOAT_MAX_VALUE:Number = (Math.pow(2, 24) - 1) * Math.pow(2, 104);
+    public static const FLOAT_MIN_VALUE:Number = Math.pow(2, -149);
+    public static const LONG_MAX_VALUE:Number = 9223372036854775807;
+    public static const LONG_MIN_VALUE:Number = -9223372036854775808;
+    public static const SHORT_MAX_VALUE:Number = 32767;
+    public static const SHORT_MIN_VALUE:Number = -32768;
+    public static const BYTE_MAX_VALUE:Number = 127;
+    public static const BYTE_MIN_VALUE:Number = -128;
+    public static const ULONG_MAX_VALUE:Number = 18446744073709551615;
+    public static const USHORT_MAX_VALUE:Number = 65535;
+    public static const UBYTE_MAX_VALUE:Number = 255;
+
+    private var marshallers:Object;
+    private var unmarshallers:Object;
+    private var constants:SchemaConstants;
+    private var datatypes:SchemaDatatypes;
+    private var _validating:Boolean;
+
+
+    /*
+        var elements:XMLList = definition.elements();
+        for each (var child:XML in elements)
+        {
+            // <enumeration>
+            if (child.name() == constants.enumerationTypeQName)
+            {
+                validated = validateEnumeration(value, definition);
+                if (validated)
+                {
+                    isType = true;
+                }
+                else
+                {
+                    throw new Error(tokenType.localName + " does not satisfy enumeration values");
+                }
+            }
+
+
+
+        }
+
+
+        //check for patterns
+        if (definition.child(schemaConstants.patternQName).length() > 0)
+        {
+            childXML = restrictionXML.child(schemaConstants.patternQName);
+            var pattern:String = childXML.@value;
+            var regEx:RegExp = new RegExp(pattern,"");
+            str = String(value);
+            if (regEx.test(str))
+            {
+                isType = true;
+            }
+            else
+            {
+                throw new Error(tokenType.localName + " does not satisfy the pattern");
+            }
+        }
+
+        //check for mininclusive and maxinclusive
+        if (restrictionXML.child(schemaConstants.maxInclusiveQName).length() > 0 ||
+           restrictionXML.child(schemaConstants.minInclusiveQName).length() > 0 )
+         {
+            var max:Number;
+            var min:Number;
+
+            if (restrictionXML.child(schemaConstants.maxInclusiveQName).length() > 0 &&
+                                  restrictionXML.child(schemaConstants.minInclusiveQName).length() > 0 )
+            {
+                for each (childXML in childXMLList)
+                {
+                    if (childXML.name() == schemaConstants.minInclusiveQName)
+                    {
+                        min = parseInt(childXML.@value);
+
+                    }
+                    if (childXML.name() == schemaConstants.maxInclusiveQName)
+                    {
+                        max = parseInt(childXML.@value);
+                    }
+                }
+                num = Number(value);
+                if (num >= min && num <= max)
+                {
+                    isType = true;
+                }
+                else
+                {
+                    throw new Error(num + " outside the range.");
+                }
+            }
+            else if (restrictionXML.child(schemaConstants.maxInclusiveQName).length() > 0)
+            {
+                childXML = restrictionXML.child(schemaConstants.maxInclusiveQName);
+                max = parseInt(childXML.@value);
+                num = Number(value);
+
+                if (num <= max)
+                {
+                    isType = true;
+                }
+                else
+                {
+                    throw new Error(num + " greater than maximum: " +  max);
+                }
+            }
+            else if (restrictionXML.child(schemaConstants.minInclusiveQName).length() > 0 )
+            {
+                childXML = restrictionXML.child(schemaConstants.minInclusiveQName);
+                min = parseInt(childXML.@value);
+                num = Number(value);
+
+                if (num >= min)
+                {
+                    isType = true;
+                }
+                else
+                {
+                    throw new Error(num + " less than minimum: " + min);
+                }
+            }
+
+         }
+         //check for max and min length
+         if (restrictionXML.child(schemaConstants.maxLengthQName).length() > 0 ||
+                                  restrictionXML.child(schemaConstants.minLengthQName).length() > 0)
+         {
+            var max1:Number;
+            var min1:Number;
+
+            if (restrictionXML.child(schemaConstants.maxLengthQName).length() > 0 &&
+                                  restrictionXML.child(schemaConstants.minLengthQName).length() > 0)
+            {
+                for each (childXML in childXMLList)
+                {
+                    if (childXML.name() == schemaConstants.minLengthQName)
+                    {
+                        min1 = parseInt(childXML.@value);
+                    }
+                    if (childXML.name() == schemaConstants.maxLengthQName)
+                    {
+                        max1 = parseInt(childXML.@value);
+                    }
+                }
+                str = String(value);
+                if (str.length >= min1 && str.length <= max1)
+                {
+                     isType = true;
+                }
+                else
+                {
+                    throw new Error(str + " outside the range.");
+                }
+            }
+            else if (restrictionXML.child(schemaConstants.maxLengthQName).length() > 0)
+            {
+                childXML = restrictionXML.child(schemaConstants.maxLengthQName);
+                max1 = parseInt(childXML.@value);
+                str = String(value);
+                if (str.length <= max1)
+                {
+                    isType = true;
+                }
+                else
+                {
+                    throw new Error(str + " greater than maximum: " +  max1);
+                }
+            }
+            else if (restrictionXML.child(schemaConstants.minLengthQName).length() > 0)
+            {
+                childXML = restrictionXML.child(schemaConstants.minLengthQName);
+                min1 = parseInt(childXML.@value);
+                str = String(value);
+                if (str.length >= min1)
+                {
+                    isType = true;
+                }
+                else
+                {
+                    throw new Error(str + " less than minimum: " + min1);
+                }
+            }
+         }
+
+         //check for length
+         if (restrictionXML.child(schemaConstants.lengthQName).length() > 0)
+         {
+            childXML = restrictionXML.child(schemaConstants.lengthQName);
+            num = parseInt(childXML.@value);
+            str = String(value);
+            if (str.length == num)
+            {
+                isType = true;
+            }
+            else
+            {
+                throw new Error(str + " lengths does not match");
+            }
+         }
+         if (isType == true)
+         {
+            var baseName:String = restrictionXML.@base;
+            var baseQName:QName = getQNameForPrefixedName(baseName, restrictionXML);
+            childBody = encodeBuiltInType(tokenType, baseQName, value);
+            var retChildBody:XMLList = new XMLList(childBody);
+            return retChildBody;
+         }
+         else if (isContent == true)
+         {
+             return retSequence;
+         }
+         else
+         {
+             throw new Error("Cannot be added to body tag");
+         }
+
+         return null;
+
+         //[FIXME] validate sequence, all etc
+
+
+   */
+
+   /*
+    // 1. get the enumeration type values and validate against the arguments
+    private function validateEnumeration(value:*, restrictionXML:XML):Boolean
+    {
+        var schemaConstants:SchemaConstants = schemaManager.schemaConstants;
+
+        var list:Array = [];
+        var i:uint;
+        var enumXMLList:XMLList = restrictionXML.child(schemaConstants.enumerationTypeQName);
+        var enumXML:XML;
+
+        //get the values in an array
+        for each (enumXML in enumXMLList)
+        {
+            var value1:String = enumXML.@value;
+            list.push(value1);
+        }
+
+        for (i = 0; i < list.length; i++)
+        {
+            if (value.toString() == list[i])
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+    */
+}
+
+}


Transferred from the Adobe Open Source Subversion Repository

Revision: 16563
Author:   clucier@adobe.com
Date:     2010-06-15T13:28:53.613737Z

Log Message:
-----------
Fix build.

Modified Paths:
--------------
/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/RichText.as

git-svn-id: 7146a936b37d36e51ea0d66f8acbe0b8281bc7b7@1280392 13f79535-47bb-0310-9956-ffa450edef68

--- frameworks/projects/spark/src/spark/components/RichText.as
+++ frameworks/projects/spark/src/spark/components/RichText.as
@@ -919,7 +919,7 @@
         if (_textFlow.flowComposer)
         {
             _textFlow.flowComposer.swfContext =
-                ISWFContext(getEmbeddedSwfContext);
+                ISWFContext(getEmbeddedFontContext());
         }

         return _textFlow;


Transferred from the Adobe Open Source Subversion Repository

Revision: 22261
Author:   jasonsj@adobe.com
Date:     2011-08-27T22:44:04.359747Z

Log Message:
-----------
Add soft keyboard handling to Scroller even when Application.resizeForSoftKeyboard=false. When a Scroller descendant receives focus, justify the element above the keyboard.

QE notes: Add new tests for Scroller using Application.resizeForSoftKeyboard=false.
Doc notes:
Bugs:
SDK-29800 Scroller doesn't auto-scroll if resizeForSoftKeyboard dynamically changed
Reviewer: jszeto, eday, egeorgie
Tests run: checkintests, mustella-mobile
Is noteworthy for integration: no

Modified Paths:
--------------
/private/flex/sdk/branches/4.y/frameworks/projects/spark/src/spark/components/Scroller.as

git-svn-id: 7146a936b37d36e51ea0d66f8acbe0b8281bc7b7@1280544 13f79535-47bb-0310-9956-ffa450edef68

--- frameworks/projects/spark/src/spark/components/Scroller.as
+++ frameworks/projects/spark/src/spark/components/Scroller.as
@@ -42,10 +42,8 @@
 import mx.events.PropertyChangeEvent;
 import mx.events.TouchInteractionEvent;
 import mx.managers.IFocusManagerComponent;
-import mx.styles.ISimpleStyleClient;
 import mx.styles.IStyleClient;

-import spark.components.ScrollSnappingMode;
 import spark.components.supportClasses.GroupBase;
 import spark.components.supportClasses.ScrollerLayout;
 import spark.components.supportClasses.SkinnableComponent;
@@ -1378,8 +1376,53 @@
             }

             // Scroll the element into view
+            var delta:Point = layout.getScrollPositionDeltaToAnyElement(element,
+                elementLocalBounds, entireElementVisible);

-            var delta:Point = layout.getScrollPositionDeltaToAnyElement(element, elementLocalBounds, entireElementVisible);
+            // Compute new delta if element is visible in the viewport bounds but is
+            // clipped/obscured by the soft keyboard
+            var topLevelApp:Application = FlexGlobals.topLevelApplication as Application;
+            var eltBounds:Rectangle;
+            var adjustForSoftKeyboard:Boolean = topLevelApp &&
+                                                (!topLevelApp.resizeForSoftKeyboard) &&
+                                                (stage.softKeyboardRect.height > 0);
+
+            if (adjustForSoftKeyboard)
+            {
+                eltBounds = layout.getChildElementBounds(element);
+
+                // Get keyboard y-position in the scroller's coordinates
+                var keyboardTopLocal:Number = this.globalToLocal(stage.softKeyboardRect.topLeft).y;
+                var scrollerHeight:Number = this.getLayoutBoundsHeight();
+
+                // Does the keyboard clip the scroller?
+                // Is the bottom of the element clipped or outside the visible
+                // scroller height?
+                if ((keyboardTopLocal >= 0) &&
+                    (keyboardTopLocal < scrollerHeight) &&
+                    ((eltBounds.bottom - viewport.verticalScrollPosition) > keyboardTopLocal))
+                {
+                    // Compute a new delta to accomodate the soft keyboard
+                    var dy:Number = 0;
+
+                    if (eltBounds.height > keyboardTopLocal)
+                    {
+                        // Top justify if the element is taller than the
+                        // scroller's visible height
+                        dy = eltBounds.top;
+                    }
+                    else
+                    {
+                        // Bottom justify the element
+                        dy = eltBounds.bottom - keyboardTopLocal;
+                    }
+
+                    var dx:Number = (delta) ? delta.x : 0;
+
+                    // account for current verticalScrollPosition
+                    delta = new Point(dx, dy - viewport.verticalScrollPosition);
+                }
+            }

             if (delta)
             {
@@ -1389,7 +1432,9 @@
                 // We only care about focusThickness if we are positioning the whole element
                 if (!elementLocalBounds)
                 {
-                    var eltBounds:Rectangle = layout.getChildElementBounds(element);
+                    if (!eltBounds)
+                        eltBounds = layout.getChildElementBounds(element);
+
                     var focusThickness:Number = 0;

                     if (element is IStyleClient)
@@ -1408,7 +1453,10 @@
                         else if (viewport.horizontalScrollPosition + width < eltBounds.right + focusThickness)
                             viewport.horizontalScrollPosition = eltBounds.right + focusThickness - width;
                     }
-                    snapContentScrollPosition();
+
+                    // Do not snap vertically when increasing the
+                    // verticalScrollPosition for the soft keyboard
+                    snapContentScrollPosition(true, !adjustForSoftKeyboard);
                 }

                 if (doValidateNow && viewport is UIComponent)
@@ -2587,12 +2635,9 @@
     {
         super.createChildren();

-        var topLevelApp:Application = FlexGlobals.topLevelApplication as Application;
-
         // Only listen for softKeyboardEvents if the
         // softKeyboardBehavior attribute in the application descriptor equals "none"
-        // Check that the top level app has resizeForSoftKeyboard == true
-        if (topLevelApp && topLevelApp.resizeForSoftKeyboard && Application.softKeyboardBehavior == "none")
+        if (Application.softKeyboardBehavior == "none")
         {
             addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATE,
                 softKeyboardActivateHandler, false,
@@ -3021,15 +3066,21 @@
      *  @private
      *  Snap the scroll positions to valid values.
      */
-    private function snapContentScrollPosition():void
-    {
-        viewport.horizontalScrollPosition = getSnappedPosition(
-            Math.min(Math.max(minHorizontalScrollPosition, viewport.horizontalScrollPosition), maxHorizontalScrollPosition),
-            HORIZONTAL_SCROLL_POSITION);
-
-        viewport.verticalScrollPosition = getSnappedPosition(
-            Math.min(Math.max(minVerticalScrollPosition, viewport.verticalScrollPosition), maxVerticalScrollPosition),
-            VERTICAL_SCROLL_POSITION);
+    private function snapContentScrollPosition(snapHorizontal:Boolean = true, snapVertical:Boolean = true):void
+    {
+        if (snapHorizontal)
+        {
+            viewport.horizontalScrollPosition = getSnappedPosition(
+                Math.min(Math.max(minHorizontalScrollPosition, viewport.horizontalScrollPosition), maxHorizontalScrollPosition),
+                HORIZONTAL_SCROLL_POSITION);
+        }
+
+        if (snapVertical)
+        {
+            viewport.verticalScrollPosition = getSnappedPosition(
+                Math.min(Math.max(minVerticalScrollPosition, viewport.verticalScrollPosition), maxVerticalScrollPosition),
+                VERTICAL_SCROLL_POSITION);
+        }
     }

     /**


Transferred from the Adobe Open Source Subversion Repository

Revision: 16665
Author:   rfrishbe@adobe.com
Date:     2010-06-24T22:40:15.397962Z

Log Message:
-----------
Initial checkin for touch-enabled Lists.  This has not been code reviewed or spec-approved.  There will be lots of changes that will occur with this, but this will allow people to easily create touch-enabled Lists

QE notes: None
Doc notes: None
Bugs: -
Reviewer: None - this will get reviewed later
Tests run: checkintests, full mustella run (some tests failed and were off by a few pixels)
Is noteworthy for integration: No

Modified Paths:
--------------
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/mobiletheme/defaults.css</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/List.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/framework/defaults.css</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/mobiletheme/src/MobileThemeClasses.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/supportClasses/ScrollerLayout.as</path>
<path kind="file" action="A">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/supportClasses/TouchScrollingEasing.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/mobiletheme/src/spark/skins/mobile/VScrollBarThumbSkin.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/supportClasses/GroupBase.as</path>
<path kind="file" action="A">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/events/TouchScrollEvent.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/framework/src/mx/core/UIComponent.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/RichEditableText.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/Scroller.as</path>
<path kind="file" action="A">/private/flex/sdk/branches/hero_private/frameworks/projects/mobiletheme/src/spark/skins/mobile/HScrollBarTrackSkin.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/supportClasses/TrackBase.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/core/IViewport.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/spark/src/spark/components/supportClasses/ButtonBase.as</path>
<path kind="file" action="M">/private/flex/sdk/branches/hero_private/frameworks/projects/framework/src/mx/core/ScrollPolicy.as</path>
<path kind="file" action="A">/private/flex/sdk/branches/hero_private/frameworks/projects/mobiletheme/src/spark/skins/mobile/HScrollBarThumbSkin.as</path>
<path kind="file" action="A">/private/flex/sdk/branches/hero_private/frameworks/projects/mobiletheme/src/spark/skins/mobile/HScrollBarSkin.as</path>

git-svn-id: 7146a936b37d36e51ea0d66f8acbe0b8281bc7b7@1280476 13f79535-47bb-0310-9956-ffa450edef68

--- frameworks/projects/spark/src/spark/components/Scroller.as
+++ frameworks/projects/spark/src/spark/components/Scroller.as
@@ -12,27 +12,35 @@

 package spark.components
 {
+import flash.display.InteractiveObject;
 import flash.events.Event;
 import flash.events.FocusEvent;
+import flash.events.KeyboardEvent;
 import flash.events.MouseEvent;
-import flash.events.KeyboardEvent;
+import flash.geom.Point;
 import flash.system.ApplicationDomain;
 import flash.text.TextField;
 import flash.ui.Keyboard;

-import spark.components.Group;
-import spark.components.supportClasses.ScrollerLayout;
-import spark.components.supportClasses.SkinnableComponent;
-import spark.core.IViewport;
-import spark.core.NavigationUnit;
 import mx.core.IInvalidating;
 import mx.core.IVisualElement;
 import mx.core.IVisualElementContainer;
 import mx.core.LayoutDirection;
 import mx.core.ScrollPolicy;
+import mx.events.EffectEvent;
 import mx.events.PropertyChangeEvent;
 import mx.managers.IFocusManagerComponent;
-import flash.display.InteractiveObject;
+
+import spark.components.Group;
+import spark.components.supportClasses.ScrollerLayout;
+import spark.components.supportClasses.SkinnableComponent;
+import spark.components.supportClasses.TouchScrollingEasing;
+import spark.core.IViewport;
+import spark.core.NavigationUnit;
+import spark.effects.Animate;
+import spark.effects.animation.MotionPath;
+import spark.effects.animation.SimpleMotionPath;
+import spark.events.TouchScrollEvent;

 include "../styles/metadata/BasicInheritingTextStyles.as"
 include "../styles/metadata/AdvancedInheritingTextStyles.as"
@@ -819,13 +827,270 @@
      */
     override public function styleChanged(styleProp:String):void
     {
+        super.styleChanged(styleProp);
+
         var allStyles:Boolean = (styleProp == null || styleProp == "styleName");
-        super.styleChanged(styleProp);
+
         if (allStyles || styleProp == "horizontalScrollPolicy" ||
             styleProp == "verticalScrollPolicy")
         {
             invalidateSkin();
         }
+
+        if (allStyles || styleProp == "inputMode")
+        {
+            if (getStyle("inputMode") == "touch")
+            {
+                installTouchListeners();
+
+                touchScrollHelper = new TouchScrollHelper(this);
+                touchScrollHelper.horizontalSlop = MIN_HORIZONTAL_SLOP;
+                touchScrollHelper.verticalSlop = MIN_VERTICAL_SLOP;
+                touchScrollHelper.diagonalSlop = MIN_DIAGONAL_SLOP;
+            }
+            else
+            {
+                uninstallTouchListeners();
+            }
+        }
+    }
+
+    /**
+     *  @private
+     */
+    private function installTouchListeners():void
+    {
+        addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
+        addEventListener(TouchScrollEvent.TOUCH_SCROLL_STARTING, touchScrollStartingHandler);
+        addEventListener(TouchScrollEvent.TOUCH_SCROLL_START, touchScrollStartHandler);
+        addEventListener(TouchScrollEvent.TOUCH_SCROLL_DRAG, touchScrollDragHandler);
+        addEventListener(TouchScrollEvent.TOUCH_SCROLL_END, touchScrollEndHandler);
+        addEventListener(TouchScrollEvent.TOUCH_SCROLL_THROW, touchScrollThrowHandler);
+    }
+
+    // ScrollerLayout vars
+    public var horizontalScrollInProgress:Boolean = false;
+    public var verticalScrollInProgress:Boolean = false;
+
+    // SLOP CONSTANTS
+    private static const MIN_HORIZONTAL_SLOP:int = 10;
+    private static const MIN_VERTICAL_SLOP:int = 10;
+    private static const MIN_DIAGONAL_SLOP:int = 10;
+
+    /**
+     *  @private
+     */
+    private function uninstallTouchListeners():void
+    {
+        removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
+        removeEventListener(TouchScrollEvent.TOUCH_SCROLL_STARTING, touchScrollStartingHandler);
+        removeEventListener(TouchScrollEvent.TOUCH_SCROLL_START, touchScrollStartHandler);
+        removeEventListener(TouchScrollEvent.TOUCH_SCROLL_END, touchScrollEndHandler);
+        removeEventListener(TouchScrollEvent.TOUCH_SCROLL_DRAG, touchScrollDragHandler);
+        removeEventListener(TouchScrollEvent.TOUCH_SCROLL_THROW, touchScrollThrowHandler);
+    }
+
+    private function touchScrollStartingHandler(event:TouchScrollEvent):void
+    {
+        if (event.scrollingObject != this)
+        {
+            touchScrollHelper.stopScrollWatch();
+        }
+    }
+
+    private function touchScrollStartHandler(event:TouchScrollEvent):void
+    {
+        installSwallowingMouseHandlers();
+        hspBeforeTouchScroll = viewport.horizontalScrollPosition;
+        vspBeforeTouchScroll = viewport.verticalScrollPosition;
+
+        if (viewport.scrollAxis == "horizontal" || viewport.scrollAxis == "diagonal")
+            horizontalScrollInProgress = true;
+        if (viewport.scrollAxis == "vertical" || viewport.scrollAxis == "diagonal")
+            verticalScrollInProgress = true;
+
+        // need to invaliadte the ScrollerLayout object so it'll update the
+        // scrollbars in overlay mode
+        skin.invalidateDisplayList();
+    }
+
+    private function installSwallowingMouseHandlers():void
+    {
+        // block click events from occuring on me
+        addEventListener(MouseEvent.CLICK, touchScrolling_captureMouseHandler, true);
+        addEventListener(MouseEvent.MOUSE_DOWN, touchScrolling_captureMouseHandler, true);
+//        addEventListener(MouseEvent.MOUSE_OVER, touchScrolling_captureMouseHandler, true);
+//        addEventListener(MouseEvent.ROLL_OVER, touchScrolling_captureMouseHandler, true);
+    }
+
+    private function uninstallSwallowingMouseHandlers():void
+    {
+        removeEventListener(MouseEvent.CLICK, touchScrolling_captureMouseHandler, true);
+        removeEventListener(MouseEvent.MOUSE_DOWN, touchScrolling_captureMouseHandler, true);
+//        removeEventListener(MouseEvent.MOUSE_OVER, touchScrolling_captureMouseHandler, true);
+//        removeEventListener(MouseEvent.ROLL_OVER, touchScrolling_captureMouseHandler, true);
+    }
+
+    private var stoppedPreemptively:Boolean = false;
+
+    private function touchScrolling_captureMouseHandler(event:MouseEvent):void
+    {
+        switch(event.type)
+        {
+            case MouseEvent.MOUSE_DOWN:
+                if (throwEffect && throwEffect.isPlaying)
+                {
+                    // stop the effect.  we don't want to move it to its final value...we want to stop it in place
+                    // FIXME (rfrishbe): however if it's below 0 or above maxVSP, we should snap it to these values
+                    stoppedPreemptively = true;
+                    throwEffect.stop();
+
+                    // get new values in case we start scrolling again
+                    hspBeforeTouchScroll = viewport.horizontalScrollPosition;
+                    vspBeforeTouchScroll = viewport.verticalScrollPosition;
+                }
+
+                touchScrollHelper.startScrollWatch(event);
+                event.stopImmediatePropagation();
+                break;
+            case MouseEvent.ROLL_OVER:
+            case MouseEvent.MOUSE_OVER:
+            case MouseEvent.CLICK:
+                event.stopImmediatePropagation();
+                break;
+        }
+    }
+
+    private function mouseDownHandler(event:MouseEvent):void
+    {
+        // FIXME (rfrishbe): if currently scrolling, we should stop scrolling
+        // and move the VSP/HSP in to range (0, maxVSP).
+
+        touchScrollHelper.startScrollWatch(event);
+    }
+
+    private var touchScrollHelper:TouchScrollHelper;
+    private var hspBeforeTouchScroll:Number;
+    private var vspBeforeTouchScroll:Number;
+
+    private function touchScrollDragHandler(event:TouchScrollEvent):void
+    {
+        var xMove:int = 0;
+        var yMove:int = 0;
+
+        if (viewport.scrollAxis == "horizontal" || viewport.scrollAxis == "diagonal")
+            xMove = event.dragX;
+
+        if (viewport.scrollAxis == "vertical" || viewport.scrollAxis == "diagonal")
+            yMove = event.dragY
+
+        // FIXME (rfrishbe): figure out how we want negative signs to work
+        viewport.horizontalScrollPosition = hspBeforeTouchScroll - xMove;
+        viewport.verticalScrollPosition = vspBeforeTouchScroll - yMove;
+    }
+
+    private function touchScrollEndHandler(event:TouchScrollEvent):void
+    {
+        horizontalScrollInProgress = false;
+        verticalScrollInProgress = false;
+        uninstallSwallowingMouseHandlers();
+
+        // need to invaliadte the ScrollerLayout object so it'll update the
+        // scrollbars in overlay mode
+        skin.invalidateDisplayList();
+    }
+
+    private var throwEaser:TouchScrollingEasing;
+    private var EFFECT_TIME:int = 3000;
+    private var throwEffect:Animate;
+
+    private function setUpThrowEffect(velocity:Point):void
+    {
+        if (!throwEffect)
+        {
+            throwEffect = new Animate();
+            throwEffect.addEventListener(EffectEvent.EFFECT_END, throwEffect_effectEndHandler);
+
+            throwEaser = new TouchScrollingEasing(0);
+        }
+        var finalHSP:Number = viewport.horizontalScrollPosition;
+        var finalVSP:Number = viewport.verticalScrollPosition;
+
+        // want velocity to be 0 and to decrease by currentFriction every
+        // SCROLLING_TIMER_DELAY over EFFECT_TIME total
+        var decelerationRate:Point = velocity.clone();
+        decelerationRate.x = decelerationRate.x/EFFECT_TIME;
+        decelerationRate.y = decelerationRate.y/EFFECT_TIME;
+
+        // figure out where we're scrolling to
+        if (viewport.scrollAxis == "horizontal" || viewport.scrollAxis == "diagonal")
+        {
+            var hsp:Number = viewport.horizontalScrollPosition;
+            var viewportWidth:Number = isNaN(viewport.width) ? 0 : viewport.width;
+            var cWidth:Number = viewport.contentWidth;
+            var maxWidth:Number = Math.max(0, (cWidth == 0) ? viewport.horizontalScrollPosition : cWidth - viewportWidth);
+
+            // initialVelocity.y * ellapsedTime - (0.5 * frictionToUse * ellapsedTime * ellapsedTime);
+            finalHSP = viewport.horizontalScrollPosition - (velocity.x * EFFECT_TIME) + (.5 * decelerationRate.x * EFFECT_TIME * EFFECT_TIME);
+//            finalHSP = Math.max(0, Math.min(finalHSP, maxWidth));
+
+        }
+
+        if (viewport.scrollAxis == "vertical" || viewport.scrollAxis == "diagonal")
+        {
+            var vsp:Number = viewport.verticalScrollPosition;
+            var viewportHeight:Number = isNaN(viewport.height) ? 0 : viewport.height;
+            var cHeight:Number = viewport.contentHeight;
+            var maxHeight:Number = Math.max(0, (cHeight == 0) ? viewport.verticalScrollPosition : cHeight - viewportHeight);
+
+            // initialVelocity.y * ellapsedTime - (0.5 * frictionToUse * ellapsedTime * ellapsedTime);
+            finalVSP = viewport.verticalScrollPosition - (velocity.y * EFFECT_TIME) + (.5 * decelerationRate.y * EFFECT_TIME * EFFECT_TIME);
+//            finalVSP = Math.max(0, Math.min(finalVSP, maxHeight));
+        }
+
+        // set up easer.  Sometimes these values won't be exactly right, but it shouldn't
+        // matter if maxHeight is wrong as long as we aren't actually moving it around
+        // in that axis.
+        throwEaser.velocity = velocity;
+        throwEaser.maxHeight = maxHeight;
+        throwEaser.maxWidth = maxWidth;
+        throwEaser.maxExtraY = viewportHeight;
+        throwEaser.maxExtraX = viewportWidth;
+
+        // FIXME (rfrishbe): could move some of this to a setup function to do only once
+        // set up throw effect
+        throwEffect.target = viewport;
+        throwEffect.duration = EFFECT_TIME;
+
+        // maybe use motion paths with more keyframes for the bounce effect??
+        var horizontalMP:MotionPath = new SimpleMotionPath("horizontalScrollPosition", hsp, finalHSP);
+        var verticalMP:MotionPath = new SimpleMotionPath("verticalScrollPosition", vsp, finalVSP);
+
+        if (viewport.scrollAxis == "diagonal")
+            throwEffect.motionPaths = Vector.<MotionPath>([horizontalMP, verticalMP]);
+        else if (viewport.scrollAxis == "horizontal")
+            throwEffect.motionPaths = Vector.<MotionPath>([horizontalMP]);
+        else if (viewport.scrollAxis == "vertical")
+            throwEffect.motionPaths = Vector.<MotionPath>([verticalMP]);
+
+        throwEffect.easer = throwEaser;
+    }
+
+    private function throwEffect_effectEndHandler(event:EffectEvent):void
+    {
+        // if we stopped the effect ourself (because someone pressed down), then let's not consider
+        // this the end
+        if (stoppedPreemptively)
+            return;
+
+        dispatchEvent(new TouchScrollEvent(TouchScrollEvent.TOUCH_SCROLL_THROW_ANIMATION_END));
+    }
+
+    private function touchScrollThrowHandler(event:TouchScrollEvent):void
+    {
+        stoppedPreemptively = false;
+        setUpThrowEffect(event.throwVelocity);
+        throwEffect.play();
     }

     /**
@@ -1028,3 +1293,349 @@
 }

 }
+
+import flash.display.DisplayObject;
+import flash.events.Event;
+import flash.events.MouseEvent;
+import flash.events.TouchEvent;
+import flash.geom.Point;
+import flash.utils.getTimer;
+
+import mx.events.SandboxMouseEvent;
+
+import spark.components.Scroller;
+import spark.events.TouchScrollEvent;
+
+class TouchScrollHelper
+{
+    private static const HISTORY:int = 5;
+    private static const MIN_START_VELOCITY:Number = 0.05;
+
+    private static function calculateVelocity(clickHistory:Vector.<Point>, timeHistory:Vector.<int>):Vector.<Point>
+    {
+        var len:int = clickHistory.length - 1;
+        if (len <= 0)
+
+            return null;
+
+        var velocities:Vector.<Point> = new Vector.<Point>(len);
+
+        for (var i:int = 0; i < len; i++)
+        {
+            var point1:Point = clickHistory[i];
+            var point2:Point = clickHistory[i+1];
+            var distance:Point = point2.subtract(point1);
+            var time:Number = timeHistory[i+1] - timeHistory[i];
+            velocities[i] = new Point(distance.x/time, distance.y/time);
+        }
+
+        return velocities;
+    }
+
+    private static function averagePoints(points:Vector.<Point>, weighted:Vector.<Number>):Point
+    {
+        var len:int = points.length;
+
+        var currentPoint:Point = new Point(0, 0);
+        var totalWeight:Number = 0;
+
+        for (var i:int = 0; i < len; i++)
+        {
+            var point:Point = new Point(points[i].x, points[i].y);
+            totalWeight += weighted[i];
+            point.x *= weighted[i];
+            point.y *= weighted[i];
+            currentPoint.x += point.x;
+            currentPoint.y += point.y;
+        }
+
+        currentPoint.x = currentPoint.x/totalWeight;
+        currentPoint.y = currentPoint.y/totalWeight;
+
+        return currentPoint;
+    }
+
+    public function TouchScrollHelper(scroller:Scroller)
+    {
+        super();
+
+        clickHistory = new Vector.<Point>();
+        timeHistory = new Vector.<int>();
+
+        this.scroller = scroller;
+    }
+
+    public var horizontalSlop:Number;
+    public var verticalSlop:Number;
+    public var diagonalSlop:Number;
+    public var scroller:Scroller;
+
+    // each of these per mouseEvent (or per touchPoint ID)
+    private var mouseDownedPoint:Point;
+    private var scrollStartPoint:Point;
+    private var startTime:Number;
+    private var mouseDownedDisplayObject:DisplayObject;
+    private var clickHistory:Vector.<Point>;
+    private var timeHistory:Vector.<int>;
+    private var isScrolling:Boolean;
+
+    public function startScrollWatch(event:Event):void
+    {
+        startTime = flash.utils.getTimer();
+
+        if (event is MouseEvent && event.type == MouseEvent.MOUSE_DOWN)
+        {
+            var mouseEvent:MouseEvent = event as MouseEvent;
+
+            if (!isScrolling)
+            {
+                this.mouseDownedDisplayObject = mouseEvent.target as DisplayObject;
+
+                mouseDownedPoint = new Point(mouseEvent.stageX, mouseEvent.stageY);
+            }
+
+            installMouseListeners();
+
+            // if we were already scrolling, continue scrolling
+            if (isScrolling)
+            {
+                // FIXME (rfrishbe): below is same as in mouseMove...should it be?
+//                var scrollDragStartEvent:TouchScrollEvent = new TouchScrollEvent(TouchScrollEvent.TOUCH_SCROLL_START);
+//                scrollDragStartEvent.scrollingObject = scroller;
+//                scroller.dispatchEvent(scrollDragStartEvent);
+                scrollStartPoint = new Point(mouseEvent.stageX, mouseEvent.stageY);
+            }
+
+            // only need to do this stuff once per target
+            // store last 5 values
+            clickHistory.length = 0;
+            timeHistory.length = 0;
+
+            addMouseEventHistory(mouseEvent);
+        }
+        else if (event is TouchEvent && event.type == TouchEvent.TOUCH_BEGIN)
+        {
+            // TouchEvent case
+        }
+    }
+
+    public function stopScrollWatch():void
+    {
+        uninstallMouseListeners();
+    }
+
+    private function addMouseEventHistory(event:MouseEvent):Point
+    {
+        var p:Point = new Point(event.stageX, event.stageY);
+        var differencePoint:Point = p.subtract(mouseDownedPoint);
+
+        if (clickHistory.length >= HISTORY)
+        {
+            clickHistory.shift();
+            timeHistory.shift();
+        }
+
+        clickHistory.push(differencePoint);
+        timeHistory.push(flash.utils.getTimer() - startTime);
+
+        return differencePoint;
+    }
+
+    // event listener helpers
+    private function installMouseListeners():void
+    {
+        var sbRoot:DisplayObject = scroller.systemManager.getSandboxRoot();
+
+        sbRoot.addEventListener(MouseEvent.MOUSE_MOVE, sbRoot_mouseMoveHandler, true);
+        sbRoot.addEventListener(MouseEvent.MOUSE_UP, sbRoot_mouseUpHandler, true);
+        sbRoot.addEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, sbRoot_mouseUpHandler);
+
+        scroller.systemManager.deployMouseShields(true);
+    }
+
+    private function uninstallMouseListeners():void
+    {
+        var sbRoot:DisplayObject = scroller.systemManager.getSandboxRoot();
+
+        // mouse events added in installMouseListeners()
+        sbRoot.removeEventListener(MouseEvent.MOUSE_MOVE, sbRoot_mouseMoveHandler, true);
+        sbRoot.removeEventListener(MouseEvent.MOUSE_UP, sbRoot_mouseUpHandler, true);
+        sbRoot.removeEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, sbRoot_mouseUpHandler);
+
+        scroller.systemManager.deployMouseShields(false);
+    }
+
+    // event listeners
+    /**
+     *  @private
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function sbRoot_mouseMoveHandler(event:MouseEvent):void
+    {
+        var mouseDownedDifference:Point = addMouseEventHistory(event);
+
+        if (!isScrolling)
+        {
+            var shouldBeScrolling:Boolean = false;
+
+            switch (scroller.viewport.scrollAxis)
+            {
+                case "vertical":
+                    shouldBeScrolling = Math.abs(mouseDownedDifference.y) >= verticalSlop;
+                    break;
+                case "horizontal":
+                    shouldBeScrolling = Math.abs(mouseDownedDifference.x) >= horizontalSlop;
+                    break;
+                case "diagonal":
+                    shouldBeScrolling = Math.abs(mouseDownedDifference.length) >= diagonalSlop;
+                    break;
+            }
+
+            // Check to see if we should be scrolling
+            if (shouldBeScrolling)
+            {
+                // cancellable and bubbling event
+                var scrollDragStartingEvent:TouchScrollEvent = new TouchScrollEvent(TouchScrollEvent.TOUCH_SCROLL_STARTING, true, true);
+                scrollDragStartingEvent.scrollingObject = scroller;
+                var eventAccepted:Boolean = mouseDownedDisplayObject.dispatchEvent(scrollDragStartingEvent);
+
+                if (!eventAccepted)
+                {
+                    // TODO (rfrishbe): do we need to call updateAfterEvent() here and below?
+                    event.updateAfterEvent();
+
+                    // if our mouse target has been rejected, let's give up and remove all the listeners
+                    uninstallMouseListeners();
+
+                    return;
+                }
+
+                var scrollDragStartEvent:TouchScrollEvent = new TouchScrollEvent(TouchScrollEvent.TOUCH_SCROLL_START);
+                scrollDragStartEvent.scrollingObject = scroller;
+                scroller.dispatchEvent(scrollDragStartEvent);
+
+                // FIXME (rfrishbe): the difference should not be from the original point but from the slop.
+                // otherwise we "jump" on the first move.
+                // we should reset startPoint here to be this point minus slop (in the direction that caused the scroll)
+                scrollStartPoint = new Point(event.stageX, event.stageY);
+                isScrolling = true;
+
+                // velocity calculations come from mouseDownedPoint.  The drag ones com from scrollStartPoint.
+            }
+
+
+        }
+
+        // if we are scrolling
+        if (isScrolling)
+        {
+            var p:Point = new Point(event.stageX, event.stageY);
+            var scrollDifferencePoint:Point = p.subtract(scrollStartPoint);
+
+            var scrollDragEvent:TouchScrollEvent = new TouchScrollEvent(TouchScrollEvent.TOUCH_SCROLL_DRAG, false, false);
+            scrollDragEvent.scrollingObject = scroller;
+            scrollDragEvent.dragX = scrollDifferencePoint.x;
+            scrollDragEvent.dragY = scrollDifferencePoint.y;
+
+            scroller.dispatchEvent(scrollDragEvent);
+            event.updateAfterEvent();
+        }
+    }
+
+    /**
+     *  @private
+     *  Called when the user releases the TitleWindow.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function sbRoot_mouseUpHandler(event:Event):void
+    {
+        uninstallMouseListeners();
+
+        // FIXME (rfrishbe): can we scroll by velocity even tho no drag took place?
+        if (!isScrolling)
+            return;
+
+        if (event is MouseEvent)
+            addMouseEventHistory(event as MouseEvent);
+
+        // decide about throw
+
+        // pad click and timeHistory if needed
+        var currentTime:Number = getTimer();
+
+        // calculate average time b/w events and see if the last two (mouseMove and this mouseUp)
+        // were far apart.  If they were, then don't do anything if the velocity of them is small.
+        var averageTime:Number = 0;
+        var len:int = timeHistory.length;
+
+        // FIXME (rfrishbe): this isn't true sine we could get a mouseUpSomehwere event
+        // gauranteed to have 2 mouse events b/c atleast a mousedown and a mouse up
+
+        for (var i:int = 0; i < len - 2; i++)
+        {
+            averageTime += timeHistory[i+1] - timeHistory[i];
+        }
+        averageTime /= len-2;
+
+        var lastTime:Number = timeHistory[len-1] - timeHistory[len-2];
+        var lastVelocity:Point = clickHistory[len-1].subtract(clickHistory[len-2]);
+        lastVelocity.x /= lastTime;
+        lastVelocity.y /= lastTime;
+
+        // FIXME (rfrishbe): should be minXVelocity, minYVelocity, minDiagonalVelocity
+        // FIXME (rfrishbe): this should be parameterized better
+        if ( (lastTime >= 3*averageTime) &&
+            (lastVelocity.length <= MIN_START_VELOCITY))
+        {
+            isScrolling = false;
+
+            // don't do anything
+            var scrollEndEvent:TouchScrollEvent = new TouchScrollEvent(TouchScrollEvent.TOUCH_SCROLL_END);
+            scrollEndEvent.scrollingObject = scroller;
+            scroller.dispatchEvent(scrollEndEvent);
+            return;
+        }
+
+
+        var velocities:Vector.<Point> = calculateVelocity(clickHistory, timeHistory);
+
+        var velocityWeights:Vector.<Number> = Vector.<Number>([1,1.33,1.66,2]);
+        var throwVelocity:Point = averagePoints(velocities, velocityWeights);
+
+        //            trace("velocity: ", initialVelocity.y);
+        if (throwVelocity.length > MIN_START_VELOCITY)
+        {
+            var scrollEndAndThrowEvent:TouchScrollEvent = new TouchScrollEvent(TouchScrollEvent.TOUCH_SCROLL_THROW, false, false);
+            scrollEndAndThrowEvent.scrollingObject = scroller;
+            scrollEndAndThrowEvent.throwVelocity = throwVelocity;
+
+            scroller.addEventListener(TouchScrollEvent.TOUCH_SCROLL_THROW_ANIMATION_END, scroller_touchScrollThrowAnimationEnd);
+            scroller.dispatchEvent(scrollEndAndThrowEvent);
+        }
+
+        // if we own this user gesture, don't let others see this event
+//        if (isScrolling)
+//            event.stopImmediatePropagation();
+    }
+
+    private function scroller_touchScrollThrowAnimationEnd(event:TouchScrollEvent):void
+    {
+        scroller.removeEventListener(TouchScrollEvent.TOUCH_SCROLL_THROW_ANIMATION_END, scroller_touchScrollThrowAnimationEnd);
+
+        isScrolling = false;
+
+        var scrollEndEvent:TouchScrollEvent = new TouchScrollEvent(TouchScrollEvent.TOUCH_SCROLL_END);
+        scrollEndEvent.scrollingObject = scroller;
+        scroller.dispatchEvent(scrollEndEvent);
+    }
+
+
+}