//
// $Id: $

package flash.errors {

public class MemoryError extends Error
{
    public function MemoryError(message:String = "")
    {
        super(message);
    }
}

public class EOFError extends Error
{
    public function EOFError(message:String = "")
    {
        super(message);
    }
}

public class IllegalOperationError extends Error
{
    public function IllegalOperationError (str :String = null)
    {
    }
}
}

package flash.events {

import flash.utils.Dictionary;

public class Event
{
    public static const CONNECT :String = "connect";
    public static const CLOSE :String = "close";
    public static const COMPLETE:String = "complete";

    public function Event (type :String, bubbles :Boolean = false, cancelable :Boolean = false)
    {
        if (bubbles || cancelable) {
            throw new Error("Not implemented");
        }
        _type = type;
    }

    public function get type () :String
    {
        return _type;
    }

    public function get target () :Object
    {
        return null;
    }

    public function clone () :Event
    {
        return new Event(_type);
    }

    private var _type :String;
}

public class TextEvent extends Event
{
    public var text :String;

    public function TextEvent (
        type :String, bubbles :Boolean = false, cancelable :Boolean = false, text :String = "")
    {
        super(type, bubbles, cancelable);
        this.text = text;
    }
}

public class ErrorEvent extends TextEvent
{
    public static const ERROR :String = "error";

    public function ErrorEvent (
        type :String, bubbles :Boolean = false, cancelable :Boolean = false,
        text :String = "", id :int = 0)
    {
        super(type, bubbles, cancelable, text);

        _errorId = id;
    }

    public function get errorID () :int
    {
        return _errorId;
    }

    protected var _errorId :int;
}

public class SecurityErrorEvent extends ErrorEvent
{
    public static const SECURITY_ERROR :String = "securityError";

    public function SecurityErrorEvent (
        type :String, bubbles :Boolean = false, cancelable :Boolean = false,
        text :String = "", id :int = 0)
    {
        super(type, bubbles, cancelable);
    }
}

public class EventDispatcher
{
    public function addEventListener (
        type :String, listener :Function, useCapture :Boolean = false,
        priority :int = 0, useWeakReference :Boolean = false) :void
    {
        if (useCapture || priority != 0 || useWeakReference) {
            throw new Error("Fancy addEventListener not implemented");
        }

        var listeners :Array = _listenerMap[type] as Array;
        if (listeners == null) {
            _listenerMap[type] = listeners = new Array();

        } else if (-1 == _listenerMap.indexOf(listener)) {
            return;
        }
        listeners.push(listener);
    }

    public function removeEventListener (
        type :String, listener :Function, useCapture :Boolean = false) :void
    {
        if (useCapture) {
            throw new Error("Fancy removeListener not implemented");
        }
        var listeners :Array = _listenerMap[type] as Array;
        if (listeners != null) {
            var ix :int = listeners.indexOf(listener);
            if (ix >= 0) {
                listeners.splice(ix, 1);
            }
        }
    }

    public function dispatchEvent (event :Event) :Boolean
    {
        var listeners :Array = _listenerMap[event.type] as Array;
        for each (var listener :Function in listeners) {
            try {
                listener(event);
            } catch (err :Error) {
                trace("Event[" + event + "] dispatch error: " + err);
            }
        }
        // TODO: "A value of true unless preventDefault() is called on the event,
        //        in which case it returns false. "
        return true;
    }

    private var _listenerMap :Dictionary = new Dictionary();
}

public class TimerEvent extends Event
{
    public static const TIMER :String = "timer";
    public static const TIMER_COMPLETE :String = "timerComplete";

    public function TimerEvent (
        type :String, bubbles :Boolean = false, cancelable :Boolean = false, text :String = "")
    {
        super(type, bubbles, cancelable);
    }
}
}

package flash.utils {

import flash.events.EventDispatcher;
import flash.events.TimerEvent;

import avmplus.*;

public function describeType (c :Object) :XML
{
    throw new Error("describeType() Not implemented");
}

public function getDefinitionByName (name :String) :Class
{
    return Domain.currentDomain.getClass(name.replace("::", "."));
}

public function getQualifiedClassName (c :*) :String
{
    return Domain.currentDomain.getClassName(c);
}

public function getQualifiedSuperclassName (c :*) :String
{
    throw new Error("getQualifiedSuperclassName() not implemented");
}

var timers :Dictionary = new Dictionary();
var timerIx :uint = 1;

public function setTimeout (closure :Function, delay :Number, ... arguments) :uint
{
    return createTimer(closure, delay, true, arguments);
}

public function setInterval (closure :Function, delay :Number, ... arguments) :uint
{
    return createTimer(closure, delay, false, arguments);
}

public function clearTimeout (id :uint): void
{
    destroyTimer(id);
}

public function clearInterval (id :uint): void
{
    destroyTimer(id);
}

public function getTimer () :int
{
    return System.getTimer();
}

function createTimer (closure :Function, delay :Number, timeout :Boolean, ... arguments) :uint
{
    var timer :Timer = new Timer(delay, timeout ? 1 : 0);
    var fun :Function = function (event :TimerEvent) :void {
        if (timeout) {
            destroyTimer(timerIx);
        }
        closure.apply(null, arguments);
    };
    timer.addEventListener(TimerEvent.TIMER, fun);
    timers[timerIx] = [ timer, fun ];
    return timerIx ++;
}

function destroyTimer (id :uint) :void
{
    var bits :Array = timers[id];
    if (bits) {
        bits[0].removeEventListener(TimerEvent.TIMER, bits[1]);
        bits[0].stop();
        delete timers[id];
    }
}

public interface IDataInput
{
    function readBytes(bytes :ByteArray, offset :uint=0, length :uint=0) :void;
    function readBoolean() :Boolean;
    function readByte() :int;
    function readUnsignedByte() :uint;
    function readObject () :*
    function readShort() :int;
    function readUnsignedShort() :uint;
    function readInt() :int;
    function readUnsignedInt() :uint;
    function readFloat() :Number;
    function readDouble() :Number;
    function readUTF() :String;
    function readUTFBytes(length :uint) :String;
    function get bytesAvailable() :uint;
    function get endian() :String;
    function set endian(type :String) :void;
}

public interface IDataOutput
{
    function writeBytes(bytes :ByteArray, offset :uint = 0, length :uint = 0) :void;
    function writeBoolean(value :Boolean) :void;
    function writeByte(value :int) :void;
    function writeObject (object :*) :void
    function writeShort(value :int) :void;
    function writeInt(value :int) :void;
    function writeUnsignedInt(value :uint) :void;
    function writeFloat(value :Number) :void;
    function writeDouble(value :Number) :void;
    function writeUTF(value :String) :void;
    function writeUTFBytes(value :String) :void;
    function get endian() :String;
    function set endian(type :String) :void;
    function get objectEncoding () :uint;
    function set objectEncoding (encoding :uint) :void;
}

public interface IExternalizable
{
    function readExternal (input: IDataInput) :void;
    function writeExternal (output: IDataOutput) :void;
}
}

package flash.net
{

import avmplus.Domain;

public function getClassByAlias (aliasName :String) :Class
{
    var className :String = AMF3.getClassNameByAlias(aliasName);
    if (className == null) {
        throw new VerifyError("Alias not registered.");
    }
    return Domain.currentDomain.getClass(className);
}

public function registerClassAlias (aliasName :String, classObject :Class) :void
{
    // TODO: protect against user code
    AMF3.registerClassAlias(aliasName, classObject);
}

public class ObjectEncoding
{
    public static const AMF0 :uint = 0;
    public static const AMF3 :uint = 3;
    public static const DEFAULT :uint = AMF3;
}
}

//
// $Id: $

package flash.errors {

public class MemoryError extends Error
{
    public function MemoryError(message:String = "")
    {
        super(message);
    }
}

public class EOFError extends Error
{
    public function EOFError(message:String = "")
    {
        super(message);
    }
}

public class IllegalOperationError extends Error
{
    public function IllegalOperationError (message :String = null)
    {
        super(message);
    }
}
}

