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

	import flash.utils.ByteArray;

	public final class IndexStream
	{
		use namespace minko_stream;

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

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

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

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

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

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

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

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

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

			initialize(data, length, usage);
		}

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

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

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

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

			invalidate();
		}

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

			return _data[index];
		}

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

			_data[index] = value;
		}

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

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

			invalidate();

			return deletedIndices;
		}

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

			var numIndices : int = length;

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

			return indices;
		}

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

			var length : int = indices.length;

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

			_data.length = length;

			invalidate();
		}

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

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

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

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

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

			invalidate();

			return this;
		}

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

			var numIndices : int = _data.length;

			count ||= indices.length;

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

			invalidate();
		}

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

			_data = null;
			_usage = StreamUsage.STATIC;
		}

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

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

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

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

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

			return indices;
		}

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

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

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

			return stream;
		}
	}
}

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

	import flash.utils.ByteArray;

	public final class IndexStream
	{
		use namespace minko_stream;

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

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

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

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

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

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

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

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

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

			initialize(data, length, usage);
		}

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

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

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

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

			invalidate();
		}

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

			return _data[index];
		}

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

			_data[index] = value;
		}

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

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

			invalidate();

			return deletedIndices;
		}

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

			var numIndices : int = length;

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

			return indices;
		}

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

			var length : int = indices.length;

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

			_data.length = length;

			invalidate();
		}

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

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

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

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

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

			invalidate();

			return this;
		}

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

			var numIndices : int = _data.length;

			count ||= indices.length;

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

			invalidate();
		}

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

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

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

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

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

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

			return indices;
		}

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

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

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

			return stream;
		}
	}
}

add an optional StreamUsage argument to IndexStream.clone() and the possibility to "wait for upload" when calling IndexStream.disposeLocalData()


/*
 * Copyright (C) 2003-2012 Igniterealtime Community Contributors
 *
 *     Daniel Henninger
 *     Derrick Grigg <dgrigg@rogers.com>
 *     Juga Paazmaya <olavic@gmail.com>
 *     Nick Velloff <nick.velloff@gmail.com>
 *     Sean Treadway <seant@oncotype.dk>
 *     Sean Voisen <sean@voisen.org>
 *     Mark Walters <mark@yourpalmark.com>
 *     Michael McCarthy <mikeycmccarthy@gmail.com>
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.igniterealtime.xiff.si
{
	import flash.events.EventDispatcher;

	import org.igniterealtime.xiff.core.*;
	import org.igniterealtime.xiff.data.forms.*;
	import org.igniterealtime.xiff.data.si.*;
	import org.igniterealtime.xiff.events.*;

	/**
	 *
	 * @eventType org.igniterealtime.xiff.events.FileTransferEvent.TRANSFER_INIT
	 */
	[Event(name="transferInit", type="org.igniterealtime.xiff.events.FileTransferEvent")]

	/**
	 * Manages Stream Initiation (XEP-0095) based File Transfer (XEP-0096)
	 *
	 * @see http://xmpp.org/extensions/xep-0095.html
	 * @see http://xmpp.org/extensions/xep-0096.html
	 */
	public class FileTransferManager extends EventDispatcher
	{

		private var _connection:IXMPPConnection;

		/**
		 *
		 * @param	aConnection A reference to an XMPPConnection class instance
		 */
		public function FileTransferManager( aConnection:IXMPPConnection = null )
		{
			if ( aConnection != null )
			{
				connection = aConnection;
			}
        }

		/**
		 * The connection used for sending and receiving data.
		 */
		public function get connection():IXMPPConnection
		{
			return _connection;
		}
		public function set connection( value:IXMPPConnection ):void
		{
			if ( _connection != null )
			{
			}

			_connection = value;
			_connection.enableExtensions( FileTransferExtension, FormExtension, StreamInitiationExtension );
		}
	}
}

/*
 * Copyright (C) 2003-2012 Igniterealtime Community Contributors
 *
 *     Daniel Henninger
 *     Derrick Grigg <dgrigg@rogers.com>
 *     Juga Paazmaya <olavic@gmail.com>
 *     Nick Velloff <nick.velloff@gmail.com>
 *     Sean Treadway <seant@oncotype.dk>
 *     Sean Voisen <sean@voisen.org>
 *     Mark Walters
 *     Michael McCarthy <mikeycmccarthy@gmail.com>
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.igniterealtime.xiff.si
{
	import flash.events.EventDispatcher;
	import flash.utils.ByteArray;

	import org.igniterealtime.xiff.core.*;
	import org.igniterealtime.xiff.data.*;
	import org.igniterealtime.xiff.data.feature.FeatureNegotiationExtension;
	import org.igniterealtime.xiff.data.forms.*;
	import org.igniterealtime.xiff.data.forms.enum.*;
	import org.igniterealtime.xiff.data.si.*;
	import org.igniterealtime.xiff.data.stream.*;
	import org.igniterealtime.xiff.data.message.*;
	import org.igniterealtime.xiff.events.*;

	/**
	 *
	 * @eventType org.igniterealtime.xiff.events.FileTransferEvent.TRANSFER_INIT
	 */
	[Event(name="transferInit", type="org.igniterealtime.xiff.events.FileTransferEvent")]
	/**
	 *
	 * @eventType org.igniterealtime.xiff.events.FileTransferEvent.FEATURE_NEGOTIATED
	 */
	[Event(name="featureNegotiated", type="org.igniterealtime.xiff.events.FileTransferEvent")]

	/**
	 * INCOMPLETE
	 *
	 * Manages Stream Initiation (XEP-0095) based File Transfer (XEP-0096)
	 *
	 * @see http://xmpp.org/extensions/xep-0095.html
	 * @see http://xmpp.org/extensions/xep-0096.html
	 */
	public class FileTransferManager extends EventDispatcher
	{

		private var _connection:IXMPPConnection;
		private var _outGoingData:ByteArray;
		private var _sequence:uint = 0;
		private var _chunkSize:uint = 4096;

		/**
		 * Set the out going data first.
		 * Then beginNegotiation with all the relevant information
		 * Once FEATURE_NEGOTIATED occurs, call initiateSession.
		 *
		 * @param	aConnection A reference to an XMPPConnection class instance
		 */
		public function FileTransferManager( aConnection:IXMPPConnection = null )
		{
			if ( aConnection != null )
			{
				connection = aConnection;
			}
		}

		/**
		 * Negotiate a suitable transfer protocol.
		 * Feature negotiation is done with a FormExtension.
		 *
		 *
It can for example contain a list of possible stream methods.
		 * It seems that SOCKS5 Bytestreams (XEP-0065) will always be
		 * preferable, but it not implemented in XIFF.

		 *
		 * @see http://xmpp.org/extensions/xep-0065.html
		 * @see http://xmpp.org/extensions/xep-0066.html
		 * @see http://xmpp.org/extensions/xep-0047.html
		 */
		public function beginNegotiation(to:EscapedJID, fileName:String, fileSize:uint, mimeType:String):void
		{
			/*
			<iq type='set' id='offer1' to='receiver@jabber.org/resource'>
			  <si xmlns='http://jabber.org/protocol/si'
				  id='a0'
				  mime-type='text/plain'
				  profile='http://jabber.org/protocol/si/profile/file-transfer'>
				<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'
					  name='test.txt'
					  size='1022'/>
				<feature xmlns='http://jabber.org/protocol/feature-neg'>
				  <x xmlns='jabber:x:data' type='form'>
					<field var='stream-method' type='list-single'>
					  <option><value>http://jabber.org/protocol/bytestreams</value></option>
					  <option><value>http://jabber.org/protocol/ibb</value></option>
					</field>
				  </x>
				</feature>
			  </si>
			</iq>
			*/
			var options:Array = [
				{ label: "XEP-0066: Out of Band Data", value: OutOfBoundDataExtension.NS },
				{ label: "XEP-0047: In-Band Bytestreams", value: IBBExtension.NS }
			];
			var field:FormField = new FormField(
				FormFieldType.LIST_SINGLE,
				"stream-method",
				null,
				options
			);
			var formExt:FormExtension = new FormExtension();
			formExt.type = FormType.FORM;
			formExt.fields = [field];

			var featExt:FeatureNegotiationExtension = new FeatureNegotiationExtension();
			featExt.addExtension(formExt);

			var fileExt:FileTransferExtension = new FileTransferExtension();
			fileExt.name = fileName;
			fileExt.size = fileSize;
			fileExt.addExtension(featExt);

			var siExt:StreamInitiationExtension = new StreamInitiationExtension();
			siExt.id = "letsdoit";
			siExt.profile = FileTransferExtension.NS;
			siExt.mimeType = mimeType;
			siExt.addExtension(featExt);

			var iq:IQ = new IQ(to, IQ.TYPE_SET, null, beginNegotiation_callback, beginNegotiation_errorCallback);
			iq.addExtension(siExt);
			_connection.send(iq);
		}

		/**
		 * Called once the IQ stanza with the proper id is coming back
		 */
		private function beginNegotiation_callback(iq:IQ):void
		{
			var event:FileTransferEvent = new FileTransferEvent(FileTransferEvent.FEATURE_NEGOTIATED);
			dispatchEvent(event);
			/*
			<iq type='result' to='sender@jabber.org/resource' id='offer1'>
			  <si xmlns='http://jabber.org/protocol/si'>
				<feature xmlns='http://jabber.org/protocol/feature-neg'>
				  <x xmlns='jabber:x:data' type='submit'>
					<field var='stream-method'>
					  <value>http://jabber.org/protocol/bytestreams</value>
					</field>
				  </x>
				</feature>
			  </si>
			</iq>
			*/
			var siExt:StreamInitiationExtension = iq.getAllExtensionsByNS(StreamInitiationExtension.NS)[0] as StreamInitiationExtension;

			var featExt:FeatureNegotiationExtension = siExt.getAllExtensionsByNS(FeatureNegotiationExtension.NS)[0] as FeatureNegotiationExtension;

			var formExt:FormExtension = featExt.getAllExtensionsByNS(FormExtension.NS)[0] as FormExtension;

			var field:FormField = formExt.getFormField("stream-method");
			trace("beginNegotiation_callback. field.value: " + field.value);
		}

		private function beginNegotiation_errorCallback(iq:IQ):void
		{
			// fail....
		}

		/**
		 * Once the stream method has been negotiated, initiate the session.
		 */
		public function initiateSession(to:EscapedJID):void
		{
			/*
			<iq from='romeo@montague.net/orchard'
				id='jn3h8g65'
				to='juliet@capulet.com/balcony'
				type='set'>
			  <open xmlns='http://jabber.org/protocol/ibb'
					block-size='4096'
					sid='i781hf64'
					stanza='iq'/>
			</iq>
			*/
			var openExt:IBBOpenExtension = new IBBOpenExtension();
			openExt.sid = "letsdoit";
			openExt.blockSize = _chunkSize;

			var iq:IQ = new IQ(to, IQ.TYPE_SET, null, initiateSession_callback, initiateSession_errorCallback);
			iq.addExtension(openExt);
			_connection.send(iq);
		}

		/**
		 * Called once the IQ stanza with the proper id is coming back
		 */
		private function initiateSession_callback(iq:IQ):void
		{
			var event:FileTransferEvent = new FileTransferEvent(FileTransferEvent.TRANSFER_INIT);
			dispatchEvent(event);
			/*
			<iq from='juliet@capulet.com/balcony'
				id='jn3h8g65'
				to='romeo@montague.net/orchard'
				type='result'/>
			*/
			sendChunk(iq.from);
		}

		private function initiateSession_errorCallback(iq:IQ):void
		{
			// fail....
		}

		private function sendChunk(to:EscapedJID):void
		{
			/*
			<iq from='romeo@montague.net/orchard'
				id='kr91n475'
				to='juliet@capulet.com/balcony'
				type='set'>
			  <data xmlns='http://jabber.org/protocol/ibb' seq='0' sid='i781hf64'>
				qANQR1DBwU4DX7jmYZnncmUQB/9KuKBddzQH+tZ1ZywKK0yHKnq57kWq+RFtQdCJ
				WpdWpR0uQsuJe7+vh3NWn59/gTc5MDlX8dS9p0ovStmNcyLhxVgmqS8ZKhsblVeu
				IpQ0JgavABqibJolc3BKrVtVV1igKiX/N7Pi8RtY1K18toaMDhdEfhBRzO/XB0+P
				AQhYlRjNacGcslkhXqNjK5Va4tuOAPy2n1Q8UUrHbUd0g+xJ9Bm0G0LZXyvCWyKH
				kuNEHFQiLuCY6Iv0myq6iX6tjuHehZlFSh80b5BVV9tNLwNR5Eqz1klxMhoghJOA
			  </data>
			</iq>
			*/
			var iq:IQ = new IQ(to, IQ.TYPE_SET, null, sendChunk_callback, sendChunk_errorCallback);
			var len:uint = Math.min(_outGoingData.bytesAvailable, _chunkSize);
			if (len > 0)
			{
				var dataExt:IBBDataExtension = new IBBDataExtension();
				dataExt.sid = "letsdoit";
				dataExt.seq = _sequence;
				dataExt.data = _outGoingData.readUTFBytes(len);
				iq.addExtension(dataExt);
				++_sequence;
			}
			else
			{
				iq.addExtension(new IBBCloseExtension());
			}

			_connection.send(iq);
		}

		private function sendChunk_callback(iq:IQ):void
		{
			if (_outGoingData.bytesAvailable > 0)
			{
				sendChunk(iq.from);
			}
		}

		private function sendChunk_errorCallback(iq:IQ):void
		{
			// fail....
		}

		/**
		 * Base64 encoded data that is sent to the recipient.
		 */
		public function get outGoingData():ByteArray
		{
			return _outGoingData;
		}
		public function set outGoingData( value:ByteArray ):void
		{
			_outGoingData = value;
		}

		/**
		 * The connection used for sending and receiving data.
		 */
		public function get connection():IXMPPConnection
		{
			return _connection;
		}
		public function set connection( value:IXMPPConnection ):void
		{
			if ( _connection != null )
			{
			}

			_connection = value;
			_connection.enableExtensions( FileTransferExtension, FormExtension, StreamInitiationExtension );
		}
	}
}

