/*
 * FLINT PARTICLE SYSTEM
 * .....................
 *
 * Author: Richard Lord
 * Copyright (c) Richard Lord 2008-2011
 * 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.twoD.zones
{
	import org.flintparticles.twoD.particles.Particle2D;

	import flash.geom.Point;

	/**
	 * The DiscSectorZone zone defines a section of a Disc zone. The disc
	 * on which it's based have a hole in the middle, like a doughnut.
	 */

	public class DiscSectorZone implements Zone2D
	{
		private var _center:Point;
		private var _innerRadius:Number;
		private var _outerRadius:Number;
		private var _innerSq:Number;
		private var _outerSq:Number;
		private var _minAngle:Number;
		private var _maxAngle:Number;
		private var _minAllowed:Number;
		private var _minNormal:Point;
		private var _maxNormal:Point;

		private static var TWOPI:Number = Math.PI * 2;

		/**
		 * The constructor defines a DiscSectorZone zone.
		 *
		 * @param center The centre of the disc.
		 * @param outerRadius The radius of the outer edge of the disc.
		 * @param innerRadius If set, this defines the radius of the inner
		 * edge of the disc. Points closer to the center than this inner radius
		 * are excluded from the zone. If this parameter is not set then all
		 * points inside the outer radius are included in the zone.
		 * @param minAngle The minimum angle, in radians, for points to be included in the zone.
		 * An angle of zero is horizontal and to the right. Positive angles are in a clockwise
		 * direction (towards the graphical y axis). Angles are converted to a value between 0
		 * and two times PI.
		 * @param maxAngle The maximum angle, in radians, for points to be included in the zone.
		 * An angle of zero is horizontal and to the right. Positive angles are in a clockwise
		 * direction (towards the graphical y axis). Angles are converted to a value between 0
		 * and two times PI.
		 */
		public function DiscSectorZone( center:Point = null, outerRadius:Number = 0, innerRadius:Number = 0, minAngle:Number = 0, maxAngle:Number = 0 )
		{
			if( outerRadius < innerRadius )
			{
				throw new Error( "The outerRadius (" + outerRadius + ") can't be smaller than the innerRadius (" + innerRadius + ") in your DiscSectorZone. N.B. the outerRadius is the second argument in the constructor and the innerRadius is the third argument." );
			}
			_center = center ? center.clone() : new Point( 0, 0 );
			_innerRadius = innerRadius;
			_outerRadius = outerRadius;
			_innerSq = _innerRadius * _innerRadius;
			_outerSq = _outerRadius * _outerRadius;
			_minAngle = minAngle;
			_maxAngle = maxAngle;
			if( ! isNaN( _maxAngle ) )
			{
				while ( _maxAngle > TWOPI )
				{
					_maxAngle -= TWOPI;
				}
				while ( _maxAngle < 0 )
				{
					_maxAngle += TWOPI;
				}
				_minAllowed = _maxAngle - TWOPI;
				if( ! isNaN( _minAngle ) )
				{
					if ( minAngle == maxAngle )
					{
						_minAngle = _maxAngle;
					}
					else
					{
						_minAngle = clamp( _minAngle );
					}
				}
				calculateNormals();
			}
		}

		private function clamp( angle:Number ):Number
		{
			if( ! isNaN( _maxAngle ) )
			{
				while ( angle > _maxAngle )
				{
					angle -= TWOPI;
				}
				while ( angle < _minAllowed )
				{
					angle += TWOPI;
				}
			}
			return angle;
		}

		private function calculateNormals():void
		{
			if( ! isNaN( _minAngle ) )
			{
				_minNormal = new Point( Math.sin( _minAngle ), - Math.cos( _minAngle ) );
				_minNormal.normalize( 1 );
			}
			if( ! isNaN( _maxAngle ) )
			{
				_maxNormal = new Point( - Math.sin( _maxAngle ), Math.cos( _maxAngle ) );
				_maxNormal.normalize( 1 );
			}
		}

		/**
		 * The centre of the disc.
		 */
		public function get center() : Point
		{
			return _center;
		}

		public function set center( value : Point ) : void
		{
			_center = value;
		}

		/**
		 * The x coordinate of the point that is the center of the disc.
		 */
		public function get centerX() : Number
		{
			return _center.x;
		}

		public function set centerX( value : Number ) : void
		{
			_center.x = value;
		}

		/**
		 * The y coordinate of the point that is the center of the disc.
		 */
		public function get centerY() : Number
		{
			return _center.y;
		}

		public function set centerY( value : Number ) : void
		{
			_center.y = value;
		}

		/**
		 * The radius of the inner edge of the disc.
		 */
		public function get innerRadius() : Number
		{
			return _innerRadius;
		}

		public function set innerRadius( value : Number ) : void
		{
			_innerRadius = value;
			_innerSq = _innerRadius * _innerRadius;
		}

		/**
		 * The radius of the outer edge of the disc.
		 */
		public function get outerRadius() : Number
		{
			return _outerRadius;
		}

		public function set outerRadius( value : Number ) : void
		{
			_outerRadius = value;
			_outerSq = _outerRadius * _outerRadius;
		}

		/**
		 * The minimum angle, in radians, for points to be included in the zone.
		 * An angle of zero is horizontal and to the right. Positive angles are in a clockwise
		 * direction (towards the graphical y axis). Angles are converted to a value between 0
		 * and two times PI.
		 */
		public function get minAngle() : Number
		{
			return _minAngle;
		}

		public function set minAngle( value : Number ) : void
		{
			_minAngle = clamp( value );
			calculateNormals();
		}

		/**
		 * The maximum angle, in radians, for points to be included in the zone.
		 * An angle of zero is horizontal and to the right. Positive angles are in a clockwise
		 * direction (towards the graphical y axis). Angles are converted to a value between 0
		 * and two times PI.
		 */
		public function get maxAngle() : Number
		{
			return _maxAngle;
		}

		public function set maxAngle( value : Number ) : void
		{
			_maxAngle = value;
			while ( _maxAngle > TWOPI )
			{
				_maxAngle -= TWOPI;
			}
			while ( _maxAngle < 0 )
			{
				_maxAngle += TWOPI;
			}
			_minAllowed = _maxAngle - TWOPI;
			_minAngle = clamp( _minAngle );
			calculateNormals();
		}

		/**
		 * The contains method determines whether a point is inside the zone.
		 * This method is used by the initializers and actions that
		 * use the zone. Usually, it need not be called directly by the user.
		 *
		 * @param x The x coordinate of the location to test for.
		 * @param y The y coordinate of the location to test for.
		 * @return true if point is inside the zone, false if it is outside.
		 */
		public function contains( x:Number, y:Number ):Boolean
		{
			x -= _center.x;
			y -= _center.y;
			var distSq:Number = x * x + y * y;
			if ( distSq > _outerSq || distSq < _innerSq )
			{
				return false;
			}
			var angle:Number = Math.atan2( y, x );
			angle = clamp( angle );
			return angle >= _minAngle;
		}

		/**
		 * The getLocation method returns a random point inside the zone.
		 * This method is used by the initializers and actions that
		 * use the zone. Usually, it need not be called directly by the user.
		 *
		 * @return a random point inside the zone.
		 */
		public function getLocation():Point
		{
			var rand:Number = Math.random();
			var point:Point =  Point.polar( _innerRadius + (1 - rand * rand ) * ( _outerRadius - _innerRadius ), _minAngle + Math.random() * ( _maxAngle - _minAngle ) );
			point.x += _center.x;
			point.y += _center.y;
			return point;
		}

		/**
		 * The getArea method returns the size of the zone.
		 * This method is used by the MultiZone class. Usually,
		 * it need not be called directly by the user.
		 *
		 * @return a random point inside the zone.
		 */
		public function getArea():Number
		{
			return ( Math.PI * _outerSq - Math.PI * _innerSq );
		}

		/**
		 * Manages collisions between a particle and the zone. The particle will collide with the edges of
		 * the disc sector defined for this zone, from inside or outside the disc. In the interests of speed,
		 * these collisions do not take account of the collisionRadius of the particle.
		 *
		 * @param particle The particle to be tested for collision with the zone.
		 * @param bounce The coefficient of restitution for the collision.
		 *
		 * @return Whether a collision occured.
		 */
		public function collideParticle(particle:Particle2D, bounce:Number = 1):Boolean
		{
			// This is approximate, since accurate calculations would be quite complex and thus time consuming

			var xNow:Number = particle.x - _center.x;
			var yNow:Number = particle.y - _center.y;
			var xThen:Number = particle.previousX - _center.x;
			var yThen:Number = particle.previousY - _center.y;
			var insideNow:Boolean = true;
			var insideThen:Boolean = true;

			var distThenSq:Number = xThen * xThen + yThen * yThen;
			var distNowSq:Number = xNow * xNow + yNow * yNow;
			if ( distThenSq > _outerSq || distThenSq < _innerSq )
			{
				insideThen = false;
			}
			if ( distNowSq > _outerSq || distNowSq < _innerSq )
			{
				insideNow = false;
			}
			if ( (! insideNow) && (! insideThen) )
			{
				return false;
			}

			var angleThen:Number = clamp( Math.atan2( yThen, xThen ) );
			var angleNow:Number = clamp( Math.atan2( yNow, xNow ) );
			insideThen = insideThen && angleThen >= minAngle;
			insideNow = insideNow && angleNow >= _minAngle;
			if ( insideNow == insideThen )
			{
				return false;
			}

			var adjustSpeed:Number;
			var dotProduct:Number = particle.velX * xNow + particle.velY * yNow;
			var factor:Number;
			var normalSpeed:Number;

			if( insideNow )
			{
				if( distThenSq > _outerSq )
				{
					// bounce off outer radius
					adjustSpeed = ( 1 + bounce ) * dotProduct / distNowSq;
					particle.velX -= adjustSpeed * xNow;
					particle.velY -= adjustSpeed * yNow;
				}
				else if( distThenSq < _innerSq )
				{
					// bounce off inner radius
					adjustSpeed = ( 1 + bounce ) * dotProduct / distNowSq;
					particle.velX -= adjustSpeed * xNow;
					particle.velY -= adjustSpeed * yNow;
				}
				if( angleThen < _minAngle )
				{
					if( angleThen < ( _minAllowed + _minAngle ) / 2 )
					{
						// bounce off max radius
						normalSpeed = _maxNormal.x * particle.velX + _maxNormal.y * particle.velY;
						factor = ( 1 + bounce ) * normalSpeed;
						particle.velX -= factor * _maxNormal.x;
						particle.velY -= factor * _maxNormal.y;
					}
					else
					{
						// bounce off min radius
						normalSpeed = _minNormal.x * particle.velX + _minNormal.y * particle.velY;
						factor = ( 1 + bounce ) * normalSpeed;
						particle.velX -= factor * _minNormal.x;
						particle.velY -= factor * _minNormal.y;
					}
				}
			}
			else // inside then
			{
				if( distNowSq > _outerSq )
				{
					// bounce off outer radius
					adjustSpeed = ( 1 + bounce ) * dotProduct / distNowSq;
					particle.velX -= adjustSpeed * xNow;
					particle.velY -= adjustSpeed * yNow;
				}
				else if( distNowSq < _innerSq )
				{
					// bounce off inner radius
					adjustSpeed = ( 1 + bounce ) * dotProduct / distNowSq;
					particle.velX -= adjustSpeed * xNow;
					particle.velY -= adjustSpeed * yNow;
				}
				if( angleNow < _minAngle )
				{
					if( angleNow < ( _minAllowed + _minAngle ) / 2 )
					{
						// bounce off max radius
						normalSpeed = _maxNormal.x * particle.velX + _maxNormal.y * particle.velY;
						factor = ( 1 + bounce ) * normalSpeed;
						particle.velX -= factor * _maxNormal.x;
						particle.velY -= factor * _maxNormal.y;
					}
					else
					{
						// bounce off min radius
						normalSpeed = _minNormal.x * particle.velX + _minNormal.y * particle.velY;
						factor = ( 1 + bounce ) * normalSpeed;
						particle.velX -= factor * _minNormal.x;
						particle.velY -= factor * _minNormal.y;
					}
				}
			}
			particle.x = particle.previousX;
			particle.y = particle.previousY;

			return true;
		}
	}
}

