package aerys.minko.render
{
	import aerys.minko.ns.minko_render;
	import aerys.minko.render.resource.Context3DResource;
	import aerys.minko.render.resource.IndexBuffer3DResource;
	import aerys.minko.render.resource.Program3DResource;
	import aerys.minko.render.resource.VertexBuffer3DResource;
	import aerys.minko.render.resource.texture.ITextureResource;
	import aerys.minko.render.shader.binding.IBinder;
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.enum.Blending;
	import aerys.minko.type.enum.ColorMask;
	import aerys.minko.type.enum.TriangleCulling;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Vector4;
	import aerys.minko.render.geometry.stream.IVertexStream;
	import aerys.minko.render.geometry.stream.StreamUsage;
	import aerys.minko.render.geometry.stream.VertexStream;
	import aerys.minko.render.geometry.stream.format.VertexComponent;
	import aerys.minko.render.geometry.stream.format.VertexFormat;

	import flash.display3D.Context3DProgramType;
	import flash.utils.Dictionary;

	/**
	 * DrawCall objects contain all the shader constants and buffer settings required
	 * to perform drawing operations using the Stage3D API.
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class DrawCall
	{
		use namespace minko_render;

		private static const PROGRAM_TYPE_VERTEX	: String			= Context3DProgramType.VERTEX;
		private static const PROGRAM_TYPE_FRAGMENT	: String			= Context3DProgramType.FRAGMENT;
		private static const NUM_TEXTURES			: uint				= 8;
		private static const NUM_VERTEX_BUFFERS		: uint				= 8;

		private static const TMP_VECTOR4			: Vector4			= new Vector4();
		private static const TMP_NUMBERS			: Vector.<Number>	= new Vector.<Number>(0xffff, true);
		private static const TMP_INTS				: Vector.<int>		= new Vector.<int>(0xffff, true);

		private var _bindings			: Object							= null;

		private var _vsInputComponents	: Vector.<VertexComponent>			= null;
		private var _vsInputIndices		: Vector.<uint>						= null;
		private var _cpuConstants		: Dictionary						= null;
		private var _vsConstants		: Vector.<Number>					= null;
		private var _fsConstants		: Vector.<Number>					= null;
		private var _fsTextures			: Vector.<ITextureResource>			= new Vector.<ITextureResource>(NUM_TEXTURES, true);

		// states
		private var _indexBuffer		: IndexBuffer3DResource				= null;
		private var _firstIndex			: int								= 0;
		private var _numTriangles		: int								= -1;

		private var _vertexBuffers		: Vector.<VertexBuffer3DResource>	= new Vector.<VertexBuffer3DResource>(NUM_VERTEX_BUFFERS, true);
		private var _numVertexComponents: uint								= 0;
		private var _offsets			: Vector.<int>						= new Vector.<int>(8, true);
		private var _formats			: Vector.<String>					= new Vector.<String>(8, true);

		private var _blending			: uint								= 0;
		private var _blendingSource		: String							= null;
		private var _blendingDest		: String							= null;

		private var _triangleCulling	: uint								= 0;
		private var _triangleCullingStr	: String							= null;

		private var _colorMask			: uint								= 0;
		private var _colorMaskR			: Boolean							= true;
		private var _colorMaskG			: Boolean							= true;
		private var _colorMaskB			: Boolean							= true;
		private var _colorMaskA			: Boolean							= true;

		private var _enabled			: Boolean							= true;

		private var _depth				: Number							= 0.;
		private var _invalidDepth		: Boolean							= false;
		private var _localToWorld		: Matrix4x4							= null;
		private var _worldToScreen		: Matrix4x4							= null;

		public function get vertexComponents() : Vector.<VertexComponent>
		{
			return _vsInputComponents;
		}

		public function get blending() : uint
		{
			return _blending;
		}
		public function set blending(value : uint) : void
		{
			_blending = value;
			_blendingSource = Blending.STRINGS[int(value & 0xffff)];
			_blendingDest	= Blending.STRINGS[int(value >>> 16)]
		}

		public function get triangleCulling() : uint
		{
			return _triangleCulling;
		}
		public function set triangleCulling(value : uint) : void
		{
			_triangleCulling = value;
			_triangleCullingStr = TriangleCulling.STRINGS[value];
		}

		public function get colorMask() : uint
		{
			return _colorMask;
		}
		public function set colorMask(value : uint) : void
		{
			_colorMask = value;
			_colorMaskR = (value & ColorMask.RED) != 0;
			_colorMaskG = (value & ColorMask.GREEN) != 0;
			_colorMaskB = (value & ColorMask.BLUE) != 0;
			_colorMaskA = (value & ColorMask.ALPHA) != 0;
		}

		public function get enabled() : Boolean
		{
			return _enabled;
		}
		public function set enabled(value : Boolean) : void
		{
			_enabled = value;
		}

		public function get depth() : Number
		{
			if (_invalidDepth)
			{
				_invalidDepth = false;

				if (_localToWorld != null && _worldToScreen != null)
				{
					_localToWorld.transformVector(Vector4.ZERO, TMP_VECTOR4);
					_depth = _worldToScreen.transformVector(TMP_VECTOR4, TMP_VECTOR4).z;
				}
			}

			return _depth;
		}

		public function configure(program		: Program3DResource,
								  geometry		: Geometry,
								  meshBindings	: DataBindings,
								  sceneBindings	: DataBindings,
								  computeDepth	: Boolean) : void
		{
			if (_bindings != null)
				unsetBindings(meshBindings, sceneBindings);

			_invalidDepth = computeDepth;

			setProgram(program);
			updateGeometry(geometry);
			setGeometry(geometry);
			setBindings(meshBindings, sceneBindings, computeDepth);
		}

		public function unsetBindings(meshBindings	: DataBindings,
									  sceneBindings	: DataBindings) : void
		{
			trace('unset bindings');

			for (var parameter : String in _bindings)
			{
				meshBindings.removeCallback(parameter, parameterChangedHandler);
				sceneBindings.removeCallback(parameter, parameterChangedHandler);
			}

			if (sceneBindings.hasCallback('worldToScreen', transformChangedHandler))
				sceneBindings.removeCallback('worldToScreen', transformChangedHandler);

			if (meshBindings.hasCallback('localToWorld', transformChangedHandler))
				meshBindings.removeCallback('localToWorld', transformChangedHandler);
		}

		private function setProgram(program : Program3DResource) : void
		{
			_cpuConstants		= new Dictionary();
			_vsConstants		= program._vsConstants.concat();
			_fsConstants		= program._fsConstants.concat();
			_fsTextures			= program._fsTextures.concat();
			_vsInputComponents	= program._vertexInputComponents;
			_vsInputIndices		= program._vertexInputIndices;
			_bindings			= program._bindings;

			triangleCulling		= TriangleCulling.FRONT;
			blending			= Blending.NORMAL;
			colorMask			= ColorMask.RGBA;
		}

		 /**
		  * Ask geometry to compute additional vertex data if needed for this drawcall.
		  */
		public function updateGeometry(geometry : Geometry) : void
		{
			var vertexFormat : VertexFormat	= geometry.format;

			if (_vsInputComponents.indexOf(VertexComponent.TANGENT) >= 0
				&& !vertexFormat.hasComponent(VertexComponent.TANGENT))
			{
				geometry.computeTangentSpace(StreamUsage.DYNAMIC);
			}
			else if (_vsInputComponents.indexOf(VertexComponent.NORMAL) >= 0
				&& !vertexFormat.hasComponent(VertexComponent.NORMAL))
			{
				geometry.computeNormals(StreamUsage.DYNAMIC);
			}
		}

		/**
		 * Obtain a reference to each buffer and offset that apply() may possibly need.
		 *
		 */
		public function setGeometry(geometry : Geometry, frame : uint = 0) : void
		{
			_numVertexComponents = _vsInputComponents.length;
			_indexBuffer		 = geometry.indexStream.resource;
			_firstIndex			 = geometry.firstIndex;
			_numTriangles		 = geometry.numTriangles;

			for (var i : uint = 0; i < _numVertexComponents; ++i)
			{
				var component : VertexComponent	= _vsInputComponents[i];
				var index	  : uint			= _vsInputIndices[i];

				if (component)
				{
					var vertexStream	: IVertexStream = geometry.getVertexStream(index + frame);
					var stream	 		: VertexStream	= vertexStream.getStreamByComponent(component);

					if (stream == null)
					{
						throw new Error(
							'Missing vertex component: \'' + component.toString() + '\'.'
						);
					}

					_vertexBuffers[i]	= stream.resource;
					_formats[i]			= component.nativeFormatString;
					_offsets[i]			= stream.format.getOffsetForComponent(component);
				}
			}
		}

		/**
		 * @fixme There is a bug here
		 * @fixme We splitted properties between scene and mesh
		 * @fixme it should be done on the compiler also to avoid this ugly hack
		 */
		private function setBindings(meshBindings	: DataBindings,
									 sceneBindings	: DataBindings,
									 computeDepth	: Boolean) : void
		{
			trace('set bindings');

			for (var parameter : String in _bindings)
			{
				meshBindings.addCallback(parameter, parameterChangedHandler);
				sceneBindings.addCallback(parameter, parameterChangedHandler);

				if (meshBindings.propertyExists(parameter))
					setParameter(parameter, meshBindings.getProperty(parameter));
				else if (sceneBindings.propertyExists(parameter))
					setParameter(parameter, sceneBindings.getProperty(parameter));
			}

			if (computeDepth)
			{
				_worldToScreen = sceneBindings.getProperty('worldToScreen') as Matrix4x4;
				_localToWorld = meshBindings.getProperty('localToWorld') as Matrix4x4;

				sceneBindings.addCallback('worldToScreen', transformChangedHandler);
				meshBindings.addCallback('localToWorld', transformChangedHandler);

				_invalidDepth = true;
			}
		}

		public function apply(context : Context3DResource, previous : DrawCall) : uint
		{
			if (!_enabled)
				return 0;

			context.setColorMask(_colorMaskR, _colorMaskG, _colorMaskB, _colorMaskA)
				   .setProgramConstantsFromVector(PROGRAM_TYPE_VERTEX, 0, _vsConstants)
				   .setProgramConstantsFromVector(PROGRAM_TYPE_FRAGMENT, 0, _fsConstants);

			var numTextures	: uint	= _fsTextures.length;
			var maxTextures	: uint	= previous ? previous._fsTextures.length : NUM_TEXTURES;
			var maxBuffers	: uint	= previous ? previous._numVertexComponents : NUM_VERTEX_BUFFERS;
			var i 			: uint 	= 0;

			// setup textures
			for (i = 0; i < numTextures; ++i)
			{
				context.setTextureAt(
					i,
					(_fsTextures[i] as ITextureResource).getNativeTexture(context)
				);
			}

			while (i < maxTextures)
				context.setTextureAt(i++, null);

			// setup buffers
			for (i = 0; i < _numVertexComponents; ++i)
			{
				context.setVertexBufferAt(
					i,
					(_vertexBuffers[i] as VertexBuffer3DResource).getVertexBuffer3D(context),
					_offsets[i],
					_formats[i]
				);
			}

			while (i < maxBuffers)
				context.setVertexBufferAt(i++, null);

			// draw triangles
			context.drawTriangles(
				_indexBuffer.getIndexBuffer3D(context),
				_firstIndex,
				_numTriangles
			);

			return _numTriangles == -1 ? _indexBuffer.numIndices / 3 : _numTriangles;
		}

		public function setParameter(name : String, value : Object) : void
		{
			var binding : IBinder = _bindings[name] as IBinder;
			trace(name);
			if (binding != null)
				binding.set(_cpuConstants, _vsConstants, _fsConstants, _fsTextures, value);
		}

		private function parameterChangedHandler(dataBindings	: DataBindings,
												 property		: String,
												 oldValue		: Object,
												 newValue		: Object) : void
		{
			newValue !== null && setParameter(property, newValue);
		}

		private function transformChangedHandler(bindings 	: DataBindings,
												 property 	: String,
												 oldValue	: Matrix4x4,
												 newValue 	: Matrix4x4) : void
		{
			if (property == 'worldToScreen')
				_worldToScreen = newValue;
			else if (property == 'localToWorld')
				_localToWorld = newValue;

			_invalidDepth = true;
		}
	}
}

