////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package mx.formatters
{

import flash.events.Event;

import mx.core.mx_internal;
import mx.resources.IResourceManager;
import mx.resources.ResourceManager;

use namespace mx_internal;

[ResourceBundle("formatters")]
[ResourceBundle("SharedResources")]

/**
 *  The DateBase class contains the localized string information
 *  used by the mx.formatters.DateFormatter class and the parsing function
 *  that renders the pattern.
 *  This is a helper class for the DateFormatter class that is not usually
 *  used independently.
 *
 *  @see mx.formatters.DateFormatter
 *
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
public class DateBase
{
    include "../core/Version.as";

    //--------------------------------------------------------------------------
    //
    //  Class variables
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private static var initialized:Boolean = false;

    /**
     *  @private
     *  Storage for the resourceManager getter.
     *  This gets initialized on first access,
     *  not at static initialization time, in order to ensure
     *  that the Singleton registry has already been initialized.
     */
    private static var _resourceManager:IResourceManager;

    /**
     *  @private
     *  A reference to the object which manages
     *  all of the application's localized resources.
     *  This is a singleton instance which implements
     *  the IResourceManager interface.
     */
    private static function get resourceManager():IResourceManager
    {
        if (!_resourceManager)
            _resourceManager = ResourceManager.getInstance();

        return _resourceManager;
    }

    //--------------------------------------------------------------------------
    //
    //  Class properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  dayNamesLong
    //----------------------------------

    /**
     *  @private
     *  Storage for the dayNamesLong property.
     */
    private static var _dayNamesLong:Array; /* of String */

    /**
     *  @private
     */
    private static var dayNamesLongOverride:Array; /* of String */

    /**
     *  Long format of day names.
     *
     *  @default ["Sunday", "Monday", "Tuesday", "Wednesday",
     *  "Thursday", "Friday", "Saturday"]
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function get dayNamesLong():Array /* of String */
    {
        initialize();

        return _dayNamesLong;
    }

    /**
     *  @private
     */
    public static function set dayNamesLong(value:Array /* of String*/):void
    {
        dayNamesLongOverride = value;

        _dayNamesLong = value != null ?
                        value :
                        resourceManager.getStringArray(
                            "SharedResources", "dayNames");
    }

    //----------------------------------
    //  dayNamesShort
    //----------------------------------

    /**
     *  @private
     *  Storage for the dayNamesShort property.
     */
    private static var _dayNamesShort:Array; /* of String */

    /**
     *  @private
     */
    private static var dayNamesShortOverride:Array; /* of String */

    /**
     *  Short format of day names.
     *
     *  @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function get dayNamesShort():Array /* of String */
    {
        initialize();

        return _dayNamesShort;
    }

    /**
     *  @private
     */
    public static function set dayNamesShort(value:Array /* of String*/):void
    {
        dayNamesShortOverride = value;

        _dayNamesShort = value != null ?
                         value :
                         resourceManager.getStringArray(
                             "formatters", "dayNamesShort");
    }

    //----------------------------------
    //  defaultStringKey
    //----------------------------------

    /**
     *  @private
     */
    mx_internal static function get defaultStringKey():Array /* of String */
    {
        initialize();

        return monthNamesLong.concat(timeOfDay);
    }

    //----------------------------------
    //  monthNamesLong
    //----------------------------------

    /**
     *  @private
     *  Storage for the monthNamesLong property.
     */
    private static var _monthNamesLong:Array; /* of String */

    /**
     *  @private
     */
    private static var monthNamesLongOverride:Array; /* of String */

    /**
     *  Long format of month names.
     *
     *  @default ["January", "February", "March", "April", "May", "June",
     *  "July", "August", "September", "October", "November", "December"].
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function get monthNamesLong():Array /* of String */
    {
        initialize();

        return _monthNamesLong;
    }

    /**
     *  @private
     */
    public static function set monthNamesLong(value:Array /* of String*/):void
    {
        monthNamesLongOverride = value;

        _monthNamesLong = value != null ?
                          value :
                          resourceManager.getStringArray(
                              "SharedResources", "monthNames");

        if (value == null)
        {
            // Currently there is no way to get a null
            // string from resourceBundles using getString
            // Hence monthSymbol is a space in English, but
            // we actually want it to be a null string.
            var monthSymbol:String = resourceManager.getString(
                "SharedResources", "monthSymbol");
            if (monthSymbol != " ")
            {
                // _monthNamesLong will be null if there are no resources.
                var n:int = _monthNamesLong ? _monthNamesLong.length : 0;
                for (var i:int = 0; i < n; i++)
                {
                    _monthNamesLong[i] += monthSymbol;
                }
            }
        }
    }

    //----------------------------------
    //  monthNamesShort
    //----------------------------------

    /**
     *  @private
     *  Storage for the monthNamesShort property.
     */
    private static var _monthNamesShort:Array; /* of String */

    /**
     *  @private
     */
    private static var monthNamesShortOverride:Array; /* of String */

    /**
     *  Short format of month names.
     *
     *  @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
     *  "Jul", "Aug", "Sep", "Oct","Nov", "Dec"]
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function get monthNamesShort():Array /* of String */
    {
        initialize();

        return _monthNamesShort;
    }

    /**
     *  @private
     */
    public static function set monthNamesShort(value:Array /* of String*/):void
    {
        monthNamesShortOverride = value;

        _monthNamesShort = value != null ?
                           value :
                           resourceManager.getStringArray(
                               "formatters", "monthNamesShort");
        if (value == null)
        {
            // Currently there is no way to get a null
            // string from resourceBundles using getString
            // Hence monthSymbol is a space in English, but
            // we actually want it to be a null string.
            var monthSymbol:String = resourceManager.getString(
                "SharedResources", "monthSymbol");
            if (monthSymbol != " ")
            {
                // _monthNamesShort will be null if there are no resources.
                var n:int = _monthNamesShort ? _monthNamesShort.length : 0;
                for (var i:int = 0; i < n; i++)
                {
                    _monthNamesShort[i] += monthSymbol;
                }
            }
        }
    }

    //----------------------------------
    //  timeOfDay
    //----------------------------------

    /**
     *  @private
     *  Storage for the timeOfDay property.
     */
    private static var _timeOfDay:Array; /* of String */

    /**
     *  @private
     */
    private static var timeOfDayOverride:Array; /* of String */

    /**
     *  Time of day names.
     *
     *  @default ["AM", "PM"]
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function get timeOfDay():Array /* of String */
    {
        initialize();

        return _timeOfDay;
    }

    /**
     *  @private
     */
    public static function set timeOfDay(value:Array /* of String */):void
    {
        timeOfDayOverride = value;

        var am:String = resourceManager.getString("formatters", "am");
        var pm:String = resourceManager.getString("formatters", "pm");

        _timeOfDay = value != null ? value : [ am, pm ];
    }

    //--------------------------------------------------------------------------
    //
    //  Class methods
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private static function initialize():void
    {
        if (!initialized)
        {
            // Register as a weak listener for "change" events
            // from ResourceManager.
            resourceManager.addEventListener(
                Event.CHANGE, static_resourceManager_changeHandler,
                false, 0, true);

            static_resourcesChanged();

            initialized = true;
        }
    }

    /**
     *  @private
     */
    private static function static_resourcesChanged():void
    {
        dayNamesLong = dayNamesLongOverride;
        dayNamesShort = dayNamesShortOverride;
        monthNamesLong = monthNamesLongOverride;
        monthNamesShort = monthNamesShortOverride;
        timeOfDay = timeOfDayOverride;
    }

    /**
     *  @private
     *  Parses token objects and renders the elements of the formatted String.
     *  For details about token objects, see StringFormatter.
     *
     *  @param date Date object.
     *
     *  @param tokenInfo Array object that contains token object descriptions.
     *
     *  @return Formatted string.
     */
    mx_internal static function extractTokenDate(date:Date,
                                            tokenInfo:Object):String
    {
        initialize();

        var result:String = "";

        var key:int = int(tokenInfo.end) - int(tokenInfo.begin);

        var day:int;
        var hours:int;

        switch (tokenInfo.token)
        {
            case "Y":
            {
                // year
                var year:String = date.getFullYear().toString();
                if (key < 3)
                    return year.substr(2);
                else if (key > 4)
                    return setValue(Number(year), key);
                else
                    return year;
            }

            case "M":
            {
                // month in year
                var month:int = int(date.getMonth());
                if (key < 3)
                {
                    month++; // zero based
                    result += setValue(month, key);
                    return result;
                }
                else if (key == 3)
                {
                    return monthNamesShort[month];
                }
                else
                {
                    return monthNamesLong[month];
                }
            }

            case "D":
            {
                // day in month
                day = int(date.getDate());
                result += setValue(day, key);
                return result;
            }

            case "E":
            {
                // day in the week
                day = int(date.getDay());
                if (key < 3)
                {
                    result += setValue(day, key);
                    return result;
                }
                else if (key == 3)
                {
                    return dayNamesShort[day];
                }
                else
                {
                    return dayNamesLong[day];
                }
            }

            case "A":
            {
                // am/pm marker
                hours = int(date.getHours());
                if (hours < 12)
                    return timeOfDay[0];
                else
                    return timeOfDay[1];
            }

            case "H":
            {
                // hour in day (1-24)
                hours = int(date.getHours());
                if (hours == 0)
                    hours = 24;
                result += setValue(hours, key);
                return result;
            }

            case "J":
            {
                // hour in day (0-23)
                hours = int(date.getHours());
                result += setValue(hours, key);
                return result;
            }

            case "K":
            {
                // hour in am/pm (0-11)
                hours = int(date.getHours());
                if (hours >= 12)
                    hours = hours - 12;
                result += setValue(hours, key);
                return result;
            }

            case "L":
            {
                // hour in am/pm (1-12)
                hours = int(date.getHours());
                if (hours == 0)
                    hours = 12;
                else if (hours > 12)
                    hours = hours - 12;
                result += setValue(hours, key);
                return result;
            }

            case "N":
            {
                // minutes in hour
                var mins:int = int(date.getMinutes());
                result += setValue(mins, key);
                return result;
            }

            case "S":
            {
                // seconds in minute
                var sec:int = int(date.getSeconds());
                result += setValue(sec, key);
                return result;
            }

			case "Q":
			{
				// milliseconds in second
				var ms:int = int(date.getMilliseconds());
				result += setValue(ms, key);
				return result;
			}

			case "Z":
			case "O":
			{
				// timezone offset
				var offset:int = int(date.timezoneOffset);
				hours = offset/60;
				mins = offset - hours*60;
				var tzStr:String = "";

				if (tokenInfo.token == "Z")
					tzStr = "Z";

				if (key < 2 && mins == 0)
					tzStr += setValue(hours, key);
				else if (key < 3)
					tzStr += setValue(hours, key) + ":" + setValue(mins, key);

				if (offset >= 0)
					tzStr = "+" + tzStr;

				result += tzStr;

				return result;
			}
        }

        return result;
    }

    /**
     *  @private
     *  Makes a given length of digits longer by padding with zeroes.
     *
     *  @param value Value to pad.
     *
     *  @param key Length of the string to pad.
     *
     *  @return Formatted string.
     */
    private static function setValue(value:Object, key:int):String
    {
        var result:String = "";

        var vLen:int = value.toString().length;
        if (vLen < key)
        {
            var n:int = key - vLen;
            for (var i:int = 0; i < n; i++)
            {
                result += "0"
            }
        }

        result += value.toString();

        return result;
    }

    //--------------------------------------------------------------------------
    //
    //  Class event handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private static function static_resourceManager_changeHandler(event:Event):void
    {
        static_resourcesChanged();
    }
}

}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package mx.formatters
{

import flash.events.Event;

import mx.core.mx_internal;
import mx.resources.IResourceManager;
import mx.resources.ResourceManager;

use namespace mx_internal;

[ResourceBundle("formatters")]
[ResourceBundle("SharedResources")]

/**
 *  The DateBase class contains the localized string information
 *  used by the mx.formatters.DateFormatter class and the parsing function
 *  that renders the pattern.
 *  This is a helper class for the DateFormatter class that is not usually
 *  used independently.
 *
 *  @see mx.formatters.DateFormatter
 *
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
public class DateBase
{
    include "../core/Version.as";

    //--------------------------------------------------------------------------
    //
    //  Class variables
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private static var initialized:Boolean = false;

    /**
     *  @private
     *  Storage for the resourceManager getter.
     *  This gets initialized on first access,
     *  not at static initialization time, in order to ensure
     *  that the Singleton registry has already been initialized.
     */
    private static var _resourceManager:IResourceManager;

    /**
     *  @private
     *  A reference to the object which manages
     *  all of the application's localized resources.
     *  This is a singleton instance which implements
     *  the IResourceManager interface.
     */
    private static function get resourceManager():IResourceManager
    {
        if (!_resourceManager)
            _resourceManager = ResourceManager.getInstance();

        return _resourceManager;
    }

    //--------------------------------------------------------------------------
    //
    //  Class properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  dayNamesLong
    //----------------------------------

    /**
     *  @private
     *  Storage for the dayNamesLong property.
     */
    private static var _dayNamesLong:Array; /* of String */

    /**
     *  @private
     */
    private static var dayNamesLongOverride:Array; /* of String */

    /**
     *  Long format of day names.
     *
     *  @default ["Sunday", "Monday", "Tuesday", "Wednesday",
     *  "Thursday", "Friday", "Saturday"]
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function get dayNamesLong():Array /* of String */
    {
        initialize();

        return _dayNamesLong;
    }

    /**
     *  @private
     */
    public static function set dayNamesLong(value:Array /* of String*/):void
    {
        dayNamesLongOverride = value;

        _dayNamesLong = value != null ?
                        value :
                        resourceManager.getStringArray(
                            "SharedResources", "dayNames");
    }

    //----------------------------------
    //  dayNamesShort
    //----------------------------------

    /**
     *  @private
     *  Storage for the dayNamesShort property.
     */
    private static var _dayNamesShort:Array; /* of String */

    /**
     *  @private
     */
    private static var dayNamesShortOverride:Array; /* of String */

    /**
     *  Short format of day names.
     *
     *  @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function get dayNamesShort():Array /* of String */
    {
        initialize();

        return _dayNamesShort;
    }

    /**
     *  @private
     */
    public static function set dayNamesShort(value:Array /* of String*/):void
    {
        dayNamesShortOverride = value;

        _dayNamesShort = value != null ?
                         value :
                         resourceManager.getStringArray(
                             "formatters", "dayNamesShort");
    }

    //----------------------------------
    //  defaultStringKey
    //----------------------------------

    /**
     *  @private
     */
    mx_internal static function get defaultStringKey():Array /* of String */
    {
        initialize();

        return monthNamesLong.concat(timeOfDay);
    }

    //----------------------------------
    //  monthNamesLong
    //----------------------------------

    /**
     *  @private
     *  Storage for the monthNamesLong property.
     */
    private static var _monthNamesLong:Array; /* of String */

    /**
     *  @private
     */
    private static var monthNamesLongOverride:Array; /* of String */

    /**
     *  Long format of month names.
     *
     *  @default ["January", "February", "March", "April", "May", "June",
     *  "July", "August", "September", "October", "November", "December"].
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function get monthNamesLong():Array /* of String */
    {
        initialize();

        return _monthNamesLong;
    }

    /**
     *  @private
     */
    public static function set monthNamesLong(value:Array /* of String*/):void
    {
        monthNamesLongOverride = value;

        _monthNamesLong = value != null ?
                          value :
                          resourceManager.getStringArray(
                              "SharedResources", "monthNames");

        if (value == null)
        {
            // Currently there is no way to get a null
            // string from resourceBundles using getString
            // Hence monthSymbol is a space in English, but
            // we actually want it to be a null string.
            var monthSymbol:String = resourceManager.getString(
                "SharedResources", "monthSymbol");
            if (monthSymbol != " ")
            {
                // _monthNamesLong will be null if there are no resources.
                var n:int = _monthNamesLong ? _monthNamesLong.length : 0;
                for (var i:int = 0; i < n; i++)
                {
                    _monthNamesLong[i] += monthSymbol;
                }
            }
        }
    }

    //----------------------------------
    //  monthNamesShort
    //----------------------------------

    /**
     *  @private
     *  Storage for the monthNamesShort property.
     */
    private static var _monthNamesShort:Array; /* of String */

    /**
     *  @private
     */
    private static var monthNamesShortOverride:Array; /* of String */

    /**
     *  Short format of month names.
     *
     *  @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
     *  "Jul", "Aug", "Sep", "Oct","Nov", "Dec"]
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function get monthNamesShort():Array /* of String */
    {
        initialize();

        return _monthNamesShort;
    }

    /**
     *  @private
     */
    public static function set monthNamesShort(value:Array /* of String*/):void
    {
        monthNamesShortOverride = value;

        _monthNamesShort = value != null ?
                           value :
                           resourceManager.getStringArray(
                               "formatters", "monthNamesShort");
        if (value == null)
        {
            // Currently there is no way to get a null
            // string from resourceBundles using getString
            // Hence monthSymbol is a space in English, but
            // we actually want it to be a null string.
            var monthSymbol:String = resourceManager.getString(
                "SharedResources", "monthSymbol");
            if (monthSymbol != " ")
            {
                // _monthNamesShort will be null if there are no resources.
                var n:int = _monthNamesShort ? _monthNamesShort.length : 0;
                for (var i:int = 0; i < n; i++)
                {
                    _monthNamesShort[i] += monthSymbol;
                }
            }
        }
    }

    //----------------------------------
    //  timeOfDay
    //----------------------------------

    /**
     *  @private
     *  Storage for the timeOfDay property.
     */
    private static var _timeOfDay:Array; /* of String */

    /**
     *  @private
     */
    private static var timeOfDayOverride:Array; /* of String */

    /**
     *  Time of day names.
     *
     *  @default ["AM", "PM"]
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function get timeOfDay():Array /* of String */
    {
        initialize();

        return _timeOfDay;
    }

    /**
     *  @private
     */
    public static function set timeOfDay(value:Array /* of String */):void
    {
        timeOfDayOverride = value;

        var am:String = resourceManager.getString("formatters", "am");
        var pm:String = resourceManager.getString("formatters", "pm");

        _timeOfDay = value != null ? value : [ am, pm ];
    }

    //--------------------------------------------------------------------------
    //
    //  Class methods
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private static function initialize():void
    {
        if (!initialized)
        {
            // Register as a weak listener for "change" events
            // from ResourceManager.
            resourceManager.addEventListener(
                Event.CHANGE, static_resourceManager_changeHandler,
                false, 0, true);

            static_resourcesChanged();

            initialized = true;
        }
    }

    /**
     *  @private
     */
    private static function static_resourcesChanged():void
    {
        dayNamesLong = dayNamesLongOverride;
        dayNamesShort = dayNamesShortOverride;
        monthNamesLong = monthNamesLongOverride;
        monthNamesShort = monthNamesShortOverride;
        timeOfDay = timeOfDayOverride;
    }

    /**
     *  @private
     *  Parses token objects and renders the elements of the formatted String.
     *  For details about token objects, see StringFormatter.
     *
     *  @param date Date object.
     *
     *  @param tokenInfo Array object that contains token object descriptions.
     *
     *  @return Formatted string.
     */
    mx_internal static function extractTokenDate(date:Date,
                                            tokenInfo:Object):String
    {
        initialize();

        var result:String = "";

        var key:int = int(tokenInfo.end) - int(tokenInfo.begin);

        var day:int;
        var hours:int;

        switch (tokenInfo.token)
        {
            case "Y":
            {
                // year
                var year:String = date.getFullYear().toString();
                if (key < 3)
                    return year.substr(2);
                else if (key > 4)
                    return setValue(Number(year), key);
                else
                    return year;
            }

            case "M":
            {
                // month in year
                var month:int = int(date.getMonth());
                if (key < 3)
                {
                    month++; // zero based
                    result += setValue(month, key);
                    return result;
                }
                else if (key == 3)
                {
                    return monthNamesShort[month];
                }
                else
                {
                    return monthNamesLong[month];
                }
            }

            case "D":
            {
                // day in month
                day = int(date.getDate());
                result += setValue(day, key);
                return result;
            }

            case "E":
            {
                // day in the week
                day = int(date.getDay());
                if (key < 3)
                {
                    result += setValue(day, key);
                    return result;
                }
                else if (key == 3)
                {
                    return dayNamesShort[day];
                }
                else
                {
                    return dayNamesLong[day];
                }
            }

            case "A":
            {
                // am/pm marker
                hours = int(date.getHours());
                if (hours < 12)
                    return timeOfDay[0];
                else
                    return timeOfDay[1];
            }

            case "H":
            {
                // hour in day (1-24)
                hours = int(date.getHours());
                if (hours == 0)
                    hours = 24;
                result += setValue(hours, key);
                return result;
            }

            case "J":
            {
                // hour in day (0-23)
                hours = int(date.getHours());
                result += setValue(hours, key);
                return result;
            }

            case "K":
            {
                // hour in am/pm (0-11)
                hours = int(date.getHours());
                if (hours >= 12)
                    hours = hours - 12;
                result += setValue(hours, key);
                return result;
            }

            case "L":
            {
                // hour in am/pm (1-12)
                hours = int(date.getHours());
                if (hours == 0)
                    hours = 12;
                else if (hours > 12)
                    hours = hours - 12;
                result += setValue(hours, key);
                return result;
            }

            case "N":
            {
                // minutes in hour
                var mins:int = int(date.getMinutes());
                result += setValue(mins, key);
                return result;
            }

            case "S":
            {
                // seconds in minute
                var sec:int = int(date.getSeconds());
                result += setValue(sec, key);
                return result;
            }

			case "Q":
			{
				// milliseconds in second
				var ms:int = int(date.getMilliseconds());
				result += setValue(ms, key);
				return result;
			}

			case "Z":
			case "O":
			{
				// timezone offset
				var offset:int = -1 * int(date.timezoneOffset);
				hours = offset/60;
				mins = offset - hours*60;
				var tzStr:String = "";

				if (tokenInfo.token == "Z")
					tzStr = "Z";

				if (key < 2 && mins == 0)
					tzStr += setValue(hours, key);
				else if (key < 3)
					tzStr += setValue(hours, key) + ":" + setValue(mins, key);

				if (offset >= 0)
					tzStr = "+" + tzStr;

				result += tzStr;

				return result;
			}
        }

        return result;
    }

    /**
     *  @private
     *  Makes a given length of digits longer by padding with zeroes.
     *
     *  @param value Value to pad.
     *
     *  @param key Length of the string to pad.
     *
     *  @return Formatted string.
     */
    private static function setValue(value:Object, key:int):String
    {
        var result:String = "";

        var vLen:int = value.toString().length;
        if (vLen < key)
        {
            var n:int = key - vLen;
            for (var i:int = 0; i < n; i++)
            {
                result += "0"
            }
        }

        result += value.toString();

        return result;
    }

    //--------------------------------------------------------------------------
    //
    //  Class event handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private static function static_resourceManager_changeHandler(event:Event):void
    {
        static_resourcesChanged();
    }
}

}