package flash.events {

import flash.utils.Dictionary;

public class Event
{
    public static const CONNECT :String = "connect";
    public static const CLOSE :String = "close";
    public static const COMPLETE:String = "complete";

    public function Event (type :String, bubbles :Boolean = false, cancelable :Boolean = false)
    {
        if (bubbles || cancelable) {
            throw new Error("Not implemented");
        }
        _type = type;
    }

    public function get type () :String
    {
        return _type;
    }

    public function get target () :Object
    {
        return null;
    }

    public function clone () :Event
    {
        return new Event(_type);
    }

    private var _type :String;
}

public class TextEvent extends Event
{
    public var text :String;

    public function TextEvent (
        type :String, bubbles :Boolean = false, cancelable :Boolean = false, text :String = "")
    {
        super(type, bubbles, cancelable);
        this.text = text;
    }
}

public class ErrorEvent extends TextEvent
{
    public static const ERROR :String = "error";

    public function ErrorEvent (
        type :String, bubbles :Boolean = false, cancelable :Boolean = false,
        text :String = "", id :int = 0)
    {
        super(type, bubbles, cancelable, text);

        _errorId = id;
    }

    public function get errorID () :int
    {
        return _errorId;
    }

    protected var _errorId :int;
}

public class SecurityErrorEvent extends ErrorEvent
{
    public static const SECURITY_ERROR :String = "securityError";

    public function SecurityErrorEvent (
        type :String, bubbles :Boolean = false, cancelable :Boolean = false,
        text :String = "", id :int = 0)
    {
        super(type, bubbles, cancelable);
    }
}

public class EventDispatcher
{
    public function addEventListener (
        type :String, listener :Function, useCapture :Boolean = false,
        priority :int = 0, useWeakReference :Boolean = false) :void
    {
        if (useCapture || priority != 0 || useWeakReference) {
            throw new Error("Fancy addEventListener not implemented");
        }

        var listeners :Array = _listenerMap[type] as Array;
        if (listeners == null) {
            _listenerMap[type] = listeners = new Array();

        } else if (-1 == _listenerMap.indexOf(listener)) {
            return;
        }
        listeners.push(listener);
    }

    public function removeEventListener (
        type :String, listener :Function, useCapture :Boolean = false) :void
    {
        if (useCapture) {
            throw new Error("Fancy removeListener not implemented");
        }
        var listeners :Array = _listenerMap[type] as Array;
        if (listeners != null) {
            var ix :int = listeners.indexOf(listener);
            if (ix >= 0) {
                listeners.splice(ix, 1);
            }
        }
    }

    public function dispatchEvent (event :Event) :Boolean
    {
        var listeners :Array = _listenerMap[event.type] as Array;
        for each (var listener :Function in listeners) {
            try {
                listener(event);
            } catch (err :Error) {
                trace("Event[" + event + "] dispatch error: " + err);
            }
        }
        // TODO: "A value of true unless preventDefault() is called on the event,
        //        in which case it returns false. "
        return true;
    }

    private var _listenerMap :Dictionary = new Dictionary();
}

public class TimerEvent extends Event
{
    public static const TIMER :String = "timer";
    public static const TIMER_COMPLETE :String = "timerComplete";

    public function TimerEvent (
        type :String, bubbles :Boolean = false, cancelable :Boolean = false, text :String = "")
    {
        super(type, bubbles, cancelable);
    }
}
}

package flash.utils {

import flash.events.EventDispatcher;
import flash.events.TimerEvent;

import avmplus.*;

public function describeType (c :Object) :XML
{
    throw new Error("describeType() Not implemented");
}

public function getDefinitionByName (name :String) :Class
{
    return Domain.currentDomain.getClass(name.replace("::", "."));
}

public function getQualifiedClassName (c :*) :String
{
    return Domain.currentDomain.getClassName(c);
}

public function getQualifiedSuperclassName (c :*) :String
{
    throw new Error("getQualifiedSuperclassName() not implemented");
}

var timers :Dictionary = new Dictionary();
var timerIx :uint = 1;

public function setTimeout (closure :Function, delay :Number, ... arguments) :uint
{
    return createTimer(closure, delay, true, arguments);
}

public function setInterval (closure :Function, delay :Number, ... arguments) :uint
{
    return createTimer(closure, delay, false, arguments);
}

public function clearTimeout (id :uint): void
{
    destroyTimer(id);
}

public function clearInterval (id :uint): void
{
    destroyTimer(id);
}

public function getTimer () :int
{
    return System.getTimer();
}

function createTimer (closure :Function, delay :Number, timeout :Boolean, ... arguments) :uint
{
    var timer :Timer = new Timer(delay, timeout ? 1 : 0);
    var fun :Function = function (event :TimerEvent) :void {
        if (timeout) {
            destroyTimer(timerIx);
        }
        closure.apply(null, arguments);
    };
    timer.addEventListener(TimerEvent.TIMER, fun);
    timers[timerIx] = [ timer, fun ];
    return timerIx ++;
}

function destroyTimer (id :uint) :void
{
    var bits :Array = timers[id];
    if (bits) {
        bits[0].removeEventListener(TimerEvent.TIMER, bits[1]);
        bits[0].stop();
        delete timers[id];
    }
}

public interface IDataInput
{
    function readBytes(bytes :ByteArray, offset :uint=0, length :uint=0) :void;
    function readBoolean() :Boolean;
    function readByte() :int;
    function readUnsignedByte() :uint;
    function readObject () :*
    function readShort() :int;
    function readUnsignedShort() :uint;
    function readInt() :int;
    function readUnsignedInt() :uint;
    function readFloat() :Number;
    function readDouble() :Number;
    function readUTF() :String;
    function readUTFBytes(length :uint) :String;
    function get bytesAvailable() :uint;
    function get endian() :String;
    function set endian(type :String) :void;
}

public interface IDataOutput
{
    function writeBytes(bytes :ByteArray, offset :uint = 0, length :uint = 0) :void;
    function writeBoolean(value :Boolean) :void;
    function writeByte(value :int) :void;
    function writeObject (object :*) :void
    function writeShort(value :int) :void;
    function writeInt(value :int) :void;
    function writeUnsignedInt(value :uint) :void;
    function writeFloat(value :Number) :void;
    function writeDouble(value :Number) :void;
    function writeUTF(value :String) :void;
    function writeUTFBytes(value :String) :void;
    function get endian() :String;
    function set endian(type :String) :void;
    function get objectEncoding () :uint;
    function set objectEncoding (encoding :uint) :void;
}

public interface IExternalizable
{
    function readExternal (input: IDataInput) :void;
    function writeExternal (output: IDataOutput) :void;
}
}

package flash.net
{

import avmplus.Domain;

public function getClassByAlias (aliasName :String) :Class
{
    var className :String = AMF3.getClassNameByAlias(aliasName);
    if (className == null) {
        throw new VerifyError("Alias not registered.");
    }
    return Domain.currentDomain.getClass(className);
}

public function registerClassAlias (aliasName :String, classObject :Class) :void
{
    // TODO: protect against user code
    AMF3.registerClassAlias(aliasName, classObject);
}

public class ObjectEncoding
{
    public static const AMF0 :uint = 0;
    public static const AMF3 :uint = 3;
    public static const DEFAULT :uint = AMF3;
}
}

Tweak fix.

