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
		{
			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;

			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;
		}
	}
}

Fix for Bug #1232 bug in DrawCall.unsetBindings if _bindingsConsumer==null, the program is not created if enabled == false which is the expected behavior.


package com.kaltura.kdpfl.plugin.component
{
	import com.kaltura.kdpfl.ApplicationFacade;
	import com.kaltura.kdpfl.model.SequenceProxy;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.model.type.SequenceContextType;
	import com.kaltura.kdpfl.view.controls.KTrace;
	import com.kaltura.puremvc.as3.patterns.mediator.SequenceMultiMediator;
	import com.kaltura.types.KalturaAdProtocolType;
	import com.kaltura.types.KalturaAdType;
	import com.kaltura.vo.KalturaAdCuePoint;

	import flash.events.TimerEvent;
	import flash.utils.Timer;

	import org.puremvc.as3.interfaces.INotification;

	public class VastMediator extends SequenceMultiMediator
	{
		public static const NAME : String = "vastMediator";

		public static const VAST_AD_PROVIDER : String = "vast";

		public var sequenceProxy : SequenceProxy;

		private var _shouldPlayPreroll : Boolean = false;
		private var _shouldPlayPostroll : Boolean = false;

		private var _reached25:Boolean = false;
		private var _reached50:Boolean = false;
		private var _reached75:Boolean = false;
		private var _loadedFirstOverlayVAST : Boolean = false;
		private var _playedFirstMidroll : Boolean = false;
		private var _pluginCode : vastPluginCode;
		private var _adStarted:Boolean = false;

	//	private var _vastMidrollTimer : Timer;

		/**
		 * @copy adContext
		 * */
		private var _adContext:String;

		/**
		 * @copy #isListening
		 */
		private var _isListening : Boolean = false;

		/**
		 * Constructor.
		 * @param pluginCode
		 * @param viewComponent
		 */
		public function VastMediator(pluginCode : vastPluginCode, viewComponent:Object=null)
		{
			super(viewComponent);
			_pluginCode = pluginCode;
		}

		override public function listNotificationInterests():Array
		{
			var interests : Array = [
									NotificationType.PLAYER_UPDATE_PLAYHEAD,
									NotificationType.SEQUENCE_ITEM_PLAY_END,
									NotificationType.HAS_OPENED_FULL_SCREEN,
									NotificationType.HAS_CLOSED_FULL_SCREEN,
									NotificationType.ENTRY_READY,
									"vastStartedPlaying",
									NotificationType.PLAYER_PAUSED,
									NotificationType.PLAYER_PLAYED,
									NotificationType.AD_OPPORTUNITY,
									NotificationType.CHANGE_MEDIA_PROCESS_STARTED,
									NotificationType.ROOT_RESIZE,
									NotificationType.SEQUENCE_SKIP_NEXT
									];

			return interests;
		}

		/**
		 * only use notification for stats, where the actual timing doesn't matter.

		 * adStart and adClick are sent from VastLinearAdProxy.
		 * @param notification
		 */
		override public function handleNotification(notification:INotification):void
		{
			if (!sequenceProxy)
				sequenceProxy = facade.retrieveProxy(SequenceProxy.NAME) as SequenceProxy;

			switch (notification.getName()) {

				case NotificationType.PLAYER_UPDATE_PLAYHEAD:
					if (_isListening) {
						// only listen to notification while active.
						handlePlayhead(notification.getBody());
					}
					else
					{
						if (!_loadedFirstOverlayVAST && _pluginCode.overlayStartAt >= Number(notification.getBody()))
						{
							_loadedFirstOverlayVAST = true;
							_pluginCode.loadNonLinearAd();

							if (_pluginCode.overlayInterval)
							{
								var overlayDisplayDuration : Number = _pluginCode["overlayDisplayDuration"] ? _pluginCode["overlayDisplayDuration"] : 5;
								var overlayTimer : Timer = new Timer((_pluginCode["overlayInterval"] + overlayDisplayDuration)*1000 ,1);
								overlayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, loadOverlay );
								overlayTimer.start();
							}
						}

						if ( !_playedFirstMidroll && _pluginCode.showFirstMidrollAt && _pluginCode.showFirstMidrollAt <= Number(notification.getBody()) )
						{
							_playedFirstMidroll = true;

							_pluginCode.midrollUrlArr.push( _pluginCode.midrollUrl );

						/*	if ( _pluginCode.midrollInterval )
							{
								_vastMidrollTimer = new Timer (_pluginCode.midrollInterval);

								_vastMidrollTimer.start()
							}*/

							startVASTMidroll();
						}
					}
					break;
				case NotificationType.PLAYER_PLAYED:
				//	if (_playedFirstMidroll && _vastMidrollTimer)
				//		_vastMidrollTimer.start();

					break;
				case NotificationType.PLAYER_PAUSED:
				//	if (_playedFirstMidroll && _vastMidrollTimer)
				//		_vastMidrollTimer.stop();
					break;
				case "vastStartedPlaying":
					isListening = true;
					break;
				case NotificationType.SEQUENCE_ITEM_PLAY_END:
					if (isListening ) {
						sendNotification("adEnd", {timeSlot:getTimeSlot()});
						_pluginCode["playedPrerollsSingleEntry"] = 0;
						_pluginCode["playedPostrollsSingleEntry"] = 0;
						_pluginCode.finishLinearAd();
						enableGUI(true)
					}
					// stop listening to notifications
					isListening = false;
					break;
				case NotificationType.HAS_OPENED_FULL_SCREEN:
					if (_isListening)
					{
						_pluginCode.sendLinearTrackEvent("trkFullScreenEvent");
					}
					break;
				case NotificationType.HAS_CLOSED_FULL_SCREEN:
					if (_isListening)
					{
						_pluginCode.sendLinearTrackEvent("trkExitFullScreenEvent");
					}
					break;
				case NotificationType.ENTRY_READY :

					if (!sequenceProxy.vo.isInSequence)
					{
						_loadedFirstOverlayVAST = false;
						_playedFirstMidroll = false;
					}
					break;
				case NotificationType.AD_OPPORTUNITY:

					if (_pluginCode.trackCuePoints == "true")
					{
						var adContext : String = notification.getBody().context;

						var cuePoint : KalturaAdCuePoint = notification.getBody().cuePoint as KalturaAdCuePoint;

						if ( cuePoint.protocolType == KalturaAdProtocolType.VAST || cuePoint.protocolType == KalturaAdProtocolType.VAST_2_0)
						{
							switch ( adContext )
							{
								case SequenceContextType.PRE:
									if (cuePoint.adType == KalturaAdType.VIDEO)
									{
											_pluginCode.prerollUrlArr.push( cuePoint.sourceUrl );
											sequenceProxy.vo.preSequenceArr.push(_pluginCode);
									}
									break;
								case SequenceContextType.POST:
									if (cuePoint.adType == KalturaAdType.VIDEO)
									{
										_pluginCode.postrollUrlArr.push( cuePoint.sourceURL );
										sequenceProxy.vo.postSequenceArr.push(_pluginCode);
									}
									break;
								case SequenceContextType.MID:

									resolveMidrollAd( cuePoint , sequenceProxy);

									break;
							}
						}
					}

					break;
					case NotificationType.CHANGE_MEDIA_PROCESS_STARTED:

						// Restore previous situation (if existed at all)
						if (_pluginCode.prerollUrl )
						{
							_pluginCode.prerollUrlArr = new Array( _pluginCode.prerollUrl );
						}

						if (_pluginCode.postrollUrl)
						{
							_pluginCode.postrollUrlArr = new Array(_pluginCode.postrollUrl);
						}

					break;

					case NotificationType.SEQUENCE_SKIP_NEXT:
						//if at least one vast ad played, most likely skip was for vast ad
						if (_adStarted)
						{
							_pluginCode.sendLinearTrackEvent("trkSkipEvent");
						}
					break;
			}

		}

		private function startVASTMidroll (e : TimerEvent = null) : void
		{
			sequenceProxy.vo.midCurrentIndex = 0;

			sendNotification( NotificationType.DO_PAUSE );

			sequenceProxy.playNextInSequence();
		}

		private function resolveMidrollAd( midrollObject : KalturaAdCuePoint , sequenceProxy: SequenceProxy) : void
		{
			if ( midrollObject.adType == KalturaAdType.VIDEO )
			{
				sendNotification( NotificationType.DO_PAUSE );

				_pluginCode.midrollUrlArr.push( midrollObject.sourceUrl );

				sequenceProxy.vo.midrollArr.push(_pluginCode);

			}
			else
			{
				sendNotification ("changeOverlayDisplayDuration" , {newDuration : ((midrollObject.endTime - midrollObject.startTime) / 1000)})
				_pluginCode.loadNonLinearAd( midrollObject.sourceUrl );
			}
		}

		private function loadOverlay ( e: TimerEvent) : void
		{
			_pluginCode.loadNonLinearAd();
			(e.target as Timer).start();
		}
		/**
		 * @return	statistics string for current ad context
		 */
		private function getTimeSlot():String {
			var res:String;
			switch (adContext) {
				case "pre":
					res = "preroll";
					break;
				case "mid":
					res = "midroll";
					break;
				case "post":
					res = "postroll";
					break;
			}
			return res;
		}

		/**
		 * dispatch progress event if needed.
		 * @param o progress notification body
		 */
		private function handlePlayhead(o:Object):void {
			var duration:Number = (facade.retrieveMediator("kMediaPlayerMediator"))["player"]["duration"];
			var fraction:Number = (o as Number) / duration;
			if (!_reached25 && fraction > 0.25) {
				sendNotification("firstQuartileOfAd", {timeSlot:getTimeSlot()});
				_reached25 = true;
			}
			else if (!_reached50 && fraction > 0.5){
				sendNotification("midOfAd", {timeSlot:getTimeSlot()});
				_reached50 = true;
			}
			else if (!_reached75 && fraction > 0.75){
				sendNotification("ThirdQueartileOfAd", {timeSlot:getTimeSlot()});
				_reached75 = true;
			}
			_pluginCode.checkLinearProgress(o as Number);
		}

		/**
		 * resets the "reached" variables.
		 * used when we have more than 1 ad.
		 */
		public function reset():void {
			_reached25 = false;
			_reached50 = false;
			_reached75 = false;
		}

		public function get shouldPlayPreroll () : Boolean
		{
			return _shouldPlayPreroll;
		}

		public function get shouldPlayPostroll () : Boolean
		{
			return _shouldPlayPreroll;
		}

		public function enableGUI (enabled : Boolean) : void
		{
			sendNotification("enableGui", {guiEnabled : enabled, enableType : "full"});
		}

		/**
		 * indicates the mediator is listening to notifications.
		 */
		public function get isListening():Boolean {
			return _isListening;
		}

		/**
		 * @private
		 */
		public function set isListening(value:Boolean):void {
			if (_isListening != value) {
				_isListening = value;
				if (value) {
					reset();
				}
			}
		}

		/**
		 * current ad context.

		 * possible values enumerated in SequenceContextType
		 */
		public function get adContext():String
		{
			return _adContext;
		}

		/**
		 * @private
		 */
		public function set adContext(value:String):void
		{
			_adContext = value;
		}

	}
}

