package aerys.minko.scene.visitor.data
{
	import aerys.minko.type.math.Matrix4x4;

	public final class LocalData
	{
		private static const _SCREEN_TO_UV : Matrix4x4 = new Matrix4x4(
			0.5,		0.0,		0.0,	0.0,
			0.0, 		-0.5,		0.0,	0.0,
			0.0,		0.0,		1.0,	0.0,
			0.5, 		0.5,		0.0, 	1.0
		);

		public static const VIEW						: String = 'view';
		public static const WORLD						: String = 'world';
		public static const PROJECTION					: String = 'projection';

		public static const WORLD_INVERSE				: String = 'worldInverse';
		public static const VIEW_INVERSE				: String = 'viewInverse';

		public static const SCREEN_TO_UV				: String = 'screenToUv';

		public static const LOCAL_TO_VIEW				: String = 'localToView';
		public static const LOCAL_TO_SCREEN				: String = 'localToScreen';
		public static const LOCAL_TO_UV					: String = 'localToUv';

		protected var _world							: Matrix4x4;
		protected var _view								: Matrix4x4;
		protected var _projection						: Matrix4x4;

		protected var _viewInverse						: Matrix4x4;
		protected var _viewInverse_viewVersion			: uint;

		protected var _worldInverse						: Matrix4x4;
		protected var _worldInverse_worldVersion		: uint;

		protected var _localToView						: Matrix4x4;
		protected var _localToView_viewVersion			: uint;
		protected var _localToView_worldVersion			: uint;

		protected var _localToScreen					: Matrix4x4;
		protected var _localToScreen_localToViewVersion : uint;
		protected var _localToScreen_projectionVersion	: uint;

		protected var _localToUv						: Matrix4x4;
		protected var _localToUv_localToScreenVersion	: uint;

		public function get view() : Matrix4x4
		{
			return _view;
		}

		public function get world() : Matrix4x4
		{
			return _world;
		}

		public function get projection() : Matrix4x4
		{
			return _projection;
		}

		public function get worldInverse() : Matrix4x4
		{
			var worldMatrix : Matrix4x4 = world;

			if (_worldInverse_worldVersion != worldMatrix.version)
			{
				_worldInverse = Matrix4x4.invert(worldMatrix, _worldInverse);
				_worldInverse_worldVersion = worldMatrix.version;
			}

			return _worldInverse;
		}

		public function get viewInverse() : Matrix4x4
		{
			var viewMatrix : Matrix4x4 = view;

			if (_viewInverse_viewVersion != viewMatrix.version)
			{
				_viewInverse = Matrix4x4.invert(viewMatrix, _viewInverse);
				_viewInverse_viewVersion = viewMatrix.version;
			}

			return _viewInverse;
		}

		public function get localToView() : Matrix4x4
		{
			var worldMatrix	: Matrix4x4 = world;
			var viewMatrix	: Matrix4x4 = view;

			if (_localToView_worldVersion != worldMatrix.version ||
				_localToView_viewVersion != viewMatrix.version)
			{
				_localToView = Matrix4x4.multiply(viewMatrix, worldMatrix, _localToView);
				_localToView_worldVersion	= worldMatrix.version;
				_localToView_viewVersion	= viewMatrix.version;
			}

			return _localToView;
		}

		public function get localToScreen() : Matrix4x4
		{
			var localToViewMatrix	: Matrix4x4 = localToView;
			var projectionMatrix	: Matrix4x4 = projection;

			if (_localToScreen_localToViewVersion != localToViewMatrix.version ||
				_localToScreen_projectionVersion != projectionMatrix.version)
			{
				_localToScreen = Matrix4x4.multiply(projectionMatrix, localToViewMatrix, _localToScreen);
				_localToScreen_localToViewVersion	= localToViewMatrix.version;
				_localToScreen_projectionVersion	= projectionMatrix.version;
			}

			return _localToScreen;
		}

		public function get screentoUv() : Matrix4x4
		{
			return _SCREEN_TO_UV;
		}

		public function get localToUv() : Matrix4x4
		{
			var localToScreenMatrix : Matrix4x4 = localToScreen;
			var screenToUvMatrix	: Matrix4x4 = screentoUv;

			if (_localToUv_localToScreenVersion != localToScreenMatrix.version)
			{
				_localToUv = Matrix4x4.multiply(screenToUvMatrix, localToScreenMatrix);
				_localToUv_localToScreenVersion = localToScreenMatrix.version;
			}

			return _localToUv;
		}

		public function set world(value : Matrix4x4) : void
		{
			Matrix4x4.copy(value, _world);
		}

		public function set view(value : Matrix4x4) : void
		{
			Matrix4x4.copy(value, _view);
		}

		public function set projection(value : Matrix4x4) : void
		{
			Matrix4x4.copy(value, _projection);
		}

		public function LocalData()
		{
			_world								= new Matrix4x4();
			_view								= new Matrix4x4();
			_projection							= new Matrix4x4();

			reset();
		}

		public function reset() : void
		{
			_viewInverse_viewVersion			= uint.MAX_VALUE;
			_worldInverse_worldVersion			= uint.MAX_VALUE;
			_localToView_viewVersion			= uint.MAX_VALUE;
			_localToView_worldVersion			= uint.MAX_VALUE;
			_localToScreen_localToViewVersion	= uint.MAX_VALUE;
			_localToScreen_projectionVersion	= uint.MAX_VALUE;
		}
	}
}

package aerys.minko.scene.visitor.data
{
	import aerys.minko.type.math.Matrix4x4;

	public final class LocalData
	{
		private static const _SCREEN_TO_UV : Matrix4x4 = new Matrix4x4(
			0.5,		0.0,		0.0,	0.0,
			0.0, 		-0.5,		0.0,	0.0,
			0.0,		0.0,		1.0,	0.0,
			0.5, 		0.5,		0.0, 	1.0
		);

		public static const VIEW						: String = 'view';
		public static const WORLD						: String = 'world';
		public static const PROJECTION					: String = 'projection';

		public static const WORLD_INVERSE				: String = 'worldInverse';
		public static const VIEW_INVERSE				: String = 'viewInverse';

		public static const SCREEN_TO_UV				: String = 'screenToUv';

		public static const LOCAL_TO_VIEW				: String = 'localToView';
		public static const LOCAL_TO_SCREEN				: String = 'localToScreen';
		public static const LOCAL_TO_UV					: String = 'localToUv';

		public static const GLOBAL_TO_SCREEN			: String = 'globalToScreen';

		protected var _world							: Matrix4x4;
		protected var _view								: Matrix4x4;
		protected var _projection						: Matrix4x4;

		protected var _viewInverse						: Matrix4x4;
		protected var _viewInverse_viewVersion			: uint;

		protected var _worldInverse						: Matrix4x4;
		protected var _worldInverse_worldVersion		: uint;

		protected var _localToView						: Matrix4x4;
		protected var _localToView_viewVersion			: uint;
		protected var _localToView_worldVersion			: uint;

		protected var _localToScreen					: Matrix4x4;
		protected var _localToScreen_localToViewVersion : uint;
		protected var _localToScreen_projectionVersion	: uint;

		protected var _localToUv						: Matrix4x4;
		protected var _localToUv_localToScreenVersion	: uint;

		protected var _globalToScreen					: Matrix4x4;
		protected var _globalToScreen_viewVersion		: uint;
		protected var _globalToScreen_projectionVersion	: uint;

		public function get view() : Matrix4x4
		{
			return _view;
		}

		public function get world() : Matrix4x4
		{
			return _world;
		}

		public function get projection() : Matrix4x4
		{
			return _projection;
		}

		public function get worldInverse() : Matrix4x4
		{
			var worldMatrix : Matrix4x4 = world;

			if (_worldInverse_worldVersion != worldMatrix.version)
			{
				_worldInverse = Matrix4x4.invert(worldMatrix, _worldInverse);
				_worldInverse_worldVersion = worldMatrix.version;
			}

			return _worldInverse;
		}

		public function get viewInverse() : Matrix4x4
		{
			var viewMatrix : Matrix4x4 = view;

			if (_viewInverse_viewVersion != viewMatrix.version)
			{
				_viewInverse = Matrix4x4.invert(viewMatrix, _viewInverse);
				_viewInverse_viewVersion = viewMatrix.version;
			}

			return _viewInverse;
		}

		public function get localToView() : Matrix4x4
		{
			var worldMatrix	: Matrix4x4 = world;
			var viewMatrix	: Matrix4x4 = view;

			if (_localToView_worldVersion != worldMatrix.version ||
				_localToView_viewVersion != viewMatrix.version)
			{
				_localToView = Matrix4x4.multiply(viewMatrix, worldMatrix, _localToView);
				_localToView_worldVersion	= worldMatrix.version;
				_localToView_viewVersion	= viewMatrix.version;
			}

			return _localToView;
		}

		public function get localToScreen() : Matrix4x4
		{
			var localToViewMatrix	: Matrix4x4 = localToView;
			var projectionMatrix	: Matrix4x4 = projection;

			if (_localToScreen_localToViewVersion != localToViewMatrix.version ||
				_localToScreen_projectionVersion != projectionMatrix.version)
			{
				_localToScreen = Matrix4x4.multiply(projectionMatrix, localToViewMatrix, _localToScreen);
				_localToScreen_localToViewVersion	= localToViewMatrix.version;
				_localToScreen_projectionVersion	= projectionMatrix.version;
			}

			return _localToScreen;
		}

		public function get screentoUv() : Matrix4x4
		{
			return _SCREEN_TO_UV;
		}

		public function get localToUv() : Matrix4x4
		{
			var localToScreenMatrix : Matrix4x4 = localToScreen;
			var screenToUvMatrix	: Matrix4x4 = screentoUv;

			if (_localToUv_localToScreenVersion != localToScreenMatrix.version)
			{
				_localToUv = Matrix4x4.multiply(screenToUvMatrix, localToScreenMatrix);
				_localToUv_localToScreenVersion = localToScreenMatrix.version;
			}

			return _localToUv;
		}

		public function get globalToScreen() : Matrix4x4
		{
			var projectionMatrix	: Matrix4x4 = projection;
			var viewMatrix			: Matrix4x4 = view;

			if (_globalToScreen_projectionVersion != projectionMatrix.version ||
				_globalToScreen_viewVersion != viewMatrix.version)
			{
				_globalToScreen = Matrix4x4.multiply(projectionMatrix, viewMatrix, _globalToScreen);
				_globalToScreen_projectionVersion	= projectionMatrix.version;
				_globalToScreen_viewVersion			= viewMatrix.version;
			}

			return _globalToScreen;
		}

		public function set world(value : Matrix4x4) : void
		{
			Matrix4x4.copy(value, _world);
		}

		public function set view(value : Matrix4x4) : void
		{
			Matrix4x4.copy(value, _view);
		}

		public function set projection(value : Matrix4x4) : void
		{
			Matrix4x4.copy(value, _projection);
		}

		public function LocalData()
		{
			_world								= new Matrix4x4();
			_view								= new Matrix4x4();
			_projection							= new Matrix4x4();

			reset();
		}

		public function reset() : void
		{
			_viewInverse_viewVersion			= uint.MAX_VALUE;
			_worldInverse_worldVersion			= uint.MAX_VALUE;
			_localToView_viewVersion			= uint.MAX_VALUE;
			_localToView_worldVersion			= uint.MAX_VALUE;
			_localToScreen_localToViewVersion	= uint.MAX_VALUE;
			_localToScreen_projectionVersion	= uint.MAX_VALUE;
			_globalToScreen_projectionVersion	= uint.MAX_VALUE;
			_globalToScreen_viewVersion			= uint.MAX_VALUE;
		}
	}
}