Work in progress...

Work in progress...

git-svn-id: c197267f952b24206666de142881703007ca05d5@13250 b35dd754-fafc-0310-a699-88a17e54d16e


package org.mangui.chromeless {
    import flash.net.URLStream;

    import org.mangui.hls.model.Level;
    import org.mangui.hls.*;
    import org.mangui.hls.utils.*;

    import flash.display.*;
    import flash.events.*;
    import flash.external.ExternalInterface;
    import flash.geom.Rectangle;
    import flash.media.Video;
    import flash.media.SoundTransform;
    import flash.media.StageVideo;
    import flash.media.StageVideoAvailability;
    import flash.utils.setTimeout;

    // import com.sociodox.theminer.*;
    public class ChromelessPlayer extends Sprite {
        /** reference to the framework. **/
        protected var _hls : HLS;
        /** Sheet to place on top of the video. **/
        protected var _sheet : Sprite;
        /** Reference to the stage video element. **/
        protected var _stageVideo : StageVideo = null;
        /** Reference to the video element. **/
        protected var _video : Video = null;
        /** Video size **/
        protected var _videoWidth : int = 0;
        protected var _videoHeight : int = 0;
        /** current media position */
        protected var _media_position : Number;
        protected var _duration : Number;
    /** URL autoload feature */
        protected var _autoLoad : Boolean = false;

        /** Initialization. **/
        public function ChromelessPlayer() {
            _setupStage();
            _setupSheet();
            _setupExternalGetters();
            _setupExternalCallers();

            setTimeout(_pingJavascript, 50);
        };

        protected function _setupExternalGetters():void {
            ExternalInterface.addCallback("getLevel", _getLevel);
            ExternalInterface.addCallback("getLevels", _getLevels);
            ExternalInterface.addCallback("getAutoLevel", _getAutoLevel);
            ExternalInterface.addCallback("getDuration", _getDuration);
            ExternalInterface.addCallback("getPosition", _getPosition);
            ExternalInterface.addCallback("getPlaybackState", _getPlaybackState);
            ExternalInterface.addCallback("getSeekState", _getSeekState);
            ExternalInterface.addCallback("getType", _getType);
            ExternalInterface.addCallback("getmaxBufferLength", _getmaxBufferLength);
            ExternalInterface.addCallback("getminBufferLength", _getminBufferLength);
            ExternalInterface.addCallback("getlowBufferLength", _getlowBufferLength);
            ExternalInterface.addCallback("getbufferLength", _getbufferLength);
            ExternalInterface.addCallback("getLogDebug", _getLogDebug);
            ExternalInterface.addCallback("getLogDebug2", _getLogDebug2);
            ExternalInterface.addCallback("getCapLeveltoStage", _getCapLeveltoStage);
            ExternalInterface.addCallback("getflushLiveURLCache", _getflushLiveURLCache);
            ExternalInterface.addCallback("getstartFromLevel", _getstartFromLevel);
            ExternalInterface.addCallback("getseekFromLowestLevel", _getseekFromLevel);
            ExternalInterface.addCallback("getJSURLStream", _getJSURLStream);
            ExternalInterface.addCallback("getPlayerVersion", _getPlayerVersion);
            ExternalInterface.addCallback("getAudioTrackList", _getAudioTrackList);
            ExternalInterface.addCallback("getAudioTrackId", _getAudioTrackId);
        };

        protected function _setupExternalCallers():void {
            ExternalInterface.addCallback("playerLoad", _load);
            ExternalInterface.addCallback("playerPlay", _play);
            ExternalInterface.addCallback("playerPause", _pause);
            ExternalInterface.addCallback("playerResume", _resume);
            ExternalInterface.addCallback("playerSeek", _seek);
            ExternalInterface.addCallback("playerStop", _stop);
            ExternalInterface.addCallback("playerVolume", _volume);
            ExternalInterface.addCallback("playerSetLevel", _setLevel);
            ExternalInterface.addCallback("playerSmoothSetLevel", _smoothSetLevel);
            ExternalInterface.addCallback("playerSetmaxBufferLength", _setmaxBufferLength);
            ExternalInterface.addCallback("playerSetminBufferLength", _setminBufferLength);
            ExternalInterface.addCallback("playerSetlowBufferLength", _setlowBufferLength);
            ExternalInterface.addCallback("playerSetflushLiveURLCache", _setflushLiveURLCache);
            ExternalInterface.addCallback("playerSetstartFromLevel", _setstartFromLevel);
            ExternalInterface.addCallback("playerSetseekFromLevel", _setseekFromLevel);
            ExternalInterface.addCallback("playerSetLogDebug", _setLogDebug);
            ExternalInterface.addCallback("playerSetLogDebug2", _setLogDebug2);
            ExternalInterface.addCallback("playerCapLeveltoStage", _setCapLeveltoStage);
            ExternalInterface.addCallback("playerSetAudioTrack", _setAudioTrack);
            ExternalInterface.addCallback("playerSetJSURLStream", _setJSURLStream);
        };

        protected function _setupStage():void {
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.fullScreenSourceRect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
            stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState);
            stage.addEventListener(Event.RESIZE, _onStageResize);
        }

        protected function _setupSheet():void {
            // Draw sheet for catching clicks
            _sheet = new Sprite();
            _sheet.graphics.beginFill(0x000000, 0);
            _sheet.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
            _sheet.addEventListener(MouseEvent.CLICK, _clickHandler);
            _sheet.buttonMode = true;
            addChild(_sheet);
        }

        /** Notify javascript the framework is ready. **/
        protected function _pingJavascript() : void {
            ExternalInterface.call("onHLSReady", ExternalInterface.objectID);
        };

        /** Forward events from the framework. **/
        protected function _completeHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onComplete");
            }
        };

        protected function _errorHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                var hlsError : HLSError = event.error;
                ExternalInterface.call("onError", hlsError.code, hlsError.url, hlsError.msg);
            }
        };

        protected function _fragmentHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onFragment", event.metrics.bandwidth, event.metrics.level, stage.stageWidth);
            }
        };

        protected function _manifestHandler(event : HLSEvent) : void {
            _duration = event.levels[_hls.startlevel].duration;

            if (_autoLoad) {
                _play();
            }

            if (ExternalInterface.available) {
                ExternalInterface.call("onManifest", _duration);
            }
        };

        protected function _mediaTimeHandler(event : HLSEvent) : void {
            _duration = event.mediatime.duration;
            _media_position = event.mediatime.position;
            if (ExternalInterface.available) {
                ExternalInterface.call("onPosition", event.mediatime.position, event.mediatime.duration, event.mediatime.live_sliding,event.mediatime.buffer, event.mediatime.program_date);
            }

            var videoWidth : int = _video ? _video.videoWidth : _stageVideo.videoWidth;
            var videoHeight : int = _video ? _video.videoHeight : _stageVideo.videoHeight;

            if (videoWidth && videoHeight) {
                var changed : Boolean = _videoWidth != videoWidth || _videoHeight != videoHeight;
                if (changed) {
                    _videoHeight = videoHeight;
                    _videoWidth = videoWidth;
                    _resize();
                    if (ExternalInterface.available) {
                        ExternalInterface.call("onVideoSize", _videoWidth, _videoHeight);
                    }
                }
            }
        };

        protected function _stateHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onState", event.state);
            }
        };

        protected function _levelSwitchHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onSwitch", event.level);
            }
        };

        protected function _audioTracksListChange(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onAudioTracksListChange", _getAudioTrackList());
            }
        }

        protected function _audioTrackChange(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onAudioTrackChange", event.audioTrack);
            }
        }

        /** Javascript getters. **/
        protected function _getLevel() : int {
            return _hls.level;
        };

        protected function _getLevels() : Vector.<Level> {
            return _hls.levels;
        };

        protected function _getAutoLevel() : Boolean {
            return _hls.autolevel;
        };

        protected function _getDuration() : Number {
            return _duration;
        };

        protected function _getPosition() : Number {
            return _hls.position;
        };

        protected function _getPlaybackState() : String {
            return _hls.playbackState;
        };

        protected function _getSeekState() : String {
            return _hls.seekState;
        };

        protected function _getType() : String {
            return _hls.type;
        };

        protected function _getbufferLength() : Number {
            return _hls.bufferLength;
        };

        protected function _getmaxBufferLength() : Number {
            return HLSSettings.maxBufferLength;
        };

        protected function _getminBufferLength() : Number {
            return HLSSettings.minBufferLength;
        };

        protected function _getlowBufferLength() : Number {
            return HLSSettings.lowBufferLength;
        };

        protected function _getflushLiveURLCache() : Boolean {
            return HLSSettings.flushLiveURLCache;
        };

        protected function _getstartFromLevel() : int {
            return HLSSettings.startFromLevel;
        };

        protected function _getseekFromLevel() : int {
            return HLSSettings.seekFromLevel;
        };

        protected function _getLogDebug() : Boolean {
            return HLSSettings.logDebug;
        };

        protected function _getLogDebug2() : Boolean {
            return HLSSettings.logDebug2;
        };

        protected function _getCapLeveltoStage() : Boolean {
            return HLSSettings.capLevelToStage;
        };

        protected function _getJSURLStream() : Boolean {
            return (_hls.URLstream is JSURLStream);
        };

        protected function _getPlayerVersion() : Number {
            return 2;
        };

        protected function _getAudioTrackList() : Array {
            var list : Array = [];
            var vec : Vector.<HLSAudioTrack> = _hls.audioTracks;
            for (var i : Object in vec) {
                list.push(vec[i]);
            }
            return list;
        };

        protected function _getAudioTrackId() : int {
            return _hls.audioTrack;
        };

        /** Javascript calls. **/
        protected function _load(url : String) : void {
            _hls.load(url);
        };

        protected function _play() : void {
            _hls.stream.play();
        };

        protected function _pause() : void {
            _hls.stream.pause();
        };

        protected function _resume() : void {
            _hls.stream.resume();
        };

        protected function _seek(position : Number) : void {
            _hls.stream.seek(position);
        };

        protected function _stop() : void {
            _hls.stream.close();
        };

        protected function _volume(percent : Number) : void {
            _hls.stream.soundTransform = new SoundTransform(percent / 100);
        };

        protected function _setLevel(level : int) : void {
            _smoothSetLevel(level);
            if (!isNaN(_media_position) && level != -1) {
                _hls.stream.seek(_media_position);
            }
        };

        protected function _smoothSetLevel(level : int) : void {
            if (level != _hls.level) {
                _hls.level = level;
            }
        };

        protected function _setmaxBufferLength(new_len : Number) : void {
            HLSSettings.maxBufferLength = new_len;
        };

        protected function _setminBufferLength(new_len : Number) : void {
            HLSSettings.minBufferLength = new_len;
        };

        protected function _setlowBufferLength(new_len : Number) : void {
            HLSSettings.lowBufferLength = new_len;
        };

        protected function _setflushLiveURLCache(flushLiveURLCache : Boolean) : void {
            HLSSettings.flushLiveURLCache = flushLiveURLCache;
        };

        protected function _setstartFromLevel(startFromLevel : int) : void {
            HLSSettings.startFromLevel = startFromLevel;
        };

        protected function _setseekFromLevel(seekFromLevel : int) : void {
            HLSSettings.seekFromLevel = seekFromLevel;
        };

        protected function _setLogDebug(debug : Boolean) : void {
            HLSSettings.logDebug = debug;
        };

        protected function _setLogDebug2(debug2 : Boolean) : void {
            HLSSettings.logDebug2 = debug2;
        };

        protected function _setCapLeveltoStage(value : Boolean) : void {
            HLSSettings.capLevelToStage = value;
        };

        protected function _setJSURLStream(jsURLstream : Boolean) : void {
            if (jsURLstream) {
                _hls.URLstream = JSURLStream as Class;
            } else {
                _hls.URLstream = URLStream as Class;
            }
        };

        protected function _setAudioTrack(val : int) : void {
            if (val == _hls.audioTrack) return;
            _hls.audioTrack = val;
            if (!isNaN(_media_position)) {
                _hls.stream.seek(_media_position);
            }
        };

        /** Mouse click handler. **/
        protected function _clickHandler(event : MouseEvent) : void {
            if (stage.displayState == StageDisplayState.FULL_SCREEN_INTERACTIVE || stage.displayState == StageDisplayState.FULL_SCREEN) {
                stage.displayState = StageDisplayState.NORMAL;
            } else {
                stage.displayState = StageDisplayState.FULL_SCREEN;
            }
        };

        /** StageVideo detector. **/
        protected function _onStageVideoState(event : StageVideoAvailabilityEvent) : void {
            var available : Boolean = (event.availability == StageVideoAvailability.AVAILABLE);
            _hls = new HLS();
            _hls.stage = stage;
            _hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE, _completeHandler);
            _hls.addEventListener(HLSEvent.ERROR, _errorHandler);
            _hls.addEventListener(HLSEvent.FRAGMENT_LOADED, _fragmentHandler);
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestHandler);
            _hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler);
            _hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            _hls.addEventListener(HLSEvent.AUDIO_TRACKS_LIST_CHANGE, _audioTracksListChange);
            _hls.addEventListener(HLSEvent.AUDIO_TRACK_CHANGE, _audioTrackChange);

            if (available && stage.stageVideos.length > 0) {
                _stageVideo = stage.stageVideos[0];
                _stageVideo.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
                _stageVideo.attachNetStream(_hls.stream);
            } else {
                _video = new Video(stage.stageWidth, stage.stageHeight);
                addChild(_video);
                _video.smoothing = true;
                _video.attachNetStream(_hls.stream);
            }
            stage.removeEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState);

            var autoLoadUrl : String = root.loaderInfo.parameters.url as String;
            if (autoLoadUrl != null) {
                _autoLoad = true;
                _load(autoLoadUrl);
            }
        };

        protected function _onStageResize(event : Event) : void {
            stage.fullScreenSourceRect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
            _sheet.width = stage.stageWidth;
            _sheet.height = stage.stageHeight;
            _resize();
        };

        protected function _resize() : void {
            var rect : Rectangle;
            rect = ScaleVideo.resizeRectangle(_videoWidth, _videoHeight, stage.stageWidth, stage.stageHeight);
            // resize video
            if (_video) {
                _video.width = rect.width;
                _video.height = rect.height;
                _video.x = rect.x;
                _video.y = rect.y;
            } else if (_stageVideo) {
                _stageVideo.viewPort = rect;
            }
        }
    }
}