Tweak fix.


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

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

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

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

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

		private var _bindings			: Object							= null;

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

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

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

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

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

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

		private var _enabled			: Boolean							= true;

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

		private var _bindingsConsumer	: DrawCallBindingsConsumer;

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

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

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

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

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

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

				if (_localToWorld != null && _worldToScreen != null)
				{
					var worldSpacePosition 	: Vector4	= _localToWorld.transformVector(
						_center, TMP_VECTOR4
					);
					var screenSpacePosition : Vector4 	= _worldToScreen.transformVector(
						worldSpacePosition, TMP_VECTOR4
					);

					_depth = screenSpacePosition.z / screenSpacePosition.w;
				}
			}

			return _depth;
		}

		public function configure(program		: Program3DResource,
								  geometry		: Geometry,
								  meshBindings	: DataBindings,
								  sceneBindings	: DataBindings,
								  computeDepth	: Boolean) : void
		{
			_invalidDepth = computeDepth;

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

		public function unsetBindings(meshBindings	: DataBindings,
									  sceneBindings	: DataBindings) : void
		{
			if (_bindingsConsumer != null)
			{
				meshBindings.removeConsumer(_bindingsConsumer);
				sceneBindings.removeConsumer(_bindingsConsumer);
			}

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

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

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

			_bindingsConsumer	= new DrawCallBindingsConsumer(
				_bindings,
				_cpuConstants,
				_vsConstants,
				_fsConstants,
				_fsTextures
			);
			_bindingsConsumer.enabled = _enabled;

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

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

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

		/**
		 * Obtain a reference to each buffer and offset that apply() may possibly need.
		 *
		 */
		public function setGeometry(geometry : Geometry, frame : uint = 0) : void
		{
			if (!_vsInputComponents)
				return ;

			updateGeometry(geometry);

			_center = geometry.boundingSphere
				? geometry.boundingSphere.center
				: Vector4.ZERO;

			_numVertexComponents = _vsInputComponents.length;
			_indexBuffer		 = geometry.indexStream.resource;
			_firstIndex			 = geometry.firstIndex;
			_numTriangles		 = geometry.numTriangles;

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

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

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

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

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

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

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

				_invalidDepth = true;
			}
		}

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

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

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

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

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

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

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

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

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

		public function setParameter(name : String, value : Object) : void
		{
			_bindingsConsumer.setProperty(name, value);
		}

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

			_invalidDepth = true;
		}
	}
}

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

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

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

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

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

		private var _bindings			: Object							= null;

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

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

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

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

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

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

		private var _enabled			: Boolean							= true;

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

		private var _bindingsConsumer	: DrawCallBindingsConsumer;

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

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

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

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

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

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

				if (_localToWorld != null && _worldToScreen != null)
				{
					var worldSpacePosition 	: Vector4	= _localToWorld.transformVector(
						_center, TMP_VECTOR4
					);
					var screenSpacePosition : Vector4 	= _worldToScreen.transformVector(
						worldSpacePosition, TMP_VECTOR4
					);

					_depth = screenSpacePosition.z / screenSpacePosition.w;
				}
			}

			return _depth;
		}

		public function configure(program		: Program3DResource,
								  geometry		: Geometry,
								  meshBindings	: DataBindings,
								  sceneBindings	: DataBindings,
								  computeDepth	: Boolean) : void
		{
			_invalidDepth = computeDepth;

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

		public function unsetBindings(meshBindings	: DataBindings,
									  sceneBindings	: DataBindings) : void
		{
			if (_bindingsConsumer != null)
			{
				meshBindings.removeConsumer(_bindingsConsumer);
				sceneBindings.removeConsumer(_bindingsConsumer);
			}

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

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

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

			_bindingsConsumer	= new DrawCallBindingsConsumer(
				_bindings,
				_cpuConstants,
				_vsConstants,
				_fsConstants,
				_fsTextures
			);
			_bindingsConsumer.enabled = _enabled;

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

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

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

		/**
		 * Obtain a reference to each buffer and offset that apply() may possibly need.
		 *
		 */
		public function setGeometry(geometry : Geometry, frame : uint = 0) : void
		{
			if (!_vsInputComponents)
				return ;

			updateGeometry(geometry);

			_center = geometry.boundingSphere
				? geometry.boundingSphere.center
				: Vector4.ZERO;

			_numVertexComponents = _vsInputComponents.length;
			_indexBuffer		 = geometry.indexStream.resource;
			_firstIndex			 = geometry.firstIndex;
			_numTriangles		 = geometry.numTriangles;

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

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

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

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

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

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

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

				_invalidDepth = true;
			}
		}

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

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

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

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

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

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

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

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

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

		public function setParameter(name : String, value : Object) : void
		{
			_bindingsConsumer.setProperty(name, value);
		}

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

			_invalidDepth = true;
		}
	}
}

fix DrawCall.updateGeometry() to avoid re-computing normals if they already exist when computing tangent space


//
// Flump - Copyright 2013 Flump Authors