fix time zone offset sign

fix time zone offset sign


//
// Flump - Copyright 2013 Flump Authors

package flump.mold {

import flump.display.Movie;

/** @private */
public class MovieMold
{
    public var id :String;
    public var layers :Vector.<LayerMold> = new <LayerMold>[];
    public var labels :Vector.<Vector.<String>>;

    public static function fromJSON (o :Object) :MovieMold {
        const mold :MovieMold = new MovieMold();
        mold.id = require(o, "id");
        for each (var layer :Object in require(o, "layers")) mold.layers.push(LayerMold.fromJSON(layer));
        return mold;
    }

    public function get frames () :int {
        var frames :int = 0;
        for each (var layer :LayerMold in layers) frames = Math.max(frames, layer.frames);
        return frames;
    }

    public function get flipbook () :Boolean { return (layers.length > 0 && layers[0].flipbook); }

    public function fillLabels () :void {
        labels = new Vector.<Vector.<String>>(frames, true);
        if (labels.length == 0) {
            return;
        }
        labels[0] = new <String>[];
        labels[0].push(Movie.FIRST_FRAME);
        labels[frames - 1] = new <String>[];
        labels[frames - 1].push(Movie.LAST_FRAME);
        for each (var layer :LayerMold in layers) {
            for each (var kf :KeyframeMold in layer.keyframes) {
                if (kf.label == null) continue;
                if (labels[kf.index] == null) labels[kf.index] = new <String>[];
                labels[kf.index].push(kf.label);
            }

        }
    }

    public function scale (scale :Number) :MovieMold {
        const clone :MovieMold = fromJSON(JSON.parse(JSON.stringify(this)));
        for each (var layer :LayerMold in clone.layers) {
            for each (var kf :KeyframeMold in layer.keyframes) {
                kf.x *= scale;
                kf.y *= scale;
                kf.pivotX *= scale;
                kf.pivotY *= scale;
            }
        }
        return clone;
    }

    public function toJSON (_:*) :Object {
        const json :Object = {
            id: id,
            layers: layers
        };
        return json
    }

    public function toXML () :XML {
        var xml :XML = <movie name={id}/>;
        for each (var layer :LayerMold in layers) xml.appendChild(layer.toXML());
        return xml;
    }

}
}