Add a globalToScreen matrix getter in LocalData


package io.github.jwhile.impetus
{
    import flash.display.Sprite;
    import flash.external.ExternalInterface;

    public class Impetus extends Sprite
    {
        private sounds:Vector.<ImpetusSound>;

        public function Impetus():void
        {
            sounds = new Vector.<ImpetusSound>();

            if(ExternalInterface.available)
            {
                ExternalInterface.addCallback('getSound', getSound);

                ExternalInterface.call("console.log", "Impetus loaded. (https://github.com/JWhile/Impetus)");
            }
        }

        public function getSound(url:String)
        {
            var len:int = sounds.length;

            for(var i:int = 0; i < len; i++)
            {
                if(sounds[i].getUrl() === url)
                {
                    return sounds[i];
                }
            }

            var s:ImpetusSound = new ImpetusSound();

            sounds.push(s);

            return s;
        }
    }
}

package io.github.jwhile.impetus
{
    import flash.display.Sprite;
    import flash.external.ExternalInterface;

    public class Impetus extends Sprite
    {
        private sounds:Vector.<ImpetusSound>;

        public function Impetus():void
        {
            this.sounds = new Vector.<ImpetusSound>();

            if(ExternalInterface.available)
            {
                ExternalInterface.addCallback('getSound', this.getSound);

                ExternalInterface.call("console.log", "Impetus loaded. (https://github.com/JWhile/Impetus)");
            }
        }

        public function getSound(url:String)
        {
            var len:int = this.sounds.length;

            for(var i:int = 0; i < len; i++)
            {
                if(this.sounds[i].getUrl() === url)
                {
                    return this.sounds[i];
                }
            }

            var s:ImpetusSound = new ImpetusSound(url);

            this.sounds.push(s);

            return s;
        }
    }
}

Use this keyword + Send sound url to ImpetusSound


package aerys.minko.scene.controller.camera
{
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.controller.EnterFrameController;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Vector4;

	import flash.display.BitmapData;
	import flash.events.IEventDispatcher;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.ui.Mouse;
	import flash.ui.MouseCursor;

	public class ArcBallController extends EnterFrameController
	{
		private static const TMP_MATRIX : Matrix4x4 = new Matrix4x4();
		private static const EPSILON	: Number	= 0.001;

		protected var _mousePosition	: Point		= new Point();

		private var _enabled			: Boolean	= true;

		private var _distance			: Number	= 1.;
		private var _yaw				: Number	= 0;
		private var _pitch				: Number	= 0;
		private var _update				: Boolean	= true;

		private var _position			: Vector4	= new Vector4(0, 0, 0, 1);
		private var _lookAt				: Vector4	= new Vector4(0, 0, 0, 1);
		private var _up					: Vector4	= new Vector4(0, 1, 0, 1);

		private var _minDistance		: Number	= 0.1;
		private var _maxDistance		: Number	= 1000;

		private var _distanceStep		: Number	= 1;
		private var _yawStep			: Number	= 0.01;
		private var _pitchStep			: Number	= 0.01;

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

		protected function set update(value : Boolean) : void
		{
			_update = value;
		}

		/**
		 * Distance between look at point and target
		 */
		public function get distance() : Number
		{
			return _distance;
		}
		public function set distance(value : Number) : void
		{
			_distance = value;
			_update = true;
		}

		/**
		 * Horizontal rotation angle (in radians)
		 */
		public function get yaw() : Number
		{
			return _yaw;
		}
		public function set yaw(value : Number) : void
		{
			_yaw = value;
			_update = true;
		}

		/**
		 * Vertical rotation angle (in radians)
		 */
		public function get pitch() : Number
		{
			return _pitch;
		}
		public function set pitch(value : Number)	: void
		{
			_pitch = value;
			_update = true;
		}

		/**
		 * Position the targets will look at
		 */
		public function get lookAt() : Vector4
		{
			return _lookAt;
		}

		/**
		 * Up vector the targets will rotate around
		 */
		public function get up() : Vector4
		{
			return _up;
		}

		/**
		 * Minimum distance constrain between look at point and target.
		 */
		public function get minDistance() : Number
		{
			return _minDistance;
		}
		public function set minDistance(value : Number)	: void
		{
			_minDistance = value;
			_update = true;
		}

		/**
		 * Maximum distance constrain between look at point and target.
		 */
		public function get maxDistance() : Number
		{
			return _maxDistance;
		}
		public function set maxDistance(value : Number)	: void
		{
			_maxDistance = value;
			_update = true;
		}

		/**
		 * Distance step applied to the camera when the mouse wheel is used in meters/wheel rotation unit
		 */
		public function get distanceStep() : Number
		{
			return _distanceStep;
		}
		public function set distanceStep(value : Number) : void
		{
			_distanceStep = value;
		}

		/**
		 * Horizontal angular step applied to the camera when the mouse is moved in radian/pixel
		 */
		public function get yawStep() : Number
		{
			return _yawStep;
		}
		public function set yawStep(value : Number) : void
		{
			_yawStep = value;
		}

		/**
		 * Vertical angular step applied to the camera when the mouse is moved in radian/pixel
		 */
		public function get pitchStep() : Number
		{
			return _pitchStep;
		}

		public function set pitchStep(value : Number)	: void
		{
			_pitchStep = value;
		}

		public function ArcBallController()
		{
			super();

			_pitch = Math.PI * .5;
			_lookAt.changed.add(updateNextFrameHandler);
			_up.changed.add(updateNextFrameHandler);
		}

		public function bindDefaultControls(dispatcher : IEventDispatcher) : ArcBallController
		{
			dispatcher.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
			dispatcher.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);

			return this;
		}

		public function unbindDefaultControls(dispatcher : IEventDispatcher) : ArcBallController
		{
			dispatcher.removeEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
			dispatcher.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);

			return this;
		}

		override protected function targetAddedHandler(ctrl 	: EnterFrameController,
													   target 	: ISceneNode) : void
		{
			super.targetAddedHandler(ctrl, target);

			_update = true;
			updateTargets();
		}

		override protected function sceneEnterFrameHandler(scene		: Scene,
														   viewport		: Viewport,
														   destination	: BitmapData,
														   time			: Number) : void
		{
			updateTargets();
		}

		private function updateTargets() : void
		{
			if (_update && _enabled)
			{
				if (_distance < _minDistance)
					_distance = _minDistance;
				if (_distance > _maxDistance)
					_distance = _maxDistance;
				if (_pitch <= EPSILON)
					_pitch = EPSILON;
				if (_pitch > Math.PI - EPSILON)
					_pitch = Math.PI - EPSILON;

				_position.set(
					_lookAt.x + _distance * Math.cos(_yaw) * Math.sin(_pitch),
					_lookAt.y + _distance * Math.cos(_pitch),
					_lookAt.z + _distance * Math.sin(_yaw) * Math.sin(_pitch)
				);

				TMP_MATRIX.lookAt(_lookAt, _position, _up);

				var numTargets : uint = this.numTargets;
				for (var targetId : uint = 0; targetId < numTargets; ++targetId)
					getTarget(targetId).transform.copyFrom(TMP_MATRIX);

				_update = false;
			}
		}

		protected function mouseWheelHandler(e : MouseEvent) : void
		{
			_distance	-=	e.delta * _distanceStep;
			_update		=	true;
		}

		protected function mouseMoveHandler(e : MouseEvent) : void
		{
			// compute position
			if (e.buttonDown && _enabled)
			{
				_yaw	+= (_mousePosition.x - e.stageX) * _yawStep;
				_pitch	+= (_mousePosition.y - e.stageY) * _pitchStep;

				_update = true;
			}

			_mousePosition.setTo(e.stageX, e.stageY);
		}

		private function updateNextFrameHandler(vector : Vector4) : void
		{
			_update = true;
		}
	}
}

