/*
 * FLINT PARTICLE SYSTEM
 * .....................
 *
 * Author: Richard Lord
 * Copyright (c) Richard Lord 2008-2010
 * http://flintparticles.org
 *
 *
 * Licence Agreement
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.flintparticles.threeD.renderers
{
	import org.flintparticles.threeD.geom.Matrix3DUtils;
	import org.flintparticles.threeD.geom.Vector3DUtils;
	import org.flintparticles.threeD.renderers.controllers.CameraController;

	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;

	/**
	 * The camera class is used by Flint's internal 3D renderers to manage the view on the 3D
	 * world that is displayed by the renderer. Each renderer has a camera property, which is
	 * its camera object.
	 */
	public class Camera
	{
		private var _projectionDistance:Number = 400;
		private var _nearDistance:Number = 10;
		private var _farDistance:Number = 2000;

		private var _transform:Matrix3D;
		private var _spaceTransform:Matrix3D;

		private var _position:Vector3D;
		private var _down:Vector3D;
		private var _target:Vector3D;

		private var _controller:CameraController;

		/*
		 * These properties have private getters because they can be
		 * invalidated when other properties are set - the getter
		 * recalculates the value if it has been invalidated
		 */
		private var _pDirection:Vector3D;
		private var _pTrack:Vector3D;
		private var _pFront:Vector3D;

		/**
		 * The constructor creates a Camera object. Usually, users don't need to create camera
		 * objects, but will use the camera objects that are properties of Flint's renderers.
		 */
		public function Camera()
		{
			_position = new Vector3D( 0, 0, 0, 1 );
			_target = new Vector3D( 0, 0, 0, 1 );
			_down = new Vector3D( 0, 1, 0 );
			_pDirection = new Vector3D( 0, 0, 1 );
		}

		/**
		 * The point that the camera looks at. Setting this will
		 * invalidate any setting for the camera direction - the direction
		 * will be recalculated based on the position and the target.
		 *
		 * @see #direction
		 */
		public function get target():Vector3D
		{
			return _target.clone();
		}
		public function set target( value:Vector3D ):void
		{
			_target = Vector3DUtils.clonePoint( value );
			_pDirection = null;
			_pTrack = null;
			_spaceTransform = null;
		}

		/**
		 * The location of the camera.
		 */
		public function get position():Vector3D
		{
			return _position.clone();
		}
		public function set position( value:Vector3D ):void
		{
			_position = Vector3DUtils.clonePoint( value );
			_spaceTransform = null;
			if( _target )
			{
				_pDirection = null;
				_pTrack = null;
			}
		}

		/**
		 * The direction the camera is pointing. Setting this will invalidate any
		 * setting for the target, since the camera now points in this direction
		 * rather than pointing towards the target.
		 *
		 * @see #target
		 */
		public function get direction():Vector3D
		{
			return _direction.clone();
		}
		public function set direction( value:Vector3D ):void
		{
			_pDirection = Vector3DUtils.cloneUnit( value );
			_target = null;
			_spaceTransform = null;
			_pTrack = null;
		}

		/**
		 * The down direction for the camera. If this is not perpendicular to the direction the camera points,
		 * the camera is tilted down or up from this up direction to point in the direction or at the target.
		 */
		public function get down():Vector3D
		{
			return _down.clone();
		}
		public function set down( value:Vector3D ):void
		{
			_down = Vector3DUtils.cloneUnit( value );
			_spaceTransform = null;
			_pTrack = null;
		}

		/**
		 * The transform matrix that converts positions in world space to positions in camera space.
		 * The projection transform is part of this transform - so vectors need only to have their
		 * project method called to get their position in 2D camera space.
		 */
		public function get transform():Matrix3D
		{
			if( !_spaceTransform || !_transform )
			{
				_transform = spaceTransform.clone();
				var projectionTransform:Matrix3D = new Matrix3D( Vector.<Number>( [
					_projectionDistance, 0, 0, 0,
					0, _projectionDistance, 0, 0,
					0, 0, 1, 1,
					0, 0, 0, 0
				] ) );
				_transform.append( projectionTransform );
			}
			return _transform;
		}

		/**
		 * The transform matrix that converts positions in world space to positions in camera space.
		 * The projection transform is not part of this transform.
		 */
		public function get spaceTransform():Matrix3D
		{
			if( !_spaceTransform )
			{
				var realDown:Vector3D = _direction.crossProduct( _track );
				_spaceTransform = Matrix3DUtils.newBasisTransform(
					Vector3DUtils.cloneUnit( _track ),
					Vector3DUtils.cloneUnit( realDown ),
					Vector3DUtils.cloneUnit( _direction ) );
				_spaceTransform.prependTranslation( -_position.x, -_position.y, -_position.z );
			}
			return _spaceTransform;
		}

		/**
		 * Dolly or Track the camera in/out in the direction it's facing.
		 *
		 * @param distance The distance to move the camera. Positive values track in and
		 * negative values track out.
		 */
		public function dolly( distance:Number ):void
		{
			var dollyVector:Vector3D = _direction.clone();
			dollyVector.scaleBy( distance );
			_position.incrementBy( dollyVector );
			_spaceTransform = null;
		}

		/**
		 * Raise or lower the camera.
		 *
		 * @param distance The distance to lift the camera. Positive values raise the camera
		 * and negative values lower the camera.
		 */
		public function lift( distance:Number ):void
		{
			var liftVector:Vector3D = _down.clone();
			liftVector.scaleBy( -distance );
			_position.incrementBy( liftVector );
			_spaceTransform = null;
		}

		/**
		 * Dolly or Track the camera left/right.
		 *
		 * @param distance The distance to move the camera. Positive values move the camera to the
		 * right, negative values move it to the left.
		 */
		public function track( distance:Number ):void
		{
			var trackVector:Vector3D = _track.clone();
			trackVector.scaleBy( distance );
			_position.incrementBy( trackVector );
			_spaceTransform = null;
		}

		/**
		 * Tilt the camera up or down.
		 *
		 * @param The angle (in radians) to tilt the camera. Positive values tilt up,
		 * negative values tilt down.
		 */
		public function tilt( angle:Number ):void
		{
			var m:Matrix3D = Matrix3DUtils.newRotate( angle, _track );
			_pDirection = m.transformVector( _direction );
			_spaceTransform = null;
			_target = null;
		}

		/**
		 * Pan the camera left or right.
		 *
		 * @param The angle (in radians) to pan the camera. Positive values pan right,
		 * negative values pan left.
		 */
		public function pan( angle:Number ):void
		{
			var m:Matrix3D = Matrix3DUtils.newRotate( angle, _down );
			_pDirection = m.transformVector( _direction );
			_pTrack = null;
			_spaceTransform = null;
			_target = null;
		}

		/**
		 * Roll the camera clockwise or counter-clockwise.
		 *
		 * @param The angle (in radians) to roll the camera. Positive values roll clockwise,
		 * negative values roll counter-clockwise.
		 */
		public function roll( angle:Number ):void
		{
			var m:Matrix3D = Matrix3DUtils.newRotate( angle, _front );
			_down = m.transformVector( _down );
			_pTrack = null;
			_spaceTransform = null;
		}

		/**
		 * Orbit the camera around the target.
		 *
		 * @param The angle (in radians) to orbit the camera. Positive values orbit to the right,
		 * negative values orbit to the left.
		 */
		public function orbit( angle:Number ):void
		{
			if( !_target )
			{
				throw new Error( "Attempting to orbit camera when no target is set" );
			}
			var m:Matrix3D = Matrix3DUtils.newRotate( -angle, down );
			_position = m.transformVector( _position );
			_pDirection = null;
			_pTrack = null;
			_spaceTransform = null;
		}

		/**
		 * The distance to the camera's near plane
		 * - particles closer than this are not rendered.
		 *
		 * The default value is 10.
		 */
		public function get nearPlaneDistance():Number
		{
			return _nearDistance;
		}
		public function set nearPlaneDistance( value:Number ):void
		{
			_nearDistance = value;
		}

		/**
		 * The distance to the camera's far plane
		 * - particles farther away than this are not rendered.
		 *
		 * The default value is 2000.
		 */
		public function get farPlaneDistance():Number
		{
			return _farDistance;
		}
		public function set farPlaneDistance( value:Number ):void
		{
			_farDistance = value;
		}

		/**
		 * The distance to the camera's projection distance. Particles this
		 * distance from the camera are rendered at their normal size. Perspective
		 * will cause closer particles to appear larger than normal and more
		 * distant particles to appear smaller than normal.
		 *
		 * The default value is 400.
		 */
		public function get projectionDistance():Number
		{
			return _projectionDistance;
		}
		public function set projectionDistance( value:Number ):void
		{
			_projectionDistance = value;
			_transform = null;
		}

		/*
		 * private getters for properties that can be invaliadated.
		 */
		private function get _track():Vector3D
		{
			if( _pTrack == null )
			{
				_pTrack = _down.crossProduct( _direction );
			}
			_pFront == null;
			return _pTrack;
		}

		private function get _front():Vector3D
		{
			if( _pFront == null )
			{
				_pFront = _track.crossProduct( _down );
			}
			return _pFront;
		}

		private function get _direction():Vector3D
		{
			if( _pDirection == null && _target )
			{
				_pDirection = _target.subtract( _position );
				_pDirection.normalize();
			}
			return _pDirection;
		}

		public function get controller():CameraController
		{
			return _controller;
		}
		public function set controller( value:CameraController ):void
		{
			if( _controller )
			{
				_controller.camera = null;
			}
			_controller = value;
			_controller.camera = this;
		}
	}
}