//
// Flump - Copyright 2013 Flump Authors

package flump.mold {

import flump.display.Movie;

/** @private */
public class MovieMold
{
    public var id :String;
    public var layers :Vector.<LayerMold> = new <LayerMold>[];
    public var labels :Vector.<Vector.<String>>;

    public static function fromJSON (o :Object) :MovieMold {
        const mold :MovieMold = new MovieMold();
        mold.id = require(o, "id");
        for each (var layer :Object in require(o, "layers")) mold.layers.push(LayerMold.fromJSON(layer));
        return mold;
    }

    public function get frames () :int {
        var frames :int = 0;
        for each (var layer :LayerMold in layers) frames = Math.max(frames, layer.frames);
        return frames;
    }

    public function get flipbook () :Boolean { return (layers.length > 0 && layers[0].flipbook); }

    public function fillLabels () :void {
        labels = new Vector.<Vector.<String>>(frames, true);
        if (labels.length == 0) {
            return;
        }
        labels[0] = new <String>[];
        labels[0].push(Movie.FIRST_FRAME);
        if (labels.length > 1) {
            // If we only have 1 frame, don't overwrite labels[0]
            labels[frames - 1] = new <String>[];
        }
        labels[frames - 1].push(Movie.LAST_FRAME);
        for each (var layer :LayerMold in layers) {
            for each (var kf :KeyframeMold in layer.keyframes) {
                if (kf.label == null) continue;
                if (labels[kf.index] == null) labels[kf.index] = new <String>[];
                labels[kf.index].push(kf.label);
            }

        }
    }

    public function scale (scale :Number) :MovieMold {
        const clone :MovieMold = fromJSON(JSON.parse(JSON.stringify(this)));
        for each (var layer :LayerMold in clone.layers) {
            for each (var kf :KeyframeMold in layer.keyframes) {
                kf.x *= scale;
                kf.y *= scale;
                kf.pivotX *= scale;
                kf.pivotY *= scale;
            }
        }
        return clone;
    }

    public function toJSON (_:*) :Object {
        const json :Object = {
            id: id,
            layers: layers
        };
        return json
    }

    public function toXML () :XML {
        var xml :XML = <movie name={id}/>;
        for each (var layer :LayerMold in layers) xml.appendChild(layer.toXML());
        return xml;
    }

}
}

Fix fillLabels for 1-frame movies

Fix fillLabels for 1-frame movies


/**
  * LzMouseKernel.as
  *
  * @copyright Copyright 2001-2008 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic AS2
  */

