////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008-2013 Esri. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
////////////////////////////////////////////////////////////////////////////////
package modules.supportClasses
{

[Bindable]
public class LayerSettings
{
    public var name:String;
    public var fields:Array = [];

    //preserve
    private var showObjectId:XML;
    private var showGlobalId:XML;
    private var exportLocation:XML;
    private var showAttachments:XML;
    private var showRelatedRecords:XML;
    private var columnsOrder:XML;
    private var sublayer:XML;

    public function fromXML(layerSettingsXML:XML):void
    {
        name = layerSettingsXML.@name[0];

        var parsedFieldSettings:FieldSettings;
        for each (var fieldXML:XML in layerSettingsXML.fields.field)
        {
            parsedFieldSettings = new FieldSettings();
            parsedFieldSettings.fromXML(fieldXML);
            if (parsedFieldSettings.name)
            {
                fields.push(parsedFieldSettings);
            }
        }

        showObjectId = layerSettingsXML.showobjectid[0];
        showGlobalId = layerSettingsXML.showglobalid[0];
        exportLocation = layerSettingsXML.exportlocation[0];
        showAttachments = layerSettingsXML.showattachments[0];
        showRelatedRecords = layerSettingsXML.showrelatedrecords[0];
        columnsOrder = layerSettingsXML.columnsorder[0];
        sublayer = layerSettingsXML.sublayer[0];
    }

    public function toXML():XML
    {
        var configXML:XML = <layer name={name}/>;
        if (fields && fields.length > 0)
        {
            var fieldsXML:XML = <fields/>;
            var fieldXML:XML;
            for each (var field:FieldSettings in fields)
            {
                fieldXML = <field name={field.name}/>;
                if (field.alias)
                {
                    fieldXML.@alias = field.alias;
                }
                if (field.tooltip)
                {
                    fieldXML.@tooltip = field.tooltip;
                }

                fieldsXML.appendChild(fieldXML);
            }

            if (fieldsXML.children().length() > 0)
            {
                configXML.appendChild(fieldsXML);
            }
        }

        if (showObjectId)
        {
            configXML.appendChild(showObjectId);

        }
        if (showGlobalId)
        {
            configXML.appendChild(showGlobalId);
        }
        if (exportLocation)
        {
            configXML.appendChild(exportLocation);
        }
        if (showAttachments)
        {
            configXML.appendChild(showAttachments);
        }
        if (showRelatedRecords)
        {
            configXML.appendChild(showRelatedRecords);
        }
        if (columnsOrder)
        {
            configXML.appendChild(columnsOrder);
        }
        if (sublayer)
        {
            configXML.appendChild(sublayer);
        }

        return configXML;
    }
}
}

////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008-2013 Esri. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
////////////////////////////////////////////////////////////////////////////////
package modules.supportClasses
{

[Bindable]
public class LayerSettings
{
    public var name:String;
    public var fields:Array = [];

    //preserve
    private var showObjectId:XML;
    private var showGlobalId:XML;
    private var exportLocation:XML;
    private var showAttachments:XML;
    private var showRelatedRecords:XML;
    private var columnsOrder:XML;
    private var sublayer:XML;

    public function fromXML(layerSettingsXML:XML):void
    {
        name = layerSettingsXML.@name[0];

        var parsedFieldSettings:FieldSettings;
        for each (var fieldXML:XML in layerSettingsXML.fields.field)
        {
            parsedFieldSettings = new FieldSettings();
            parsedFieldSettings.fromXML(fieldXML);
            if (parsedFieldSettings.name)
            {
                fields.push(parsedFieldSettings);
            }
        }

        showObjectId = layerSettingsXML.showobjectid[0];
        showGlobalId = layerSettingsXML.showglobalid[0];
        exportLocation = layerSettingsXML.exportlocation[0];
        showAttachments = layerSettingsXML.showattachments[0];
        showRelatedRecords = layerSettingsXML.showrelatedrecords[0];
        columnsOrder = layerSettingsXML.columnsorder[0];
        sublayer = layerSettingsXML.sublayer[0];
    }

    public function toXML():XML
    {
        var configXML:XML = <layer name={name}/>;
        if (fields && fields.length > 0)
        {
            var fieldsXML:XML = <fields/>;
            for each (var field:FieldSettings in fields)
            {
                fieldsXML.appendChild(field.toXML());
            }

            if (fieldsXML.children().length() > 0)
            {
                configXML.appendChild(fieldsXML);
            }
        }

        if (showObjectId)
        {
            configXML.appendChild(showObjectId);

        }
        if (showGlobalId)
        {
            configXML.appendChild(showGlobalId);
        }
        if (exportLocation)
        {
            configXML.appendChild(exportLocation);
        }
        if (showAttachments)
        {
            configXML.appendChild(showAttachments);
        }
        if (showRelatedRecords)
        {
            configXML.appendChild(showRelatedRecords);
        }
        if (columnsOrder)
        {
            configXML.appendChild(columnsOrder);
        }
        if (sublayer)
        {
            configXML.appendChild(sublayer);
        }

        return configXML;
    }
}
}

Remove redundant code.

Remove redundant code.


package dolly {
import dolly.core.dolly_internal;

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

use namespace dolly_internal;

public class ClonerTest {

	private var classMarkedAsCloneable:ClassMarkedAsCloneable;
	private var classWithSomeCloneableFields:ClassWithSomeCloneableFields;
	private var classMarkedAsCloneableType:Type;
	private var classWithSomeCloneableFieldsType:Type;

	[Before]
	public function before():void {
		classMarkedAsCloneable = new ClassMarkedAsCloneable();
		classMarkedAsCloneable.property1 = "value 1";
		classMarkedAsCloneable.property2 = "value 2";
		classMarkedAsCloneable.property3 = "value 3";
		classMarkedAsCloneable.writableField = "value 4";

		classWithSomeCloneableFields = new ClassWithSomeCloneableFields();
		classWithSomeCloneableFields.property1 = "value 1";
		classWithSomeCloneableFields.property2 = "value 2";
		classWithSomeCloneableFields.property3 = "value 3";
		classWithSomeCloneableFields.writableField = "value 4";

		classMarkedAsCloneableType = Type.forInstance(classMarkedAsCloneable);
		classWithSomeCloneableFieldsType = Type.forInstance(classWithSomeCloneableFields);
	}

	[After]
	public function after():void {
		classMarkedAsCloneable = null;
		classWithSomeCloneableFields = null;
		classMarkedAsCloneableType = null;
		classWithSomeCloneableFieldsType = null;
	}

	[Test]
	public function testGetCloneableFields():void {
		var cloneableFields:Vector.<Field> = Cloner.getCloneableFieldsForType(classMarkedAsCloneableType);

		assertNotNull(cloneableFields);
		assertEquals(4, cloneableFields.length);

		cloneableFields = Cloner.getCloneableFieldsForType(classWithSomeCloneableFieldsType);

		assertNotNull(cloneableFields);
		assertEquals(3, cloneableFields.length);
	}

	[Test]
	public function testCloneWithClassLevelMetadata():void {
		const clone1:ClassMarkedAsCloneable = Cloner.clone(classMarkedAsCloneable) as ClassMarkedAsCloneable;

		assertNotNull(clone1);
		assertNotNull(clone1.property1);
		assertEquals(clone1.property1, classMarkedAsCloneable.property1);
		assertNotNull(clone1.property2);
		assertEquals(clone1.property2, classMarkedAsCloneable.property2);
		assertNotNull(clone1.property3);
		assertEquals(clone1.property3, classMarkedAsCloneable.property3);
		assertNotNull(clone1.writableField);
		assertEquals(clone1.writableField, classMarkedAsCloneable.writableField);
	}

	public function testCloneWithPropertyLevelMetadata():void {
		const clone2:ClassWithSomeCloneableFields = Cloner.clone(
				classWithSomeCloneableFields
		) as ClassWithSomeCloneableFields;

		assertNotNull(clone2);
		assertNull(clone2.property1);
		assertNotNull(clone2.property2);
		assertEquals(clone2.property2, classWithSomeCloneableFields.property2);
		assertNotNull(clone2.property3);
		assertEquals(clone2.property3, classWithSomeCloneableFields.property3);
		assertNotNull(clone2.writableField);
		assertEquals(clone2.writableField, classWithSomeCloneableFields.writableField);
	}
}
}