package aerys.minko.scene.controller.camera
{
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.controller.EnterFrameController;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Vector4;

	import flash.display.BitmapData;
	import flash.events.IEventDispatcher;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.ui.Mouse;
	import flash.ui.MouseCursor;

	public class ArcBallController extends EnterFrameController
	{
		private static const TMP_MATRIX : Matrix4x4 = new Matrix4x4();
		private static const EPSILON	: Number	= 0.001;

		protected var _mousePosition	: Point		= new Point();

		private var _enabled			: Boolean	= true;

		private var _distance			: Number	= 1.;
		private var _yaw				: Number	= 0;
		private var _pitch				: Number	= 0;
		private var _update				: Boolean	= true;

		private var _position			: Vector4	= new Vector4(0, 0, 0, 1);
		private var _lookAt				: Vector4	= new Vector4(0, 0, 0, 1);
		private var _up					: Vector4	= new Vector4(0, 1, 0, 1);

		private var _minDistance		: Number	= 1.;
		private var _maxDistance		: Number	= 1000;

		private var _distanceStep		: Number	= 1;
		private var _yawStep			: Number	= 0.01;
		private var _pitchStep			: Number	= 0.01;

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

		protected function set update(value : Boolean) : void
		{
			_update = value;
		}

		/**
		 * Distance between look at point and target
		 */
		public function get distance() : Number
		{
			return _distance;
		}
		public function set distance(value : Number) : void
		{
			_distance = value;
			_update = true;
		}

		/**
		 * Horizontal rotation angle (in radians)
		 */
		public function get yaw() : Number
		{
			return _yaw;
		}
		public function set yaw(value : Number) : void
		{
			_yaw = value;
			_update = true;
		}

		/**
		 * Vertical rotation angle (in radians)
		 */
		public function get pitch() : Number
		{
			return _pitch;
		}
		public function set pitch(value : Number)	: void
		{
			_pitch = value;
			_update = true;
		}

		/**
		 * Position the targets will look at
		 */
		public function get lookAt() : Vector4
		{
			return _lookAt;
		}

		/**
		 * Up vector the targets will rotate around
		 */
		public function get up() : Vector4
		{
			return _up;
		}

		/**
		 * Minimum distance constrain between look at point and target.
		 */
		public function get minDistance() : Number
		{
			return _minDistance;
		}
		public function set minDistance(value : Number)	: void
		{
			_minDistance = value;
			_update = true;
		}

		/**
		 * Maximum distance constrain between look at point and target.
		 */
		public function get maxDistance() : Number
		{
			return _maxDistance;
		}
		public function set maxDistance(value : Number)	: void
		{
			_maxDistance = value;
			_update = true;
		}

		/**
		 * Distance step applied to the camera when the mouse wheel is used in meters/wheel rotation unit
		 */
		public function get distanceStep() : Number
		{
			return _distanceStep;
		}
		public function set distanceStep(value : Number) : void
		{
			_distanceStep = value;
		}

		/**
		 * Horizontal angular step applied to the camera when the mouse is moved in radian/pixel
		 */
		public function get yawStep() : Number
		{
			return _yawStep;
		}
		public function set yawStep(value : Number) : void
		{
			_yawStep = value;
		}

		/**
		 * Vertical angular step applied to the camera when the mouse is moved in radian/pixel
		 */
		public function get pitchStep() : Number
		{
			return _pitchStep;
		}

		public function set pitchStep(value : Number)	: void
		{
			_pitchStep = value;
		}

		public function ArcBallController()
		{
			super();

			_pitch = Math.PI * .5;
			_lookAt.changed.add(updateNextFrameHandler);
			_up.changed.add(updateNextFrameHandler);
		}

		public function bindDefaultControls(dispatcher : IEventDispatcher) : ArcBallController
		{
			dispatcher.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
			dispatcher.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);

			return this;
		}

		public function unbindDefaultControls(dispatcher : IEventDispatcher) : ArcBallController
		{
			dispatcher.removeEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
			dispatcher.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);

			return this;
		}

		override protected function targetAddedHandler(ctrl 	: EnterFrameController,
													   target 	: ISceneNode) : void
		{
			super.targetAddedHandler(ctrl, target);

			_update = true;
			updateTargets();
		}

		override protected function sceneEnterFrameHandler(scene		: Scene,
														   viewport		: Viewport,
														   destination	: BitmapData,
														   time			: Number) : void
		{
			updateTargets();
		}

		private function updateTargets() : void
		{
			if (_update && _enabled)
			{
				if (_distance < _minDistance)
					_distance = _minDistance;
				if (_distance > _maxDistance)
					_distance = _maxDistance;
				if (_pitch <= EPSILON)
					_pitch = EPSILON;
				if (_pitch > Math.PI - EPSILON)
					_pitch = Math.PI - EPSILON;

				_position.set(
					_lookAt.x + _distance * Math.cos(_yaw) * Math.sin(_pitch),
					_lookAt.y + _distance * Math.cos(_pitch),
					_lookAt.z + _distance * Math.sin(_yaw) * Math.sin(_pitch)
				);

				TMP_MATRIX.lookAt(_lookAt, _position, _up);

				var numTargets : uint = this.numTargets;
				for (var targetId : uint = 0; targetId < numTargets; ++targetId)
					getTarget(targetId).transform.copyFrom(TMP_MATRIX);

				_update = false;
			}
		}

		protected function mouseWheelHandler(e : MouseEvent) : void
		{
			_distance	-=	e.delta * _distanceStep;
			_update		=	true;
		}

		protected function mouseMoveHandler(e : MouseEvent) : void
		{
			// compute position
			if (e.buttonDown && _enabled)
			{
				_yaw	+= (_mousePosition.x - e.stageX) * _yawStep;
				_pitch	+= (_mousePosition.y - e.stageY) * _pitchStep;

				_update = true;
			}

			_mousePosition.setTo(e.stageX, e.stageY);
		}

		private function updateNextFrameHandler(vector : Vector4) : void
		{
			_update = true;
		}
	}
}

set ArcBallController.minDistance to 1.0 by default


// Editor main handlers (add your local handler in proper main handler to prevent losing events)
const String EDITOR_EVENT_SCENE_LOADED("EditorEventSceneLoaded");
const String EDITOR_EVENT_ORIGIN_START_HOVER("EditorEventOriginStartHover");
const String EDITOR_EVENT_ORIGIN_END_HOVER("EditorEventOriginEndHover");

void EditorSubscribeToEvents()
{
    SubscribeToEvent("KeyDown", "EditorMainHandleKeyDown");
    SubscribeToEvent("KeyUp", "EditorMainHandleKeyUp");

    SubscribeToEvent("MouseMove", "EditorMainHandleMouseMove");
    SubscribeToEvent("MouseWheel", "EditorMainHandleMouseWheel");
    SubscribeToEvent("MouseButtonDown", "EditorMainHandleMouseButtonDown");
    SubscribeToEvent("MouseButtonUp", "EditorMainHandleMouseButtonUp");

    SubscribeToEvent("PostRenderUpdate", "EditorMainHandlePostRenderUpdate");

    SubscribeToEvent("UIMouseClick", "EditorMainHandleUIMouseClick");
    SubscribeToEvent("UIMouseClickEnd", "EditorMainHandleUIMouseClickEnd");

    SubscribeToEvent("BeginViewUpdate", "EditorMainHandleBeginViewUpdate");
    SubscribeToEvent("EndViewUpdate", "EditorMainHandleEndViewUpdate");
    SubscribeToEvent("BeginViewRender", "EditorMainHandleBeginViewRender");
    SubscribeToEvent("EndViewRender", "EditorMainHandleEndViewRender");

    SubscribeToEvent(EDITOR_EVENT_SCENE_LOADED, "EditorMainHandleSceneLoaded");

    SubscribeToEvent("HoverBegin", "EditorMainHandleHoverBegin");
    SubscribeToEvent("HoverEnd", "EditorMainHandleHoverEnd");

    SubscribeToEvent(EDITOR_EVENT_ORIGIN_START_HOVER, "EditorMainHandleOriginStartHover");
    SubscribeToEvent(EDITOR_EVENT_ORIGIN_END_HOVER, "EditorMainHandleOriginEndHover");

    SubscribeToEvent("NodeAdded", "EditorMainHandleNodeAdded");
    SubscribeToEvent("NodeRemoved", "EditorMainHandleNodeRemoved");

    SubscribeToEvent("NodeNameChanged", "EditorMainHandleNodeNameChanged");
}