package com.kaltura.kdpfl.plugin.component
{
	import com.kaltura.kdpfl.ApplicationFacade;
	import com.kaltura.kdpfl.model.SequenceProxy;
	import com.kaltura.kdpfl.model.type.NotificationType;
	import com.kaltura.kdpfl.model.type.SequenceContextType;
	import com.kaltura.kdpfl.view.controls.KTrace;
	import com.kaltura.puremvc.as3.patterns.mediator.SequenceMultiMediator;
	import com.kaltura.types.KalturaAdProtocolType;
	import com.kaltura.types.KalturaAdType;
	import com.kaltura.vo.KalturaAdCuePoint;

	import flash.events.TimerEvent;
	import flash.utils.Timer;

	import org.puremvc.as3.interfaces.INotification;

	public class VastMediator extends SequenceMultiMediator
	{
		public static const NAME : String = "vastMediator";

		public static const VAST_AD_PROVIDER : String = "vast";

		public var sequenceProxy : SequenceProxy;

		private var _shouldPlayPreroll : Boolean = false;
		private var _shouldPlayPostroll : Boolean = false;

		private var _reached25:Boolean = false;
		private var _reached50:Boolean = false;
		private var _reached75:Boolean = false;
		private var _loadedFirstOverlayVAST : Boolean = false;
		private var _playedFirstMidroll : Boolean = false;
		private var _pluginCode : vastPluginCode;
		private var _adStarted:Boolean = false;

	//	private var _vastMidrollTimer : Timer;

		/**
		 * @copy adContext
		 * */
		private var _adContext:String;

		/**
		 * @copy #isListening
		 */
		private var _isListening : Boolean = false;

		/**
		 * Constructor.
		 * @param pluginCode
		 * @param viewComponent
		 */
		public function VastMediator(pluginCode : vastPluginCode, viewComponent:Object=null)
		{
			super(viewComponent);
			_pluginCode = pluginCode;
		}

		override public function listNotificationInterests():Array
		{
			var interests : Array = [
									NotificationType.PLAYER_UPDATE_PLAYHEAD,
									NotificationType.SEQUENCE_ITEM_PLAY_END,
									NotificationType.HAS_OPENED_FULL_SCREEN,
									NotificationType.HAS_CLOSED_FULL_SCREEN,
									NotificationType.ENTRY_READY,
									"vastStartedPlaying",
									NotificationType.PLAYER_PAUSED,
									NotificationType.PLAYER_PLAYED,
									NotificationType.AD_OPPORTUNITY,
									NotificationType.CHANGE_MEDIA_PROCESS_STARTED,
									NotificationType.ROOT_RESIZE,
									NotificationType.SEQUENCE_SKIP_NEXT
									];

			return interests;
		}

		/**
		 * only use notification for stats, where the actual timing doesn't matter.

		 * adStart and adClick are sent from VastLinearAdProxy.
		 * @param notification
		 */
		override public function handleNotification(notification:INotification):void
		{
			if (!sequenceProxy)
				sequenceProxy = facade.retrieveProxy(SequenceProxy.NAME) as SequenceProxy;

			switch (notification.getName()) {

				case NotificationType.PLAYER_UPDATE_PLAYHEAD:
					if (_isListening) {
						// only listen to notification while active.
						handlePlayhead(notification.getBody());
					}
					else
					{
						if (!_loadedFirstOverlayVAST && _pluginCode.overlayStartAt >= Number(notification.getBody()))
						{
							_loadedFirstOverlayVAST = true;
							_pluginCode.loadNonLinearAd();

							if (_pluginCode.overlayInterval)
							{
								var overlayDisplayDuration : Number = _pluginCode["overlayDisplayDuration"] ? _pluginCode["overlayDisplayDuration"] : 5;
								var overlayTimer : Timer = new Timer((_pluginCode["overlayInterval"] + overlayDisplayDuration)*1000 ,1);
								overlayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, loadOverlay );
								overlayTimer.start();
							}
						}

						if ( !_playedFirstMidroll && _pluginCode.showFirstMidrollAt && _pluginCode.showFirstMidrollAt <= Number(notification.getBody()) )
						{
							_playedFirstMidroll = true;

							_pluginCode.midrollUrlArr.push( _pluginCode.midrollUrl );

						/*	if ( _pluginCode.midrollInterval )
							{
								_vastMidrollTimer = new Timer (_pluginCode.midrollInterval);

								_vastMidrollTimer.start()
							}*/

							startVASTMidroll();
						}
					}
					break;
				case NotificationType.PLAYER_PLAYED:
				//	if (_playedFirstMidroll && _vastMidrollTimer)
				//		_vastMidrollTimer.start();

					break;
				case NotificationType.PLAYER_PAUSED:
				//	if (_playedFirstMidroll && _vastMidrollTimer)
				//		_vastMidrollTimer.stop();
					break;
				case "vastStartedPlaying":
					isListening = true;
					_adStarted = true;
					break;
				case NotificationType.SEQUENCE_ITEM_PLAY_END:
					if (isListening ) {
						sendNotification("adEnd", {timeSlot:getTimeSlot()});
						_pluginCode["playedPrerollsSingleEntry"] = 0;
						_pluginCode["playedPostrollsSingleEntry"] = 0;
						_pluginCode.finishLinearAd();
						enableGUI(true)
					}
					// stop listening to notifications
					isListening = false;
					break;
				case NotificationType.HAS_OPENED_FULL_SCREEN:
					if (_isListening)
					{
						_pluginCode.sendLinearTrackEvent("trkFullScreenEvent");
					}
					break;
				case NotificationType.HAS_CLOSED_FULL_SCREEN:
					if (_isListening)
					{
						_pluginCode.sendLinearTrackEvent("trkExitFullScreenEvent");
					}
					break;
				case NotificationType.ENTRY_READY :

					if (!sequenceProxy.vo.isInSequence)
					{
						_loadedFirstOverlayVAST = false;
						_playedFirstMidroll = false;
					}
					break;
				case NotificationType.AD_OPPORTUNITY:

					if (_pluginCode.trackCuePoints == "true")
					{
						var adContext : String = notification.getBody().context;

						var cuePoint : KalturaAdCuePoint = notification.getBody().cuePoint as KalturaAdCuePoint;

						if ( cuePoint.protocolType == KalturaAdProtocolType.VAST || cuePoint.protocolType == KalturaAdProtocolType.VAST_2_0)
						{
							switch ( adContext )
							{
								case SequenceContextType.PRE:
									if (cuePoint.adType == KalturaAdType.VIDEO)
									{
											_pluginCode.prerollUrlArr.push( cuePoint.sourceUrl );
											sequenceProxy.vo.preSequenceArr.push(_pluginCode);
									}
									break;
								case SequenceContextType.POST:
									if (cuePoint.adType == KalturaAdType.VIDEO)
									{
										_pluginCode.postrollUrlArr.push( cuePoint.sourceURL );
										sequenceProxy.vo.postSequenceArr.push(_pluginCode);
									}
									break;
								case SequenceContextType.MID:

									resolveMidrollAd( cuePoint , sequenceProxy);

									break;
							}
						}
					}

					break;
					case NotificationType.CHANGE_MEDIA_PROCESS_STARTED:

						// Restore previous situation (if existed at all)
						if (_pluginCode.prerollUrl )
						{
							_pluginCode.prerollUrlArr = new Array( _pluginCode.prerollUrl );
						}

						if (_pluginCode.postrollUrl)
						{
							_pluginCode.postrollUrlArr = new Array(_pluginCode.postrollUrl);
						}

					break;

					case NotificationType.SEQUENCE_SKIP_NEXT:
						//if at least one vast ad played, most likely skip was for vast ad
						if (_adStarted)
						{
							_pluginCode.sendLinearTrackEvent("trkSkipEvent");
						}
					break;
			}

		}

		private function startVASTMidroll (e : TimerEvent = null) : void
		{
			sequenceProxy.vo.midCurrentIndex = 0;

			sendNotification( NotificationType.DO_PAUSE );

			sequenceProxy.playNextInSequence();
		}

		private function resolveMidrollAd( midrollObject : KalturaAdCuePoint , sequenceProxy: SequenceProxy) : void
		{
			if ( midrollObject.adType == KalturaAdType.VIDEO )
			{
				sendNotification( NotificationType.DO_PAUSE );

				_pluginCode.midrollUrlArr.push( midrollObject.sourceUrl );

				sequenceProxy.vo.midrollArr.push(_pluginCode);

			}
			else
			{
				sendNotification ("changeOverlayDisplayDuration" , {newDuration : ((midrollObject.endTime - midrollObject.startTime) / 1000)})
				_pluginCode.loadNonLinearAd( midrollObject.sourceUrl );
			}
		}

		private function loadOverlay ( e: TimerEvent) : void
		{
			_pluginCode.loadNonLinearAd();
			(e.target as Timer).start();
		}
		/**
		 * @return	statistics string for current ad context
		 */
		private function getTimeSlot():String {
			var res:String;
			switch (adContext) {
				case "pre":
					res = "preroll";
					break;
				case "mid":
					res = "midroll";
					break;
				case "post":
					res = "postroll";
					break;
			}
			return res;
		}

		/**
		 * dispatch progress event if needed.
		 * @param o progress notification body
		 */
		private function handlePlayhead(o:Object):void {
			var duration:Number = (facade.retrieveMediator("kMediaPlayerMediator"))["player"]["duration"];
			var fraction:Number = (o as Number) / duration;
			if (!_reached25 && fraction > 0.25) {
				sendNotification("firstQuartileOfAd", {timeSlot:getTimeSlot()});
				_reached25 = true;
			}
			else if (!_reached50 && fraction > 0.5){
				sendNotification("midOfAd", {timeSlot:getTimeSlot()});
				_reached50 = true;
			}
			else if (!_reached75 && fraction > 0.75){
				sendNotification("ThirdQueartileOfAd", {timeSlot:getTimeSlot()});
				_reached75 = true;
			}
			_pluginCode.checkLinearProgress(o as Number);
		}

		/**
		 * resets the "reached" variables.
		 * used when we have more than 1 ad.
		 */
		public function reset():void {
			_reached25 = false;
			_reached50 = false;
			_reached75 = false;
		}

		public function get shouldPlayPreroll () : Boolean
		{
			return _shouldPlayPreroll;
		}

		public function get shouldPlayPostroll () : Boolean
		{
			return _shouldPlayPreroll;
		}

		public function enableGUI (enabled : Boolean) : void
		{
			sendNotification("enableGui", {guiEnabled : enabled, enableType : "full"});
		}

		/**
		 * indicates the mediator is listening to notifications.
		 */
		public function get isListening():Boolean {
			return _isListening;
		}

		/**
		 * @private
		 */
		public function set isListening(value:Boolean):void {
			if (_isListening != value) {
				_isListening = value;
				if (value) {
					reset();
				}
			}
		}

		/**
		 * current ad context.

		 * possible values enumerated in SequenceContextType
		 */
		public function get adContext():String
		{
			return _adContext;
		}

		/**
		 * @private
		 */
		public function set adContext(value:String):void
		{
			_adContext = value;
		}

	}
}