package dolly {
import dolly.core.dolly_internal;

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

use namespace dolly_internal;

public class ClonerTest {

	private var classMarkedAsCloneable:ClassMarkedAsCloneable;
	private var classWithSomeCloneableFields:ClassWithSomeCloneableFields;
	private var classMarkedAsCloneableType:Type;
	private var classWithSomeCloneableFieldsType:Type;

	[Before]
	public function before():void {
		classMarkedAsCloneable = new ClassMarkedAsCloneable();
		classMarkedAsCloneable.property1 = "value 1";
		classMarkedAsCloneable.property2 = "value 2";
		classMarkedAsCloneable.property3 = "value 3";
		classMarkedAsCloneable.writableField = "value 4";

		classWithSomeCloneableFields = new ClassWithSomeCloneableFields();
		classWithSomeCloneableFields.property1 = "value 1";
		classWithSomeCloneableFields.property2 = "value 2";
		classWithSomeCloneableFields.property3 = "value 3";
		classWithSomeCloneableFields.writableField = "value 4";

		classMarkedAsCloneableType = Type.forInstance(classMarkedAsCloneable);
		classWithSomeCloneableFieldsType = Type.forInstance(classWithSomeCloneableFields);
	}

	[After]
	public function after():void {
		classMarkedAsCloneable = null;
		classWithSomeCloneableFields = null;
		classMarkedAsCloneableType = null;
		classWithSomeCloneableFieldsType = null;
	}

	[Test]
	public function testGetCloneableFields():void {
		var cloneableFields:Vector.<Field> = Cloner.getCloneableFieldsForType(classMarkedAsCloneableType);

		assertNotNull(cloneableFields);
		assertEquals(4, cloneableFields.length);

		cloneableFields = Cloner.getCloneableFieldsForType(classWithSomeCloneableFieldsType);

		assertNotNull(cloneableFields);
		assertEquals(3, cloneableFields.length);
	}

	[Test]
	public function testCloneWithClassLevelMetadata():void {
		const clone1:ClassMarkedAsCloneable = Cloner.clone(classMarkedAsCloneable) as ClassMarkedAsCloneable;

		assertNotNull(clone1);
		assertNotNull(clone1.property1);
		assertEquals(clone1.property1, classMarkedAsCloneable.property1);
		assertNotNull(clone1.property2);
		assertEquals(clone1.property2, classMarkedAsCloneable.property2);
		assertNotNull(clone1.property3);
		assertEquals(clone1.property3, classMarkedAsCloneable.property3);
		assertNotNull(clone1.writableField);
		assertEquals(clone1.writableField, classMarkedAsCloneable.writableField);
	}

	[Test]
	public function testCloneWithPropertyLevelMetadata():void {
		const clone2:ClassWithSomeCloneableFields = Cloner.clone(
				classWithSomeCloneableFields
		) as ClassWithSomeCloneableFields;

		assertNotNull(clone2);
		assertNull(clone2.property1);
		assertNotNull(clone2.property2);
		assertEquals(clone2.property2, classWithSomeCloneableFields.property2);
		assertNotNull(clone2.property3);
		assertEquals(clone2.property3, classWithSomeCloneableFields.property3);
		assertNotNull(clone2.writableField);
		assertEquals(clone2.writableField, classWithSomeCloneableFields.writableField);
	}
}
}

Add [Test] metadata tag.

Add [Test] metadata tag.


package org.mangui.HLS.muxing {
   import flash.utils.ByteArray;
   import org.mangui.HLS.utils.Log;

   public class ID3
   {
      public var len:Number;
      public var hasTimestamp:Boolean = false;
      public var timestamp:Number;

      /* create ID3 object by parsing ByteArray, looking for ID3 tag length, and timestamp */
      public function ID3(data:ByteArray) {
         var tagSize:uint = 0;
         try {
            var pos:Number = data.position;
            do {
              if(data.readUTFBytes(3) == 'ID3') {
                // skip 24 bits
                data.position += 3;
                // retrieve tag length
                var byte1:uint = data.readUnsignedByte() & 0x7f;
                var byte2:uint = data.readUnsignedByte() & 0x7f;
                var byte3:uint = data.readUnsignedByte() & 0x7f;
                var byte4:uint = data.readUnsignedByte() & 0x7f;
                tagSize = (byte1 << 21) + (byte2 << 14) + (byte3 << 7) + byte4;

                var end_pos:Number = data.position + tagSize;
                // read tag
                _parseFrame(data);
                data.position = end_pos;
              } else {
                data.position-=3;
                Log.debug2("ID3 len:"+ (data.position-pos));
                len = data.position-pos;
                return;
              }
            } while (true);
         } catch(e:Error)
         {
         }
         len = 0;
         return;
      };

      /*  Each Elementary Audio Stream segment MUST signal the timestamp of
          its first sample with an ID3 PRIV tag [ID3] at the beginning of
          the segment.  The ID3 PRIV owner identifier MUST be
          "com.apple.streaming.transportStreamTimestamp".  The ID3 payload
          MUST be a 33-bit MPEG-2 Program Elementary Stream timestamp
          expressed as a big-endian eight-octet number, with the upper 31
          bits set to zero.
       */

      private function _parseFrame(data:ByteArray):void {
        if (data.readUTFBytes(4) == "PRIV") {
          var frame_len:uint = data.readUnsignedInt();
          // smelling good ! 53 is the size of tag we are looking for
          if(frame_len == 53) {
            // skip flags (2 bytes)
            data.position+=2;
            // owner should be "com.apple.streaming.transportStreamTimestamp"
            if(data.readUTFBytes(44) == 'com.apple.streaming.transportStreamTimestamp') {
              // smelling even better ! we found the right descriptor
              // skip null character (string end) + 3 first bytes
              data.position+=4;
              // timestamp is 33 bit expressed as a big-endian eight-octet number, with the upper 31 bits set to zero.
              var pts_33_bit:Number = data.readUnsignedByte() & 0x1;
              hasTimestamp = true;
              timestamp = (data.readUnsignedInt()/90) << pts_33_bit;
              Log.debug("ID3 timestamp found:"+timestamp);
            }
          }
        }
      }
   }
}

package org.mangui.HLS.muxing {
   import flash.utils.ByteArray;
   import org.mangui.HLS.utils.Log;

   public class ID3
   {
      public var len:Number;
      public var hasTimestamp:Boolean = false;
      public var timestamp:Number;

      /* create ID3 object by parsing ByteArray, looking for ID3 tag length, and timestamp */
      public function ID3(data:ByteArray) {
         var tagSize:uint = 0;
         try {
            var pos:Number = data.position;
            do {
              if(data.readUTFBytes(3) == 'ID3') {
                // skip 24 bits
                data.position += 3;
                // retrieve tag length
                var byte1:uint = data.readUnsignedByte() & 0x7f;
                var byte2:uint = data.readUnsignedByte() & 0x7f;
                var byte3:uint = data.readUnsignedByte() & 0x7f;
                var byte4:uint = data.readUnsignedByte() & 0x7f;
                tagSize = (byte1 << 21) + (byte2 << 14) + (byte3 << 7) + byte4;

                var end_pos:Number = data.position + tagSize;
                // read tag
                _parseFrame(data);
                data.position = end_pos;
              } else {
                data.position-=3;
                len = data.position-pos;
                if (len) {
                  Log.debug2("ID3 len:"+ len);
                }
                return;
              }
            } while (true);
         } catch(e:Error)
         {
         }
         len = 0;
         return;
      };

      /*  Each Elementary Audio Stream segment MUST signal the timestamp of
          its first sample with an ID3 PRIV tag [ID3] at the beginning of
          the segment.  The ID3 PRIV owner identifier MUST be
          "com.apple.streaming.transportStreamTimestamp".  The ID3 payload
          MUST be a 33-bit MPEG-2 Program Elementary Stream timestamp
          expressed as a big-endian eight-octet number, with the upper 31
          bits set to zero.
       */

      private function _parseFrame(data:ByteArray):void {
        if (data.readUTFBytes(4) == "PRIV") {
          var frame_len:uint = data.readUnsignedInt();
          // smelling good ! 53 is the size of tag we are looking for
          if(frame_len == 53) {
            // skip flags (2 bytes)
            data.position+=2;
            // owner should be "com.apple.streaming.transportStreamTimestamp"
            if(data.readUTFBytes(44) == 'com.apple.streaming.transportStreamTimestamp') {
              // smelling even better ! we found the right descriptor
              // skip null character (string end) + 3 first bytes
              data.position+=4;
              // timestamp is 33 bit expressed as a big-endian eight-octet number, with the upper 31 bits set to zero.
              var pts_33_bit:Number = data.readUnsignedByte() & 0x1;
              hasTimestamp = true;
              timestamp = (data.readUnsignedInt()/90) << pts_33_bit;
              Log.debug("ID3 timestamp found:"+timestamp);
            }
          }
        }
      }
   }
}