package flump.export {

import aspire.util.F;
import aspire.util.StringUtil;

import flash.desktop.NativeApplication;
import flash.display.NativeMenu;
import flash.display.NativeMenuItem;
import flash.display.Stage;
import flash.display.StageQuality;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filesystem.File;
import flash.utils.IDataOutput;

import flump.executor.Executor;
import flump.executor.Future;
import flump.xfl.ParseError;
import flump.xfl.XflLibrary;

import mx.events.PropertyChangeEvent;
import mx.managers.PopUpManager;

import spark.components.DataGrid;
import spark.components.Window;
import spark.events.GridSelectionEvent;

public class ProjectController extends ExportController
{
    public static const NA :NativeApplication = NativeApplication.nativeApplication;

    public function ProjectController (configFile :File = null) {
        _win = new ProjectWindow();
        _win.open();
        _errorsGrid = _win.errors;
        _flashDocsGrid = _win.libraries;

        _confFile = configFile;
        if (_confFile == null) {
            _conf = new ProjectConf();
        } else {
            if (readProjectConfig()) {
                setImportDirectory(_importDirectory);
            } else {
                _importDirectory = null;
                _confFile = null;
                _conf = null;
            }
        }

        var curSelection :DocStatus = null;
        _flashDocsGrid.addEventListener(GridSelectionEvent.SELECTION_CHANGE, function (..._) :void {
            log.info("Changed", "selected", _flashDocsGrid.selectedIndices);
            onSelectedItemChanged();

            if (curSelection != null) {
                curSelection.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE,
                    onSelectedItemChanged);
            }
            var newSelection :DocStatus = _flashDocsGrid.selectedItem as DocStatus;
            if (newSelection != null) {
                newSelection.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE,
                    onSelectedItemChanged);
            }
            curSelection = newSelection;
        });

        // Reload
        _win.reload.addEventListener(MouseEvent.CLICK, F.bind(reloadNow));

        // Export
        _win.export.addEventListener(MouseEvent.CLICK, function (..._) :void {
            for each (var status :DocStatus in _flashDocsGrid.selectedItems) {
                exportFlashDocument(status);
            }
        });

        // Preview
        _win.preview.addEventListener(MouseEvent.CLICK, function (..._) :void {
            FlumpApp.app.showPreviewWindow(_conf, _flashDocsGrid.selectedItem.lib);
        });

        // Export All, Modified
        _win.exportAll.addEventListener(MouseEvent.CLICK, F.bind(exportAll, false));
        _win.exportModified.addEventListener(MouseEvent.CLICK, F.bind(exportAll, true));

        // Import/Export directories
        _importChooser = new DirChooser(null, _win.importRoot, _win.browseImport);
        _importChooser.changed.connect(setImportDirectory);
        _exportChooser = new DirChooser(null, _win.exportRoot, _win.browseExport);
        _exportChooser.changed.connect(F.bind(reloadNow));

        _importChooser.changed.connect(F.bind(setProjectDirty, true));
        _exportChooser.changed.connect(F.bind(setProjectDirty, true));

        // Edit Formats
        var editFormatsController :EditFormatsController = null;
        _win.editFormats.addEventListener(MouseEvent.CLICK, function (..._) :void {
            if (editFormatsController == null || editFormatsController.closed) {
                editFormatsController = new EditFormatsController(_conf);
                editFormatsController.formatsChanged.connect(updateUiFromConf);
                editFormatsController.formatsChanged.connect(F.bind(setProjectDirty, true));
            } else {
                editFormatsController.show();
            }
        });

        _win.addEventListener(Event.CLOSING, function (e :Event) :void {
            if (_projectDirty) {
                e.preventDefault();
                promptToSaveChanges();
            }
        });

        updateUiFromConf();
        updateWindowTitle();

        setupMenus();
    }

    public function get projectDirty () :Boolean {
        return _projectDirty;
    }

    public function save (onSuccess :Function = null) :void {
        if (_confFile == null) {
            saveAs(onSuccess);
        } else {
            saveConf(onSuccess);
        }
    }

    public function saveAs (onSuccess :Function = null) :void {
        var file :File = new File();
        file.addEventListener(Event.SELECT, function (..._) :void {
            // Ensure the filename ends with .flump
            if (!StringUtil.endsWith(file.name.toLowerCase(), ".flump")) {
                file = file.parent.resolvePath(file.name + ".flump");
            }

            _confFile = file;
            saveConf(onSuccess);
        });
        file.browseForSave("Save Flump Configuration");
    }

    public function get configFile () :File {
        return _confFile;
    }

    public function get win () :Window {
        return _win;
    }

    protected function exportAll (modifiedOnly :Boolean) :void {
        // if we have one or more combined export format, publish them
        if (hasCombinedExportConfig()) {
            var valid :Boolean = _flashDocsGrid.dataProvider.toArray()
                .every(function (status :DocStatus,..._) :Boolean { return status.isValid; });
            if (valid) exportCombined();
        }
        // now publish any appropriate single formats
        if (hasSingleExportConfig()) {
            for each (var status :DocStatus in _flashDocsGrid.dataProvider.toArray()) {
                if (status.isValid && (!modifiedOnly || status.isModified)) {
                    exportFlashDocument(status);
                }
            }
        }
    }

    protected function promptToSaveChanges () :void {
        var unsavedWindow :UnsavedChangesWindow = new UnsavedChangesWindow();
        unsavedWindow.x = (_win.width - unsavedWindow.width) * 0.5;
        unsavedWindow.y = (_win.height - unsavedWindow.height) * 0.5;
        PopUpManager.addPopUp(unsavedWindow, _win, true);

        unsavedWindow.closeButton.visible = false;
        unsavedWindow.prompt.text = "Save changes to '" + projectName + "'?";

        unsavedWindow.cancel.addEventListener(MouseEvent.CLICK, function (..._) :void {
            PopUpManager.removePopUp(unsavedWindow);
        });

        unsavedWindow.dontSave.addEventListener(MouseEvent.CLICK, function (..._) :void {
            PopUpManager.removePopUp(unsavedWindow);
            _projectDirty = false;
            _win.close();
        });

        unsavedWindow.save.addEventListener(MouseEvent.CLICK, function (..._) :void {
            PopUpManager.removePopUp(unsavedWindow);
            save(F.bind(_win.close));
        });
    }

    protected function setupMenus () :void {
        if (NativeApplication.supportsMenu) {
            // If we're on a Mac, the menus will be set up at the application level.
            return;
        }

        _win.nativeWindow.menu = new NativeMenu();

        var fileMenuItem  :NativeMenuItem =
            _win.nativeWindow.menu.addSubmenu(new NativeMenu(), "File");

        // Add save and save as by index to work with the existing items on Mac
        // Mac menus have an existing "Close" item, so everything we add should go ahead of that
        var newMenuItem :NativeMenuItem = fileMenuItem.submenu.addItemAt(new NativeMenuItem("New Project"), 0);
        newMenuItem.keyEquivalent = "n";
        newMenuItem.addEventListener(Event.SELECT, function (..._) :void {
            FlumpApp.app.newProject();
        });

        var openMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Open Project..."), 1);
        openMenuItem.keyEquivalent = "o";
        openMenuItem.addEventListener(Event.SELECT, function (..._) :void {
            FlumpApp.app.showOpenProjectDialog();
        });
        fileMenuItem.submenu.addItemAt(new NativeMenuItem("Sep", /*separator=*/true), 2);

        const saveMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Save Project"), 3);
        saveMenuItem.keyEquivalent = "s";
        saveMenuItem.addEventListener(Event.SELECT, F.bind(save));

        const saveAsMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Save Project As..."), 4);
        saveAsMenuItem.keyEquivalent = "S";
        saveAsMenuItem.addEventListener(Event.SELECT, F.bind(saveAs));
    }

    protected function updateWindowTitle () :void {
        var name :String = this.projectName;
        if (_projectDirty) name += "*";
        _win.title = name;
    }

    protected function saveConf (onSuccess :Function) :void {
        Files.write(_confFile, function (out :IDataOutput) :void {
            // Set directories relative to where this file is being saved. Fall back to absolute
            // paths if relative paths aren't possible.
            if (_importChooser.dir != null) {
                _conf.importDir = _confFile.parent.getRelativePath(_importChooser.dir, /*useDotDot=*/true);
                if (_conf.importDir == null) _conf.importDir = _importChooser.dir.nativePath;
            }

            if (_exportChooser.dir != null) {
                _conf.exportDir = _confFile.parent.getRelativePath(_exportChooser.dir, /*useDotDot=*/true);
                if (_conf.exportDir == null) _conf.exportDir = _exportChooser.dir.nativePath;
            }

            out.writeUTFBytes(JSON.stringify(_conf, null, /*space=*/2));

            setProjectDirty(false);
            updateWindowTitle();

            if (onSuccess != null) {
                onSuccess();
            }
        });
    }

    protected function reloadNow () :void {
        setImportDirectory(_importChooser.dir);
        onSelectedItemChanged();
    }

    protected function updateUiFromConf (..._) :void {
        if (_confFile != null) {
            _importChooser.dir = (_conf.importDir != null) ? _confFile.parent.resolvePath(_conf.importDir) : null;
            _exportChooser.dir = (_conf.exportDir != null) ? _confFile.parent.resolvePath(_conf.exportDir) : null;
        } else {
            _importChooser.dir = null;
            _exportChooser.dir = null;
        }

        var formatNames :Array = [];
        var hasCombined :Boolean = false;
        if (_conf != null) {
            for each (var export :ExportConf in _conf.exports) {
                formatNames.push(export.description);
                hasCombined ||= export.combine;
            }
        }
        _win.formatOverview.text = formatNames.join(", ");
        _win.exportAll.label = hasCombined ? "Export Combined" : "Export All";
        checkValid();
        _win.exportModified.enabled = !hasCombined;
        _win.export.enabled = !hasCombined;

        updateWindowTitle();
    }

    protected function hasCombinedExportConfig () :Boolean {
        if (_conf == null) return false;
        for each (var config :ExportConf in _conf.exports) if (config.combine) return true;
        return false;
    }

    protected function hasSingleExportConfig () :Boolean {
        if (_conf == null) return false;
        for each (var config :ExportConf in _conf.exports) if (!config.combine) return true;
        return false;
    }

    protected function onSelectedItemChanged (..._) :void {
        _win.export.enabled = !hasCombinedExportConfig() && _exportChooser.dir != null &&
            _flashDocsGrid.selectionLength > 0 &&
            _flashDocsGrid.selectedItems.some(function (status :DocStatus, ..._) :Boolean {
                return status.isValid;
            });

        var status :DocStatus = _flashDocsGrid.selectedItem as DocStatus;
        _win.preview.enabled = status != null && status.isValid;

        _win.selectedItem.text = (status == null ? "" : status.path);
    }

    protected function createPublisher () :Publisher {
        if (_exportChooser.dir == null || _conf.exports.length == 0) return null;
        return new Publisher(_exportChooser.dir, _conf, projectName);
    }

    protected function setImportDirectory (dir :File) :void {
        _importDirectory = dir;
        _flashDocsGrid.dataProvider.removeAll();
        _errorsGrid.dataProvider.removeAll();
        if (dir == null) {
            return;

        }
        if (_docFinder != null) {
            _docFinder.shutdownNow();
        }
        _docFinder = new Executor();
        findFlashDocuments(dir, _docFinder, true);
        _win.reload.enabled = true;
    }

    protected function exportFlashDocument (status :DocStatus) :void {
        const stage :Stage = NA.activeWindow.stage;
        const prevQuality :String = stage.quality;

        stage.quality = StageQuality.BEST;

        try {
            if (_exportChooser.dir == null) {
                throw new Error("No export directory specified.");
            }
            if (_conf.exports.length == 0) {
                throw new Error("No export formats specified.");
            }
            var published :int = createPublisher().publishSingle(status.lib);
            if (published == 0) {
                throw new Error("No suitable formats were found for publishing");
            }
        } catch (e :Error) {
            log.warning("publishing failed", e);
            ErrorWindowMgr.showErrorPopup("Publishing Failed", e.message, _win);
        }

        stage.quality = prevQuality;
        status.updateModified(Ternary.FALSE);
    }

    protected function exportCombined () :void {
        const stage :Stage = NA.activeWindow.stage;
        const prevQuality :String = stage.quality;

        stage.quality = StageQuality.BEST;

        try {
            if (_exportChooser.dir == null) {
                throw new Error("No export directory specified.");
            }
            if (_conf.exports.length == 0) {
                throw new Error("No export formats specified.");
            }
            var published :int = createPublisher().publishCombined(getLibs());
            if (published == 0) {
                throw new Error("No suitable formats were found for publishing");
            }
        } catch (e :Error) {
            log.warning("publishing failed", e);
            ErrorWindowMgr.showErrorPopup("Publishing Failed", e.message, _win);
        }

        stage.quality = prevQuality;
        for each (var status :DocStatus in _flashDocsGrid.dataProvider) {
            status.updateModified(Ternary.FALSE);
        }
    }

    protected function checkModified () :void {
        var libs :Vector.<XflLibrary> = getLibs();
        if (libs == null) return; // not done loading yet

        // all the docs we know about have been loaded
        var pub :Publisher = createPublisher();
        for (var ii :int = 0; ii < libs.length; ii++) {
            var status :DocStatus = _flashDocsGrid.dataProvider[ii];
            status.updateModified(Ternary.of(pub == null || pub.modified(libs, ii)))
        }
    }

    protected function checkValid () :void {
        if (getLibs() == null) {
            _win.exportAll.enabled = false;
            return;
        }

        _win.exportAll.enabled = !hasCombinedExportConfig() || _flashDocsGrid.dataProvider.toArray()
            .every(function (status :DocStatus, ..._) :Boolean {
                return status.isValid;
            });
    }

    override protected function setProjectDirty (val :Boolean) :void {
        super.setProjectDirty(val);
        updateWindowTitle();
    }

    override protected function handleParseError (err :ParseError) :void {
        _errorsGrid.dataProvider.addItem(err);
    }

    override protected function docLoadSucceeded (doc :DocStatus, lib :XflLibrary) :void {
        super.docLoadSucceeded(doc, lib);
        checkModified();
        checkValid();
    }

    override protected function docLoadFailed (file :File, doc :DocStatus, err :*) :void {
        super.docLoadFailed(file, doc, err);
        trace("Failed to load " + file.nativePath + ": " + err);
        throw err;
    }

    override protected function addDoc (status :DocStatus) :void {
        _flashDocsGrid.dataProvider.addItem(status);
    }

    override protected function getDocs () :Array {
        return _flashDocsGrid.dataProvider.toArray();
    }

    protected var _docFinder :Executor;
    protected var _win :ProjectWindow;
    protected var _flashDocsGrid :DataGrid;
    protected var _errorsGrid :DataGrid;
    protected var _exportChooser :DirChooser;
    protected var _importChooser :DirChooser;
}
}