fix skip statistics

vast: fix skip statistics


import mx.events.EventDispatcher;
import mx.utils.Delegate;
class com.segonquart.Configuration extends MovieClip
{
	public var addListener:Function;
	public var addEventListener:Function;
	public var removeEventListener:Function;
	static var URLAPP:String = "http://www.talking-wear.com";
	static var POLICY:String = "http://segonquart.net/crossdomain.xml";
	public function Configuration ()
	{
		Stage.addListener (this);
		mx.events.EventDispatcher.initialize (this);
	}
	public function init (m:MovieClip)
	{
		this.m = m;
		m.config ();
	}
	static function config ():Void
	{
		System.security.allowDomain (URLAPP);
		System.security.loadPolicyFile (POLICY);
		Stage.showMenu = false;
		Stage.scaleMode = "noScale";
		_global.showRedrawRegions (false);
	}
}

import mx.events.EventDispatcher;
import mx.utils.Delegate;

class com.segonquart.Configuration extends MovieClip
{
	public var addListener:Function;
	public var addEventListener:Function;
	public var removeEventListener:Function;
	static var URLAPP:String = "http://www.talking-wear.com";
	static var POLICY:String = "http://segonquart.net/crossdomain.xml";
	public function Configuration ()
	{
		Stage.addListener (this);
		mx.events.EventDispatcher.initialize (this);
	}
	public function init (m:MovieClip)
	{
		this.m = m;
		m.config ();
	}
	static function config ()
	{
		System.security.allowDomain (URLAPP);
		System.security.loadPolicyFile (POLICY);
		Stage.showMenu = false;
		Stage.scaleMode = "noScale";
		_global.showRedrawRegions (false);
	}
}

Update Configuration.as

Update Configuration.as


Scene@ newScene_;
Scene@ scene_;
Camera@ camera_;

void Start()
{
	StartScene("Scenes/Level1.xml");
	SubscribeToEvent("LevelComplete", "HandleLevelComplete");
}

void Stop()
{
}

void HandleLevelComplete(StringHash type, VariantMap& data)
{
	log.Debug("Level Complete. I should be loading "+data["NextLevel"].GetString());
	StartScene(data["NextLevel"].GetString());
}

void StartScene(String scene)
{
	newScene_ = Scene();
	newScene_.LoadAsyncXML(cache.GetFile(scene));

	SubscribeToEvent("AsyncLoadFinished", "HandleAsyncLoadFinished");
}

void HandleAsyncLoadFinished(StringHash type, VariantMap& data)
{
	UnsubscribeFromEvent("AsyncLoadFinished");

	newScene_ = data["Scene"].GetPtr();

	SubscribeToEvent("Update", "HandleDelayedStart");
}

void HandleDelayedStart(StringHash type, VariantMap& data)
{
	UnsubscribeFromEvent("Update");

	Node@ cameraNode = newScene_.GetChild("Camera", true);
	Camera@ newCamera = cameraNode.CreateComponent("Camera");

	Viewport@ viewport = Viewport(newScene_, cameraNode.GetComponent("Camera"));
	renderer.viewports[0] = viewport;

	scene_ = newScene_;
	camera_ = newCamera;
	newScene_ = null;
}

Scene@ newScene_;
Scene@ scene_;
Camera@ camera_;
Timer timer_;

void Start()
{
	StartScene("Scenes/Level1.xml");
	SubscribeToEvent("LevelComplete", "HandleLevelComplete");
}

void Stop()
{
}

void HandleLevelComplete(StringHash type, VariantMap& data)
{
	log.Debug("Level Complete. I should be loading "+data["NextLevel"].GetString());
	float timeTaken = timer_.GetMSec(false);
	log.Debug("You took " + timeTaken / 1000.f + " seconds to solve the level");

	StartScene(data["NextLevel"].GetString());
}