reduce log2 verbosity when extracting ID3 tags


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

    /**
     *  The OneFlexibleChildVerticalLayout class is a simple layout
     *  bead.  It takes the set of children and lays them out
     *  vertically in one column, separating them according to
     *  CSS layout rules for margin and padding styles. But it
     *  will size the one child to take up as much or little
     *  room as possible.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class OneFlexibleChildVerticalLayout implements IBeadLayout, IDocument
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function OneFlexibleChildVerticalLayout()
		{
		}

        /**
         *  The id of the flexible child
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public var flexibleChild:String;

        private var actualChild:ILayoutChild;

        // the strand/host container is also an ILayoutChild because
        // can have its size dictated by the host's parent which is
        // important to know for layout optimization
        private var host:ILayoutChild;

        /**
         *  @private
         *  The document.
         */
        private var document:Object;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function set strand(value:IStrand):void
		{
            host = value as ILayoutChild;
		}

        private var _maxWidth:Number;

        /**
         *  @copy org.apache.flex.core.IBead#maxWidth
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function get maxWidth():Number
        {
            return _maxWidth;
        }

        /**
         *  @private
         */
        public function set maxWidth(value:Number):void
        {
            _maxWidth = value;
        }

        private var _maxHeight:Number;

        /**
         *  @copy org.apache.flex.core.IBead#maxHeight
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function get maxHeight():Number
        {
            return _maxHeight;
        }

        /**
         *  @private
         */
        public function set maxHeight(value:Number):void
        {
            _maxHeight = value;
        }

        /**
         * @copy org.apache.flex.core.IBeadLayout#layout
         */
		public function layout():Boolean
		{
            var layoutParent:ILayoutParent = host.getBeadByType(ILayoutParent) as ILayoutParent;
            var contentView:IParentIUIBase = layoutParent ? layoutParent.contentView : IParentIUIBase(host);
			actualChild = document[flexibleChild];

            var ilc:ILayoutChild;
			var n:int = contentView.numElements;
			var marginLeft:Object;
			var marginRight:Object;
			var marginTop:Object;
			var marginBottom:Object;
			var margin:Object;
			maxWidth = 0;
			var horizontalMargins:Array = new Array(n);

            var hh:Number = layoutParent.resizableView.height;
            var padding:Object = determinePadding();
            if (isNaN(padding.paddingBottom))
                padding.paddingBottom = 0;
            hh -= padding.paddingTop + padding.paddingBottom;
            var yy:int = padding.paddingTop;
            var flexChildIndex:int;
            var ml:Number;
            var mr:Number;
            var mt:Number;
            var mb:Number;
            var lastmb:Number;
            var lastmt:Number;
            var halign:Object;

            for (var i:int = 0; i < n; i++)
            {
                var child:IUIBase = contentView.getElementAt(i) as IUIBase;
                if (child == actualChild)
                {
                    flexChildIndex = i;
                    break;
                }
                margin = ValuesManager.valuesImpl.getValue(child, "margin");
                if (margin is Array)
                {
                    if (margin.length == 1)
                        marginLeft = marginTop = marginRight = marginBottom = margin[0];
                    else if (margin.length <= 3)
                    {
                        marginLeft = marginRight = margin[1];
                        marginTop = marginBottom = margin[0];
                    }
                    else if (margin.length == 4)
                    {
                        marginLeft = margin[3];
                        marginBottom = margin[2];
                        marginRight = margin[1];
                        marginTop = margin[0];
                    }
                }
                else if (margin == null)
                {
                    marginLeft = ValuesManager.valuesImpl.getValue(child, "margin-left");
                    marginTop = ValuesManager.valuesImpl.getValue(child, "margin-top");
                    marginRight = ValuesManager.valuesImpl.getValue(child, "margin-right");
                    marginBottom = ValuesManager.valuesImpl.getValue(child, "margin-bottom");
                }
                else
                {
                    marginLeft = marginTop = marginBottom = marginRight = margin;
                }
                mt = Number(marginTop);
                if (isNaN(mt))
                    mt = 0;
                mb = Number(marginBottom);
                if (isNaN(mb))
                    mb = 0;
                if (marginLeft == "auto")
                    ml = 0;
                else
                {
                    ml = Number(marginLeft);
                    if (isNaN(ml))
                        ml = 0;
                }
                if (marginRight == "auto")
                    mr = 0;
                else
                {
                    mr = Number(marginRight);
                    if (isNaN(mr))
                        mr = 0;
                }
                child.x = ml;
                if (child is ILayoutChild)
                {
                    ilc = child as ILayoutChild;
                    if (!isNaN(ilc.percentWidth))
                        ilc.setWidth(contentView.width * ilc.percentWidth / 100, true);
                }
                maxWidth = Math.max(maxWidth, ml + child.width + mr);
                child.y = yy + mt;
                yy += child.height + mt + mb;
                lastmb = mb;
                halign = ValuesManager.valuesImpl.getValue(child, "horizontal-align");
                horizontalMargins[i] = { marginLeft: ml, marginRight: mr, halign: halign };
            }

            if (n > 0 && n > flexChildIndex)
            {
                for (i = n - 1; i > flexChildIndex; i--)
    			{
    				child = contentView.getElementAt(i) as IUIBase;
    				margin = ValuesManager.valuesImpl.getValue(child, "margin");
    				if (margin is Array)
    				{
    					if (margin.length == 1)
    						marginLeft = marginTop = marginRight = marginBottom = margin[0];
    					else if (margin.length <= 3)
    					{
    						marginLeft = marginRight = margin[1];
    						marginTop = marginBottom = margin[0];
    					}
    					else if (margin.length == 4)
    					{
    						marginLeft = margin[3];
    						marginBottom = margin[2];
    						marginRight = margin[1];
    						marginTop = margin[0];
    					}
    				}
    				else if (margin == null)
    				{
    					marginLeft = ValuesManager.valuesImpl.getValue(child, "margin-left");
    					marginTop = ValuesManager.valuesImpl.getValue(child, "margin-top");
    					marginRight = ValuesManager.valuesImpl.getValue(child, "margin-right");
    					marginBottom = ValuesManager.valuesImpl.getValue(child, "margin-bottom");
    				}
    				else
    				{
    					marginLeft = marginTop = marginBottom = marginRight = margin;
    				}
    				mt = Number(marginTop);
    				if (isNaN(mt))
    					mt = 0;
    				mb = Number(marginBottom);
    				if (isNaN(mb))
    					mb = 0;
    				if (marginLeft == "auto")
    					ml = 0;
    				else
    				{
    					ml = Number(marginLeft);
    					if (isNaN(ml))
    						ml = 0;
    				}
    				if (marginRight == "auto")
    					mr = 0;
    				else
    				{
    					mr = Number(marginRight);
    					if (isNaN(mr))
    						mr = 0;
    				}
                    child.x = ml;
                    if (child is ILayoutChild)
                    {
                        ilc = child as ILayoutChild;
                        if (!isNaN(ilc.percentWidth))
                            ilc.setWidth(contentView.width * ilc.percentWidth / 100, true);
                    }
                    maxWidth = Math.max(maxWidth, ml + child.width + mr);
                    child.y = hh - child.height - mb;
    				hh -= child.height + mt + mb;
    				lastmt = mt;
                    halign = ValuesManager.valuesImpl.getValue(child, "horizontal-align");
                    horizontalMargins[i] = { marginLeft: ml, marginRight: mr, halign: halign };
    			}

                child = contentView.getElementAt(flexChildIndex) as IUIBase;
                margin = ValuesManager.valuesImpl.getValue(child, "margin");
                if (margin is Array)
                {
                    if (margin.length == 1)
                        marginLeft = marginTop = marginRight = marginBottom = margin[0];
                    else if (margin.length <= 3)
                    {
                        marginLeft = marginRight = margin[1];
                        marginTop = marginBottom = margin[0];
                    }
                    else if (margin.length == 4)
                    {
                        marginLeft = margin[3];
                        marginBottom = margin[2];
                        marginRight = margin[1];
                        marginTop = margin[0];
                    }
                }
                else if (margin == null)
                {
                    marginLeft = ValuesManager.valuesImpl.getValue(child, "margin-left");
                    marginTop = ValuesManager.valuesImpl.getValue(child, "margin-top");
                    marginRight = ValuesManager.valuesImpl.getValue(child, "margin-right");
                    marginBottom = ValuesManager.valuesImpl.getValue(child, "margin-bottom");
                }
                else
                {
                    marginLeft = marginTop = marginBottom = marginRight = margin;
                }
                mt = Number(marginTop);
                if (isNaN(mt))
                    mt = 0;
                mb = Number(marginBottom);
                if (isNaN(mb))
                    mb = 0;
                if (marginLeft == "auto")
                    ml = 0;
                else
                {
                    ml = Number(marginLeft);
                    if (isNaN(ml))
                        ml = 0;
                }
                if (marginRight == "auto")
                    mr = 0;
                else
                {
                    mr = Number(marginRight);
                    if (isNaN(mr))
                        mr = 0;
                }
                child.x = ml;
                if (child is ILayoutChild)
                {
                    ilc = child as ILayoutChild;
                    if (!isNaN(ilc.percentWidth))
                        ilc.setWidth(contentView.width * ilc.percentWidth / 100, true);
                }
                maxWidth = Math.max(maxWidth, ml + child.width + mr);
                child.y = yy + mt;
                child.height = hh - yy - mb;
                halign = ValuesManager.valuesImpl.getValue(child, "horizontal-align");
                horizontalMargins[flexChildIndex] = { marginLeft: ml, marginRight: mr, halign: halign };
            }

            for (i = 0; i < n; i++)
			{
				var obj:Object = horizontalMargins[0]
				child = contentView.getElementAt(i) as IUIBase;
				if (obj.halign == "center")
					child.x = (maxWidth - child.width) / 2;
				else if (obj.halign == "bottom")
					child.x = maxWidth - child.width - obj.marginRight;
				else
					child.x = obj.marginLeft;
			}
            return true;
		}

        // TODO (aharui): utility class or base class
        /**
         *  Determines the top and left padding values, if any, as set by
         *  padding style values. This includes "padding" for all padding values
         *  as well as "padding-left" and "padding-top".
         *
         *  Returns an object with paddingLeft and paddingTop properties.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        protected function determinePadding():Object
        {
            var paddingLeft:Object;
            var paddingTop:Object;
            var paddingRight:Object;
            var padding:Object = ValuesManager.valuesImpl.getValue(host, "padding");
            if (typeof(padding) == "Array")
            {
                if (padding.length == 1)
                    paddingLeft = paddingTop = paddingRight = padding[0];
                else if (padding.length <= 3)
                {
                    paddingLeft = padding[1];
                    paddingTop = padding[0];
                    paddingRight = padding[1];
                }
                else if (padding.length == 4)
                {
                    paddingLeft = padding[3];
                    paddingTop = padding[0];
                    paddingRight = padding[1];
                }
            }
            else if (padding == null)
            {
                paddingLeft = ValuesManager.valuesImpl.getValue(host, "padding-left");
                paddingTop = ValuesManager.valuesImpl.getValue(host, "padding-top");
                paddingRight = ValuesManager.valuesImpl.getValue(host, "padding-right");
            }
            else
            {
                paddingLeft = paddingTop = paddingRight = padding;
            }
            var pl:Number = Number(paddingLeft);
            var pt:Number = Number(paddingTop);
            var pr:Number = Number(paddingRight);
            if (isNaN(pl))
                pl = 0;
            if (isNaN(pr))
                pr = 0;
            if (isNaN(pt))
                pt = 0;
            return {paddingLeft:pl, paddingTop:pt, paddingRight:pr};
        }

        public function setDocument(document:Object, id:String = null):void
        {
            this.document = document;
        }

    }

}

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

    /**
     *  The OneFlexibleChildVerticalLayout class is a simple layout
     *  bead.  It takes the set of children and lays them out
     *  vertically in one column, separating them according to
     *  CSS layout rules for margin and padding styles. But it
     *  will size the one child to take up as much or little
     *  room as possible.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
	public class OneFlexibleChildVerticalLayout implements IBeadLayout, IDocument
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function OneFlexibleChildVerticalLayout()
		{
		}

        /**
         *  The id of the flexible child
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public var flexibleChild:String;

        private var actualChild:ILayoutChild;

        // the strand/host container is also an ILayoutChild because
        // can have its size dictated by the host's parent which is
        // important to know for layout optimization
        private var host:ILayoutChild;

        /**
         *  @private
         *  The document.
         */
        private var document:Object;

        /**
         *  @copy org.apache.flex.core.IBead#strand
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function set strand(value:IStrand):void
		{
            host = value as ILayoutChild;
		}

        private var _maxWidth:Number;

        /**
         *  @copy org.apache.flex.core.IBead#maxWidth
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function get maxWidth():Number
        {
            return _maxWidth;
        }

        /**
         *  @private
         */
        public function set maxWidth(value:Number):void
        {
            _maxWidth = value;
        }

        private var _maxHeight:Number;

        /**
         *  @copy org.apache.flex.core.IBead#maxHeight
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function get maxHeight():Number
        {
            return _maxHeight;
        }

        /**
         *  @private
         */
        public function set maxHeight(value:Number):void
        {
            _maxHeight = value;
        }

        /**
         * @copy org.apache.flex.core.IBeadLayout#layout
         */
		public function layout():Boolean
		{
            var layoutParent:ILayoutParent = host.getBeadByType(ILayoutParent) as ILayoutParent;
            var contentView:IParentIUIBase = layoutParent ? layoutParent.contentView : IParentIUIBase(host);
			actualChild = document[flexibleChild];

            var ilc:ILayoutChild;
			var n:int = contentView.numElements;
			var marginLeft:Object;
			var marginRight:Object;
			var marginTop:Object;
			var marginBottom:Object;
			var margin:Object;
			maxWidth = 0;

            var w:Number = contentView.width;
            var hh:Number = contentView.height;
            var yy:int = 0;
            var flexChildIndex:int;
            var ml:Number;
            var mr:Number;
            var mt:Number;
            var mb:Number;
            var lastmb:Number;
            var lastmt:Number;
            var halign:Object;
            var left:Number;
            var right:Number;

            for (var i:int = 0; i < n; i++)
            {
                var child:IUIBase = contentView.getElementAt(i) as IUIBase;
                ilc = child as ILayoutChild;
                left = ValuesManager.valuesImpl.getValue(child, "left");
                right = ValuesManager.valuesImpl.getValue(child, "right");
                if (child == actualChild)
                {
                    flexChildIndex = i;
                    break;
                }
                margin = ValuesManager.valuesImpl.getValue(child, "margin");
                if (margin is Array)
                {
                    if (margin.length == 1)
                        marginLeft = marginTop = marginRight = marginBottom = margin[0];
                    else if (margin.length <= 3)
                    {
                        marginLeft = marginRight = margin[1];
                        marginTop = marginBottom = margin[0];
                    }
                    else if (margin.length == 4)
                    {
                        marginLeft = margin[3];
                        marginBottom = margin[2];
                        marginRight = margin[1];
                        marginTop = margin[0];
                    }
                }
                else if (margin == null)
                {
                    marginLeft = ValuesManager.valuesImpl.getValue(child, "margin-left");
                    marginTop = ValuesManager.valuesImpl.getValue(child, "margin-top");
                    marginRight = ValuesManager.valuesImpl.getValue(child, "margin-right");
                    marginBottom = ValuesManager.valuesImpl.getValue(child, "margin-bottom");
                }
                else
                {
                    marginLeft = marginTop = marginBottom = marginRight = margin;
                }
                mt = Number(marginTop);
                if (isNaN(mt))
                    mt = 0;
                mb = Number(marginBottom);
                if (isNaN(mb))
                    mb = 0;
                if (ilc)
                {
                    if (!isNaN(ilc.percentHeight))
                        ilc.setHeight(contentView.height * ilc.percentHeight / 100, !isNaN(ilc.percentWidth));
                }
                if (marginLeft == "auto")
                    ml = 0;
                else
                {
                    ml = Number(marginLeft);
                    if (isNaN(ml))
                        ml = 0;
                }
                if (marginRight == "auto")
                    mr = 0;
                else
                {
                    mr = Number(marginRight);
                    if (isNaN(mr))
                        mr = 0;
                }
                child.x = ml;
                if (child is ILayoutChild)
                {
                    ilc = child as ILayoutChild;
                    if (!isNaN(ilc.percentWidth))
                        ilc.setWidth(contentView.width * ilc.percentWidth / 100, !isNaN(ilc.percentHeight));
                }
                maxWidth = Math.max(maxWidth, ml + child.width + mr);
                setPositionAndWidth(child, left, ml, right, mr, w);
                child.y = yy + mt;
                yy += child.height + mt + mb;
                lastmb = mb;
            }

            if (n > 0 && n > flexChildIndex)
            {
                for (i = n - 1; i > flexChildIndex; i--)
    			{
    				child = contentView.getElementAt(i) as IUIBase;
                    ilc = child as ILayoutChild;
                    left = ValuesManager.valuesImpl.getValue(child, "left");
                    right = ValuesManager.valuesImpl.getValue(child, "right");
    				margin = ValuesManager.valuesImpl.getValue(child, "margin");
    				if (margin is Array)
    				{
    					if (margin.length == 1)
    						marginLeft = marginTop = marginRight = marginBottom = margin[0];
    					else if (margin.length <= 3)
    					{
    						marginLeft = marginRight = margin[1];
    						marginTop = marginBottom = margin[0];
    					}
    					else if (margin.length == 4)
    					{
    						marginLeft = margin[3];
    						marginBottom = margin[2];
    						marginRight = margin[1];
    						marginTop = margin[0];
    					}
    				}
    				else if (margin == null)
    				{
    					marginLeft = ValuesManager.valuesImpl.getValue(child, "margin-left");
    					marginTop = ValuesManager.valuesImpl.getValue(child, "margin-top");
    					marginRight = ValuesManager.valuesImpl.getValue(child, "margin-right");
    					marginBottom = ValuesManager.valuesImpl.getValue(child, "margin-bottom");
    				}
    				else
    				{
    					marginLeft = marginTop = marginBottom = marginRight = margin;
    				}
    				mt = Number(marginTop);
    				if (isNaN(mt))
    					mt = 0;
    				mb = Number(marginBottom);
    				if (isNaN(mb))
    					mb = 0;
                    if (ilc)
                    {
                        if (!isNaN(ilc.percentHeight))
                            ilc.setHeight(contentView.height * ilc.percentHeight / 100, !isNaN(ilc.percentWidth));
                    }
    				if (marginLeft == "auto")
    					ml = 0;
    				else
    				{
    					ml = Number(marginLeft);
    					if (isNaN(ml))
    						ml = 0;
    				}
    				if (marginRight == "auto")
    					mr = 0;
    				else
    				{
    					mr = Number(marginRight);
    					if (isNaN(mr))
    						mr = 0;
    				}
                    if (child is ILayoutChild)
                    {
                        ilc = child as ILayoutChild;
                        if (!isNaN(ilc.percentWidth))
                            ilc.setWidth(contentView.width * ilc.percentWidth / 100, !isNaN(ilc.percentHeight));
                    }
                    setPositionAndWidth(child, left, ml, right, mr, w);
                    maxWidth = Math.max(maxWidth, ml + child.width + mr);
                    child.y = hh - child.height - mb;
    				hh -= child.height + mt + mb;
    				lastmt = mt;
    			}
            }

            child = contentView.getElementAt(flexChildIndex) as IUIBase;
            ilc = child as ILayoutChild;
            left = ValuesManager.valuesImpl.getValue(child, "left");
            right = ValuesManager.valuesImpl.getValue(child, "right");
            margin = ValuesManager.valuesImpl.getValue(child, "margin");
            if (margin is Array)
            {
                if (margin.length == 1)
                    marginLeft = marginTop = marginRight = marginBottom = margin[0];
                else if (margin.length <= 3)
                {
                    marginLeft = marginRight = margin[1];
                    marginTop = marginBottom = margin[0];
                }
                else if (margin.length == 4)
                {
                    marginLeft = margin[3];
                    marginBottom = margin[2];
                    marginRight = margin[1];
                    marginTop = margin[0];
                }
            }
            else if (margin == null)
            {
                marginLeft = ValuesManager.valuesImpl.getValue(child, "margin-left");
                marginTop = ValuesManager.valuesImpl.getValue(child, "margin-top");
                marginRight = ValuesManager.valuesImpl.getValue(child, "margin-right");
                marginBottom = ValuesManager.valuesImpl.getValue(child, "margin-bottom");
            }
            else
            {
                marginLeft = marginTop = marginBottom = marginRight = margin;
            }
            mt = Number(marginTop);
            if (isNaN(mt))
                mt = 0;
            mb = Number(marginBottom);
            if (isNaN(mb))
                mb = 0;
            if (ilc)
            {
                if (!isNaN(ilc.percentHeight))
                    ilc.setHeight(contentView.height * ilc.percentHeight / 100, !isNaN(ilc.percentWidth));
            }
            if (marginLeft == "auto")
                ml = 0;
            else
            {
                ml = Number(marginLeft);
                if (isNaN(ml))
                    ml = 0;
            }
            if (marginRight == "auto")
                mr = 0;
            else
            {
                mr = Number(marginRight);
                if (isNaN(mr))
                    mr = 0;
            }
            if (child is ILayoutChild)
            {
                ilc = child as ILayoutChild;
                if (!isNaN(ilc.percentWidth))
                    ilc.setWidth(contentView.width * ilc.percentWidth / 100, !isNaN(ilc.percentHeight));
            }
            setPositionAndWidth(child, left, ml, right, mr, w);
            maxWidth = Math.max(maxWidth, ml + child.width + mr);
            child.y = yy + mt;
            child.height = hh - yy - mb;

            return true;
		}

        private function setPositionAndWidth(child:IUIBase, left:Number, ml:Number,
                                             right:Number, mr:Number, w:Number):void
        {
            var widthSet:Boolean = false;

            var ww:Number = w;
            var ilc:ILayoutChild = child as ILayoutChild;
            if (!isNaN(left))
            {
                child.x = left + ml;
                ww -= left + ml;
            }
            else
            {
                child.x = ml;
                ww -= ml;
            }
            if (!isNaN(right))
            {
                if (!isNaN(left))
                {
                    if (ilc)
                        ilc.setWidth(ww - right - mr, true);
                    else
                    {
                        child.width = ww - right - mr;
                        widthSet = true;
                    }
                }
                else
                    child.x = w - right - mr - child.width;
            }
            if (ilc)
            {
                if (!isNaN(ilc.percentWidth))
                    ilc.setWidth(w * ilc.percentWidth / 100, true);
            }
            if (!widthSet)
                child.dispatchEvent(new Event("sizeChanged"));
        }

        public function setDocument(document:Object, id:String = null):void
        {
            this.document = document;
        }

    }

}