//
// Flump - Copyright 2013 Flump Authors

package flump.export {

import aspire.util.F;
import aspire.util.StringUtil;

import flash.desktop.NativeApplication;
import flash.display.NativeMenu;
import flash.display.NativeMenuItem;
import flash.display.Stage;
import flash.display.StageQuality;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filesystem.File;
import flash.utils.IDataOutput;

import flump.executor.Executor;
import flump.xfl.ParseError;
import flump.xfl.XflLibrary;

import mx.events.PropertyChangeEvent;
import mx.managers.PopUpManager;

import spark.components.DataGrid;
import spark.components.Window;
import spark.events.GridSelectionEvent;

public class ProjectController extends ExportController
{
    public static const NA :NativeApplication = NativeApplication.nativeApplication;

    public function ProjectController (configFile :File = null) {
        _win = new ProjectWindow();
        _win.open();
        _errorsGrid = _win.errors;
        _flashDocsGrid = _win.libraries;

        _confFile = configFile;
        if (_confFile == null) {
            _conf = new ProjectConf();
        } else {
            if (readProjectConfig()) {
                setImportDirectory(_importDirectory);
            } else {
                _importDirectory = null;
                _confFile = null;
                _conf = null;
            }
        }

        var curSelection :DocStatus = null;
        _flashDocsGrid.addEventListener(GridSelectionEvent.SELECTION_CHANGE, function (..._) :void {
            log.info("Changed", "selected", _flashDocsGrid.selectedIndices);
            onSelectedItemChanged();

            if (curSelection != null) {
                curSelection.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE,
                    onSelectedItemChanged);
            }
            var newSelection :DocStatus = _flashDocsGrid.selectedItem as DocStatus;
            if (newSelection != null) {
                newSelection.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE,
                    onSelectedItemChanged);
            }
            curSelection = newSelection;
        });

        // Reload
        _win.reload.addEventListener(MouseEvent.CLICK, F.bind(reloadNow));

        // Export
        _win.export.addEventListener(MouseEvent.CLICK, function (..._) :void {
            for each (var status :DocStatus in _flashDocsGrid.selectedItems) {
                exportFlashDocument(status);
            }
        });

        // Preview
        _win.preview.addEventListener(MouseEvent.CLICK, function (..._) :void {
            FlumpApp.app.showPreviewWindow(_conf, _flashDocsGrid.selectedItem.lib);
        });

        // Export All, Modified
        _win.exportAll.addEventListener(MouseEvent.CLICK, F.bind(exportAll, false));
        _win.exportModified.addEventListener(MouseEvent.CLICK, F.bind(exportAll, true));

        // Import/Export directories
        _importChooser = new DirChooser(null, _win.importRoot, _win.browseImport);
        _importChooser.changed.connect(setImportDirectory);
        _exportChooser = new DirChooser(null, _win.exportRoot, _win.browseExport);
        _exportChooser.changed.connect(F.bind(reloadNow));

        _importChooser.changed.connect(F.bind(setProjectDirty, true));
        _exportChooser.changed.connect(F.bind(setProjectDirty, true));

        // Edit Formats
        var editFormatsController :EditFormatsController = null;
        _win.editFormats.addEventListener(MouseEvent.CLICK, function (..._) :void {
            if (editFormatsController == null || editFormatsController.closed) {
                editFormatsController = new EditFormatsController(_conf);
                editFormatsController.formatsChanged.connect(updateUiFromConf);
                editFormatsController.formatsChanged.connect(F.bind(setProjectDirty, true));
            } else {
                editFormatsController.show();
            }
        });

        _win.addEventListener(Event.CLOSING, function (e :Event) :void {
            if (_projectDirty) {
                e.preventDefault();
                promptToSaveChanges();
            }
        });

        updateUiFromConf();
        updateWindowTitle();

        setupMenus();
    }

    public function get projectDirty () :Boolean {
        return _projectDirty;
    }

    public function save (onSuccess :Function = null) :void {
        if (_confFile == null) {
            saveAs(onSuccess);
        } else {
            saveConf(onSuccess);
        }
    }

    public function saveAs (onSuccess :Function = null) :void {
        var file :File = new File();
        file.addEventListener(Event.SELECT, function (..._) :void {
            // Ensure the filename ends with .flump
            if (!StringUtil.endsWith(file.name.toLowerCase(), ".flump")) {
                file = file.parent.resolvePath(file.name + ".flump");
            }

            _confFile = file;
            saveConf(onSuccess);
        });
        file.browseForSave("Save Flump Configuration");
    }

    public function get configFile () :File {
        return _confFile;
    }

    public function get win () :Window {
        return _win;
    }

    protected function exportAll (modifiedOnly :Boolean) :void {
        // if we have one or more combined export format, publish them
        if (hasCombinedExportConfig()) {
            var valid :Boolean = _flashDocsGrid.dataProvider.toArray()
                .every(function (status :DocStatus,..._) :Boolean { return status.isValid; });
            if (valid) exportCombined();
        }
        // now publish any appropriate single formats
        if (hasSingleExportConfig()) {
            for each (var status :DocStatus in _flashDocsGrid.dataProvider.toArray()) {
                if (status.isValid && (!modifiedOnly || status.isModified)) {
                    exportFlashDocument(status);
                }
            }
        }
    }

    protected function promptToSaveChanges () :void {
        var unsavedWindow :UnsavedChangesWindow = new UnsavedChangesWindow();
        unsavedWindow.x = (_win.width - unsavedWindow.width) * 0.5;
        unsavedWindow.y = (_win.height - unsavedWindow.height) * 0.5;
        PopUpManager.addPopUp(unsavedWindow, _win, true);

        unsavedWindow.closeButton.visible = false;
        unsavedWindow.prompt.text = "Save changes to '" + projectName + "'?";

        unsavedWindow.cancel.addEventListener(MouseEvent.CLICK, function (..._) :void {
            PopUpManager.removePopUp(unsavedWindow);
        });

        unsavedWindow.dontSave.addEventListener(MouseEvent.CLICK, function (..._) :void {
            PopUpManager.removePopUp(unsavedWindow);
            _projectDirty = false;
            _win.close();
        });

        unsavedWindow.save.addEventListener(MouseEvent.CLICK, function (..._) :void {
            PopUpManager.removePopUp(unsavedWindow);
            save(F.bind(_win.close));
        });
    }

    protected function setupMenus () :void {
        if (NativeApplication.supportsMenu) {
            // If we're on a Mac, the menus will be set up at the application level.
            return;
        }

        _win.nativeWindow.menu = new NativeMenu();

        var fileMenuItem  :NativeMenuItem =
            _win.nativeWindow.menu.addSubmenu(new NativeMenu(), "File");

        // Add save and save as by index to work with the existing items on Mac
        // Mac menus have an existing "Close" item, so everything we add should go ahead of that
        var newMenuItem :NativeMenuItem = fileMenuItem.submenu.addItemAt(new NativeMenuItem("New Project"), 0);
        newMenuItem.keyEquivalent = "n";
        newMenuItem.addEventListener(Event.SELECT, function (..._) :void {
            FlumpApp.app.newProject();
        });

        var openMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Open Project..."), 1);
        openMenuItem.keyEquivalent = "o";
        openMenuItem.addEventListener(Event.SELECT, function (..._) :void {
            FlumpApp.app.showOpenProjectDialog();
        });
        fileMenuItem.submenu.addItemAt(new NativeMenuItem("Sep", /*separator=*/true), 2);

        const saveMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Save Project"), 3);
        saveMenuItem.keyEquivalent = "s";
        saveMenuItem.addEventListener(Event.SELECT, F.bind(save));

        const saveAsMenuItem :NativeMenuItem =
            fileMenuItem.submenu.addItemAt(new NativeMenuItem("Save Project As..."), 4);
        saveAsMenuItem.keyEquivalent = "S";
        saveAsMenuItem.addEventListener(Event.SELECT, F.bind(saveAs));
    }

    protected function updateWindowTitle () :void {
        var name :String = this.projectName;
        if (_projectDirty) name += "*";
        _win.title = name;
    }

    protected function saveConf (onSuccess :Function) :void {
        Files.write(_confFile, function (out :IDataOutput) :void {
            // Set directories relative to where this file is being saved. Fall back to absolute
            // paths if relative paths aren't possible.
            if (_importChooser.dir != null) {
                _conf.importDir = _confFile.parent.getRelativePath(_importChooser.dir, /*useDotDot=*/true);
                if (_conf.importDir == null) _conf.importDir = _importChooser.dir.nativePath;
            }

            if (_exportChooser.dir != null) {
                _conf.exportDir = _confFile.parent.getRelativePath(_exportChooser.dir, /*useDotDot=*/true);
                if (_conf.exportDir == null) _conf.exportDir = _exportChooser.dir.nativePath;
            }

            out.writeUTFBytes(JSON.stringify(_conf, null, /*space=*/2));

            setProjectDirty(false);
            updateWindowTitle();

            if (onSuccess != null) {
                onSuccess();
            }
        });
    }

    protected function reloadNow () :void {
        setImportDirectory(_importChooser.dir);
        onSelectedItemChanged();
    }

    protected function updateUiFromConf (..._) :void {
        if (_confFile != null) {
            _importChooser.dir = (_conf.importDir != null) ? _confFile.parent.resolvePath(_conf.importDir) : null;
            _exportChooser.dir = (_conf.exportDir != null) ? _confFile.parent.resolvePath(_conf.exportDir) : null;
        } else {
            _importChooser.dir = null;
            _exportChooser.dir = null;
        }

        var formatNames :Array = [];
        var hasCombined :Boolean = false;
        if (_conf != null) {
            for each (var exportConf :ExportConf in _conf.exports) {
                formatNames.push(exportConf.description);
                hasCombined ||= exportConf.combine;
            }
        }
        _win.formatOverview.text = formatNames.join(", ");
        _win.exportAll.label = hasCombined ? "Export Combined" : "Export All";
        checkValid();
        _win.exportModified.enabled = !hasCombined;
        _win.export.enabled = !hasCombined;

        updateWindowTitle();
    }

    protected function hasCombinedExportConfig () :Boolean {
        if (_conf == null) return false;
        for each (var config :ExportConf in _conf.exports) if (config.combine) return true;
        return false;
    }

    protected function hasSingleExportConfig () :Boolean {
        if (_conf == null) return false;
        for each (var config :ExportConf in _conf.exports) if (!config.combine) return true;
        return false;
    }

    protected function onSelectedItemChanged (..._) :void {
        _win.export.enabled = !hasCombinedExportConfig() && _exportChooser.dir != null &&
            _flashDocsGrid.selectionLength > 0 &&
            _flashDocsGrid.selectedItems.some(function (status :DocStatus, ..._) :Boolean {
                return status.isValid;
            });

        var status :DocStatus = _flashDocsGrid.selectedItem as DocStatus;
        _win.preview.enabled = status != null && status.isValid;

        _win.selectedItem.text = (status == null ? "" : status.path);
    }

    protected function createPublisher () :Publisher {
        if (_exportChooser.dir == null || _conf.exports.length == 0) return null;
        return new Publisher(_exportChooser.dir, _conf, projectName);
    }

    protected function setImportDirectory (dir :File) :void {
        _importDirectory = dir;
        _flashDocsGrid.dataProvider.removeAll();
        _errorsGrid.dataProvider.removeAll();
        if (dir == null) {
            return;

        }
        if (_docFinder != null) {
            _docFinder.shutdownNow();
        }
        _docFinder = new Executor();
        findFlashDocuments(dir, _docFinder, true);
        _win.reload.enabled = true;
    }

    protected function exportFlashDocument (status :DocStatus) :void {
        const stage :Stage = NA.activeWindow.stage;
        const prevQuality :String = stage.quality;

        stage.quality = StageQuality.BEST;

        try {
            if (_exportChooser.dir == null) {
                throw new Error("No export directory specified.");
            }
            if (_conf.exports.length == 0) {
                throw new Error("No export formats specified.");
            }
            var published :int = createPublisher().publishSingle(status.lib);
            if (published == 0) {
                throw new Error("No suitable formats were found for publishing");
            }
        } catch (e :Error) {
            log.warning("publishing failed", e);
            ErrorWindowMgr.showErrorPopup("Publishing Failed", e.message, _win);
        }

        stage.quality = prevQuality;
        status.updateModified(Ternary.FALSE);
    }

    protected function exportCombined () :void {
        const stage :Stage = NA.activeWindow.stage;
        const prevQuality :String = stage.quality;

        stage.quality = StageQuality.BEST;

        try {
            if (_exportChooser.dir == null) {
                throw new Error("No export directory specified.");
            }
            if (_conf.exports.length == 0) {
                throw new Error("No export formats specified.");
            }
            var published :int = createPublisher().publishCombined(getLibs());
            if (published == 0) {
                throw new Error("No suitable formats were found for publishing");
            }
        } catch (e :Error) {
            log.warning("publishing failed", e);
            ErrorWindowMgr.showErrorPopup("Publishing Failed", e.message, _win);
        }

        stage.quality = prevQuality;
        for each (var status :DocStatus in _flashDocsGrid.dataProvider) {
            status.updateModified(Ternary.FALSE);
        }
    }

    protected function checkModified () :void {
        var libs :Vector.<XflLibrary> = getLibs();
        if (libs == null) return; // not done loading yet

        // all the docs we know about have been loaded
        var pub :Publisher = createPublisher();
        for (var ii :int = 0; ii < libs.length; ii++) {
            var status :DocStatus = _flashDocsGrid.dataProvider[ii];
            status.updateModified(Ternary.of(pub == null || pub.modified(libs, ii)))
        }
    }

    protected function checkValid () :void {
        if (getLibs() == null) {
            _win.exportAll.enabled = false;
            return;
        }

        _win.exportAll.enabled = !hasCombinedExportConfig() || _flashDocsGrid.dataProvider.toArray()
            .every(function (status :DocStatus, ..._) :Boolean {
                return status.isValid;
            });
    }

    override protected function setProjectDirty (val :Boolean) :void {
        super.setProjectDirty(val);
        updateWindowTitle();
    }

    override protected function handleParseError (err :ParseError) :void {
        _errorsGrid.dataProvider.addItem(err);
    }

    override protected function docLoadSucceeded (doc :DocStatus, lib :XflLibrary) :void {
        super.docLoadSucceeded(doc, lib);
        checkModified();
        checkValid();
    }

    override protected function docLoadFailed (file :File, doc :DocStatus, err :*) :void {
        super.docLoadFailed(file, doc, err);
        trace("Failed to load " + file.nativePath + ": " + err);
        throw err;
    }

    override protected function addDoc (status :DocStatus) :void {
        _flashDocsGrid.dataProvider.addItem(status);
    }

    override protected function getDocs () :Array {
        return _flashDocsGrid.dataProvider.toArray();
    }

    protected var _docFinder :Executor;
    protected var _win :ProjectWindow;
    protected var _flashDocsGrid :DataGrid;
    protected var _errorsGrid :DataGrid;
    protected var _exportChooser :DirChooser;
    protected var _importChooser :DirChooser;
}
}

