package
{
	import flash.desktop.NativeApplication;
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.KeyboardEvent;
	import flash.events.SecurityErrorEvent;
	import flash.filesystem.File;
	import flash.net.URLLoader;
	import flash.net.URLRequest;

	import flashx.textLayout.events.ModelChange;

	public class LoadSwf extends Sprite
	{
		private var swfLoader:Loader = new Loader();
		private var swfMC:MovieClip = null;
		private var pathXml:XML = null;

		public function LoadSwf()
		{
			super();
			initStage();
			initPath();
		}

		//--------------------------------
		private function initStage():void {
//			stage.color = 0x00000;
			stage.addEventListener(Event.ACTIVATE, onActivate);
			stage.addEventListener(Event.DEACTIVATE, onDeActivate);
			stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
		}

		private function onKeyUp(e:KeyboardEvent):void {
			trace("onKeyUp(), "+e.keyCode);
			swfStop();
		}

		private function onDeActivate(e:Event):void {
			trace("onDeActivate()");
			swfPause();
		}

		private function onActivate(e:Event):void {
			trace("onActivate()");
			swfResume();
		}

		//---------------------------------
		private function initLoader():void {
			swfLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
			swfLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadIOError);
			swfLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loadSecurityError);

			var url:String = jointUrl(pathXml.url);
			trace("url: "+url);
			swfLoader.load(new URLRequest(url));
		}

		private function swfResume():void {
//			if (swfMC != null) {
//				swfMC.play();
//			}
		}

		private function swfPause():void {
//			if (swfMC != null) {
//				swfMC.stop();
//			}
			swfStop();
		}

		private function swfStop():void {
			swfLoader.unloadAndStop();
			NativeApplication.nativeApplication.exit();
		}

		private function loadComplete(e:Event):void {
			trace("loadComplete()");
			centerAndFull(swfLoader, e);

			swfLoader.mask = getMasker(swfLoader, e);
			stage.addChild(swfLoader.mask);

			stage.addChild(swfLoader);
			swfMC = swfLoader as MovieClip;
		}

		private function loadIOError(e:IOErrorEvent):void {
			trace("swfLoader loadIOError, "+e.toString());
		}

		private function loadSecurityError(e:SecurityErrorEvent):void {
			trace("swfLoader loadSecurityError, "+e.toString());
		}

		//-----------------------
		private function getMasker(o:DisplayObject, e:Event):Shape {
			var li:LoaderInfo = LoaderInfo(e.target);

			var masker:Shape = new Shape();
			masker.graphics.beginFill(0xff0000);
			masker.graphics.drawRect(o.x, o.y, li.width*o.scaleX, li.height*o.scaleY);
			masker.graphics.endFill();
			return masker;
		}

		private function traceInfo(o:DisplayObject):void {
			trace("x:"+o.x+", y:"+o.y
				+ "\nw:"+o.width +", h:" + o.height
				+ "\nsx:" + o.scaleX +", sy:"+o.scaleY+", sz:"+o.scaleZ);
		}

		private function centerAndFull(o:DisplayObject, e:Event):void {
			traceInfo(o);
			var loadInfo:LoaderInfo = LoaderInfo(e.target);

			//full
			var scalex:Number = stage.stageWidth / loadInfo.width;
			var scaley:Number = stage.stageHeight / loadInfo.height;
			var scale:Number = scalex < scaley ? scalex : scaley;
			o.width *= scale;
			o.height *= scale;

			//center
			o.x = (stage.stageWidth - loadInfo.width*scale) / 2;
			o.y = (stage.stageHeight - loadInfo.height*scale) / 2;

			traceInfo(o);
		}

		//-----------------------
		private function initPath():void {
			var loader:URLLoader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, function(e:Event):void {
				trace("xml load complete, \n"+e.target.data);
				pathXml = XML(e.target.data);
				initLoader();
			});
			loader.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void {
				trace("io error, "+e.toString());
			});
			loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function(e:SecurityErrorEvent):void {
				trace("security error, "+e.toString());
			});
			loader.load(new URLRequest(jointUrl("data/path.xml")));
		}

		private function getRootUrl():String {
			return File.documentsDirectory.url+File.separator;
		}

		private function jointUrl(name:String):String {
			return getRootUrl()+name;
		}

		//-- for test --
		private function readDir():void {
			var file:File = File.documentsDirectory;
			var fileObj:File;
			var docsDirectory:Array = file.getDirectoryListing();

			trace(file.url+"\n"+file.nativePath);
			for (var i:int = 0; i < docsDirectory.length; i++) {
				fileObj = docsDirectory[i];
				trace(fileObj.name);
			}
		}
	}
}

package
{
	import flash.desktop.NativeApplication;
	import flash.display.AVM1Movie;
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.KeyboardEvent;
	import flash.events.SecurityErrorEvent;
	import flash.filesystem.File;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.drm.AuthenticationMethod;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;

	import flashx.textLayout.events.ModelChange;

	public class LoadSwf extends Sprite
	{
		private var swfLoader:Loader = new Loader();
		private var swfMC:MovieClip = null;
		private var pathXml:XML = null;

		public function LoadSwf()
		{
			super();
			initStage();
			initPath();
		}

		//--------------------------------
		private function initStage():void {
//			stage.color = 0x00000;
			stage.addEventListener(Event.ACTIVATE, onActivate);
			stage.addEventListener(Event.DEACTIVATE, onDeActivate);
			stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
		}

		private function onKeyUp(e:KeyboardEvent):void {
			trace("onKeyUp(), "+e.keyCode);
			swfStop();
		}

		private function onDeActivate(e:Event):void {
			trace("onDeActivate()");
			swfPause();
		}

		private function onActivate(e:Event):void {
			trace("onActivate()");
			swfResume();
		}

		//---------------------------------
		private function initLoader():void {
			swfLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
			swfLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadIOError);
			swfLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loadSecurityError);

			var url:String = jointUrl(pathXml.url);
			trace("url: "+url);
			swfLoader.load(new URLRequest(url));
		}

		private function swfResume():void {
//			if (swfMC != null) {
//				swfMC.play();
//			}
		}

		private function swfPause():void {
//			if (swfMC != null) {
//				swfMC.stop();
//			}
			swfStop();
		}

		private function swfStop():void {
			swfLoader.unloadAndStop();
			NativeApplication.nativeApplication.exit();
		}

		private function loadComplete(e:Event):void {
			trace("loadComplete()");

			if (swfLoader.content is AVM1Movie) {
				var loaderInfo:LoaderInfo = LoaderInfo(e.target);
				var msg:String = loaderInfo.url + " is AVM1Movie.\n" +
					"Only support ActionScript 3.0 swf!";
				swfLoader.unloadAndStop();
				trace(msg);
				showError(msg);
				return ;
			}

			centerAndFull(swfLoader, e);

			swfLoader.mask = getMasker(swfLoader, e);
			stage.addChild(swfLoader.mask);

			stage.addChild(swfLoader);
			swfMC = swfLoader as MovieClip;
		}

		private function loadIOError(e:IOErrorEvent):void {
			trace("swfLoader loadIOError, "+e.toString());
			showError(e.toString());
		}

		private function loadSecurityError(e:SecurityErrorEvent):void {
			trace("swfLoader loadSecurityError, "+e.toString());
			showError(e.toString());
		}

		//-----------------------
		private function getMasker(o:DisplayObject, e:Event):Shape {
			var li:LoaderInfo = LoaderInfo(e.target);

			var masker:Shape = new Shape();
			masker.graphics.beginFill(0xff0000);
			masker.graphics.drawRect(o.x, o.y, li.width*o.scaleX, li.height*o.scaleY);
			masker.graphics.endFill();
			return masker;
		}

		private function traceInfo(o:DisplayObject):void {
			trace("x:"+o.x+", y:"+o.y
				+ "\nw:"+o.width +", h:" + o.height
				+ "\nsx:" + o.scaleX +", sy:"+o.scaleY+", sz:"+o.scaleZ);
		}

		private function centerAndFull(o:DisplayObject, e:Event):void {
			traceInfo(o);
			var loadInfo:LoaderInfo = LoaderInfo(e.target);

			//full
			var scalex:Number = stage.stageWidth / loadInfo.width;
			var scaley:Number = stage.stageHeight / loadInfo.height;
			var scale:Number = scalex < scaley ? scalex : scaley;
			o.width *= scale;
			o.height *= scale;

			//center
			o.x = (stage.stageWidth - loadInfo.width*scale) / 2;
			o.y = (stage.stageHeight - loadInfo.height*scale) / 2;

			traceInfo(o);
		}

		//-----------------------
		private function showError(msg:String):void {
			var tfor:TextFormat = new TextFormat();
			tfor.size = 15;
			tfor.color = 0xff0000;
			tfor.align = "center";

			var tf:TextField = new TextField();
			tf.wordWrap = true;
			tf.multiline = true;
			tf.selectable = false;
			tf.border = false;
			tf.defaultTextFormat = tfor;
			tf.text = "播放失败！\n请确认文件是否存在，或者重新打开一次!" + "\n" + msg;
			tf.setTextFormat(tfor);
			tf.width = stage.stageWidth/3*2;
			tf.height = stage.stageHeight/2;

			stage.addChild(tf);
			trace("stage: " + stage.stageWidth+","+stage.stageHeight
				+"\ntf: "+tf.x + "," + tf.y + ",  "+tf.width + ","+tf.height);
			tf.x = (stage.stageWidth - tf.width)/2;
			tf.y = (stage.stageHeight - tf.height)/2;
		}

		//-----------------------
		private function initPath():void {
			var loader:URLLoader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, function(e:Event):void {
				trace("xml load complete, \n"+e.target.data);
				pathXml = XML(e.target.data);
				initLoader();
			});
			loader.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void {
				trace("io error, "+e.toString());
				showError(e.toString());
			});
			loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function(e:SecurityErrorEvent):void {
				trace("security error, "+e.toString());
				showError(e.toString());
			});
			loader.load(new URLRequest(jointUrl("data/path.xml")));
		}

		private function getRootUrl():String {
			return File.documentsDirectory.url+File.separator;
		}

		private function jointUrl(name:String):String {
			return getRootUrl()+name;
		}

		//-- for test --
		private function readDir():void {
			var file:File = File.documentsDirectory;
			var fileObj:File;
			var docsDirectory:Array = file.getDirectoryListing();

			trace(file.url+"\n"+file.nativePath);
			for (var i:int = 0; i < docsDirectory.length; i++) {
				fileObj = docsDirectory[i];
				trace(fileObj.name);
			}
		}
	}
}