/*
 * FLINT PARTICLE SYSTEM
 * .....................
 *
 * Author: Richard Lord
 * Copyright (c) Richard Lord 2008-2010
 * http://flintparticles.org
 *
 *
 * Licence Agreement
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.flintparticles.threeD.renderers
{
	import org.flintparticles.threeD.geom.Matrix3DUtils;
	import org.flintparticles.threeD.geom.Vector3DUtils;
	import org.flintparticles.threeD.renderers.controllers.CameraController;

	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;

	/**
	 * The camera class is used by Flint's internal 3D renderers to manage the view on the 3D
	 * world that is displayed by the renderer. Each renderer has a camera property, which is
	 * its camera object.
	 */
	public class Camera
	{
		private var _projectionDistance:Number = 400;
		private var _nearDistance:Number = 10;
		private var _farDistance:Number = 2000;

		private var _transform:Matrix3D;
		private var _spaceTransform:Matrix3D;

		private var _position:Vector3D;
		private var _down:Vector3D;
		private var _target:Vector3D;

		private var _controller:CameraController;

		/*
		 * These properties have private getters because they can be
		 * invalidated when other properties are set - the getter
		 * recalculates the value if it has been invalidated
		 */
		private var _pDirection:Vector3D;
		private var _pTrack:Vector3D;
		private var _pFront:Vector3D;

		private var _realDown:Vector3D;
		private var _projectionTransform:Matrix3D;

		/**
		 * The constructor creates a Camera object. Usually, users don't need to create camera
		 * objects, but will use the camera objects that are properties of Flint's renderers.
		 */
		public function Camera()
		{
			_position = new Vector3D( 0, 0, 0, 1 );
			_target = new Vector3D( 0, 0, 0, 1 );
			_down = new Vector3D( 0, 1, 0 );
			_pDirection = new Vector3D( 0, 0, 1 );
			_realDown = new Vector3D();
			_projectionTransform = new Matrix3D;
		}

		/**
		 * The point that the camera looks at. Setting this will
		 * invalidate any setting for the camera direction - the direction
		 * will be recalculated based on the position and the target.
		 *
		 * @see #direction
		 */
		public function get target():Vector3D
		{
			return _target.clone();
		}
		public function set target( value:Vector3D ):void
		{
			_target = Vector3DUtils.clonePoint( value );
			_pDirection = null;
			_pTrack = null;
			_spaceTransform = null;
		}

		/**
		 * The location of the camera.
		 */
		public function get position():Vector3D
		{
			return _position.clone();
		}
		public function set position( value:Vector3D ):void
		{
			_position = Vector3DUtils.clonePoint( value );
			_spaceTransform = null;
			if( _target )
			{
				_pDirection = null;
				_pTrack = null;
			}
		}

		/**
		 * The direction the camera is pointing. Setting this will invalidate any
		 * setting for the target, since the camera now points in this direction
		 * rather than pointing towards the target.
		 *
		 * @see #target
		 */
		public function get direction():Vector3D
		{
			return _direction.clone();
		}
		public function set direction( value:Vector3D ):void
		{
			_pDirection = Vector3DUtils.cloneUnit( value );
			_target = null;
			_spaceTransform = null;
			_pTrack = null;
		}

		/**
		 * The down direction for the camera. If this is not perpendicular to the direction the camera points,
		 * the camera is tilted down or up from this up direction to point in the direction or at the target.
		 */
		public function get down():Vector3D
		{
			return _down.clone();
		}
		public function set down( value:Vector3D ):void
		{
			_down = Vector3DUtils.cloneUnit( value );
			_spaceTransform = null;
			_pTrack = null;
		}

		/**
		 * The transform matrix that converts positions in world space to positions in camera space.
		 * The projection transform is part of this transform - so vectors need only to have their
		 * project method called to get their position in 2D camera space.
		 */
		public function get transform():Matrix3D
		{
			if( !_spaceTransform || !_transform )
			{
				_transform = spaceTransform.clone();
				_projectionTransform.rawData = Vector.<Number>( [
					_projectionDistance, 0, 0, 0,
					0, _projectionDistance, 0, 0,
					0, 0, 1, 1,
					0, 0, 0, 0
				] );
				_transform.append( _projectionTransform );
			}
			return _transform;
		}

		/**
		 * The transform matrix that converts positions in world space to positions in camera space.
		 * The projection transform is not part of this transform.
		 */
		public function get spaceTransform():Matrix3D
		{
			if( !_spaceTransform )
			{
				// This is more efficient than
				//_realDown = _direction.crossProduct( _track );
				_realDown.x = _direction.y * _track.z - _direction.z * _track.y;
				_realDown.y = _direction.z * _track.x - _direction.x * _track.z;
				_realDown.z = _direction.x * _track.y - _direction.y * _track.x;

				_spaceTransform = Matrix3DUtils.newBasisTransform(
					Vector3DUtils.cloneUnit( _track ),
					Vector3DUtils.cloneUnit( _realDown ),
					Vector3DUtils.cloneUnit( _direction ) );
				_spaceTransform.prependTranslation( -_position.x, -_position.y, -_position.z );
			}
			return _spaceTransform;
		}

		/**
		 * Dolly or Track the camera in/out in the direction it's facing.
		 *
		 * @param distance The distance to move the camera. Positive values track in and
		 * negative values track out.
		 */
		public function dolly( distance:Number ):void
		{
			var dollyVector:Vector3D = _direction.clone();
			dollyVector.scaleBy( distance );
			_position.incrementBy( dollyVector );
			_spaceTransform = null;
		}

		/**
		 * Raise or lower the camera.
		 *
		 * @param distance The distance to lift the camera. Positive values raise the camera
		 * and negative values lower the camera.
		 */
		public function lift( distance:Number ):void
		{
			var liftVector:Vector3D = _down.clone();
			liftVector.scaleBy( -distance );
			_position.incrementBy( liftVector );
			_spaceTransform = null;
		}

		/**
		 * Dolly or Track the camera left/right.
		 *
		 * @param distance The distance to move the camera. Positive values move the camera to the
		 * right, negative values move it to the left.
		 */
		public function track( distance:Number ):void
		{
			var trackVector:Vector3D = _track.clone();
			trackVector.scaleBy( distance );
			_position.incrementBy( trackVector );
			_spaceTransform = null;
		}

		/**
		 * Tilt the camera up or down.
		 *
		 * @param The angle (in radians) to tilt the camera. Positive values tilt up,
		 * negative values tilt down.
		 */
		public function tilt( angle:Number ):void
		{
			var m:Matrix3D = Matrix3DUtils.newRotate( angle, _track );
			_pDirection = m.transformVector( _direction );
			_spaceTransform = null;
			_target = null;
		}

		/**
		 * Pan the camera left or right.
		 *
		 * @param The angle (in radians) to pan the camera. Positive values pan right,
		 * negative values pan left.
		 */
		public function pan( angle:Number ):void
		{
			var m:Matrix3D = Matrix3DUtils.newRotate( angle, _down );
			_pDirection = m.transformVector( _direction );
			_pTrack = null;
			_spaceTransform = null;
			_target = null;
		}

		/**
		 * Roll the camera clockwise or counter-clockwise.
		 *
		 * @param The angle (in radians) to roll the camera. Positive values roll clockwise,
		 * negative values roll counter-clockwise.
		 */
		public function roll( angle:Number ):void
		{
			var m:Matrix3D = Matrix3DUtils.newRotate( angle, _front );
			_down = m.transformVector( _down );
			_pTrack = null;
			_spaceTransform = null;
		}

		/**
		 * Orbit the camera around the target.
		 *
		 * @param The angle (in radians) to orbit the camera. Positive values orbit to the right,
		 * negative values orbit to the left.
		 */
		public function orbit( angle:Number ):void
		{
			if( !_target )
			{
				throw new Error( "Attempting to orbit camera when no target is set" );
			}
			var m:Matrix3D = Matrix3DUtils.newRotate( -angle, down );
			_position = m.transformVector( _position );
			_pDirection = null;
			_pTrack = null;
			_spaceTransform = null;
		}

		/**
		 * The distance to the camera's near plane
		 * - particles closer than this are not rendered.
		 *
		 * The default value is 10.
		 */
		public function get nearPlaneDistance():Number
		{
			return _nearDistance;
		}
		public function set nearPlaneDistance( value:Number ):void
		{
			_nearDistance = value;
		}

		/**
		 * The distance to the camera's far plane
		 * - particles farther away than this are not rendered.
		 *
		 * The default value is 2000.
		 */
		public function get farPlaneDistance():Number
		{
			return _farDistance;
		}
		public function set farPlaneDistance( value:Number ):void
		{
			_farDistance = value;
		}

		/**
		 * The distance to the camera's projection distance. Particles this
		 * distance from the camera are rendered at their normal size. Perspective
		 * will cause closer particles to appear larger than normal and more
		 * distant particles to appear smaller than normal.
		 *
		 * The default value is 400.
		 */
		public function get projectionDistance():Number
		{
			return _projectionDistance;
		}
		public function set projectionDistance( value:Number ):void
		{
			_projectionDistance = value;
			_transform = null;
		}

		/*
		 * private getters for properties that can be invaliadated.
		 */
		private function get _track():Vector3D
		{
			if( _pTrack == null )
			{
				_pTrack = _down.crossProduct( _direction );
			}
			_pFront == null;
			return _pTrack;
		}

		private function get _front():Vector3D
		{
			if( _pFront == null )
			{
				_pFront = _track.crossProduct( _down );
			}
			return _pFront;
		}

		private function get _direction():Vector3D
		{
			if( _pDirection == null && _target )
			{
				_pDirection = _target.subtract( _position );
				_pDirection.normalize();
			}
			return _pDirection;
		}

		public function get controller():CameraController
		{
			return _controller;
		}
		public function set controller( value:CameraController ):void
		{
			if( _controller )
			{
				_controller.camera = null;
			}
			_controller = value;
			_controller.camera = this;
		}
	}
}