package aerys.minko.render
{
	import aerys.minko.ns.minko_render;
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.geometry.stream.IVertexStream;
	import aerys.minko.render.geometry.stream.StreamUsage;
	import aerys.minko.render.geometry.stream.VertexStream;
	import aerys.minko.render.geometry.stream.format.VertexComponent;
	import aerys.minko.render.geometry.stream.format.VertexFormat;
	import aerys.minko.render.resource.Context3DResource;
	import aerys.minko.render.resource.IndexBuffer3DResource;
	import aerys.minko.render.resource.Program3DResource;
	import aerys.minko.render.resource.VertexBuffer3DResource;
	import aerys.minko.render.resource.texture.ITextureResource;
	import aerys.minko.render.shader.binding.IBinder;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.enum.Blending;
	import aerys.minko.type.enum.ColorMask;
	import aerys.minko.type.enum.TriangleCulling;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Vector4;

	import flash.display3D.Context3DProgramType;
	import flash.utils.Dictionary;

	/**
	 * DrawCall objects contain all the shader constants and buffer settings required
	 * to perform drawing operations using the Stage3D API.
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class DrawCall
	{
		use namespace minko_render;

		private static const PROGRAM_TYPE_VERTEX	: String			= Context3DProgramType.VERTEX;
		private static const PROGRAM_TYPE_FRAGMENT	: String			= Context3DProgramType.FRAGMENT;
		private static const NUM_TEXTURES			: uint				= 8;
		private static const NUM_VERTEX_BUFFERS		: uint				= 8;

		private static const TMP_VECTOR4			: Vector4			= new Vector4();
		private static const TMP_NUMBERS			: Vector.<Number>	= new Vector.<Number>(0xffff, true);
		private static const TMP_INTS				: Vector.<int>		= new Vector.<int>(0xffff, true);

		private var _bindings			: Object							= null;

		private var _vsInputComponents	: Vector.<VertexComponent>			= null;
		private var _vsInputIndices		: Vector.<uint>						= null;
		private var _cpuConstants		: Dictionary						= null;
		private var _vsConstants		: Vector.<Number>					= null;
		private var _fsConstants		: Vector.<Number>					= null;
		private var _fsTextures			: Vector.<ITextureResource>			= new Vector.<ITextureResource>(NUM_TEXTURES, true);

		// states
		private var _indexBuffer		: IndexBuffer3DResource				= null;
		private var _firstIndex			: int								= 0;
		private var _numTriangles		: int								= -1;

		private var _vertexBuffers		: Vector.<VertexBuffer3DResource>	= new Vector.<VertexBuffer3DResource>(NUM_VERTEX_BUFFERS, true);
		private var _numVertexComponents: uint								= 0;
		private var _offsets			: Vector.<int>						= new Vector.<int>(8, true);
		private var _formats			: Vector.<String>					= new Vector.<String>(8, true);

		private var _blending			: uint								= 0;
		private var _blendingSource		: String							= null;
		private var _blendingDest		: String							= null;

		private var _triangleCulling	: uint								= 0;
		private var _triangleCullingStr	: String							= null;

		private var _colorMask			: uint								= 0;
		private var _colorMaskR			: Boolean							= true;
		private var _colorMaskG			: Boolean							= true;
		private var _colorMaskB			: Boolean							= true;
		private var _colorMaskA			: Boolean							= true;

		private var _enabled			: Boolean							= true;

		private var _depth				: Number							= 0.;
		private var _invalidDepth		: Boolean							= false;
		private var _localToWorld		: Matrix4x4							= null;
		private var _worldToScreen		: Matrix4x4							= null;

		public function get vertexComponents() : Vector.<VertexComponent>
		{
			return _vsInputComponents;
		}

		public function get blending() : uint
		{
			return _blending;
		}
		public function set blending(value : uint) : void
		{
			_blending = value;
			_blendingSource = Blending.STRINGS[int(value & 0xffff)];
			_blendingDest	= Blending.STRINGS[int(value >>> 16)]
		}

		public function get triangleCulling() : uint
		{
			return _triangleCulling;
		}
		public function set triangleCulling(value : uint) : void
		{
			_triangleCulling = value;
			_triangleCullingStr = TriangleCulling.STRINGS[value];
		}

		public function get colorMask() : uint
		{
			return _colorMask;
		}
		public function set colorMask(value : uint) : void
		{
			_colorMask = value;
			_colorMaskR = (value & ColorMask.RED) != 0;
			_colorMaskG = (value & ColorMask.GREEN) != 0;
			_colorMaskB = (value & ColorMask.BLUE) != 0;
			_colorMaskA = (value & ColorMask.ALPHA) != 0;
		}

		public function get enabled() : Boolean
		{
			return _enabled;
		}
		public function set enabled(value : Boolean) : void
		{
			_enabled = value;
		}

		public function get depth() : Number
		{
			if (_invalidDepth)
			{
				_invalidDepth = false;

				if (_localToWorld != null && _worldToScreen != null)
				{
					_localToWorld.transformVector(Vector4.ZERO, TMP_VECTOR4);
					_depth = _worldToScreen.transformVector(TMP_VECTOR4, TMP_VECTOR4).z;
				}
			}

			return _depth;
		}

		public function configure(program		: Program3DResource,
								  geometry		: Geometry,
								  meshBindings	: DataBindings,
								  sceneBindings	: DataBindings,
								  computeDepth	: Boolean) : void
		{
			if (_bindings != null)
				unsetBindings(meshBindings, sceneBindings);

			_invalidDepth = computeDepth;

			setProgram(program);
			updateGeometry(geometry);
			setGeometry(geometry);
			setBindings(meshBindings, sceneBindings, computeDepth);
		}

		public function unsetBindings(meshBindings	: DataBindings,
									  sceneBindings	: DataBindings) : void
		{
			for (var parameter : String in _bindings)
			{
				meshBindings.removeCallback(parameter, parameterChangedHandler);
				sceneBindings.removeCallback(parameter, parameterChangedHandler);
			}

			if (sceneBindings.hasCallback('worldToScreen', transformChangedHandler))
				sceneBindings.removeCallback('worldToScreen', transformChangedHandler);

			if (meshBindings.hasCallback('localToWorld', transformChangedHandler))
				meshBindings.removeCallback('localToWorld', transformChangedHandler);
		}

		private function setProgram(program : Program3DResource) : void
		{
			_cpuConstants		= new Dictionary();
			_vsConstants		= program._vsConstants.concat();
			_fsConstants		= program._fsConstants.concat();
			_fsTextures			= program._fsTextures.concat();
			_vsInputComponents	= program._vertexInputComponents;
			_vsInputIndices		= program._vertexInputIndices;
			_bindings			= program._bindings;

			triangleCulling		= TriangleCulling.FRONT;
			blending			= Blending.NORMAL;
			colorMask			= ColorMask.RGBA;
		}

		 /**
		  * Ask geometry to compute additional vertex data if needed for this drawcall.
		  */
		public function updateGeometry(geometry : Geometry) : void
		{
			var vertexFormat : VertexFormat	= geometry.format;

			if (_vsInputComponents.indexOf(VertexComponent.TANGENT) >= 0
				&& !vertexFormat.hasComponent(VertexComponent.TANGENT))
			{
				geometry.computeTangentSpace(StreamUsage.DYNAMIC);
			}
			else if (_vsInputComponents.indexOf(VertexComponent.NORMAL) >= 0
				&& !vertexFormat.hasComponent(VertexComponent.NORMAL))
			{
				geometry.computeNormals(StreamUsage.DYNAMIC);
			}
		}

		/**
		 * Obtain a reference to each buffer and offset that apply() may possibly need.
		 *
		 */
		public function setGeometry(geometry : Geometry, frame : uint = 0) : void
		{
			_numVertexComponents = _vsInputComponents.length;
			_indexBuffer		 = geometry.indexStream.resource;
			_firstIndex			 = geometry.firstIndex;
			_numTriangles		 = geometry.numTriangles;

			for (var i : uint = 0; i < _numVertexComponents; ++i)
			{
				var component : VertexComponent	= _vsInputComponents[i];
				var index	  : uint			= _vsInputIndices[i];

				if (component)
				{
					var vertexStream	: IVertexStream = geometry.getVertexStream(index + frame);
					var stream	 		: VertexStream	= vertexStream.getStreamByComponent(component);

					if (stream == null)
					{
						throw new Error(
							'Missing vertex component: \'' + component.toString() + '\'.'
						);
					}

					_vertexBuffers[i]	= stream.resource;
					_formats[i]			= component.nativeFormatString;
					_offsets[i]			= stream.format.getOffsetForComponent(component);
				}
			}
		}

		/**
		 * @fixme There is a bug here
		 * @fixme We splitted properties between scene and mesh
		 * @fixme it should be done on the compiler also to avoid this ugly hack
		 */
		private function setBindings(meshBindings	: DataBindings,
									 sceneBindings	: DataBindings,
									 computeDepth	: Boolean) : void
		{
			for (var parameter : String in _bindings)
			{
				meshBindings.addCallback(parameter, parameterChangedHandler);
				sceneBindings.addCallback(parameter, parameterChangedHandler);

				if (meshBindings.propertyExists(parameter))
					setParameter(parameter, meshBindings.getProperty(parameter));
				else if (sceneBindings.propertyExists(parameter))
					setParameter(parameter, sceneBindings.getProperty(parameter));
			}

			if (computeDepth)
			{
				_worldToScreen = sceneBindings.getProperty('worldToScreen') as Matrix4x4;
				_localToWorld = meshBindings.getProperty('localToWorld') as Matrix4x4;

				sceneBindings.addCallback('worldToScreen', transformChangedHandler);
				meshBindings.addCallback('localToWorld', transformChangedHandler);

				_invalidDepth = true;
			}
		}

		public function apply(context : Context3DResource, previous : DrawCall) : uint
		{
			if (!_enabled)
				return 0;

			context.setColorMask(_colorMaskR, _colorMaskG, _colorMaskB, _colorMaskA)
				   .setProgramConstantsFromVector(PROGRAM_TYPE_VERTEX, 0, _vsConstants)
				   .setProgramConstantsFromVector(PROGRAM_TYPE_FRAGMENT, 0, _fsConstants);

			var numTextures	: uint	= _fsTextures.length;
			var maxTextures	: uint	= previous ? previous._fsTextures.length : NUM_TEXTURES;
			var maxBuffers	: uint	= previous ? previous._numVertexComponents : NUM_VERTEX_BUFFERS;
			var i 			: uint 	= 0;

			// setup textures
			for (i = 0; i < numTextures; ++i)
			{
				context.setTextureAt(
					i,
					(_fsTextures[i] as ITextureResource).getNativeTexture(context)
				);
			}

			while (i < maxTextures)
				context.setTextureAt(i++, null);

			// setup buffers
			for (i = 0; i < _numVertexComponents; ++i)
			{
				context.setVertexBufferAt(
					i,
					(_vertexBuffers[i] as VertexBuffer3DResource).getVertexBuffer3D(context),
					_offsets[i],
					_formats[i]
				);
			}

			while (i < maxBuffers)
				context.setVertexBufferAt(i++, null);

			// draw triangles
			context.drawTriangles(
				_indexBuffer.getIndexBuffer3D(context),
				_firstIndex,
				_numTriangles
			);

			return _numTriangles == -1 ? _indexBuffer.numIndices / 3 : _numTriangles;
		}

		public function setParameter(name : String, value : Object) : void
		{
			var binding : IBinder = _bindings[name] as IBinder;

			if (binding != null)
				binding.set(_cpuConstants, _vsConstants, _fsConstants, _fsTextures, value);
		}

		private function parameterChangedHandler(dataBindings	: DataBindings,
												 property		: String,
												 oldValue		: Object,
												 newValue		: Object) : void
		{
			newValue !== null && setParameter(property, newValue);
		}

		private function transformChangedHandler(bindings 	: DataBindings,
												 property 	: String,
												 oldValue	: Matrix4x4,
												 newValue 	: Matrix4x4) : void
		{
			if (property == 'worldToScreen')
				_worldToScreen = newValue;
			else if (property == 'localToWorld')
				_localToWorld = newValue;

			_invalidDepth = true;
		}
	}
}