void EditorMainHandleKeyDown(StringHash eventType, VariantMap& eventData)
{
    // EditorUI.as handler
    HandleKeyDown(eventType, eventData);

    // EditorColorWheel.as handler
    HandleColorWheelKeyDown(eventType, eventData);
}

void EditorMainHandleKeyUp(StringHash eventType, VariantMap& eventData)
{
    // EditorUI.as handler
    UnfadeUI();
}

void EditorMainHandleMouseMove(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    ViewMouseMove();

    // EditorColorWheel.as handler
    HandleColorWheelMouseMove(eventType, eventData);

    // EditorLayer.as handler
    HandleHideLayerEditor(eventType, eventData);

    // PaintSelectionMouseMove
    HandlePaintSelectionMouseMove(eventType, eventData);
}

void EditorMainHandleMouseWheel(StringHash eventType, VariantMap& eventData)
{
    // EditorColorWheel.as handler
    HandleColorWheelMouseWheel(eventType, eventData);

    // EditorLayer.as handler
    HandleMaskTypeScroll(eventType, eventData);
    // PaintSelection handler
    HandlePaintSelectionWheel(eventType, eventData);
}

void EditorMainHandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
{
    // EditorColorWheel.as handler
    HandleColorWheelMouseButtonDown(eventType, eventData);

    // EditorLayer.as handler
    HandleHideLayerEditor(eventType, eventData);

}

void EditorMainHandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
{
    // EditorUI.as handler
    UnfadeUI();
}

void EditorMainHandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    HandlePostRenderUpdate();
}

void EditorMainHandleUIMouseClick(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    ViewMouseClick();
    HandleOriginToggled(eventType, eventData);
}

void EditorMainHandleUIMouseClickEnd(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    ViewMouseClickEnd();
}

void EditorMainHandleBeginViewUpdate(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    HandleBeginViewUpdate(eventType, eventData);
}

void EditorMainHandleEndViewUpdate(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    HandleEndViewUpdate(eventType, eventData);
}

void EditorMainHandleBeginViewRender(StringHash eventType, VariantMap& eventData)
{
    HandleBeginViewRender(eventType, eventData);
}

void EditorMainHandleEndViewRender(StringHash eventType, VariantMap& eventData)
{
    HandleEndViewRender(eventType, eventData);
}

void EditorMainHandleSceneLoaded(StringHash eventType, VariantMap& eventData)
{
    HandleSceneLoadedForOrigins();
}

void EditorMainHandleHoverBegin(StringHash eventType, VariantMap& eventData)
{
    HandleOriginsHoverBegin(eventType, eventData);
}

void EditorMainHandleHoverEnd(StringHash eventType, VariantMap& eventData)
{
    HandleOriginsHoverEnd(eventType, eventData);
}

void EditorMainHandleNodeAdded(StringHash eventType, VariantMap& eventData)
{
    HandleNodeAdded(eventType, eventData);
    rebuildSceneOrigins = true;
}

void EditorMainHandleNodeRemoved(StringHash eventType, VariantMap& eventData)
{
    HandleNodeRemoved(eventType, eventData);
    rebuildSceneOrigins = true;
}

void EditorMainHandleNodeNameChanged(StringHash eventType, VariantMap& eventData)
{
    HandleNodeNameChanged(eventType, eventData);
}

void EditorMainHandleOriginStartHover(StringHash eventType, VariantMap& eventData)
{

}

void EditorMainHandleOriginEndHover(StringHash eventType, VariantMap& eventData)
{

}

// Editor main handlers (add your local handler in proper main handler to prevent losing events)
const String EDITOR_EVENT_SCENE_LOADED("EditorEventSceneLoaded");
const String EDITOR_EVENT_ORIGIN_START_HOVER("EditorEventOriginStartHover");
const String EDITOR_EVENT_ORIGIN_END_HOVER("EditorEventOriginEndHover");

void EditorSubscribeToEvents()
{
    SubscribeToEvent("KeyDown", "EditorMainHandleKeyDown");
    SubscribeToEvent("KeyUp", "EditorMainHandleKeyUp");

    SubscribeToEvent("MouseMove", "EditorMainHandleMouseMove");
    SubscribeToEvent("MouseWheel", "EditorMainHandleMouseWheel");
    SubscribeToEvent("MouseButtonDown", "EditorMainHandleMouseButtonDown");
    SubscribeToEvent("MouseButtonUp", "EditorMainHandleMouseButtonUp");

    SubscribeToEvent("PostRenderUpdate", "EditorMainHandlePostRenderUpdate");

    SubscribeToEvent("UIMouseClick", "EditorMainHandleUIMouseClick");
    SubscribeToEvent("UIMouseClickEnd", "EditorMainHandleUIMouseClickEnd");

    SubscribeToEvent("BeginViewUpdate", "EditorMainHandleBeginViewUpdate");
    SubscribeToEvent("EndViewUpdate", "EditorMainHandleEndViewUpdate");
    SubscribeToEvent("BeginViewRender", "EditorMainHandleBeginViewRender");
    SubscribeToEvent("EndViewRender", "EditorMainHandleEndViewRender");

    SubscribeToEvent(EDITOR_EVENT_SCENE_LOADED, "EditorMainHandleSceneLoaded");

    SubscribeToEvent("HoverBegin", "EditorMainHandleHoverBegin");
    SubscribeToEvent("HoverEnd", "EditorMainHandleHoverEnd");

    SubscribeToEvent(EDITOR_EVENT_ORIGIN_START_HOVER, "EditorMainHandleOriginStartHover");
    SubscribeToEvent(EDITOR_EVENT_ORIGIN_END_HOVER, "EditorMainHandleOriginEndHover");

    SubscribeToEvent("NodeAdded", "EditorMainHandleNodeAdded");
    SubscribeToEvent("NodeRemoved", "EditorMainHandleNodeRemoved");

    SubscribeToEvent("NodeNameChanged", "EditorMainHandleNodeNameChanged");
}

void EditorMainHandleKeyDown(StringHash eventType, VariantMap& eventData)
{
    // EditorUI.as handler
    HandleKeyDown(eventType, eventData);

    // EditorColorWheel.as handler
    HandleColorWheelKeyDown(eventType, eventData);
}

void EditorMainHandleKeyUp(StringHash eventType, VariantMap& eventData)
{
    // EditorUI.as handler
    UnfadeUI();
}

void EditorMainHandleMouseMove(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    ViewMouseMove();

    // EditorColorWheel.as handler
    HandleColorWheelMouseMove(eventType, eventData);

    // EditorLayer.as handler
    HandleHideLayerEditor(eventType, eventData);

    // PaintSelectionMouseMove
    HandlePaintSelectionMouseMove(eventType, eventData);
}

void EditorMainHandleMouseWheel(StringHash eventType, VariantMap& eventData)
{
    // EditorColorWheel.as handler
    HandleColorWheelMouseWheel(eventType, eventData);

    // EditorLayer.as handler
    HandleMaskTypeScroll(eventType, eventData);
    // PaintSelection handler
    HandlePaintSelectionWheel(eventType, eventData);
}

void EditorMainHandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
{
    // EditorColorWheel.as handler
    HandleColorWheelMouseButtonDown(eventType, eventData);

    // EditorLayer.as handler
    HandleHideLayerEditor(eventType, eventData);

}

void EditorMainHandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
{
    // EditorUI.as handler
    UnfadeUI();
}

void EditorMainHandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    HandlePostRenderUpdate();
}

void EditorMainHandleUIMouseClick(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    ViewMouseClick();
    HandleOriginToggled(eventType, eventData);
}

void EditorMainHandleUIMouseClickEnd(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    ViewMouseClickEnd();
}

void EditorMainHandleBeginViewUpdate(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    HandleBeginViewUpdate(eventType, eventData);
}

void EditorMainHandleEndViewUpdate(StringHash eventType, VariantMap& eventData)
{
    // EditorView.as handler
    HandleEndViewUpdate(eventType, eventData);
}

void EditorMainHandleBeginViewRender(StringHash eventType, VariantMap& eventData)
{
    HandleBeginViewRender(eventType, eventData);
}

void EditorMainHandleEndViewRender(StringHash eventType, VariantMap& eventData)
{
    HandleEndViewRender(eventType, eventData);
}

void EditorMainHandleSceneLoaded(StringHash eventType, VariantMap& eventData)
{
    HandleSceneLoadedForOrigins();
}

void EditorMainHandleHoverBegin(StringHash eventType, VariantMap& eventData)
{
    HandleOriginsHoverBegin(eventType, eventData);
}

void EditorMainHandleHoverEnd(StringHash eventType, VariantMap& eventData)
{
    HandleOriginsHoverEnd(eventType, eventData);
}

void EditorMainHandleNodeAdded(StringHash eventType, VariantMap& eventData)
{
    if (GetEventSender() !is editorScene)
        return;

    HandleNodeAdded(eventType, eventData);
    rebuildSceneOrigins = true;
}

void EditorMainHandleNodeRemoved(StringHash eventType, VariantMap& eventData)
{
    if (GetEventSender() !is editorScene)
        return;

    HandleNodeRemoved(eventType, eventData);
    rebuildSceneOrigins = true;
}

void EditorMainHandleNodeNameChanged(StringHash eventType, VariantMap& eventData)
{
    if (GetEventSender() !is editorScene)
        return;

    HandleNodeNameChanged(eventType, eventData);
}

void EditorMainHandleOriginStartHover(StringHash eventType, VariantMap& eventData)
{

}