add showError() # modified: LoadSwf/src/LoadSwf.as


////////////////////////////////////////////////////////////////////////////////
//
//  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.ILayoutParent;
	import org.apache.flex.core.IStrand;
    import org.apache.flex.core.IParent;
    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 FlexibleFirstChildHorizontalLayout class is a simple layout
     *  bead.  It takes the set of children and lays them out
     *  horizontally in one row, separating them according to
     *  CSS layout rules for margin and padding styles. But it
     *  will size the first 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 FlexibleFirstChildHorizontalLayout implements IBeadLayout
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function FlexibleFirstChildHorizontalLayout()
		{
		}

		private var _strand:IStrand;

        /**
         *  @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
		{
			_strand = value;
            IEventDispatcher(value).addEventListener("layoutNeeded", changeHandler);
			IEventDispatcher(value).addEventListener("widthChanged", changeHandler);
			IEventDispatcher(value).addEventListener("childrenAdded", changeHandler);
			IEventDispatcher(value).addEventListener("itemsCreated", changeHandler);
		}

		private function changeHandler(event:Event):void
		{
			var layoutParent:ILayoutParent = _strand.getBeadByType(ILayoutParent) as ILayoutParent;
			var contentView:IParent = layoutParent.contentView;

			var n:int = contentView.numElements;
			var marginLeft:Object;
			var marginRight:Object;
			var marginTop:Object;
			var marginBottom:Object;
			var margin:Object;
			var maxHeight:Number = 0;
			var verticalMargins:Array = [];

            var xx:Number = layoutParent.resizableView.width;
            var padding:Object = determinePadding();
            xx -= padding.paddingLeft + padding.paddingRight;

            for (var i:int = n - 1; i >= 0; i--)
			{
				var child:IUIBase = 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;
				}
				var ml:Number;
				var mr:Number;
				var mt:Number;
				var mb:Number;
				var lastmr:Number;
				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.y = mt;
				maxHeight = Math.max(maxHeight, mt + child.height + mb);
				if (i == 0)
                {
                    child.x = ml;
                    child.width = xx - mr;
                }
				else
                    child.x = xx - child.width - mr;
				xx -= child.width + mr + ml;
				lastmr = mr;
				var valign:Object = ValuesManager.valuesImpl.getValue(child, "vertical-align");
				verticalMargins.push({ marginTop: mt, marginBottom: mb, valign: valign });
			}
			for (i = 0; i < n; i++)
			{
				var obj:Object = verticalMargins[0]
				child = contentView.getElementAt(i) as IUIBase;
				if (obj.valign == "middle")
					child.y = (maxHeight - child.height) / 2;
				else if (valign == "bottom")
					child.y = maxHeight - child.height - obj.marginBottom;
				else
					child.y = obj.marginTop;
			}
            layoutParent.resizableView.height = maxHeight;
		}

        // 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(_strand, "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(_strand, "padding-left");
                paddingTop = ValuesManager.valuesImpl.getValue(_strand, "padding-top");
                paddingRight = ValuesManager.valuesImpl.getValue(_strand, "padding-right");
            }
            else
            {
                paddingLeft = paddingTop = paddingRight = padding;
            }
            var pl:Number = Number(paddingLeft);
            var pt:Number = Number(paddingTop);
            var pr:Number = Number(paddingRight);

            return {paddingLeft:pl, paddingTop:pt, paddingRight:pr};
        }

    }

}

////////////////////////////////////////////////////////////////////////////////
//
//  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.ILayoutParent;
	import org.apache.flex.core.IStrand;
    import org.apache.flex.core.IParent;
    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 FlexibleFirstChildHorizontalLayout class is a simple layout
     *  bead.  It takes the set of children and lays them out
     *  horizontally in one row, separating them according to
     *  CSS layout rules for margin and padding styles. But it
     *  will size the first 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 FlexibleFirstChildHorizontalLayout implements IBeadLayout
	{
        /**
         *  Constructor.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
		public function FlexibleFirstChildHorizontalLayout()
		{
		}

		private var _strand:IStrand;

        /**
         *  @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
		{
			_strand = value;
            IEventDispatcher(value).addEventListener("layoutNeeded", changeHandler);
			IEventDispatcher(value).addEventListener("widthChanged", changeHandler);
			IEventDispatcher(value).addEventListener("childrenAdded", changeHandler);
			IEventDispatcher(value).addEventListener("itemsCreated", changeHandler);
		}

		private function changeHandler(event:Event):void
		{
			var layoutParent:ILayoutParent = _strand.getBeadByType(ILayoutParent) as ILayoutParent;
			var contentView:IParent = layoutParent.contentView;

			var n:int = contentView.numElements;
			var marginLeft:Object;
			var marginRight:Object;
			var marginTop:Object;
			var marginBottom:Object;
			var margin:Object;
			var maxHeight:Number = 0;
			var verticalMargins:Array = [];

            var xx:Number = layoutParent.resizableView.width;
            if (isNaN(xx))
                return;
            var padding:Object = determinePadding();
            xx -= padding.paddingLeft + padding.paddingRight;

            for (var i:int = n - 1; i >= 0; i--)
			{
				var child:IUIBase = 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;
				}
				var ml:Number;
				var mr:Number;
				var mt:Number;
				var mb:Number;
				var lastmr:Number;
				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.y = mt;
				maxHeight = Math.max(maxHeight, mt + child.height + mb);
				if (i == 0)
                {
                    child.x = ml;
                    child.width = xx - mr;
                }
				else
                    child.x = xx - child.width - mr;
				xx -= child.width + mr + ml;
				lastmr = mr;
				var valign:Object = ValuesManager.valuesImpl.getValue(child, "vertical-align");
				verticalMargins.push({ marginTop: mt, marginBottom: mb, valign: valign });
			}
			for (i = 0; i < n; i++)
			{
				var obj:Object = verticalMargins[0]
				child = contentView.getElementAt(i) as IUIBase;
				if (obj.valign == "middle")
					child.y = (maxHeight - child.height) / 2;
				else if (valign == "bottom")
					child.y = maxHeight - child.height - obj.marginBottom;
				else
					child.y = obj.marginTop;
			}
            layoutParent.resizableView.height = maxHeight;
		}

        // 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(_strand, "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(_strand, "padding-left");
                paddingTop = ValuesManager.valuesImpl.getValue(_strand, "padding-top");
                paddingRight = ValuesManager.valuesImpl.getValue(_strand, "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(pt))
                pt = 0;
            if (isNaN(pr))
                pr = 0;
            return {paddingLeft:pl, paddingTop:pt, paddingRight:pr};
        }

    }

}

handle NaN

handle NaN


package aerys.minko.render.shader
{
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Vector4;

	/**
	 * ShaderSignature objects describe the "signature" of ActionScript shaders and make
	 * it posible to know whether a compatible program already exists or if the shader
	 * must be forked.
	 *
	 * @author Jean-Marc Le Roux
	 */
	public final class Signature
	{
		public static const OPERATION_EXISTS	: uint				= 1;
		public static const OPERATION_GET		: uint				= 2;

		public static const SOURCE_MESH			: uint				= 4;
		public static const SOURCE_SCENE		: uint				= 8;

		private var _keys			: Vector.<String>	= new <String>[];
		private var _values			: Vector.<Object>	= new <Object>[];
		private var _flags			: Vector.<uint>		= new <uint>[];

		public function get numKeys() : uint
		{
			return _keys.length;
		}

		public function Signature(shaderName : String)
		{
		}

		public function getKey(index : uint) : String
		{
			return _keys[index];
		}

		public function getFlags(index : uint) : uint
		{
			return _flags[index];
		}

		public function update(key 		: String,
							   value 	: Object,
							   flags	: uint) : void
		{
			// quit if we already have this entry
			var numKeys : uint = _keys.length;

			for (var keyId : uint = 0; keyId < numKeys; ++keyId)
				if (_keys[keyId] == key && _flags[keyId] == flags)
					return;

			// add the test to the list
			_keys.push(key);
			_values.push(value);
			_flags.push(flags);
		}

		public function isValid(meshBindings 	: DataBindings,
								sceneBindings	: DataBindings) : Boolean
		{
			var numKeys	: uint	= _keys.length;

			for (var i : uint = 0; i < numKeys; ++i)
			{
				var key		: String		= _keys[i];
				var flags	: uint			= _flags[i];
				var value	: Object		= _values[i];
				var source	: DataBindings	= flags & SOURCE_SCENE ? sceneBindings : meshBindings;

				if (flags & OPERATION_GET)
				{
					if (!source.propertyExists(key))
						return false;

					if (!compare(source.getProperty(key), value))
						return false;
				}
				else if (flags & OPERATION_EXISTS)
				{
					if (source.propertyExists(key) != value)
						return false;
				}
			}

			return true;
		}

		public function mergeWith(otherSignature : Signature) : void
		{
			var otherLength : uint = otherSignature._keys.length;

			for (var i : uint = 0; i < otherLength; ++i)
				update(
					otherSignature._keys[i],
					otherSignature._values[i],
					otherSignature._flags[i]
				);

		}

		public function clone() : Signature
		{
			var clone : Signature = new Signature(null);

			clone._flags	= _flags.slice();
			clone._keys		= _keys.slice();
			clone._values	= _values.slice();

			return clone;
		}

		/**
		 * @fixme This is n^2!!!!!
		 * @fixme optimize me with a dictionary!!!
		 */
		public function useProperties(properties	: Vector.<String>,
									  fromScene		: Boolean) : Boolean
		{
			var numProperties	: uint	= properties.length;
			var numKeys			: uint	= _keys.length;
			var flag			: uint	= fromScene ? SOURCE_SCENE : SOURCE_MESH;

			for (var j : uint = 0; j < numProperties; ++j)
				for (var i : uint = 0; i < numKeys; ++i)
					if (_keys[i] == properties[j] && (flag & _flags[i]) != 0)
						return true;

			return false;
		}

		private function compare(value1 : Object, value2 : Object) : Boolean
		{
			if ((value1 == null && value2 != null) || (value1 != null && value2 == null))
				return false;

			var constructor	: Object	= value1.constructor;
			var count		: int		= 0;

			if (constructor != value2.constructor)
				return false;

			if (constructor == Vector4)
				return (value1 as Vector4).equals(value2 as Vector4, true);

			if (constructor == Matrix4x4)
				return (value1 as Matrix4x4).compareTo(value2 as Matrix4x4);

			if (value1 is Vector.<Number>)
			{
				var numbers1	: Vector.<Number>	= value1 as Vector.<Number>;
				var numbers2	: Vector.<Number>	= value2 as Vector.<Number>;

				count = numbers1.length;

				if (count != numbers2.length)
					return false;

				while (count >= 0)
				{
					if (numbers1[count] != numbers2[count])
						return false;

					--count;
				}
			}
			else if (value1 is Vector.<Vector4>)
			{
				var vectors1	: Vector.<Vector4>	= value1 as Vector.<Vector4>;
				var vectors2	: Vector.<Vector4>	= value2 as Vector.<Vector4>;

				count = vectors1.length;

				if (count != vectors2.length)
					return false;

				while (count >= 0)
				{
					if (!(vectors1[count] as Vector4).equals(vectors2[count]))
						return false;

					--count;
				}
			}
			else if (value1 is Vector.<Matrix4x4>)
			{
				var matrices1 	: Vector.<Matrix4x4>	= value1 as Vector.<Matrix4x4>;
				var matrices2 	: Vector.<Matrix4x4>	= value2 as Vector.<Matrix4x4>;

				count = matrices1.length;

				if (count != matrices2.length)
					return false;

				while (count >= 0)
				{
					if (!(matrices1[count] as Matrix4x4).compareTo(matrices2[count]))
						return false;

					--count;
				}
			}

			return value1 === value2;
		}
	}
}