/*
 * FLINT PARTICLE SYSTEM
 * .....................
 *
 * Author: Richard Lord
 * Copyright (c) Richard Lord 2008-2011
 * 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.twoD.zones
{
	import org.flintparticles.twoD.particles.Particle2D;

	import flash.geom.Point;

	/**
	 * The DiscSectorZone zone defines a section of a Disc zone. The disc
	 * on which it's based have a hole in the middle, like a doughnut.
	 */

	public class DiscSectorZone implements Zone2D
	{
		private var _center:Point;
		private var _innerRadius:Number;
		private var _outerRadius:Number;
		private var _innerSq:Number;
		private var _outerSq:Number;
		private var _minAngle:Number;
		private var _maxAngle:Number;
		private var _minAllowed:Number;
		private var _minNormal:Point;
		private var _maxNormal:Point;

		private static var TWOPI:Number = Math.PI * 2;

		/**
		 * The constructor defines a DiscSectorZone zone.
		 *
		 * @param center The centre of the disc.
		 * @param outerRadius The radius of the outer edge of the disc.
		 * @param innerRadius If set, this defines the radius of the inner
		 * edge of the disc. Points closer to the center than this inner radius
		 * are excluded from the zone. If this parameter is not set then all
		 * points inside the outer radius are included in the zone.
		 * @param minAngle The minimum angle, in radians, for points to be included in the zone.
		 * An angle of zero is horizontal and to the right. Positive angles are in a clockwise
		 * direction (towards the graphical y axis). Angles are converted to a value between 0
		 * and two times PI.
		 * @param maxAngle The maximum angle, in radians, for points to be included in the zone.
		 * An angle of zero is horizontal and to the right. Positive angles are in a clockwise
		 * direction (towards the graphical y axis). Angles are converted to a value between 0
		 * and two times PI.
		 */
		public function DiscSectorZone( center:Point = null, outerRadius:Number = 0, innerRadius:Number = 0, minAngle:Number = 0, maxAngle:Number = 0 )
		{
			if( outerRadius < innerRadius )
			{
				throw new Error( "The outerRadius (" + outerRadius + ") can't be smaller than the innerRadius (" + innerRadius + ") in your DiscSectorZone. N.B. the outerRadius is the second argument in the constructor and the innerRadius is the third argument." );
			}
			_center = center ? center.clone() : new Point( 0, 0 );
			_innerRadius = innerRadius;
			_outerRadius = outerRadius;
			_innerSq = _innerRadius * _innerRadius;
			_outerSq = _outerRadius * _outerRadius;
			_minAngle = minAngle;
			_maxAngle = maxAngle;
			if( ! isNaN( _maxAngle ) )
			{
				while ( _maxAngle > TWOPI )
				{
					_maxAngle -= TWOPI;
				}
				while ( _maxAngle < 0 )
				{
					_maxAngle += TWOPI;
				}
				_minAllowed = _maxAngle - TWOPI;
				if( ! isNaN( _minAngle ) )
				{
					if ( minAngle == maxAngle )
					{
						_minAngle = _maxAngle;
					}
					else
					{
						_minAngle = clamp( _minAngle );
					}
				}
				calculateNormals();
			}
		}

		private function clamp( angle:Number ):Number
		{
			if( ! isNaN( _maxAngle ) )
			{
				while ( angle > _maxAngle )
				{
					angle -= TWOPI;
				}
				while ( angle < _minAllowed )
				{
					angle += TWOPI;
				}
			}
			return angle;
		}

		private function calculateNormals():void
		{
			if( ! isNaN( _minAngle ) )
			{
				_minNormal = new Point( Math.sin( _minAngle ), - Math.cos( _minAngle ) );
				_minNormal.normalize( 1 );
			}
			if( ! isNaN( _maxAngle ) )
			{
				_maxNormal = new Point( - Math.sin( _maxAngle ), Math.cos( _maxAngle ) );
				_maxNormal.normalize( 1 );
			}
		}

		/**
		 * The centre of the disc.
		 */
		public function get center() : Point
		{
			return _center;
		}

		public function set center( value : Point ) : void
		{
			_center = value;
		}

		/**
		 * The x coordinate of the point that is the center of the disc.
		 */
		public function get centerX() : Number
		{
			return _center.x;
		}

		public function set centerX( value : Number ) : void
		{
			_center.x = value;
		}

		/**
		 * The y coordinate of the point that is the center of the disc.
		 */
		public function get centerY() : Number
		{
			return _center.y;
		}

		public function set centerY( value : Number ) : void
		{
			_center.y = value;
		}

		/**
		 * The radius of the inner edge of the disc.
		 */
		public function get innerRadius() : Number
		{
			return _innerRadius;
		}

		public function set innerRadius( value : Number ) : void
		{
			_innerRadius = value;
			_innerSq = _innerRadius * _innerRadius;
		}

		/**
		 * The radius of the outer edge of the disc.
		 */
		public function get outerRadius() : Number
		{
			return _outerRadius;
		}

		public function set outerRadius( value : Number ) : void
		{
			_outerRadius = value;
			_outerSq = _outerRadius * _outerRadius;
		}

		/**
		 * The minimum angle, in radians, for points to be included in the zone.
		 * An angle of zero is horizontal and to the right. Positive angles are in a clockwise
		 * direction (towards the graphical y axis). Angles are converted to a value between 0
		 * and two times PI.
		 */
		public function get minAngle() : Number
		{
			return _minAngle;
		}

		public function set minAngle( value : Number ) : void
		{
			_minAngle = clamp( value );
			calculateNormals();
		}

		/**
		 * The maximum angle, in radians, for points to be included in the zone.
		 * An angle of zero is horizontal and to the right. Positive angles are in a clockwise
		 * direction (towards the graphical y axis). Angles are converted to a value between 0
		 * and two times PI.
		 */
		public function get maxAngle() : Number
		{
			return _maxAngle;
		}

		public function set maxAngle( value : Number ) : void
		{
			_maxAngle = value;
			while ( _maxAngle > TWOPI )
			{
				_maxAngle -= TWOPI;
			}
			while ( _maxAngle < 0 )
			{
				_maxAngle += TWOPI;
			}
			_minAllowed = _maxAngle - TWOPI;
			_minAngle = clamp( _minAngle );
			calculateNormals();
		}

		/**
		 * The contains method determines whether a point is inside the zone.
		 * This method is used by the initializers and actions that
		 * use the zone. Usually, it need not be called directly by the user.
		 *
		 * @param x The x coordinate of the location to test for.
		 * @param y The y coordinate of the location to test for.
		 * @return true if point is inside the zone, false if it is outside.
		 */
		public function contains( x:Number, y:Number ):Boolean
		{
			x -= _center.x;
			y -= _center.y;
			var distSq:Number = x * x + y * y;
			if ( distSq > _outerSq || distSq < _innerSq )
			{
				return false;
			}
			var angle:Number = Math.atan2( y, x );
			angle = clamp( angle );
			return angle >= _minAngle;
		}

		/**
		 * The getLocation method returns a random point inside the zone.
		 * This method is used by the initializers and actions that
		 * use the zone. Usually, it need not be called directly by the user.
		 *
		 * @return a random point inside the zone.
		 */
		public function getLocation():Point
		{
			var rand:Number = Math.random();
			var point:Point =  Point.polar( _innerRadius + (1 - rand * rand ) * ( _outerRadius - _innerRadius ), _minAngle + Math.random() * ( _maxAngle - _minAngle ) );
			point.x += _center.x;
			point.y += _center.y;
			return point;
		}

		/**
		 * The getArea method returns the size of the zone.
		 * This method is used by the MultiZone class. Usually,
		 * it need not be called directly by the user.
		 *
		 * @return a random point inside the zone.
		 */
		public function getArea():Number
		{
			return ( _outerSq - _innerSq ) * ( _maxAngle - _minAngle ) * 0.5;
		}

		/**
		 * Manages collisions between a particle and the zone. The particle will collide with the edges of
		 * the disc sector defined for this zone, from inside or outside the disc. In the interests of speed,
		 * these collisions do not take account of the collisionRadius of the particle.
		 *
		 * @param particle The particle to be tested for collision with the zone.
		 * @param bounce The coefficient of restitution for the collision.
		 *
		 * @return Whether a collision occured.
		 */
		public function collideParticle(particle:Particle2D, bounce:Number = 1):Boolean
		{
			// This is approximate, since accurate calculations would be quite complex and thus time consuming

			var xNow:Number = particle.x - _center.x;
			var yNow:Number = particle.y - _center.y;
			var xThen:Number = particle.previousX - _center.x;
			var yThen:Number = particle.previousY - _center.y;
			var insideNow:Boolean = true;
			var insideThen:Boolean = true;

			var distThenSq:Number = xThen * xThen + yThen * yThen;
			var distNowSq:Number = xNow * xNow + yNow * yNow;
			if ( distThenSq > _outerSq || distThenSq < _innerSq )
			{
				insideThen = false;
			}
			if ( distNowSq > _outerSq || distNowSq < _innerSq )
			{
				insideNow = false;
			}
			if ( (! insideNow) && (! insideThen) )
			{
				return false;
			}

			var angleThen:Number = clamp( Math.atan2( yThen, xThen ) );
			var angleNow:Number = clamp( Math.atan2( yNow, xNow ) );
			insideThen = insideThen && angleThen >= minAngle;
			insideNow = insideNow && angleNow >= _minAngle;
			if ( insideNow == insideThen )
			{
				return false;
			}

			var adjustSpeed:Number;
			var dotProduct:Number = particle.velX * xNow + particle.velY * yNow;
			var factor:Number;
			var normalSpeed:Number;

			if( insideNow )
			{
				if( distThenSq > _outerSq )
				{
					// bounce off outer radius
					adjustSpeed = ( 1 + bounce ) * dotProduct / distNowSq;
					particle.velX -= adjustSpeed * xNow;
					particle.velY -= adjustSpeed * yNow;
				}
				else if( distThenSq < _innerSq )
				{
					// bounce off inner radius
					adjustSpeed = ( 1 + bounce ) * dotProduct / distNowSq;
					particle.velX -= adjustSpeed * xNow;
					particle.velY -= adjustSpeed * yNow;
				}
				if( angleThen < _minAngle )
				{
					if( angleThen < ( _minAllowed + _minAngle ) / 2 )
					{
						// bounce off max radius
						normalSpeed = _maxNormal.x * particle.velX + _maxNormal.y * particle.velY;
						factor = ( 1 + bounce ) * normalSpeed;
						particle.velX -= factor * _maxNormal.x;
						particle.velY -= factor * _maxNormal.y;
					}
					else
					{
						// bounce off min radius
						normalSpeed = _minNormal.x * particle.velX + _minNormal.y * particle.velY;
						factor = ( 1 + bounce ) * normalSpeed;
						particle.velX -= factor * _minNormal.x;
						particle.velY -= factor * _minNormal.y;
					}
				}
			}
			else // inside then
			{
				if( distNowSq > _outerSq )
				{
					// bounce off outer radius
					adjustSpeed = ( 1 + bounce ) * dotProduct / distNowSq;
					particle.velX -= adjustSpeed * xNow;
					particle.velY -= adjustSpeed * yNow;
				}
				else if( distNowSq < _innerSq )
				{
					// bounce off inner radius
					adjustSpeed = ( 1 + bounce ) * dotProduct / distNowSq;
					particle.velX -= adjustSpeed * xNow;
					particle.velY -= adjustSpeed * yNow;
				}
				if( angleNow < _minAngle )
				{
					if( angleNow < ( _minAllowed + _minAngle ) / 2 )
					{
						// bounce off max radius
						normalSpeed = _maxNormal.x * particle.velX + _maxNormal.y * particle.velY;
						factor = ( 1 + bounce ) * normalSpeed;
						particle.velX -= factor * _maxNormal.x;
						particle.velY -= factor * _maxNormal.y;
					}
					else
					{
						// bounce off min radius
						normalSpeed = _minNormal.x * particle.velX + _minNormal.y * particle.velY;
						factor = ( 1 + bounce ) * normalSpeed;
						particle.velX -= factor * _minNormal.x;
						particle.velY -= factor * _minNormal.y;
					}
				}
			}
			particle.x = particle.previousX;
			particle.y = particle.previousY;

			return true;
		}
	}
}