void EditorMainHandleOriginEndHover(StringHash eventType, VariantMap& eventData)
{

}

Fix resource browser scene node addition/removal getting reflected in the editor hierarchy window.


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

	import org.apache.flex.core.CSSTextField;
	import org.apache.flex.core.IBead;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.core.ITextBead;
	import org.apache.flex.core.ITextModel;
	import org.apache.flex.events.Event;

	public class TextFieldBeadBase implements IBead, ITextBead
	{
		public function TextFieldBeadBase()
		{
			_textField = new CSSTextField();
		}

		private var _textField:CSSTextField;

		public function get textField() : CSSTextField
		{
			return _textField;
		}

		private var _textModel:ITextModel;

		public function get textModel() : ITextModel
		{
			return _textModel;
		}

		private var _strand:IStrand;

		public function set strand(value:IStrand):void
		{
			_strand = value;
			_textModel = value.getBeadByType(ITextModel) as ITextModel;
			textModel.addEventListener("textChange", textChangeHandler);
			textModel.addEventListener("htmlChange", htmlChangeHandler);
			textModel.addEventListener("widthChanged", sizeChangeHandler);
			textModel.addEventListener("heightChanged", sizeChangeHandler);
			DisplayObjectContainer(value).addChild(_textField);
			sizeChangeHandler(null);
			if (textModel.text !== null)
				text = textModel.text;
			if (textModel.html !== null)
				html = textModel.html;
		}

		public function get strand() : IStrand
		{
			return _strand;
		}

		public function get text():String
		{
			return _textField.text;
		}
		public function set text(value:String):void
		{
            if (value == null)
                value == "";
			_textField.text = value;
		}

		public function get html():String
		{
			return _textField.htmlText;
		}

		public function set html(value:String):void
		{
			_textField.htmlText = value;
		}

		private function textChangeHandler(event:Event):void
		{
			text = textModel.text;
		}

		private function htmlChangeHandler(event:Event):void
		{
			html = textModel.html;
		}

		private function sizeChangeHandler(event:Event):void
		{
			textField.width = DisplayObject(_strand).width;
			textField.height = DisplayObject(_strand).height;
		}
	}
}

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

	import org.apache.flex.core.CSSTextField;
	import org.apache.flex.core.IBead;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.core.ITextBead;
	import org.apache.flex.core.ITextModel;
	import org.apache.flex.events.Event;

	public class TextFieldBeadBase implements IBead, ITextBead
	{
		public function TextFieldBeadBase()
		{
			_textField = new CSSTextField();
		}

		private var _textField:CSSTextField;

		public function get textField() : CSSTextField
		{
			return _textField;
		}

		private var _textModel:ITextModel;

		public function get textModel() : ITextModel
		{
			return _textModel;
		}

		private var _strand:IStrand;

		public function set strand(value:IStrand):void
		{
			_strand = value;
			_textModel = value.getBeadByType(ITextModel) as ITextModel;
			textModel.addEventListener("textChange", textChangeHandler);
			textModel.addEventListener("htmlChange", htmlChangeHandler);
			textModel.addEventListener("widthChanged", sizeChangeHandler);
			textModel.addEventListener("heightChanged", sizeChangeHandler);
			DisplayObjectContainer(value).addChild(_textField);
			sizeChangeHandler(null);
			if (textModel.text !== null)
				text = textModel.text;
			if (textModel.html !== null)
				html = textModel.html;
		}

		public function get strand() : IStrand
		{
			return _strand;
		}

		public function get text():String
		{
			return _textField.text;
		}
		public function set text(value:String):void
		{
            if (value == null)
                value = "";
			_textField.text = value;
		}

		public function get html():String
		{
			return _textField.htmlText;
		}

		public function set html(value:String):void
		{
			_textField.htmlText = value;
		}

		private function textChangeHandler(event:Event):void
		{
			text = textModel.text;
		}

		private function htmlChangeHandler(event:Event):void
		{
			html = textModel.html;
		}

		private function sizeChangeHandler(event:Event):void
		{
			textField.width = DisplayObject(_strand).width;
			textField.height = DisplayObject(_strand).height;
		}
	}
}

handle null which could happen when restoring states


package goplayer
{
  import flash.display.DisplayObject

  public class Packer
  {
    private var base : Item
    private var items : Array

    public function Packer(base : Item, items : Array)
    { this.base = base, this.items = items }

    public function execute() : void
    {
      var current : Item = base

      for each (var item : Item in items)
        item.packLeft(current), current = item
    }

    public static function packLeft(space : Number, ... items : Array) : void
    { $packLeft(space, items) }

    public static function $packLeft(space : Number, items : Array) : void
    {
      if (items.length > 0)
        $$packLeft(new Importer(space, items).getItems())
    }

    private static function $$packLeft(items : Array) : void
    { new Packer(items[0], items.slice(1)).execute() }
  }
}

import flash.display.DisplayObject

class Item
{
  private var object : DisplayObject
  private var _width : Number

  public function Item(object : DisplayObject, width : Number)
  { this.object = object, _width = width }

  public function get width() : Number
  { return _width }

  public function get right() : Number
  { return object.x + width }

  public function packLeft(base : Item) : void
  { object.x = base.right }
}

class Importer
{
  private var space : Number
  private var items : Array

  public function Importer(space : Number, items : Array)
  { this.space = space, this.items = items }

  public function getItems() : Array
  {
    const result : Array = []

    for each (var item : Object in items)
      if (item != null)
        result.push(getItem(item))

    return result
  }

  private function getItem(item : Object) : Item
  {
    if (isCallbackFlexible(item))
      getCallback(item)(getWidth(item))

    return new Item(getDisplayObject(item), getWidth(item))
  }

  private function getDisplayObject(item : Object) : DisplayObject
  { return isImplicit(item) ? item as DisplayObject : (item as Array)[0] }

  private function getWidth(item : Object) : Number
  {
    if (isExplicit(item))
      return (item as Array)[1]
    else if (isFlexible(item))
      return flexibleWidth
    else if (isImplicit(item))
      return (item as DisplayObject).width
    else
      throw new Error
  }

  private function isImplicit(item : Object) : Boolean
  { return item is DisplayObject }

  private function isIgnoredFlexible(item : Object) : Boolean
  { return item is Array && !hasSecondEntry(item) }

  private function isExplicit(item : Object) : Boolean
  { return hasSecondEntry(item) && getSecondEntry(item) is Number }

  private function isCallbackFlexible(item : Object) : Boolean
  { return hasSecondEntry(item) && getSecondEntry(item) is Function }

  private function getCallback(item : Object) : Function
  { return getSecondEntry(item) as Function }

  private function hasSecondEntry(item : Object) : Boolean
  { return item is Array && (item as Array).length == 2 }

  private function getSecondEntry(item : Object) : Object
  { return (item as Array)[1] }

  private function isFlexible(item : Object) : Boolean
  { return isCallbackFlexible(item) || isIgnoredFlexible(item) }

  private function get flexibleWidth() : Number
  { return space / flexibleCount - totalStaticWidth }

  private function get totalStaticWidth() : Number
  {
    var result : Number = 0

    for each (var item : Object in items)
      if (!isFlexible(item))
        result += getWidth(item)

    return result
  }

  private function get flexibleCount() : int
  {
    var result : int = 0

    for each (var item : Object in items)
      if (isFlexible(item))
        ++result

    return result
  }
}

package goplayer
{
  import flash.display.DisplayObject

  public class Packer
  {
    private var base : Item
    private var items : Array

    public function Packer(base : Item, items : Array)
    { this.base = base, this.items = items }

    public function execute() : void
    {
      var current : Item = base

      for each (var item : Item in items)
        item.packLeft(current), current = item
    }

    public static function packLeft(space : Number, ... items : Array) : void
    { $packLeft(space, items) }

    public static function $packLeft(space : Number, items : Array) : void
    {
      if (items.length > 0)
        $$packLeft(new Importer(space, items).getItems())
    }

    private static function $$packLeft(items : Array) : void
    { new Packer(items[0], items.slice(1)).execute() }
  }
}

import flash.display.DisplayObject

class Item
{
  private var object : DisplayObject
  private var _width : Number

  public function Item(object : DisplayObject, width : Number)
  { this.object = object, _width = width }

  public function get width() : Number
  { return _width }

  public function get right() : Number
  { return object.x + width }

  public function packLeft(base : Item) : void
  { object.x = base.right }
}

class Importer
{
  private var space : Number
  private var items : Array

  public function Importer(space : Number, items : Array)
  { this.space = space, this.items = items }

  public function getItems() : Array
  {
    const result : Array = []

    for each (var item : Object in items)
      if (item != null)
        result.push(getItem(item))

    return result
  }

  private function getItem(item : Object) : Item
  {
    if (isCallbackFlexible(item))
      getCallback(item)(getWidth(item))

    return new Item(getDisplayObject(item), getWidth(item))
  }

  private function getDisplayObject(item : Object) : DisplayObject
  { return isImplicit(item) ? item as DisplayObject : (item as Array)[0] }

  private function getWidth(item : Object) : Number
  {
    if (isExplicit(item))
      return (item as Array)[1]
    else if (isFlexible(item))
      return flexibleWidth
    else if (isImplicit(item))
      return (item as DisplayObject).width
    else
      throw new Error("Invalid item: " + item)
  }

  private function isImplicit(item : Object) : Boolean
  { return item is DisplayObject }