Add speed improvements to Camera class

Add speed improvements to Camera class


package
{
    import flash.errors.IllegalOperationError;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.Socket;
    import flash.utils.ByteArray;
    import flash.utils.clearTimeout;
    import flash.utils.setTimeout;

    import swfcat;

    public class ProxyPair extends EventDispatcher
    {
        private var ui:swfcat;

        protected var client_addr:Object;

        /* Not defined here: subclasses should define their own
         * protected var client_socket:Object;
         */

        private var c2r_schedule:Array;

        private var relay_addr:Object;
        private var relay_socket:Socket;
        private var r2c_schedule:Array;

        // Bytes per second. Set to undefined to disable limit.
        private const RATE_LIMIT:Number = undefined; //10000;
        // Seconds.
        private const RATE_LIMIT_HISrelayY:Number = 5.0;

        private var rate_limit:RateLimit;

        // Callback id.
        private var flush_id:uint;

        public function ProxyPair(self:ProxyPair, ui:swfcat)
        {
            if (self != this) {
                //only a subclass can pass a valid reference to self
            	throw new IllegalOperationError("ProxyPair cannot be instantiated directly.");
            }

            this.ui = ui;
            this.c2r_schedule = new Array();
            this.r2c_schedule = new Array();

            if (RATE_LIMIT)
                rate_limit = new BucketRateLimit(RATE_LIMIT * RATE_LIMIT_HISrelayY, RATE_LIMIT_HISrelayY);
            else
                rate_limit = new RateUnlimit();

            setup_relay_socket();

            /* client_socket setup should be taken */
            /* care of in the subclass constructor */
        }

        public function close():void
        {
            if (relay_socket != null && relay_socket.connected) {
                relay_socket.close();
            }

            /* subclasses should override to close */
            /* their client_socket according to impl. */
        }

        public function get connected():Boolean
        {
            return (relay_socket != null && relay_socket.connected);

            /* subclasses should override to check */
            /* connectivity of their client_socket. */
        }

        public function set client(client_addr:Object):void
        {
            /* subclasses should override to */
            /* connect the client_socket here */
        }

        public function set relay(relay_addr:Object):void
        {
            this.relay_addr = relay_addr;
            log("Relay: connecting to " + relay_addr.host + ":" + relay_addr.port + ".");
            relay_socket.connect(relay_addr.host, relay_addr.port);
        }

        protected function transfer_bytes(src:Object, dst:Object, num_bytes:uint):void
        {
            /* No-op: must be overridden by subclasses */
        }

        private function setup_relay_socket():void
        {
            relay_socket = new Socket();
            relay_socket.addEventListener(Event.CONNECT, function (e:Event):void {
                log("Relay: connected to " + relay_addr.host + ":" + relay_addr.port + ".");
                if (connected) {
                    dispatchEvent(new Event(Event.CONNECT));
                }
            });
            relay_socket.addEventListener(Event.CLOSE, function (e:Event):void {
                log("Relay: closed connection.");
                close();
            });
            relay_socket.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                log("Relay: I/O error: " + e.text + ".");
                close();
            });
            relay_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                log("Relay: security error: " + e.text + ".");
                close();
            });
            relay_socket.addEventListener(ProgressEvent.SOCKET_DATA, relay_to_client);
        }

        protected function client_to_relay(e:ProgressEvent):void
        {
            c2r_schedule.push(e.bytesLoaded);
            flush();
        }

        private function relay_to_client(e:ProgressEvent):void
        {
            r2c_schedule.push(e.bytesLoaded);
            flush();
        }

        /* Send as much data as the rate limit currently allows. */
        private function flush():void
        {
            if (flush_id)
                clearTimeout(flush_id);
            flush_id = undefined;

            if (!connected)
                /* Can't do anything until connected. */
                return;

            while (!rate_limit.is_limited() && (c2r_schedule.length > 0 || r2c_schedule.length > 0)) {
                var num_bytes:uint;

                if (c2r_schedule.length > 0) {
                    num_bytes = c2r_schedule.shift();
                    transfer_bytes(null, relay_socket, num_bytes);
                    rate_limit.update(num_bytes);
                }

                if (r2c_schedule.length > 0) {
                    num_bytes = r2c_schedule.shift();
                    transfer_bytes(relay_socket, null, num_bytes);
                    rate_limit.update(num_bytes);
                }
            }

            /* Call again when safe, if necessary. */
            if (c2r_schedule.length > 0 || r2c_schedule.length > 0)
                flush_id = setTimeout(flush, rate_limit.when() * 1000);
        }

        /* Helper function to write output to the
         * swfcat console. Set as protected for
         * subclasses */
        protected function log(s:String):void
        {
            ui.puts(s);
        }
    }
}