Fix error in getArea() on DiscSectorZone


package aerys.minko.render.geometry.stream
{
	import aerys.minko.ns.minko_stream;
	import aerys.minko.render.resource.IndexBuffer3DResource;
	import aerys.minko.type.Signal;

	import flash.utils.ByteArray;

	public final class IndexStream
	{
		use namespace minko_stream;

		minko_stream var _data	: Vector.<uint>			= null;

		private var _usage		: uint					= 0;
		private var _resource	: IndexBuffer3DResource	= null;
		private var _length		: uint					= 0;

		private var _changed	: Signal				= new Signal('IndexStream.changed');

		public function get usage() : uint
		{
			return _usage;
		}

		public function get resource() : IndexBuffer3DResource
		{
			return _resource;
		}

		public function get length() : uint
		{
			return _length;
		}

		public function set length(value : uint) : void
		{
			_data.length = value;
			invalidate();
		}

		public function get changed() : Signal
		{
			return _changed;
		}

		public function IndexStream(usage	: uint,
									data 	: Vector.<uint> = null,
									length	: uint			= 0)
		{
			super();

			initialize(data, length, usage);
		}

		minko_stream function invalidate() : void
		{
			_length = _data.length;
			_changed.execute(this);
		}

		private function initialize(indices : Vector.<uint>,
									length 	: uint,
									usage	: uint) : void
		{
			_usage = usage;
			_resource = new IndexBuffer3DResource(this);

			if (indices)
			{
				var numIndices : int = indices && length == 0
									   ? indices.length
									   : Math.min(indices.length, length);

				_data = new Vector.<uint>(numIndices);
				for (var i : int = 0; i < numIndices; ++i)
					_data[i] = indices[i];
			}
			else
			{
				_data = dummyData(length);
			}

			invalidate();
		}

		/*override flash_proxy function getProperty(name : *) : *
		{
			return _data[int(name)];
		}

		override flash_proxy function setProperty(name : *, value : *) : void
		{
			var index : int = int(name);

			_data[index] = int(value);

			invalidate();
		}

		override flash_proxy function nextNameIndex(index : int) : int
		{
			return index >= _data.length ? 0 : index + 1;
		}

		override flash_proxy function nextValue(index : int) : *
		{
			return _data[int(index - 1)];
		}

		override flash_proxy function deleteProperty(name : *) : Boolean
		{
		var index 	: int = int(name);
		var length 	: int = length;

		if (index > length)
		return false;

		for (var i : int = index; i < length - 1; ++i)
		_data[i] = _data[int(i + 1)];

		_data.length = length - 1;

		invalidate();

		return true;
		}*/

		public function get(index : int) : uint
		{
			checkReadUsage(this);

			return _data[index];
		}

		public function set(index : int, value : uint) : void
		{
			checkWriteUsage(this);

			_data[index] = value;
		}

		public function deleteTriangleByIndex(index : int) : Vector.<uint>
		{
			checkWriteUsage(this);

			var deletedIndices : Vector.<uint> = _data.splice(index, 3);

			invalidate();

			return deletedIndices;
		}

		public function getIndices(indices : Vector.<uint> = null) : Vector.<uint>
		{
			checkReadUsage(this);

			var numIndices : int = length;

			indices ||= new Vector.<uint>();
			for (var i : int = 0; i < numIndices; ++i)
				indices[i] = _data[i];

			return indices;
		}

		public function setIndices(indices : Vector.<uint>) : void
		{
			checkWriteUsage(this);

			var length : int = indices.length;

			for (var i : int = 0; i < length; ++i)
				_data[i] = indices[i];

			_data.length = length;

			invalidate();
		}

		public function clone() : IndexStream
		{
			return new IndexStream(_usage, _data, length);
		}

		public function toString() : String
		{
			return _data.toString();
		}

		public function concat(indexStream : IndexStream,
							   firstIndex	: uint	= 0,
							   count		: uint	= 0,
							   offset		: uint 	= 0) : IndexStream
		{
			checkReadUsage(indexStream);
			checkWriteUsage(this);

			var numIndices 	: int 			= _data.length;
			var toConcat 	: Vector.<uint> = indexStream._data;

			count ||= toConcat.length;
			for (var i : int = 0; i < count; ++i, ++numIndices)
				_data[numIndices] = toConcat[int(firstIndex + i)] + offset;

			invalidate();

			return this;
		}

		public function push(indices 	: Vector.<uint>,
							 firstIndex	: uint	= 0,
							 count		: uint	= 0,
							 offset		: uint 	= 0) : void
		{
			checkWriteUsage(this);

			var numIndices : int = _data.length;

			count ||= indices.length;

			for (var i : int = 0; i < count; ++i)
				_data[int(numIndices++)] = indices[int(firstIndex + i)] + offset;

			invalidate();
		}

		public function disposeLocalData() : void
		{
			if (length != resource.numIndices)
			{
				throw new Error(
					"Unable to dispose local data: "
					+ "some indices have not been uploaded."
				);
			}

			_data = null;
			_usage = StreamUsage.STATIC;
		}

		public function dispose() : void
		{
			_resource.dispose();
		}

		private static function checkReadUsage(stream : IndexStream) : void
		{
			if (!(stream._usage & StreamUsage.READ))
				throw new Error(
					'Unable to read from vertex stream: stream usage '
					+ 'is not set to StreamUsage.READ.'
				);
		}

		private static function checkWriteUsage(stream : IndexStream) : void
		{
			if (!(stream._usage & StreamUsage.WRITE))
				throw new Error(
					'Unable to write in vertex stream: stream usage '
					+ 'is not set to StreamUsage.WRITE.'
				);
		}

		public static function dummyData(size : uint, offset : uint = 0) : Vector.<uint>
		{
			var indices : Vector.<uint> = new Vector.<uint>(size);

			for (var i : int = 0; i < size; ++i)
				indices[i] = i + offset;

			return indices;
		}

		public static function fromByteArray(usage		: uint,
											 data 		: ByteArray,
											 numIndices	: int) : IndexStream
		{
			var indices : Vector.<uint> = new Vector.<uint>(numIndices, true);
			var stream : IndexStream = new IndexStream(usage);

			for (var i : int = 0; i < numIndices; ++i)
				indices[i] = data.readInt();

			stream._data = indices;
			stream.invalidate();

			return stream;
		}
	}
}