void StartScene(String scene)
{
	newScene_ = Scene();
	newScene_.LoadAsyncXML(cache.GetFile(scene));

	SubscribeToEvent("AsyncLoadFinished", "HandleAsyncLoadFinished");
}

void HandleAsyncLoadFinished(StringHash type, VariantMap& data)
{
	UnsubscribeFromEvent("AsyncLoadFinished");

	newScene_ = data["Scene"].GetPtr();

	SubscribeToEvent("Update", "HandleDelayedStart");
}

void HandleDelayedStart(StringHash type, VariantMap& data)
{
	UnsubscribeFromEvent("Update");

	Node@ cameraNode = newScene_.GetChild("Camera", true);
	Camera@ newCamera = cameraNode.CreateComponent("Camera");

	Viewport@ viewport = Viewport(newScene_, cameraNode.GetComponent("Camera"));
	renderer.viewports[0] = viewport;

	scene_ = newScene_;
	camera_ = newCamera;
	newScene_ = null;

	timer_.Reset();
}

Add a level timer to the main procedure

Add a level timer to the main procedure


/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2004-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

package avmplus {

import flash.utils.ByteArray

[native(cls="DomainClass", instance="DomainObject", methods="auto")]
public class Domain
{
	private native function init(base:Domain):void;

	public function Domain(base:Domain)
	{
		init(base);
	}

	public native function loadBytes(byteArray:ByteArray);
	public native function getClass(className:String):Class;
	public native static function get currentDomain():Domain;

	public function load(filename:String)
	{
		return loadBytes(ByteArray.readFile(filename))
	}
//#ifdef AVMPLUS_MOPS
	/**
	 * Gets the minimum length of a ByteArray required to be used as
	 * ApplicationDomain.globalMemory
	 *
     * @tiptext
     * @playerversion Flash 10
     * @langversion 3.0
	 */
	public native static function get MIN_DOMAIN_MEMORY_LENGTH():uint;

	/**
	 * Gets and sets the ByteArray object on which global memory operations
	 * will operate within this ApplicationDomain
	 *
     * @tiptext
     * @playerversion Flash 10
     * @langversion 3.0
	 */
	public native function get domainMemory():ByteArray;
	public native function set domainMemory(mem:ByteArray);
//#endif AVMPLUS_MOPS
}

}

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2004-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

package avmplus {

import flash.utils.ByteArray

[native(cls="DomainClass", instance="DomainObject", methods="auto")]
public class Domain
{
	private native function init(base:Domain):void;

	public function Domain(base:Domain)
	{
		init(base);
	}

	public native function loadBytes(byteArray:ByteArray);
	public native function getClass(className:String):Class;
	public native static function get currentDomain():Domain;

	public function load(filename:String)
	{
		return loadBytes(ByteArray.readFile(filename))
	}

	/**
	 * Gets the minimum length of a ByteArray required to be used as
	 * ApplicationDomain.globalMemory
	 *
     * @tiptext
     * @playerversion Flash 10
     * @langversion 3.0
	 */
	public native static function get MIN_DOMAIN_MEMORY_LENGTH():uint;

	/**
	 * Gets and sets the ByteArray object on which global memory operations
	 * will operate within this ApplicationDomain
	 *
     * @tiptext
     * @playerversion Flash 10
     * @langversion 3.0
	 */
	public native function get domainMemory():ByteArray;
	public native function set domainMemory(mem:ByteArray);
}

}

remove one more commented out reference to AVMPLUS_MOPS (r=me)


/**
  * LzLibrary.as
  *
  * @copyright Copyright 2001-2008 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic LZX
  * @subtopic Syntax
  */

/**
  * @lzxname import
  * @initarg stage
  */
class LzLibrary extends LzNode {

    #passthrough (toplevel:true) {
  import flash.display.*;
  import flash.events.*;
  import flash.text.*;
  import flash.ui.*;
  import flash.geom.*;
  import flash.utils.*;
  import mx.controls.Button;
  import flash.net.*;
  import flash.utils.*;
  import flash.system.*;

    }#

/** @access private
  * @modifiers override
  */
static var tagname = 'import';
/** @access private */
static var attributes = new LzInheritedHash(LzNode.attributes);

/** @access private */
var loaded = false;
/** @access private */
var loading = false;
/** @access private */
var sprite = null;

/**
 * A reference to a target file whose content is treated as a
 * loadable module.
 *
 * @keywords final
 * @type string
 * @access public
 */
var href;

/**
 * When set to 'defer', the library will not be loaded until its
 * load-method has been called. Otherwise, the library loads
 * automatically.
 *
 * @keywords final
 * @type string
 * @lzxdefault "late"
 * @access public
 */
var stage = "late";//"late|defer"

/**
  * @access private
  */
function $lzc$set_stage(val) {
    this.stage = val;
}

/**
 * Sent when this library has finished loading.
 *
 * @lzxtype event
 * @access public
 */
var onload = LzDeclaredEvent;

/** @access private
 * @modifiers override
 */
function LzLibrary ( parent:LzNode? = null , attrs:Object? = null , children:Array? = null, instcall:Boolean  = false) {
    super(parent,attrs,children,instcall);
}

/**
  * @access private
  */
function $lzc$set_href(val) {
    this.href = val;
}

/**
  * @access private
  */
override function construct (parent, args) {
    this.stage = args.stage;
    super.construct.apply(this, arguments);
    LzLibrary.libraries[args.name] = this;
}

/**
  * @access private
  */
override function init( ) {
    super.init.apply(this, arguments);
    if (this.stage == "late") {
        this.load();
    }
}

/**
  * @access private
  */
override function destroy () {
    if (this.sprite) {
        this.sprite.destroy();
        this.sprite = null;
    }
    super.destroy();
}

/** @access private */
static var libraries = {};

/**
  * @access private
  */
static function findLibrary (libname){
    return LzLibrary.libraries[libname];
}

/**
  * @access private
  */
override function toString (){
    return "Library " + this.href + " named " + this.name;
}

public var loader:Loader = null;

/**
  * Loads this library dynamically at runtime. Must only be called
  * when stage was set to 'defer'.
  *
  * @access public
  */
function load () {
    if (this.loading || this.loaded) {
        return;
    }
    this.loading = true;
    var request:URLRequest = new URLRequest(this.href);
    request.method = URLRequestMethod.GET;
    this.loader = new Loader();
    var info:LoaderInfo = loader.contentLoaderInfo;
    info.addEventListener(Event.COMPLETE, handleLoadComplete);
    trace('loader.load ', this.href);
    this.loader.load(request);
}

public function handleLoadComplete(event:Event):void {
    var library:Object = event.target.content;
    library.exportClassDefs(null);
    this.libapp = library;
}

var libapp;

/**
 * Called by LzLibraryCleanup when this library has finished loading.
 *
 * @access private
 */
function loadfinished (){
  this.loading = false;
  if (this.onload.ready) this.onload.sendEvent(true);
}

/** @access private
 */
static function stripQueryString(str:String):String {
    if (str.indexOf('?') > 0) {
        str = str.substring(0,str.indexOf('?'));
    }
    return str;
}

/**
  * Callback for runtime loaded libraries
  * @access private
  */
static function __LZsnippetLoaded (url){
    // find the lib with this url
    // Strip out query string
    url = LzLibrary.stripQueryString(url);
    var lib = null;
    var libs = LzLibrary.libraries;
    for (var l in libs ) {
        var libhref = LzLibrary.stripQueryString(libs[l].href);
        if (libhref == url) {
            lib = libs[l];
            break;
        }
    }

    if (lib == null) {
        Debug.error("could not find library with href", url);
    } else {
        lib.loaded = true;
        canvas.initiatorAddNode({attrs: {libname: lib.name}, "class": LzLibraryCleanup}, 1);
        // Run the queue to instantiate all pending LzInstantiateView calls.
        canvas.initDone();
    }
}

}; // End of LzLibrary
lz[LzLibrary.tagname] = LzLibrary;  //publish

/**
  * LzLibrary.as
  *
  * @copyright Copyright 2001-2009 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic LZX
  * @subtopic Syntax
  */

/**
  * @lzxname import
  * @initarg stage
  */
class LzLibrary extends LzNode {

    #passthrough (toplevel:true) {
  import flash.display.*;
  import flash.events.*;
  import flash.text.*;
  import flash.ui.*;
  import flash.geom.*;
  import flash.utils.*;
  import mx.controls.Button;
  import flash.net.*;
  import flash.utils.*;
  import flash.system.*;

    }#

/** @access private
  * @modifiers override
  */