remove call to trace() in DrawCall

remove call to trace() in DrawCall


package
{
    import flash.display.Sprite;
    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;

    public class swfcat extends Sprite
    {
        private const TOR_ADDRESS:String = "173.255.221.44";
        private const TOR_PORT:int = 9001;

        private var output_text:TextField;

        // Socket to Tor relay.
        private var s_t:Socket;
	// Socket to Facilitator.
	private var s_f:Socket;
        // Socket to client.
        private var s_c:Socket;

        private var fac_address:String;
        private var fac_port:int;

	private var client_address:String;
	private var client_port:int;

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

        public function swfcat()
        {
            output_text = new TextField();
            output_text.width = 400;
            output_text.height = 300;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44CC44;
            addChild(output_text);

            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, parts:Array;

            puts("Parameters loaded.");
            fac_spec = this.loaderInfo.parameters["facilitator"];
            if (!fac_spec) {
                puts("Error: no \"facilitator\" specification provided.");
                return;
            }
            puts("Facilitator spec: \"" + fac_spec + "\"");
            parts = fac_spec.split(":", 2);
            if (parts.length != 2 || !parseInt(parts[1])) {
                puts("Error: Facilitator spec must be in the form \"host:port\".");
                return;
            }
            fac_address = parts[0];
            fac_port = parseInt(parts[1]);

            go(TOR_ADDRESS, TOR_PORT);
        }

        /* We connect first to the Tor relay; once that happens we connect to
           the facilitator to get a client address; once we have the address
	   of a waiting client then we connect to the client and BAM! we're in business. */
        private function go(tor_address:String, tor_port:int):void
        {
            s_t = new Socket();

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

            puts("Tor: connecting to " + tor_address + ":" + tor_port + ".");
            s_t.connect(tor_address, tor_port);
        }

        private function tor_connected(e:Event):void
        {
	    /* Got a connection to tor, now let's get served a client from the facilitator */
            s_f = new Socket();

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

            puts("Facilitator: connecting to " + fac_address + ":" + fac_port + ".");
            s_f.connect(fac_address, fac_port);

            /*s_c = new Socket();

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

            puts("Client: connecting to " + client_address + ":" + client_port + ".");
            s_c.connect(client_address, client_port);*/
        }

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

            s_f.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var client_spec:String = new String();
		client_spec = s_f.readUTF();
                puts("Facilitator: got \"" + client_spec + "\"");
                //s_c.writeBytes(bytes);
		/* Need to parse the bytes to get the new client. Fill out client_address and client_port */
            });

	    s_f.writeUTFBytes("GET / HTTP/1.0");
	    s_f.flush();

        }

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

            s_t.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var bytes:ByteArray = new ByteArray();
                s_t.readBytes(bytes, 0, e.bytesLoaded);
                puts("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);
                puts("Client: read " + bytes.length + ".");
                s_t.writeBytes(bytes);
            });
        }
    }
}

package
{
    import flash.display.Sprite;
    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;

    public class swfcat extends Sprite
    {
        private const TOR_ADDRESS:String = "173.255.221.44";
        private const TOR_PORT:int = 9001;

        private var output_text:TextField;

        // Socket to Tor relay.
        private var s_t:Socket;
	// Socket to Facilitator.
	private var s_f:Socket;
        // Socket to client.
        private var s_c:Socket;

        private var fac_address:String;
        private var fac_port:int;

	private var client_address:String;
	private var client_port:int;

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

        public function swfcat()
        {
            output_text = new TextField();
            output_text.width = 400;
            output_text.height = 300;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44CC44;
            addChild(output_text);

            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, parts:Array;

            puts("Parameters loaded.");
            fac_spec = this.loaderInfo.parameters["facilitator"];
            if (!fac_spec) {
                puts("Error: no \"facilitator\" specification provided.");
                return;
            }
            puts("Facilitator spec: \"" + fac_spec + "\"");
            parts = fac_spec.split(":", 2);
            if (parts.length != 2 || !parseInt(parts[1])) {
                puts("Error: Facilitator spec must be in the form \"host:port\".");
                return;
            }
            fac_address = parts[0];
            fac_port = parseInt(parts[1]);

            go(TOR_ADDRESS, TOR_PORT);
        }

        /* We connect first to the Tor relay; once that happens we connect to
           the facilitator to get a client address; once we have the address
	   of a waiting client then we connect to the client and BAM! we're in business. */
        private function go(tor_address:String, tor_port:int):void
        {
            s_t = new Socket();

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

            puts("Tor: connecting to " + tor_address + ":" + tor_port + ".");
            s_t.connect(tor_address, tor_port);
        }

        private function tor_connected(e:Event):void
        {
	    /* Got a connection to tor, now let's get served a client from the facilitator */
            s_f = new Socket();

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

            puts("Facilitator: connecting to " + fac_address + ":" + fac_port + ".");
            s_f.connect(fac_address, fac_port);

            /*s_c = new Socket();

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

            puts("Client: connecting to " + client_address + ":" + client_port + ".");
            s_c.connect(client_address, client_port);*/
        }

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

            s_f.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var client_spec:String = new String();
		client_spec = s_f.readUTF();
                puts("Facilitator: got \"" + client_spec + "\"");
                //s_c.writeBytes(bytes);
		/* Need to parse the bytes to get the new client. Fill out client_address and client_port */
            });

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

        }

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

            s_t.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
                var bytes:ByteArray = new ByteArray();
                s_t.readBytes(bytes, 0, e.bytesLoaded);
                puts("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);
                puts("Client: read " + bytes.length + ".");
                s_t.writeBytes(bytes);
            });
        }
    }
}

Add "\r\n\r\n" to finish the GET request.