package aerys.minko.render.geometry.stream
{
	import aerys.minko.ns.minko_stream;
	import aerys.minko.render.resource.IndexBuffer3DResource;
	import aerys.minko.type.Signal;

	import flash.utils.ByteArray;

	public final class IndexStream
	{
		use namespace minko_stream;

		minko_stream var _data	: Vector.<uint>			= null;

		private var _usage		: uint					= 0;
		private var _resource	: IndexBuffer3DResource	= null;
		private var _length		: uint					= 0;

		private var _changed	: Signal				= new Signal('IndexStream.changed');

		public function get usage() : uint
		{
			return _usage;
		}

		public function get resource() : IndexBuffer3DResource
		{
			return _resource;
		}

		public function get length() : uint
		{
			return _length;
		}

		public function set length(value : uint) : void
		{
			_data.length = value;
			invalidate();
		}

		public function get changed() : Signal
		{
			return _changed;
		}

		public function IndexStream(usage	: uint,
									data 	: Vector.<uint> = null,
									length	: uint			= 0)
		{
			super();

			initialize(data, length, usage);
		}

		minko_stream function invalidate() : void
		{
			_length = _data.length;
			_changed.execute(this);
		}

		private function initialize(indices : Vector.<uint>,
									length 	: uint,
									usage	: uint) : void
		{
			_usage = usage;
			_resource = new IndexBuffer3DResource(this);

			if (indices)
			{
				var numIndices : int = indices && length == 0
					? indices.length
					: Math.min(indices.length, length);

				_data = new Vector.<uint>(numIndices);
				for (var i : int = 0; i < numIndices; ++i)
					_data[i] = indices[i];
			}
			else
			{
				_data = dummyData(length);
			}

			invalidate();
		}

		public function get(index : int) : uint
		{
			checkReadUsage(this);

			return _data[index];
		}

		public function set(index : int, value : uint) : void
		{
			checkWriteUsage(this);

			_data[index] = value;
		}

		public function deleteTriangleByIndex(index : int) : Vector.<uint>
		{
			checkWriteUsage(this);

			var deletedIndices : Vector.<uint> = _data.splice(index, 3);

			invalidate();

			return deletedIndices;
		}

		public function getIndices(indices : Vector.<uint> = null) : Vector.<uint>
		{
			checkReadUsage(this);

			var numIndices : int = length;

			indices ||= new Vector.<uint>();
			for (var i : int = 0; i < numIndices; ++i)
				indices[i] = _data[i];

			return indices;
		}

		public function setIndices(indices : Vector.<uint>) : void
		{
			checkWriteUsage(this);

			var length : int = indices.length;

			for (var i : int = 0; i < length; ++i)
				_data[i] = indices[i];

			_data.length = length;

			invalidate();
		}

		public function clone() : IndexStream
		{
			return new IndexStream(_usage, _data, length);
		}

		public function toString() : String
		{
			return _data.toString();
		}

		public function concat(indexStream : IndexStream,
							   firstIndex	: uint	= 0,
							   count		: uint	= 0,
							   offset		: uint 	= 0) : IndexStream
		{
			checkReadUsage(indexStream);
			checkWriteUsage(this);

			var numIndices 	: int 			= _data.length;
			var toConcat 	: Vector.<uint> = indexStream._data;

			count ||= toConcat.length;
			for (var i : int = 0; i < count; ++i, ++numIndices)
				_data[numIndices] = toConcat[int(firstIndex + i)] + offset;

			invalidate();

			return this;
		}

		public function push(indices 	: Vector.<uint>,
							 firstIndex	: uint	= 0,
							 count		: uint	= 0,
							 offset		: uint 	= 0) : void
		{
			checkWriteUsage(this);

			var numIndices : int = _data.length;

			count ||= indices.length;

			for (var i : int = 0; i < count; ++i)
				_data[int(numIndices++)] = indices[int(firstIndex + i)] + offset;

			invalidate();
		}

		public function disposeLocalData() : void
		{
			if (length != resource.numIndices)
			{
				throw new Error(
					"Unable to dispose local data: "
					+ "some indices have not been uploaded."
				);
			}

			_data = null;
			_usage = StreamUsage.STATIC;
		}

		public function dispose() : void
		{
			_resource.dispose();
		}

		private static function checkReadUsage(stream : IndexStream) : void
		{
			if (!(stream._usage & StreamUsage.READ))
				throw new Error(
					'Unable to read from vertex stream: stream usage '
					+ 'is not set to StreamUsage.READ.'
				);
		}

		private static function checkWriteUsage(stream : IndexStream) : void
		{
			if (!(stream._usage & StreamUsage.WRITE))
				throw new Error(
					'Unable to write in vertex stream: stream usage '
					+ 'is not set to StreamUsage.WRITE.'
				);
		}

		public static function dummyData(size : uint, offset : uint = 0) : Vector.<uint>
		{
			var indices : Vector.<uint> = new Vector.<uint>(size);

			for (var i : int = 0; i < size; ++i)
				indices[i] = i + offset;

			return indices;
		}

		public static function fromByteArray(usage		: uint,
											 data 		: ByteArray,
											 numIndices	: int) : IndexStream
		{
			var indices : Vector.<uint> = new Vector.<uint>(numIndices, true);
			var stream : IndexStream = new IndexStream(usage);

			for (var i : int = 0; i < numIndices; ++i)
				indices[i] = data.readInt();

			stream._data = indices;
			stream.invalidate();

			return stream;
		}
	}
}

fix minor coding style issue in IndexStream.initialize()


package aerys.minko.render.geometry.stream
{
	import aerys.minko.ns.minko_stream;
	import aerys.minko.render.resource.IndexBuffer3DResource;
	import aerys.minko.type.Signal;

	import flash.utils.ByteArray;
	import flash.utils.Endian;

	public final class IndexStream
	{
		use namespace minko_stream;

		minko_stream var _data			: ByteArray;
		minko_stream var _localDispose	: Boolean;

		private var _usage		: uint;
		private var _resource	: IndexBuffer3DResource;
		private var _length		: uint;

		private var _locked		: Boolean;

		private var _changed	: Signal;

		public function get usage() : uint
		{
			return _usage;
		}

		public function get resource() : IndexBuffer3DResource
		{
			return _resource;
		}

		public function get length() : uint
		{
			return _length;
		}

		public function set length(value : uint) : void
		{
			_data.length = value << 1;
			invalidate();
		}

		public function get changed() : Signal
		{
			return _changed;
		}

		public function IndexStream(usage	: uint,
									data 	: ByteArray	= null,
									length	: uint		= 0)
		{
			super();

			initialize(data, length, usage);
		}

		minko_stream function invalidate() : void
		{
			_data.position = 0;
			_length = _data.length >>> 1;

            if (_length % 3 != 0)
                throw new Error('Invalid size');

			if (!_locked)
				_changed.execute(this);
		}

		private function initialize(data	: ByteArray,
									length 	: uint,
									usage	: uint) : void
		{
			_changed = new Signal('IndexStream.changed');

			_usage = usage;
			_resource = new IndexBuffer3DResource(this);

			_data = new ByteArray();
			_data.endian = Endian.LITTLE_ENDIAN;
			if (data)
			{
				if (data.endian != Endian.LITTLE_ENDIAN)
					throw new Error('Endianness must be Endian.LITTLE_ENDIAN.');
				if (length == 0)
					length = data.bytesAvailable;
				if (length % 6 != 0)
					throw new Error('Invalid size');

				data.readBytes(_data, 0, length);
			}
			else
			{
				_data = dummyData(length);
			}

			_data.position = 0;

			invalidate();
		}

		public function get(index : uint) : uint
		{
			var value : uint = 0;

			checkReadUsage(this);

			_data.position = index << 1;
			value = _data.readUnsignedShort();
			_data.position = 0;

			return value;
		}

		public function set(index : uint, value : uint) : void
		{
			checkWriteUsage(this);

			_data.position = index << 1;
			_data.writeShort(value);
			_data.position = 0;

			invalidate();
		}

		public function deleteTriangle(triangleIndex : uint) : void
		{
			checkWriteUsage(this);

			_data.position = 0;
			_data.writeBytes(_data, triangleIndex * 12, 12);
			_data.length -= 12;
			_data.position = 0;

			invalidate();
		}

		public function clone(usage : uint = 0) : IndexStream
		{
			return new IndexStream(usage || _usage, _data);
		}

		public function toString() : String
		{
			return _data.toString();
		}

		public function concat(indexStream : IndexStream,
							   firstIndex	: uint	= 0,
							   count		: uint	= 0,
							   indexOffset	: uint 	= 0) : IndexStream
		{
			checkReadUsage(indexStream);
			checkWriteUsage(this);

			pushBytes(indexStream._data, firstIndex, count, indexOffset);
			indexStream._data.position = 0;

			return this;
		}

		public function pushBytes(bytes			: ByteArray,
								  firstIndex	: uint	= 0,
								  count			: uint	= 0,
								  indexOffset	: uint	= 0) : IndexStream
		{
			count ||= bytes.length >>> 1;

			_data.position = _data.length;

			if (indexOffset == 0)
				_data.writeBytes(bytes, firstIndex << 1, count << 1);
			else
			{
				bytes.position = firstIndex << 1;
				for (var i : uint = 0; i < count; ++i)
					_data.writeShort(bytes.readUnsignedShort() + indexOffset);
			}

			_data.position = 0;
			bytes.position = (firstIndex + count) << 1;

			invalidate();

			return this;
		}

		public function pushVector(indices 		: Vector.<uint>,
								   firstIndex	: uint	= 0,
								   count		: uint	= 0,
								   offset		: uint 	= 0) : IndexStream
		{
			checkWriteUsage(this);

			var numIndices : int = _data.length;

			count ||= indices.length;

			_data.position = _data.length;
			for (var i : int = 0; i < count; ++i)
				_data.writeShort(indices[int(firstIndex + i)] + offset);
			_data.position = 0;

			invalidate();

			return this;
		}

		public function pushTriangle(index1 : uint, index2 : uint, index3 : uint) : IndexStream
		{
			return setTriangle(length * 3, index1, index2, index3);
		}

		public function setTriangle(triangleIndex	: uint,
									index1 			: uint,
									index2 			: uint,
									index3 			: uint) : IndexStream
		{
			_data.position = triangleIndex << 1;
			_data.writeShort(index1);
			_data.writeShort(index2);
			_data.writeShort(index3);
			_data.position = 0;

			invalidate();

			return this;
		}

		public function disposeLocalData(waitForUpload : Boolean = true) : void
		{
			if (waitForUpload && _length != resource.numIndices)
				_localDispose = true;
			else
			{
				_data = null;
				_usage = StreamUsage.STATIC;
			}
		}

		public function dispose() : void
		{
			_resource.dispose();
		}

		public function lock() : ByteArray
		{
			checkReadUsage(this);

			_data.position = 0;
			_locked = true;

			return _data;
		}

		public function unlock(hasChanged : Boolean = true) : void
		{
			_data.position = 0;

			_locked = false;

			if (hasChanged)
				invalidate();
		}

		/**
		 * Clones the IndexStream by creating a new underlying ByteArray and applying an offset on the indices.
		 *
		 * @param indexStream The IndexStream to clone.
		 * @param offset The offset to apply on the indices.
		 *
		 * @return The offseted new IndexStream.
		 *
		 */
		public static function cloneWithOffset(indexStream	: IndexStream,
											   offset 		: uint 			= 0) : IndexStream
		{
			if (!(indexStream.usage & StreamUsage.READ))
			{
				throw new Error('Unable to read from index stream: stream usage is not set to StreamUsage.READ.');
			}

			var newData		: ByteArray	= new ByteArray();
			newData.endian 				= Endian.LITTLE_ENDIAN;

			var oldData		: ByteArray	= indexStream._data;
			oldData.position = 0;
			while (oldData.bytesAvailable)
			{
				var index	: uint		= oldData.readUnsignedShort() + offset;
				newData.writeShort(index);
			}
			newData.position = 0;

			return new IndexStream(indexStream.usage, newData);
		}

		private static function checkReadUsage(stream : IndexStream) : void
		{
			if (!(stream._usage & StreamUsage.READ))
				throw new Error(
					'Unable to read from vertex stream: stream usage is not set to StreamUsage.READ.'
				);
		}

		private static function checkWriteUsage(stream : IndexStream) : void
		{
			if (!(stream._usage & StreamUsage.WRITE))
				throw new Error(
					'Unable to write in vertex stream: stream usage is not set to StreamUsage.WRITE.'
				);
		}

		public static function dummyData(size 	: uint,
										 offset : uint = 0) : ByteArray
		{
			var indices : ByteArray = new ByteArray();

			indices.endian = Endian.LITTLE_ENDIAN;
			for (var i : int = 0; i < size; ++i)
				indices.writeShort(i + offset);
			indices.position = 0;

			return indices;
		}

		public static function fromVector(usage : uint, data : Vector.<uint>) : IndexStream
		{
			var stream 		: IndexStream 	= new IndexStream(usage);
			var numIndices 	: uint 			= data.length;

			for (var i : uint = 0; i < numIndices; ++i)
				stream._data.writeShort(data[i]);
			stream._data.position = 0;
			stream._length = numIndices;

			return stream;
		}
	}
}