static var tagname = 'import';
/** @access private */
static var attributes = new LzInheritedHash(LzNode.attributes);

/** @access private */
var loaded = false;
/** @access private */
var loading = false;
/** @access private */
var sprite = null;

/**
 * A reference to a target file whose content is treated as a
 * loadable module.
 *
 * @keywords final
 * @type string
 * @access public
 */
var href;

/**
 * When set to 'defer', the library will not be loaded until its
 * load-method has been called. Otherwise, the library loads
 * automatically.
 *
 * @keywords final
 * @type string
 * @lzxdefault "late"
 * @access public
 */
var stage = "late";//"late|defer"

/**
  * @access private
  */
function $lzc$set_stage(val) {
    this.stage = val;
}

/**
 * Sent when this library has finished loading.
 *
 * @lzxtype event
 * @access public
 */
var onload = LzDeclaredEvent;

/** @access private
 * @modifiers override
 */
function LzLibrary ( parent:LzNode? = null , attrs:Object? = null , children:Array? = null, instcall:Boolean  = false) {
    super(parent,attrs,children,instcall);
}

/**
  * @access private
  */
function $lzc$set_href(val) {
    this.href = val;
}

/**
  * @access private
  */
override function construct (parent, args) {
    this.stage = args.stage;
    super.construct.apply(this, arguments);
    LzLibrary.libraries[args.name] = this;
}

/**
  * @access private
  */
override function init( ) {
    super.init.apply(this, arguments);
    if (this.stage == "late") {
        this.load();
    }
}

/**
  * @access private
  */
override function destroy () {
    if (this.sprite) {
        this.sprite.destroy();
        this.sprite = null;
    }
    super.destroy();
}

/** @access private */
static var libraries = {};

/**
  * @access private
  */
static function findLibrary (libname){
    return LzLibrary.libraries[libname];
}

/**
  * @access private
  */
override function toString (){
    return "Library " + this.href + " named " + this.name;
}

public var loader:Loader = null;

/**
  * Loads this library dynamically at runtime. Must only be called
  * when stage was set to 'defer'.
  *
  * @access public
  */
function load () {
    if (this.loading || this.loaded) {
        return;
    }
    this.loading = true;
    var loadurl:LzURL = lz.Browser.getBaseURL();
    loadurl.file = this.href;
    var request:URLRequest = new URLRequest(loadurl.toString());
    request.method = URLRequestMethod.GET;
    this.loader = new Loader();
    var info:LoaderInfo = loader.contentLoaderInfo;
    info.addEventListener(Event.COMPLETE, handleLoadComplete);
    this.loader.load(request);
}

public function handleLoadComplete(event:Event):void {
    var library:Object = event.target.content;
    library.exportClassDefs(null);
    this.libapp = library;
}

var libapp;

/**
 * Called by LzLibraryCleanup when this library has finished loading.
 *
 * @access private
 */
function loadfinished (){
  this.loading = false;
  if (this.onload.ready) this.onload.sendEvent(true);
}

/** @access private
 */
static function stripQueryString(str:String):String {
    if (str.indexOf('?') > 0) {
        str = str.substring(0,str.indexOf('?'));
    }
    return str;
}

/**
  * Callback for runtime loaded libraries
  * @access private
  */
static function __LZsnippetLoaded (url){
    // find the lib with this url
    // Strip out query string
    url = LzLibrary.stripQueryString(url);
    var lib = null;
    var libs = LzLibrary.libraries;
    for (var l in libs ) {
        var libhref = LzLibrary.stripQueryString(libs[l].href);
        if (libhref == url) {
            lib = libs[l];
            break;
        }
    }

    if (lib == null) {
        Debug.error("could not find library with href", url);
    } else {
        lib.loaded = true;
        canvas.initiatorAddNode({attrs: {libname: lib.name}, "class": LzLibraryCleanup}, 1);
        // Run the queue to instantiate all pending LzInstantiateView calls.
        canvas.initDone();
    }
}

}; // End of LzLibrary
lz[LzLibrary.tagname] = LzLibrary;  //publish

Change 20090112-hqm-j by hqm@badtzmaru.home on 2009-01-12 13:25:36 EST
    in /Users/hqm/openlaszlo/trunk5
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: Allow snippets to load into app, when wrapper html file comes from different URL

New Features:

Bugs Fixed: LPP-7615

Technical Reviewer: ptw
QA Reviewer: (pending)
Doc Reviewer: (pending)

Documentation:

Release Notes:

Details:

+ When a snippet is compiled, the URL to load it from at runtime is
baked in as a relative path, like "build/myapp/mylib.lzx.swf".

The SWF9 library loader was creating a new URLRequest using that
relative URL, and when loaded, it would load relative to the current
HTML wrapper's URL, which could be in some place random.

The fix is to construct the library load URL by grabbing
lz.Browser.getBaseURL(), which, in swf9 anyway, seems URL that the app
swf code loaded from rather than the location of the HTML wrapper
page.

This change constructs the the snippet's load URL by appending
lz.Browser.getBaseURL() to the snippet's baked-in relative path.

+ The test case from the bug report may not work in SWF8 and DHTML
either, I haven't tested those runtimes yet. This is just a fix for
swf9.

Tests:

+ test case from bug report
+ regular tests in test/snippets in swf9

git-svn-id: d62bde4b5aa582fdf07c8d403e53e0399a7ed285@12429 fa20e4f9-1d0a-0410-b5f3-dc9c16b8b17c


/**
 * User: MerlinDS
 * Date: 18.07.2014
 * Time: 17:42
 */
package com.merlinds.miracle_tool.viewer {
	import com.bit101.components.List;
	import com.bit101.components.List;
	import com.bit101.components.Window;
	import com.merlinds.debug.log;
	import com.merlinds.miracle.Miracle;
	import com.merlinds.miracle.display.MiracleImage;
	import com.merlinds.miracle.utils.Asset;
	import com.merlinds.miracle_tool.models.AppModel;

	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.utils.ByteArray;

	[SWF(backgroundColor="0x333333", frameRate=60)]
	public class ViewerView extends Sprite {

		private var _model:AppModel;
		private var _assets:Vector.<Asset>;
		private var _window:Window;

		public function ViewerView(model:AppModel = null) {
			super();
			_model = model;
			if(_model == null){
				_model = new AppModel();
			}

			this.addEventListener(Event.ADDED_TO_STAGE, this.initialize);
		}
		//==============================================================================
		//{region							PUBLIC METHODS
		//} endregion PUBLIC METHODS ===================================================

		//==============================================================================
		//{region						PRIVATE\PROTECTED METHODS
		private function initialize(event:Event):void {
			this.removeEventListener(event.type, this.initialize);
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			this.stage.align = StageAlign.TOP_LEFT;
			Miracle.start(this.stage, this.createHandler, true);
		}

		private function choseAnimation():void {
			//find animation asset and add all of animations to chose list
			var n:int = _assets.length;
			for(var i:int = 0; i < n; i++){
				var asset:Asset = _assets[i];
				if(asset.type == Asset.TEXTURE_TYPE){
					//parse output
					_window = new Window(this, 0, 0, "Chose mesh");
					var list:List = new List(_window, 0, 0, this.getTexture(asset.output));
					_window.x = this.stage.stageWidth - _window.width >> 1;
					_window.y = this.stage.stageHeight - _window.height >> 1;
					list.addEventListener(Event.SELECT, this.selectAnimationHandler);
				}
			}

		}

		private function getTexture(bytes:ByteArray):Array {
			var result:Array = [1, 2, 3];
			return result;
		}

		private function getAnimations(bytes:ByteArray):Array {
			var result:Array = [1, 2, 3];
			return result;
		}
		//} endregion PRIVATE\PROTECTED METHODS ========================================

		//==============================================================================
		//{region							EVENTS HANDLERS
		private function createHandler(animation:Boolean = false):void {
			//TODO: Show view if it exit
			if(animation || _model.viewerInput == null){
				_model.viewerInput = _model.lastFileDirection;
				_model.viewerInput.addEventListener(Event.SELECT, this.selectFileHandler);
				_model.viewerInput.browseForOpen("Open " + animation ? "Animation" : "Sprite"
						+ "file that you want to view");
			}
		}

		private function selectFileHandler(event:Event):void {
			_model.viewerInput.removeEventListener(event.type, this.selectFileHandler);
			_model.lastFileDirection = _model.viewerInput.parent;
			var byteArray:ByteArray = new ByteArray();
			var stream:FileStream = new FileStream();
			stream.open(_model.viewerInput, FileMode.READ);
			stream.readBytes(byteArray);
			stream.close();
			//parse
			if(_assets == null)_assets = new <Asset>[];
			_assets.push(new Asset(_model.viewerInput.name, byteArray));
//			var name:String = asset.name;
			if(_assets.length > 1){
				this.choseAnimation();
				Miracle.createScene(_assets, 1);
				Miracle.resume();
			}else{
				this.createHandler(true);
			}
		}

		private function selectAnimationHandler(event:Event):void {
			var list:List = event.target as List;
			list.removeEventListener(event.type, this.selectAnimationHandler);
			log(this, "selectAnimationHandler", list.selectedItem);
			this.removeChild(_window);
			_window = null;
			//add animation to miracle
			var name:String = list.selectedItem.toString();
			Miracle.currentScene.createImage(name)
			 .addEventListener(Event.ADDED_TO_STAGE, this.imageAddedToStage);
		}

		private function imageAddedToStage(event:Event):void {
			var target:MiracleImage = event.target as MiracleImage;
			target.moveTO(
					this.stage.stageWidth - target.width >> 1,
					this.stage.stageHeight - target.height >> 1
			);
		}
		//} endregion EVENTS HANDLERS ==================================================

		//==============================================================================
		//{region							GETTERS/SETTERS
		//} endregion GETTERS/SETTERS ==================================================
	}
}