import flash.utils.getTimer;

class RateLimit
{
    public function RateLimit()
    {
    }

    public function update(n:Number):Boolean
    {
        return true;
    }

    public function when():Number
    {
        return 0.0;
    }

    public function is_limited():Boolean
    {
        return false;
    }
}

class RateUnlimit extends RateLimit
{
    public function RateUnlimit()
    {
    }

    public override function update(n:Number):Boolean
    {
        return true;
    }

    public override function when():Number
    {
        return 0.0;
    }

    public override function is_limited():Boolean
    {
        return false;
    }
}

class BucketRateLimit extends RateLimit
{
    private var amount:Number;
    private var capacity:Number;
    private var time:Number;
    private var last_update:uint;

    public function BucketRateLimit(capacity:Number, time:Number)
    {
        this.amount = 0.0;
        /* capacity / time is the rate we are aiming for. */
        this.capacity = capacity;
        this.time = time;
        this.last_update = getTimer();
    }

    private function age():void
    {
        var now:uint;
        var delta:Number;

        now = getTimer();
        delta = (now - last_update) / 1000.0;
        last_update = now;

        amount -= delta * capacity / time;
        if (amount < 0.0)
            amount = 0.0;
    }

    public override function update(n:Number):Boolean
    {
        age();
        amount += n;

        return amount <= capacity;
    }

    public override function when():Number
    {
        age();
        return (amount - capacity) / (capacity / time);
    }

    public override function is_limited():Boolean
    {
        age();
        return amount > capacity;
    }
}

package
{
    import flash.errors.IllegalOperationError;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.Socket;
    import flash.utils.ByteArray;
    import flash.utils.clearTimeout;
    import flash.utils.setTimeout;

    import swfcat;

    public class ProxyPair extends EventDispatcher
    {
        private var ui:swfcat;

        protected var client_addr:Object;

        /* Not defined here: subclasses should define their own
         * protected var client_socket:Object;
         */

        private var c2r_schedule:Array;

        private var relay_addr:Object;
        private var relay_socket:Socket;
        private var r2c_schedule:Array;

        // Bytes per second. Set to undefined to disable limit.
        private const RATE_LIMIT:Number = undefined; //10000;
        // Seconds.
        private const RATE_LIMIT_HISTORY:Number = 5.0;

        private var rate_limit:RateLimit;

        // Callback id.
        private var flush_id:uint;

        public function ProxyPair(self:ProxyPair, ui:swfcat)
        {
            if (self != this) {
                //only a subclass can pass a valid reference to self
            	throw new IllegalOperationError("ProxyPair cannot be instantiated directly.");
            }

            this.ui = ui;
            this.c2r_schedule = new Array();
            this.r2c_schedule = new Array();

            if (RATE_LIMIT)
                rate_limit = new BucketRateLimit(RATE_LIMIT * RATE_LIMIT_HISTORY, RATE_LIMIT_HISTORY);
            else
                rate_limit = new RateUnlimit();

            setup_relay_socket();

            /* client_socket setup should be taken */
            /* care of in the subclass constructor */
        }

        public function close():void
        {
            if (relay_socket != null && relay_socket.connected) {
                relay_socket.close();
            }

            /* subclasses should override to close */
            /* their client_socket according to impl. */
        }

        public function get connected():Boolean
        {
            return (relay_socket != null && relay_socket.connected);

            /* subclasses should override to check */
            /* connectivity of their client_socket. */
        }

        public function set client(client_addr:Object):void
        {
            /* subclasses should override to */
            /* connect the client_socket here */
        }

        public function set relay(relay_addr:Object):void
        {
            this.relay_addr = relay_addr;
            log("Relay: connecting to " + relay_addr.host + ":" + relay_addr.port + ".");
            relay_socket.connect(relay_addr.host, relay_addr.port);
        }

        protected function transfer_bytes(src:Object, dst:Object, num_bytes:uint):void
        {
            /* No-op: must be overridden by subclasses */
        }

        private function setup_relay_socket():void
        {
            relay_socket = new Socket();
            relay_socket.addEventListener(Event.CONNECT, function (e:Event):void {
                log("Relay: connected to " + relay_addr.host + ":" + relay_addr.port + ".");
                if (connected) {
                    dispatchEvent(new Event(Event.CONNECT));
                }
            });
            relay_socket.addEventListener(Event.CLOSE, function (e:Event):void {
                log("Relay: closed connection.");
                close();
            });
            relay_socket.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                log("Relay: I/O error: " + e.text + ".");
                close();
            });
            relay_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                log("Relay: security error: " + e.text + ".");
                close();
            });
            relay_socket.addEventListener(ProgressEvent.SOCKET_DATA, relay_to_client);
        }

        protected function client_to_relay(e:ProgressEvent):void
        {
            c2r_schedule.push(e.bytesLoaded);
            flush();
        }

        private function relay_to_client(e:ProgressEvent):void
        {
            r2c_schedule.push(e.bytesLoaded);
            flush();
        }

        /* Send as much data as the rate limit currently allows. */
        private function flush():void
        {
            if (flush_id)
                clearTimeout(flush_id);
            flush_id = undefined;

            if (!connected)
                /* Can't do anything until connected. */
                return;

            while (!rate_limit.is_limited() && (c2r_schedule.length > 0 || r2c_schedule.length > 0)) {
                var num_bytes:uint;

                if (c2r_schedule.length > 0) {
                    num_bytes = c2r_schedule.shift();
                    transfer_bytes(null, relay_socket, num_bytes);
                    rate_limit.update(num_bytes);
                }

                if (r2c_schedule.length > 0) {
                    num_bytes = r2c_schedule.shift();
                    transfer_bytes(relay_socket, null, num_bytes);
                    rate_limit.update(num_bytes);
                }
            }

            /* Call again when safe, if necessary. */
            if (c2r_schedule.length > 0 || r2c_schedule.length > 0)
                flush_id = setTimeout(flush, rate_limit.when() * 1000);
        }

        /* Helper function to write output to the
         * swfcat console. Set as protected for
         * subclasses */
        protected function log(s:String):void
        {
            ui.puts(s);
        }
    }
}

import flash.utils.getTimer;

class RateLimit
{
    public function RateLimit()
    {
    }

    public function update(n:Number):Boolean
    {
        return true;
    }

    public function when():Number
    {
        return 0.0;
    }

    public function is_limited():Boolean
    {
        return false;
    }
}

class RateUnlimit extends RateLimit
{
    public function RateUnlimit()
    {
    }

    public override function update(n:Number):Boolean
    {
        return true;
    }

    public override function when():Number
    {
        return 0.0;
    }

    public override function is_limited():Boolean
    {
        return false;
    }
}

class BucketRateLimit extends RateLimit
{
    private var amount:Number;
    private var capacity:Number;
    private var time:Number;
    private var last_update:uint;

    public function BucketRateLimit(capacity:Number, time:Number)
    {
        this.amount = 0.0;
        /* capacity / time is the rate we are aiming for. */
        this.capacity = capacity;
        this.time = time;
        this.last_update = getTimer();
    }

    private function age():void
    {
        var now:uint;
        var delta:Number;

        now = getTimer();
        delta = (now - last_update) / 1000.0;
        last_update = now;

        amount -= delta * capacity / time;
        if (amount < 0.0)
            amount = 0.0;
    }

    public override function update(n:Number):Boolean
    {
        age();
        amount += n;

        return amount <= capacity;
    }

    public override function when():Number
    {
        age();
        return (amount - capacity) / (capacity / time);
    }

    public override function is_limited():Boolean
    {
        age();
        return amount > capacity;
    }
}

Fix a global search-and-replace error.

Fix a global search-and-replace error.

RATE_LIMIT_HISrelayY -> RATE_LIMIT_HISTORY