// Receives mouse events from the runtime
class LzMouseKernel  {
    #passthrough (toplevel:true) {
    import flash.display.*;
    import flash.events.*;
    import flash.utils.*;
    import flash.ui.*;
    }#

    // sends mouse events to the callback
    static function __sendEvent(view, eventname) {
        if (LzMouseKernel.__callback) LzMouseKernel.__scope[LzMouseKernel.__callback](eventname, view);
        //Debug.write('LzMouseKernel event', eventname);
    }
    static var __callback = null;
    static var __scope = null;
    static var __lastMouseDown = null;
    static var __listeneradded:Boolean = false ;

    /**
     * Shows or hides the hand cursor for all clickable views.
     */
    static var showhandcursor:Boolean = true;

    static function setCallback (scope, funcname) {
        LzMouseKernel.__scope = scope;
        LzMouseKernel.__callback = funcname;
        if (LzMouseKernel.__listeneradded == false) {
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_MOVE, __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_UP,   __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_DOWN, __mouseHandler);
            LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE,     __mouseLeavesHandler);
            LzMouseKernel.__listeneradded = true;
        }
    }

    // Handles global mouse events
    static function __mouseHandler(event:MouseEvent):void {
        var eventname = 'on' + event.type.toLowerCase();
        //Debug.write('__mouseHandler', eventname);

        if (eventname == 'onmouseup' && __lastMouseDown != null) {
            // call mouseup on the sprite that got the last mouse down
            LzMouseKernel.__lastMouseDown.__globalmouseup(event);
            __lastMouseDown = null;
        } else {
            LzMouseKernel.__sendEvent(null, eventname);
        }
    }

    // handles MOUSE_LEAVES event
    static function __mouseLeavesHandler(event:Event):void {
        var eventname = 'on' + event.type.toLowerCase();
        LzMouseKernel.__sendEvent(null, eventname);
    }

    /**
    * Shows or hides the hand cursor for all clickable views.
    * @param Boolean show: true shows the hand cursor for buttons, false hides it
    */
    static function showHandCursor (show) {
        LzMouseKernel.showhandcursor = show;
    }

    static var __amLocked:Boolean = false;
    static var cursorSprite:Sprite = null;
    static var globalCursorResource:String = null;
    static var lastCursorResource:String = null;

    /**
    * Sets the cursor to a resource
    * @param String what: The resource to use as the cursor.
    */
    static function setCursorGlobal ( what:String ){
        globalCursorResource = what;
        setCursorLocal(what);
    }

    static function setCursorLocal ( what:String ) {
        if ( LzMouseKernel.__amLocked ) { return; }
        Mouse.hide();
        cursorSprite.x = LFCApplication.stage.mouseX ;
        cursorSprite.y = LFCApplication.stage.mouseY ;
        LFCApplication.setChildIndex(cursorSprite, LFCApplication._sprite.numChildren-1);
        if (lastCursorResource != what) {
            if (cursorSprite.numChildren > 0) {
                cursorSprite.removeChildAt(0);
            }
            var resourceSprite = getCursorResource(what);
            resourceSprite.y = 1;
            cursorSprite.addChild( resourceSprite );
            lastCursorResource = what;
        }
        // respond to mouse move events
        cursorSprite.startDrag();
        LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler);
        cursorSprite.visible = true;
    }

    static function mouseLeaveHandler(evt:Event):void {
        cursorSprite.visible = false;
    }

    static function getCursorResource (resource:String):Sprite {
          var resinfo = LzResourceLibrary[resource];
          var assetclass;
          var frames = resinfo.frames;
          var asset:DisplayObject;
          // single frame resources get an entry in LzResourceLibrary which has
          // 'assetclass' pointing to the resource Class object.
          if (resinfo.assetclass is Class) {
              assetclass = resinfo.assetclass;
          } else {
              // Multiframe resources have an array of Class objects in frames[]
              assetclass = frames[0];
          }

          if (! assetclass) return;
          asset = new assetclass();
          asset.scaleX = 1.0;
          asset.scaleY = 1.0;
          //Debug.write('cursor asset', asset);
          return asset;
    }

    /**
    * This function restores the default cursor if there is no locked cursor on
    * the screen.
    *
    * @access private
    */
    static function restoreCursor ( ){
        if ( LzMouseKernel.__amLocked ) { return; }
        cursorSprite.stopDrag();
        cursorSprite.visible = false;
        globalCursorResource = null;
        Mouse.show();
    }

    /** Called by LzSprite to restore cursor to global value.
     */
    static function restoreCursorLocal ( ){
        if ( LzMouseKernel.__amLocked ) { return; }
        if (globalCursorResource == null) {
            // Restore to system default pointer
            restoreCursor();
        } else {
            // Restore to the last value set by setCursorGlobal
            setCursorLocal(globalCursorResource);
        }
    }

    /**
    * Prevents the cursor from being changed until unlock is called.
    *
    */
    static function lock (){
        LzMouseKernel.__amLocked = true;
    }

    /**
    * Restores the default cursor.
    *
    */
    static function unlock (){
        LzMouseKernel.__amLocked = false;
        LzMouseKernel.restoreCursor();
    }

    static function initCursor () {
        cursorSprite = new Sprite();
        cursorSprite.mouseEnabled = false;
        // Add the cursor DisplayObject to the root sprite
        LFCApplication.addChild(cursorSprite);
        cursorSprite.x = -10000;
        cursorSprite.y = -10000;
    }
}

/**
  * LzMouseKernel.as
  *
  * @copyright Copyright 2001-2008 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic AS2
  */

// Receives mouse events from the runtime
class LzMouseKernel  {
    #passthrough (toplevel:true) {
    import flash.display.*;
    import flash.events.*;
    import flash.utils.*;
    import flash.ui.*;
    }#

    // sends mouse events to the callback
    static function __sendEvent(view, eventname) {
        if (LzMouseKernel.__callback) LzMouseKernel.__scope[LzMouseKernel.__callback](eventname, view);
        //Debug.write('LzMouseKernel event', eventname);
    }
    static var __callback = null;
    static var __scope = null;
    static var __lastMouseDown = null;
    static var __listeneradded:Boolean = false ;

    /**
     * Shows or hides the hand cursor for all clickable views.
     */
    static var showhandcursor:Boolean = true;

    static function setCallback (scope, funcname) {
        LzMouseKernel.__scope = scope;
        LzMouseKernel.__callback = funcname;
        if (LzMouseKernel.__listeneradded == false) {
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_MOVE, __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_UP,   __mouseHandler);
            LFCApplication.stage.addEventListener(MouseEvent.MOUSE_DOWN, __mouseHandler);
            LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE,     __mouseLeavesHandler);
            LzMouseKernel.__listeneradded = true;
        }
    }

    // Handles global mouse events
    static function __mouseHandler(event:MouseEvent):void {
        var eventname = 'on' + event.type.toLowerCase();
        //Debug.write('__mouseHandler', eventname);

        if (eventname == 'onmouseup' && __lastMouseDown != null) {
            // call mouseup on the sprite that got the last mouse down
            LzMouseKernel.__lastMouseDown.__globalmouseup(event);
            __lastMouseDown = null;
        } else {
            LzMouseKernel.__sendEvent(null, eventname);
        }
    }

    // handles MOUSE_LEAVES event
    static function __mouseLeavesHandler(event:Event):void {
        var eventname = 'on' + event.type.toLowerCase();
        LzMouseKernel.__sendEvent(null, eventname);
    }

    /**
    * Shows or hides the hand cursor for all clickable views.
    * @param Boolean show: true shows the hand cursor for buttons, false hides it
    */
    static function showHandCursor (show) {
        LzMouseKernel.showhandcursor = show;
    }

    static var __amLocked:Boolean = false;
    static var cursorSprite:Sprite = null;
    static var globalCursorResource:String = null;
    static var lastCursorResource:String = null;

    /**
    * Sets the cursor to a resource
    * @param String what: The resource to use as the cursor.
    */
    static function setCursorGlobal ( what:String ){
        globalCursorResource = what;
        setCursorLocal(what);
    }

    static function setCursorLocal ( what:String ) {
        if ( LzMouseKernel.__amLocked ) { return; }
        Mouse.hide();
        cursorSprite.x = LFCApplication.stage.mouseX + 1;
        cursorSprite.y = LFCApplication.stage.mouseY + 1;
        LFCApplication.setChildIndex(cursorSprite, LFCApplication._sprite.numChildren-1);
        if (lastCursorResource != what) {
            if (cursorSprite.numChildren > 0) {
                cursorSprite.removeChildAt(0);
            }
            var resourceSprite = getCursorResource(what);
            cursorSprite.addChild( resourceSprite );
            lastCursorResource = what;
        }
        // respond to mouse move events
        cursorSprite.startDrag();
        LFCApplication.stage.addEventListener(Event.MOUSE_LEAVE, mouseLeaveHandler);
        cursorSprite.visible = true;
    }

    static function mouseLeaveHandler(evt:Event):void {
        cursorSprite.visible = false;
    }

    static function getCursorResource (resource:String):Sprite {
          var resinfo = LzResourceLibrary[resource];
          var assetclass;
          var frames = resinfo.frames;
          var asset:DisplayObject;
          // single frame resources get an entry in LzResourceLibrary which has
          // 'assetclass' pointing to the resource Class object.
          if (resinfo.assetclass is Class) {
              assetclass = resinfo.assetclass;
          } else {
              // Multiframe resources have an array of Class objects in frames[]
              assetclass = frames[0];
          }

          if (! assetclass) return;
          asset = new assetclass();
          asset.scaleX = 1.0;
          asset.scaleY = 1.0;
          //Debug.write('cursor asset', asset);
          return asset;
    }

    /**
    * This function restores the default cursor if there is no locked cursor on
    * the screen.
    *
    * @access private
    */
    static function restoreCursor ( ){
        if ( LzMouseKernel.__amLocked ) { return; }
        cursorSprite.stopDrag();
        cursorSprite.visible = false;
        globalCursorResource = null;
        Mouse.show();
    }

    /** Called by LzSprite to restore cursor to global value.
     */
    static function restoreCursorLocal ( ){
        if ( LzMouseKernel.__amLocked ) { return; }
        if (globalCursorResource == null) {
            // Restore to system default pointer
            restoreCursor();
        } else {
            // Restore to the last value set by setCursorGlobal
            setCursorLocal(globalCursorResource);
        }
    }

    /**
    * Prevents the cursor from being changed until unlock is called.
    *
    */
    static function lock (){
        LzMouseKernel.__amLocked = true;
    }

    /**
    * Restores the default cursor.
    *
    */
    static function unlock (){
        LzMouseKernel.__amLocked = false;
        LzMouseKernel.restoreCursor();
    }

    static function initCursor () {
        cursorSprite = new Sprite();
        cursorSprite.mouseEnabled = false;
        // Add the cursor DisplayObject to the root sprite
        LFCApplication.addChild(cursorSprite);
        cursorSprite.x = -10000;
        cursorSprite.y = -10000;
    }
}