package {

    import flash.display.MovieClip;
    import flash.events.MouseEvent;
    import flash.geom.Point;

    import org.concord.sparks.JavaScript;
    import org.concord.sparks.util.Display;

    public class Probe extends MovieClip {

        private var circuit:Circuit;
        private var connection:Object = null;
        private var id:String;

        private var _dragging:Boolean = false;
        private var _down:Boolean = false;

        public function Probe() {
            //trace('ENTER Probe#Probe');
            super();
            id = this.name;
            trace('Probe id=' + id);

            this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            this.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
			stage.addEventListener(MouseEvent.MOUSE_OUT, onMouseUp);
        }

        public function getId() {
            return id;
        }

        public function getConnection():Object {
            return connection;
        }

        public function setConnection(connection:Object):void {
            this.connection = connection;
        }

        public function setCircuit(circuit:Circuit) {
            //trace('ENTER Probe#setCircuit');
            this.circuit = circuit;
        }

        public function getTipPos():Point {
            return Display.localToStage(this.parent, new Point(this.x, this.y));
        }

        public function onMouseDown(event:MouseEvent):void {
            _down = true;
			circuit.putProbeOnTop(this);
        }

        public function onMouseUp(event:MouseEvent):void {
            trace('ENTER Probe#onMouseUp');
            this.stopDrag();
            _down = false;
            if (_dragging) {
                _dragging = false;
                circuit.updateProbeConnection(this);
            }
        }

        public function onMouseMove(event:MouseEvent):void {
            //trace('ENTER Probe#onMouseMove');
            if (! _down) {
                return;
            }
            if (_dragging) {
                circuit.updateResistorEndColors(this);
            }
            else {
                _dragging = true;
                this.startDrag();
                if (connection) {
                    disConnect();
                }
            }
        }

        private function disConnect():void {
            JavaScript.instance().sendEvent('disconnect', 'probe', getId(), connection.getId());
            connection = null;
        }
    }
}

package {
    //test change//

    import flash.display.MovieClip;
    import flash.events.MouseEvent;
    import flash.geom.Point;

    import org.concord.sparks.JavaScript;
    import org.concord.sparks.util.Display;

    public class Probe extends MovieClip {

        private var circuit:Circuit;
        private var connection:Object = null;
        private var id:String;

        private var _dragging:Boolean = false;
        private var _down:Boolean = false;

        public function Probe() {
            //trace('ENTER Probe#Probe');
            super();
            id = this.name;
            trace('Probe id=' + id);

            this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            this.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
			stage.addEventListener(MouseEvent.MOUSE_OUT, onMouseUp);
        }

        public function getId() {
            return id;
        }

        public function getConnection():Object {
            return connection;
        }

        public function setConnection(connection:Object):void {
            this.connection = connection;
        }

        public function setCircuit(circuit:Circuit) {
            //trace('ENTER Probe#setCircuit');
            this.circuit = circuit;
        }

        public function getTipPos():Point {
            return Display.localToStage(this.parent, new Point(this.x, this.y));
        }

        public function onMouseDown(event:MouseEvent):void {
            _down = true;
			circuit.putProbeOnTop(this);
        }

        public function onMouseUp(event:MouseEvent):void {
            trace('ENTER Probe#onMouseUp');
            this.stopDrag();
            _down = false;
            if (_dragging) {
                _dragging = false;
                circuit.updateProbeConnection(this);
            }
        }

        public function onMouseMove(event:MouseEvent):void {
            //trace('ENTER Probe#onMouseMove');
            if (! _down) {
                return;
            }
            if (_dragging) {
                circuit.updateResistorEndColors(this);
            }
            else {
                _dragging = true;
                this.startDrag();
                if (connection) {
                    disConnect();
                }
            }
        }

        private function disConnect():void {
            JavaScript.instance().sendEvent('disconnect', 'probe', getId(), connection.getId());
            connection = null;
        }
    }
}

test change

test change


package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.events.Event;
    import flash.utils.setTimeout;

    import FacilitatorSocket;
    import events.FacilitatorSocketEvent;

    import ProxyPair;
    import RTMFPProxyPair;
    import SQSProxyPair;
    import TCPProxyPair;

    import rtmfp.CirrusSocket;
    import rtmfp.events.CirrusSocketEvent;

    public class swfcat extends Sprite
    {
        /* Adobe's Cirrus server for RTMFP connections.
           The Cirrus key is defined at compile time by
           reading from the CIRRUS_KEY environment var. */
        private const DEFAULT_CIRRUS_ADDR:String = "rtmfp://p2p.rtmfp.net";
        private const DEFAULT_CIRRUS_KEY:String = RTMFP::CIRRUS_KEY;

        private const DEFAULT_FACILITATOR_ADDR:Object = {
            host: "tor-facilitator.bamsoftware.com",
            port: 9002
        };

        /* Default Tor client to use in case of RTMFP connection */
        private const DEFAULT_TOR_CLIENT_ADDR:Object = {
            host: "127.0.0.1",
            port: 9002
        };

        /* Poll facilitator every 10 sec */
        private const DEFAULT_FAC_POLL_INTERVAL:uint = 10000;

        // Socket to Cirrus server
        private var s_c:CirrusSocket;
        // Socket to facilitator.
        private var s_f:FacilitatorSocket;
        // Handle local-remote traffic
        private var p_p:ProxyPair;

        private var client_id:String;
        private var proxy_pair_factory:Function;

        private var proxy_pairs:Array;

        private var debug_mode:Boolean;
        private var proxy_mode:Boolean;

        /* TextField for debug output. */
        private var output_text:TextField;

        /* Badge for display */
        private var badge:InternetFreedomBadge;

        private var fac_addr:Object;
        private var relay_addr:Object;

        public function swfcat()
        {
            proxy_mode = false;
            debug_mode = false;

            // Absolute positioning.
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;

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

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

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

            debug_mode = (this.loaderInfo.parameters["debug"] != null)
            proxy_mode = (this.loaderInfo.parameters["proxy"] != null);
            if (proxy_mode && !debug_mode) {
                badge = new InternetFreedomBadge(this);
                badge.display();
            } else {
                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;
                addChild(output_text);
            }

            puts("Starting: parameters loaded.");

            /* TODO: use this to have multiple proxies going at once */
            proxy_pairs = new Array();

            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
        {
            if (proxy_mode) {
                establish_facilitator_connection();
            } else {
                establish_cirrus_connection();
            }
        }

        private function establish_cirrus_connection():void
        {
            s_c = new CirrusSocket();
            s_c.addEventListener(CirrusSocketEvent.CONNECT_SUCCESS, function (e:CirrusSocketEvent):void {
                puts("Cirrus: connected with id " + s_c.id + ".");
                if (proxy_mode) {
                    start_proxy_pair();
                    s_c.send_hello(client_id);
                } else {
                    establish_facilitator_connection();
                }
            });
            s_c.addEventListener(CirrusSocketEvent.CONNECT_FAILED, function (e:CirrusSocketEvent):void {
                puts("Error: failed to connect to Cirrus.");
            });
            s_c.addEventListener(CirrusSocketEvent.CONNECT_CLOSED, function (e:CirrusSocketEvent):void {
                puts("Cirrus: closed connection.");
            });
            s_c.addEventListener(CirrusSocketEvent.HELLO_RECEIVED, function (e:CirrusSocketEvent):void {
                puts("Cirrus: received hello from peer " + e.peer);

                /* don't bother if we already have a proxy going */
                if (p_p != null && p_p.connected) {
                    return;
                }

                /* if we're in proxy mode, we should have already set
                   up a proxy pair */
                if (!proxy_mode) {
                    relay_addr = DEFAULT_TOR_CLIENT_ADDR;
                    proxy_pair_factory = rtmfp_proxy_pair_factory;
                    start_proxy_pair();
                    s_c.send_hello(e.peer);
                } else if (!debug_mode && badge != null) {
                    badge.total_proxy_pairs++;
                    badge.num_proxy_pairs++;
                }

                p_p.client = {peer: e.peer, stream: e.stream};
            });

            s_c.connect(DEFAULT_CIRRUS_ADDR, DEFAULT_CIRRUS_KEY);
        }

        private function establish_facilitator_connection():void
        {
            s_f = new FacilitatorSocket(fac_addr.host, fac_addr.port);
            s_f.addEventListener(FacilitatorSocketEvent.CONNECT_FAILED, function (e:Event):void {
                puts("Facilitator: connect failed.");
                setTimeout(establish_facilitator_connection, DEFAULT_FAC_POLL_INTERVAL);
            });

            if (proxy_mode) {
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATION_RECEIVED, function (e:FacilitatorSocketEvent):void {
                    var client_addr:Object = parse_addr_spec(e.client);
                    relay_addr = parse_addr_spec(e.relay);
                    if (client_addr == null) {
                        puts("Facilitator: got registration " + e.client);
                        proxy_pair_factory = rtmfp_proxy_pair_factory;
                        if (s_c == null || !s_c.connected) {
                            client_id = e.client;
                            establish_cirrus_connection();
                        } else {
                            start_proxy_pair();
                            s_c.send_hello(e.client);
                        }
                    } else {
                        proxy_pair_factory = tcp_proxy_pair_factory;
                        start_proxy_pair();
                        p_p.client = client_addr;
                    }
                });
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATIONS_EMPTY, function (e:Event):void {
                    puts("Facilitator: no registrations available.");
                    setTimeout(establish_facilitator_connection, DEFAULT_FAC_POLL_INTERVAL);
                });
                puts("Facilitator: getting registration.");
                s_f.get_registration();
            } else {
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATION_FAILED, function (e:Event):void {
                    puts("Facilitator: registration failed.");
                    setTimeout(establish_facilitator_connection, DEFAULT_FAC_POLL_INTERVAL);
                });
                puts("Facilitator: posting registration.");
                s_f.post_registration(s_c.id);
            }
        }

        private function start_proxy_pair():void
        {
            p_p = proxy_pair_factory();
            p_p.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("ProxyPair: connected!");
            });
            p_p.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("ProxyPair: connection closed.");
                p_p = null;
                if (proxy_mode && !debug_mode && badge != null) {
                    badge.num_proxy_pairs--;
                }
                establish_facilitator_connection();
            });
            p_p.relay = relay_addr;
        }

        private function rtmfp_proxy_pair_factory():ProxyPair
        {
            return new RTMFPProxyPair(this, s_c, s_c.local_stream_name);
        }

        // currently is the same as TCPProxyPair
        // could be interesting to see how this works
        // can't imagine it will work terribly well...
        private function sqs_proxy_pair_factory():ProxyPair
        {
            return new SQSProxyPair(this);
        }

        private function tcp_proxy_pair_factory():ProxyPair
        {
            return new TCPProxyPair(this);
        }

        /* Parse an address in the form "host:port". Returns an Object with
           keys "host" (String) and "port" (int). Returns null on error. */
        private 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.text.TextField;