  private function isIgnoredFlexible(item : Object) : Boolean
  { return item is Array && !hasSecondEntry(item) }

  private function isExplicit(item : Object) : Boolean
  { return hasSecondEntry(item) && getSecondEntry(item) is Number }

  private function isCallbackFlexible(item : Object) : Boolean
  { return hasSecondEntry(item) && getSecondEntry(item) is Function }

  private function getCallback(item : Object) : Function
  { return getSecondEntry(item) as Function }

  private function hasSecondEntry(item : Object) : Boolean
  { return item is Array && (item as Array).length == 2 }

  private function getSecondEntry(item : Object) : Object
  { return (item as Array)[1] }

  private function isFlexible(item : Object) : Boolean
  { return isCallbackFlexible(item) || isIgnoredFlexible(item) }

  private function get flexibleWidth() : Number
  { return space / flexibleCount - totalStaticWidth }

  private function get totalStaticWidth() : Number
  {
    var result : Number = 0

    for each (var item : Object in items)
      if (!isFlexible(item))
        result += getWidth(item)

    return result
  }

  private function get flexibleCount() : int
  {
    var result : int = 0

    for each (var item : Object in items)
      if (isFlexible(item))
        ++result

    return result
  }
}

Add error message to Packer.

Add error message to Packer.


package dolly {

import dolly.core.dolly_internal;
import dolly.core.errors.CloningError;
import dolly.core.metadata.MetadataName;
import dolly.utils.ClassUtil;

import flash.utils.ByteArray;
import flash.utils.Dictionary;

import org.as3commons.reflect.Accessor;
import org.as3commons.reflect.AccessorAccess;
import org.as3commons.reflect.Field;
import org.as3commons.reflect.Type;

use namespace dolly_internal;

public class Cloner {

	private static var _isClassPreparedForCloningMap:Dictionary = new Dictionary();

	private static function isTypeCloneable(type:Type):Boolean {
		return type.hasMetadata(MetadataName.CLONEABLE);
	}

	private static function isTypePreparedForCloning(type:Type):Boolean {
		return _isClassPreparedForCloningMap[type.clazz];
	}

	private static function failIfTypeIsNotCloneable(type:Type):void {
		if (!isTypeCloneable(type)) {
			throw new CloningError(
					CloningError.CLASS_IS_NOT_CLONEABLE_MESSAGE,
					CloningError.CLASS_IS_NOT_CLONEABLE_CODE
			);
		}
	}

	private static function prepareTypeForCloning(type:Type):void {
		ClassUtil.registerAliasFor(type.clazz);

		_isClassPreparedForCloningMap[type.clazz] = true;
	}

	dolly_internal static function findAllWritableFieldsForType(type:Type):Vector.<Field> {
		failIfTypeIsNotCloneable(type);

		if (!isTypePreparedForCloning(type)) {
			prepareTypeForCloning(type);
		}

		const result:Vector.<Field> = new Vector.<Field>();

		for each(var field:Field in type.properties) {
			if (!(field is Accessor) || (field as Accessor).access == AccessorAccess.READ_WRITE) {
				result.push(field);
			}
		}

		return result;
	}

	dolly_internal static function doClone(source:*, type:Type = null):* {
		if (!type) {
			type = Type.forInstance(source);
		}

		if (!isTypePreparedForCloning(type)) {
			prepareTypeForCloning(type);
		}

		const fieldsToClone:Vector.<Field> = findAllWritableFieldsForType(type);

		for each(var field:Field in fieldsToClone) {
			var fieldType:Type = field.type;
			if (isTypeCloneable(fieldType) && !isTypePreparedForCloning(fieldType)) {
				prepareTypeForCloning(fieldType);
			}
		}

		const byteArray:ByteArray = new ByteArray();
		byteArray.writeObject(source);
		byteArray.position = 0;

		const clonedInstance:* = byteArray.readObject();

		return clonedInstance;
	}

	public static function clone(source:*):* {
		const type:Type = Type.forInstance(source);

		failIfTypeIsNotCloneable(type);

		return doClone(source, type);
	}
}
}

package dolly {

import dolly.core.dolly_internal;
import dolly.core.errors.CloningError;
import dolly.core.metadata.MetadataName;
import dolly.utils.ClassUtil;

import flash.utils.ByteArray;
import flash.utils.Dictionary;

import org.as3commons.reflect.Accessor;
import org.as3commons.reflect.AccessorAccess;
import org.as3commons.reflect.Field;
import org.as3commons.reflect.Type;

use namespace dolly_internal;

public class Cloner {

	private static var _isClassPreparedForCloningMap:Dictionary = new Dictionary();

	private static function isTypeCloneable(type:Type):Boolean {
		return type.hasMetadata(MetadataName.CLONEABLE);
	}

	private static function isTypePreparedForCloning(type:Type):Boolean {
		return _isClassPreparedForCloningMap[type.clazz];
	}

	private static function failIfTypeIsNotCloneable(type:Type):void {
		if (!isTypeCloneable(type)) {
			throw new CloningError(
					CloningError.CLASS_IS_NOT_CLONEABLE_MESSAGE,
					CloningError.CLASS_IS_NOT_CLONEABLE_CODE
			);
		}
	}

	private static function prepareTypeForCloning(type:Type):void {
		if (isTypePreparedForCloning(type)) {
			return;
		}

		ClassUtil.registerAliasFor(type.clazz);

		_isClassPreparedForCloningMap[type.clazz] = true;
	}

	dolly_internal static function findAllWritableFieldsForType(type:Type):Vector.<Field> {
		failIfTypeIsNotCloneable(type);

		if (!isTypePreparedForCloning(type)) {
			prepareTypeForCloning(type);
		}

		const result:Vector.<Field> = new Vector.<Field>();

		for each(var field:Field in type.properties) {
			if (!(field is Accessor) || (field as Accessor).access == AccessorAccess.READ_WRITE) {
				result.push(field);
			}
		}

		return result;
	}

	dolly_internal static function doClone(source:*, type:Type = null):* {
		if (!type) {
			type = Type.forInstance(source);
		}

		prepareTypeForCloning(type);

		const fieldsToClone:Vector.<Field> = findAllWritableFieldsForType(type);

		for each(var field:Field in fieldsToClone) {
			var fieldType:Type = field.type;
			if (isTypeCloneable(fieldType)) {
				prepareTypeForCloning(fieldType);
			}
		}

		const byteArray:ByteArray = new ByteArray();
		byteArray.writeObject(source);
		byteArray.position = 0;

		const clonedInstance:* = byteArray.readObject();

		return clonedInstance;
	}

	public static function clone(source:*):* {
		const type:Type = Type.forInstance(source);

		failIfTypeIsNotCloneable(type);

		return doClone(source, type);
	}
}
}

Move "if" statement checking for is type prepared for cloning to method Cloner.prepareTypeForCloning().