Change 20080825-hqm-A by hqm@badtzmaru.home on 2008-08-25 17:01:30 EDT
    in /Users/hqm/openlaszlo/trunk/WEB-INF/lps/lfc
    for http://svn.openlaszlo.org/openlaszlo/trunk/WEB-INF/lps/lfc

Summary: slight improvement for swf9 cursor tracking

New Features:

Bugs Fixed:

Technical Reviewer: (pending)
QA Reviewer: (pending)
Doc Reviewer: (pending)

Documentation:

Release Notes:

Details:

The sprite only needs to be offset from the mouse position while loading, it can
then track the mouse exactly via startDrag as usual.

Tests:

examples/components/grid_example.lzx
 -- cursor changes to arrows and does not blink when over column resizer.

git-svn-id: d62bde4b5aa582fdf07c8d403e53e0399a7ed285@10770 fa20e4f9-1d0a-0410-b5f3-dc9c16b8b17c


package dolly {

import dolly.core.dolly_internal;
import dolly.data.CompositeCloneableClass;

import org.as3commons.reflect.Type;

use namespace dolly_internal;

public class CloningOfCompositeCloneableClassTest {

	private var compositeCloneableClass:CompositeCloneableClass;
	private var compositeCloneableClassType:Type;

	[Before]
	public function before():void {
		compositeCloneableClass = new CompositeCloneableClass();

		compositeCloneableClassType = Type.forInstance(compositeCloneableClass);
	}

	[After]
	public function after():void {
		compositeCloneableClass = null;
		compositeCloneableClassType = null;
	}
}
}

package dolly {
import dolly.core.dolly_internal;
import dolly.data.CompositeCloneableClass;

import org.as3commons.reflect.Field;
import org.as3commons.reflect.Type;
import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertNotNull;

use namespace dolly_internal;

public class CloningOfCompositeCloneableClassTest {

	private var compositeCloneableClass:CompositeCloneableClass;
	private var compositeCloneableClassType:Type;

	[Before]
	public function before():void {
		compositeCloneableClass = new CompositeCloneableClass();

		compositeCloneableClassType = Type.forInstance(compositeCloneableClass);
	}

	[After]
	public function after():void {
		compositeCloneableClass = null;
		compositeCloneableClassType = null;
	}

	[Test]
	public function findingAllWritableFieldsForType():void {
		const writableFields:Vector.<Field> = Cloner.findAllWritableFieldsForType(compositeCloneableClassType);
		assertNotNull(writableFields);
		assertEquals(4, writableFields.length);
	}
}
}

Test for calculation of cloneable fields in CompositeCloneableClass.


package net.manaca.preloaders
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.system.Capabilities;
import flash.utils.getDefinitionByName;

import net.manaca.application.IApplication;

/**
 * The Preloader class is used by the application to monitor
 * the download and initialization status of a flash application.
 * It is also responsible for downloading the runtime shared libraries (RSLs).
 * @author Sean Zou
 *
 */
public class PreloaderBase extends MovieClip
{
    //==========================================================================
    //  Variables
    //==========================================================================

    //==========================================================================
    //  Constructor
    //==========================================================================
    /**
     * Application Constructor.
     */
    public function PreloaderBase()
    {
        //stop at the first frame
        stop();

        stage.align = StageAlign.TOP_LEFT;
        stage.scaleMode = StageScaleMode.NO_SCALE;

        if(root && root.loaderInfo)
        {
            root.loaderInfo.addEventListener(Event.INIT, initHanlder);
        }
    }

    //==========================================================================
    //  Methods
    //==========================================================================

    /**
     * Create a PreloaderDisplay.
     * the default create a DownloadProgressBar.
     * @return
     *
     */
    protected function initDisplay():void
    {
    }

    /**
     * Create a application.
     * @param params
     * @return
     *
     */
    protected function createApplication():Object
    {
        var mainClassName:String;

        if (mainClassName == null)
        {
            var url:String = loaderInfo.loaderURL;

            var dot:int = url.lastIndexOf(".");
            var slash:int = url.lastIndexOf("/");
            mainClassName = url.substring(slash + 1, dot);

            /* modify at flex 2 debuging error. */
            if(Capabilities.isDebugger && mainClassName.indexOf("_debug") != -1)
            {
                mainClassName = mainClassName.slice(0, -6);
            }

            mainClassName = decodeURIComponent(mainClassName);
        }
        var mainClass:Class = Class(getDefinitionByName(mainClassName));
        return mainClass ? new mainClass() : null;
    }

    /**
     * @private
     * initialize the application.
     */
    protected function initialize():void
    {
        nextFrame();

        var app:Object = createApplication();
        addChild(app as Sprite);

        if(app is IApplication)
        {
            IApplication(app).initialize();
        }
    }

    //==========================================================================
    //  Event handlers
    //==========================================================================
    /**
     * Handler the init event.
     * @param e
     *
     */
    protected function initHanlder(event:Event):void
    {
        initDisplay();

        addEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }

    /**
     * Use frame based loading
     */
    protected function enterFrameHandler(event:Event):void
    {
        if (loaderInfo.bytesLoaded == loaderInfo.bytesTotal)
        {
            removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
            initialize();
        }
    }
}
}

package net.manaca.preloaders
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.system.Capabilities;
import flash.utils.getDefinitionByName;

import net.manaca.application.IApplication;

/**
 * The Preloader class is used by the application to monitor
 * the download and initialization status of a flash application.
 * It is also responsible for downloading the runtime shared libraries (RSLs).
 * @author Sean Zou
 *
 */
public class PreloaderBase extends MovieClip
{
    //==========================================================================
    //  Variables
    //==========================================================================

    //==========================================================================
    //  Constructor
    //==========================================================================
    /**
     * Application Constructor.
     */
    public function PreloaderBase()
    {
        //stop at the first frame
        stop();

        stage.align = StageAlign.TOP_LEFT;
        stage.scaleMode = StageScaleMode.NO_SCALE;

        if(root && root.loaderInfo)
        {
            root.loaderInfo.addEventListener(Event.INIT, initHanlder);
        }
    }

    //==========================================================================
    //  Methods
    //==========================================================================

    /**
     * Create a PreloaderDisplay.
     * the default create a DownloadProgressBar.
     * @return
     *
     */
    protected function initDisplay():void
    {
    }

    /**
     * Create a application.
     * @param params
     * @return
     *
     */
    protected function createApplication():Object
    {
        var mainClassName:String;

        if (mainClassName == null)
        {
            var url:String = loaderInfo.loaderURL;

            var dot:int = url.lastIndexOf(".");
            var slash:int = url.lastIndexOf("/");
            mainClassName = url.substring(slash + 1, dot);

            /* modify at flex 2 debuging error. */
            if(Capabilities.isDebugger && mainClassName.indexOf("_debug") != -1)
            {
                mainClassName = mainClassName.slice(0, -6);
            }

            mainClassName = decodeURIComponent(mainClassName);
        }
        var mainClass:Class = Class(getDefinitionByName(mainClassName));
        return mainClass ? new mainClass() : null;
    }

    /**
     * @private
     * initialize the application.
     */
    protected function initialize():void
    {
        nextFrame();

        var app:Object = createApplication();
        addChild(app as Sprite);

        if(app is IApplication)
        {
            IApplication(app).initialize();
        }
    }

    //==========================================================================
    //  Event handlers
    //==========================================================================
    /**
     * Handler the init event.
     * @param e
     *
     */
    protected function initHanlder(event:Event):void
    {
        initDisplay();

        addEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }

    /**
     * Use frame based loading
     */
    protected function enterFrameHandler(event:Event):void
    {
        var loaded:uint = loaderInfo.bytesLoaded;
        var total:uint = loaderInfo.bytesTotal;
        if((loaded >= total && total > 0) || (total == 0 && loaded > 0)
            || (root is MovieClip && (MovieClip(root).totalFrames > 2) &&
                (MovieClip(root).framesLoaded >= 2)))
        {
            removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
            initialize();
        }
    }
}
}