package org.mangui.chromeless {
    import flash.net.URLStream;

    import org.mangui.hls.model.Level;
    import org.mangui.hls.*;
    import org.mangui.hls.utils.*;

    import flash.display.*;
    import flash.events.*;
    import flash.external.ExternalInterface;
    import flash.geom.Rectangle;
    import flash.media.Video;
    import flash.media.SoundTransform;
    import flash.media.StageVideo;
    import flash.media.StageVideoAvailability;
    import flash.utils.setTimeout;

    // import com.sociodox.theminer.*;
    public class ChromelessPlayer extends Sprite {
        /** reference to the framework. **/
        protected var _hls : HLS;
        /** Sheet to place on top of the video. **/
        protected var _sheet : Sprite;
        /** Reference to the stage video element. **/
        protected var _stageVideo : StageVideo = null;
        /** Reference to the video element. **/
        protected var _video : Video = null;
        /** Video size **/
        protected var _videoWidth : int = 0;
        protected var _videoHeight : int = 0;
        /** current media position */
        protected var _media_position : Number;
        protected var _duration : Number;
    /** URL autoload feature */
        protected var _autoLoad : Boolean = false;

        /** Initialization. **/
        public function ChromelessPlayer() {
            _setupStage();
            _setupSheet();
            _setupExternalGetters();
            _setupExternalCallers();

            setTimeout(_pingJavascript, 50);
        };

        protected function _setupExternalGetters():void {
            ExternalInterface.addCallback("getLevel", _getLevel);
            ExternalInterface.addCallback("getLevels", _getLevels);
            ExternalInterface.addCallback("getAutoLevel", _getAutoLevel);
            ExternalInterface.addCallback("getDuration", _getDuration);
            ExternalInterface.addCallback("getPosition", _getPosition);
            ExternalInterface.addCallback("getPlaybackState", _getPlaybackState);
            ExternalInterface.addCallback("getSeekState", _getSeekState);
            ExternalInterface.addCallback("getType", _getType);
            ExternalInterface.addCallback("getmaxBufferLength", _getmaxBufferLength);
            ExternalInterface.addCallback("getminBufferLength", _getminBufferLength);
            ExternalInterface.addCallback("getlowBufferLength", _getlowBufferLength);
            ExternalInterface.addCallback("getbufferLength", _getbufferLength);
            ExternalInterface.addCallback("getLogDebug", _getLogDebug);
            ExternalInterface.addCallback("getLogDebug2", _getLogDebug2);
            ExternalInterface.addCallback("getCapLeveltoStage", _getCapLeveltoStage);
            ExternalInterface.addCallback("getflushLiveURLCache", _getflushLiveURLCache);
            ExternalInterface.addCallback("getstartFromLevel", _getstartFromLevel);
            ExternalInterface.addCallback("getseekFromLowestLevel", _getseekFromLevel);
            ExternalInterface.addCallback("getJSURLStream", _getJSURLStream);
            ExternalInterface.addCallback("getPlayerVersion", _getPlayerVersion);
            ExternalInterface.addCallback("getAudioTrackList", _getAudioTrackList);
            ExternalInterface.addCallback("getAudioTrackId", _getAudioTrackId);
        };

        protected function _setupExternalCallers():void {
            ExternalInterface.addCallback("playerLoad", _load);
            ExternalInterface.addCallback("playerPlay", _play);
            ExternalInterface.addCallback("playerPause", _pause);
            ExternalInterface.addCallback("playerResume", _resume);
            ExternalInterface.addCallback("playerSeek", _seek);
            ExternalInterface.addCallback("playerStop", _stop);
            ExternalInterface.addCallback("playerVolume", _volume);
            ExternalInterface.addCallback("playerSetLevel", _setLevel);
            ExternalInterface.addCallback("playerSmoothSetLevel", _smoothSetLevel);
            ExternalInterface.addCallback("playerSetmaxBufferLength", _setmaxBufferLength);
            ExternalInterface.addCallback("playerSetminBufferLength", _setminBufferLength);
            ExternalInterface.addCallback("playerSetlowBufferLength", _setlowBufferLength);
            ExternalInterface.addCallback("playerSetflushLiveURLCache", _setflushLiveURLCache);
            ExternalInterface.addCallback("playerSetstartFromLevel", _setstartFromLevel);
            ExternalInterface.addCallback("playerSetseekFromLevel", _setseekFromLevel);
            ExternalInterface.addCallback("playerSetLogDebug", _setLogDebug);
            ExternalInterface.addCallback("playerSetLogDebug2", _setLogDebug2);
            ExternalInterface.addCallback("playerCapLeveltoStage", _setCapLeveltoStage);
            ExternalInterface.addCallback("playerSetAudioTrack", _setAudioTrack);
            ExternalInterface.addCallback("playerSetJSURLStream", _setJSURLStream);
        };

        protected function _setupStage():void {
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.fullScreenSourceRect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
            stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState);
            stage.addEventListener(Event.RESIZE, _onStageResize);
        }

        protected function _setupSheet():void {
            // Draw sheet for catching clicks
            _sheet = new Sprite();
            _sheet.graphics.beginFill(0x000000, 0);
            _sheet.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
            _sheet.addEventListener(MouseEvent.CLICK, _clickHandler);
            _sheet.buttonMode = true;
            addChild(_sheet);
        }

        /** Notify javascript the framework is ready. **/
        protected function _pingJavascript() : void {
            ExternalInterface.call("onHLSReady", ExternalInterface.objectID);
        };

        /** Forward events from the framework. **/
        protected function _completeHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onComplete");
            }
        };

        protected function _errorHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                var hlsError : HLSError = event.error;
                ExternalInterface.call("onError", hlsError.code, hlsError.url, hlsError.msg);
            }
        };

        protected function _fragmentHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onFragment", event.metrics.bandwidth, event.metrics.level, stage.stageWidth);
            }
        };

        protected function _manifestHandler(event : HLSEvent) : void {
            _duration = event.levels[_hls.startlevel].duration;

            if (_autoLoad) {
                _play();
            }

            if (ExternalInterface.available) {
                ExternalInterface.call("onManifest", _duration);
            }
        };

        protected function _mediaTimeHandler(event : HLSEvent) : void {
            _duration = event.mediatime.duration;
            _media_position = event.mediatime.position;
            if (ExternalInterface.available) {
                ExternalInterface.call("onPosition", event.mediatime.position, event.mediatime.duration, event.mediatime.live_sliding,event.mediatime.buffer, event.mediatime.program_date);
            }

            var videoWidth : int = _video ? _video.videoWidth : _stageVideo.videoWidth;
            var videoHeight : int = _video ? _video.videoHeight : _stageVideo.videoHeight;

            if (videoWidth && videoHeight) {
                var changed : Boolean = _videoWidth != videoWidth || _videoHeight != videoHeight;
                if (changed) {
                    _videoHeight = videoHeight;
                    _videoWidth = videoWidth;
                    _resize();
                    if (ExternalInterface.available) {
                        ExternalInterface.call("onVideoSize", _videoWidth, _videoHeight);
                    }
                }
            }
        };

        protected function _stateHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onState", event.state);
            }
        };

        protected function _levelSwitchHandler(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onSwitch", event.level);
            }
        };

        protected function _audioTracksListChange(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onAudioTracksListChange", _getAudioTrackList());
            }
        }

        protected function _audioTrackChange(event : HLSEvent) : void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onAudioTrackChange", event.audioTrack);
            }
        }

        /** Javascript getters. **/
        protected function _getLevel() : int {
            return _hls.level;
        };

        protected function _getLevels() : Vector.<Level> {
            return _hls.levels;
        };

        protected function _getAutoLevel() : Boolean {
            return _hls.autolevel;
        };

        protected function _getDuration() : Number {
            return _duration;
        };

        protected function _getPosition() : Number {
            return _hls.position;
        };

        protected function _getPlaybackState() : String {
            return _hls.playbackState;
        };

        protected function _getSeekState() : String {
            return _hls.seekState;
        };

        protected function _getType() : String {
            return _hls.type;
        };

        protected function _getbufferLength() : Number {
            return _hls.bufferLength;
        };

        protected function _getmaxBufferLength() : Number {
            return HLSSettings.maxBufferLength;
        };

        protected function _getminBufferLength() : Number {
            return HLSSettings.minBufferLength;
        };

        protected function _getlowBufferLength() : Number {
            return HLSSettings.lowBufferLength;
        };

        protected function _getflushLiveURLCache() : Boolean {
            return HLSSettings.flushLiveURLCache;
        };

        protected function _getstartFromLevel() : int {
            return HLSSettings.startFromLevel;
        };

        protected function _getseekFromLevel() : int {
            return HLSSettings.seekFromLevel;
        };

        protected function _getLogDebug() : Boolean {
            return HLSSettings.logDebug;
        };

        protected function _getLogDebug2() : Boolean {
            return HLSSettings.logDebug2;
        };

        protected function _getCapLeveltoStage() : Boolean {
            return HLSSettings.capLevelToStage;
        };

        protected function _getJSURLStream() : Boolean {
            return (_hls.URLstream is JSURLStream);
        };

        protected function _getPlayerVersion() : Number {
            return 2;
        };

        protected function _getAudioTrackList() : Array {
            var list : Array = [];
            var vec : Vector.<HLSAudioTrack> = _hls.audioTracks;
            for (var i : Object in vec) {
                list.push(vec[i]);
            }
            return list;
        };

        protected function _getAudioTrackId() : int {
            return _hls.audioTrack;
        };

        /** Javascript calls. **/
        protected function _load(url : String) : void {
            _hls.load(url);
        };

        protected function _play() : void {
            _hls.stream.play();
        };

        protected function _pause() : void {
            _hls.stream.pause();
        };

        protected function _resume() : void {
            _hls.stream.resume();
        };

        protected function _seek(position : Number) : void {
            _hls.stream.seek(position);
        };

        protected function _stop() : void {
            _hls.stream.close();
        };

        protected function _volume(percent : Number) : void {
            _hls.stream.soundTransform = new SoundTransform(percent / 100);
        };

        protected function _setLevel(level : int) : void {
            _smoothSetLevel(level);
            if (!isNaN(_media_position) && level != -1) {
                _hls.stream.seek(_media_position);
            }
        };

        protected function _smoothSetLevel(level : int) : void {
            if (level != _hls.level) {
                _hls.level = level;
            }
        };

        protected function _setmaxBufferLength(new_len : Number) : void {
            HLSSettings.maxBufferLength = new_len;
        };

        protected function _setminBufferLength(new_len : Number) : void {
            HLSSettings.minBufferLength = new_len;
        };

        protected function _setlowBufferLength(new_len : Number) : void {
            HLSSettings.lowBufferLength = new_len;
        };

        protected function _setflushLiveURLCache(flushLiveURLCache : Boolean) : void {
            HLSSettings.flushLiveURLCache = flushLiveURLCache;
        };

        protected function _setstartFromLevel(startFromLevel : int) : void {
            HLSSettings.startFromLevel = startFromLevel;
        };

        protected function _setseekFromLevel(seekFromLevel : int) : void {
            HLSSettings.seekFromLevel = seekFromLevel;
        };

        protected function _setLogDebug(debug : Boolean) : void {
            HLSSettings.logDebug = debug;
        };

        protected function _setLogDebug2(debug2 : Boolean) : void {
            HLSSettings.logDebug2 = debug2;
        };

        protected function _setCapLeveltoStage(value : Boolean) : void {
            HLSSettings.capLevelToStage = value;
        };

        protected function _setJSURLStream(jsURLstream : Boolean) : void {
            if (jsURLstream) {
                _hls.URLstream = JSURLStream as Class;
            } else {
                _hls.URLstream = URLStream as Class;
            }
        };

        protected function _setAudioTrack(val : int) : void {
            if (val == _hls.audioTrack) return;
            _hls.audioTrack = val;
            if (!isNaN(_media_position)) {
                _hls.stream.seek(_media_position);
            }
        };

        /** Mouse click handler. **/
        protected function _clickHandler(event : MouseEvent) : void {
            if (stage.displayState == StageDisplayState.FULL_SCREEN_INTERACTIVE || stage.displayState == StageDisplayState.FULL_SCREEN) {
                stage.displayState = StageDisplayState.NORMAL;
            } else {
                stage.displayState = StageDisplayState.FULL_SCREEN;
            }
        };

        /** StageVideo detector. **/
        protected function _onStageVideoState(event : StageVideoAvailabilityEvent) : void {
            var available : Boolean = (event.availability == StageVideoAvailability.AVAILABLE);
            _hls = new HLS();
            _hls.stage = stage;
            _hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE, _completeHandler);
            _hls.addEventListener(HLSEvent.ERROR, _errorHandler);
            _hls.addEventListener(HLSEvent.FRAGMENT_LOADED, _fragmentHandler);
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestHandler);
            _hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler);
            _hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            _hls.addEventListener(HLSEvent.AUDIO_TRACKS_LIST_CHANGE, _audioTracksListChange);
            _hls.addEventListener(HLSEvent.AUDIO_TRACK_CHANGE, _audioTrackChange);

            if (available && stage.stageVideos.length > 0) {
                _stageVideo = stage.stageVideos[0];
                _stageVideo.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
                _stageVideo.attachNetStream(_hls.stream);
            } else {
                _video = new Video(stage.stageWidth, stage.stageHeight);
                addChild(_video);
                _video.smoothing = true;
                _video.attachNetStream(_hls.stream);
            }
            stage.removeEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState);

            var autoLoadUrl : String = root.loaderInfo.parameters.url as String;
            if (autoLoadUrl != null) {
                _autoLoad = true;
                _load(autoLoadUrl);
            }
        };

        protected function _onStageResize(event : Event) : void {
            stage.fullScreenSourceRect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
            _sheet.width = stage.stageWidth;
            _sheet.height = stage.stageHeight;
            _resize();
        };

        protected function _resize() : void {
            var rect : Rectangle;
            rect = ScaleVideo.resizeRectangle(_videoWidth, _videoHeight, stage.stageWidth, stage.stageHeight);
            // resize video
            if (_video) {
                _video.width = rect.width;
                _video.height = rect.height;
                _video.x = rect.x;
                _video.y = rect.y;
            } else if (_stageVideo && rect.width > 0) {
                _stageVideo.viewPort = rect;
            }
        }
    }
}