package flash.display3D
{
	import flash.events.EventDispatcher;
	import flash.geom.Matrix3D;
	import flash.utils.ByteArray;
	import flash.display3D.textures.*;
	import flash.geom.Rectangle;
	import flash.display.BitmapData;
	/**
	 * ...
	 * @author lizhi http://matrix3d.github.io/
	 */
	public final class Context3D extends EventDispatcher
	{
		public var canvas:HTMLCanvasElement;
		public var gl:WebGLRenderingContext;
		private var currentProgram:Program3D;
		private var currentTextures:Object = { };
		private var currentVBufs:Object = { };
		public function Context3D()
		{
			super();
		}

		public static function get supportsVideoTexture():Boolean  { return false }

		public function get driverInfo():String  { return null }

		public function dispose(recreate:Boolean = true):void
		{
		}

		public function get enableErrorChecking():Boolean  { return false }

		public function set enableErrorChecking(toggle:Boolean):void
		{
		}

		public function configureBackBuffer(width:int, height:int, antiAlias:int, enableDepthAndStencil:Boolean = true, wantsBestResolution:Boolean = false):void
		{
			canvas.width = width;
			canvas.height = height;
			canvas.style.width = width + "px";
			canvas.style.height = height + "px";
			gl.viewport(0, 0, width, height);
			if (enableDepthAndStencil)
			{
				gl.enable(gl.DEPTH_TEST);
				gl.enable(gl.STENCIL_TEST);
			}
			else
			{
				gl.disable(gl.DEPTH_TEST);
				gl.disable(gl.STENCIL_TEST);
			}
		}

		public function clear(red:Number = 0, green:Number = 0, blue:Number = 0, alpha:Number = 1, depth:Number = 1, stencil:uint = 0, mask:uint = 4294967295):void
		{
			SpriteFlexjs.dirtyGraphics = true;
			gl.clearColor(red, green, blue, alpha);
			gl.clearDepth(depth);
			gl.clearStencil(stencil);
			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
		}

		public function drawTriangles(indexBuffer:IndexBuffer3D, firstIndex:int = 0, numTriangles:int = -1):void
		{
			gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buff);
			gl.drawElements(gl.TRIANGLES, numTriangles < 0 ? indexBuffer.count : numTriangles * 3, gl.UNSIGNED_SHORT, firstIndex * 2);
		}

		public function present():void
		{
			SpriteFlexjs.dirtyGraphics = true;
		}

		public function setProgram(program:Program3D):void
		{
			if(currentProgram!=program){
				currentProgram = program;
				gl.useProgram(program.program);
			}
		}

		public function setProgramConstantsFromVector(programType:String, firstRegister:int, data:Vector.<Number>, numRegisters:int = -1):void
		{
			//var num:int =gl.getProgramParameter(currentProgram.program, gl.ACTIVE_UNIFORMS);
			//var count:int = 0;
			//for (var i:int = 0; i < num;i++ ) {
			//	var au:WebGLActiveInfo = gl(currentProgram.program, i);
			//}
			setProgramConstantsFromVectorGL(getUniformLocationName(programType, firstRegister), data, numRegisters);
		}

		public function setProgramConstantsFromMatrix(programType:String, firstRegister:int, matrix:Matrix3D, transposedMatrix:Boolean = false):void
		{
			setProgramConstantsFromMatrixGL(getUniformLocationName(programType, firstRegister), matrix, transposedMatrix);
		}

		public function setProgramConstantsFromByteArray(programType:String, firstRegister:int, numRegisters:int, data:ByteArray, byteArrayOffset:uint):void
		{
		}
		public function setProgramConstantsFromVectorGL(name:String, data:Vector.<Number>, numRegisters:int = -1):void
		{
			gl.uniform4fv(getUniformLocation(name), data as Object);
		}

		/**
		 * @flexjsignorecoercion Object
		 */
		public function setProgramConstantsFromMatrixGL(name:String, matrix:Matrix3D, transposedMatrix:Boolean = false):void
		{
			if (transposedMatrix) {
				matrix.transpose();
			}
			gl.uniformMatrix4fv(getUniformLocation(name), false, matrix.rawData as Object);
			if (transposedMatrix) {
				matrix.transpose();
			}
		}

		public function setProgramConstantsFromByteArrayGL(name:String , numRegisters:int, data:ByteArray, byteArrayOffset:uint):void
		{
		}

		private function getUniformLocationName(programType:String, register:int):String
		{
			return (Context3DProgramType.VERTEX === programType) ? ("vc" + register) : ("fc" + register);
		}
		private function getUniformLocation(name:String):WebGLUniformLocation
		{
			return currentProgram.getUniformLocation(name);
		}

		public function setVertexBufferAt(index:int, buffer:VertexBuffer3D, bufferOffset:int = 0, format:String = "float4"):void
		{
			setVertexBufferAtGL("va" + index, buffer, bufferOffset, format);
		}

		public function setVertexBufferAtGL(name:String, buffer:VertexBuffer3D, bufferOffset:int = 0, format:String = "float4"):void {
			if (currentVBufs[name] != buffer) {
				currentVBufs[name] = buffer;
				var loc:Number= currentProgram.getAttribLocation(name);
				gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buff);
				var type:int = gl.FLOAT;
				var size:int = 0;
				var normalized:Boolean = false;
				switch (format)
				{
				case Context3DVertexBufferFormat.FLOAT_1:
					size = 1;
					break;
				case Context3DVertexBufferFormat.FLOAT_2:
					size = 2;
					break;
				case Context3DVertexBufferFormat.FLOAT_3:
					size = 3;
					break;
				case Context3DVertexBufferFormat.FLOAT_4:
					size = 4;
					break;
				case Context3DVertexBufferFormat.BYTES_4:
					size = 4;
					type = gl.UNSIGNED_BYTE;
					normalized = true;
					break;
				}
				gl.vertexAttribPointer(loc, size, type, normalized, buffer.data32PerVertex * 4, bufferOffset*4);
			}
		}

		public function setBlendFactors(sourceFactor:String, destinationFactor:String):void
		{
			gl.enable(gl.BLEND);
			gl.blendEquation(gl.FUNC_ADD);
			gl.blendFunc(Context3DBlendFactor.getGLVal(gl,sourceFactor), Context3DBlendFactor.getGLVal(gl,destinationFactor));
		}

		public function setColorMask(red:Boolean, green:Boolean, blue:Boolean, alpha:Boolean):void
		{
			gl.colorMask(red, green, blue, alpha);
		}

		public function setDepthTest(depthMask:Boolean, passCompareMode:String):void
		{
			gl.depthFunc(Context3DCompareMode.getGLVal(gl,passCompareMode));
			gl.depthMask(depthMask);
		}

		public function setTextureAt(sampler:int, texture:TextureBase):void
		{
			if (texture == null)
			{
				this.setTextureInternal(sampler, null);
			}
			else if (texture is Texture)
			{
				this.setTextureInternal(sampler, texture as Texture);
			}
			else if (texture is CubeTexture)
			{
				this.setCubeTextureInternal(sampler, texture as CubeTexture);
			}
			else if (texture is RectangleTexture)
			{
				this.setRectangleTextureInternal(sampler, texture as RectangleTexture);
			}
			else if (texture is VideoTexture)
			{
				this.setVideoTextureInternal(sampler, texture as VideoTexture);
			}
		}

		public function setRenderToTexture(texture:TextureBase, enableDepthAndStencil:Boolean = false, antiAlias:int = 0, surfaceSelector:int = 0, colorOutputIndex:int = 0):void
		{
			var targetType:uint = 0;
			if (texture is Texture)
			{
				targetType = 1;
			}
			else if (texture is CubeTexture)
			{
				targetType = 2;
			}
			else if (texture is RectangleTexture)
			{
				targetType = 3;
			}
			else if (texture != null)
			{
				throw "texture argument not derived from TextureBase (can be Texture, CubeTexture, or if supported, RectangleTexture)";
			}
			this.setRenderToTextureInternal(texture, targetType, enableDepthAndStencil, antiAlias, surfaceSelector, colorOutputIndex);
		}

		public function setRenderToBackBuffer():void
		{
		}

		private function setRenderToTextureInternal(param1:TextureBase, param2:int, param3:Boolean, param4:int, param5:int, param6:int):void
		{
		}

		public function setCulling(triangleFaceToCull:String):void
		{
			if (triangleFaceToCull === Context3DTriangleFace.NONE)
			{
				gl.disable(gl.CULL_FACE);
			}
			else
			{
				gl.enable(gl.CULL_FACE);
				gl.cullFace(Context3DTriangleFace.getGLVal(gl,triangleFaceToCull));
			}
		}

		public function setStencilActions(triangleFace:String = "frontAndBack", compareMode:String = "always", actionOnBothPass:String = "keep", actionOnDepthFail:String = "keep", actionOnDepthPassStencilFail:String = "keep"):void
		{
		}

		public function setStencilReferenceValue(referenceValue:uint, readMask:uint = 255, writeMask:uint = 255):void
		{
		}

		public function setScissorRectangle(rectangle:Rectangle):void
		{
			if (rectangle == null)
			{
				gl.disable(gl.SCISSOR_TEST);
			}
			else
			{
				gl.enable(gl.SCISSOR_TEST);
				gl.scissor(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
			}
		}

		public function createVertexBuffer(numVertices:int, data32PerVertex:int, bufferUsage:String = "staticDraw"):VertexBuffer3D
		{
			var buffer:VertexBuffer3D = new VertexBuffer3D;
			buffer.buff = gl.createBuffer();
			buffer.data32PerVertex = data32PerVertex;
			buffer.gl = gl;
			return buffer;
		}

		public function createIndexBuffer(numIndices:int, bufferUsage:String = "staticDraw"):IndexBuffer3D
		{
			var buffer:IndexBuffer3D = new IndexBuffer3D;
			buffer.buff = gl.createBuffer();
			buffer.gl = gl;
			buffer.count = numIndices;
			return buffer;
		}

		public function createTexture(width:int, height:int, format:String, optimizeForRenderToTexture:Boolean, streamingLevels:int = 0):Texture
		{
			var t:Texture = new Texture;
			t.gl = gl;
			t.texture = gl.createTexture();
			return t;
		}

		public function createCubeTexture(size:int, format:String, optimizeForRenderToTexture:Boolean, streamingLevels:int = 0):CubeTexture  { return null }

		public function createRectangleTexture(width:int, height:int, format:String, optimizeForRenderToTexture:Boolean):RectangleTexture  { return null }

		public function createProgram():Program3D
		{
			var p:Program3D = new Program3D;
			p.gl = gl;
			p.program = gl.createProgram();
			return p;
		}

		public function drawToBitmapData(destination:BitmapData):void
		{
		}

		public function setSamplerStateAt(sampler:int, wrap:String, filter:String, mipfilter:String):void
		{
		}

		public function get profile():String  { return null }

		private function setTextureInternal(sampler:int, texture:Texture):void
		{
			setTextureAtGL("fs" + sampler, sampler, texture);
		}

		public function setTextureAtGL(name:String, sampler:int, texture:Texture):void {
			if (currentTextures[name] != texture) {
				currentTextures[name] = texture;
				if (texture)
				{
					gl.activeTexture(WebGLRenderingContext["TEXTURE"+sampler]);
					gl.bindTexture(gl.TEXTURE_2D, texture.texture);
					gl.uniform1i(currentProgram.getUniformLocation(name), sampler);
				}
			}
		}

		private function setCubeTextureInternal(param1:int, param2:CubeTexture):void
		{
		}

		private function setRectangleTextureInternal(param1:int, param2:RectangleTexture):void
		{
		}

		private function setVideoTextureInternal(param1:int, param2:VideoTexture):void
		{
		}

		public function get backBufferWidth():int  { return 0 }

		public function get backBufferHeight():int  { return 0 }

		public function get maxBackBufferWidth():int  { return 0 }

		public function set maxBackBufferWidth(width:int):void
		{
		}

		public function get maxBackBufferHeight():int  { return 0 }

		public function set maxBackBufferHeight(height:int):void
		{
		}

		public function createVideoTexture():VideoTexture  { return null }
	}
}