fix up this layout

fix up this layout


package org.mangui.hls.model {
    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    import org.mangui.hls.utils.PTS;

    /** HLS streaming quality level. **/
    public class Level {
        /** audio only Level ? **/
        public var audio : Boolean;
        /** Level Bitrate. **/
        public var bitrate : Number;
        /** Level Name. **/
        public var name : String;
        /** level index **/
        public var index : int = 0;
        /** video width (from playlist) **/
        public var width : int;
        /** video height (from playlist) **/
        public var height : int;
        /** URL of this bitrate level (for M3U8). **/
        public var url : String;
        /** Level fragments **/
        public var fragments : Vector.<Fragment>;
        /** min sequence number from M3U8. **/
        public var start_seqnum : int;
        /** max sequence number from M3U8. **/
        public var end_seqnum : int;
        /** target fragment duration from M3U8 **/
        public var targetduration : Number;
        /** average fragment duration **/
        public var averageduration : Number;
        /** Total duration **/
        public var duration : Number;
        /**  Audio Identifier **/
        public var audio_stream_id : String;

        /** Create the quality level. **/
        public function Level() : void {
            this.fragments = new Vector.<Fragment>();
        };

        /** Return the Fragment before a given time position. **/
        public function getFragmentBeforePosition(position : Number) : Fragment {
            if (fragments[0].data.valid && position < fragments[0].start_time)
                return fragments[0];

            var len : int = fragments.length;
            for (var i : int = 0; i < len; i++) {
                /* check whether fragment contains current position */
                if (fragments[i].data.valid && fragments[i].start_time <= position && fragments[i].start_time + fragments[i].duration > position) {
                    return fragments[i];
                }
            }
            return fragments[len - 1];
        };

        /** Return the sequence number from a given program date **/
        public function getSeqNumFromProgramDate(program_date : Number) : int {
            if (program_date < fragments[0].program_date)
                return -1;

            var len : int = fragments.length;
            for (var i : int = 0; i < len; i++) {
                /* check whether fragment contains current position */
                if (fragments[i].data.valid && fragments[i].program_date <= program_date && fragments[i].program_date + 1000 * fragments[i].duration > program_date) {
                    return (start_seqnum + i);
                }
            }
            return -1;
        };

        /** Return the sequence number nearest a PTS **/
        public function getSeqNumNearestPTS(pts : Number, continuity : int) : Number {
            if (fragments.length == 0)
                return -1;
            var firstIndex : Number = getFirstIndexfromContinuity(continuity);
            if (firstIndex == -1 || isNaN(fragments[firstIndex].data.pts_start_computed))
                return -1;
            var lastIndex : Number = getLastIndexfromContinuity(continuity);

            for (var i : int = firstIndex; i <= lastIndex; i++) {
                var frag : Fragment = fragments[i];
                /* check nearest fragment */
                if ( frag.data.valid && (frag.duration >= 0) && (Math.abs(frag.data.pts_start_computed - pts) < Math.abs(frag.data.pts_start_computed + 1000 * frag.duration - pts))) {
                    return frag.seqnum;
                }
            }
            // requested PTS above max PTS of this level
            return Number.POSITIVE_INFINITY;
        };

        public function getLevelstartPTS() : Number {
            if (fragments.length)
                return fragments[0].data.pts_start_computed;
            else
                return NaN;
        }

        /** Return the fragment index from fragment sequence number **/
        public function getFragmentfromSeqNum(seqnum : Number) : Fragment {
            var index : int = getIndexfromSeqNum(seqnum);
            if (index != -1) {
                return fragments[index];
            } else {
                return null;
            }
        }

        /** Return the fragment index from fragment sequence number **/
        private function getIndexfromSeqNum(seqnum : int) : int {
            if (seqnum >= start_seqnum && seqnum <= end_seqnum) {
                return (fragments.length - 1 - (end_seqnum - seqnum));
            } else {
                return -1;
            }
        }

        /** Return the first index matching with given continuity counter **/
        private function getFirstIndexfromContinuity(continuity : int) : int {
            // look for first fragment matching with given continuity index
            var len : int = fragments.length;
            for (var i : int = 0; i < len; i++) {
                if (fragments[i].continuity == continuity)
                    return i;
            }
            return -1;
        }

        /** Return the first seqnum matching with given continuity counter **/
        public function getFirstSeqNumfromContinuity(continuity : int) : Number {
            var index : int = getFirstIndexfromContinuity(continuity);
            if (index == -1) {
                return Number.NEGATIVE_INFINITY;
            }
            return fragments[index].seqnum;
        }

        /** Return the last seqnum matching with given continuity counter **/
        public function getLastSeqNumfromContinuity(continuity : int) : Number {
            var index : int = getLastIndexfromContinuity(continuity);
            if (index == -1) {
                return Number.NEGATIVE_INFINITY;
            }
            return fragments[index].seqnum;
        }

        /** Return the last index matching with given continuity counter **/
        private function getLastIndexfromContinuity(continuity : Number) : int {
            var firstIndex : int = getFirstIndexfromContinuity(continuity);
            if (firstIndex == -1)
                return -1;

            var lastIndex : int = firstIndex;
            // look for first fragment matching with given continuity index
            for (var i : int = firstIndex; i < fragments.length; i++) {
                if (fragments[i].continuity == continuity)
                    lastIndex = i;
                else
                    break;
            }
            return lastIndex;
        }

        /** set Fragments **/
        public function updateFragments(_fragments : Vector.<Fragment>) : void {
            var idx_with_metrics : int = -1;
            var len : int = _fragments.length;
            var frag : Fragment;
            // update PTS from previous fragments
            for (var i : int = 0; i < len; i++) {
                frag = getFragmentfromSeqNum(_fragments[i].seqnum);
                if (frag != null && !isNaN(frag.data.pts_start)) {
                    _fragments[i].data = frag.data;
                    idx_with_metrics = i;
                }
            }
            updateFragmentsProgramDate(_fragments);

            fragments = _fragments;
            start_seqnum = _fragments[0].seqnum;
            end_seqnum = _fragments[len - 1].seqnum;

            if (idx_with_metrics != -1) {
                // if at least one fragment contains PTS info, recompute PTS information for all fragments
                updateFragment(fragments[idx_with_metrics].seqnum, true, fragments[idx_with_metrics].data.pts_start, fragments[idx_with_metrics].data.pts_start + 1000 * fragments[idx_with_metrics].duration);
            } else {
                duration = _fragments[len - 1].start_time + _fragments[len - 1].duration;
            }
            averageduration = duration / len;
        }

        private function updateFragmentsProgramDate(_fragments : Vector.<Fragment>) : void {
            var len : int = _fragments.length;
            var continuity : int;
            var program_date : Number;
            var frag : Fragment;
            for (var i : int = 0; i < len; i++) {
                frag = _fragments[i];
                if (frag.continuity != continuity) {
                    continuity = frag.continuity;
                    program_date = 0;
                }
                if (frag.program_date) {
                    program_date = frag.program_date + 1000 * frag.duration;
                } else if (program_date) {
                    frag.program_date = program_date;
                }
            }
        }

        private function _updatePTS(from_index : int, to_index : int) : void {
            // CONFIG::LOGGING {
            // Log.info("updateFragmentPTS from/to:" + from_index + "/" + to_index);
            // }
            var frag_from : Fragment = fragments[from_index];
            var frag_to : Fragment = fragments[to_index];

            if (frag_from.data.valid && frag_to.data.valid) {
                if (!isNaN(frag_to.data.pts_start)) {
                    // we know PTS[to_index]
                    frag_to.data.pts_start_computed = frag_to.data.pts_start;
                    /* normalize computed PTS value based on known PTS value.
                     * this is to avoid computing wrong fragment duration in case of PTS looping */
                    var from_pts : Number = PTS.normalize(frag_to.data.pts_start, frag_from.data.pts_start_computed);
                    /* update fragment duration.
                    it helps to fix drifts between playlist reported duration and fragment real duration */
                    if (to_index > from_index) {
                        frag_from.duration = (frag_to.data.pts_start - from_pts) / 1000;
                        CONFIG::LOGGING {
                            if (frag_from.duration < 0) {
                                Log.error("negative duration computed for " + frag_from + ", there should be some duration drift between playlist and fragment!");
                            }
                        }
                    } else {
                        frag_to.duration = ( from_pts - frag_to.data.pts_start) / 1000;
                        CONFIG::LOGGING {
                            if (frag_to.duration < 0) {
                                Log.error("negative duration computed for " + frag_to + ", there should be some duration drift between playlist and fragment!");
                            }
                        }
                    }
                } else {
                    // we dont know PTS[to_index]
                    if (to_index > from_index)
                        frag_to.data.pts_start_computed = frag_from.data.pts_start_computed + 1000 * frag_from.duration;
                    else
                        frag_to.data.pts_start_computed = frag_from.data.pts_start_computed - 1000 * frag_to.duration;
                }
            }
        }

        public function updateFragment(seqnum : Number, valid : Boolean, min_pts : Number = 0, max_pts : Number = 0) : Number {
            // CONFIG::LOGGING {
            // Log.info("updatePTS : seqnum/min/max:" + seqnum + '/' + min_pts + '/' + max_pts);
            // }
            // get fragment from seqnum
            var fragIdx : int = getIndexfromSeqNum(seqnum);
            if (fragIdx != -1) {
                var frag : Fragment = fragments[fragIdx];
                // update fragment start PTS + duration
                if (valid) {
                    frag.data.pts_start = min_pts;
                    frag.data.pts_start_computed = min_pts;
                    frag.duration = (max_pts - min_pts) / 1000;
                } else {
                    frag.duration = 0;
                }
                frag.data.valid = valid;
                // CONFIG::LOGGING {
                // Log.info("SN["+fragments[fragIdx].seqnum+"]:pts/duration:" + fragments[fragIdx].start_pts_computed + "/" + fragments[fragIdx].duration);
                // }

                // adjust fragment PTS/duration from seqnum-1 to frag 0
                for (var i : int = fragIdx; i > 0 && fragments[i - 1].continuity == frag.continuity; i--) {
                    _updatePTS(i, i - 1);
                    // CONFIG::LOGGING {
                    // Log.info("SN["+fragments[i-1].seqnum+"]:pts/duration:" + fragments[i-1].start_pts_computed + "/" + fragments[i-1].duration);
                    // }
                }

                // adjust fragment PTS/duration from seqnum to last frag
                for (i = fragIdx; i < fragments.length - 1 && fragments[i + 1].continuity == frag.continuity; i++) {
                    _updatePTS(i, i + 1);
                    // CONFIG::LOGGING {
                    // Log.info("SN["+fragments[i+1].seqnum+"]:pts/duration:" + fragments[i+1].start_pts_computed + "/" + fragments[i+1].duration);
                    // }
                }

                // second, adjust fragment offset
                var start_time_offset : Number = fragments[0].start_time;
                var len : int = fragments.length;
                for (i = 0; i < len; i++) {
                    fragments[i].start_time = start_time_offset;
                    start_time_offset += fragments[i].duration;
                    // CONFIG::LOGGING {
                    // Log.info("SN["+fragments[i].seqnum+"]:start_time/continuity/pts/duration:" + fragments[i].start_time + "/" + fragments[i].continuity + "/"+ fragments[i].start_pts_computed + "/" + fragments[i].duration);
                    // }
                }
                duration = start_time_offset;
                return frag.start_time;
            } else {
                CONFIG::LOGGING {
                    Log.error("updateFragment:seqnum " + seqnum + "not found!");
                }
                return 0;
            }
        }
    }
}