fix coding style

fix coding style


package com.axis.rtspclient {
  import flash.utils.ByteArray;

  public class RTPTiming {

    public var rtpTime:Object;
    public var range:Object;
    public var live:Boolean;

    public function RTPTiming(rtpTime:Object, range:Object, live:Boolean) {
      this.rtpTime = rtpTime;
      this.range = range;
      this.live = live;
    }

    public function rtpTimeForControl(control:String):Number {
      /* control parameter and url in rtp-info may not equal but control
       * parameter is always part of the url */
      for (var c:String in this.rtpTime) {
        if (c.indexOf(control) >= 0) {
          return this.rtpTime[c];
        }
      }
      return 0;
    }

    public function toString():String {
      var res:String = 'rtpTime:';
      for (var control:String in rtpTime) {
        res += '\n  ' + control + ': ' + rtpTime[control];
      }
      if (range) {
        res += '\nrange: ' + range.from + ' - ' + range.to;
      }
      res += '\nlive: ' + live;
      return res;
    }

    public static function parse(rtpInfo:String, range:String):RTPTiming {
      var rtpTime:Object = {};
      for each (var track:String in rtpInfo.split(',')) {
        var rtpTimeMatch:Object = /^.*url=([^;]*);.*rtptime=(\d+).*$/.exec(track);
        rtpTime[rtpTimeMatch[1]] = parseInt(rtpTimeMatch[2]);
      }
      var rangeMatch:Object = /^npt=(.*)-(.*)$/.exec(range);
      var rangeFrom:String = rangeMatch[1];
      var rangeTo:String = rangeMatch[2];
      var from:Number = 0;
      var to:Number = rangeTo.length > 0 ? Math.round(parseFloat(rangeTo) * 1000) : -1;
      var live:Boolean = rangeFrom == 'now' || to == -1;
      if (rangeFrom != 'now') {
        from = Math.round(parseFloat(rangeFrom) * 1000);
        /* Some idiot RTSP servers writes Range: npt=0.000-0.000 in the header... */
        to = to <= from ? -1 : to;
      }

      return new RTPTiming(rtpTime, { from: from, to: to }, live);
    }
  }
}