package flash.display3D
{
	import flash.events.EventDispatcher;
	import flash.geom.Matrix3D;
	import flash.utils.ByteArray;
	import flash.display3D.textures.*;
	import flash.geom.Rectangle;
	import flash.display.BitmapData;
	/**
	 * ...
	 * @author lizhi http://matrix3d.github.io/
	 */
	public final class Context3D extends EventDispatcher
	{
		public var canvas:HTMLCanvasElement;
		public var gl:WebGLRenderingContext;
		private var currentProgram:Program3D;
		private var currentTextures:Object = { };
		private var currentVBufs:Object = { };
		public function Context3D()
		{
			super();
		}

		public static function get supportsVideoTexture():Boolean  { return false }

		public function get driverInfo():String  { return null }

		public function dispose(recreate:Boolean = true):void
		{
		}

		public function get enableErrorChecking():Boolean  { return false }

		public function set enableErrorChecking(toggle:Boolean):void
		{
		}

		public function configureBackBuffer(width:int, height:int, antiAlias:int, enableDepthAndStencil:Boolean = true, wantsBestResolution:Boolean = false):void
		{
			canvas.width = width;
			canvas.height = height;
			canvas.style.width = width + "px";
			canvas.style.height = height + "px";
			gl.viewport(0, 0, width, height);
			if (enableDepthAndStencil)
			{
				gl.enable(gl.DEPTH_TEST);
				gl.enable(gl.STENCIL_TEST);
			}
			else
			{
				gl.disable(gl.DEPTH_TEST);
				gl.disable(gl.STENCIL_TEST);
			}
		}

		public function clear(red:Number = 0, green:Number = 0, blue:Number = 0, alpha:Number = 1, depth:Number = 1, stencil:uint = 0, mask:uint = 4294967295):void
		{
			SpriteFlexjs.dirtyGraphics = true;
			gl.clearColor(red, green, blue, alpha);
			gl.clearDepth(depth);
			gl.clearStencil(stencil);
			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
		}

		public function drawTriangles(indexBuffer:IndexBuffer3D, firstIndex:int = 0, numTriangles:int = -1):void
		{
			gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buff);
			gl.drawElements(gl.TRIANGLES, numTriangles < 0 ? indexBuffer.count : numTriangles * 3, gl.UNSIGNED_SHORT, firstIndex * 2);
		}

		public function present():void
		{
			SpriteFlexjs.dirtyGraphics = true;
		}

		public function setProgram(program:Program3D):void
		{
			if(currentProgram!=program){
				currentProgram = program;
				gl.useProgram(program.program);
			}
		}

		public function setProgramConstantsFromVector(programType:String, firstRegister:int, data:Vector.<Number>, numRegisters:int = -1):void
		{
			//var num:int =gl.getProgramParameter(currentProgram.program, gl.ACTIVE_UNIFORMS);
			//var count:int = 0;
			//for (var i:int = 0; i < num;i++ ) {
			//	var au:WebGLActiveInfo = gl(currentProgram.program, i);
			//}
			setProgramConstantsFromVectorGL(getUniformLocationName(programType, firstRegister), data, numRegisters);
		}

		public function setProgramConstantsFromMatrix(programType:String, firstRegister:int, matrix:Matrix3D, transposedMatrix:Boolean = false):void
		{
			setProgramConstantsFromMatrixGL(getUniformLocationName(programType, firstRegister), matrix, transposedMatrix);
		}

		public function setProgramConstantsFromByteArray(programType:String, firstRegister:int, numRegisters:int, data:ByteArray, byteArrayOffset:uint):void
		{
		}
		public function setProgramConstantsFromVectorGL(name:String, data:Vector.<Number>, numRegisters:int = -1):void
		{
			gl.uniform4fv(getUniformLocation(name), data as Object);
		}

		/**
		 * @flexjsignorecoercion Object
		 */
		public function setProgramConstantsFromMatrixGL(name:String, matrix:Matrix3D, transposedMatrix:Boolean = false):void
		{
			if (transposedMatrix) {
				matrix.transpose();
			}
			gl.uniformMatrix4fv(getUniformLocation(name), false, matrix.rawData as Object);
			if (transposedMatrix) {
				matrix.transpose();
			}
		}

		public function setProgramConstantsFromByteArrayGL(name:String , numRegisters:int, data:ByteArray, byteArrayOffset:uint):void
		{
		}

		private function getUniformLocationName(programType:String, register:int):String
		{
			return (Context3DProgramType.VERTEX === programType) ? ("vc" + register) : ("fc" + register);
		}
		private function getUniformLocation(name:String):WebGLUniformLocation
		{
			return currentProgram.getUniformLocation(name);
		}

		public function setVertexBufferAt(index:int, buffer:VertexBuffer3D, bufferOffset:int = 0, format:String = "float4"):void
		{
			setVertexBufferAtGL("va" + index, buffer, bufferOffset, format);
		}

		public function setVertexBufferAtGL(name:String, buffer:VertexBuffer3D, bufferOffset:int = 0, format:String = "float4"):void {
			if (currentVBufs[name] != buffer) {
				currentVBufs[name] = buffer;
				var loc:Number= currentProgram.getAttribLocation(name);
				gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buff);
				var type:int = gl.FLOAT;
				var size:int = 0;
				var mul:int=4;
				var normalized:Boolean = false;
				switch (format)
				{
				case Context3DVertexBufferFormat.FLOAT_1:
					size = 1;
					break;
				case Context3DVertexBufferFormat.FLOAT_2:
					size = 2;
					break;
				case Context3DVertexBufferFormat.FLOAT_3:
					size = 3;
					break;
				case Context3DVertexBufferFormat.FLOAT_4:
					size = 4;
					break;
				case Context3DVertexBufferFormat.BYTES_4:
					size = 4;
					type = gl.UNSIGNED_BYTE;
					normalized = true;
					mul = 1;
					break;
				}
				gl.vertexAttribPointer(loc, size, type, normalized, buffer.data32PerVertex * mul, bufferOffset*mul);
			}
		}

		public function setBlendFactors(sourceFactor:String, destinationFactor:String):void
		{
			gl.enable(gl.BLEND);
			gl.blendEquation(gl.FUNC_ADD);
			gl.blendFunc(Context3DBlendFactor.getGLVal(gl,sourceFactor), Context3DBlendFactor.getGLVal(gl,destinationFactor));
		}

		public function setColorMask(red:Boolean, green:Boolean, blue:Boolean, alpha:Boolean):void
		{
			gl.colorMask(red, green, blue, alpha);
		}

		public function setDepthTest(depthMask:Boolean, passCompareMode:String):void
		{
			gl.depthFunc(Context3DCompareMode.getGLVal(gl,passCompareMode));
			gl.depthMask(depthMask);
		}

		public function setTextureAt(sampler:int, texture:TextureBase):void
		{
			if (texture == null)
			{
				this.setTextureInternal(sampler, null);
			}
			else if (texture is Texture)
			{
				this.setTextureInternal(sampler, texture as Texture);
			}
			else if (texture is CubeTexture)
			{
				this.setCubeTextureInternal(sampler, texture as CubeTexture);
			}
			else if (texture is RectangleTexture)
			{
				this.setRectangleTextureInternal(sampler, texture as RectangleTexture);
			}
			else if (texture is VideoTexture)
			{
				this.setVideoTextureInternal(sampler, texture as VideoTexture);
			}
		}

		public function setRenderToTexture(texture:TextureBase, enableDepthAndStencil:Boolean = false, antiAlias:int = 0, surfaceSelector:int = 0, colorOutputIndex:int = 0):void
		{
			var targetType:uint = 0;
			if (texture is Texture)
			{
				targetType = 1;
			}
			else if (texture is CubeTexture)
			{
				targetType = 2;
			}
			else if (texture is RectangleTexture)
			{
				targetType = 3;
			}
			else if (texture != null)
			{
				throw "texture argument not derived from TextureBase (can be Texture, CubeTexture, or if supported, RectangleTexture)";
			}
			this.setRenderToTextureInternal(texture, targetType, enableDepthAndStencil, antiAlias, surfaceSelector, colorOutputIndex);
		}

		public function setRenderToBackBuffer():void
		{
		}

		private function setRenderToTextureInternal(param1:TextureBase, param2:int, param3:Boolean, param4:int, param5:int, param6:int):void
		{
		}

		public function setCulling(triangleFaceToCull:String):void
		{
			if (triangleFaceToCull === Context3DTriangleFace.NONE)
			{
				gl.disable(gl.CULL_FACE);
			}
			else
			{
				gl.enable(gl.CULL_FACE);
				gl.cullFace(Context3DTriangleFace.getGLVal(gl,triangleFaceToCull));
			}
		}

		public function setStencilActions(triangleFace:String = "frontAndBack", compareMode:String = "always", actionOnBothPass:String = "keep", actionOnDepthFail:String = "keep", actionOnDepthPassStencilFail:String = "keep"):void
		{
		}

		public function setStencilReferenceValue(referenceValue:uint, readMask:uint = 255, writeMask:uint = 255):void
		{
		}

		public function setScissorRectangle(rectangle:Rectangle):void
		{
			if (rectangle == null)
			{
				gl.disable(gl.SCISSOR_TEST);
			}
			else
			{
				gl.enable(gl.SCISSOR_TEST);
				gl.scissor(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
			}
		}

		public function createVertexBuffer(numVertices:int, data32PerVertex:int, bufferUsage:String = "staticDraw"):VertexBuffer3D
		{
			var buffer:VertexBuffer3D = new VertexBuffer3D;
			buffer.buff = gl.createBuffer();
			buffer.data32PerVertex = data32PerVertex;
			buffer.gl = gl;
			return buffer;
		}

		public function createIndexBuffer(numIndices:int, bufferUsage:String = "staticDraw"):IndexBuffer3D
		{
			var buffer:IndexBuffer3D = new IndexBuffer3D;
			buffer.buff = gl.createBuffer();
			buffer.gl = gl;
			buffer.count = numIndices;
			return buffer;
		}

		public function createTexture(width:int, height:int, format:String, optimizeForRenderToTexture:Boolean, streamingLevels:int = 0):Texture
		{
			var t:Texture = new Texture;
			t.gl = gl;
			t.texture = gl.createTexture();
			return t;
		}

		public function createCubeTexture(size:int, format:String, optimizeForRenderToTexture:Boolean, streamingLevels:int = 0):CubeTexture  { return null }

		public function createRectangleTexture(width:int, height:int, format:String, optimizeForRenderToTexture:Boolean):RectangleTexture  { return null }

		public function createProgram():Program3D
		{
			var p:Program3D = new Program3D;
			p.gl = gl;
			p.program = gl.createProgram();
			return p;
		}

		public function drawToBitmapData(destination:BitmapData):void
		{
		}

		public function setSamplerStateAt(sampler:int, wrap:String, filter:String, mipfilter:String):void
		{
		}

		public function get profile():String  { return null }

		private function setTextureInternal(sampler:int, texture:Texture):void
		{
			setTextureAtGL("fs" + sampler, sampler, texture);
		}

		public function setTextureAtGL(name:String, sampler:int, texture:Texture):void {
			if (currentTextures[name] != texture) {
				currentTextures[name] = texture;
				if (texture)
				{
					gl.activeTexture(WebGLRenderingContext["TEXTURE"+sampler]);
					gl.bindTexture(gl.TEXTURE_2D, texture.texture);
					gl.uniform1i(currentProgram.getUniformLocation(name), sampler);
				}
			}
		}

		private function setCubeTextureInternal(param1:int, param2:CubeTexture):void
		{
		}

		private function setRectangleTextureInternal(param1:int, param2:RectangleTexture):void
		{
		}

		private function setVideoTextureInternal(param1:int, param2:VideoTexture):void
		{
		}

		public function get backBufferWidth():int  { return 0 }

		public function get backBufferHeight():int  { return 0 }

		public function get maxBackBufferWidth():int  { return 0 }

		public function set maxBackBufferWidth(width:int):void
		{
		}

		public function get maxBackBufferHeight():int  { return 0 }

		public function set maxBackBufferHeight(height:int):void
		{
		}

		public function createVideoTexture():VideoTexture  { return null }
	}
}