/**
 * User: MerlinDS
 * Date: 18.07.2014
 * Time: 17:42
 */
package com.merlinds.miracle_tool.viewer {
	import com.bit101.components.List;
	import com.bit101.components.List;
	import com.bit101.components.Window;
	import com.merlinds.debug.log;
	import com.merlinds.miracle.Miracle;
	import com.merlinds.miracle.display.MiracleImage;
	import com.merlinds.miracle.utils.Asset;
	import com.merlinds.miracle.utils.MtfReader;
	import com.merlinds.miracle_tool.models.AppModel;

	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.utils.ByteArray;

	[SWF(backgroundColor="0x333333", frameRate=60)]
	public class ViewerView extends Sprite {

		private var _model:AppModel;
		private var _assets:Vector.<Asset>;
		private var _window:Window;
		private var _name:String;

		public function ViewerView(model:AppModel = null) {
			super();
			_model = model;
			if(_model == null){
				_model = new AppModel();
			}

			this.addEventListener(Event.ADDED_TO_STAGE, this.initialize);
		}
		//==============================================================================
		//{region							PUBLIC METHODS
		//} endregion PUBLIC METHODS ===================================================

		//==============================================================================
		//{region						PRIVATE\PROTECTED METHODS
		private function initialize(event:Event):void {
			this.removeEventListener(event.type, this.initialize);
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			this.stage.align = StageAlign.TOP_LEFT;
			Miracle.start(this.stage, this.createHandler, true);
		}

		private function choseAnimation():void {
			//find animation asset and add all of animations to chose list
			var n:int = _assets.length;
			for(var i:int = 0; i < n; i++){
				var asset:Asset = _assets[i];
				if(asset.type == Asset.TIMELINE_TYPE){
					//parse output
					_window = new Window(this, 0, 0, "Chose mesh");
					var list:List = new List(_window, 0, 0, this.getAnimations(asset.output));
					_window.x = this.stage.stageWidth - _window.width;
					list.addEventListener(Event.SELECT, this.selectAnimationHandler);
				}
			}
		}

		private function getAnimations(bytes:ByteArray):Array {
			var result:Array = [];

			return result;
		}
		//} endregion PRIVATE\PROTECTED METHODS ========================================

		//==============================================================================
		//{region							EVENTS HANDLERS
		private function createHandler(animation:Boolean = false):void {
			//TODO: Show view if it exit
			if(animation || _model.viewerInput == null){
				_model.viewerInput = _model.lastFileDirection;
				_model.viewerInput.addEventListener(Event.SELECT, this.selectFileHandler);
				_model.viewerInput.browseForOpen("Open " + animation ? "Animation" : "Sprite"
						+ "file that you want to view");
			}
		}

		private function selectFileHandler(event:Event):void {
			_model.viewerInput.removeEventListener(event.type, this.selectFileHandler);
			_model.lastFileDirection = _model.viewerInput.parent;
			var byteArray:ByteArray = new ByteArray();
			var stream:FileStream = new FileStream();
			stream.open(_model.viewerInput, FileMode.READ);
			stream.readBytes(byteArray);
			stream.close();
			//parse
			if(_assets == null)_assets = new <Asset>[];
			var asset:Asset = new Asset(_model.viewerInput.name, byteArray);
			if(asset.type == Asset.TIMELINE_TYPE){
				_name = asset.name;
			}
			_assets.push(asset);
//			var name:String = asset.name;
			if(_assets.length > 1){
				this.choseAnimation();
				Miracle.createScene(_assets, 1);
				Miracle.currentScene.createImage(_name)
						.addEventListener(Event.ADDED_TO_STAGE, this.imageAddedToStage);
				Miracle.resume();
			}else{
				this.createHandler(true);
			}
		}

		private function selectAnimationHandler(event:Event):void {
			var list:List = event.target as List;
			log(this, "selectAnimationHandler", list.selectedItem);
			//add animation to miracle
			var name:String = list.selectedItem.toString();

		}

		private function imageAddedToStage(event:Event):void {
			var target:MiracleImage = event.target as MiracleImage;
			target.moveTO(
					this.stage.stageWidth - target.width >> 1,
					this.stage.stageHeight - target.height >> 1
			);
		}
		//} endregion EVENTS HANDLERS ==================================================

		//==============================================================================
		//{region							GETTERS/SETTERS
		//} endregion GETTERS/SETTERS ==================================================
	}
}

change viewer

change viewer


package dolly {

import dolly.core.dolly_internal;
import dolly.data.CompositeCopyableClass;

import mx.collections.ArrayCollection;
import mx.collections.ArrayList;

import org.as3commons.reflect.Field;
import org.as3commons.reflect.Type;
import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertFalse;
import org.flexunit.asserts.assertNotNull;
import org.hamcrest.assertThat;
import org.hamcrest.collection.array;
import org.hamcrest.collection.arrayWithSize;
import org.hamcrest.collection.everyItem;
import org.hamcrest.core.isA;
import org.hamcrest.object.equalTo;

use namespace dolly_internal;

public class CopyingOfCompositeCopyableClassTest {

	private var compositeCopyableClass:CompositeCopyableClass;
	private var compositeCopyableClassType:Type;

	[Before]
	public function before():void {
		compositeCopyableClass = new CompositeCopyableClass();
		compositeCopyableClass.array = [1, 2, 3, 4, 5];
		compositeCopyableClass.arrayList = new ArrayList([1, 2, 3, 4, 5]);
		compositeCopyableClass.arrayCollection = new ArrayCollection([1, 2, 3, 4, 5]);

		compositeCopyableClassType = Type.forInstance(compositeCopyableClass);
	}

	[After]
	public function after():void {
		compositeCopyableClass = null;
		compositeCopyableClassType = null;
	}

	[Test]
	public function findingAllWritableFieldsForType():void {
		const writableFields:Vector.<Field> = Copier.findCopyableFieldsForType(compositeCopyableClassType);
		assertNotNull(writableFields);
		assertEquals(4, writableFields.length);
	}

	[Test]
	public function copyingOfArray():void {
		const copy:CompositeCopyableClass = Copier.copy(compositeCopyableClass);

		assertNotNull(copy.array);
		assertThat(copy.array, arrayWithSize(5));
		assertThat(copy.array, compositeCopyableClass.array);
		assertFalse(copy.array == compositeCopyableClass.array);
		assertThat(copy.array, everyItem(isA(Number)));
		assertThat(copy.array, array(equalTo(1), equalTo(2), equalTo(3), equalTo(4), equalTo(5)));
	}
}
}