修正在带有gzip的请求时，导致无法判断flash是否加载完成的错误.

修正在带有gzip的请求时，导致无法判断flash是否加载完成的错误.


/*
    Weave (Web-based Analysis and Visualization Environment)
    Copyright (C) 2008-2011 University of Massachusetts Lowell

    This file is a part of Weave.

    Weave is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License, Version 3,
    as published by the Free Software Foundation.

    Weave is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Weave.  If not, see <http://www.gnu.org/licenses/>.
*/

package weave.core
{
	/**
	 * LinkableBoolean, LinkableString and LinkableNumber contain simple, immutable data types.  LinkableXML
	 * is an exception because it contains an XML object that can be manipulated.  Changes to the internal
	 * XML object cannot be detected automatically, so a detectChanges() function is provided.  However, if
	 * two LinkableXML objects have the same internal XML object, modifying the internal XML of one object
	 * would inadvertently modify the internal XML of another.  To avoid this situation, LinkableXML creates
	 * a copy of the XML that you set as the session state.
	 *
	 * @author adufilie
	 * @see weave.core.LinkableVariable
	 */
	public class LinkableXML extends LinkableVariable
	{
		public function LinkableXML(allowNull:Boolean = true)
		{
			super(String, allowNull ? null : notNull);
		}

		private function notNull(value:Object):Boolean
		{
			return value != null;
		}

		/**
		 * This function will run the callbacks attached to this LinkableXML if the session state has changed.
		 * This function should be called if the XML is modified without calling set value() or setSessionState().
		 */
		public function detectChanges():void
		{
			value = value;
		}

		/**
		 * This is the sessioned XML value for this object.
		 */
		public function get value():XML
		{
			// validate local XML version of the session state String if necessary
			if (_prevTriggerCount != triggerCounter)
			{
				_prevTriggerCount = triggerCounter;
				_sessionStateXML = null;
				try
				{
					if (_sessionState) // false if empty string (prefer null over empty xml)
						_sessionStateXML = XML(_sessionState);
				}
				catch (e:Error)
				{
					// xml parsing failed, so keep null
				}
			}
			return _sessionStateXML;
		}
		/**
		 * This will save a COPY of the value passed in to prevent multiple LinkableXML objects from having the same internal XML object.
		 * @param value An XML to copy and save as the sessioned value for this object.
		 */
		public function set value(value:XML):void
		{
			var str:String = value ? value.toXMLString() : null;
			setSessionState(str);
		}

		override public function setSessionState(value:Object):void
		{
			if (value && value.hasOwnProperty(XML_STRING))
				value = value[XML_STRING];
			if (value is XML)
				value = (value as XML).toXMLString();
			super.setSessionState(value);
		}

		override public function getSessionState():Object
		{
			// return an XMLString wrapper object for use with WeaveXMLEncoder.
			var result:Object = {};
			result[XML_STRING] = _sessionState;
			return result;
		}

		public static const XML_STRING:String = "XMLString";

		/**
		 * This is used to store an XML value, which is separate from the actual session state String.
		 */
		private var _sessionStateXML:XML = null;

		/**
		 * This is the trigger count at the time when _sessionStateXML was last updated.
		 */
		private var _prevTriggerCount:uint = triggerCounter;
	}
}

/*
    Weave (Web-based Analysis and Visualization Environment)
    Copyright (C) 2008-2011 University of Massachusetts Lowell

    This file is a part of Weave.

    Weave is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License, Version 3,
    as published by the Free Software Foundation.

    Weave is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Weave.  If not, see <http://www.gnu.org/licenses/>.
*/

package weave.core
{
	/**
	 * LinkableBoolean, LinkableString and LinkableNumber contain simple, immutable data types.  LinkableXML
	 * is an exception because it contains an XML object that can be manipulated.  Changes to the internal
	 * XML object cannot be detected automatically, so a detectChanges() function is provided.  However, if
	 * two LinkableXML objects have the same internal XML object, modifying the internal XML of one object
	 * would inadvertently modify the internal XML of another.  To avoid this situation, LinkableXML creates
	 * a copy of the XML that you set as the session state.
	 *
	 * @author adufilie
	 * @see weave.core.LinkableVariable
	 */
	public class LinkableXML extends LinkableVariable
	{
		public function LinkableXML(allowNull:Boolean = true)
		{
			super(String, verifyXMLString);
			_allowNull = allowNull;
		}

		private var _allowNull:Boolean;

		private function verifyXMLString(value:String):Boolean
		{
			if (value == null)
				return _allowNull;

			try {
				XML(value);
				return true;
			}
			catch (e:*) { }
			return false;
		}

		/**
		 * This function will run the callbacks attached to this LinkableXML if the session state has changed.
		 * This function should be called if the XML is modified without calling set value() or setSessionState().
		 */
		public function detectChanges():void
		{
			value = value;
		}

		/**
		 * This is the sessioned XML value for this object.
		 */
		public function get value():XML
		{
			// validate local XML version of the session state String if necessary
			if (_prevTriggerCount != triggerCounter)
			{
				_prevTriggerCount = triggerCounter;
				_sessionStateXML = null;
				try
				{
					if (_sessionState) // false if empty string (prefer null over empty xml)
						_sessionStateXML = XML(_sessionState);
				}
				catch (e:Error)
				{
					// xml parsing failed, so keep null
				}
			}
			return _sessionStateXML;
		}
		/**
		 * This will save a COPY of the value passed in to prevent multiple LinkableXML objects from having the same internal XML object.
		 * @param value An XML to copy and save as the sessioned value for this object.
		 */
		public function set value(value:XML):void
		{
			var str:String = value ? value.toXMLString() : null;
			setSessionState(str);
		}

		override public function setSessionState(value:Object):void
		{
			if (value && value.hasOwnProperty(XML_STRING))
				value = value[XML_STRING];
			if (value is XML)
				value = (value as XML).toXMLString();
			super.setSessionState(value);
		}

		override public function getSessionState():Object
		{
			// return an XMLString wrapper object for use with WeaveXMLEncoder.
			var result:Object = {};
			result[XML_STRING] = _sessionState;
			return result;
		}

		public static const XML_STRING:String = "XMLString";

		/**
		 * This is used to store an XML value, which is separate from the actual session state String.
		 */
		private var _sessionStateXML:XML = null;

		/**
		 * This is the trigger count at the time when _sessionStateXML was last updated.
		 */
		private var _prevTriggerCount:uint = triggerCounter;
	}
}

verify function now checks for valid xml


//
// Flump - Copyright 2013 Flump Authors