package aerys.minko.render.geometry.stream
{
	import aerys.minko.ns.minko_stream;
	import aerys.minko.render.resource.IndexBuffer3DResource;
	import aerys.minko.type.Signal;

	import flash.utils.ByteArray;
	import flash.utils.Endian;

	public final class IndexStream
	{
		use namespace minko_stream;

		minko_stream var _data			: ByteArray;
		minko_stream var _localDispose	: Boolean;

		private var _usage		: uint;
		private var _resource	: IndexBuffer3DResource;
		private var _length		: uint;

		private var _locked		: Boolean;

		private var _changed	: Signal;

		public function get usage() : uint
		{
			return _usage;
		}

		public function get resource() : IndexBuffer3DResource
		{
			return _resource;
		}

		public function get length() : uint
		{
			return _length;
		}

		public function set length(value : uint) : void
		{
			_data.length = value << 1;
			invalidate();
		}

		public function get changed() : Signal
		{
			return _changed;
		}

		public function IndexStream(usage	: uint,
									data 	: ByteArray	= null,
									length	: uint		= 0)
		{
			super();

			initialize(data, length, usage);
		}

		minko_stream function invalidate() : void
		{
			_data.position = 0;
			_length = _data.length >>> 1;

            if (_length % 3 != 0)
                throw new Error('Invalid size');

			if (!_locked)
				_changed.execute(this);
		}

		private function initialize(data	: ByteArray,
									length 	: uint,
									usage	: uint) : void
		{
			_changed = new Signal('IndexStream.changed');

			_usage = usage;
			_resource = new IndexBuffer3DResource(this);

			_data = new ByteArray();
			_data.endian = Endian.LITTLE_ENDIAN;
			if (data)
			{
				if (data.endian != Endian.LITTLE_ENDIAN)
					throw new Error('Endianness must be Endian.LITTLE_ENDIAN.');
				if (length == 0)
					length = data.bytesAvailable;
				if (length % 6 != 0)
					throw new Error('Invalid size');

				data.readBytes(_data, 0, length);
			}
			else
			{
				_data = dummyData(length);
			}

			_data.position = 0;

			invalidate();
		}

		public function get(index : uint) : uint
		{
			var value : uint = 0;

			checkReadUsage(this);

			_data.position = index << 1;
			value = _data.readUnsignedShort();
			_data.position = 0;

			return value;
		}

		public function set(index : uint, value : uint) : void
		{
			checkWriteUsage(this);

			_data.position = index << 1;
			_data.writeShort(value);
			_data.position = 0;

			invalidate();
		}

		public function deleteTriangle(triangleIndex : uint) : void
		{
			checkWriteUsage(this);

			_data.position = _data.length - 6;
			var v0 : uint = _data.readShort();
			var v1 : uint = _data.readShort();
			var v2 : uint = _data.readShort();

			_data.position = triangleIndex * 12;
			_data.writeShort(v0);
			_data.writeShort(v1);
			_data.writeShort(v2);

//			_data.writeBytes(_data, triangleIndex * 12, 12);
			_data.length -= 6;
			_data.position = 0;

			invalidate();
		}

		public function clone(usage : uint = 0) : IndexStream
		{
			return new IndexStream(usage || _usage, _data);
		}

		public function toString() : String
		{
			return _data.toString();
		}

		public function concat(indexStream : IndexStream,
							   firstIndex	: uint	= 0,
							   count		: uint	= 0,
							   indexOffset	: uint 	= 0) : IndexStream
		{
			checkReadUsage(indexStream);
			checkWriteUsage(this);

			pushBytes(indexStream._data, firstIndex, count, indexOffset);
			indexStream._data.position = 0;

			return this;
		}

		public function pushBytes(bytes			: ByteArray,
								  firstIndex	: uint	= 0,
								  count			: uint	= 0,
								  indexOffset	: uint	= 0) : IndexStream
		{
			count ||= bytes.length >>> 1;

			_data.position = _data.length;

			if (indexOffset == 0)
				_data.writeBytes(bytes, firstIndex << 1, count << 1);
			else
			{
				bytes.position = firstIndex << 1;
				for (var i : uint = 0; i < count; ++i)
					_data.writeShort(bytes.readUnsignedShort() + indexOffset);
			}

			_data.position = 0;
			bytes.position = (firstIndex + count) << 1;

			invalidate();

			return this;
		}

		public function pushVector(indices 		: Vector.<uint>,
								   firstIndex	: uint	= 0,
								   count		: uint	= 0,
								   offset		: uint 	= 0) : IndexStream
		{
			checkWriteUsage(this);

			var numIndices : int = _data.length;

			count ||= indices.length;

			_data.position = _data.length;
			for (var i : int = 0; i < count; ++i)
				_data.writeShort(indices[int(firstIndex + i)] + offset);
			_data.position = 0;

			invalidate();

			return this;
		}

		public function pushTriangle(index1 : uint, index2 : uint, index3 : uint) : IndexStream
		{
			return setTriangle(length * 3, index1, index2, index3);
		}

		public function setTriangle(triangleIndex	: uint,
									index1 			: uint,
									index2 			: uint,
									index3 			: uint) : IndexStream
		{
			_data.position = triangleIndex << 1;
			_data.writeShort(index1);
			_data.writeShort(index2);
			_data.writeShort(index3);
			_data.position = 0;

			invalidate();

			return this;
		}

		public function disposeLocalData(waitForUpload : Boolean = true) : void
		{
			if (waitForUpload && _length != resource.numIndices)
				_localDispose = true;
			else
			{
				_data = null;
				_usage = StreamUsage.STATIC;
			}
		}

		public function dispose() : void
		{
			_resource.dispose();
		}

		public function lock() : ByteArray
		{
			checkReadUsage(this);

			_data.position = 0;
			_locked = true;

			return _data;
		}

		public function unlock(hasChanged : Boolean = true) : void
		{
			_data.position = 0;

			_locked = false;

			if (hasChanged)
				invalidate();
		}

		/**
		 * Clones the IndexStream by creating a new underlying ByteArray and applying an offset on the indices.
		 *
		 * @param indexStream The IndexStream to clone.
		 * @param offset The offset to apply on the indices.
		 *
		 * @return The offseted new IndexStream.
		 *
		 */
		public static function cloneWithOffset(indexStream	: IndexStream,
											   offset 		: uint 			= 0) : IndexStream
		{
			if (!(indexStream.usage & StreamUsage.READ))
			{
				throw new Error('Unable to read from index stream: stream usage is not set to StreamUsage.READ.');
			}

			var newData		: ByteArray	= new ByteArray();
			newData.endian 				= Endian.LITTLE_ENDIAN;

			var oldData		: ByteArray	= indexStream._data;
			oldData.position = 0;
			while (oldData.bytesAvailable)
			{
				var index	: uint		= oldData.readUnsignedShort() + offset;
				newData.writeShort(index);
			}
			newData.position = 0;

			return new IndexStream(indexStream.usage, newData);
		}

		private static function checkReadUsage(stream : IndexStream) : void
		{
			if (!(stream._usage & StreamUsage.READ))
				throw new Error(
					'Unable to read from vertex stream: stream usage is not set to StreamUsage.READ.'
				);
		}

		private static function checkWriteUsage(stream : IndexStream) : void
		{
			if (!(stream._usage & StreamUsage.WRITE))
				throw new Error(
					'Unable to write in vertex stream: stream usage is not set to StreamUsage.WRITE.'
				);
		}

		public static function dummyData(size 	: uint,
										 offset : uint = 0) : ByteArray
		{
			var indices : ByteArray = new ByteArray();

			indices.endian = Endian.LITTLE_ENDIAN;
			for (var i : int = 0; i < size; ++i)
				indices.writeShort(i + offset);
			indices.position = 0;

			return indices;
		}

		public static function fromVector(usage : uint, data : Vector.<uint>) : IndexStream
		{
			var stream 		: IndexStream 	= new IndexStream(usage);
			var numIndices 	: uint 			= data.length;

			for (var i : uint = 0; i < numIndices; ++i)
				stream._data.writeShort(data[i]);
			stream._data.position = 0;
			stream._length = numIndices;

			return stream;
		}
	}
}

Fix delete triangle function

Fix delete triangle function


package flash.display {
  import Object;
  import flash.display.IBitmapDrawable;
  import flash.geom.Rectangle;
  import flash.geom.Matrix;
  import flash.geom.Point;
  import flash.filters.BitmapFilter;
  import flash.utils.ByteArray;
  import flash.geom.ColorTransform;
  import flash.geom.Rectangle;
  public class BitmapData implements IBitmapDrawable {
    public function BitmapData(width:int, height:int, transparent:Boolean = true, fillColor:uint = 4294967295) {}
    public native function clone():BitmapData;
    public native function get width():int;
    public native function get height():int;
    public native function get transparent():Boolean;
    public function get rect():Rectangle { notImplemented("rect"); }
    public native function getPixel(x:int, y:int):uint;
    public native function getPixel32(x:int, y:int):uint;
    public native function setPixel(x:int, y:int, color:uint):void;
    public native function setPixel32(x:int, y:int, color:uint):void;
    public native function applyFilter(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, filter:BitmapFilter):void;
    public native function colorTransform(rect:Rectangle, colorTransform:ColorTransform):void;
    public native function compare(otherBitmapData:BitmapData):Object;
    public native function copyChannel(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, sourceChannel:uint, destChannel:uint):void;
    public native function copyPixels(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, alphaBitmapData:BitmapData = null, alphaPoint:Point = null, mergeAlpha:Boolean = false):void;
    public native function dispose():void;
    public native function draw(source:IBitmapDrawable, matrix:Matrix = null, colorTransform:ColorTransform = null, blendMode:String = null, clipRect:Rectangle = null, smoothing:Boolean = false):void;
    public native function drawWithQuality(source:IBitmapDrawable, matrix:Matrix = null, colorTransform:ColorTransform = null, blendMode:String = null, clipRect:Rectangle = null, smoothing:Boolean = false, quality:String = null):void;
    public native function fillRect(rect:Rectangle, color:uint):void;
    public native function floodFill(x:int, y:int, color:uint):void;
    public native function generateFilterRect(sourceRect:Rectangle, filter:BitmapFilter):Rectangle;
    public native function getColorBoundsRect(mask:uint, color:uint, findColor:Boolean = true):Rectangle;
    public native function getPixels(rect:Rectangle):ByteArray;
    public native function copyPixelsToByteArray(rect:Rectangle, data:ByteArray):void;
    public native function getVector(rect:Rectangle):Vector;
    public native function hitTest(firstPoint:Point, firstAlphaThreshold:uint, secondObject:Object, secondBitmapDataPoint:Point = null, secondAlphaThreshold:uint = 1):Boolean;
    public native function merge(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, redMultiplier:uint, greenMultiplier:uint, blueMultiplier:uint, alphaMultiplier:uint):void;
    public native function noise(randomSeed:int, low:uint = 0, high:uint = 255, channelOptions:uint = 7, grayScale:Boolean = false):void;
    public native function paletteMap(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, redArray:Array = null, greenArray:Array = null, blueArray:Array = null, alphaArray:Array = null):void;
    public native function perlinNoise(baseX:Number, baseY:Number, numOctaves:uint, randomSeed:int, stitch:Boolean, fractalNoise:Boolean, channelOptions:uint = 7, grayScale:Boolean = false, offsets:Array = null):void;
    public native function pixelDissolve(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, randomSeed:int = 0, numPixels:int = 0, fillColor:uint = 0):int;
    public native function scroll(x:int, y:int):void;
    public native function setPixels(rect:Rectangle, inputByteArray:ByteArray):void;
    public native function setVector(rect:Rectangle, inputVector:Vector):void;
    public native function threshold(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, operation:String, threshold:uint, color:uint = 0, mask:uint = 4294967295, copySource:Boolean = false):uint;
    public native function lock():void;
    public native function unlock(changeRect:Rectangle = null):void;
    public native function histogram(hRect:Rectangle = null):Vector;
    public native function encode(rect:Rectangle, compressor:Object, byteArray:ByteArray = null):ByteArray;
  }
}