package org.mangui.hls.model {
    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    import org.mangui.hls.utils.PTS;

    /** HLS streaming quality level. **/
    public class Level {
        /** audio only Level ? **/
        public var audio : Boolean;
        /** Level Bitrate. **/
        public var bitrate : Number;
        /** Level Name. **/
        public var name : String;
        /** level index **/
        public var index : int = 0;
        /** video width (from playlist) **/
        public var width : int;
        /** video height (from playlist) **/
        public var height : int;
        /** URL of this bitrate level (for M3U8). **/
        public var url : String;
        /** Level fragments **/
        public var fragments : Vector.<Fragment>;
        /** min sequence number from M3U8. **/
        public var start_seqnum : int;
        /** max sequence number from M3U8. **/
        public var end_seqnum : int;
        /** target fragment duration from M3U8 **/
        public var targetduration : Number;
        /** average fragment duration **/
        public var averageduration : Number;
        /** Total duration **/
        public var duration : Number;
        /**  Audio Identifier **/
        public var audio_stream_id : String;

        /** Create the quality level. **/
        public function Level() : void {
            this.fragments = new Vector.<Fragment>();
        };

        /** Return the Fragment before a given time position. **/
        public function getFragmentBeforePosition(position : Number) : Fragment {
            if (fragments[0].data.valid && position < fragments[0].start_time)
                return fragments[0];

            var len : int = fragments.length;
            for (var i : int = 0; i < len; i++) {
                /* check whether fragment contains current position */
                if (fragments[i].data.valid && fragments[i].start_time <= position && fragments[i].start_time + fragments[i].duration > position) {
                    return fragments[i];
                }
            }
            return fragments[len - 1];
        };

        /** Return the sequence number from a given program date **/
        public function getSeqNumFromProgramDate(program_date : Number) : int {
            if (program_date < fragments[0].program_date)
                return -1;

            var len : int = fragments.length;
            for (var i : int = 0; i < len; i++) {
                /* check whether fragment contains current position */
                if (fragments[i].data.valid && fragments[i].program_date <= program_date && fragments[i].program_date + 1000 * fragments[i].duration > program_date) {
                    return (start_seqnum + i);
                }
            }
            return -1;
        };

        /** Return the sequence number nearest a PTS **/
        public function getSeqNumNearestPTS(pts : Number, continuity : int) : Number {
            if (fragments.length == 0)
                return -1;
            var firstIndex : Number = getFirstIndexfromContinuity(continuity);
            if (firstIndex == -1 || isNaN(fragments[firstIndex].data.pts_start_computed))
                return -1;
            var lastIndex : Number = getLastIndexfromContinuity(continuity);

            for (var i : int = firstIndex; i <= lastIndex; i++) {
                var frag : Fragment = fragments[i];
                /* check nearest fragment */
                if ( frag.data.valid && (frag.duration >= 0) && (Math.abs(frag.data.pts_start_computed - pts) < Math.abs(frag.data.pts_start_computed + 1000 * frag.duration - pts))) {
                    return frag.seqnum;
                }
            }
            // requested PTS above max PTS of this level
            return Number.POSITIVE_INFINITY;
        };

        public function getLevelstartPTS() : Number {
            if (fragments.length)
                return fragments[0].data.pts_start_computed;
            else
                return NaN;
        }

        /** Return the fragment index from fragment sequence number **/
        public function getFragmentfromSeqNum(seqnum : Number) : Fragment {
            var index : int = getIndexfromSeqNum(seqnum);
            if (index != -1) {
                return fragments[index];
            } else {
                return null;
            }
        }

        /** Return the fragment index from fragment sequence number **/
        private function getIndexfromSeqNum(seqnum : int) : int {
            if (seqnum >= start_seqnum && seqnum <= end_seqnum) {
                return (fragments.length - 1 - (end_seqnum - seqnum));
            } else {
                return -1;
            }
        }

        /** Return the first index matching with given continuity counter **/
        private function getFirstIndexfromContinuity(continuity : int) : int {
            // look for first fragment matching with given continuity index
            var len : int = fragments.length;
            for (var i : int = 0; i < len; i++) {
                if (fragments[i].continuity == continuity)
                    return i;
            }
            return -1;
        }

        /** Return the first seqnum matching with given continuity counter **/
        public function getFirstSeqNumfromContinuity(continuity : int) : Number {
            var index : int = getFirstIndexfromContinuity(continuity);
            if (index == -1) {
                return Number.NEGATIVE_INFINITY;
            }
            return fragments[index].seqnum;
        }

        /** Return the last seqnum matching with given continuity counter **/
        public function getLastSeqNumfromContinuity(continuity : int) : Number {
            var index : int = getLastIndexfromContinuity(continuity);
            if (index == -1) {
                return Number.NEGATIVE_INFINITY;
            }
            return fragments[index].seqnum;
        }

        /** Return the last index matching with given continuity counter **/
        private function getLastIndexfromContinuity(continuity : Number) : int {
            var firstIndex : int = getFirstIndexfromContinuity(continuity);
            if (firstIndex == -1)
                return -1;

            var lastIndex : int = firstIndex;
            // look for first fragment matching with given continuity index
            for (var i : int = firstIndex; i < fragments.length; i++) {
                if (fragments[i].continuity == continuity)
                    lastIndex = i;
                else
                    break;
            }
            return lastIndex;
        }

        /** set Fragments **/
        public function updateFragments(_fragments : Vector.<Fragment>) : void {
            var idx_with_metrics : int = -1;
            var len : int = _fragments.length;
            var frag : Fragment;
            // update PTS from previous fragments
            for (var i : int = 0; i < len; i++) {
                frag = getFragmentfromSeqNum(_fragments[i].seqnum);
                if (frag != null && !isNaN(frag.data.pts_start)) {
                    _fragments[i].data = frag.data;
                    idx_with_metrics = i;
                }
            }
            updateFragmentsProgramDate(_fragments);

            fragments = _fragments;
            start_seqnum = _fragments[0].seqnum;
            end_seqnum = _fragments[len - 1].seqnum;

            if (idx_with_metrics != -1) {
                frag = fragments[idx_with_metrics];
                // if at least one fragment contains PTS info, recompute PTS information for all fragments
                CONFIG::LOGGING {
                    Log.debug("updateFragments: found PTS info from previous playlist,seqnum/PTS:" + frag.seqnum + "/" + frag.data.pts_start);
                }
                updateFragment(frag.seqnum, true, frag.data.pts_start, frag.data.pts_start + 1000 * frag.duration);
            } else {
                CONFIG::LOGGING {
                    Log.debug("updateFragments: unknown PTS info for this level");
                }
                duration = _fragments[len - 1].start_time + _fragments[len - 1].duration;
            }
            averageduration = duration / len;
        }

        private function updateFragmentsProgramDate(_fragments : Vector.<Fragment>) : void {
            var len : int = _fragments.length;
            var continuity : int;
            var program_date : Number;
            var frag : Fragment;
            for (var i : int = 0; i < len; i++) {
                frag = _fragments[i];
                if (frag.continuity != continuity) {
                    continuity = frag.continuity;
                    program_date = 0;
                }
                if (frag.program_date) {
                    program_date = frag.program_date + 1000 * frag.duration;
                } else if (program_date) {
                    frag.program_date = program_date;
                }
            }
        }

        private function _updatePTS(from_index : int, to_index : int) : void {
            // CONFIG::LOGGING {
            // Log.info("updateFragmentPTS from/to:" + from_index + "/" + to_index);
            // }
            var frag_from : Fragment = fragments[from_index];
            var frag_to : Fragment = fragments[to_index];

            if (frag_from.data.valid && frag_to.data.valid) {
                if (!isNaN(frag_to.data.pts_start)) {
                    // we know PTS[to_index]
                    frag_to.data.pts_start_computed = frag_to.data.pts_start;
                    /* normalize computed PTS value based on known PTS value.
                     * this is to avoid computing wrong fragment duration in case of PTS looping */
                    var from_pts : Number = PTS.normalize(frag_to.data.pts_start, frag_from.data.pts_start_computed);
                    /* update fragment duration.
                    it helps to fix drifts between playlist reported duration and fragment real duration */
                    if (to_index > from_index) {
                        frag_from.duration = (frag_to.data.pts_start - from_pts) / 1000;
                        CONFIG::LOGGING {
                            if (frag_from.duration < 0) {
                                Log.error("negative duration computed for " + frag_from + ", there should be some duration drift between playlist and fragment!");
                            }
                        }
                    } else {
                        frag_to.duration = ( from_pts - frag_to.data.pts_start) / 1000;
                        CONFIG::LOGGING {
                            if (frag_to.duration < 0) {
                                Log.error("negative duration computed for " + frag_to + ", there should be some duration drift between playlist and fragment!");
                            }
                        }
                    }
                } else {
                    // we dont know PTS[to_index]
                    if (to_index > from_index)
                        frag_to.data.pts_start_computed = frag_from.data.pts_start_computed + 1000 * frag_from.duration;
                    else
                        frag_to.data.pts_start_computed = frag_from.data.pts_start_computed - 1000 * frag_to.duration;
                }
            }
        }

        public function updateFragment(seqnum : Number, valid : Boolean, min_pts : Number = 0, max_pts : Number = 0) : Number {
            // CONFIG::LOGGING {
            // Log.info("updatePTS : seqnum/min/max:" + seqnum + '/' + min_pts + '/' + max_pts);
            // }
            // get fragment from seqnum
            var fragIdx : int = getIndexfromSeqNum(seqnum);
            if (fragIdx != -1) {
                var frag : Fragment = fragments[fragIdx];
                // update fragment start PTS + duration
                if (valid) {
                    frag.data.pts_start = min_pts;
                    frag.data.pts_start_computed = min_pts;
                    frag.duration = (max_pts - min_pts) / 1000;
                } else {
                    frag.duration = 0;
                }
                frag.data.valid = valid;
                // CONFIG::LOGGING {
                // Log.info("SN["+fragments[fragIdx].seqnum+"]:pts/duration:" + fragments[fragIdx].start_pts_computed + "/" + fragments[fragIdx].duration);
                // }

                // adjust fragment PTS/duration from seqnum-1 to frag 0
                for (var i : int = fragIdx; i > 0 && fragments[i - 1].continuity == frag.continuity; i--) {
                    _updatePTS(i, i - 1);
                    // CONFIG::LOGGING {
                    // Log.info("SN["+fragments[i-1].seqnum+"]:pts/duration:" + fragments[i-1].start_pts_computed + "/" + fragments[i-1].duration);
                    // }
                }

                // adjust fragment PTS/duration from seqnum to last frag
                for (i = fragIdx; i < fragments.length - 1 && fragments[i + 1].continuity == frag.continuity; i++) {
                    _updatePTS(i, i + 1);
                    // CONFIG::LOGGING {
                    // Log.info("SN["+fragments[i+1].seqnum+"]:pts/duration:" + fragments[i+1].start_pts_computed + "/" + fragments[i+1].duration);
                    // }
                }

                // second, adjust fragment offset
                var start_time_offset : Number = fragments[0].start_time;
                var len : int = fragments.length;
                for (i = 0; i < len; i++) {
                    fragments[i].start_time = start_time_offset;
                    start_time_offset += fragments[i].duration;
                    // CONFIG::LOGGING {
                    // Log.info("SN["+fragments[i].seqnum+"]:start_time/continuity/pts/duration:" + fragments[i].start_time + "/" + fragments[i].continuity + "/"+ fragments[i].start_pts_computed + "/" + fragments[i].duration);
                    // }
                }
                duration = start_time_offset;
                return frag.start_time;
            } else {
                CONFIG::LOGGING {
                    Log.error("updateFragment:seqnum " + seqnum + " not found!");
                }
                return 0;
            }
        }
    }
}