package dolly {

import dolly.core.dolly_internal;
import dolly.data.CompositeCopyableClass;

import mx.collections.ArrayCollection;
import mx.collections.ArrayList;

import org.as3commons.reflect.Field;
import org.as3commons.reflect.Type;
import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertFalse;
import org.flexunit.asserts.assertNotNull;
import org.hamcrest.assertThat;
import org.hamcrest.collection.array;
import org.hamcrest.collection.arrayWithSize;
import org.hamcrest.collection.everyItem;
import org.hamcrest.core.isA;
import org.hamcrest.object.equalTo;

use namespace dolly_internal;

public class CopyingOfCompositeCopyableClassTest {

	private var compositeCopyableClass:CompositeCopyableClass;
	private var compositeCopyableClassType:Type;

	[Before]
	public function before():void {
		compositeCopyableClass = new CompositeCopyableClass();
		compositeCopyableClass.array = [1, 2, 3, 4, 5];
		compositeCopyableClass.arrayList = new ArrayList([1, 2, 3, 4, 5]);
		compositeCopyableClass.arrayCollection = new ArrayCollection([1, 2, 3, 4, 5]);

		compositeCopyableClassType = Type.forInstance(compositeCopyableClass);
	}

	[After]
	public function after():void {
		compositeCopyableClass = null;
		compositeCopyableClassType = null;
	}

	[Test]
	public function findingAllWritableFieldsForType():void {
		const writableFields:Vector.<Field> = Copier.findCopyableFieldsForType(compositeCopyableClassType);
		assertNotNull(writableFields);
		assertEquals(4, writableFields.length);
	}

	[Test]
	public function copyingOfArray():void {
		const copy:CompositeCopyableClass = Copier.copy(compositeCopyableClass);

		assertNotNull(copy.array);
		assertThat(copy.array, arrayWithSize(5));
		assertThat(copy.array, compositeCopyableClass.array);
		assertFalse(copy.array == compositeCopyableClass.array);
		assertThat(copy.array, everyItem(isA(Number)));
		assertThat(copy.array, array(equalTo(1), equalTo(2), equalTo(3), equalTo(4), equalTo(5)));
	}

	[Test]
	public function copyingOfArrayList():void {
		const copy:CompositeCopyableClass = Copier.copy(compositeCopyableClass);

		const arrayList:ArrayList = copy.arrayList;

		assertNotNull(arrayList);
		assertFalse(copy.arrayList == compositeCopyableClass.arrayList);
		assertEquals(arrayList.length, 5);
		assertEquals(arrayList.getItemAt(0), 1);
		assertEquals(arrayList.getItemAt(1), 2);
		assertEquals(arrayList.getItemAt(2), 3);
		assertEquals(arrayList.getItemAt(3), 4);
		assertEquals(arrayList.getItemAt(4), 5);
	}
}
}

Test for copying of ArrayList in CompositeCloneableClass.


package model
{
	import dragonBones.objects.AnimationData;
	import dragonBones.objects.ArmatureData;
	import dragonBones.objects.MovementBoneData;
	import dragonBones.objects.MovementData;
	import dragonBones.objects.XMLDataParser;
	import dragonBones.utils.ConstValues;
	import dragonBones.utils.dragonBones_internal;

	import flash.events.Event;

	import message.MessageDispatcher;

	import mx.collections.XMLListCollection;

	use namespace dragonBones_internal;

	/**
	 * Manage selected animation data
	 */
	public class AnimationDataProxy
	{
		public var movementsMC:XMLListCollection;

		private var _xml:XML;
		private var _movementsXMLList:XMLList;

		private var _movementXML:XML;
		private var _movementBonesXMLList:XMLList;

		private var _movementBoneXML:XML;

		public function get animationName():String
		{
			return _xml?_xml.attribute(ConstValues.A_NAME):"";
		}

		public function get movementName():String
		{
			return _movementXML?_movementXML.attribute(ConstValues.A_NAME):"";
		}

		public function get boneName():String
		{
			return _movementBoneXML?_movementBoneXML.attribute(ConstValues.A_NAME):"";
		}

		public function get durationTo():Number
		{
			if(!_movementXML)
			{
				return -1;
			}
			return int(_movementXML.attribute(ConstValues.A_DURATION_TO)) / ImportDataProxy.getInstance().frameRate;
		}
		public function set durationTo(value:Number):void
		{
			if(_movementXML)
			{
				_movementXML.@[ConstValues.A_DURATION_TO] = Math.round(value * ImportDataProxy.getInstance().frameRate);
				updateMovement();
			}
		}

		public function get durationTween():Number
		{
			if(_movementXML?int(_movementXML.attribute(ConstValues.A_DURATION)) == 1:true)
			{
				return -1;
			}
			return int(_movementXML.attribute(ConstValues.A_DURATION_TWEEN)) / ImportDataProxy.getInstance().frameRate;
		}
		public function set durationTween(value:Number):void
		{
			if(_movementXML)
			{
				_movementXML.@[ConstValues.A_DURATION_TWEEN] = Math.round(value * ImportDataProxy.getInstance().frameRate);
				updateMovement();
			}
		}

		public function get loop():Boolean
		{
			return _movementXML?Boolean(int(_movementXML.attribute(ConstValues.A_LOOP)) == 1):false;
		}
		public function set loop(value:Boolean):void
		{
			if(_movementXML)
			{
				_movementXML.@[ConstValues.A_LOOP] = value?1:0;
				updateMovement();
			}
		}

		public function get tweenEasing():Number
		{
			return _movementXML?Number(_movementXML.attribute(ConstValues.A_TWEEN_EASING)):-1.1;
		}
		public function set tweenEasing(value:Number):void
		{
			if(_movementXML)
			{
				if(value<-1)
				{
					_movementXML.@[ConstValues.A_TWEEN_EASING] = NaN;
				}
				else
				{
					_movementXML.@[ConstValues.A_TWEEN_EASING] = value;
				}
				updateMovement();
			}
		}

		public function get boneScale():Number
		{
			if(!_movementBoneXML || int(_movementXML.attribute(ConstValues.A_DURATION)) < 2)
			{
				return NaN;
			}
			return Number(_movementBoneXML.attribute(ConstValues.A_MOVEMENT_SCALE)) * 100;
		}
		public function set boneScale(value:Number):void
		{
			if(_movementBoneXML)
			{
				_movementBoneXML.@[ConstValues.A_MOVEMENT_SCALE] = value * 0.01;
				updateMovementBone();
			}
		}

		public function get boneDelay():Number
		{
			if(!_movementBoneXML || int(_movementXML.attribute(ConstValues.A_DURATION)) < 2)
			{
				return NaN;
			}
			return Number(_movementBoneXML.attribute(ConstValues.A_MOVEMENT_DELAY))* 100;
		}
		public function set boneDelay(value:Number):void
		{
			if(_movementBoneXML)
			{
				_movementBoneXML.@[ConstValues.A_MOVEMENT_DELAY] = value * 0.01;
				updateMovementBone();
			}
		}

		public function AnimationDataProxy()
		{
			movementsMC = new XMLListCollection();
		}

		internal function setData(xml:XML):void
		{
			_xml = xml;
			if(_xml)
			{
				_movementsXMLList = _xml.elements(ConstValues.MOVEMENT);
			}
			else
			{
				_movementsXMLList = null;
			}

			movementsMC.source = _movementsXMLList;

			MessageDispatcher.dispatchEvent(MessageDispatcher.CHANGE_ANIMATION_DATA, animationName);

			changeMovement();
		}

		public function changeMovement(movementName:String = null, isChangedByArmature:Boolean = false):void
		{
			_movementXML = ImportDataProxy.getElementByName(_movementsXMLList, movementName, true);
			if(_movementXML)
			{
				_movementBonesXMLList = _movementXML.elements(ConstValues.BONE);
			}
			else
			{
				_movementBonesXMLList = null;
			}

			MessageDispatcher.dispatchEvent(MessageDispatcher.CHANGE_MOVEMENT_DATA, this.movementName, isChangedByArmature);

			changeMovementBone(ImportDataProxy.getInstance().armatureDataProxy.boneName);
		}

		public function changeMovementBone(boneName:String = null):void
		{
			var movementBoneXML:XML = ImportDataProxy.getElementByName(_movementBonesXMLList, boneName, true);
			if(movementBoneXML == _movementBoneXML)
			{
				return;
			}
			_movementBoneXML = movementBoneXML;
			MessageDispatcher.dispatchEvent(MessageDispatcher.CHANGE_MOVEMENT_BONE_DATA , this.boneName);
		}

		internal function updateBoneParent(boneName:String):void
		{
			var animationData:AnimationData = ImportDataProxy.getInstance().skeletonData.getAnimationData(animationName);
			var armatureData:ArmatureData = ImportDataProxy.getInstance().skeletonData.getArmatureData(animationName);
			XMLDataParser.parseAnimationData(_xml, animationData, armatureData);
		}

		private function updateMovement():void
		{
			var animationData:AnimationData = ImportDataProxy.getInstance().skeletonData.getAnimationData(animationName);
			var movementData:MovementData = animationData.getMovementData(movementName);

			movementData.durationTo = durationTo;
			movementData.durationTween = durationTween;
			movementData.loop = loop;
			movementData.tweenEasing = tweenEasing;

			if(!ImportDataProxy.getInstance().isExportedSource)
			{
				JSFLProxy.getInstance().changeMovement(animationName, movementName, _movementXML);
			}

			MessageDispatcher.dispatchEvent(MessageDispatcher.UPDATE_MOVEMENT_DATA, movementName);
		}

		private function updateMovementBone():void
		{
			var animationData:AnimationData = ImportDataProxy.getInstance().skeletonData.getAnimationData(animationName);
			var movementData:MovementData = animationData.getMovementData(movementName);
			var movementBoneData:MovementBoneData = movementData.getMovementBoneData(boneName);

			movementBoneData.scale = boneScale * 0.01;
			movementBoneData.delay = boneDelay * 0.01;
			if(movementBoneData.delay > 0)
			{
				movementBoneData.delay -= 1;
			}

			if(!ImportDataProxy.getInstance().isExportedSource)
			{
				var movementXMLCopy:XML = _movementXML.copy();
				delete movementXMLCopy.elements(ConstValues.BONE).*;
				delete movementXMLCopy[ConstValues.FRAME];
				JSFLProxy.getInstance().changeMovement(animationName, movementName, movementXMLCopy);
			}

			MessageDispatcher.dispatchEvent(MessageDispatcher.UPDATE_MOVEMENT_BONE_DATA, movementName);
		}
	}
}