add robustness in _resize() related to https://github.com/bemtv/flashls/commit/4635e3b1a667d34923b6377037ac8a8d2a686dc5


package
{
	import com.adobe.images.PNGEncoder;

	import flash.desktop.NativeApplication;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.InvokeEvent;
	import flash.events.TimerEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileStream;
	import flash.geom.Rectangle;
	import flash.geom.Matrix;
	import flash.net.URLRequest;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	public class swf2png extends Sprite
	{
		public var outputWidth:int;
		public var outputHeight:int;
		public var loadedSwf:MovieClip;
		public var loader:Loader;
		private var counter:int = 0;
		private var timer:Timer;
		private var totalFrames:int;
		private var inputFileName:String;
		private var inputFilePath:String;
		private var prefix:String;
		private var separator:String = "_";
		private var outfield:TextField;
		private var outputDirPath:String;
		private var offsetMatrix:Matrix;
		private var bBox:Rectangle;

		public function swf2png() {
			NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, onInvoke);
			outfield = new TextField();
			outfield.autoSize = TextFieldAutoSize.LEFT;
			stage.addChild(outfield);
			stage.frameRate = 12;
		}

		//Loads in file
		private function loadSwf():void {

			loader = new Loader();
			log("Loading " + inputFilePath);
			loader.load(new URLRequest("file://" + inputFilePath));
			loader.contentLoaderInfo.addEventListener(Event.INIT, startLoop);

		}

		//Event handler called when the swf is loaded. Sets it up and starts the export loop
		private function startLoop(ev:Event):void {
			loadedSwf = MovieClip(ev.target.content);
			outputWidth = Math.ceil(ev.target.width);
			outputHeight = Math.ceil(ev.target.height);
			log("Loaded!");
			totalFrames = loadedSwf.totalFrames;
			log("Frame count: " + totalFrames);

			calculateBBox();

			stopClip(loadedSwf);
			goToFrame(loadedSwf, 0);
			timer = new Timer(1);

			timer.addEventListener(TimerEvent.TIMER, step);
			timer.start();

		}

		private function calculateBBox():void {
			stopClip(loadedSwf);
			goToFrame(loadedSwf, 0);

			var boundsArray:Object = {
				"x" : new Array(),
				"y" : new Array(),
				"w" : new Array(),
				"h" : new Array()
			}
			var r:Rectangle;
			for(var currentFrame:int = 0; currentFrame < totalFrames; currentFrame++) {
				goToFrame(loadedSwf, currentFrame);
				r = loader.content.getRect(stage);
				boundsArray.x.push(r.x);
				boundsArray.y.push(r.y);
				boundsArray.w.push(r.width);
				boundsArray.h.push(r.height);
			}

			boundsArray.x.sort(Array.NUMERIC);
			boundsArray.y.sort(Array.NUMERIC);
			boundsArray.w.sort(Array.NUMERIC | Array.DESCENDING);
			boundsArray.h.sort(Array.NUMERIC | Array.DESCENDING);

			bBox = new Rectangle(
				Math.floor(boundsArray.x[0]),
				Math.floor(boundsArray.y[0]),
				Math.ceil(boundsArray.w[0]),
				Math.ceil(boundsArray.h[0])
			);

			log("Bounding box: " + bBox)

			outputWidth = bBox.width;
			outputHeight = bBox.height;
			offsetMatrix = new Matrix();
			offsetMatrix.translate(bBox.x * -1, bBox.y * -1);

			return;
		}

		//Called for every frame
		private function step(ev:TimerEvent):void {
			counter++;
			if(counter <= totalFrames) {
				goToFrame(loadedSwf, counter);
				saveFrame();
			}
			else {
				timer.stop();
				log("Done!");
				exit(0);
				return;
			}
		}

		//Saves the current frame of the loader object to a png
		private function saveFrame():void {
			var bitmapData:BitmapData = new BitmapData(outputWidth, outputHeight, true, 0x0);
			bitmapData.draw(loader.content, offsetMatrix);
			var bytearr:ByteArray = PNGEncoder.encode(bitmapData);
			var outfileName:String = outputDirPath + File.separator + prefix + separator + counter + ".png"
			var file:File = new File(outfileName);

			log("Writing: " + outfileName);
			var stream:FileStream = new FileStream();
			stream.open(file, "write");
			stream.writeBytes(bytearr);
			stream.close();
		}

		//Stops the movie clip and all its subclips.
		private function stopClip(inMc:MovieClip):void {
			var l:int = inMc.numChildren;
			for (var i:int = 0; i < l; i++)
			{
				var mc:MovieClip = inMc.getChildAt(i) as MovieClip;
				if(mc) {
					mc.stop();
					if(mc.numChildren > 0) {
						stopClip(mc);
					}
				}
			}
			inMc.stop();
		}

		//Traverses the movie clip and sets the current frame for all subclips too, looping them where needed.
		private function goToFrame(inMc:MovieClip, frameNo:int):void {
			var l:int = inMc.numChildren;
			for (var i:int = 0; i < l; i++)
			{
				var mc:MovieClip = inMc.getChildAt(i) as MovieClip;
				if(mc) {
					mc.gotoAndStop(frameNo % inMc.totalFrames);
					if(mc.numChildren > 0) {
						goToFrame(mc, frameNo);
					}
				}
			}
			inMc.gotoAndStop(frameNo % inMc.totalFrames);
		}

		//Finds and checks for existance of input file
		private function getInputFile(ev:InvokeEvent):String {
			if(ev.arguments && ev.arguments.length) {
				inputFileName = ev.arguments[0];
				var matches:Array = inputFileName.match(/([a-zA-Z0-9_\-\+\.]*?)\.swf$/);
				if(!matches) {
					// File inputFileName not valid
					exit(2);
					return "";
				}
				prefix = matches[1];
				log("Prefix: " + prefix);
				var f:File = new File(ev.currentDirectory.nativePath);
				f = f.resolvePath(inputFileName);
				if(!f.exists) {
					log("Input file not found!");
					//Input file not found
					exit(3);
					return "";
				}
				return f.nativePath;
			}
			else {
				//App opened without input data
				exit(1);
				return "";
			}
		}

		//Finds and checks for existance of output directory
		private function getOutputDir(ev:InvokeEvent):String {
			var d:File;
			if(ev.arguments.length > 1) {
				outputDirPath = ev.arguments[1];
				d = new File(ev.currentDirectory.nativePath);
				d = d.resolvePath(outputDirPath);
				if(!d.isDirectory) {
					//outdir not a directory
					exit(4);
					return "";
				}
				log("inpt: " + d.nativePath);
				return d.nativePath;
			}
			else {
				if(ev.currentDirectory.nativePath === '/') {
					if(ev.arguments.length) {
						d = new File(ev.arguments[0]);
						d = d.resolvePath('..');
						return d.nativePath;
					}
					else {
						return File.desktopDirectory.nativePath;
					}
				}
				else {
					log("cwd: " + ev.currentDirectory.nativePath);
					return ev.currentDirectory.nativePath;
				}
				return "";
			}
		}

		private function log(message:String="", add_new_line:Boolean=true):void {
			outfield.appendText((add_new_line ? "\n" : "") + message);
		}

		//Invoke handler called when started
		private function onInvoke(ev:InvokeEvent):void {

			inputFilePath = getInputFile(ev);
			outputDirPath = getOutputDir(ev);

			log("Input file: " + inputFilePath);
			log("Output directory: " + outputDirPath);
			loadSwf();
		}

		private function exit(code:int=0):void {
			NativeApplication.nativeApplication.exit(code);
		}
	}
}