Remove unused import

Remove unused import


package {

    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.DataEvent;
    import flash.events.IOErrorEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.XMLSocket;
    import flash.system.System;
    import flash.utils.getQualifiedClassName;
    import flash.sampler.*;

    public class Agent extends Sprite {
	    private static const HOST:String = "localhost";
	    private static const PORT:int = 42624;
	    private static const PREFIX:String = "[AGENT]";

	    private var _host:String;
	    private var _port:int;
	    private var _socket:XMLSocket;
	    private var _connected:Boolean;

    	public function Agent() {
    	    trace(PREFIX, "Loaded");

    	    _host = loaderInfo.parameters["host"] || HOST;
    	    _port = loaderInfo.parameters["port"] || PORT;

	        _socket = new XMLSocket();

            _socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, fail);
            _socket.addEventListener(IOErrorEvent.IO_ERROR, fail);
            _socket.addEventListener(DataEvent.DATA, dataReceived);
            _socket.addEventListener(Event.CONNECT, connected);
            _socket.addEventListener(Event.CLOSE, close);

            connect();
    	}

    	private function dataReceived(e:DataEvent):void {
    	    trace(PREFIX, "Received command", e.data);

    	    switch( e.data ) {
                case "GET MEMORY":
                    _socket.send("MEMORY: " + new Date().time + " " + System.totalMemory);
                    return;
                case "START SAMPLING":
                    startSampling();
                    _socket.send("OK START");
                    return;
                case "PAUSE SAMPLING":
                    pauseSampling();
                    _socket.send("OK PAUSE");

                    trace(getSampleCount());
                    for each (var s:Sample in getSamples()) {
                        trace(sampleToString(s));
                    }
                    return;
                case "STOP SAMPLING":
                    stopSampling();
                    _socket.send("OK STOP");
                    return;
                case "GET SAMPLES":
                    _socket.send("SENDING SAMPLES: " + )
    	    }
    	}

    	private function sampleToString(s:Sample):String {
            if( s is NewObjectSample ) {
                var nos:NewObjectSample = s as NewObjectSample;

                return "[NewObjectSample" +
                    "\n     id: " + nos.id +
                    "\n   type: " + getQualifiedClassName(nos.type) +
                    "\n   time: " + nos.time +
                    "\n  stack: " + stackToString(nos.stack) +
                    "\n]";

            } else if( s is DeleteObjectSample ) {
                var dos:DeleteObjectSample = s as DeleteObjectSample;

                return "[DeleteObjectSample" +
                    "\n     id: " + dos.id +
                    "\n   size: " + dos.size +
                    "\n   time: " + dos.time +
                    "\n  stack: " + stackToString(dos.stack) +
                    "\n]";
            }

            return "[Sample" +
                "\n   time: " + s.time +
                "\n  stack: " + stackToString(s.stack) +
                "\n]";
    	}

    	private function stackToString(stack:Array):String {
    	    if( null == stack ) {
    	        return "(none)";
    	    }

    	    var separator:String = "\n    ";
    	    return separator + stack.join(separator);
    	}

    	private function connect():void {
    	    trace(PREFIX, "Trying to connect to", _host, ":", _port);

    	    try {
    	        _socket.connect(_host, _port);
	        } catch (e:Error) {
	            trace(PREFIX, "Unable to connect", e);
	        }
    	}

    	private function connected(e:Event):void {
    	   _connected = true;

    	   trace(PREFIX, "Connected");

    	   _socket.send("AGENT READY");
    	}

    	private function close(e:Event):void {
    	   _connected = false;

    	   trace(PREFIX, "Disconnected");
    	}

    	private function fail(e:Event):void {
    	    _socket.close();
    	    _connected = false;
            trace(PREFIX, "Communication failure", e);
    	}
    }
}