package model
{
	import dragonBones.objects.AnimationData;
	import dragonBones.objects.ArmatureData;
	import dragonBones.objects.MovementBoneData;
	import dragonBones.objects.MovementData;
	import dragonBones.objects.XMLDataParser;
	import dragonBones.utils.ConstValues;
	import dragonBones.utils.dragonBones_internal;

	import flash.events.Event;

	import message.MessageDispatcher;

	import mx.collections.XMLListCollection;

	use namespace dragonBones_internal;

	/**
	 * Manage selected animation data
	 */
	public class AnimationDataProxy
	{
		public var movementsMC:XMLListCollection;

		private var _xml:XML;
		private var _movementsXMLList:XMLList;

		private var _movementXML:XML;
		private var _movementBonesXMLList:XMLList;

		private var _movementBoneXML:XML;

		public function get animationName():String
		{
			return _xml?_xml.attribute(ConstValues.A_NAME):"";
		}

		public function get movementName():String
		{
			return _movementXML?_movementXML.attribute(ConstValues.A_NAME):"";
		}

		public function get boneName():String
		{
			return _movementBoneXML?_movementBoneXML.attribute(ConstValues.A_NAME):"";
		}

		public function get durationTo():Number
		{
			if(!_movementXML)
			{
				return -1;
			}
			return int(_movementXML.attribute(ConstValues.A_DURATION_TO)) / ImportDataProxy.getInstance().frameRate;
		}
		public function set durationTo(value:Number):void
		{
			if(_movementXML)
			{
				_movementXML.@[ConstValues.A_DURATION_TO] = Math.round(value * ImportDataProxy.getInstance().frameRate);
				updateMovement();
			}
		}

		public function get durationTween():Number
		{
			if(_movementXML?int(_movementXML.attribute(ConstValues.A_DURATION)) == 1:true)
			{
				return -1;
			}
			return int(_movementXML.attribute(ConstValues.A_DURATION_TWEEN)) / ImportDataProxy.getInstance().frameRate;
		}
		public function set durationTween(value:Number):void
		{
			if(_movementXML)
			{
				_movementXML.@[ConstValues.A_DURATION_TWEEN] = Math.round(value * ImportDataProxy.getInstance().frameRate);
				updateMovement();
			}
		}

		public function get loop():Boolean
		{
			return _movementXML?Boolean(int(_movementXML.attribute(ConstValues.A_LOOP)) == 1):false;
		}
		public function set loop(value:Boolean):void
		{
			if(_movementXML)
			{
				_movementXML.@[ConstValues.A_LOOP] = value?1:0;
				updateMovement();
			}
		}

		public function get tweenEasing():Number
		{
			return _movementXML?Number(_movementXML.attribute(ConstValues.A_TWEEN_EASING)):-1.1;
		}
		public function set tweenEasing(value:Number):void
		{
			if(_movementXML)
			{
				if(value<-1)
				{
					_movementXML.@[ConstValues.A_TWEEN_EASING] = NaN;
				}
				else
				{
					_movementXML.@[ConstValues.A_TWEEN_EASING] = value;
				}
				updateMovement();
			}
		}

		public function get boneScale():Number
		{
			if(!_movementBoneXML || int(_movementXML.attribute(ConstValues.A_DURATION)) < 2)
			{
				return NaN;
			}
			return Number(_movementBoneXML.attribute(ConstValues.A_MOVEMENT_SCALE)) * 100;
		}
		public function set boneScale(value:Number):void
		{
			if(_movementBoneXML)
			{
				_movementBoneXML.@[ConstValues.A_MOVEMENT_SCALE] = value * 0.01;
				updateMovementBone();
			}
		}

		public function get boneDelay():Number
		{
			if(!_movementBoneXML || int(_movementXML.attribute(ConstValues.A_DURATION)) < 2)
			{
				return NaN;
			}
			return Number(_movementBoneXML.attribute(ConstValues.A_MOVEMENT_DELAY))* 100;
		}
		public function set boneDelay(value:Number):void
		{
			if(_movementBoneXML)
			{
				_movementBoneXML.@[ConstValues.A_MOVEMENT_DELAY] = value * 0.01;
				updateMovementBone();
			}
		}

		public function AnimationDataProxy()
		{
			movementsMC = new XMLListCollection();
		}

		internal function setData(xml:XML):void
		{
			_xml = xml;
			if(_xml)
			{
				_movementsXMLList = _xml.elements(ConstValues.MOVEMENT);
			}
			else
			{
				_movementsXMLList = null;
			}

			movementsMC.source = _movementsXMLList;

			MessageDispatcher.dispatchEvent(MessageDispatcher.CHANGE_ANIMATION_DATA, animationName);

			changeMovement();
		}

		public function changeMovement(movementName:String = null, isChangedByArmature:Boolean = false):void
		{
			_movementXML = ImportDataProxy.getElementByName(_movementsXMLList, movementName, true);
			if(_movementXML)
			{
				_movementBonesXMLList = _movementXML.elements(ConstValues.BONE);
			}
			else
			{
				_movementBonesXMLList = null;
			}

			MessageDispatcher.dispatchEvent(MessageDispatcher.CHANGE_MOVEMENT_DATA, this.movementName, isChangedByArmature);

			changeMovementBone(ImportDataProxy.getInstance().armatureDataProxy.boneName);
		}

		public function changeMovementBone(boneName:String = null):void
		{
			var movementBoneXML:XML = ImportDataProxy.getElementByName(_movementBonesXMLList, boneName, true);
			if(movementBoneXML == _movementBoneXML)
			{
				return;
			}
			_movementBoneXML = movementBoneXML;
			MessageDispatcher.dispatchEvent(MessageDispatcher.CHANGE_MOVEMENT_BONE_DATA , this.boneName);
		}

		internal function updateBoneParent(boneName:String):void
		{
			if(_xml)
			{
				var animationData:AnimationData = ImportDataProxy.getInstance().skeletonData.getAnimationData(animationName);
				var armatureData:ArmatureData = ImportDataProxy.getInstance().skeletonData.getArmatureData(animationName);
				XMLDataParser.parseAnimationData(_xml, animationData, armatureData);
			}
		}

		private function updateMovement():void
		{
			var animationData:AnimationData = ImportDataProxy.getInstance().skeletonData.getAnimationData(animationName);
			var movementData:MovementData = animationData.getMovementData(movementName);

			movementData.durationTo = durationTo;
			movementData.durationTween = durationTween;
			movementData.loop = loop;
			movementData.tweenEasing = tweenEasing;

			if(!ImportDataProxy.getInstance().isExportedSource)
			{
				JSFLProxy.getInstance().changeMovement(animationName, movementName, _movementXML);
			}

			MessageDispatcher.dispatchEvent(MessageDispatcher.UPDATE_MOVEMENT_DATA, movementName);
		}

		private function updateMovementBone():void
		{
			var animationData:AnimationData = ImportDataProxy.getInstance().skeletonData.getAnimationData(animationName);
			var movementData:MovementData = animationData.getMovementData(movementName);
			var movementBoneData:MovementBoneData = movementData.getMovementBoneData(boneName);

			movementBoneData.scale = boneScale * 0.01;
			movementBoneData.delay = boneDelay * 0.01;
			if(movementBoneData.delay > 0)
			{
				movementBoneData.delay -= 1;
			}

			if(!ImportDataProxy.getInstance().isExportedSource)
			{
				var movementXMLCopy:XML = _movementXML.copy();
				delete movementXMLCopy.elements(ConstValues.BONE).*;
				delete movementXMLCopy[ConstValues.FRAME];
				JSFLProxy.getInstance().changeMovement(animationName, movementName, movementXMLCopy);
			}

			MessageDispatcher.dispatchEvent(MessageDispatcher.UPDATE_MOVEMENT_BONE_DATA, movementName);
		}
	}
}

fix bone parent change bug

fix bone parent change bug