import flash.text.TextFormat;

class InternetFreedomBadge {

    private var ui:swfcat;

    private var _num_proxy_pairs:uint;
    private var _total_proxy_pairs:uint;

    [Embed(source="badge.png")]
    private var BadgeImage:Class;
    private var tot_client_count_tf:TextField;
    private var tot_client_count_fmt:TextFormat;
    private var cur_client_count_tf:TextField;
    private var cur_client_count_fmt:TextFormat;

    public function InternetFreedomBadge(ui:swfcat)
    {
        this.ui = ui;
        _num_proxy_pairs = 0;
        _total_proxy_pairs = 0;

        /* Setup client counter for badge. */
        tot_client_count_fmt = new TextFormat();
        tot_client_count_fmt.color = 0xFFFFFF;
        tot_client_count_fmt.align = "center";
        tot_client_count_fmt.font = "courier-new";
        tot_client_count_fmt.bold = true;
        tot_client_count_fmt.size = 10;
        tot_client_count_tf = new TextField();
        tot_client_count_tf.width = 20;
        tot_client_count_tf.height = 17;
        tot_client_count_tf.background = false;
        tot_client_count_tf.defaultTextFormat = tot_client_count_fmt;
        tot_client_count_tf.x=47;
        tot_client_count_tf.y=0;

        cur_client_count_fmt = new TextFormat();
        cur_client_count_fmt.color = 0xFFFFFF;
        cur_client_count_fmt.align = "center";
        cur_client_count_fmt.font = "courier-new";
        cur_client_count_fmt.bold = true;
        cur_client_count_fmt.size = 10;
        cur_client_count_tf = new TextField();
        cur_client_count_tf.width = 20;
        cur_client_count_tf.height = 17;
        cur_client_count_tf.background = false;
        cur_client_count_tf.defaultTextFormat = cur_client_count_fmt;
        cur_client_count_tf.x=47;
        cur_client_count_tf.y=6;

        /* Update the client counter on badge. */
        update_client_count();
    }

    public function display():void
    {
        ui.addChild(new BadgeImage());
        /* Tried unsuccessfully to add counter to badge. */
        /* For now, need two addChilds :( */
        ui.addChild(tot_client_count_tf);
        ui.addChild(cur_client_count_tf);
    }

    public function get num_proxy_pairs():uint
    {
        return _num_proxy_pairs;
    }

    public function set num_proxy_pairs(amount:uint):void
    {
        _num_proxy_pairs = amount;
        update_client_count();
    }

    public function get total_proxy_pairs():uint
    {
        return _total_proxy_pairs;
    }

    public function set total_proxy_pairs(amount:uint):void
    {
        _total_proxy_pairs = amount;
        /* note: doesn't update, so be sure to update this
           before you update num_proxy_pairs! */
    }

    private function update_client_count():void
    {
        /* Update total client count. */
        if (String(total_proxy_pairs).length == 1)
            tot_client_count_tf.text = "0" + String(total_proxy_pairs);
        else
            tot_client_count_tf.text = String(total_proxy_pairs);

        /* Update current client count. */
        cur_client_count_tf.text = "";
        for(var i:Number = 0; i < num_proxy_pairs; i++)
            cur_client_count_tf.appendText(".");
    }
}

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.events.Event;
    import flash.utils.setTimeout;

    import FacilitatorSocket;
    import events.FacilitatorSocketEvent;

    import ProxyPair;
    import RTMFPProxyPair;
    import SQSProxyPair;
    import TCPProxyPair;

    import rtmfp.CirrusSocket;
    import rtmfp.events.CirrusSocketEvent;

    public class swfcat extends Sprite
    {
        /* Adobe's Cirrus server for RTMFP connections.
           The Cirrus key is defined at compile time by
           reading from the CIRRUS_KEY environment var. */
        private const DEFAULT_CIRRUS_ADDR:String = "rtmfp://p2p.rtmfp.net";
        private const DEFAULT_CIRRUS_KEY:String = RTMFP::CIRRUS_KEY;

        private const DEFAULT_FACILITATOR_ADDR:Object = {
            host: "tor-facilitator.bamsoftware.com",
            port: 9002
        };

        /* Default Tor client to use in case of RTMFP connection */
        private const DEFAULT_TOR_CLIENT_ADDR:Object = {
            host: "127.0.0.1",
            port: 9002
        };

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

        // Socket to Cirrus server
        private var s_c:CirrusSocket;
        // Socket to facilitator.
        private var s_f:FacilitatorSocket;
        // Handle local-remote traffic
        private var p_p:ProxyPair;

        private var client_id:String;
        private var proxy_pair_factory:Function;

        private var proxy_pairs:Array;

        private var debug_mode:Boolean;
        private var proxy_mode:Boolean;

        /* TextField for debug output. */
        private var output_text:TextField;

        /* Badge for display */
        private var badge:InternetFreedomBadge;

        private var fac_addr:Object;
        private var relay_addr:Object;

        public function swfcat()
        {
            proxy_mode = false;
            debug_mode = false;

            // Absolute positioning.
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;

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

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

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

            debug_mode = (this.loaderInfo.parameters["debug"] != null)
            proxy_mode = (this.loaderInfo.parameters["proxy"] != null);
            if (proxy_mode && !debug_mode) {
                badge = new InternetFreedomBadge(this);
                badge.display();
            } else {
                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;
                addChild(output_text);
            }

            puts("Starting: parameters loaded.");

            /* TODO: use this to have multiple proxies going at once */
            proxy_pairs = new Array();

            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
        {
            if (proxy_mode) {
                establish_facilitator_connection();
            } else {
                establish_cirrus_connection();
            }
        }

        private function establish_cirrus_connection():void
        {
            s_c = new CirrusSocket();
            s_c.addEventListener(CirrusSocketEvent.CONNECT_SUCCESS, function (e:CirrusSocketEvent):void {
                puts("Cirrus: connected with id " + s_c.id + ".");
                if (proxy_mode) {
                    start_proxy_pair();
                    s_c.send_hello(client_id);
                } else {
                    establish_facilitator_connection();
                }
            });
            s_c.addEventListener(CirrusSocketEvent.CONNECT_FAILED, function (e:CirrusSocketEvent):void {
                puts("Error: failed to connect to Cirrus.");
            });
            s_c.addEventListener(CirrusSocketEvent.CONNECT_CLOSED, function (e:CirrusSocketEvent):void {
                puts("Cirrus: closed connection.");
            });
            s_c.addEventListener(CirrusSocketEvent.HELLO_RECEIVED, function (e:CirrusSocketEvent):void {
                puts("Cirrus: received hello from peer " + e.peer);

                /* don't bother if we already have a proxy going */
                if (p_p != null && p_p.connected) {
                    return;
                }

                /* if we're in proxy mode, we should have already set
                   up a proxy pair */
                if (!proxy_mode) {
                    relay_addr = DEFAULT_TOR_CLIENT_ADDR;
                    proxy_pair_factory = rtmfp_proxy_pair_factory;
                    start_proxy_pair();
                    s_c.send_hello(e.peer);
                } else if (!debug_mode && badge != null) {
                    badge.total_proxy_pairs++;
                    badge.num_proxy_pairs++;
                }

                p_p.client = {peer: e.peer, stream: e.stream};
            });

            s_c.connect(DEFAULT_CIRRUS_ADDR, DEFAULT_CIRRUS_KEY);
        }

        private function establish_facilitator_connection():void
        {
            s_f = new FacilitatorSocket(fac_addr.host, fac_addr.port);
            s_f.addEventListener(FacilitatorSocketEvent.CONNECT_FAILED, function (e:Event):void {
                puts("Facilitator: connect failed.");
                setTimeout(establish_facilitator_connection, FACILITATOR_POLL_INTERVAL);
            });

            if (proxy_mode) {
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATION_RECEIVED, function (e:FacilitatorSocketEvent):void {
                    var client_addr:Object = parse_addr_spec(e.client);
                    relay_addr = parse_addr_spec(e.relay);
                    if (client_addr == null) {
                        puts("Facilitator: got registration " + e.client);
                        proxy_pair_factory = rtmfp_proxy_pair_factory;
                        if (s_c == null || !s_c.connected) {
                            client_id = e.client;
                            establish_cirrus_connection();
                        } else {
                            start_proxy_pair();
                            s_c.send_hello(e.client);
                        }
                    } else {
                        proxy_pair_factory = tcp_proxy_pair_factory;
                        start_proxy_pair();
                        p_p.client = client_addr;
                    }
                });
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATIONS_EMPTY, function (e:Event):void {
                    puts("Facilitator: no registrations available.");
                    setTimeout(establish_facilitator_connection, FACILITATOR_POLL_INTERVAL);
                });
                puts("Facilitator: getting registration.");
                s_f.get_registration();
            } else {
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATION_FAILED, function (e:Event):void {
                    puts("Facilitator: registration failed.");
                    setTimeout(establish_facilitator_connection, FACILITATOR_POLL_INTERVAL);
                });
                puts("Facilitator: posting registration.");
                s_f.post_registration(s_c.id);
            }
        }

        private function start_proxy_pair():void
        {
            p_p = proxy_pair_factory();
            p_p.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("ProxyPair: connected!");
            });
            p_p.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("ProxyPair: connection closed.");
                p_p = null;
                if (proxy_mode && !debug_mode && badge != null) {
                    badge.num_proxy_pairs--;
                }
                establish_facilitator_connection();
            });
            p_p.relay = relay_addr;
        }

        private function rtmfp_proxy_pair_factory():ProxyPair
        {
            return new RTMFPProxyPair(this, s_c, s_c.local_stream_name);
        }

        // currently is the same as TCPProxyPair
        // could be interesting to see how this works
        // can't imagine it will work terribly well...
        private function sqs_proxy_pair_factory():ProxyPair
        {
            return new SQSProxyPair(this);
        }

        private function tcp_proxy_pair_factory():ProxyPair
        {
            return new TCPProxyPair(this);
        }

        /* Parse an address in the form "host:port". Returns an Object with
           keys "host" (String) and "port" (int). Returns null on error. */
        private 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.text.TextField;
import flash.text.TextFormat;