Update Context3D.as

Update Context3D.as


//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.util {

import flash.display.Sprite;

import flash.events.Event;

/**
 * A simple mechanism for queueing functions to be called on the next frame.
 * Similar to UIComponent's callLater, only flex-free.
 */
public class MethodQueue
{
    /**
     * Call the specified method at the entry to the next frame.
     */
    public static function callLater (fn :Function, args :Array = null) :void
    {
        _methodQueue.push([fn, args]);
        if (!_d.hasEventListener(Event.ENTER_FRAME)) {
            _d.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
        }
    }

    /**
     * Handle a frame event: call any queued functions.
     */
    protected static function handleEnterFrame (event :Event) :void
    {
        // swap out the working set
        var methods :Array = _methodQueue;
        _methodQueue = [];

        // safely call each function
        for each (var arr :Array in methods) {
            var fn :Function = (arr[0] as Function);
            var args :Array = (arr[1] as Array);
            try {
                fn.apply(null, args);

            } catch (e :Error) {
                Log.getLog(MethodQueue).warning("Error calling deferred method " +
                    "[e=" + e + ", fn=" + fn + ", args=" + args + "].");
            }
        }

        // If no new functions were added while we were calling the current set,
        // then remove the listener.
        if (_methodQueue.length == 0) {
            _d.removeEventListener(Event.ENTER_FRAME, handleEnterFrame);
        }
    }