package
{
	import com.adobe.images.PNGEncoder;

	import flash.desktop.NativeApplication;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.InvokeEvent;
	import flash.events.TimerEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileStream;
	import flash.geom.Rectangle;
	import flash.geom.Matrix;
	import flash.net.URLRequest;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	public class swf2png extends Sprite
	{
		public var outputWidth:int;
		public var outputHeight:int;
		public var loadedSwf:MovieClip;
		public var loader:Loader;
		private var counter:int = 0;
		private var timer:Timer;
		private var totalFrames:int;
		private var inputFileName:String;
		private var inputFilePath:String;
		private var prefix:String;
		private var separator:String = "_";
		private var outfield:TextField;
		private var outputDirPath:String;
		private var offsetMatrix:Matrix;
		private var bBox:Rectangle;

		public function swf2png() {
			NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, onInvoke);
			outfield = new TextField();
			outfield.autoSize = TextFieldAutoSize.LEFT;
			stage.addChild(outfield);
			stage.frameRate = 12;
		}

		//Loads in file
		private function loadSwf():void {

			loader = new Loader();
			log("Loading " + inputFilePath);
			loader.load(new URLRequest("file://" + inputFilePath));
			loader.contentLoaderInfo.addEventListener(Event.INIT, startLoop);

		}

		//Event handler called when the swf is loaded. Sets it up and starts the export loop
		private function startLoop(ev:Event):void {
			loadedSwf = MovieClip(ev.target.content);
			outputWidth = Math.ceil(ev.target.width);
			outputHeight = Math.ceil(ev.target.height);
			log("Loaded!");
			totalFrames = loadedSwf.totalFrames;
			log("Frame count: " + totalFrames);

			calculateBBox();

			stopClip(loadedSwf);
			goToFrame(loadedSwf, 0);
			timer = new Timer(1);

			timer.addEventListener(TimerEvent.TIMER, step);
			timer.start();

		}

		private function calculateBBox():void {
			stopClip(loadedSwf);
			goToFrame(loadedSwf, 0);

			var boundsArray:Object = {
				"x" : new Array(),
				"y" : new Array(),
				"w" : new Array(),
				"h" : new Array()
			}
			var r:Rectangle;
			for(var currentFrame:int = 0; currentFrame < totalFrames; currentFrame++) {
				goToFrame(loadedSwf, currentFrame);
				r = loader.content.getRect(stage);
				boundsArray.x.push(r.x);
				boundsArray.y.push(r.y);
				boundsArray.w.push(r.width);
				boundsArray.h.push(r.height);
			}

			boundsArray.x.sort(Array.NUMERIC);
			boundsArray.y.sort(Array.NUMERIC);
			boundsArray.w.sort(Array.NUMERIC | Array.DESCENDING);
			boundsArray.h.sort(Array.NUMERIC | Array.DESCENDING);

			bBox = new Rectangle(
				Math.floor(boundsArray.x[0]),
				Math.floor(boundsArray.y[0]),
				Math.ceil(boundsArray.w[0]),
				Math.ceil(boundsArray.h[0])
			);

			log("Bounding box: " + bBox)

			outputWidth = bBox.width;
			outputHeight = bBox.height;
			offsetMatrix = new Matrix();
			offsetMatrix.translate(bBox.x * -1, bBox.y * -1);

			return;
		}

		//Called for every frame
		private function step(ev:TimerEvent):void {
			counter++;
			if(counter <= totalFrames) {
				goToFrame(loadedSwf, counter);
				saveFrame();
			}
			else {
				timer.stop();
				log("Done!");
				exit(0);
				return;
			}
		}

		//Saves the current frame of the loader object to a png
		private function saveFrame():void {
			var bitmapData:BitmapData = new BitmapData(outputWidth, outputHeight, true, 0x0);
			bitmapData.draw(loader.content, offsetMatrix);
			var bytearr:ByteArray = PNGEncoder.encode(bitmapData);
			var increment:String = '';
			if(totalFrames > 1) {
				increment = separator + counter;
			}
			var outfileName:String = outputDirPath + File.separator + prefix + increment + ".png"
			var file:File = new File(outfileName);

			log("Writing: " + outfileName);
			var stream:FileStream = new FileStream();
			stream.open(file, "write");
			stream.writeBytes(bytearr);
			stream.close();
		}

		//Stops the movie clip and all its subclips.
		private function stopClip(inMc:MovieClip):void {
			var l:int = inMc.numChildren;
			for (var i:int = 0; i < l; i++)
			{
				var mc:MovieClip = inMc.getChildAt(i) as MovieClip;
				if(mc) {
					mc.stop();
					if(mc.numChildren > 0) {
						stopClip(mc);
					}
				}
			}
			inMc.stop();
		}

		//Traverses the movie clip and sets the current frame for all subclips too, looping them where needed.
		private function goToFrame(inMc:MovieClip, frameNo:int):void {
			var l:int = inMc.numChildren;
			for (var i:int = 0; i < l; i++)
			{
				var mc:MovieClip = inMc.getChildAt(i) as MovieClip;
				if(mc) {
					mc.gotoAndStop(frameNo % inMc.totalFrames);
					if(mc.numChildren > 0) {
						goToFrame(mc, frameNo);
					}
				}
			}
			inMc.gotoAndStop(frameNo % inMc.totalFrames);
		}

		//Finds and checks for existance of input file
		private function getInputFile(ev:InvokeEvent):String {
			if(ev.arguments && ev.arguments.length) {
				inputFileName = ev.arguments[0];
				var matches:Array = inputFileName.match(/([a-zA-Z0-9_\-\+\.]*?)\.swf$/);
				if(!matches) {
					// File inputFileName not valid
					exit(2);
					return "";
				}
				prefix = matches[1];
				log("Prefix: " + prefix);
				var f:File = new File(ev.currentDirectory.nativePath);
				f = f.resolvePath(inputFileName);
				if(!f.exists) {
					log("Input file not found!");
					//Input file not found
					exit(3);
					return "";
				}
				return f.nativePath;
			}
			else {
				//App opened without input data
				exit(1);
				return "";
			}
		}

		//Finds and checks for existance of output directory
		private function getOutputDir(ev:InvokeEvent):String {
			var d:File;
			if(ev.arguments.length > 1) {
				outputDirPath = ev.arguments[1];
				d = new File(ev.currentDirectory.nativePath);
				d = d.resolvePath(outputDirPath);
				if(!d.isDirectory) {
					//outdir not a directory
					exit(4);
					return "";
				}
				log("inpt: " + d.nativePath);
				return d.nativePath;
			}
			else {
				if(ev.currentDirectory.nativePath === '/') {
					if(ev.arguments.length) {
						d = new File(ev.arguments[0]);
						d = d.resolvePath('..');
						return d.nativePath;
					}
					else {
						return File.desktopDirectory.nativePath;
					}
				}
				else {
					log("cwd: " + ev.currentDirectory.nativePath);
					return ev.currentDirectory.nativePath;
				}
				return "";
			}
		}

		private function log(message:String="", add_new_line:Boolean=true):void {
			outfield.appendText((add_new_line ? "\n" : "") + message);
		}

		//Invoke handler called when started
		private function onInvoke(ev:InvokeEvent):void {

			inputFilePath = getInputFile(ev);
			outputDirPath = getOutputDir(ev);

			log("Input file: " + inputFilePath);
			log("Output directory: " + outputDirPath);
			loadSwf();
		}

		private function exit(code:int=0):void {
			NativeApplication.nativeApplication.exit(code);
		}
	}
}