package com.axis.rtspclient {
  import flash.utils.ByteArray;

  public class RTPTiming {

    public var rtpTime:Object;
    public var range:Object;
    public var live:Boolean;

    public function RTPTiming(rtpTime:Object, range:Object, live:Boolean) {
      this.rtpTime = rtpTime;
      this.range = range;
      this.live = live;
    }

    public function rtpTimeForControl(control:String):Number {
      /* control parameter and url in rtp-info may not equal but control
       * parameter is always part of the url */
      for (var c:String in this.rtpTime) {
        if (c.indexOf(control) >= 0) {
          return this.rtpTime[c];
        }
      }
      return 0;
    }

    public function toString():String {
      var res:String = 'rtpTime:';
      for (var control:String in rtpTime) {
        res += '\n  ' + control + ': ' + rtpTime[control];
      }
      if (range) {
        res += '\nrange: ' + range.from + ' - ' + range.to;
      }
      res += '\nlive: ' + live;
      return res;
    }

    public static function parse(rtpInfo:String, range:String):RTPTiming {
      var rtpTime:Object = {};
      for each (var track:String in rtpInfo.split(',')) {
        var rtpTimeMatch:Object = /^.*url=([^;]*);.*rtptime=(\d+).*$/.exec(track);
        rtpTime[rtpTimeMatch[1]] = parseInt(rtpTimeMatch[2]);
      }
      var rangeMatch:Object = /^npt=(.*)-(.*)$/.exec(range);
      var rangeFrom:String = rangeMatch[1];
      var rangeTo:String = rangeMatch[2];
      var from:Number = 0;
      var to:Number = rangeTo.length > 0 ? Math.round(parseFloat(rangeTo) * 1000) : -1;
      var live:Boolean = rangeFrom == 'now';
      if (rangeFrom != 'now') {
        from = Math.round(parseFloat(rangeFrom) * 1000);
        /* Some idiot RTSP servers writes Range: npt=0.000-0.000 in the header... */
        to = to <= from ? -1 : to;
      }

      return new RTPTiming(rtpTime, { from: from, to: to }, live);
    }
  }
}

RTSP: Fix incorrect assumption about live streaming