class InternetFreedomBadge {

    private var ui:swfcat;

    private var _num_proxy_pairs:uint;
    private var _total_proxy_pairs:uint;

    [Embed(source="badge.png")]
    private var BadgeImage:Class;
    private var tot_client_count_tf:TextField;
    private var tot_client_count_fmt:TextFormat;
    private var cur_client_count_tf:TextField;
    private var cur_client_count_fmt:TextFormat;

    public function InternetFreedomBadge(ui:swfcat)
    {
        this.ui = ui;
        _num_proxy_pairs = 0;
        _total_proxy_pairs = 0;

        /* Setup client counter for badge. */
        tot_client_count_fmt = new TextFormat();
        tot_client_count_fmt.color = 0xFFFFFF;
        tot_client_count_fmt.align = "center";
        tot_client_count_fmt.font = "courier-new";
        tot_client_count_fmt.bold = true;
        tot_client_count_fmt.size = 10;
        tot_client_count_tf = new TextField();
        tot_client_count_tf.width = 20;
        tot_client_count_tf.height = 17;
        tot_client_count_tf.background = false;
        tot_client_count_tf.defaultTextFormat = tot_client_count_fmt;
        tot_client_count_tf.x=47;
        tot_client_count_tf.y=0;

        cur_client_count_fmt = new TextFormat();
        cur_client_count_fmt.color = 0xFFFFFF;
        cur_client_count_fmt.align = "center";
        cur_client_count_fmt.font = "courier-new";
        cur_client_count_fmt.bold = true;
        cur_client_count_fmt.size = 10;
        cur_client_count_tf = new TextField();
        cur_client_count_tf.width = 20;
        cur_client_count_tf.height = 17;
        cur_client_count_tf.background = false;
        cur_client_count_tf.defaultTextFormat = cur_client_count_fmt;
        cur_client_count_tf.x=47;
        cur_client_count_tf.y=6;

        /* Update the client counter on badge. */
        update_client_count();
    }

    public function display():void
    {
        ui.addChild(new BadgeImage());
        /* Tried unsuccessfully to add counter to badge. */
        /* For now, need two addChilds :( */
        ui.addChild(tot_client_count_tf);
        ui.addChild(cur_client_count_tf);
    }

    public function get num_proxy_pairs():uint
    {
        return _num_proxy_pairs;
    }

    public function set num_proxy_pairs(amount:uint):void
    {
        _num_proxy_pairs = amount;
        update_client_count();
    }

    public function get total_proxy_pairs():uint
    {
        return _total_proxy_pairs;
    }

    public function set total_proxy_pairs(amount:uint):void
    {
        _total_proxy_pairs = amount;
        /* note: doesn't update, so be sure to update this
           before you update num_proxy_pairs! */
    }

    private function update_client_count():void
    {
        /* Update total client count. */
        if (String(total_proxy_pairs).length == 1)
            tot_client_count_tf.text = "0" + String(total_proxy_pairs);
        else
            tot_client_count_tf.text = String(total_proxy_pairs);

        /* Update current client count. */
        cur_client_count_tf.text = "";
        for(var i:Number = 0; i < num_proxy_pairs; i++)
            cur_client_count_tf.appendText(".");
    }
}

Rename DEFAULT_FAC_POLL_INTERVAL to FACILITATOR_POLL_INTERVAL.


/**
 * ScrubberMediator
 *
 * @langversion 3.0
 * @playerversion Flash 9.0.28.0
 * @author Dan X Bacon		baconoppenheim.com
 */
 package com.kaltura.kdpfl.view.controls
{

import com.kaltura.kdpfl.model.ConfigProxy;
import com.kaltura.kdpfl.model.type.NotificationType;
import com.kaltura.vo.KalturaLiveStreamEntry;
import com.kaltura.vo.KalturaPlayableEntry;

import flash.events.Event;

import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.mediator.Mediator;

/**
 * Mediator for the scrubber component of the KDP.
 * @author Hila
 *
 */
public class ScrubberMediator extends Mediator
{
	public static const NAME:String = "scrubberMediator";

	protected static const STATE_READY:String 		= "ready";
	protected static const STATE_NOT_READY:String	= "notReady";
	protected static const STATE_PLAYING:String		= "playing";
	protected static const STATE_PAUSED:String		= "paused";
	protected static const STATE_SEEKING:String		= "seeking";

	private var _state:String = STATE_NOT_READY;
	private var _prevState:String = STATE_NOT_READY;

	private var _bytesLoaded:Number = 0;
	private var _bytesTotal:Number = 0;
    private var _flashvars:Object;
    private var _isLiveRtmp:Boolean=false;
	//private var _loadIndicatorX : Number;

	public function ScrubberMediator( viewComponent:Object=null )
	{
		super( NAME, viewComponent );
		//if ()
		scrubber.enabled = false;

		var configProxy : ConfigProxy = facade.retrieveProxy( ConfigProxy.NAME ) as ConfigProxy;
		_flashvars = configProxy.vo.flashvars;

		scrubber.addEventListener( KScrubber.EVENT_SEEK, onSeek, false, 0, true );
		scrubber.addEventListener( KScrubber.EVENT_SEEK_START, onSeekStart, false, 0, true );
		scrubber.addEventListener( KScrubber.EVENT_SEEK_END, onSeekEnd, false, 0, true );
		scrubber.addEventListener( KScrubber.EVENT_DRAG, onStartDrag, false, 0, true );
		scrubber.addEventListener( KScrubber.EVENT_DRAG_END, onEndDrag, false, 0, true );
	}
	/**
	 * Event handler for the Scubber dragging event.
	 * @param evt
	 *
	 */
	private function onStartDrag( evt:Event ):void
	{
		sendNotification( NotificationType.SCRUBBER_DRAG_START );
	}
	/**
	 * Event listener for the Scrubber_drag_stop event.
	 * @param evt
	 *
	 */
	private function onEndDrag( evt:Event ):void
	{
		sendNotification( NotificationType.SCRUBBER_DRAG_END);
	}
	private function onSeekStart( evt:Event ):void
	{
		//sendNotification( NotificationType.PLAYER_SEEK_START );
		_prevState = _state;
		_state = STATE_SEEKING;
		//sendNotification( NotificationType.DO_PAUSE );
	}
	/**
	 *  Handler for the  KScrubber.EVENT_SEEK_START event.
	 * @param evt - event of type  KScrubber.EVENT_SEEK_STAR
	 *
	 */
	private function onSeek( evt:Event ):void
	{
		var seekPosition:Number = scrubber.getPosition();
		sendNotification( NotificationType.DO_SEEK, seekPosition );
	}

	/**
 	*  Handler for the  KScrubber.EVENT_SEEK_END event.
 	* @param evt - event of type  KScrubber.EVENT_SEEK_END
	 *
 	*/
	private function onSeekEnd( evt:Event ):void
	{
		//sendNotification( NotificationType.PLAYER_SEEK_END );
		/*if( _prevState == STATE_PLAYING )
		{
			//trace( "DO_PLAY" );
			sendNotification( NotificationType.DO_PLAY );
			_prevState = STATE_PLAYING;
		}	*/
	}

	override public function handleNotification( note:INotification ):void
	{
		switch( note.getName() )
		{
			case NotificationType.KDP_READY:
				_state = STATE_READY;
			break;

			case NotificationType.PLAYER_PLAYED:
				_state = STATE_PLAYING;
			break;

			case NotificationType.PLAYER_PAUSED:
				_state = STATE_PAUSED;
			break;

			case NotificationType.PLAYER_UPDATE_PLAYHEAD:
					var position:Number = note.getBody() as Number;
					scrubber.setPosition( position );

			break;

			case NotificationType.DURATION_CHANGE:
				var duration:Number = note.getBody().newValue;
				scrubber.duration = duration;
			break;

			case NotificationType.BYTES_TOTAL_CHANGE:
				_bytesTotal = note.getBody().newValue ;
			break;

			case NotificationType.BYTES_DOWNLOADED_CHANGE:
				_bytesLoaded = note.getBody().newValue;
				var loadProgress:Number = _bytesLoaded/_bytesTotal;
				scrubber.setLoadProgress( loadProgress );
				break;
			case NotificationType.INTELLI_SEEK:
				var startLoadFrom : Number = note.getBody().intelliseekTo;
				var percentIntelli : Number = startLoadFrom/scrubber.duration;
				scrubber.loadIndicatorX = percentIntelli * scrubber.width;
				scrubber.indicatorWidth = scrubber.width - scrubber.loadIndicatorX;
				break;
			case NotificationType.CHANGE_MEDIA:
				scrubber.loadIndicatorX = 0;
				break;
			case NotificationType.ENTRY_READY:
				var entry:object = note.getBody();
				if (!entry is KalturaLiveStreamEntry)
					scrubber.duration = entry is KalturaPlayableEntry ? (entry as KalturaPlayableEntry).duration : 0;
				break;

			case NotificationType.POST_SEQUENCE_COMPLETE:
				//tell the scrubber that it should be drawn as progress complete
				//fix a bug where calling enableGUI reset the scrubber position to the start
				scrubber.progressComplete = true;
				break;
		}
	}

	override public function listNotificationInterests():Array
	{
		return [
				NotificationType.KDP_READY,
				NotificationType.PLAYER_PLAYED,
				NotificationType.PLAYER_PAUSED,
				NotificationType.PLAYER_UPDATE_PLAYHEAD,
				NotificationType.DURATION_CHANGE,
				NotificationType.BYTES_TOTAL_CHANGE,
				NotificationType.BYTES_DOWNLOADED_CHANGE,
				NotificationType.INTELLI_SEEK,
				NotificationType.CHANGE_MEDIA,
				NotificationType.ENTRY_READY,
				NotificationType.POST_SEQUENCE_COMPLETE
			   ];
	}

	public function get scrubber():KScrubber
	{
		return( viewComponent as KScrubber );
	}
}
}