Remove frame count when only one frame. Fixes #5.

The frame count suffix is only added when there's more than one frame in the swf.


package org.arisgames.editor.components
{
import com.ninem.controls.TreeBrowser;
import com.ninem.events.TreeBrowserEvent;

import flash.events.MouseEvent;

import mx.containers.Panel;
import mx.controls.Alert;
import mx.controls.Button;
import mx.controls.DataGrid;
import mx.core.ClassFactory;
import mx.events.DynamicEvent;
import mx.events.FlexEvent;
import mx.managers.PopUpManager;
import mx.rpc.Responder;

import org.arisgames.editor.data.arisserver.Media;
import org.arisgames.editor.data.businessobjects.ObjectPaletteItemBO;
import org.arisgames.editor.models.GameModel;
import org.arisgames.editor.services.AppServices;
import org.arisgames.editor.util.AppConstants;
import org.arisgames.editor.util.AppDynamicEventManager;
import org.arisgames.editor.util.AppUtils;

public class ItemEditorMediaPickerView extends Panel
{
    // Data Object
	public var delegate:Object;
    private var objectPaletteItem:ObjectPaletteItemBO;
    private var isIconPicker:Boolean = false;
	private var isNPC:Boolean = false;
	private var uploadFormVisable:Boolean = false;

    // Media Data
    [Bindable] public var xmlData:XML;

    // GUI
    [Bindable] public var treeBrowser:TreeBrowser;
    [Bindable] public var closeButton:Button;
	[Bindable] public var selectButton:Button;

	// Tree Icons
	[Bindable]
	[Embed(source="assets/img/separator.png")]
	public var separatorIcon:Class; //If these names are changed, make sure it is also changed in AppConstants (MEDIATREEICON_*)

	[Bindable]
	[Embed(source="assets/img/upload.png")]
	public var uploadIcon:Class; //If these names are changed, make sure it is also changed in AppConstants (MEDIATREEICON_*)

	private var mediaUploader:ItemEditorMediaPickerUploadFormMX;

    /**
     * Constructor
     */
    public function ItemEditorMediaPickerView()
    {
        super();
        this.addEventListener(FlexEvent.CREATION_COMPLETE, handleInit);
    }

    private function handleInit(event:FlexEvent):void
    {
        trace("in ItemEditorMediaPickerView's handleInit");
        var cf:ClassFactory = new ClassFactory(ItemEditorMediaPickerCustomEditorMX);
        cf.properties = {objectPaletteItem: this.objectPaletteItem};
        treeBrowser.detailRenderer = cf;
        treeBrowser.addEventListener(TreeBrowserEvent.NODE_SELECTED, onNodeSelected);
        closeButton.addEventListener(MouseEvent.CLICK, handleCloseButton);
		selectButton.addEventListener(MouseEvent.CLICK, handleSelectButton);
        // Load Game's Media Into XML
        AppServices.getInstance().getMediaForGame(GameModel.getInstance().game.gameId, new Responder(handleLoadingOfMediaIntoXML, handleFault));
    }

    private function printXMLData():void
    {
        trace("XML Data = '" + xmlData.toXMLString() + "'");
    }

    public function setObjectPaletteItem(opi:ObjectPaletteItemBO):void
    {
        trace("setting objectPaletteItem with name = '" + opi.name + "' in ItemEditorPlaqueView");
        objectPaletteItem = opi;
    }

    public function isInIconPickerMode():Boolean
    {
        return isIconPicker;
    }

    public function setIsIconPicker(isIconPickerMode:Boolean):void
    {
        trace("setting isIconPicker mode to: " + isIconPickerMode);
        this.isIconPicker = isIconPickerMode;
        this.updateTitleBasedOnMode();
    }

    private function updateTitleBasedOnMode():void
    {
        if (isIconPicker)
        {
            title = "Icon Picker";
        }
        else
        {
            title = "Media Picker";
        }
    }

    private function handleCloseButton(evt:MouseEvent):void
    {
        trace("ItemEditorMediaPickerView: handleCloseButton()");
		PopUpManager.removePopUp(this);
    }

	private function handleSelectButton(evt:MouseEvent):void
	{
		trace("Select Button clicked...");

		var m:Media = new Media();
		m.mediaId = treeBrowser.selectedItem.@mediaId;
		m.name = treeBrowser.selectedItem.@name;
		m.type = treeBrowser.selectedItem.@type;
		m.urlPath = treeBrowser.selectedItem.@urlPath;
		m.fileName = treeBrowser.selectedItem.@fileName;
		m.isDefault = treeBrowser.selectedItem.@isDefault;

		if (delegate.hasOwnProperty("didSelectMediaItem")){
			delegate.didSelectMediaItem(this, m);
		}

		PopUpManager.removePopUp(this);
	}

    private function handleLoadingOfMediaIntoXML(obj:Object):void
    {
		if(this.objectPaletteItem.objectType == AppConstants.CONTENTTYPE_CHARACTER_DATABASE){
			this.isNPC = true;
			trace("This is an NPC, so disallow Audio/Visual media");
		}

        trace("In handleLoadingOfMediaIntoXML() Result called with obj = " + obj + "; Result = " + obj.result);
        if (obj.result.returnCode != 0)
        {
            trace("Bad loading of media attempt... let's see what happened.  Error = '" + obj.result.returnCodeDescription + "'");
            var msg:String = obj.result.returnCodeDescription;
            Alert.show("Error Was: " + msg, "Error While Loading Media");
			return;
        }

        //Init the XML Data
		xmlData = new XML('<nodes label="' + AppConstants.MEDIATYPE + '"/>');

		if (!this.isIconPicker && !this.isNPC) {
			xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_IMAGE + '"/>');
			xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_AUDIO + '"/>');
			xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_VIDEO + '"/>');
		}

		this.printXMLData();

		var media:Array = obj.result.data as Array;
        trace("Number of Media Objects returned from server = '" + media.length + "'");

		//Puts media items in xml readable format
		var numImageDefaults:Number = 0;
		var numAudioDefaults:Number = 0;
		var numVideoDefaults:Number = 0;
		var numIconDefaults:Number = 0;
		for (var k:Number = 0; k <= 1; k++) //Iterates over list twice; once for uploaded items, again for default
		{
			for (var j:Number = 0; j < media.length; j++)
			{
				var o:Object = media[j];

				if((k==0 && !o.is_default) || (k==1 && o.is_default)){
					var m:Media = new Media();

					m.mediaId = o.media_id;
					m.name = o.name;
					m.type = o.type;
					m.urlPath = o.url_path;
					m.fileName = o.file_name;
					m.isDefault = o.is_default; //for both default files and uploaded files

					var node:String = "<node label='" + AppUtils.filterStringToXMLEscapeCharacters(m.name) + "' mediaId='" + m.mediaId + "' type='" + m.type + "' urlPath='" + m.urlPath + "' fileName='" + m.fileName + "' isDefault='" + m.isDefault + "'/>";

					switch (m.type)
					{
						case AppConstants.MEDIATYPE_IMAGE:
							if(!this.isNPC){
								if (!this.isIconPicker) {
									//Only add "-----------" (AppConstants.MEDIATYPE_SEPARATOR) if there are also defaults to choose from
									if(k > numImageDefaults) xmlData.node.(@label == AppConstants.MEDIATYPE_IMAGE).appendChild('<node label="' + AppConstants.MEDIATYPE_SEPARATOR + '" icon="'+ AppConstants.MEDIATREEICON_SEPARATOR +'"/>');
									xmlData.node.(@label == AppConstants.MEDIATYPE_IMAGE).appendChild(node);
									numImageDefaults+=k;
								}
							}
							else{
								if (!this.isIconPicker) {
									if(k > numIconDefaults) xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_SEPARATOR + '" icon="'+ AppConstants.MEDIATREEICON_SEPARATOR +'"/>');
									xmlData.appendChild(node);
									numIconDefaults+=k;
								}
							}
							break;
						case AppConstants.MEDIATYPE_AUDIO:
							if (!this.isIconPicker && !this.isNPC) {
								//Only add "-----------" (AppConstants.MEDIATYPE_SEPARATOR) if there are also defaults to choose from
								if(k > numAudioDefaults) xmlData.node.(@label == AppConstants.MEDIATYPE_AUDIO).appendChild('<node label="' + AppConstants.MEDIATYPE_SEPARATOR + '" icon="'+ AppConstants.MEDIATREEICON_SEPARATOR +'"/>');
								xmlData.node.(@label == AppConstants.MEDIATYPE_AUDIO).appendChild(node);
								numAudioDefaults+=k;
							}
							break;
						case AppConstants.MEDIATYPE_VIDEO:
							if (!this.isIconPicker && !this.isNPC) {
								//Only add "-----------" (AppConstants.MEDIATYPE_SEPARATOR) if there are also defaults to choose from
								if(k > numVideoDefaults) xmlData.node.(@label == AppConstants.MEDIATYPE_VIDEO).appendChild('<node label="' + AppConstants.MEDIATYPE_SEPARATOR + '" icon="'+ AppConstants.MEDIATREEICON_SEPARATOR +'"/>');
								xmlData.node.(@label == AppConstants.MEDIATYPE_VIDEO).appendChild(node);
								numVideoDefaults+=k;
							}
							break;
						case AppConstants.MEDIATYPE_ICON:
							if (this.isIconPicker) {
								trace("hello?:"+node);
								if(k > numIconDefaults) xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_SEPARATOR + '" icon="'+ AppConstants.MEDIATREEICON_SEPARATOR +'"/>');
								xmlData.appendChild(node);
								numIconDefaults+=k;
							}
							break;
						default:
							trace("Default statement reached in load media.  This SHOULD NOT HAPPEN.  The offending mediaId = '" + m.mediaId + "' and type = '" + m.type + "'");
							break;
					}
				}
			}

			//Add "Upload new" in between uploaded and default pictures
			if(k==0 && (!this.isIconPicker && !this.isNPC)){
				xmlData.node.(@label == AppConstants.MEDIATYPE_IMAGE).appendChild('<node label="' + AppConstants.MEDIATYPE_UPLOADNEW + '" icon="'+ AppConstants.MEDIATREEICON_UPLOAD +'"/>');
				xmlData.node.(@label == AppConstants.MEDIATYPE_AUDIO).appendChild('<node label="' + AppConstants.MEDIATYPE_UPLOADNEW + '" icon="'+ AppConstants.MEDIATREEICON_UPLOAD +'"/>');
				xmlData.node.(@label == AppConstants.MEDIATYPE_VIDEO).appendChild('<node label="' + AppConstants.MEDIATYPE_UPLOADNEW + '" icon="'+ AppConstants.MEDIATREEICON_UPLOAD +'"/>');
			}
			else if(k == 0){
				xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_UPLOADNEW + '" icon="'+ AppConstants.MEDIATREEICON_UPLOAD +'"/>');
			}
		}
        trace("ItemEditorMediaPickerView: handleLoadingOfMediaIntoXML: Just finished loading Media Objects into XML.  Here's what the new XML looks like:");
        this.printXMLData();

        trace("Finished with handleLoadingOfMediaIntoXML().");
    }

    public function handleFault(obj:Object):void
    {
        trace("Fault called: " + obj.message);
        Alert.show("Error occurred: " + obj.message, "Problems Loading Media");
    }

    private function onNodeSelected(event:TreeBrowserEvent):void
    {
        if (!event.isBranch)
        {
            if (event.item.@label == AppConstants.MEDIATYPE_UPLOADNEW)
            {
                if (uploadFormVisable == false) {
					trace("ItemEditorMediaPickerView: onNodeSelected label == MEDIATYPE_UPLOADNEW, display form");
					selectButton.enabled = false;
					uploadFormVisable = true;
	                this.displayMediaUploader();
					PopUpManager.removePopUp(this);
				}
				else trace("ItemEditorMediaPickerView: ignored second TreeBrowserEvent == MEDIATYPE_UPLOADNEW");

            }
			else if(event.item.@label == AppConstants.MEDIATYPE_SEPARATOR){
				//Do Nothing
				trace("Separator Selected");
				selectButton.enabled = false;
			}
            else
            {
                trace("ItemEditorMediaPickerView: onNodeSelected is a media item");
				selectButton.enabled = true;
            }
        }
		else {
			selectButton.enabled = false;
		}
    }

    private function displayMediaUploader():void
    {
        trace("itemEditorMediaPickerPickerView: displayMediaUploader");
        mediaUploader = new ItemEditorMediaPickerUploadFormMX();
		mediaUploader.isIconPicker = this.isIconPicker;
		mediaUploader.delegate = this;
        PopUpManager.addPopUp(mediaUploader, AppUtils.getInstance().getMainView(), true);
		PopUpManager.centerPopUp(mediaUploader);
    }

	public function didUploadMedia(uploader:ItemEditorMediaPickerUploadFormMX, m:Media):void{
		trace("ItemEditorMediaPicker: didUploadMedia");

		uploadFormVisable = false;

		if (delegate.hasOwnProperty("didSelectMediaItem")){
			delegate.didSelectMediaItem(this, m);
		}

		PopUpManager.removePopUp(uploader);
	}

}
}