package flump.display {

import flump.mold.MovieMold;

import react.Signal;

import starling.animation.IAnimatable;
import starling.display.DisplayObject;
import starling.display.Sprite;
import starling.events.Event;

/**
 * A movie created from flump-exported data. It has children corresponding to the layers in the
 * movie in Flash, in the same order and with the same names. It fills in those children
 * initially with the image or movie of the symbol on that exported layer. After the initial
 * population, it only applies the keyframe-based transformations to the child at the index
 * corresponding to the layer. This means it's safe to swap in other DisplayObjects at those
 * positions to have them animated in place of the initial child.
 *
 *
A Movie will not animate unless it's added to a Juggler (or its advanceTime() function
 * is otherwise called). When the movie is added to a juggler, it advances its playhead with the
 * frame ticks if isPlaying is true. It will automatically remove itself from its juggler when
 * removed from the stage.

 *
 * @see Library and LibraryLoader to create instances of Movie.
 */
public class Movie extends Sprite
    implements IAnimatable
{
    /** A label fired by all movies when entering their first frame. */
    public static const FIRST_FRAME :String = "flump.movie.FIRST_FRAME";

    /** A label fired by all movies when entering their last frame. */
    public static const LAST_FRAME :String = "flump.movie.LAST_FRAME";

    /** Fires the label string whenever it's passed in playing. */
    public const labelPassed :Signal = new Signal(String);

    /** @private */
    public function Movie (src :MovieMold, frameRate :Number, library :Library) {
        this.name = src.id;
        _labels = src.labels;
        _frameRate = frameRate;
        if (src.flipbook) {
            _layers = new Vector.<Layer>(1, true);
            _layers[0] = new Layer(this, src.layers[0], library, /*flipbook=*/true);
            _numFrames = src.layers[0].frames;
        } else {
            _layers = new Vector.<Layer>(src.layers.length, true);
            for (var ii :int = 0; ii < _layers.length; ii++) {
                _layers[ii] = new Layer(this, src.layers[ii], library, /*flipbook=*/false);
                _numFrames = Math.max(src.layers[ii].frames, _numFrames);
            }
        }
        _duration = _numFrames / _frameRate;
        updateFrame(0, 0);

        // When we're removed from the stage, remove ourselves from any juggler animating us.
        addEventListener(Event.REMOVED_FROM_STAGE, function (..._) :void {
            dispatchEventWith(Event.REMOVE_FROM_JUGGLER);
        });
    }

    /** @return the frame being displayed. */
    public function get frame () :int { return _frame; }

    /** @return the number of frames in the movie. */
    public function get numFrames () :int { return _numFrames; }

    /** @return true if the movie is currently playing. */
    public function get isPlaying () :Boolean { return _playing; }

    /** @return true if the movie contains the given label. */
    public function hasLabel (label :String) :Boolean {
        return getFrameForLabel(label) >= 0;
    }

    /** Plays the movie from its current frame. The movie will loop forever.  */
    public function loop () :Movie {
        _playing = true;
        _stopFrame = NO_FRAME;
        return this;
    }

    /** Plays the movie from its current frame, stopping when it reaches its last frame. */
    public function playOnce () :Movie { return playTo(LAST_FRAME); }

    /**
     * Moves to the given String label or int frame. Doesn't alter playing status or stop frame.
     * If there are labels at the given position, they're fired as part of the goto, even if the
     * current frame is equal to the destination. Labels between the current frame and the
     * destination frame are not fired.
     *
     * @param position the int frame or String label to goto.
     *
     * @return this movie for chaining
     *
     * @throws Error if position isn't an int or String, or if it is a String and that String isn't
     * a label on this movie
     */
    public function goTo (position :Object) :Movie {
        const frame :int = extractFrame(position);
        updateFrame(frame, 0);
        return this;
    }

   /**
    * Plays the movie from its current frame. The movie will stop when it reaches the given label
    * or frame.
    *
    * @param position to int frame or String label to stop at
    *
    * @return this movie for chaining
    *
    * @throws Error if position isn't an int or String, or if it is a String and that String isn't
    * a label on this movie.
    */
    public function playTo (position :Object) :Movie {
       // won't play if we're already at the stop position
       return stopAt(position).play();
    }

    /** Sets the stop frame for this Movie.
     *
     * @param position the int frame or String label to stop at.
     *
     * @return this movie for chaining
     *
     * @throws Error if position isn't an int or String, or if it is a String and that String isn't
     * a label on this movie.
     */
    public function stopAt (position :Object) :Movie {
        _stopFrame = extractFrame(position);
        return this;
    }

    /** Sets the movie playing.  Movie will automatically stop at its stopFrame, if one is set,
     *  otherwise it will loop forever.
     *
     * @return this movie for chaining
     */
    public function play () :Movie {
        // set playing to true unless movie is at the stop frame
        _playing = (_frame != _stopFrame) || (_stopFrame == NO_FRAME);
        return this;
    }

    /** Stops playback if it's currently active. Doesn't alter the current frame or stop frame. */
    public function stop () :Movie {
        _playing = false;
        return this;
    }

    /** Advances the playhead by the give number of seconds. From IAnimatable. */
    public function advanceTime (dt :Number) :void {
        if (dt < 0) throw new Error("Invalid time [dt=" + dt + "]");
        if (_skipAdvanceTime) { _skipAdvanceTime = false; return; }
        if (!_playing) return;

        if (_numFrames > 1) {
            _playTime += dt;
            var actualPlaytime :Number = _playTime;
            if (_playTime >= _duration) _playTime %= _duration;

            // If _playTime is very close to _duration, rounding error can cause us to
            // land on lastFrame + 1. Protect against that.
            var newFrame :int = clamp(int(_playTime * _frameRate), 0, _numFrames - 1);

            // If the update crosses or goes to the stopFrame:
            // go to the stopFrame, stop the movie, clear the stopFrame
            if (_stopFrame != NO_FRAME) {
                // how many frames remain to the stopframe?
                var framesRemaining :int =
                    (_frame <= _stopFrame ? _stopFrame - _frame : _numFrames - _frame + _stopFrame);
                var framesElapsed :int = int(actualPlaytime * _frameRate) - _frame;
                if (framesElapsed >= framesRemaining) {
                    _playing = false;
                    newFrame = _stopFrame;
                }
            }
            updateFrame(newFrame, dt);
        }

        for (var ii :int = this.numChildren - 1; ii >= 0; --ii) {
            var child :DisplayObject = getChildAt(ii);
            if (child is Movie) {
                Movie(child).advanceTime(dt);
            }
        }
    }

    /**
     * @private
     *
     * Called when the Movie has been newly added to a layer.
     */
    internal function addedToLayer () :void {
        goTo(0);
        _skipAdvanceTime = true;
    }

    /** @private */
    protected function extractFrame (position :Object) :int {
        if (position is int) return int(position);
        if (!(position is String)) throw new Error("Movie position must be an int frame or String label");
        const label :String = String(position);
        var frame :int = getFrameForLabel(label);
        if (frame < 0) {
            throw new Error("No such label '" + label + "'");
        }
        return frame;
    }

    /**
     * @private
     *
     * Returns the frame index for the given label, or -1 if the label doesn't exist.
     */
    protected function getFrameForLabel (label :String) :int {
        for (var ii :int = 0; ii < _labels.length; ii++) {
            if (_labels[ii] != null && _labels[ii].indexOf(label) != -1) return ii;
        }
        return -1;
    }

    /**
     * @private
     *
     * @param dt the timeline's elapsed time since the last update. This should be 0
     * for updates that are the result of a "goTo" call.
     */
    protected function updateFrame (newFrame :int, dt :Number) :void {
        if (newFrame < 0 || newFrame >= _numFrames) {
            throw new Error("Invalid frame [frame=" + newFrame,
                " validRange=0-" + (_numFrames - 1) + "]");
        }

        if (_isUpdatingFrame) {
            _pendingFrame = newFrame;
            return;
        } else {
            _pendingFrame = NO_FRAME;
            _isUpdatingFrame = true;
        }

        const isGoTo :Boolean = (dt <= 0);
        const wrapped :Boolean = (dt >= _duration) || (newFrame < _frame);

        if (newFrame != _frame) {
            if (wrapped) {
                for each (var layer :Layer in _layers) {
                    layer.movieLooped();
                }
            }
            for each (layer in _layers) layer.drawFrame(newFrame);
        }

        if (isGoTo) _playTime = newFrame / _frameRate;

        // Update the frame before firing frame label signals, so if firing changes the frame,
        // it sticks.
        const oldFrame :int = _frame;
        _frame = newFrame;

        // determine which labels to fire signals for
        var startFrame :int;
        var frameCount :int;
        if (isGoTo) {
            startFrame = newFrame;
            frameCount = 1;
        } else {
            startFrame = (oldFrame + 1 < _numFrames ? oldFrame + 1 : 0);
            frameCount = (_frame - oldFrame);
            if (wrapped) frameCount += _numFrames;
        }

        // Fire signals. Stop if pendingFrame is updated, which indicates that the client
        // has called goTo()
        var frameIdx :int = startFrame;
        for (var ii :int = 0; ii < frameCount; ++ii) {
            if (_pendingFrame != NO_FRAME) break;

            if (_labels[frameIdx] != null) {
                for each (var label :String in _labels[frameIdx]) {
                    labelPassed.emit(label);
                    if (_pendingFrame != NO_FRAME) break;
                }
            }

            // avoid modulo division by updating frameIdx each time through the loop
            if (++frameIdx == _numFrames) {
                frameIdx = 0;
            }
        }

        _isUpdatingFrame = false;
        // If we were interrupted by a goTo(), update to that frame now.
        if (_pendingFrame != NO_FRAME) {
            newFrame = _pendingFrame;
            updateFrame(newFrame, 0);
        }
    }

    protected static function clamp (n :Number, min :Number, max :Number) :Number {
        return Math.min(Math.max(n, min), max);
    }

    /** @private */
    protected var _isUpdatingFrame :Boolean;
    /** @private */
    protected var _pendingFrame :int = NO_FRAME;
    /** @private */
    protected var _frame :int = NO_FRAME, _stopFrame :int = NO_FRAME;
    /** @private */
    protected var _playing :Boolean = true;
    /** @private */
    protected var _playTime :Number, _duration :Number;
    /** @private */
    protected var _layers :Vector.<Layer>;
    /** @private */
    protected var _numFrames :int;
    /** @private */
    protected var _frameRate :Number;
    /** @private */
    protected var _labels :Vector.<Vector.<String>>;
    /** @private */
    private var _skipAdvanceTime :Boolean = false;
    /** @private */
    internal var _playerData :MoviePlayerNode;

    private static const NO_FRAME :int = -1;
}
}

//
// Flump - Copyright 2013 Flump Authors