Do not assume live stream if to-value in range header is empty.


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls {
    import flash.display.Stage;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.net.URLStream;
    import flash.events.EventDispatcher;
    import flash.events.Event;

    import org.mangui.hls.controller.LevelController;
    import org.mangui.hls.controller.AudioTrackController;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.loader.LevelLoader;
    import org.mangui.hls.loader.AltAudioLevelLoader;
    import org.mangui.hls.model.Level;
    import org.mangui.hls.model.AudioTrack;
    import org.mangui.hls.playlist.AltAudioTrack;
    import org.mangui.hls.stream.HLSNetStream;
    import org.mangui.hls.stream.StreamBuffer;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Class that manages the streaming process. **/
    public class HLS extends EventDispatcher {
        private var _levelLoader : LevelLoader;
        private var _altAudioLevelLoader : AltAudioLevelLoader;
        private var _audioTrackController : AudioTrackController;
        private var _levelController : LevelController;
        private var _streamBuffer : StreamBuffer;
        /** HLS NetStream **/
        private var _hlsNetStream : HLSNetStream;
        /** HLS URLStream **/
        private var _hlsURLStream : Class;
        private var _client : Object = {};
        private var _stage : Stage;
        /* level handling */
        private var _level : int;
        /* overrided quality_manual_level level */
        private var _manual_level : int = -1;

        /** Create and connect all components. **/
        public function HLS() {
            var connection : NetConnection = new NetConnection();
            connection.connect(null);
            _levelLoader = new LevelLoader(this);
            _altAudioLevelLoader = new AltAudioLevelLoader(this);
            _audioTrackController = new AudioTrackController(this);
            _levelController = new LevelController(this);
            _streamBuffer = new StreamBuffer(this, _audioTrackController, _levelController);
            _hlsURLStream = URLStream as Class;
            // default loader
            _hlsNetStream = new HLSNetStream(connection, this, _streamBuffer);
            this.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
        };

        /** Forward internal errors. **/
        override public function dispatchEvent(event : Event) : Boolean {
            if (event.type == HLSEvent.ERROR) {
                CONFIG::LOGGING {
                    Log.error((event as HLSEvent).error);
                }
                _hlsNetStream.close();
            }
            return super.dispatchEvent(event);
        };

        private function _levelSwitchHandler(event : HLSEvent) : void {
            _level = event.level;
        };

        public function dispose() : void {
            this.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            _levelLoader.dispose();
            _altAudioLevelLoader.dispose();
            _audioTrackController.dispose();
            _levelController.dispose();
            _streamBuffer.dispose();
            _hlsNetStream.dispose_();
            _levelLoader = null;
            _altAudioLevelLoader = null;
            _audioTrackController = null;
            _levelController = null;
            _hlsNetStream = null;
            _client = null;
            _stage = null;
            _hlsNetStream = null;
        }

        /** Return the quality level used when starting a fresh playback **/
        public function get startlevel() : int {
            return _levelController.startlevel;
        };

        /** Return the quality level used after a seek operation **/
        public function get seeklevel() : int {
            return _levelController.seeklevel;
        };

        /** Return the quality level of the currently played fragment **/
        public function get playbacklevel() : int {
            return _hlsNetStream.playbackLevel;
        };

        /** Return the quality level of last loaded fragment **/
        public function get level() : int {
            return _level;
        };

        /*  set quality level for next loaded fragment (-1 for automatic level selection) */
        public function set level(level : int) : void {
            _manual_level = level;
        };

        /* check if we are in automatic level selection mode */
        public function get autolevel() : Boolean {
            return (_manual_level == -1);
        };

        /* return manual level */
        public function get manuallevel() : int {
            return _manual_level;
        };

        /** Return a Vector of quality level **/
        public function get levels() : Vector.<Level> {
            return _levelLoader.levels;
        };

        /** Return the current playback position. **/
        public function get position() : Number {
            return _streamBuffer.position;
        };

        /** Return the current playback state. **/
        public function get playbackState() : String {
            return _hlsNetStream.playbackState;
        };

        /** Return the current seek state. **/
        public function get seekState() : String {
            return _hlsNetStream.seekState;
        };

        /** Return the type of stream (VOD/LIVE). **/
        public function get type() : String {
            return _levelLoader.type;
        };

        /** Load and parse a new HLS URL **/
        public function load(url : String) : void {
            _hlsNetStream.close();
            _levelLoader.load(url);
        };

        /** return HLS NetStream **/
        public function get stream() : NetStream {
            return _hlsNetStream;
        }

        public function get client() : Object {
            return _client;
        }

        public function set client(value : Object) : void {
            _client = value;
        }

        /** get current Buffer Length  **/
        public function get bufferLength() : Number {
            return _hlsNetStream.bufferLength;
        };

        /** get audio tracks list**/
        public function get audioTracks() : Vector.<AudioTrack> {
            return _audioTrackController.audioTracks;
        };

        /** get alternate audio tracks list from playlist **/
        public function get altAudioTracks() : Vector.<AltAudioTrack> {
            return _levelLoader.altAudioTracks;
        };

        /** get index of the selected audio track (index in audio track lists) **/
        public function get audioTrack() : int {
            return _audioTrackController.audioTrack;
        };

        /** select an audio track, based on its index in audio track lists**/
        public function set audioTrack(val : int) : void {
            _audioTrackController.audioTrack = val;
        }

        /* set stage */
        public function set stage(stage : Stage) : void {
            _stage = stage;
        }

        /* get stage */
        public function get stage() : Stage {
            return _stage;
        }

        /* set URL stream loader */
        public function set URLstream(urlstream : Class) : void {
            _hlsURLStream = urlstream;
        }

        /* retrieve URL stream loader */
        public function get URLstream() : Class {
            return _hlsURLStream;
        }
    }
    ;
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls {
    import flash.display.Stage;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.net.URLStream;
    import flash.events.EventDispatcher;
    import flash.events.Event;

    import org.mangui.hls.controller.LevelController;
    import org.mangui.hls.controller.AudioTrackController;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.loader.LevelLoader;
    import org.mangui.hls.loader.AltAudioLevelLoader;
    import org.mangui.hls.model.Level;
    import org.mangui.hls.model.AudioTrack;
    import org.mangui.hls.playlist.AltAudioTrack;
    import org.mangui.hls.stream.HLSNetStream;
    import org.mangui.hls.stream.StreamBuffer;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Class that manages the streaming process. **/
    public class HLS extends EventDispatcher {
        private var _levelLoader : LevelLoader;
        private var _altAudioLevelLoader : AltAudioLevelLoader;
        private var _audioTrackController : AudioTrackController;
        private var _levelController : LevelController;
        private var _streamBuffer : StreamBuffer;
        /** HLS NetStream **/
        private var _hlsNetStream : HLSNetStream;
        /** HLS URLStream **/
        private var _hlsURLStream : Class;
        private var _client : Object = {};
        private var _stage : Stage;
        /* level handling */
        private var _level : int;
        /* overrided quality_manual_level level */
        private var _manual_level : int = -1;

        /** Create and connect all components. **/
        public function HLS() {
            var connection : NetConnection = new NetConnection();
            connection.connect(null);
            _levelLoader = new LevelLoader(this);
            _altAudioLevelLoader = new AltAudioLevelLoader(this);
            _audioTrackController = new AudioTrackController(this);
            _levelController = new LevelController(this);
            _streamBuffer = new StreamBuffer(this, _audioTrackController, _levelController);
            _hlsURLStream = URLStream as Class;
            // default loader
            _hlsNetStream = new HLSNetStream(connection, this, _streamBuffer);
            this.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
        };

        /** Forward internal errors. **/
        override public function dispatchEvent(event : Event) : Boolean {
            if (event.type == HLSEvent.ERROR) {
                CONFIG::LOGGING {
                    Log.error((event as HLSEvent).error);
                }
                _hlsNetStream.close();
            }
            return super.dispatchEvent(event);
        };

        private function _levelSwitchHandler(event : HLSEvent) : void {
            _level = event.level;
        };

        public function dispose() : void {
            this.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            _levelLoader.dispose();
            _altAudioLevelLoader.dispose();
            _audioTrackController.dispose();
            _levelController.dispose();
            _hlsNetStream.dispose_();
            _streamBuffer.dispose();
            _levelLoader = null;
            _altAudioLevelLoader = null;
            _audioTrackController = null;
            _levelController = null;
            _hlsNetStream = null;
            _client = null;
            _stage = null;
            _hlsNetStream = null;
        }

        /** Return the quality level used when starting a fresh playback **/
        public function get startlevel() : int {
            return _levelController.startlevel;
        };

        /** Return the quality level used after a seek operation **/
        public function get seeklevel() : int {
            return _levelController.seeklevel;
        };

        /** Return the quality level of the currently played fragment **/
        public function get playbacklevel() : int {
            return _hlsNetStream.playbackLevel;
        };

        /** Return the quality level of last loaded fragment **/
        public function get level() : int {
            return _level;
        };

        /*  set quality level for next loaded fragment (-1 for automatic level selection) */
        public function set level(level : int) : void {
            _manual_level = level;
        };

        /* check if we are in automatic level selection mode */
        public function get autolevel() : Boolean {
            return (_manual_level == -1);
        };

        /* return manual level */
        public function get manuallevel() : int {
            return _manual_level;
        };

        /** Return a Vector of quality level **/
        public function get levels() : Vector.<Level> {
            return _levelLoader.levels;
        };

        /** Return the current playback position. **/
        public function get position() : Number {
            return _streamBuffer.position;
        };

        /** Return the current playback state. **/
        public function get playbackState() : String {
            return _hlsNetStream.playbackState;
        };

        /** Return the current seek state. **/
        public function get seekState() : String {
            return _hlsNetStream.seekState;
        };

        /** Return the type of stream (VOD/LIVE). **/
        public function get type() : String {
            return _levelLoader.type;
        };

        /** Load and parse a new HLS URL **/
        public function load(url : String) : void {
            _hlsNetStream.close();
            _levelLoader.load(url);
        };

        /** return HLS NetStream **/
        public function get stream() : NetStream {
            return _hlsNetStream;
        }

        public function get client() : Object {
            return _client;
        }

        public function set client(value : Object) : void {
            _client = value;
        }

        /** get current Buffer Length  **/
        public function get bufferLength() : Number {
            return _hlsNetStream.bufferLength;
        };

        /** get audio tracks list**/
        public function get audioTracks() : Vector.<AudioTrack> {
            return _audioTrackController.audioTracks;
        };

        /** get alternate audio tracks list from playlist **/
        public function get altAudioTracks() : Vector.<AltAudioTrack> {
            return _levelLoader.altAudioTracks;
        };

        /** get index of the selected audio track (index in audio track lists) **/
        public function get audioTrack() : int {
            return _audioTrackController.audioTrack;
        };

        /** select an audio track, based on its index in audio track lists**/
        public function set audioTrack(val : int) : void {
            _audioTrackController.audioTrack = val;
        }

        /* set stage */
        public function set stage(stage : Stage) : void {
            _stage = stage;
        }

        /* get stage */
        public function get stage() : Stage {
            return _stage;
        }

        /* set URL stream loader */
        public function set URLstream(urlstream : Class) : void {
            _hlsURLStream = urlstream;
        }

        /* retrieve URL stream loader */
        public function get URLstream() : Class {
            return _hlsURLStream;
        }
    }
    ;
}

Fix for dispose function in HLS.as

Fix for dispose function in HLS.as

while disposing HLS stream it throws a NPE,

TypeError: Error #1009: Cannot access a property or method of a null object reference.
at org.mangui.hls.stream::StreamBuffer/stop()[flashls/src/org/mangui/hls/stream/StreamBuffer.as:87]
at org.mangui.hls.stream::HLSNetStream/close()[flashls/src/org/mangui/hls/stream/HLSNetStream.as:346]
at org.mangui.hls.stream::HLSNetStream/dispose_()[flashls/src/org/mangui/hls/stream/HLSNetStream.as:353]
at org.mangui.hls::HLS/dispose()[flashls/src/org/mangui/hls/HLS.as:81]

Fixed it by disposing _hlsNetStream first and then disposing _streamBuffer in HLS.as


////////////////////////////////////////////////////////////////////////////////
//
//  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 spark.skins.ios7
{

import flash.display.DisplayObject;

import mx.core.DPIClassification;
import mx.core.mx_internal;
import mx.events.FlexEvent;

import spark.components.supportClasses.StyleableTextField;
import spark.skins.ios7.assets.Button_up;
import spark.skins.mobile.supportClasses.ButtonSkinBase;

use namespace mx_internal;

/**
 *  ActionScript-based skin for Button controls in mobile applications. The skin supports
 *  iconClass and labelPlacement. It uses FXG classes to
 *  implement the vector drawing.
 *
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 2.5
 *  @productversion Flex 4.5
 */
public class ButtonSkin extends ButtonSkinBase
{
    //--------------------------------------------------------------------------
    //
    //  Class constants
    //
    //--------------------------------------------------------------------------

    /**
     * An array of color distribution ratios.
     * This is used in the chrome color fill.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    mx_internal static const CHROME_COLOR_RATIOS:Array = [0, 127.5];

    /**
     * An array of alpha values for the corresponding colors in the colors array.
     * This is used in the chrome color fill.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    mx_internal static const CHROME_COLOR_ALPHAS:Array = [1, 1];

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------
    /**
     *  Constructor.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    public function ButtonSkin()
    {
        super();
		//In iOS7, buttons look like simple links, without any shape containing the text
		//We still need to assign an asset to determine the size of the button
		//Button_up is a simple transparent graphic object
		upBorderSkin = spark.skins.ios7.assets.Button_up;
		downBorderSkin = spark.skins.ios7.assets.Button_up;
		layoutCornerEllipseSize = 0;

        switch (applicationDPI)
        {
			case DPIClassification.DPI_640:
			{

				layoutGap = 20;
				layoutPaddingLeft = 40;
				layoutPaddingRight = 40;
				layoutPaddingTop = 40;
				layoutPaddingBottom = 40;
				layoutBorderSize = 2;
				measuredDefaultWidth = 128;
				measuredDefaultHeight = 172;

				break;
			}
			case DPIClassification.DPI_480:
			{

				layoutGap = 14;
				layoutPaddingLeft = 30;
				layoutPaddingRight = 30;
				layoutPaddingTop = 30;
				layoutPaddingBottom = 30;
				layoutBorderSize = 2;
				measuredDefaultWidth = 96;
				measuredDefaultHeight = 130;

				break;
			}
            case DPIClassification.DPI_320:
            {

                layoutGap = 10;
                layoutPaddingLeft = 20;
                layoutPaddingRight = 20;
                layoutPaddingTop = 20;
                layoutPaddingBottom = 20;
                layoutBorderSize = 2;
                measuredDefaultWidth = 64;
                measuredDefaultHeight = 86;

                break;
            }
			case DPIClassification.DPI_240:
			{

				layoutGap = 7;
				layoutPaddingLeft = 15;
				layoutPaddingRight = 15;
				layoutPaddingTop = 15;
				layoutPaddingBottom = 15;
				layoutBorderSize = 1;
				measuredDefaultWidth = 48;
				measuredDefaultHeight = 65;

				break;
			}
			case DPIClassification.DPI_120:
			{

				layoutGap = 4;
				layoutPaddingLeft = 8;
				layoutPaddingRight = 8;
				layoutPaddingTop = 8;
				layoutPaddingBottom = 8;
				layoutBorderSize = 1;
				measuredDefaultWidth = 24;
				measuredDefaultHeight = 33;

				break;
			}
            default:
            {

                layoutGap = 5;
                layoutPaddingLeft = 10;
                layoutPaddingRight = 10;
                layoutPaddingTop = 10;
                layoutPaddingBottom = 10;
                layoutBorderSize = 1;
                measuredDefaultWidth = 32;
                measuredDefaultHeight = 43;

                break;
            }
        }
    }

    //--------------------------------------------------------------------------
    //
    //  Layout variables
    //
    //--------------------------------------------------------------------------

    /**
     *  Defines the corner radius.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    protected var layoutCornerEllipseSize:uint;

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------
    private var _border:DisplayObject;

    private var changeFXGSkin:Boolean = false;

    private var borderClass:Class;

    mx_internal var fillColorStyleName:String = "chromeColor";

    /**
     *  Defines the shadow for the Button control's label.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    public var labelDisplayShadow:StyleableTextField;

    /**
     *  Read-only button border graphic. Use getBorderClassForCurrentState()
     *  to specify a graphic per-state.
     *
     *  @see #getBorderClassForCurrentState()
     */
    protected function get border():DisplayObject
    {
        return _border;
    }

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    /**
     *  Class to use for the border in the up state.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     *
     *  @default Button_up
     */
    protected var upBorderSkin:Class;

    /**
     *  Class to use for the border in the down state.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     *
     *  @default Button_down
     */
    protected var downBorderSkin:Class;

    //--------------------------------------------------------------------------
    //
    //  Overridden methods
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override protected function createChildren():void
    {
        super.createChildren();
        setStyle("textAlign", "center");
    }

    /**
     *  @private
     */
    override protected function commitCurrentState():void
    {
        super.commitCurrentState();

        borderClass = getBorderClassForCurrentState();

        if (!(_border is borderClass))
            changeFXGSkin = true;

        // update borderClass and background
        invalidateDisplayList();
    }

    /**
     *  @private
     */
    override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void
    {
        super.layoutContents(unscaledWidth, unscaledHeight);

        // size the FXG background
        if (changeFXGSkin)
        {
            changeFXGSkin = false;

            if (_border)
            {
                removeChild(_border);
                _border = null;
            }

            if (borderClass)
            {
                _border = new borderClass();
                addChildAt(_border, 0);
            }
        }

        layoutBorder(unscaledWidth, unscaledHeight);

    }

    /**
     *  Position the background of the skin. Override this function to re-position the background.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    mx_internal function layoutBorder(unscaledWidth:Number, unscaledHeight:Number):void
    {
        setElementSize(border, unscaledWidth, unscaledHeight);
        setElementPosition(border, 0, 0);
    }

    /**
     *  Returns the borderClass to use based on the currentState.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    protected function getBorderClassForCurrentState():Class
    {
        if (currentState == "down")
            return downBorderSkin;
        else
            return upBorderSkin;
    }

    //--------------------------------------------------------------------------
    //
    //  Event Handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override protected function labelDisplay_valueCommitHandler(event:FlexEvent):void
    {
        super.labelDisplay_valueCommitHandler(event);
    }

}
}

////////////////////////////////////////////////////////////////////////////////
//
//  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 spark.skins.ios7
{

import flash.display.DisplayObject;

import mx.core.DPIClassification;
import mx.core.mx_internal;
import mx.events.FlexEvent;

import spark.components.supportClasses.StyleableTextField;
import spark.skins.ios7.assets.Button_up;
import spark.skins.mobile.supportClasses.ButtonSkinBase;

use namespace mx_internal;

/**
 *  ActionScript-based skin for Button controls in mobile applications. The skin supports
 *  iconClass and labelPlacement. It uses FXG classes to
 *  implement the vector drawing.
 *
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 2.5
 *  @productversion Flex 4.5
 */
public class ButtonSkin extends ButtonSkinBase
{
    //--------------------------------------------------------------------------
    //
    //  Class constants
    //
    //--------------------------------------------------------------------------

    /**
     * An array of color distribution ratios.
     * This is used in the chrome color fill.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    mx_internal static const CHROME_COLOR_RATIOS:Array = [0, 127.5];

    /**
     * An array of alpha values for the corresponding colors in the colors array.
     * This is used in the chrome color fill.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    mx_internal static const CHROME_COLOR_ALPHAS:Array = [1, 1];

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------
    /**
     *  Constructor.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    public function ButtonSkin()
    {
        super();
		//In iOS7, buttons look like simple links, without any shape containing the text
		//We still need to assign an asset to determine the size of the button
		//Button_up is a simple transparent graphic object
		upBorderSkin = spark.skins.ios7.assets.Button_up;
		downBorderSkin = spark.skins.ios7.assets.Button_up;
		layoutCornerEllipseSize = 0;

        switch (applicationDPI)
        {
			case DPIClassification.DPI_640:
			{

				layoutGap = 20;
				layoutPaddingLeft = 40;
				layoutPaddingRight = 40;
				layoutPaddingTop = 40;
				layoutPaddingBottom = 40;
				layoutBorderSize = 2;
				measuredDefaultWidth = 128;
				measuredDefaultHeight = 172;

				break;
			}
			case DPIClassification.DPI_480:
			{

				layoutGap = 14;
				layoutPaddingLeft = 30;
				layoutPaddingRight = 30;
				layoutPaddingTop = 30;
				layoutPaddingBottom = 30;
				layoutBorderSize = 2;
				measuredDefaultWidth = 96;
				measuredDefaultHeight = 130;

				break;
			}
            case DPIClassification.DPI_320:
            {

                layoutGap = 10;
                layoutPaddingLeft = 20;
                layoutPaddingRight = 20;
                layoutPaddingTop = 20;
                layoutPaddingBottom = 20;
                layoutBorderSize = 2;
                measuredDefaultWidth = 64;
                measuredDefaultHeight = 86;

                break;
            }
			case DPIClassification.DPI_240:
			{

				layoutGap = 7;
				layoutPaddingLeft = 15;
				layoutPaddingRight = 15;
				layoutPaddingTop = 15;
				layoutPaddingBottom = 15;
				layoutBorderSize = 1;
				measuredDefaultWidth = 48;
				measuredDefaultHeight = 65;

				break;
			}
			case DPIClassification.DPI_120:
			{

				layoutGap = 4;
				layoutPaddingLeft = 8;
				layoutPaddingRight = 8;
				layoutPaddingTop = 8;
				layoutPaddingBottom = 8;
				layoutBorderSize = 1;
				measuredDefaultWidth = 24;
				measuredDefaultHeight = 33;

				break;
			}
            default:
            {

                layoutGap = 5;
                layoutPaddingLeft = 10;
                layoutPaddingRight = 10;
                layoutPaddingTop = 10;
                layoutPaddingBottom = 10;
                layoutBorderSize = 1;
                measuredDefaultWidth = 32;
                measuredDefaultHeight = 43;

                break;
            }
        }
    }

    //--------------------------------------------------------------------------
    //
    //  Layout variables
    //
    //--------------------------------------------------------------------------

    /**
     *  Defines the corner radius.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    protected var layoutCornerEllipseSize:uint;

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------
    private var _border:DisplayObject;

    private var changeFXGSkin:Boolean = false;

    private var borderClass:Class;

    mx_internal var fillColorStyleName:String = "chromeColor";

    /**
     *  Defines the shadow for the Button control's label.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    public var labelDisplayShadow:StyleableTextField;

    /**
     *  Read-only button border graphic. Use getBorderClassForCurrentState()
     *  to specify a graphic per-state.
     *
     *  @see #getBorderClassForCurrentState()
     */
    protected function get border():DisplayObject
    {
        return _border;
    }

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    /**
     *  Class to use for the border in the up state.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     *
     *  @default Button_up
     */
    protected var upBorderSkin:Class;

    /**
     *  Class to use for the border in the down state.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     *
     *  @default Button_down
     */
    protected var downBorderSkin:Class;

    //--------------------------------------------------------------------------
    //
    //  Overridden methods
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override protected function createChildren():void
    {
        super.createChildren();
        setStyle("textAlign", "center");
    }

	override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
	{
		super.updateDisplayList(unscaledWidth,unscaledHeight);
		if(currentState == "down")
		{
			this.alpha = 0.5;
		}
		else
		{
			this.alpha = 1.0;
		}
	}

    /**
     *  @private
     */
    override protected function commitCurrentState():void
    {
        super.commitCurrentState();

        borderClass = getBorderClassForCurrentState();

        if (!(_border is borderClass))
            changeFXGSkin = true;

        // update borderClass and background
        invalidateDisplayList();
    }

    /**
     *  @private
     */
    override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void
    {
        super.layoutContents(unscaledWidth, unscaledHeight);

        // size the FXG background
        if (changeFXGSkin)
        {
            changeFXGSkin = false;

            if (_border)
            {
                removeChild(_border);
                _border = null;
            }

            if (borderClass)
            {
                _border = new borderClass();
                addChildAt(_border, 0);
            }
        }

        layoutBorder(unscaledWidth, unscaledHeight);

    }

    /**
     *  Position the background of the skin. Override this function to re-position the background.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    mx_internal function layoutBorder(unscaledWidth:Number, unscaledHeight:Number):void
    {
        setElementSize(border, unscaledWidth, unscaledHeight);
        setElementPosition(border, 0, 0);
    }

    /**
     *  Returns the borderClass to use based on the currentState.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */
    protected function getBorderClassForCurrentState():Class
    {
        if (currentState == "down")
            return downBorderSkin;
        else
            return upBorderSkin;
    }

    //--------------------------------------------------------------------------
    //
    //  Event Handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override protected function labelDisplay_valueCommitHandler(event:FlexEvent):void
    {
        super.labelDisplay_valueCommitHandler(event);
    }

}
}

Fix Button's downstate

Fix Button's downstate


package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.text.TextField;
    import flash.net.Socket;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.utils.ByteArray;
    import flash.utils.setTimeout;

    public class swfcat extends Sprite
    {
        /* David's relay (nickname 3VXRyxz67OeRoqHn) that also serves a
           crossdomain policy. */
        private const DEFAULT_TOR_ADDR:Object = {
            host: "173.255.221.44",
            port: 9001
        };
        private const DEFAULT_FACILITATOR_ADDR:Object = {
            host: "173.255.221.44",
            port: 9002
        };

        // Milliseconds.
        private const FACILITATOR_POLL_INTERVAL:int = 10000;

        // Socket to facilitator.
        private var s_f:Socket;

        private var output_text:TextField;

        private var fac_addr:Object;

        [Embed(source="badge.png")]
        private var BadgeImage:Class;

        public function puts(s:String):void
        {
            output_text.appendText(s + "\n");
            output_text.scrollV = output_text.maxScrollV;
        }

        public function swfcat()
        {
            // Absolute positioning.
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;

            output_text = new TextField();
            output_text.width = stage.stageWidth;
            output_text.height = stage.stageHeight;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44cc44;

            puts("Starting.");
            // Wait until the query string parameters are loaded.
            this.loaderInfo.addEventListener(Event.COMPLETE, loaderinfo_complete);
        }

        private function loaderinfo_complete(e:Event):void
        {
            var fac_spec:String;

            puts("Parameters loaded.");

            if (this.loaderInfo.parameters["debug"])
                addChild(output_text);
            else
                addChild(new BadgeImage());

            fac_spec = this.loaderInfo.parameters["facilitator"];
            if (fac_spec) {
                puts("Facilitator spec: \"" + fac_spec + "\"");
                fac_addr = parse_addr_spec(fac_spec);
                if (!fac_addr) {
                    puts("Error: Facilitator spec must be in the form \"host:port\".");
                    return;
                }
            } else {
                fac_addr = DEFAULT_FACILITATOR_ADDR;
            }

            main();
        }

        /* The main logic begins here, after start-up issues are taken care of. */
        private function main():void
        {
            s_f = new Socket();

            s_f.addEventListener(Event.CONNECT, fac_connected);
            s_f.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("Facilitator: closed connection.");
                setTimeout(main, FACILITATOR_POLL_INTERVAL);
            });
            s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("Facilitator: I/O error: " + e.text + ".");
            });
            s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("Facilitator: security error: " + e.text + ".");
            });

            puts("Facilitator: connecting to " + fac_addr.host + ":" + fac_addr.port + ".");
            s_f.connect(fac_addr.host, fac_addr.port);
        }

        private function fac_connected(e:Event):void
        {
            puts("Facilitator: connected.");

            s_f.addEventListener(ProgressEvent.SOCKET_DATA, fac_data);

            s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
        }

        private function fac_data(e:ProgressEvent):void
        {
            var client_spec:String;
            var client_addr:Object;
            var proxy_pair:Object;

            client_spec = s_f.readMultiByte(e.bytesLoaded, "utf-8");
            puts("Facilitator: got \"" + client_spec + "\".");

            client_addr = parse_addr_spec(client_spec);
            if (!client_addr) {
                puts("Error: Client spec must be in the form \"host:port\".");
                return;
            }
            if (client_addr.host == "0.0.0.0" && client_addr.port == 0) {
                puts("Error: Facilitator has no clients.");
                return;
            }

            proxy_pair = new ProxyPair(this, client_addr, DEFAULT_TOR_ADDR);
            proxy_pair.connect();
        }

        /* Parse an address in the form "host:port". Returns an Object with
           keys "host" (String) and "port" (int). Returns null on error. */
        private static function parse_addr_spec(spec:String):Object
        {
            var parts:Array;
            var addr:Object;

            parts = spec.split(":", 2);
            if (parts.length != 2 || !parseInt(parts[1]))
                return null;
            addr = {}
            addr.host = parts[0];
            addr.port = parseInt(parts[1]);

            return addr;
        }
    }
}