package org.arisgames.editor.components
{
import com.ninem.controls.TreeBrowser;
import com.ninem.events.TreeBrowserEvent;

import flash.events.MouseEvent;

import mx.containers.Panel;
import mx.controls.Alert;
import mx.controls.Button;
import mx.controls.DataGrid;
import mx.core.ClassFactory;
import mx.events.DynamicEvent;
import mx.events.FlexEvent;
import mx.managers.PopUpManager;
import mx.rpc.Responder;

import org.arisgames.editor.data.arisserver.Media;
import org.arisgames.editor.data.businessobjects.ObjectPaletteItemBO;
import org.arisgames.editor.models.GameModel;
import org.arisgames.editor.services.AppServices;
import org.arisgames.editor.util.AppConstants;
import org.arisgames.editor.util.AppDynamicEventManager;
import org.arisgames.editor.util.AppUtils;

public class ItemEditorMediaPickerView extends Panel
{
    // Data Object
	public var delegate:Object;
    private var objectPaletteItem:ObjectPaletteItemBO;
    private var isIconPicker:Boolean = false;
	private var isNPC:Boolean = false;
	private var uploadFormVisable:Boolean = false;

    // Media Data
    [Bindable] public var xmlData:XML;

    // GUI
    [Bindable] public var treeBrowser:TreeBrowser;
    [Bindable] public var closeButton:Button;
	[Bindable] public var selectButton:Button;

	// Tree Icons
	[Bindable]
	[Embed(source="assets/img/separator.png")]
	public var separatorIcon:Class; //If these names are changed, make sure it is also changed in AppConstants (MEDIATREEICON_*)

	[Bindable]
	[Embed(source="assets/img/upload.png")]
	public var uploadIcon:Class; //If these names are changed, make sure it is also changed in AppConstants (MEDIATREEICON_*)

	private var mediaUploader:ItemEditorMediaPickerUploadFormMX;

    /**
     * Constructor
     */
    public function ItemEditorMediaPickerView()
    {
        super();
        this.addEventListener(FlexEvent.CREATION_COMPLETE, handleInit);
    }

    private function handleInit(event:FlexEvent):void
    {
        trace("in ItemEditorMediaPickerView's handleInit");
        var cf:ClassFactory = new ClassFactory(ItemEditorMediaPickerCustomEditorMX);
        cf.properties = {objectPaletteItem: this.objectPaletteItem};
        treeBrowser.detailRenderer = cf;
        treeBrowser.addEventListener(TreeBrowserEvent.NODE_SELECTED, onNodeSelected);
        closeButton.addEventListener(MouseEvent.CLICK, handleCloseButton);
		selectButton.addEventListener(MouseEvent.CLICK, handleSelectButton);
        // Load Game's Media Into XML
        AppServices.getInstance().getMediaForGame(GameModel.getInstance().game.gameId, new Responder(handleLoadingOfMediaIntoXML, handleFault));
    }

    private function printXMLData():void
    {
        trace("XML Data = '" + xmlData.toXMLString() + "'");
    }

    public function setObjectPaletteItem(opi:ObjectPaletteItemBO):void
    {
        trace("setting objectPaletteItem with name = '" + opi.name + "' in ItemEditorPlaqueView");
        objectPaletteItem = opi;
    }

    public function isInIconPickerMode():Boolean
    {
        return isIconPicker;
    }

    public function setIsIconPicker(isIconPickerMode:Boolean):void
    {
        trace("setting isIconPicker mode to: " + isIconPickerMode);
        this.isIconPicker = isIconPickerMode;
        this.updateTitleBasedOnMode();
    }

    private function updateTitleBasedOnMode():void
    {
        if (isIconPicker)
        {
            title = "Icon Picker";
        }
        else
        {
            title = "Media Picker";
        }
    }

    private function handleCloseButton(evt:MouseEvent):void
    {
        trace("ItemEditorMediaPickerView: handleCloseButton()");
		PopUpManager.removePopUp(this);
    }

	private function handleSelectButton(evt:MouseEvent):void
	{
		trace("Select Button clicked...");

		var m:Media = new Media();
		m.mediaId = treeBrowser.selectedItem.@mediaId;
		m.name = treeBrowser.selectedItem.@name;
		m.type = treeBrowser.selectedItem.@type;
		m.urlPath = treeBrowser.selectedItem.@urlPath;
		m.fileName = treeBrowser.selectedItem.@fileName;
		m.isDefault = treeBrowser.selectedItem.@isDefault;

		if (delegate.hasOwnProperty("didSelectMediaItem")){
			delegate.didSelectMediaItem(this, m);
		}

		PopUpManager.removePopUp(this);
	}

    private function handleLoadingOfMediaIntoXML(obj:Object):void
    {
		if(this.objectPaletteItem.objectType == AppConstants.CONTENTTYPE_CHARACTER_DATABASE){
			this.isNPC = true;
			trace("This is an NPC, so disallow Audio/Visual media");
		}

        trace("In handleLoadingOfMediaIntoXML() Result called with obj = " + obj + "; Result = " + obj.result);
        if (obj.result.returnCode != 0)
        {
            trace("Bad loading of media attempt... let's see what happened.  Error = '" + obj.result.returnCodeDescription + "'");
            var msg:String = obj.result.returnCodeDescription;
            Alert.show("Error Was: " + msg, "Error While Loading Media");
			return;
        }

        //Init the XML Data
		xmlData = new XML('<nodes label="' + AppConstants.MEDIATYPE + '"/>');

		if (!this.isIconPicker && !this.isNPC) {
			xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_IMAGE + '"/>');
			xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_AUDIO + '"/>');
			xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_VIDEO + '"/>');
		}

		this.printXMLData();

		var media:Array = obj.result.data as Array;
        trace("Number of Media Objects returned from server = '" + media.length + "'");

		//Puts media items in xml readable format
		var numImageDefaults:Number = 0;
		var numAudioDefaults:Number = 0;
		var numVideoDefaults:Number = 0;
		var numIconDefaults:Number = 0;
		for (var k:Number = 0; k <= 1; k++) //Iterates over list twice; once for uploaded items, again for default
		{
			//Add "Upload new" in between uploaded and default pictures
			if(k==0 && (!this.isIconPicker && !this.isNPC)){
				xmlData.node.(@label == AppConstants.MEDIATYPE_IMAGE).appendChild('<node label="' + AppConstants.MEDIATYPE_UPLOADNEW + '" icon="'+ AppConstants.MEDIATREEICON_UPLOAD +'"/>');
				xmlData.node.(@label == AppConstants.MEDIATYPE_AUDIO).appendChild('<node label="' + AppConstants.MEDIATYPE_UPLOADNEW + '" icon="'+ AppConstants.MEDIATREEICON_UPLOAD +'"/>');
				xmlData.node.(@label == AppConstants.MEDIATYPE_VIDEO).appendChild('<node label="' + AppConstants.MEDIATYPE_UPLOADNEW + '" icon="'+ AppConstants.MEDIATREEICON_UPLOAD +'"/>');
			}
			else if(k == 0){
				xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_UPLOADNEW + '" icon="'+ AppConstants.MEDIATREEICON_UPLOAD +'"/>');
			}
			for (var j:Number = 0; j < media.length; j++)
			{
				var o:Object = media[j];

				if((k==0 && !o.is_default) || (k==1 && o.is_default)){
					var m:Media = new Media();

					m.mediaId = o.media_id;
					m.name = o.name;
					m.type = o.type;
					m.urlPath = o.url_path;
					m.fileName = o.file_name;
					m.isDefault = o.is_default; //for both default files and uploaded files

					var node:String = "<node label='" + AppUtils.filterStringToXMLEscapeCharacters(m.name) + "' mediaId='" + m.mediaId + "' type='" + m.type + "' urlPath='" + m.urlPath + "' fileName='" + m.fileName + "' isDefault='" + m.isDefault + "'/>";

					switch (m.type)
					{
						case AppConstants.MEDIATYPE_IMAGE:
							if(!this.isNPC){
								if (!this.isIconPicker) {
									//Only add "-----------" (AppConstants.MEDIATYPE_SEPARATOR) if there are also defaults to choose from
									if(k > numImageDefaults) xmlData.node.(@label == AppConstants.MEDIATYPE_IMAGE).appendChild('<node label="' + AppConstants.MEDIATYPE_SEPARATOR + '" icon="'+ AppConstants.MEDIATREEICON_SEPARATOR +'"/>');
									xmlData.node.(@label == AppConstants.MEDIATYPE_IMAGE).appendChild(node);
									numImageDefaults+=k;
								}
							}
							else{
								if (!this.isIconPicker) {
									if(k > numIconDefaults) xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_SEPARATOR + '" icon="'+ AppConstants.MEDIATREEICON_SEPARATOR +'"/>');
									xmlData.appendChild(node);
									numIconDefaults+=k;
								}
							}
							break;
						case AppConstants.MEDIATYPE_AUDIO:
							if (!this.isIconPicker && !this.isNPC) {
								//Only add "-----------" (AppConstants.MEDIATYPE_SEPARATOR) if there are also defaults to choose from
								if(k > numAudioDefaults) xmlData.node.(@label == AppConstants.MEDIATYPE_AUDIO).appendChild('<node label="' + AppConstants.MEDIATYPE_SEPARATOR + '" icon="'+ AppConstants.MEDIATREEICON_SEPARATOR +'"/>');
								xmlData.node.(@label == AppConstants.MEDIATYPE_AUDIO).appendChild(node);
								numAudioDefaults+=k;
							}
							break;
						case AppConstants.MEDIATYPE_VIDEO:
							if (!this.isIconPicker && !this.isNPC) {
								//Only add "-----------" (AppConstants.MEDIATYPE_SEPARATOR) if there are also defaults to choose from
								if(k > numVideoDefaults) xmlData.node.(@label == AppConstants.MEDIATYPE_VIDEO).appendChild('<node label="' + AppConstants.MEDIATYPE_SEPARATOR + '" icon="'+ AppConstants.MEDIATREEICON_SEPARATOR +'"/>');
								xmlData.node.(@label == AppConstants.MEDIATYPE_VIDEO).appendChild(node);
								numVideoDefaults+=k;
							}
							break;
						case AppConstants.MEDIATYPE_ICON:
							if (this.isIconPicker) {
								trace("hello?:"+node);
								if(k > numIconDefaults) xmlData.appendChild('<node label="' + AppConstants.MEDIATYPE_SEPARATOR + '" icon="'+ AppConstants.MEDIATREEICON_SEPARATOR +'"/>');
								xmlData.appendChild(node);
								numIconDefaults+=k;
							}
							break;
						default:
							trace("Default statement reached in load media.  This SHOULD NOT HAPPEN.  The offending mediaId = '" + m.mediaId + "' and type = '" + m.type + "'");
							break;
					}
				}
			}
		}
        trace("ItemEditorMediaPickerView: handleLoadingOfMediaIntoXML: Just finished loading Media Objects into XML.  Here's what the new XML looks like:");
        this.printXMLData();

        trace("Finished with handleLoadingOfMediaIntoXML().");
    }

    public function handleFault(obj:Object):void
    {
        trace("Fault called: " + obj.message);
        Alert.show("Error occurred: " + obj.message, "Problems Loading Media");
    }

    private function onNodeSelected(event:TreeBrowserEvent):void
    {
        if (!event.isBranch)
        {
            if (event.item.@label == AppConstants.MEDIATYPE_UPLOADNEW)
            {
                if (uploadFormVisable == false) {
					trace("ItemEditorMediaPickerView: onNodeSelected label == MEDIATYPE_UPLOADNEW, display form");
					selectButton.enabled = false;
					uploadFormVisable = true;
	                this.displayMediaUploader();
					PopUpManager.removePopUp(this);
				}
				else trace("ItemEditorMediaPickerView: ignored second TreeBrowserEvent == MEDIATYPE_UPLOADNEW");

            }
			else if(event.item.@label == AppConstants.MEDIATYPE_SEPARATOR){
				//Do Nothing
				trace("Separator Selected");
				selectButton.enabled = false;
			}
            else
            {
                trace("ItemEditorMediaPickerView: onNodeSelected is a media item");
				selectButton.enabled = true;
            }
        }
		else {
			selectButton.enabled = false;
		}
    }

    private function displayMediaUploader():void
    {
        trace("itemEditorMediaPickerPickerView: displayMediaUploader");
        mediaUploader = new ItemEditorMediaPickerUploadFormMX();
		mediaUploader.isIconPicker = this.isIconPicker;
		mediaUploader.delegate = this;
        PopUpManager.addPopUp(mediaUploader, AppUtils.getInstance().getMainView(), true);
		PopUpManager.centerPopUp(mediaUploader);
    }

	public function didUploadMedia(uploader:ItemEditorMediaPickerUploadFormMX, m:Media):void{
		trace("ItemEditorMediaPicker: didUploadMedia");

		uploadFormVisable = false;

		if (delegate.hasOwnProperty("didSelectMediaItem")){
			delegate.didSelectMediaItem(this, m);
		}

		PopUpManager.removePopUp(uploader);
	}

}
}

Put Upload New button at top of media picker list