package flump.display {

import flump.mold.MovieMold;

import react.Signal;

import starling.animation.IAnimatable;
import starling.display.DisplayObject;
import starling.display.Sprite;
import starling.events.Event;

/**
 * A movie created from flump-exported data. It has children corresponding to the layers in the
 * movie in Flash, in the same order and with the same names. It fills in those children
 * initially with the image or movie of the symbol on that exported layer. After the initial
 * population, it only applies the keyframe-based transformations to the child at the index
 * corresponding to the layer. This means it's safe to swap in other DisplayObjects at those
 * positions to have them animated in place of the initial child.
 *
 *
A Movie will not animate unless it's added to a Juggler (or its advanceTime() function
 * is otherwise called). When the movie is added to a juggler, it advances its playhead with the
 * frame ticks if isPlaying is true. It will automatically remove itself from its juggler when
 * removed from the stage.

 *
 * @see Library and LibraryLoader to create instances of Movie.
 */
public class Movie extends Sprite
    implements IAnimatable
{
    /** A label fired by all movies when entering their first frame. */
    public static const FIRST_FRAME :String = "flump.movie.FIRST_FRAME";

    /** A label fired by all movies when entering their last frame. */
    public static const LAST_FRAME :String = "flump.movie.LAST_FRAME";

    /** Fires the label string whenever it's passed in playing. */
    public const labelPassed :Signal = new Signal(String);

    /** @private */
    public function Movie (src :MovieMold, frameRate :Number, library :Library) {
        this.name = src.id;
        _labels = src.labels;
        _frameRate = frameRate;
        if (src.flipbook) {
            _layers = new Vector.<Layer>(1, true);
            _layers[0] = new Layer(this, src.layers[0], library, /*flipbook=*/true);
            _numFrames = src.layers[0].frames;
        } else {
            _layers = new Vector.<Layer>(src.layers.length, true);
            for (var ii :int = 0; ii < _layers.length; ii++) {
                _layers[ii] = new Layer(this, src.layers[ii], library, /*flipbook=*/false);
                _numFrames = Math.max(src.layers[ii].frames, _numFrames);
            }
        }
        _duration = _numFrames / _frameRate;
        updateFrame(0, 0);

        // When we're removed from the stage, remove ourselves from any juggler animating us.
        addEventListener(Event.REMOVED_FROM_STAGE, function (..._) :void {
            dispatchEventWith(Event.REMOVE_FROM_JUGGLER);
        });
    }

    /** @return the frame being displayed. */
    public function get frame () :int { return _frame; }

    /** @return the number of frames in the movie. */
    public function get numFrames () :int { return _numFrames; }

    /** @return true if the movie is currently playing. */
    public function get isPlaying () :Boolean { return _playing; }

    /** @return true if the movie contains the given label. */
    public function hasLabel (label :String) :Boolean {
        return getFrameForLabel(label) >= 0;
    }

    /** Plays the movie from its current frame. The movie will loop forever.  */
    public function loop () :Movie {
        _playing = true;
        _stopFrame = NO_FRAME;
        return this;
    }

    /** Plays the movie from its current frame, stopping when it reaches its last frame. */
    public function playOnce () :Movie { return playTo(LAST_FRAME); }

    /**
     * Moves to the given String label or int frame. Doesn't alter playing status or stop frame.
     * If there are labels at the given position, they're fired as part of the goto, even if the
     * current frame is equal to the destination. Labels between the current frame and the
     * destination frame are not fired.
     *
     * @param position the int frame or String label to goto.
     *
     * @return this movie for chaining
     *
     * @throws Error if position isn't an int or String, or if it is a String and that String isn't
     * a label on this movie
     */
    public function goTo (position :Object) :Movie {
        const frame :int = extractFrame(position);
        updateFrame(frame, 0);
        return this;
    }

   /**
    * Plays the movie from its current frame. The movie will stop when it reaches the given label
    * or frame.
    *
    * @param position to int frame or String label to stop at
    *
    * @return this movie for chaining
    *
    * @throws Error if position isn't an int or String, or if it is a String and that String isn't
    * a label on this movie.
    */
    public function playTo (position :Object) :Movie {
       // won't play if we're already at the stop position
       return stopAt(position).play();
    }

    /**
     * Sets the stop frame for this Movie.
     *
     * @param position the int frame or String label to stop at.
     *
     * @return this movie for chaining
     *
     * @throws Error if position isn't an int or String, or if it is a String and that String isn't
     * a label on this movie.
     */
    public function stopAt (position :Object) :Movie {
        _stopFrame = extractFrame(position);
        return this;
    }

    /**
     * Sets the movie playing. It will automatically stop at its stopFrame, if one is set,
     * otherwise it will loop forever.
     *
     * @return this movie for chaining
     */
    public function play () :Movie {
        // set playing to true unless movie is at the stop frame
        _playing = (_frame != _stopFrame);
        return this;
    }

    /** Stops playback if it's currently active. Doesn't alter the current frame or stop frame. */
    public function stop () :Movie {
        _playing = false;
        return this;
    }

    /** Advances the playhead by the give number of seconds. From IAnimatable. */
    public function advanceTime (dt :Number) :void {
        if (dt < 0) throw new Error("Invalid time [dt=" + dt + "]");
        if (_skipAdvanceTime) { _skipAdvanceTime = false; return; }
        if (!_playing) return;

        if (_numFrames > 1) {
            _playTime += dt;
            var actualPlaytime :Number = _playTime;
            if (_playTime >= _duration) _playTime %= _duration;

            // If _playTime is very close to _duration, rounding error can cause us to
            // land on lastFrame + 1. Protect against that.
            var newFrame :int = clamp(int(_playTime * _frameRate), 0, _numFrames - 1);

            // If the update crosses or goes to the stopFrame:
            // go to the stopFrame, stop the movie, clear the stopFrame
            if (_stopFrame != NO_FRAME) {
                // how many frames remain to the stopframe?
                var framesRemaining :int =
                    (_frame <= _stopFrame ? _stopFrame - _frame : _numFrames - _frame + _stopFrame);
                var framesElapsed :int = int(actualPlaytime * _frameRate) - _frame;
                if (framesElapsed >= framesRemaining) {
                    _playing = false;
                    newFrame = _stopFrame;
                }
            }
            updateFrame(newFrame, dt);
        }

        for (var ii :int = this.numChildren - 1; ii >= 0; --ii) {
            var child :DisplayObject = getChildAt(ii);
            if (child is Movie) {
                Movie(child).advanceTime(dt);
            }
        }
    }

    /**
     * @private
     *
     * Called when the Movie has been newly added to a layer.
     */
    internal function addedToLayer () :void {
        goTo(0);
        _skipAdvanceTime = true;
    }

    /** @private */
    protected function extractFrame (position :Object) :int {
        if (position is int) return int(position);
        if (!(position is String)) throw new Error("Movie position must be an int frame or String label");
        const label :String = String(position);
        var frame :int = getFrameForLabel(label);
        if (frame < 0) {
            throw new Error("No such label '" + label + "'");
        }
        return frame;
    }

    /**
     * @private
     *
     * Returns the frame index for the given label, or -1 if the label doesn't exist.
     */
    protected function getFrameForLabel (label :String) :int {
        for (var ii :int = 0; ii < _labels.length; ii++) {
            if (_labels[ii] != null && _labels[ii].indexOf(label) != -1) return ii;
        }
        return -1;
    }

    /**
     * @private
     *
     * @param dt the timeline's elapsed time since the last update. This should be 0
     * for updates that are the result of a "goTo" call.
     */
    protected function updateFrame (newFrame :int, dt :Number) :void {
        if (newFrame < 0 || newFrame >= _numFrames) {
            throw new Error("Invalid frame [frame=" + newFrame,
                " validRange=0-" + (_numFrames - 1) + "]");
        }

        if (_isUpdatingFrame) {
            _pendingFrame = newFrame;
            return;
        } else {
            _pendingFrame = NO_FRAME;
            _isUpdatingFrame = true;
        }

        const isGoTo :Boolean = (dt <= 0);
        const wrapped :Boolean = (dt >= _duration) || (newFrame < _frame);

        if (newFrame != _frame) {
            if (wrapped) {
                for each (var layer :Layer in _layers) {
                    layer.movieLooped();
                }
            }
            for each (layer in _layers) layer.drawFrame(newFrame);
        }

        if (isGoTo) _playTime = newFrame / _frameRate;

        // Update the frame before firing frame label signals, so if firing changes the frame,
        // it sticks.
        const oldFrame :int = _frame;
        _frame = newFrame;

        // determine which labels to fire signals for
        var startFrame :int;
        var frameCount :int;
        if (isGoTo) {
            startFrame = newFrame;
            frameCount = 1;
        } else {
            startFrame = (oldFrame + 1 < _numFrames ? oldFrame + 1 : 0);
            frameCount = (_frame - oldFrame);
            if (wrapped) frameCount += _numFrames;
        }

        // Fire signals. Stop if pendingFrame is updated, which indicates that the client
        // has called goTo()
        var frameIdx :int = startFrame;
        for (var ii :int = 0; ii < frameCount; ++ii) {
            if (_pendingFrame != NO_FRAME) break;

            if (_labels[frameIdx] != null) {
                for each (var label :String in _labels[frameIdx]) {
                    labelPassed.emit(label);
                    if (_pendingFrame != NO_FRAME) break;
                }
            }

            // avoid modulo division by updating frameIdx each time through the loop
            if (++frameIdx == _numFrames) {
                frameIdx = 0;
            }
        }

        _isUpdatingFrame = false;
        // If we were interrupted by a goTo(), update to that frame now.
        if (_pendingFrame != NO_FRAME) {
            newFrame = _pendingFrame;
            updateFrame(newFrame, 0);
        }
    }

    protected static function clamp (n :Number, min :Number, max :Number) :Number {
        return Math.min(Math.max(n, min), max);
    }

    /** @private */
    protected var _isUpdatingFrame :Boolean;
    /** @private */
    protected var _pendingFrame :int = NO_FRAME;
    /** @private */
    protected var _frame :int = NO_FRAME, _stopFrame :int = NO_FRAME;
    /** @private */
    protected var _playing :Boolean = true;
    /** @private */
    protected var _playTime :Number, _duration :Number;
    /** @private */
    protected var _layers :Vector.<Layer>;
    /** @private */
    protected var _numFrames :int;
    /** @private */
    protected var _frameRate :Number;
    /** @private */
    protected var _labels :Vector.<Vector.<String>>;
    /** @private */
    private var _skipAdvanceTime :Boolean = false;
    /** @private */
    internal var _playerData :MoviePlayerNode;

    private static const NO_FRAME :int = -1;
}
}

Remove superfluous conditional

Remove superfluous conditional

If _stopFrame is NO_FRAME (-1) _frame should never equal it