/**
 * ScrubberMediator
 *
 * @langversion 3.0
 * @playerversion Flash 9.0.28.0
 * @author Dan X Bacon		baconoppenheim.com
 */
 package com.kaltura.kdpfl.view.controls
{

import com.kaltura.kdpfl.model.ConfigProxy;
import com.kaltura.kdpfl.model.type.NotificationType;
import com.kaltura.vo.KalturaLiveStreamEntry;
import com.kaltura.vo.KalturaPlayableEntry;

import flash.events.Event;

import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.mediator.Mediator;

/**
 * Mediator for the scrubber component of the KDP.
 * @author Hila
 *
 */
public class ScrubberMediator extends Mediator
{
	public static const NAME:String = "scrubberMediator";

	protected static const STATE_READY:String 		= "ready";
	protected static const STATE_NOT_READY:String	= "notReady";
	protected static const STATE_PLAYING:String		= "playing";
	protected static const STATE_PAUSED:String		= "paused";
	protected static const STATE_SEEKING:String		= "seeking";

	private var _state:String = STATE_NOT_READY;
	private var _prevState:String = STATE_NOT_READY;

	private var _bytesLoaded:Number = 0;
	private var _bytesTotal:Number = 0;
    private var _flashvars:Object;
    private var _isLiveRtmp:Boolean=false;
	//private var _loadIndicatorX : Number;

	public function ScrubberMediator( viewComponent:Object=null )
	{
		super( NAME, viewComponent );
		//if ()
		scrubber.enabled = false;

		var configProxy : ConfigProxy = facade.retrieveProxy( ConfigProxy.NAME ) as ConfigProxy;
		_flashvars = configProxy.vo.flashvars;

		scrubber.addEventListener( KScrubber.EVENT_SEEK, onSeek, false, 0, true );
		scrubber.addEventListener( KScrubber.EVENT_SEEK_START, onSeekStart, false, 0, true );
		scrubber.addEventListener( KScrubber.EVENT_SEEK_END, onSeekEnd, false, 0, true );
		scrubber.addEventListener( KScrubber.EVENT_DRAG, onStartDrag, false, 0, true );
		scrubber.addEventListener( KScrubber.EVENT_DRAG_END, onEndDrag, false, 0, true );
	}
	/**
	 * Event handler for the Scubber dragging event.
	 * @param evt
	 *
	 */
	private function onStartDrag( evt:Event ):void
	{
		sendNotification( NotificationType.SCRUBBER_DRAG_START );
	}
	/**
	 * Event listener for the Scrubber_drag_stop event.
	 * @param evt
	 *
	 */
	private function onEndDrag( evt:Event ):void
	{
		sendNotification( NotificationType.SCRUBBER_DRAG_END);
	}
	private function onSeekStart( evt:Event ):void
	{
		//sendNotification( NotificationType.PLAYER_SEEK_START );
		_prevState = _state;
		_state = STATE_SEEKING;
		//sendNotification( NotificationType.DO_PAUSE );
	}
	/**
	 *  Handler for the  KScrubber.EVENT_SEEK_START event.
	 * @param evt - event of type  KScrubber.EVENT_SEEK_STAR
	 *
	 */
	private function onSeek( evt:Event ):void
	{
		var seekPosition:Number = scrubber.getPosition();
		sendNotification( NotificationType.DO_SEEK, seekPosition );
	}

	/**
 	*  Handler for the  KScrubber.EVENT_SEEK_END event.
 	* @param evt - event of type  KScrubber.EVENT_SEEK_END
	 *
 	*/
	private function onSeekEnd( evt:Event ):void
	{
		//sendNotification( NotificationType.PLAYER_SEEK_END );
		/*if( _prevState == STATE_PLAYING )
		{
			//trace( "DO_PLAY" );
			sendNotification( NotificationType.DO_PLAY );
			_prevState = STATE_PLAYING;
		}	*/
	}

	override public function handleNotification( note:INotification ):void
	{
		switch( note.getName() )
		{
			case NotificationType.KDP_READY:
				_state = STATE_READY;
			break;

			case NotificationType.PLAYER_PLAYED:
				_state = STATE_PLAYING;
			break;

			case NotificationType.PLAYER_PAUSED:
				_state = STATE_PAUSED;
			break;

			case NotificationType.PLAYER_UPDATE_PLAYHEAD:
					var position:Number = note.getBody() as Number;
					scrubber.setPosition( position );

			break;

			case NotificationType.DURATION_CHANGE:
				var duration:Number = note.getBody().newValue;
				scrubber.duration = duration;
			break;

			case NotificationType.BYTES_TOTAL_CHANGE:
				_bytesTotal = note.getBody().newValue ;
			break;

			case NotificationType.BYTES_DOWNLOADED_CHANGE:
				_bytesLoaded = note.getBody().newValue;
				var loadProgress:Number = _bytesLoaded/_bytesTotal;
				scrubber.setLoadProgress( loadProgress );
				break;
			case NotificationType.INTELLI_SEEK:
				var startLoadFrom : Number = note.getBody().intelliseekTo;
				var percentIntelli : Number = startLoadFrom/scrubber.duration;
				scrubber.loadIndicatorX = percentIntelli * scrubber.width;
				scrubber.indicatorWidth = scrubber.width - scrubber.loadIndicatorX;
				break;
			case NotificationType.CHANGE_MEDIA:
				scrubber.loadIndicatorX = 0;
				break;
			case NotificationType.ENTRY_READY:
				var entry:object = note.getBody();
				if (!(entry is KalturaLiveStreamEntry))
					scrubber.duration = entry is KalturaPlayableEntry ? (entry as KalturaPlayableEntry).duration : 0;
				break;

			case NotificationType.POST_SEQUENCE_COMPLETE:
				//tell the scrubber that it should be drawn as progress complete
				//fix a bug where calling enableGUI reset the scrubber position to the start
				scrubber.progressComplete = true;
				break;
		}
	}

	override public function listNotificationInterests():Array
	{
		return [
				NotificationType.KDP_READY,
				NotificationType.PLAYER_PLAYED,
				NotificationType.PLAYER_PAUSED,
				NotificationType.PLAYER_UPDATE_PLAYHEAD,
				NotificationType.DURATION_CHANGE,
				NotificationType.BYTES_TOTAL_CHANGE,
				NotificationType.BYTES_DOWNLOADED_CHANGE,
				NotificationType.INTELLI_SEEK,
				NotificationType.CHANGE_MEDIA,
				NotificationType.ENTRY_READY,
				NotificationType.POST_SEQUENCE_COMPLETE
			   ];
	}

	public function get scrubber():KScrubber
	{
		return( viewComponent as KScrubber );
	}
}
}

fix scrubber initial duration

qnd: fix scrubber initial duration

git-svn-id: 3f608e5a9a704dd448217c0a64c508e7f145cfa1@92658 6b8eccd3-e8c5-4e7d-8186-e12b5326b719


package {

import flash.display.Sprite;
import flash.events.Event;
import flash.events.SampleDataEvent;
import flash.external.ExternalInterface;
import flash.media.Microphone;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.utils.getQualifiedClassName;
import flash.system.Security;

/**
 * Audio main.
 */
public class MicrophoneMain extends Sprite {

    private var ns:String;
    private var debug:Boolean;

    private var mic:Microphone;
    private var frameBuffer:Array;

    /**
     * Constructor
     */
    public function MicrophoneMain() {
        if (!ExternalInterface.available) {
            throw new Error("ExternalInterface not available");
        }

        ns = loaderInfo.parameters["namespace"] || getQualifiedClassName(this);
        debug = false;

        Security.allowDomain("*");
        Security.allowInsecureDomain("*");

        frameBuffer = [];

        /**
         * Javascript can call these
         */
        ExternalInterface.addCallback("__initialize", initialize);
        ExternalInterface.addCallback("__enable", enable);
        ExternalInterface.addCallback("__disable", disable);
        ExternalInterface.addCallback("__receiveFrames", receiveFrames);
        ExternalInterface.addCallback("__setDebug", setDebug);
        ExternalInterface.addCallback("__test", test);

        invoke("__onFlashInitialized");
    }

    private function initialize():void {
        if (Microphone.names.length == 0) {
            invoke("__onLog", "No microphones detected.");
            return;
        }
        mic = Microphone.getMicrophone();
        mic.gain = 100;
        mic.rate = 44;
    }

    private function enable():void {
        mic.addEventListener(SampleDataEvent.SAMPLE_DATA, handleSampleData);
    }

    private function disable():void {
        mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, handleSampleData);
        var frame:ByteArray = frameBuffer.shift();
        while (frame) {
            frame.clear();
            frame = frameBuffer.shift();
        }
    }

    private function encodePCM(data:ByteArray):String {
        var samples:ByteArray = new ByteArray();
        data.position = 0;
        while (data.bytesAvailable) {
            var val:int = (1 + data.readFloat()) * 27647;
            if (val == 0 || val == 92 || val == 8232 || val == 8233) {
                val += 2;
            }
            samples.writeUTFBytes(String.fromCharCode(val));
        }
        return String(samples);
    }

    private function receiveFrames():String {
        var samples:Array = [];
        var frame:ByteArray = frameBuffer.shift();
        while (frame) {
            samples.push(encodePCM(frame));
            frame.clear();
            frame = frameBuffer.shift();
        }
        return samples.join("");
    }

    private function handleSampleData(event:SampleDataEvent):void {
        var frame:ByteArray = new ByteArray();
        frame.writeBytes(event.data, 0, event.data.length);
        frameBuffer.push(frame);
        invoke("__onQueuedSamples", frameBuffer.length);
    }

    private function setDebug(val:Boolean):void {
        invoke("__onLog", "setDebug");
        ExternalInterface.marshallExceptions = val;
        debug = val;
    }

    /**
     * Conveniently invoke a function in Javascript.
     *
     * @param method String The method to call.
     */
    private function invoke(method:String, ...params):void {
        params = params || [];
        ExternalInterface.call.apply(ExternalInterface,
                [ns + "." + method].concat(params));
    }

}

}