/*
 * Copyright 2014 Mozilla Foundation
 *
 * 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
 *totalMemory
 *     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 flash.display {
  import Object;
  import flash.display.IBitmapDrawable;
  import flash.geom.Rectangle;
  import flash.geom.Matrix;
  import flash.geom.Point;
  import flash.filters.BitmapFilter;
  import flash.utils.ByteArray;
  import flash.geom.ColorTransform;
  import flash.geom.Rectangle;

  [native(cls='BitmapData')]
  public class BitmapData implements IBitmapDrawable {
    public function BitmapData(width:int, height:int, transparent:Boolean = true, fillColor:uint = 4294967295) {}
    public native function clone():BitmapData;
    public native function get width():int;
    public native function get height():int;
    public native function get transparent():Boolean;
    public function get rect():Rectangle { return new Rectangle (0, 0, width(), height()); }
    public native function getPixel(x:int, y:int):uint;
    public native function getPixel32(x:int, y:int):uint;
    public native function setPixel(x:int, y:int, color:uint):void;
    public native function setPixel32(x:int, y:int, color:uint):void;
    public native function applyFilter(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, filter:BitmapFilter):void;
    public native function colorTransform(rect:Rectangle, colorTransform:ColorTransform):void;
    public native function compare(otherBitmapData:BitmapData):Object;
    public native function copyChannel(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, sourceChannel:uint, destChannel:uint):void;
    public native function copyPixels(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, alphaBitmapData:BitmapData = null, alphaPoint:Point = null, mergeAlpha:Boolean = false):void;
    public native function dispose():void;
    public native function draw(source:IBitmapDrawable, matrix:Matrix = null, colorTransform:ColorTransform = null, blendMode:String = null, clipRect:Rectangle = null, smoothing:Boolean = false):void;
    public native function drawWithQuality(source:IBitmapDrawable, matrix:Matrix = null, colorTransform:ColorTransform = null, blendMode:String = null, clipRect:Rectangle = null, smoothing:Boolean = false, quality:String = null):void;
    public native function fillRect(rect:Rectangle, color:uint):void;
    public native function floodFill(x:int, y:int, color:uint):void;
    public native function generateFilterRect(sourceRect:Rectangle, filter:BitmapFilter):Rectangle;
    public native function getColorBoundsRect(mask:uint, color:uint, findColor:Boolean = true):Rectangle;
    public native function getPixels(rect:Rectangle):ByteArray;
    public native function copyPixelsToByteArray(rect:Rectangle, data:ByteArray):void;
    public native function getVector(rect:Rectangle):Vector;
    public native function hitTest(firstPoint:Point, firstAlphaThreshold:uint, secondObject:Object, secondBitmapDataPoint:Point = null, secondAlphaThreshold:uint = 1):Boolean;
    public native function merge(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, redMultiplier:uint, greenMultiplier:uint, blueMultiplier:uint, alphaMultiplier:uint):void;
    public native function noise(randomSeed:int, low:uint = 0, high:uint = 255, channelOptions:uint = 7, grayScale:Boolean = false):void;
    public native function paletteMap(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, redArray:Array = null, greenArray:Array = null, blueArray:Array = null, alphaArray:Array = null):void;
    public native function perlinNoise(baseX:Number, baseY:Number, numOctaves:uint, randomSeed:int, stitch:Boolean, fractalNoise:Boolean, channelOptions:uint = 7, grayScale:Boolean = false, offsets:Array = null):void;
    public native function pixelDissolve(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, randomSeed:int = 0, numPixels:int = 0, fillColor:uint = 0):int;
    public native function scroll(x:int, y:int):void;
    public native function setPixels(rect:Rectangle, inputByteArray:ByteArray):void;
    public native function setVector(rect:Rectangle, inputVector:Vector):void;
    public native function threshold(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, operation:String, threshold:uint, color:uint = 0, mask:uint = 4294967295, copySource:Boolean = false):uint;
    public native function lock():void;
    public native function unlock(changeRect:Rectangle = null):void;
    public native function histogram(hRect:Rectangle = null):Vector;
    public native function encode(rect:Rectangle, compressor:Object, byteArray:ByteArray = null):ByteArray;
  }
}

Implement BitmapData()

Implement BitmapData()


package org.concord.sparks {

    import flash.events.ErrorEvent;
    import flash.external.ExternalInterface;

    import org.concord.sparks.Activity;

    // Interface with external JavaScript
    public class JavaScript {

        private static var _instance:JavaScript;
        private static var _fromWithin:Boolean = false;

        private var activity:Activity;

        public static function instance() {
          if (! _instance) {
            _fromWithin = true;
            _instance = new JavaScript();
            _fromWithin = false;
          }
          return _instance;
        }

        public function JavaScript() {
            if (! _fromWithin) {
              throw new Error("Constructor JavaScript() is not supposed to be called from outside.");
            }
            this.activity = activity;
            setupCallbacks();
        }

        public function setActivity(activity:Activity) {
            this.activity = activity;
        }

        public function call(func:String, ... values) {
            var args = values;
            args.unshift(func);
            ExternalInterface.call.apply(null, args);
        }

        public function sendEvent(name:String, ... values) {
            var time = String(new Date().valueOf());
            ExternalInterface.call('receiveEvent', name, values.join('|'), time);
        }

        private function setupCallbacks():void {
            ExternalInterface.addCallback("sendMessageToFlash",
                    getMessageFromJavaScript);
        }

        private function parseParameter(parm) {
            var tokens:Array = parm.split(':');
            switch (tokens[1]) {
                case "int":
                    return parseInt(tokens[0]);
                default: //string
                    return tokens[0];
            }
        }

        private function getMessageFromJavaScript(... Arguments):String {
            try {
                return activity.processMessageFromJavaScript(Arguments);
            }
            catch (e:ErrorEvent) {
                return 'flash_error|' + e.toString();
            }
            catch (e:Error) {
                return 'flash_error|' + e.name + '\n' + e.message + '\n' + e.getStackTrace();
            }
            return 'flash_error|this point should be unreachable';
        }
    }
}

package org.concord.sparks {

    import flash.events.ErrorEvent;
    import flash.external.ExternalInterface;

    import org.concord.sparks.Activity;

    // Interface with external JavaScript
    public class JavaScript {

        private static var _instance:JavaScript;
        private static var _fromWithin:Boolean = false;

        private var activity:Activity;

        public static function instance() {
          if (! _instance) {
            _fromWithin = true;
            _instance = new JavaScript();
            _fromWithin = false;
          }
          return _instance;
        }

        public function JavaScript() {
            if (! _fromWithin) {
              throw new Error("Constructor JavaScript() is not supposed to be called from outside.");
            }
            this.activity = activity;
            setupCallbacks();
        }

        public function setActivity(activity:Activity) {
            this.activity = activity;
        }

        public function call(func:String, ... values) {
            var args = values;
            args.unshift(func);
            if (ExternalInterface.available) {
                ExternalInterface.call.apply(null, args);
            }
            else {
                trace('ExternalInterface unavailable: tried to call ' + func + '(' + args + ')');
            }
        }

        public function sendEvent(name:String, ... values) {
            var time = String(new Date().valueOf());
            if (ExternalInterface.available) {
                ExternalInterface.call('receiveEvent', name, values.join('|'), time);
            }
            else {
                trace('ExternalInterface unavailable: tried to call receiveEvent(' + name + ', '  + values.join('|') + ', ' + time + ')');
            }
        }

        private function setupCallbacks():void {
            if (ExternalInterface.available) {
                ExternalInterface.addCallback("sendMessageToFlash",
                    getMessageFromJavaScript);
            }
        }

        private function parseParameter(parm) {
            var tokens:Array = parm.split(':');
            switch (tokens[1]) {
                case "int":
                    return parseInt(tokens[0]);
                default: //string
                    return tokens[0];
            }
        }

        private function getMessageFromJavaScript(... Arguments):String {
            try {
                return activity.processMessageFromJavaScript(Arguments);
            }
            catch (e:ErrorEvent) {
                return 'flash_error|' + e.toString();
            }
            catch (e:Error) {
                return 'flash_error|' + e.name + '\n' + e.message + '\n' + e.getStackTrace();
            }
            return 'flash_error|this point should be unreachable';
        }
    }
}

Check if ExternalInterface is available

Check if ExternalInterface is available


/*
 *  SteamConstants.as
 *  This file is part of FRESteamWorks.
 *
 *  Created by David ´Oldes´ Oliva on 3/29/12.
 *  Contributors: Ventero <http://github.com/Ventero>
 *  Copyright (c) 2012 Amanita Design. All rights reserved.
 *  Copyright (c) 2012-2013 Level Up Labs, LLC. All rights reserved.
 */

package com.amanitadesign.steam
{
	public class SteamConstants
	{
		/* response values */
		public static const RESPONSE_OK:int = 0;
		public static const RESPONSE_FAILED:int = 1;

		/* response types */
		public static const RESPONSE_OnUserStatsReceived:int = 0;
		public static const RESPONSE_OnUserStatsStored:int = 1;
		public static const RESPONSE_OnAchievementStored:int = 2;
		public static const RESPONSE_OnGameOverlayActivated:int = 3;
		public static const RESPONSE_OnFileShared:int = 4;
		public static const RESPONSE_OnUGCDownload:int = 5;
		public static const RESPONSE_OnPublishWorkshopFile:int = 6;
		public static const RESPONSE_OnDeletePublishedFile:int = 7;
		public static const RESPONSE_OnGetPublishedFileDetails:int = 8;
		public static const RESPONSE_OnEnumerateUserPublishedFiles:int = 9;
		public static const RESPONSE_OnEnumeratePublishedWorkshopFiles:int = 10;
		public static const RESPONSE_OnEnumerateUserSubscribedFiles:int = 11;
		public static const RESPONSE_OnEnumerateUserSharedWorkshopFiles:int = 12;
		public static const RESPONSE_OnEnumeratePublishedFilesByUserAction:int = 13;
		public static const RESPONSE_OnCommitPublishedFileUpdate:int = 14;
		public static const RESPONSE_OnSubscribePublishedFile:int = 15;
		public static const RESPONSE_OnUnsubscribePublishedFile:int = 16;
		public static const RESPONSE_OnGetPublishedItemVoteDetails:int = 17;
		public static const RESPONSE_OnUpdateUserPublishedItemVote:int = 18;
		public static const RESPONSE_OnSetUserPublishedFileAction:int = 19;
	}

}