import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Socket;
import flash.utils.ByteArray;

/* An instance of a client-relay connection. */
class ProxyPair
{
    // Address ({host, port}) of client.
    private var addr_c:Object;
    // Address ({host, port}) of relay.
    private var addr_r:Object;

    // Socket to client.
    private var s_c:Socket;
    // Socket to relay.
    private var s_r:Socket;

    // Parent swfcat, for UI updates.
    private var ui:swfcat;

    private function log(msg:String):void
    {
        ui.puts(id() + ": " + msg)
    }

    // String describing this pair for output.
    private function id():String
    {
        return "<" + this.addr_c.host + ":" + this.addr_c.port +
            "," + this.addr_r.host + ":" + this.addr_r.port + ">";
    }

    public function ProxyPair(ui:swfcat, addr_c:Object, addr_r:Object)
    {
        this.ui = ui;
        this.addr_c = addr_c;
        this.addr_r = addr_r;
    }

    public function connect():void
    {
        s_r = new Socket();

        s_r.addEventListener(Event.CONNECT, tor_connected);
        s_r.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Tor: closed.");
            if (s_c.connected)
                s_c.close();
        });
        s_r.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Tor: I/O error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
        });
        s_r.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Tor: security error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
        });

        log("Tor: connecting to " + addr_r.host + ":" + addr_r.port + ".");
        s_r.connect(addr_r.host, addr_r.port);
    }

    private function tor_connected(e:Event):void
    {
        log("Tor: connected.");

        s_c = new Socket();

        s_c.addEventListener(Event.CONNECT, client_connected);
        s_c.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Client: closed.");
            if (s_r.connected)
                s_r.close();
        });
        s_c.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Client: I/O error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
        });
        s_c.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Client: security error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
        });

        log("Client: connecting to " + addr_c.host + ":" + addr_c.port + ".");
        s_c.connect(addr_c.host, addr_c.port);
    }

    private function client_connected(e:Event):void
    {
        log("Client: connected.");

        s_r.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_r.readBytes(bytes, 0, e.bytesLoaded);
            log("Tor: read " + bytes.length + ".");
            s_c.writeBytes(bytes);
        });
        s_c.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_c.readBytes(bytes, 0, e.bytesLoaded);
            log("Client: read " + bytes.length + ".");
            s_r.writeBytes(bytes);
        });
    }
}

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.text.TextField;
    import flash.net.Socket;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.utils.ByteArray;
    import flash.utils.setTimeout;

    public class swfcat extends Sprite
    {
        /* David's relay (nickname 3VXRyxz67OeRoqHn) that also serves a
           crossdomain policy. */
        private const DEFAULT_TOR_ADDR:Object = {
            host: "173.255.221.44",
            port: 9001
        };
        private const DEFAULT_FACILITATOR_ADDR:Object = {
            host: "173.255.221.44",
            port: 9002
        };

        // Milliseconds.
        private const FACILITATOR_POLL_INTERVAL:int = 10000;

        // Socket to facilitator.
        private var s_f:Socket;

        private var output_text:TextField;

        private var fac_addr:Object;

        [Embed(source="badge.png")]
        private var BadgeImage:Class;

        public function puts(s:String):void
        {
            output_text.appendText(s + "\n");
            output_text.scrollV = output_text.maxScrollV;
        }

        public function swfcat()
        {
            // Absolute positioning.
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;

            output_text = new TextField();
            output_text.width = stage.stageWidth;
            output_text.height = stage.stageHeight;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44cc44;

            puts("Starting.");
            // Wait until the query string parameters are loaded.
            this.loaderInfo.addEventListener(Event.COMPLETE, loaderinfo_complete);
        }

        private function loaderinfo_complete(e:Event):void
        {
            var fac_spec:String;

            puts("Parameters loaded.");

            if (this.loaderInfo.parameters["debug"])
                addChild(output_text);
            else
                addChild(new BadgeImage());

            fac_spec = this.loaderInfo.parameters["facilitator"];
            if (fac_spec) {
                puts("Facilitator spec: \"" + fac_spec + "\"");
                fac_addr = parse_addr_spec(fac_spec);
                if (!fac_addr) {
                    puts("Error: Facilitator spec must be in the form \"host:port\".");
                    return;
                }
            } else {
                fac_addr = DEFAULT_FACILITATOR_ADDR;
            }

            main();
        }

        /* The main logic begins here, after start-up issues are taken care of. */
        private function main():void
        {
            s_f = new Socket();

            s_f.addEventListener(Event.CONNECT, fac_connected);
            s_f.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("Facilitator: closed connection.");
                setTimeout(main, FACILITATOR_POLL_INTERVAL);
            });
            s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("Facilitator: I/O error: " + e.text + ".");
            });
            s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("Facilitator: security error: " + e.text + ".");
            });

            puts("Facilitator: connecting to " + fac_addr.host + ":" + fac_addr.port + ".");
            s_f.connect(fac_addr.host, fac_addr.port);
        }

        private function fac_connected(e:Event):void
        {
            puts("Facilitator: connected.");

            s_f.addEventListener(ProgressEvent.SOCKET_DATA, fac_data);

            s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
        }

        private function fac_data(e:ProgressEvent):void
        {
            var client_spec:String;
            var client_addr:Object;
            var proxy_pair:Object;

            client_spec = s_f.readMultiByte(e.bytesLoaded, "utf-8");
            puts("Facilitator: got \"" + client_spec + "\".");

            client_addr = parse_addr_spec(client_spec);
            if (!client_addr) {
                puts("Error: Client spec must be in the form \"host:port\".");
                return;
            }
            if (client_addr.host == "0.0.0.0" && client_addr.port == 0) {
                puts("Error: Facilitator has no clients.");
                return;
            }

            proxy_pair = new ProxyPair(this, client_addr, DEFAULT_TOR_ADDR);
            proxy_pair.addEventListener(Event.COMPLETE, function():void {
                proxy_pair.log("Complete.");
            })
            proxy_pair.connect();
        }

        /* Parse an address in the form "host:port". Returns an Object with
           keys "host" (String) and "port" (int). Returns null on error. */
        private static function parse_addr_spec(spec:String):Object
        {
            var parts:Array;
            var addr:Object;

            parts = spec.split(":", 2);
            if (parts.length != 2 || !parseInt(parts[1]))
                return null;
            addr = {}
            addr.host = parts[0];
            addr.port = parseInt(parts[1]);

            return addr;
        }
    }
}