package {

import flash.display.Sprite;
import flash.events.Event;
import flash.events.SampleDataEvent;
import flash.external.ExternalInterface;
import flash.media.Microphone;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.utils.getQualifiedClassName;
import flash.system.Security;

/**
 * Audio main.
 */
public class MicrophoneMain extends Sprite {

    private var ns:String;
    private var debug:Boolean;

    private var mic:Microphone;
    private var frameBuffer:Array;

    /**
     * Constructor
     */
    public function MicrophoneMain() {
        if (!ExternalInterface.available) {
            throw new Error("ExternalInterface not available");
        }

        ns = loaderInfo.parameters["namespace"] || getQualifiedClassName(this);
        debug = false;

        Security.allowDomain("*");
        Security.allowInsecureDomain("*");

        frameBuffer = [];

        /**
         * Javascript can call these
         */
        ExternalInterface.addCallback("__initialize", initialize);
        ExternalInterface.addCallback("__enable", enable);
        ExternalInterface.addCallback("__disable", disable);
        ExternalInterface.addCallback("__receiveFrames", receiveFrames);
        ExternalInterface.addCallback("__setDebug", setDebug);

        invoke("__onFlashInitialized");
    }

    private function initialize():void {
        if (Microphone.names.length == 0) {
            invoke("__onLog", "No microphones detected.");
            return;
        }
        mic = Microphone.getMicrophone();
        mic.gain = 100;
        mic.rate = 44;
    }

    private function enable():void {
        mic.addEventListener(SampleDataEvent.SAMPLE_DATA, handleSampleData);
    }

    private function disable():void {
        mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, handleSampleData);
        var frame:ByteArray = frameBuffer.shift();
        while (frame) {
            frame.clear();
            frame = frameBuffer.shift();
        }
    }

    private function encodePCM(data:ByteArray):String {
        var samples:ByteArray = new ByteArray();
        data.position = 0;
        while (data.bytesAvailable) {
            var val:int = (1 + data.readFloat()) * 27647;
            if (val == 0 || val == 92 || val == 8232 || val == 8233) {
                val += 2;
            }
            samples.writeUTFBytes(String.fromCharCode(val));
        }
        return String(samples);
    }

    private function receiveFrames():String {
        var samples:Array = [];
        var frame:ByteArray = frameBuffer.shift();
        while (frame) {
            samples.push(encodePCM(frame));
            frame.clear();
            frame = frameBuffer.shift();
        }
        return samples.join("");
    }

    private function handleSampleData(event:SampleDataEvent):void {
        var frame:ByteArray = new ByteArray();
        frame.writeBytes(event.data, 0, event.data.length);
        frameBuffer.push(frame);
        invoke("__onQueuedSamples", frameBuffer.length);
    }

    private function setDebug(val:Boolean):void {
        invoke("__onLog", "setDebug");
        ExternalInterface.marshallExceptions = val;
        debug = val;
    }

    /**
     * Conveniently invoke a function in Javascript.
     *
     * @param method String The method to call.
     */
    private function invoke(method:String, ...params):void {
        params = params || [];
        ExternalInterface.call.apply(ExternalInterface,
                [ns + "." + method].concat(params));
    }

}

}

Remove test external interface decl

Remove test external interface decl


package aerys.minko.scene.controller.debug
{
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.geometry.primitive.LineGeometry;
	import aerys.minko.render.geometry.stream.iterator.TriangleIterator;
	import aerys.minko.render.geometry.stream.iterator.TriangleReference;
	import aerys.minko.render.material.line.LineMaterial;
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Mesh;
	import aerys.minko.type.math.Matrix4x4;

	import flash.utils.Dictionary;

	public final class WireframeDebugController extends AbstractController
	{
		private static var _wireframeMaterial	: LineMaterial;

		private var _targetToWireframe	: Dictionary	= new Dictionary();
		private var _geometryToMesh		: Dictionary 	= new Dictionary();
		private var _visible 			: Boolean 		= true;

		public function get material() : LineMaterial
		{
			return _wireframeMaterial
				|| (_wireframeMaterial = new LineMaterial({diffuseColor : 0xffffffff, lineThickness	: 1.}));
		}

		public function set visible(value : Boolean) : void
		{
			_visible = value;

			for each (var wireframe : Mesh in _targetToWireframe)
				wireframe.visible = value;
		}

		public function get visible() : Boolean
		{
			return _visible;
		}

		public function WireframeDebugController()
		{
			super(Mesh);

			initialize();
		}

		private function initialize() : void
		{
			targetAdded.add(targetAddedHandler);
			targetRemoved.add(targetRemovedHandler);
		}

		private function targetAddedHandler(ctrl	: WireframeDebugController,
											target	: Mesh) : void
		{
			if (target.scene)
				addedHandler(target, target.parent);
			else
				target.added.add(addedHandler);
			target.removed.add(removedHandler);
		}

		private function addedHandler(target 	: Mesh,
									  ancestor	: Group) : void
		{
			if (!target.scene || target.geometry == null)
				return ;

			var triangles	: TriangleIterator = new TriangleIterator(
				target.geometry.getVertexStream(),
				target.geometry.indexStream
			);
			var linesGeom	: LineGeometry		= new LineGeometry();
			for each (var triangle : TriangleReference in triangles)
			{
				linesGeom.moveTo(triangle.v0.x, triangle.v0.y, triangle.v0.z)
					.lineTo(triangle.v1.x, triangle.v1.y, triangle.v1.z)
					.lineTo(triangle.v2.x, triangle.v2.y, triangle.v2.z)
					.lineTo(triangle.v0.x, triangle.v0.y, triangle.v0.z);
			}

			var lines		: Mesh				= new Mesh(linesGeom, this.material);
			lines.visible 	= _visible;
			_targetToWireframe[target] = lines;

			target.localToWorldTransformChanged.add(updateTransform);
			updateTransform(target, target.getLocalToWorldTransform());
			target.scene.addChild(lines);
		}

		private function updateTransform(child : ISceneNode, childLocalToWorld : Matrix4x4) : void
		{
			var lineMesh : Mesh = _targetToWireframe[child];

			if (lineMesh == null)
				child.localToWorldTransformChanged.remove(updateTransform);
			else
				lineMesh.transform.copyFrom(childLocalToWorld);
		}

		private function removeWireframes(target : Mesh) : void
		{
			if (_targetToWireframe[target])
				_targetToWireframe[target].parent = null;
			_targetToWireframe[target] = null;
		}

		private function targetRemovedHandler(ctrl		: WireframeDebugController,
											  target	: Mesh) : void
		{
			if (target.added.hasCallback(addedHandler))
				target.added.remove(addedHandler);
			target.removed.remove(removedHandler);
			if (target.localToWorldTransformChanged.hasCallback(updateTransform))
				target.localToWorldTransformChanged.remove(updateTransform);
			removeWireframes(target);
		}

		private function removedHandler(target : Mesh, ancestor : Group) : void
		{
			removeWireframes(target);
		}

		override public function clone():AbstractController
		{
			return null;
		}
	}
}

package aerys.minko.scene.controller.debug
{
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.geometry.primitive.LineGeometry;
	import aerys.minko.render.geometry.stream.iterator.TriangleIterator;
	import aerys.minko.render.geometry.stream.iterator.TriangleReference;
	import aerys.minko.render.material.line.LineMaterial;
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Mesh;
	import aerys.minko.type.math.Matrix4x4;

	import flash.utils.Dictionary;

	public final class WireframeDebugController extends AbstractController
	{
		private static var _wireframeMaterial	: LineMaterial;

		private var _targetToWireframe	: Dictionary	= new Dictionary();
		private var _geometryToMesh		: Dictionary 	= new Dictionary();
		private var _visible 			: Boolean 		= true;

		public function get material() : LineMaterial
		{
			return _wireframeMaterial
				|| (_wireframeMaterial = new LineMaterial({diffuseColor : 0x509dc7ff, lineThickness	: 1.}));
		}

		public function set visible(value : Boolean) : void
		{
			_visible = value;

			for each (var wireframe : Mesh in _targetToWireframe)
				wireframe.visible = value;
		}

		public function get visible() : Boolean
		{
			return _visible;
		}

		public function WireframeDebugController()
		{
			super(Mesh);

			initialize();
		}

		private function initialize() : void
		{
			targetAdded.add(targetAddedHandler);
			targetRemoved.add(targetRemovedHandler);
		}

		private function targetAddedHandler(ctrl	: WireframeDebugController,
											target	: Mesh) : void
		{
			if (target.scene)
				addedHandler(target, target.parent);
			else
				target.added.add(addedHandler);
			target.removed.add(removedHandler);
		}

		private function addedHandler(target 	: Mesh,
									  ancestor	: Group) : void
		{
			if (!target.scene || target.geometry == null)
				return ;

			var triangles	: TriangleIterator = new TriangleIterator(
				target.geometry.getVertexStream(),
				target.geometry.indexStream
			);
			var linesGeom	: LineGeometry		= new LineGeometry();
			for each (var triangle : TriangleReference in triangles)
			{
				linesGeom.moveTo(triangle.v0.x, triangle.v0.y, triangle.v0.z)
					.lineTo(triangle.v1.x, triangle.v1.y, triangle.v1.z)
					.lineTo(triangle.v2.x, triangle.v2.y, triangle.v2.z)
					.lineTo(triangle.v0.x, triangle.v0.y, triangle.v0.z);
			}

			var lines		: Mesh				= new Mesh(linesGeom, this.material);
			lines.visible 	= _visible;
			_targetToWireframe[target] = lines;

			target.localToWorldTransformChanged.add(updateTransform);
			updateTransform(target, target.getLocalToWorldTransform());
			target.scene.addChild(lines);
		}

		private function updateTransform(child : ISceneNode, childLocalToWorld : Matrix4x4) : void
		{
			var lineMesh : Mesh = _targetToWireframe[child];

			if (lineMesh == null)
				child.localToWorldTransformChanged.remove(updateTransform);
			else
				lineMesh.transform.copyFrom(childLocalToWorld);
		}

		private function removeWireframes(target : Mesh) : void
		{
			if (_targetToWireframe[target])
				_targetToWireframe[target].parent = null;
			_targetToWireframe[target] = null;
		}

		private function targetRemovedHandler(ctrl		: WireframeDebugController,
											  target	: Mesh) : void
		{
			if (target.added.hasCallback(addedHandler))
				target.added.remove(addedHandler);
			target.removed.remove(removedHandler);
			if (target.localToWorldTransformChanged.hasCallback(updateTransform))
				target.localToWorldTransformChanged.remove(updateTransform);
			removeWireframes(target);
		}

		private function removedHandler(target : Mesh, ancestor : Group) : void
		{
			removeWireframes(target);
		}

		override public function clone():AbstractController
		{
			return null;
		}
	}
}

Change color of wireframe debug controller