package {

    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.DataEvent;
    import flash.events.IOErrorEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.XMLSocket;
    import flash.system.System;
    import flash.utils.getTimer;
    import flash.utils.getQualifiedClassName;
    import flash.sampler.*;

    public class Agent extends Sprite {
	    private static const HOST:String = "localhost";
	    private static const PORT:int = 42624;
	    private static const PREFIX:String = "[AGENT]";

	    private var _host:String;
	    private var _port:int;
	    private var _socket:XMLSocket;
	    private var _connected:Boolean;

    	public function Agent() {
    	    trace(PREFIX, "Loaded");

    	    _host = loaderInfo.parameters["host"] || HOST;
    	    _port = loaderInfo.parameters["port"] || PORT;

	        _socket = new XMLSocket();

            _socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, fail);
            _socket.addEventListener(IOErrorEvent.IO_ERROR, fail);
            _socket.addEventListener(DataEvent.DATA, dataReceived);
            _socket.addEventListener(Event.CONNECT, connected);
            _socket.addEventListener(Event.CLOSE, close);

            connect();
    	}

    	private function dataReceived(e:DataEvent):void {
    	    trace(PREFIX, "Received command", e.data);

    	    switch( e.data ) {
                case "GET MEMORY":
                    _socket.send("MEMORY: " + new Date().time + " " + System.totalMemory);
                    return;

                case "START SAMPLING":
                    startSampling();
                    _socket.send("OK START");
                    return;

                case "PAUSE SAMPLING":
                    pauseSampling();
                    _socket.send("OK PAUSE");
                    return;

                case "STOP SAMPLING":
                    stopSampling();
                    _socket.send("OK STOP");
                    return;

                case "GET SAMPLES":
                    trace(PREFIX, "Sending", getSampleCount(), "samples at", getTimer());

                    _socket.send("SENDING SAMPLES: " + getSampleCount());

                    for each (var s:Sample in getSamples()) {
                        _socket.send(sampleToString(s));
                    }

                    trace(PREFIX, "Done sending samples");

                    return;
    	    }
    	}

    	private function sampleToString(s:Sample):String {
            if( s is NewObjectSample ) {
                var nos:NewObjectSample = s as NewObjectSample;

                return "[NewObjectSample" +
                    "\n   time: " + nos.time +
                    "\n     id: " + nos.id +
                    "\n   type: " + getQualifiedClassName(nos.type) +
                    stackToString(nos.stack) +
                    "\n]";

            } else if( s is DeleteObjectSample ) {
                var dos:DeleteObjectSample = s as DeleteObjectSample;

                return "[DeleteObjectSample" +
                    "\n   time: " + dos.time +
                    "\n     id: " + dos.id +
                    "\n   size: " + dos.size +
                    stackToString(dos.stack) +
                    "\n]";
            }

            return "[Sample" +
                "\n   time: " + s.time +
                stackToString(s.stack) +
                "\n]";
    	}

    	private function stackToString(stack:Array):String {
    	    if( null == stack ) {
    	        return "";
    	    }

    	    var separator:String = "\n    ";
    	    return "\n  stack: " + separator + stack.join(separator);
    	}

    	private function connect():void {
    	    trace(PREFIX, "Trying to connect to", _host, ":", _port);

    	    try {
    	        _socket.connect(_host, _port);
	        } catch (e:Error) {
	            trace(PREFIX, "Unable to connect", e);
	        }
    	}

    	private function connected(e:Event):void {
    	   _connected = true;

    	   trace(PREFIX, "Connected");

    	   _socket.send("AGENT READY");
    	}

    	private function close(e:Event):void {
    	   _connected = false;

    	   trace(PREFIX, "Disconnected");
    	}

    	private function fail(e:Event):void {
    	    _socket.close();
    	    _connected = false;
            trace(PREFIX, "Communication failure", e);
    	}
    }
}