    /** A display object on which we listen for ENTER_FRAME. */
    protected static var _d :Sprite = new Sprite();

    /** The currently queued functions. */
    protected static var _methodQueue :Array = [];
}
}

//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.util {

import flash.events.TimerEvent;

import flash.utils.Timer;

/**
 * A simple mechanism for queueing functions to be called on the next frame.
 * Similar to UIComponent's callLater, only flex-free.
 */
public class MethodQueue
{
    /**
     * Call the specified method at the entry to the next frame.
     */
    public static function callLater (fn :Function, args :Array = null) :void
    {
        _methodQueue.push([fn, args]);
        _t.start(); // starts the timer if it's not already running
    }

    /**
     * Handle a timer event: call any queued functions.
     */
    protected static function handleTimer (event :TimerEvent) :void
    {
        // swap out the working set
        var methods :Array = _methodQueue;
        _methodQueue = [];

        // safely call each function
        for each (var arr :Array in methods) {
            var fn :Function = (arr[0] as Function);
            var args :Array = (arr[1] as Array);
            try {
                fn.apply(null, args);

            } catch (e :Error) {
                Log.getLog(MethodQueue).warning("Error calling deferred method " +
                    "[e=" + e + ", fn=" + fn + ", args=" + args + "].");
            }
        }

        // If no new functions were added while we were calling the current set,
        // then stop firing
        if (_methodQueue.length == 0) {
            _t.stop();
        }
    }

    /** A timer that will fire as quickly as possible. */
    protected static var _t :Timer = new Timer(1);

    /** The currently queued functions. */
    protected static var _methodQueue :Array = [];

    // a bit of static initialization
    _t.addEventListener(TimerEvent.TIMER, handleTimer);
}
}

Use a Timer instead of ENTER_FRAME.
This is a potentially more dangerous change, but nothing
seems awry. Eyes peeled!

git-svn-id: a1a4b28b82a3276cc491891159dd9963a0a72fae@5017 542714f4-19e9-0310-aa3c-eee0fc999fb1


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

import com.esri.ags.portal.Portal;
import com.esri.builder.supportClasses.URLUtil;

import flash.events.Event;
import flash.events.EventDispatcher;

import mx.utils.StringUtil;

public class PortalModel extends EventDispatcher
{
    //--------------------------------------------------------------------------
    //
    //  Constants
    //
    //--------------------------------------------------------------------------

    public static const DEFAULT_PORTAL_URL:String = "https://www.arcgis.com/";

    private static var instance:PortalModel;

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

    public static function getInstance():PortalModel
    {
        if (!instance)
        {
            instance = new PortalModel(new SingletonEnforcer());
        }

        return instance;
    }

    public function cleanUpPortalURL(url:String):String
    {
        var cleanURL:String = url;
        cleanURL = StringUtil.trim(cleanURL);
        cleanURL = replacePreviousDefaultPortalURL(cleanURL);
        cleanURL = cleanURL.replace(/\/sharing\/content\/items\/?$/i, '');
        cleanURL = URLUtil.ensureTrailingForwardSlash(cleanURL);
        cleanURL = URLUtil.encode(cleanURL);
        return cleanURL;
    }

    private function replacePreviousDefaultPortalURL(url:String):String
    {
        const previousDefaultPortalURL:String = "http://www.arcgis.com/";
        return url.replace(previousDefaultPortalURL, DEFAULT_PORTAL_URL);
    }

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    public function PortalModel(singletonEnforcer:SingletonEnforcer)
    {
        if (!singletonEnforcer)
        {
            throw new Error("Class should not be instantiated - use getInstance()");
        }
    }

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

    //----------------------------------
    //  portal
    //----------------------------------

    [Bindable]
    public var portal:Portal = new Portal();

    //----------------------------------
    //  portalURL
    //----------------------------------

    /**
     * The ArcGIS Portal URL
     */
    private var _portalURL:String;

    [Bindable("userDefinedPortalURLChanged")]
    public function get portalURL():String
    {
        return _portalURL;
    }

    public function set portalURL(value:String):void
    {
        if (_portalURL != value)
        {
            _portalURL = cleanUpPortalURL(value);
            dispatchEvent(new Event("userDefinedPortalURLChanged"));
        }
    }
}
}

class SingletonEnforcer
{
}

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

import com.esri.ags.components.IdentityManager;
import com.esri.ags.components.supportClasses.Credential;
import com.esri.ags.portal.Portal;
import com.esri.builder.supportClasses.PortalUtil;
import com.esri.builder.supportClasses.URLUtil;

import flash.events.Event;
import flash.events.EventDispatcher;

import mx.utils.StringUtil;

public class PortalModel extends EventDispatcher
{
    //--------------------------------------------------------------------------
    //
    //  Constants
    //
    //--------------------------------------------------------------------------

    public static const DEFAULT_PORTAL_URL:String = "https://www.arcgis.com/";

    private static var instance:PortalModel;

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

    public static function getInstance():PortalModel
    {
        if (!instance)
        {
            instance = new PortalModel(new SingletonEnforcer());
        }

        return instance;
    }

    public function cleanUpPortalURL(url:String):String
    {
        var cleanURL:String = url;
        cleanURL = StringUtil.trim(cleanURL);
        cleanURL = replacePreviousDefaultPortalURL(cleanURL);
        cleanURL = cleanURL.replace(/\/sharing\/content\/items\/?$/i, '');
        cleanURL = URLUtil.ensureTrailingForwardSlash(cleanURL);
        cleanURL = URLUtil.encode(cleanURL);
        return cleanURL;
    }

    private function replacePreviousDefaultPortalURL(url:String):String
    {
        const previousDefaultPortalURL:String = "http://www.arcgis.com/";
        return url.replace(previousDefaultPortalURL, DEFAULT_PORTAL_URL);
    }

    public function canSignOut():Boolean
    {
        var credential:Credential = IdentityManager.instance.findCredential(PortalUtil.toPortalSharingURL(portal.url));
        return portal.signedIn && (credential != null);
    }

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    public function PortalModel(singletonEnforcer:SingletonEnforcer)
    {
        if (!singletonEnforcer)
        {
            throw new Error("Class should not be instantiated - use getInstance()");
        }
    }

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

    //----------------------------------
    //  portal
    //----------------------------------

    [Bindable]
    public var portal:Portal = new Portal();

    //----------------------------------
    //  portalURL
    //----------------------------------

    /**
     * The ArcGIS Portal URL
     */
    private var _portalURL:String;

    [Bindable("userDefinedPortalURLChanged")]
    public function get portalURL():String
    {
        return _portalURL;
    }

    public function set portalURL(value:String):void
    {
        if (_portalURL != value)
        {
            _portalURL = cleanUpPortalURL(value);
            dispatchEvent(new Event("userDefinedPortalURLChanged"));
        }
    }
}
}

class SingletonEnforcer
{
}

Add logic to PortalModel to determine if the user can sign out.