package aerys.minko.render.geometry.stream
{
	import flash.utils.ByteArray;
	import flash.utils.Endian;

	import aerys.minko.ns.minko_stream;
	import aerys.minko.render.resource.IndexBuffer3DResource;
	import aerys.minko.type.Signal;

	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;
		private var _contextLost	: Signal;

		public function get contextLost():Signal
		{
			return _contextLost;
		}

		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, 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 index 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 index 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 flash.utils.ByteArray;
	import flash.utils.Endian;

	import aerys.minko.ns.minko_stream;
	import aerys.minko.render.resource.IndexBuffer3DResource;
	import aerys.minko.type.Signal;

	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;
		private var _contextLost	: Signal;

		public function get contextLost():Signal
		{
			return _contextLost;
		}

		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 * 6;
			_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, 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 index 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 index 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

Fix delete triangle


package krewfw.builtin_actor.ui {

    import flash.display.Stage;
    import flash.events.KeyboardEvent;
    import flash.geom.Point;
    import flash.ui.Keyboard;
    import flash.utils.Dictionary;

    import starling.display.DisplayObject;
    import starling.display.Image;
    import starling.display.Quad;
    import starling.events.Touch;
    import starling.events.TouchEvent;
    import starling.events.TouchPhase;

    import krewfw.NativeStageAccessor;
    import krewfw.core.KrewActor;

    /**
     * いわゆるバーチャルジョイスティック。
     *
     * maxFingerMove に 100 を指定すると、中心から 100 ピクセル指を動かした際に
     * スティックの傾きが最大となる。maxImageMove はこのときのスティック画像の
     * 中心からの移動量である。
     */
    //------------------------------------------------------------
    public class SimpleVirtualJoystick extends KrewActor {

        /**
         * ジョイスティックに対して触れた・動かした・離した際にこのイベントが投げられる。
         * イベントの引数は {velocityX:Number, velocityY:Number}.
         * ジョイスティックの傾きの x, y 成分が [0, 1] の値で渡される。
         * 離した際のイベントでは velocityX, velocityY は共に 0 となる。
         */
        public static const UPDATE_JOYSTICK:String = "krew.updateJoystick";

        public var maxFingerMove:Number = 60;
        public var maxImageMove:Number  = 40;

        private var _stickImage:DisplayObject;

        private var _keyDowns:Dictionary = new Dictionary();
        private var _numKeyDown:int = 0;

        //------------------------------------------------------------
        public function SimpleVirtualJoystick(holderImage:Image=null,
                                              stickImage:Image=null,
                                              touchSize:Number=130)
        {
            touchable = true;

            // default display object
            if (holderImage == null) {
                var defaultHolder:Quad = new Quad(100, 100, 0x777777);
                var defaultStick :Quad = new Quad( 50,  50, 0xee4444);
                _setCenterPivot(defaultHolder);
                _setCenterPivot(defaultStick);
                addChild(defaultHolder);
                addChild(defaultStick);
                _stickImage = defaultStick;
            } else {
                addImage(holderImage);
                addImage(stickImage);
                _stickImage = stickImage;
            }

            super.addTouchMarginNode(touchSize, touchSize);
            addEventListener(TouchEvent.TOUCH, _onTouch);

            _initKeyboardEventListener();
        }

        private function _setCenterPivot(dispObj:DisplayObject):void {
            dispObj.pivotX = dispObj.width  / 2;
            dispObj.pivotY = dispObj.height / 2;
        }

        protected override function onDispose():void {
            var stage:Stage = NativeStageAccessor.stage;
            if (stage == null) { return; }

            stage.removeEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
            stage.removeEventListener(KeyboardEvent.KEY_UP,   _onKeyUp);
        }

        private function _onTouch(event:TouchEvent):void {
            var touchBegan:Touch = event.getTouch(this, TouchPhase.BEGAN);
            if (touchBegan) { _onTouchStart(touchBegan); }

            var touchMoved:Touch = event.getTouch(this, TouchPhase.MOVED);
            if (touchMoved) { _onTouchMove(touchMoved); }

            var touchEnded:Touch = event.getTouch(this, TouchPhase.ENDED);
            if (touchEnded) { _onTouchEnd(touchEnded); }
        }

        private function _onTouchStart(touchBegan:Touch):void {
            _onTouchMove(touchBegan);
        }

        private function _onTouchMove(touchMoved:Touch):void {
            var localPos:Point = touchMoved.getLocation(this);
            var stickX:Number = localPos.x;
            var stickY:Number = localPos.y;

            var fingerDistance:Number = krew.getDistance(0, 0, stickX, stickY);
            var scaleToFingerLimit:Number = fingerDistance / maxFingerMove;
            if (scaleToFingerLimit > 1) {
                stickX /= scaleToFingerLimit;
                stickY /= scaleToFingerLimit;
            }

            var moveScale:Number = maxImageMove / maxFingerMove;
            _stickImage.x = stickX * moveScale;
            _stickImage.y = stickY * moveScale;

            sendMessage(UPDATE_JOYSTICK, {
                velocityX: _stickImage.x / maxImageMove,
                velocityY: _stickImage.y / maxImageMove
            });
        }

        private function _onTouchEnd(touchEnded:Touch):void {
            _stickImage.x = 0;
            _stickImage.y = 0;
            sendMessage(UPDATE_JOYSTICK, {velocityX: 0, velocityY: 0});
        }

        public override function onUpdate(passedTime:Number):void {
            _onKeyUpdate();
        }

        //------------------------------------------------------------
        // Keyboard Event
        //------------------------------------------------------------

        private function _initKeyboardEventListener():void {
            var stage:Stage = NativeStageAccessor.stage;
            if (stage == null) { return; }

            stage.addEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
            stage.addEventListener(KeyboardEvent.KEY_UP,   _onKeyUp);
        }

        private function _onKeyDown(event:KeyboardEvent):void {
            if (!_keyDowns[event.keyCode]) { ++_numKeyDown; }
            _keyDowns[event.keyCode] = true;
        }

        private function _onKeyUp(event:KeyboardEvent):void {
            if (_keyDowns[event.keyCode]) { --_numKeyDown; }
            _keyDowns[event.keyCode] = false;

            // 全部離したタイミング
            if (_numKeyDown == 0) {
                sendMessage(UPDATE_JOYSTICK, {
                    velocityX: 0,
                    velocityY: 0
                });
            }
        }

        /**
         * PC 用にキーボードでの操作でもメッセージを投げる。
         * マウスでジョイスティックが動かされていた場合はそちらを優先
         */
        private function _onKeyUpdate():void {
            if (_numKeyDown == 0) { return; }
            if (!(_stickImage.x == 0  &&  _stickImage.y == 0)) { return; }

            var keyVelocityX:Number = 0;
            var keyVelocityY:Number = 0;
            if (_keyDowns[Keyboard.LEFT ]) { keyVelocityX -= 1.0; }
            if (_keyDowns[Keyboard.RIGHT]) { keyVelocityX += 1.0; }
            if (_keyDowns[Keyboard.UP   ]) { keyVelocityY -= 1.0; }
            if (_keyDowns[Keyboard.DOWN ]) { keyVelocityY += 1.0; }

            sendMessage(UPDATE_JOYSTICK, {
                velocityX: keyVelocityX,
                velocityY: keyVelocityY
            });
        }

    }
}

package krewfw.builtin_actor.ui {

    import flash.display.Stage;
    import flash.events.KeyboardEvent;
    import flash.geom.Point;
    import flash.ui.Keyboard;
    import flash.utils.Dictionary;

    import starling.display.DisplayObject;
    import starling.display.Image;
    import starling.display.Quad;
    import starling.events.Touch;
    import starling.events.TouchEvent;
    import starling.events.TouchPhase;

    import krewfw.NativeStageAccessor;
    import krewfw.core.KrewActor;

    /**
     * いわゆるバーチャルジョイスティック。
     *
     * maxFingerMove に 100 を指定すると、中心から 100 ピクセル指を動かした際に
     * スティックの傾きが最大となる。maxImageMove はこのときのスティック画像の
     * 中心からの移動量である。
     */
    //------------------------------------------------------------
    public class SimpleVirtualJoystick extends KrewActor {

        /**
         * ジョイスティックに対して触れた・動かした・離した際にこのイベントが投げられる。
         * イベントの引数は {velocityX:Number, velocityY:Number}.
         * ジョイスティックの傾きの x, y 成分が [0, 1] の値で渡される。
         * 離した際のイベントでは velocityX, velocityY は共に 0 となる。
         */
        public static const UPDATE_JOYSTICK:String = "krew.updateJoystick";

        public var maxFingerMove:Number = 60;
        public var maxImageMove:Number  = 40;

        private var _stickImage:DisplayObject;

        private var _keyDowns:Dictionary = new Dictionary();
        private var _numKeyDown:int = 0;

        //------------------------------------------------------------
        public function SimpleVirtualJoystick(holderImage:Image=null,
                                              stickImage:Image=null,
                                              touchSize:Number=130)
        {
            touchable = true;

            // default display object
            if (holderImage == null) {
                var defaultHolder:Quad = new Quad(100, 100, 0x777777);
                var defaultStick :Quad = new Quad( 50,  50, 0xee4444);
                _setCenterPivot(defaultHolder);
                _setCenterPivot(defaultStick);
                addChild(defaultHolder);
                addChild(defaultStick);
                _stickImage = defaultStick;
            } else {
                addImage(holderImage);
                addImage(stickImage);
                _stickImage = stickImage;
            }

            super.addTouchMarginNode(touchSize, touchSize);
            addEventListener(TouchEvent.TOUCH, _onTouch);

            _initKeyboardEventListener();
        }

        private function _setCenterPivot(dispObj:DisplayObject):void {
            dispObj.pivotX = dispObj.width  / 2;
            dispObj.pivotY = dispObj.height / 2;
        }

        protected override function onDispose():void {
            var stage:Stage = NativeStageAccessor.stage;
            if (stage == null) { return; }

            stage.removeEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
            stage.removeEventListener(KeyboardEvent.KEY_UP,   _onKeyUp);
        }

        private function _onTouch(event:TouchEvent):void {
            var touchBegan:Touch = event.getTouch(this, TouchPhase.BEGAN);
            if (touchBegan) { _onTouchStart(touchBegan); }

            var touchMoved:Touch = event.getTouch(this, TouchPhase.MOVED);
            if (touchMoved) { _onTouchMove(touchMoved); }

            var touchEnded:Touch = event.getTouch(this, TouchPhase.ENDED);
            if (touchEnded) { _onTouchEnd(touchEnded); }
        }

        private function _onTouchStart(touchBegan:Touch):void {
            _onTouchMove(touchBegan);
        }

        private function _onTouchMove(touchMoved:Touch):void {
            var localPos:Point = touchMoved.getLocation(this);
            var stickX:Number = localPos.x;
            var stickY:Number = localPos.y;

            var fingerDistance:Number = krew.distance(0, 0, stickX, stickY);
            var scaleToFingerLimit:Number = fingerDistance / maxFingerMove;
            if (scaleToFingerLimit > 1) {
                stickX /= scaleToFingerLimit;
                stickY /= scaleToFingerLimit;
            }

            var moveScale:Number = maxImageMove / maxFingerMove;
            _stickImage.x = stickX * moveScale;
            _stickImage.y = stickY * moveScale;

            sendMessage(UPDATE_JOYSTICK, {
                velocityX: _stickImage.x / maxImageMove,
                velocityY: _stickImage.y / maxImageMove
            });
        }

        private function _onTouchEnd(touchEnded:Touch):void {
            _stickImage.x = 0;
            _stickImage.y = 0;
            sendMessage(UPDATE_JOYSTICK, {velocityX: 0, velocityY: 0});
        }

        public override function onUpdate(passedTime:Number):void {
            _onKeyUpdate();
        }

        //------------------------------------------------------------
        // Keyboard Event
        //------------------------------------------------------------

        private function _initKeyboardEventListener():void {
            var stage:Stage = NativeStageAccessor.stage;
            if (stage == null) { return; }

            stage.addEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
            stage.addEventListener(KeyboardEvent.KEY_UP,   _onKeyUp);
        }

        private function _onKeyDown(event:KeyboardEvent):void {
            if (!_keyDowns[event.keyCode]) { ++_numKeyDown; }
            _keyDowns[event.keyCode] = true;
        }

        private function _onKeyUp(event:KeyboardEvent):void {
            if (_keyDowns[event.keyCode]) { --_numKeyDown; }
            _keyDowns[event.keyCode] = false;

            // 全部離したタイミング
            if (_numKeyDown == 0) {
                sendMessage(UPDATE_JOYSTICK, {
                    velocityX: 0,
                    velocityY: 0
                });
            }
        }

        /**
         * PC 用にキーボードでの操作でもメッセージを投げる。
         * マウスでジョイスティックが動かされていた場合はそちらを優先
         */
        private function _onKeyUpdate():void {
            if (_numKeyDown == 0) { return; }
            if (!(_stickImage.x == 0  &&  _stickImage.y == 0)) { return; }

            var keyVelocityX:Number = 0;
            var keyVelocityY:Number = 0;
            if (_keyDowns[Keyboard.LEFT ]) { keyVelocityX -= 1.0; }
            if (_keyDowns[Keyboard.RIGHT]) { keyVelocityX += 1.0; }
            if (_keyDowns[Keyboard.UP   ]) { keyVelocityY -= 1.0; }
            if (_keyDowns[Keyboard.DOWN ]) { keyVelocityY += 1.0; }

            sendMessage(UPDATE_JOYSTICK, {
                velocityX: keyVelocityX,
                velocityY: keyVelocityY
            });
        }

    }
}

Fix old interface

Fix old interface


package com.kaltura.kdpfl.plugin
{
	import com.akamai.playeranalytics.AnalyticsPluginLoader;
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.model.type.StreamerType;
	import com.kaltura.types.KalturaMediaType;
	import com.kaltura.vo.KalturaBaseEntry;
	import com.kaltura.vo.KalturaFlavorAsset;
	import com.kaltura.vo.KalturaMediaEntry;

	import flash.system.Capabilities;

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

	public class akamaiMediaAnalyticsMediator extends Mediator
	{
		public static const NAME:String = "akamaiMediaAnalyticsMediator";
		private var _mediaProxy:MediaProxy;

		public function akamaiMediaAnalyticsMediator(viewComponent:Object=null)
		{
			super(NAME, viewComponent);
		}

		override public function onRegister():void
		{
			_mediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
			super.onRegister();
		}

		override public function listNotificationInterests():Array
		{
			return [NotificationType.MEDIA_READY, NotificationType.MEDIA_ELEMENT_READY, NotificationType.SWITCHING_CHANGE_COMPLETE];
		}

		override public function handleNotification(notification:INotification):void
		{
			switch (notification.getName())
			{
				case NotificationType.MEDIA_READY:
					//populate analytics metadata
					var configProxy:ConfigProxy = facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy;
					var entry:KalturaBaseEntry = _mediaProxy.vo.entry;

					AnalyticsPluginLoader.setData("title", entry.name);
					AnalyticsPluginLoader.setData("entryId", entry.id);
					AnalyticsPluginLoader.setData("category", entry.categories);
					AnalyticsPluginLoader.setData("partnerId", configProxy.vo.flashvars.partnerId);

					//find content type
					if (entry is KalturaMediaEntry)
					{
						var mediaEntry:KalturaMediaEntry = entry as KalturaMediaEntry;

						AnalyticsPluginLoader.setData("contentLength", mediaEntry.duration);
						var contentType:String;
						switch (mediaEntry.mediaType)
						{
							case KalturaMediaType.VIDEO:
								contentType = "video";
								break;
							case KalturaMediaType.AUDIO:
								contentType = "audio";
								break;
							case KalturaMediaType.IMAGE:
								contentType = "image";
								break;
							default:
								contentType = "live";
						}
						AnalyticsPluginLoader.setData("contentType", contentType);
					}

					AnalyticsPluginLoader.setData("device", Capabilities.os );
					AnalyticsPluginLoader.setData("playerId", configProxy.vo.flashvars.uiConfId);

					break;

				case NotificationType.SWITCHING_CHANGE_COMPLETE:
					AnalyticsPluginLoader.setData("flavorId", getFlavorIdByIndex(notification.getBody().newIndex));
					break;

				case NotificationType.MEDIA_ELEMENT_READY:
					//find starting flavor ID
					var flavorId:String;
					if (_mediaProxy.vo.deliveryType == StreamerType.HTTP)
					{
						flavorId = _mediaProxy.vo.selectedFlavorId;
					}
					else if (_mediaProxy.vo.deliveryType == StreamerType.RTMP)
					{
						flavorId = getFlavorIdByIndex(_mediaProxy.startingIndex);
					}

					if (flavorId)
						AnalyticsPluginLoader.setData("flavorId", flavorId);

					break;
			}
		}

		/**
		 * returns the id of the flavor asset in the given index. null if index is invalid.
		 * @param index
		 * @return
		 *
		 */
		private function getFlavorIdByIndex(index:int):String
		{
			var flavorId:String;

			if (index >= 0 &&
				_mediaProxy.vo.kalturaMediaFlavorArray &&
				index < _mediaProxy.vo.kalturaMediaFlavorArray.length)	{

				flavorId = (_mediaProxy.vo.kalturaMediaFlavorArray[index] as KalturaFlavorAsset).id;
			}

			return flavorId;
		}
	}
}

package com.kaltura.kdpfl.plugin
{
	import com.akamai.playeranalytics.AnalyticsPluginLoader;
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.model.type.StreamerType;
	import com.kaltura.types.KalturaMediaType;
	import com.kaltura.vo.KalturaBaseEntry;
	import com.kaltura.vo.KalturaFlavorAsset;
	import com.kaltura.vo.KalturaMediaEntry;

	import flash.system.Capabilities;

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

	public class akamaiMediaAnalyticsMediator extends Mediator
	{
		public static const NAME:String = "akamaiMediaAnalyticsMediator";
		private var _mediaProxy:MediaProxy;

		public function akamaiMediaAnalyticsMediator(viewComponent:Object=null)
		{
			super(NAME, viewComponent);
		}

		override public function onRegister():void
		{
			_mediaProxy = facade.retrieveProxy(MediaProxy.NAME) as MediaProxy;
			super.onRegister();
		}

		override public function listNotificationInterests():Array
		{
			return [NotificationType.MEDIA_READY, NotificationType.MEDIA_ELEMENT_READY];
		}

		override public function handleNotification(notification:INotification):void
		{
			switch (notification.getName())
			{
				case NotificationType.MEDIA_READY:
					//populate analytics metadata
					var configProxy:ConfigProxy = facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy;
					var entry:KalturaBaseEntry = _mediaProxy.vo.entry;

					AnalyticsPluginLoader.setData("title", entry.name);
					AnalyticsPluginLoader.setData("entryId", entry.id);
					AnalyticsPluginLoader.setData("category", entry.categories);
					AnalyticsPluginLoader.setData("publisherId", configProxy.vo.flashvars.partnerId);

					//find content type
					if (entry is KalturaMediaEntry)
					{
						var mediaEntry:KalturaMediaEntry = entry as KalturaMediaEntry;

						AnalyticsPluginLoader.setData("contentLength", mediaEntry.duration);
						var contentType:String;
						switch (mediaEntry.mediaType)
						{
							case KalturaMediaType.VIDEO:
								contentType = "video";
								break;
							case KalturaMediaType.AUDIO:
								contentType = "audio";
								break;
							case KalturaMediaType.IMAGE:
								contentType = "image";
								break;
							default:
								contentType = "live";
						}
						AnalyticsPluginLoader.setData("contentType", contentType);
					}

					AnalyticsPluginLoader.setData("device", Capabilities.os );
					AnalyticsPluginLoader.setData("playerId", configProxy.vo.flashvars.uiConfId);

					break;

				case NotificationType.MEDIA_ELEMENT_READY:
					//find starting flavor ID
					var flavorId:String;
					if (_mediaProxy.vo.deliveryType == StreamerType.HTTP)
					{
						flavorId = _mediaProxy.vo.selectedFlavorId;
					}
					else if (_mediaProxy.vo.deliveryType == StreamerType.RTMP)
					{
						flavorId = getFlavorIdByIndex(_mediaProxy.startingIndex);
					}

					if (flavorId)
						AnalyticsPluginLoader.setData("flavorId", flavorId);

					break;
			}
		}

		/**
		 * returns the id of the flavor asset in the given index. null if index is invalid.
		 * @param index
		 * @return
		 *
		 */
		private function getFlavorIdByIndex(index:int):String
		{
			var flavorId:String;

			if (index >= 0 &&
				_mediaProxy.vo.kalturaMediaFlavorArray &&
				index < _mediaProxy.vo.kalturaMediaFlavorArray.length)	{

				flavorId = (_mediaProxy.vo.kalturaMediaFlavorArray[index] as KalturaFlavorAsset).id;
			}

			return flavorId;
		}
	}
}

qnd: fix custom data after a clarification call with akamai

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