/*
 *  SteamConstants.as
 *  This file is part of FRESteamWorks.
 *
 *  Created by David ´Oldes´ Oliva on 3/29/12.
 *  Contributors: Ventero <http://github.com/Ventero>
 *  Copyright (c) 2012 Amanita Design. All rights reserved.
 *  Copyright (c) 2012-2013 Level Up Labs, LLC. All rights reserved.
 */

package com.amanitadesign.steam
{
	public class SteamConstants
	{
		/* response values */
		public static const RESPONSE_OK:int = 0;
		public static const RESPONSE_FAILED:int = 1;

		/* response types */
		public static const RESPONSE_OnUserStatsReceived:int = 0;
		public static const RESPONSE_OnUserStatsStored:int = 1;
		public static const RESPONSE_OnAchievementStored:int = 2;
		public static const RESPONSE_OnGameOverlayActivated:int = 3;
		public static const RESPONSE_OnFileShared:int = 4;
		public static const RESPONSE_OnUGCDownload:int = 5;
		public static const RESPONSE_OnPublishWorkshopFile:int = 6;
		public static const RESPONSE_OnDeletePublishedFile:int = 7;
		public static const RESPONSE_OnGetPublishedFileDetails:int = 8;
		public static const RESPONSE_OnEnumerateUserPublishedFiles:int = 9;
		public static const RESPONSE_OnEnumeratePublishedWorkshopFiles:int = 10;
		public static const RESPONSE_OnEnumerateUserSubscribedFiles:int = 11;
		public static const RESPONSE_OnEnumerateUserSharedWorkshopFiles:int = 12;
		public static const RESPONSE_OnEnumeratePublishedFilesByUserAction:int = 13;
		public static const RESPONSE_OnCommitPublishedFileUpdate:int = 14;
		public static const RESPONSE_OnSubscribePublishedFile:int = 15;
		public static const RESPONSE_OnUnsubscribePublishedFile:int = 16;
		public static const RESPONSE_OnGetPublishedItemVoteDetails:int = 17;
		public static const RESPONSE_OnUpdateUserPublishedItemVote:int = 18;
		public static const RESPONSE_OnSetUserPublishedFileAction:int = 19;
		public static const RESPONSE_OnDLCInstalled:int = 20;
	}

}

Add OnDLCInstalled event id to SteamConstants


/*
 * Copyright 2008 Adobe Systems Inc., 2008 Google Inc.
 *
 * 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.
 *
 * Contributor(s):
 *   Zwetan Kjukov <zwetan@gmail.com>.
 *   Marc Alcaraz <ekameleon@gmail.com>.
 */

package com.google.analytics.utils
{
    import core.strings.userAgent;

    import com.google.analytics.core.ga_internal;
    import com.google.analytics.debug.DebugConfiguration;
    import com.google.analytics.external.HTMLDOM;

    import flash.system.Capabilities;
    import flash.system.Security;
    import flash.system.System;

    /**
     * Environment provides informations for the local environment.
     */
    public class Environment
    {
        private var _debug:DebugConfiguration;
        private var _dom:HTMLDOM;

        private var _protocol:Protocols;
        private var _appName:String;
        private var _appVersion:Version;
        private var _userAgent:String;
        private var _url:String;

        /**
         * Creates a new Environment instance.
         * @param url The URL of the SWF.
         * @param app The application name
         * @param version The application version
         * @param dom the HTMLDOM reference.
         */
        public function Environment( url:String = "", app:String = "", version:String = "",
                                     debug:DebugConfiguration = null, dom:HTMLDOM = null )
        {
            var v:Version;

            if( app == "" )
            {
                if( isAIR() )
                {
                    app = "AIR";
                }
                else
                {
                    app = "Flash";
                }
            }

            if( version == "" )
            {
                v = flashVersion;
            }
            else
            {
                v = Version.fromString( version );
            }

            _url        = url;
            _appName    = app;
            _appVersion = v;

            _debug      = debug;
            _dom        = dom;
        }

        /**
         * @private
         */
        private function _findProtocol():void
        {
            var p:Protocols = Protocols.none;

            if(_url != "")
            {
                // file://
                // http://
                // https://
                var URL:String = _url.toLowerCase();
                var test:String = URL.substr(0,5);

                switch( test )
                {
                    case "file:":
                    {
                        p = Protocols.file;
                        break;
                    }
                    case "http:":
                    {
                        p = Protocols.HTTP;
                        break;
                    }
                    case "https":
                    {
                        if(URL.charAt(5) == ":")
                        {
                            p = Protocols.HTTPS;
                        }
                        break;
                    }
                    default:
                    {
                        _protocol = Protocols.none;
                    }
                }
            }

            /*TODO:
              if _url is not found (if someone forgot to add the tracker to the display list)
              we could use the alternative to get the dom.location and find the protocol from that string
              off course only if we have access to ExternalInterface
            */

            var _proto:String = _dom.protocol;

            var proto:String = (p.toString()+":").toLowerCase();

            if( _proto && _proto != proto && _debug )
            {
                _debug.warning( "Protocol mismatch: SWF="+proto+", DOM="+_proto );
            }

            _protocol = p;
        }

        /**
         * Indicates the name of the application.
         */
        public function get appName():String
        {
            return _appName;
        }

        /**
         * @private
         */
        public function set appName( value:String ):void
        {
            _appName = value;
            _defineUserAgent();
        }

        /**
         * Indicates the version of the application.
         */
        public function get appVersion():Version
        {
            return _appVersion;
        }

        /**
         * @private
         */
        public function set appVersion( value:Version ):void
        {
            _appVersion = value;
            _defineUserAgent();

        }

        /**
         * Sets the stage reference value of the application.
         */
        ga_internal function set url( value:String ):void
        {
            _url = value;
        }

        /**
         * Indicates the location of swf.
         */
        public function get locationSWFPath():String
        {
            return _url;
        }

        /**
         * Indicates the referrer value.
         */
        public function get referrer():String
        {
            var _referrer:String = _dom.referrer;

            if( _referrer )
            {
                return _referrer;
            }

            if( protocol == Protocols.file )
            {
                return "localhost";
            }

            return "";
        }

        /**
         * Indicates the title of the document.
         */
        public function get documentTitle():String
        {
            var _title:String = _dom.title;

            if( _title )
            {
                return _title;
            }

            return "";
        }

        /**
         * Indicates the local domain name value.
         */
        public function get domainName():String
        {
            if( protocol == Protocols.HTTP ||
                protocol == Protocols.HTTPS )
            {
                var URL:String = _url.toLowerCase();
                var str:String;
                var end:int;

                if( protocol == Protocols.HTTP )
                {
                    str = URL.split( "http://" ).join( "" );
                }
                else if( protocol == Protocols.HTTPS )
                {
                    str = URL.split( "https://" ).join( "" );
                }

                end = str.indexOf( "/" );

                if( end > -1 )
                {
                    str = str.substring(0,end);
                }

                return str;
            }

            if( protocol == Protocols.file )
            {
                return "localhost";
            }

            return "";
        }

        /**
         * Indicates if the application is running in AIR.
         */
        public function isAIR():Boolean
        {
            //return (playerType == "Desktop") && (Security.sandboxType.toString() == "application");
            return Security.sandboxType == "application";
        }

        /**
         * Indicates if the SWF is embeded in an HTML page.
         * @return true if the SWF is embeded in an HTML page.
         */
        public function isInHTML():Boolean
        {
            return Capabilities.playerType == "PlugIn" ;
        }

        /**
         * Indicates the locationPath value.
         */
        public function get locationPath():String
        {
            var _pathname:String = _dom.pathname;

            if( _pathname )
            {
                return _pathname;
            }

            return "";
        }

        /**
         * Indicates the locationSearch value.
         */
        public function get locationSearch():String
        {
            var _search:String = _dom.search;

            if( _search )
            {
                return _search;
            }

            return "";
        }

        /**
         * Returns the flash version object representation of the application.
         *
This object contains the attributes major, minor, build and revision :

         *
Example :
         *

         * import com.google.analytics.utils.Environment ;
         *
         * var info:Environment = new Environment( "http://www.domain.com" ) ;
         * var version:Object = info.flashVersion ;
         *
         * trace( version.major    ) ; // 9
         * trace( version.minor    ) ; // 0
         * trace( version.build    ) ; // 124
         * trace( version.revision ) ; // 0
         *

         * @return the flash version object representation of the application.
         */
        public function get flashVersion():Version
        {
            var v:Version = Version.fromString( Capabilities.version.split( " " )[1], "," ) ;
            return v ;
        }

        /**
         * Returns the language string as a lowercase two-letter language code from ISO 639-1.
         * @see Capabilities.language
         */
        public function get language():String
        {
            var _lang:String = _dom.language;
            var lang:String  = Capabilities.language;

            if( _lang )
            {
                if( (_lang.length > lang.length) &&
                    (_lang.substr(0,lang.length) == lang) )
                {
                    lang = _lang;
                }
            }

            return lang;
        }

        /**
         * Returns the internal character set used by the flash player
         *
Logic : by default flash player use unicode internally so we return UTF-8.

         *
If the player use the system code page then we try to return the char set of the browser.

         * @return the internal character set used by the flash player
         */
        public function get languageEncoding():String
        {
            if( System.useCodePage )
            {
                var _charset:String = _dom.characterSet;
                if( _charset )
                {
                    return _charset;
                }
                return "-"; //not found
            }
            //default
            return "UTF-8" ;
        }

        /**
         * Returns the operating system string.
         *
Note: The flash documentation indicate those strings

         *
"Windows XP"

         *
"Windows 2000"

         *
"Windows NT"

         *
"Windows 98/ME"

         *
"Windows 95"

         *
"Windows CE" (available only in Flash Player SDK, not in the desktop version)

         *
"Linux"

         *
"MacOS"

         *
Other strings we can obtain ( not documented : "Mac OS 10.5.4" , "Windows Vista")

         * @return the operating system string.
         * @see Capabilities.os
         */
        public function get operatingSystem():String
        {
            return Capabilities.os ;
        }

        /**
         * Returns the player type.
         *
Note : The flash documentation indicate those strings.

         *
"ActiveX"    : for the Flash Player ActiveX control used by Microsoft Internet Explorer.

         *
"Desktop"    : for the Adobe AIR runtime (except for SWF content loaded by an HTML page, which has Capabilities.playerType set to "PlugIn").

         *
"External"   : for the external Flash Player "PlugIn" for the Flash Player browser plug-in (and for SWF content loaded by an HTML page in an AIR application).

         *
"StandAlone" : for the stand-alone Flash Player.

         * @return the player type.
         * @see Capabilities.playerType
         */
        public function get playerType():String
        {
            return Capabilities.playerType;
        }

        /**
         * Returns the platform, can be "Windows", "Macintosh" or "Linux"
         * @see Capabilities.manufacturer
         */
        public function get platform():String
        {
            var p:String = Capabilities.manufacturer;
            return p.split( "Adobe " )[1];
        }

        /**
         * Indicates the Protocols object of this local info.
         */
        public function get protocol():Protocols
        {
            if(!_protocol)
            {
                _findProtocol();
            }

            return _protocol;
        }

        /**
         * Indicates the height of the screen.
         * @see Capabilities.screenResolutionY
         */
        public function get screenHeight():Number
        {
            return Capabilities.screenResolutionY;
        }

        /**
         * Indicates the width of the screen.
         * @see Capabilities.screenResolutionX
         */
        public function get screenWidth():Number
        {
            return Capabilities.screenResolutionX;
        }

        /**
         * In AIR we can use flash.display.Screen to directly get the colorDepth property
         * in flash player we can only access screenColor in flash.system.Capabilities.
         *
Some ref : http://en.wikipedia.org/wiki/Color_depth

         *
"color" -> 16-bit or 24-bit or 32-bit

         *
"gray"  -> 2-bit

         *
"bw"    -> 1-bit

         */
        public function get screenColorDepth():String
        {
            var color:String;
            switch( Capabilities.screenColor )
            {
                case "bw":
                {
                    color = "1";
                    break;
                }
                case "gray":
                {
                    color = "2";
                    break;
                }
                /* note:
                   as we have no way to know if
                   we are in 16-bit, 24-bit or 32-bit
                   we gives 24-bit by default
                */
                case "color" :
                default      :
                {
                    color = "24" ;
                }
            }

            var _colorDepth:String = _dom.colorDepth;

            if( _colorDepth )
            {
                color = _colorDepth;
            }

            return color;
        }

        private function _defineUserAgent():void
        {
            _userAgent = core.strings.userAgent( appName + "/" + appVersion.toString(4) );
        }

        /**
         * Defines a custom user agent.
         *
For case where the user would want to define its own application name and version
         * it is possible to change appName and appVersion which are in sync with
         * applicationProduct and applicationVersion properties.

         */
        public function get userAgent():String
        {
            if( !_userAgent )
            {
                 _defineUserAgent();
            }

            return _userAgent;
        }

        /**
         * @private
         */
        public function set userAgent( custom:String ):void
        {
            _userAgent = custom;
        }

    }
}