improve debug logs

improve debug logs


package com.rails2u.bridge {
    import flash.utils.Proxy;
    import flash.external.ExternalInterface;
    import flash.utils.flash_proxy;

    public dynamic class JSProxy extends Proxy {
        use namespace flash_proxy;

        public static var proxyLogger:Function = function(...args):void {};
        private var _stack:Array;
        private var _nextCallIsstack:Boolean = false;

        public function JSProxy(stack:Array = null) {
            _stack = stack || [];
        }

        public function get proxy():JSProxy {
            return getProperty('proxy');
        }

        public static function get proxy():JSProxy {
             return new JSProxy();
        }

        flash_proxy override function callProperty(name:*, ...args):* {
            var $name:* = $check(name);
            if ($name) {
                return callJS(newStack($name, args));
            } else {
                return new JSProxy(newStack(name, args));
            }
        }

        flash_proxy function $check(name:*):* {
            if (name.toString().indexOf('$') == 0 && name.toString().length >= 2) {
                var r:String = name.toString().replace(/^\$+/, '');
                if (name is QName) {
                    return new QName(name.uri, r);
                } else {
                    return r;
                }
            } else {
                return '';
            }
        }

        flash_proxy override function getProperty(name:*):* {
            var $name:* = $check(name);
            if ($name) {
                return callJS(newStack($name));
            } else {
                return new JSProxy(newStack(name));
            }
        }

        flash_proxy override function getDescendants(name:*):* {
            return callJS(newStack(name));
        }

        flash_proxy function newStack(name:*, args:Array = null):Array {
            var stack:Array = _stack.slice();
            if (args != null) {
                stack.push([name, args.slice()]);
            } else {
                stack.push(name);
            }
            return stack;
        }

        flash_proxy override function hasProperty(name:*):Boolean {
            return callJS(newStack(new QName('', name))) !== undefined;
        }

        flash_proxy override function setProperty(name:*, value:*):void {
            callJSSetProperty(newStack(name), value);
        }

        flash_proxy override function deleteProperty(name:*):Boolean {
            return false;
        }

        public function toString():String {
            var res:Array = createCommand(_stack.slice());
            var cmd:String = res[0];
            var args:Array = res[1];
            return '[JSProxy] ' + cmd + ' :: ' + args.join(', ');
        }

        flash_proxy function callJSSetProperty(stack:Array, value:*):* {
            if (!ExternalInterface.available)
                return null;

            var res:Array = createCommand(stack);
            var cmd:String = res[0];
            var args:Array = res[1];
            args.push(value);
            var argsString:String = createArgsString(args.length);

            cmd = "(function(" + argsString + ") {return " + cmd + " = _" + (args.length - 1).toString() + ";})";
            proxyLogger(cmd);
            return ExternalInterface.call.apply(null, [cmd].concat(args));
        }

        flash_proxy function callJS(stack:Array):* {
            if (!ExternalInterface.available)
                return null;

            var res:Array = createCommand(stack);
            var cmd:String = res[0];
            var args:Array = res[1];
            var argsString:String = createArgsString(args.length);

            if (args.length > 0) {
                cmd = "(function(" + argsString + ") {return " + cmd + ";})";
                proxyLogger(cmd);
                proxyLogger(args);
                return ExternalInterface.call.apply(null, [cmd].concat(args));
            } else {
                cmd = "(function() {return " + cmd + ";})";
                proxyLogger(cmd);
                return ExternalInterface.call(cmd);
            }
        }

        flash_proxy function createArgsString(len:uint):String {
            var res:Array = [];
            for (var i:uint = 0; i < len; i++) {
                res.push('_' + i);
            }
            return res.join(', ');
        }

        flash_proxy function createCommand(stack:Array):Array {
            var receivers:Array = [];
            var args:Array = [];
            var argsStrings:Array = [];
            while (stack.length) {
                var o:* = stack.shift();
                if (o is Array) {
                    o = o as Array;
                    var cmd:String = nameToString(o[0]) + '(';
                    var cmdArgs:Array = [];
                    var a:Array = o[1].slice();
                    while (a.length) {
                        cmdArgs.push('_' + args.length);
                        args.push(a.shift());
                    }
                    cmd += cmdArgs.join(', ');
                    cmd += ')';
                    receivers.push(cmd);
                } else {
                    receivers.push(nameToString(o));
                }
            }
            return [receivers.join('.').replace(/\.\[/g, '['), args];
        }

        flash_proxy function nameToString(name:*):String {
            if (name is QName) {
                return name.localName.toString();
            } else {
                return '[' + name.toString() + ']';
            }
        }
    }
}

package com.rails2u.bridge {
    import flash.utils.Proxy;
    import flash.external.ExternalInterface;
    import flash.utils.flash_proxy;

    public dynamic class JSProxy extends Proxy {
        use namespace flash_proxy;

        public static var proxyLogger:Function = function(...args):void {};
        private var _stack:Array;
        private var _nextCallIsstack:Boolean = false;

        public function JSProxy(stack:Array = null) {
            _stack = stack || [];
        }

        public function get proxy():JSProxy {
            return getProperty('proxy');
        }

        public static function get proxy():JSProxy {
             return new JSProxy();
        }

        flash_proxy override function callProperty(name:*, ...args):* {
            var $name:* = $check(name);
            if ($name) {
                return callJS(newStack($name, args));
            } else {
                return new JSProxy(newStack(name, args));
            }
        }

        flash_proxy function $check(name:*):* {
            if (name.toString().indexOf('$') == 0 && name.toString().length >= 2) {
                var r:String = name.toString().replace(/^\$+/, '');
                if (name is QName) {
                    return new QName(name.uri, r);
                } else {
                    return r;
                }
            } else {
                return '';
            }
        }

        flash_proxy override function getProperty(name:*):* {
            var $name:* = $check(name);
            if ($name) {
                return callJS(newStack($name));
            } else {
                return new JSProxy(newStack(name));
            }
        }

        flash_proxy override function getDescendants(name:*):* {
            return callJS(newStack(name));
        }

        flash_proxy function newStack(name:*, args:Array = null):Array {
            var stack:Array = _stack.slice();
            if (args != null) {
                stack.push([name, args.slice()]);
            } else {
                stack.push(name);
            }
            return stack;
        }

        flash_proxy override function hasProperty(name:*):Boolean {
            return callJS(newStack(new QName('', name))) !== undefined;
        }

        flash_proxy override function setProperty(name:*, value:*):void {
            var $name:* = $check(name);
            if ($name) {
                callJSSetProperty(newStack($name), value);
            } else {
                callJSSetProperty(newStack(name), value);
            }
        }

        flash_proxy override function deleteProperty(name:*):Boolean {
            return false;
        }

        public function toString():String {
            var res:Array = createCommand(_stack.slice());
            var cmd:String = res[0];
            var args:Array = res[1];
            return '[JSProxy] ' + cmd + ' :: ' + args.join(', ');
        }

        flash_proxy function callJSSetProperty(stack:Array, value:*):void {
            if (!ExternalInterface.available)
                return;

            var res:Array = createCommand(stack);
            var cmd:String = res[0];
            var args:Array = res[1];
            args.push(value);
            var argsString:String = createArgsString(args.length);

            cmd = "(function(" + argsString + ") {setTimeout(function(){try{" + cmd + " = _" + (args.length - 1).toString() + "}catch(e){};}, 0);})";
            proxyLogger(cmd);
            ExternalInterface.call.apply(null, [cmd].concat(args));
        }

        flash_proxy function callJS(stack:Array):* {
            if (!ExternalInterface.available)
                return null;

            var res:Array = createCommand(stack);
            var cmd:String = res[0];
            var args:Array = res[1];
            var argsString:String = createArgsString(args.length);

            if (args.length > 0) {
                cmd = "(function(" + argsString + ") {return " + cmd + ";})";
                proxyLogger(cmd);
                proxyLogger(args);
                return ExternalInterface.call.apply(null, [cmd].concat(args));
            } else {
                cmd = "(function() {return " + cmd + ";})";
                proxyLogger(cmd);
                return ExternalInterface.call(cmd);
            }
        }

        flash_proxy function createArgsString(len:uint):String {
            var res:Array = [];
            for (var i:uint = 0; i < len; i++) {
                res.push('_' + i);
            }
            return res.join(', ');
        }

        flash_proxy function createCommand(stack:Array):Array {
            var receivers:Array = [];
            var args:Array = [];
            var argsStrings:Array = [];
            while (stack.length) {
                var o:* = stack.shift();
                if (o is Array) {
                    o = o as Array;
                    var cmd:String = nameToString(o[0]) + '(';
                    var cmdArgs:Array = [];
                    var a:Array = o[1].slice();
                    while (a.length) {
                        cmdArgs.push('_' + args.length);
                        args.push(a.shift());
                    }
                    cmd += cmdArgs.join(', ');
                    cmd += ')';
                    receivers.push(cmd);
                } else {
                    receivers.push(nameToString(o));
                }
            }
            return [receivers.join('.').replace(/\.\[/g, '['), args];
        }

        flash_proxy function nameToString(name:*):String {
            if (name is QName) {
                return name.localName.toString();
            } else {
                return '[' + name.toString() + ']';
            }
        }
    }
}

append setter setTimeout()

append setter setTimeout()

git-svn-id: 864080e30cc358c5edb906449bbf014f90258007@66 96db6a20-122f-0410-8d9f-89437bbe4005