import flash.display.Sprite;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Socket;
import flash.utils.ByteArray;

/* An instance of a client-relay connection. */
class ProxyPair extends EventDispatcher
{
    // Address ({host, port}) of client.
    private var addr_c:Object;
    // Address ({host, port}) of relay.
    private var addr_r:Object;

    // Socket to client.
    private var s_c:Socket;
    // Socket to relay.
    private var s_r:Socket;

    // Parent swfcat, for UI updates.
    private var ui:swfcat;

    private function log(msg:String):void
    {
        ui.puts(id() + ": " + msg)
    }

    // String describing this pair for output.
    private function id():String
    {
        return "<" + this.addr_c.host + ":" + this.addr_c.port +
            "," + this.addr_r.host + ":" + this.addr_r.port + ">";
    }

    public function ProxyPair(ui:swfcat, addr_c:Object, addr_r:Object)
    {
        this.ui = ui;
        this.addr_c = addr_c;
        this.addr_r = addr_r;
    }

    public function connect():void
    {
        s_r = new Socket();

        s_r.addEventListener(Event.CONNECT, tor_connected);
        s_r.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Tor: closed.");
            if (s_c.connected)
                s_c.close();
            dispatchEvent(new Event(Event.COMPLETE));
        });
        s_r.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Tor: I/O error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
            dispatchEvent(new Event(Event.COMPLETE));
        });
        s_r.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Tor: security error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
            dispatchEvent(new Event(Event.COMPLETE));
        });

        log("Tor: connecting to " + addr_r.host + ":" + addr_r.port + ".");
        s_r.connect(addr_r.host, addr_r.port);
    }

    private function tor_connected(e:Event):void
    {
        log("Tor: connected.");

        s_c = new Socket();

        s_c.addEventListener(Event.CONNECT, client_connected);
        s_c.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Client: closed.");
            if (s_r.connected)
                s_r.close();
            dispatchEvent(new Event(Event.COMPLETE));
        });
        s_c.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Client: I/O error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
            dispatchEvent(new Event(Event.COMPLETE));
        });
        s_c.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Client: security error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
            dispatchEvent(new Event(Event.COMPLETE));
        });

        log("Client: connecting to " + addr_c.host + ":" + addr_c.port + ".");
        s_c.connect(addr_c.host, addr_c.port);
    }

    private function client_connected(e:Event):void
    {
        log("Client: connected.");

        s_r.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_r.readBytes(bytes, 0, e.bytesLoaded);
            log("Tor: read " + bytes.length + ".");
            s_c.writeBytes(bytes);
        });
        s_c.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_c.readBytes(bytes, 0, e.bytesLoaded);
            log("Client: read " + bytes.length + ".");
            s_r.writeBytes(bytes);
        });
    }
}

Add an Event.COMPLETE message when a ProxyPair finishes.