fix sending samples

fix sending samples


package aerys.minko.scene.controller
{
	import aerys.minko.ns.minko_scene;
	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;
	import flash.utils.Dictionary;

    use namespace minko_scene;

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

        private var _targetsListLocked  : Boolean;
        private var _targetsToAdd       : Vector.<ISceneNode>;
        private var _targetsToRemove    : Vector.<ISceneNode>;

		private var _updateRate			: Number	= 0.;

        protected function get scene() : Scene
        {
            return _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 updateRate() : Number
		{
			return _updateRate;
		}
		protected function set updateRate(value : Number) : void
		{
			_updateRate = value;
		}

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

            initialize();
		}

        protected function initialize() : void
        {
            _started = new Dictionary(true);
        }

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

            if (_scene && scene != _scene)
                throw new Error(
                    'The same ScriptController instance can not be used in more than one scene ' +
                    'at a time.'
                );

            _scene = scene;
        }

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

            if (getNumTargetsInScene(scene))
                _scene = null;
        }

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

			if (_updateRate != 0. && deltaTime < 1000. / _updateRate)
				return;

			_viewport = viewport;
			_lastTime = time;

            lockTargetsList();
            beforeUpdate();

			var numTargets : uint = this.numTargets;

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

                if (target.scene)
                {
                    if (!_started[target])
                    {
                        _started[target] = true;
                        start(target);
                    }

    				update(target);
                }
                else if (_started[target])
                {
                    _started[target] = false;
                    stop(target);
                }
            }

            afterUpdate();
            unlockTargetsList();
		}

        /**
         * The 'start' method is called on a script target at the first frame occuring after it
         * has been added to the scene.
         *
         * @param target
         *
         */
		protected function start(target : ISceneNode) : void
		{
			// nothing
		}

        /**
         * The 'beforeUpdate' method is called before each target is updated via the 'update'
         * method.
         *
         */
        protected function beforeUpdate() : void
        {
            // nothing
        }

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

        /**
         * The 'afterUpdate' method is called after each target has been updated via the 'update'
         * method.
         *
         */
        protected function afterUpdate() : void
        {
            // nothing
        }

        /**
         * The 'start' method is called on a script target at the first frame occuring after it
         * has been removed from the scene.
         *
         * @param target
         *
         */
		protected function stop(target : ISceneNode) : void
		{
			// nothing
		}

        private function lockTargetsList() : void
        {
            _targetsListLocked = true;
        }

        private function unlockTargetsList() : void
        {
            _targetsListLocked = false;

            if (_targetsToAdd)
            {
                var numTargetsToAdd : uint = _targetsToAdd.length;
                for (var i : uint = 0; i < numTargetsToAdd; ++i)
                    addTarget(_targetsToAdd[i]);
                _targetsToAdd = null;
            }

            if (_targetsToRemove)
            {
                var numTargetsToRemove : uint = _targetsToRemove.length;
                for (var j : uint = 0; j < numTargetsToRemove; ++j)
                    removeTarget(_targetsToRemove[j]);
                _targetsToRemove = null;
            }
        }

        override minko_scene function addTarget(target : ISceneNode) : void
        {
            if (_targetsListLocked)
            {
                if (_targetsToAdd)
                    _targetsToAdd.push(target);
                else
                    _targetsToAdd = new <ISceneNode>[target];
            }
            else
                super.addTarget(target);
        }

        override minko_scene function removeTarget(target : ISceneNode) : void
        {
            if (_targetsListLocked)
            {
                if (_targetsToRemove)
                    _targetsToRemove.push(target);
                else
                    _targetsToRemove = new <ISceneNode>[target];
            }
            else
                super.removeTarget(target);
        }
	}
}

package aerys.minko.scene.controller
{
	import aerys.minko.ns.minko_scene;
	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;
	import flash.utils.Dictionary;

    use namespace minko_scene;

	public class AbstractScriptController extends EnterFrameController
	{
        private var _scene              : Scene;
        private var _started            : Dictionary;
		private var _time				: Number;
		private var _lastTime		    : Number;
		private var _deltaTime		    : Number;
		private var _currentTarget	    : ISceneNode;
		private var _viewport		    : Viewport;

        private var _targetsListLocked  : Boolean;
        private var _targetsToAdd       : Vector.<ISceneNode>;
        private var _targetsToRemove    : Vector.<ISceneNode>;

		private var _updateRate			: Number;

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

		protected function get time() : Number
		{
			return _time;
		}

		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 updateRate() : Number
		{
			return _updateRate;
		}
		protected function set updateRate(value : Number) : void
		{
			_updateRate = value;
		}

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

            initialize();
		}

        protected function initialize() : void
        {
            _started = new Dictionary(true);
        }

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

            if (_scene && scene != _scene)
                throw new Error(
                    'The same ScriptController instance can not be used in more than one scene ' +
                    'at a time.'
                );

            _scene = scene;
        }

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

            if (getNumTargetsInScene(scene))
                _scene = null;
        }

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

			if (_updateRate != 0. && deltaTime < 1000. / _updateRate)
				return;

			_viewport = viewport;

            lockTargetsList();
            beforeUpdate();

			var numTargets : uint = this.numTargets;

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

                if (target.scene)
                {
                    if (!_started[target])
                    {
                        _started[target] = true;
                        start(target);
                    }

    				update(target);
                }
                else if (_started[target])
                {
                    _started[target] = false;
                    stop(target);
                }
            }

            afterUpdate();
            unlockTargetsList();

			_lastTime = time;
		}

        /**
         * The 'start' method is called on a script target at the first frame occuring after it
         * has been added to the scene.
         *
         * @param target
         *
         */
		protected function start(target : ISceneNode) : void
		{
			// nothing
		}

        /**
         * The 'beforeUpdate' method is called before each target is updated via the 'update'
         * method.
         *
         */
        protected function beforeUpdate() : void
        {
            // nothing
        }

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

        /**
         * The 'afterUpdate' method is called after each target has been updated via the 'update'
         * method.
         *
         */
        protected function afterUpdate() : void
        {
            // nothing
        }

        /**
         * The 'start' method is called on a script target at the first frame occuring after it
         * has been removed from the scene.
         *
         * @param target
         *
         */
		protected function stop(target : ISceneNode) : void
		{
			// nothing
		}

        private function lockTargetsList() : void
        {
            _targetsListLocked = true;
        }

        private function unlockTargetsList() : void
        {
            _targetsListLocked = false;

            if (_targetsToAdd)
            {
                var numTargetsToAdd : uint = _targetsToAdd.length;
                for (var i : uint = 0; i < numTargetsToAdd; ++i)
                    addTarget(_targetsToAdd[i]);
                _targetsToAdd = null;
            }

            if (_targetsToRemove)
            {
                var numTargetsToRemove : uint = _targetsToRemove.length;
                for (var j : uint = 0; j < numTargetsToRemove; ++j)
                    removeTarget(_targetsToRemove[j]);
                _targetsToRemove = null;
            }
        }

        override minko_scene function addTarget(target : ISceneNode) : void
        {
            if (_targetsListLocked)
            {
                if (_targetsToAdd)
                    _targetsToAdd.push(target);
                else
                    _targetsToAdd = new <ISceneNode>[target];
            }
            else
                super.addTarget(target);
        }

        override minko_scene function removeTarget(target : ISceneNode) : void
        {
            if (_targetsListLocked)
            {
                if (_targetsToRemove)
                    _targetsToRemove.push(target);
                else
                    _targetsToRemove = new <ISceneNode>[target];
            }
            else
                super.removeTarget(target);
        }
	}
}

add AbstractScriptController.time protected property