fix comment for asdoc

fix comment for asdoc


package aerys.minko.scene.controller
{
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.KeyboardManager;
	import aerys.minko.type.MouseManager;

	import flash.display.BitmapData;

	public class ScriptController extends EnterFrameController
	{
		private var _lastTime		: Number;
		private var _deltaTime		: Number;
		private var _currentTarget	: ISceneNode;
		private var _viewport		: Viewport;
		private var _scene			: Scene;

		protected function get deltaTime() : Number
		{
			return _deltaTime;
		}

		protected function get keyboard() : KeyboardManager
		{
			return _viewport.keyboardManager;
		}

		protected function get mouse() : MouseManager
		{
			return _viewport.mouseManager;
		}

		protected function get viewport() : Viewport
		{
			return _viewport;
		}

		protected function get scene() : Scene
		{
			return _scene;
		}

		public function ScriptController(targetType	: Class = null)
		{
			super(targetType);

			initialize();
		}

		private function initialize() : void
		{
			var numTargets : uint = this.numTargets;

			for (var i : uint = 0; i < numTargets; ++i)
				start(getTarget(i));
		}

		override protected function sceneEnterFrameHandler(scene		: Scene,
														   viewport		: Viewport,
														   destination	: BitmapData,
														   time			: Number) : void
		{
			_viewport = viewport;
			_deltaTime = _lastTime - time;
			_lastTime = time;
			_scene = scene;

			var numTargets : uint = this.numTargets;

			for (var i : uint = 0; i < numTargets; ++i)
				update(getTarget(i));
		}

		override protected function targetRemovedFromSceneHandler(target	: ISceneNode,
																  scene		: Scene) : void
		{
			super.targetRemovedFromSceneHandler(target, scene);

			var numTargets : uint = this.numTargets;

			for (var i : uint = 0; i < numTargets; ++i)
				stop(getTarget(i));
		}

		protected function start(target : ISceneNode) : void
		{
			// nothing
		}

		protected function update(target : ISceneNode) : void
		{
			// nothing
		}

		protected function stop(target : ISceneNode) : void
		{
			// nothing
		}
	}
}

package aerys.minko.scene.controller
{
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.KeyboardManager;
	import aerys.minko.type.MouseManager;

	import flash.display.BitmapData;

	public class ScriptController extends EnterFrameController
	{
		private var _lastTime		: Number;
		private var _deltaTime		: Number;
		private var _currentTarget	: ISceneNode;
		private var _viewport		: Viewport;

		protected function get deltaTime() : Number
		{
			return _deltaTime;
		}

		protected function get keyboard() : KeyboardManager
		{
			return _viewport.keyboardManager;
		}

		protected function get mouse() : MouseManager
		{
			return _viewport.mouseManager;
		}

		protected function get viewport() : Viewport
		{
			return _viewport;
		}

		public function ScriptController(targetType	: Class = null)
		{
			super(targetType);

			var numTargets : uint = this.numTargets;

			for (var i : uint = 0; i < numTargets; ++i)
				initialize(getTarget(i));
		}

		override protected function sceneEnterFrameHandler(scene		: Scene,
														   viewport		: Viewport,
														   destination	: BitmapData,
														   time			: Number) : void
		{
			_viewport = viewport;
			_deltaTime = _lastTime - time;
			_lastTime = time;

			var numTargets : uint = this.numTargets;

			for (var i : uint = 0; i < numTargets; ++i)
            {
                var target : ISceneNode = getTarget(i);

                if (target.scene)
    				update(target);
            }
		}

        override protected function targetAddedToSceneHandler(target    : ISceneNode,
                                                              scene     : Scene) : void
        {
            super.targetAddedToSceneHandler(target, scene);

            var numTargets : uint = this.numTargets;

            for (var i : uint = 0; i < numTargets; ++i)
                start(getTarget(i));
        }

		override protected function targetRemovedFromSceneHandler(target	: ISceneNode,
																  scene		: Scene) : void
		{
			super.targetRemovedFromSceneHandler(target, scene);

			var numTargets : uint = this.numTargets;

			for (var i : uint = 0; i < numTargets; ++i)
				stop(getTarget(i));
		}

        protected function initialize(target : ISceneNode) : void
        {
            // nothing
        }

		protected function start(target : ISceneNode) : void
		{
			// nothing
		}

		protected function update(target : ISceneNode) : void
		{
			// nothing
		}

		protected function stop(target : ISceneNode) : void
		{
			// nothing
		}
	}
}

add ScriptController.initialize() fix ScriptController.start() that was never actually called


//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/vilya/
//
// 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.ezgame.client {

import flash.display.DisplayObject;

import flash.geom.Rectangle;

import mx.core.mx_internal;
import mx.core.IFlexDisplayObject;
import mx.core.IInvalidating;

import mx.containers.VBox;

import mx.managers.IFocusManagerComponent;

import mx.skins.ProgrammaticSkin;

import com.threerings.flash.MediaContainer;

// TODO: there are focus issues in here that need dealing with.
//
// 1) The focus rectangle is drawn at the wrong size in scrolling games
// 2) We can get focus without having the pink focus rectangle...
// 3) When the mouse leaves the flash player and returns, this
//    damn thing doesn't seem to grip onto the focus.
//
public class GameContainer extends VBox
    implements IFocusManagerComponent
{
    public function GameContainer (url :String)
    {
        rawChildren.addChild(_game = new MediaContainer(url));

        tabEnabled = true; // turned off by Container
//        focusRect = true; // we need the focus rect
    }

    public function getMediaContainer () :MediaContainer
    {
        return _game;
    }

//    override public function setFocus () :void
//    {
//        if (stage) {
//            try {
//                stage.focus = this;
//            } catch (e :Error) {
//                trace("Apparently, this might happen: " + e)
//            }
//        }
//    }

    override protected function adjustFocusRect (obj :DisplayObject = null) :void
    {
        super.adjustFocusRect(obj);

        // TODO: this is probably all wrong
        var focusObj :IFlexDisplayObject =
            IFlexDisplayObject(mx_internal::getFocusObject());
        if (focusObj) {
            var r :Rectangle = transform.pixelBounds;
            focusObj.setActualSize(r.width - 2, r.height - 2);
            focusObj.move(0, 0);

            if (focusObj is IInvalidating) {
                IInvalidating(focusObj).validateNow();

            } else if (focusObj is ProgrammaticSkin) {
                ProgrammaticSkin(focusObj).validateNow();
            }
        }
    }

    protected var _game :MediaContainer;
}
}

//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/vilya/
//
// 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.ezgame.client {

import flash.display.DisplayObject;

import flash.geom.Rectangle;

import flash.text.TextField;

import mx.core.mx_internal;
import mx.core.IFlexDisplayObject;
import mx.core.IInvalidating;

import mx.containers.VBox;

import com.threerings.flash.MediaContainer;

// TODO: there are focus issues in here that need dealing with.
//
// 1) The focus rectangle is drawn at the wrong size in scrolling games
// 2) We can get focus without having the pink focus rectangle...
// 3) When the mouse leaves the flash player and returns, this
//    damn thing doesn't seem to grip onto the focus.
//
public class GameContainer extends VBox
{
    public function GameContainer (url :String)
    {
        rawChildren.addChild(_keyGrabber = new TextField());
        _keyGrabber.selectable = false;
        rawChildren.addChild(_game = new MediaContainer(url));

        tabEnabled = true; // turned off by Container
//        focusRect = true; // we need the focus rect
    }

    public function getMediaContainer () :MediaContainer
    {
        return _game;
    }

    override public function setActualSize (w :Number, h :Number) :void
    {
        super.setActualSize(w, h);
        _keyGrabber.width = w;
        _keyGrabber.height = h;
    }

//    override public function setFocus () :void
//    {
//        if (stage) {
//            try {
//                stage.focus = this;
//            } catch (e :Error) {
//                trace("Apparently, this might happen: " + e)
//            }
//        }
//    }

//    override protected function adjustFocusRect (obj :DisplayObject = null) :void
//    {
//        super.adjustFocusRect(obj);
//
//        // TODO: this is probably all wrong
//        var focusObj :IFlexDisplayObject =
//            IFlexDisplayObject(mx_internal::getFocusObject());
//        if (focusObj) {
//            var r :Rectangle = transform.pixelBounds;
//            focusObj.setActualSize(r.width - 2, r.height - 2);
//            focusObj.move(0, 0);
//
//            if (focusObj is IInvalidating) {
//                IInvalidating(focusObj).validateNow();
//
//            } else if (focusObj is ProgrammaticSkin) {
//                ProgrammaticSkin(focusObj).validateNow();
//            }
//        }
//    }

    protected var _game :MediaContainer;

    protected var _keyGrabber :TextField;
}
}

Use the TextField hack ourselves.
- Flash seems to sketch out when key events go elsewhere
- Whirled now does the handy thing of re-routing 'word' keypresses
  to the ChatControl, unless a TextField has focus. We want to automatically
  let games get any and all keypresses...
- The damn focus stuff is broken anyway

Untested, Nathan will test.

git-svn-id: a3e1eb16dde062992de22c830ed8045c8013209a@231 c613c5cb-e716-0410-b11b-feb51c14d237