package com
{
	import com.errors.RuntimeError;
	import com.utils.URLStreamProgress;

	import flash.events.DataEvent;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.FileReference;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.net.URLRequestMethod;
	import flash.net.URLStream;
	import flash.net.URLVariables;
	import flash.utils.ByteArray;
	import flash.utils.clearTimeout;
	import flash.utils.setTimeout;

	import mxi.Utils;
	import mxi.events.OErrorEvent;
	import mxi.events.OProgressEvent;

	public class XMLHttpRequest extends EventDispatcher
	{
		// events dispatched by this class
		public static var dispatches:Object = {
			"LoadStart": Event.OPEN,
			"Progress": OProgressEvent.PROGRESS,
			"UploadProgress": ProgressEvent.PROGRESS,
			"Load": Event.COMPLETE,
			"Error": OErrorEvent.ERROR
		};

		public static const UNSENT:uint = 0;
		public static const OPENED:uint = 1;
		public static const HEADERS_RECEIVED:uint = 2;
		public static const LOADING:uint = 3;
		public static const DONE:uint = 4;
		public static const ABORTED:uint = 5;

		private var _methods:Object = {
			'GET': URLRequestMethod.GET,
			'POST': URLRequestMethod.POST
		};

		private var _options:Object;

		private var _multipart:Boolean = false;

		private var _blob:*;

		private var _blobName:String;

		private var _blobFieldName:String = 'Filedata';

		private var _postData:URLVariables = new URLVariables;

		private var _conn:*;

		private var _response:ByteArray;

		private var _status:int;

		private var _readyState:uint = XMLHttpRequest.UNSENT;

		private var _onCompleteTimeout:uint = 0;

		private var _notCachedSize:uint = 0;

		public function append(name:String, value:*) : void
		{
			_multipart = true;

			if (name == 'Filename') { // Flash will add this by itself, so we need to omit potential duplicate
				return;
			}

			if (/\[\]$/.test(name)) { // handle arrays
				if (!_postData.hasOwnProperty(name)) {
					_postData[name] = [];
				}
				_postData[name].push(value);
			} else {
				_postData[name] = value;
			}
		}

		public function appendBlob(name:String, blob:*, fileName:String = null) : void
		{
			_multipart = true;

			_blobName = fileName || 'blob' + new Date().getTime();

			if (typeof blob === 'string') {
				blob = Moxie.compFactory.get(blob);
				if (!fileName && blob is File && blob.hasOwnProperty('name')) {
					_blobName = blob.name;
				}
			}

			_blob = blob;
			_blobFieldName = name;
		}

		public function send(meta:Object, blob:* = null) : void
		{
			if (_blob) {
				blob = _blob;
			}

			meta.method = meta.method.toUpperCase();
			_options = meta;

			if (typeof blob === 'string') {
				blob = Moxie.compFactory.get(blob);
				if (blob is File && blob.hasOwnProperty('name')) {
					_blobName = blob.name;
				}
			}

			if (blob && _options.method == 'POST') {
				if (_options.transport == 'client' && _multipart && blob.isFileRef() && Utils.isEmptyObj(_options.headers)) {
					_uploadFileRef(blob);
				} else {
					_preloadBlob(blob, _doURLStreamRequest);
				}
			} else {
				_doURLStreamRequest();
			}
		}

		public function getResponseAsBlob() : Object
		{
			var blob:Blob;

			if (!_response) {
				return null;
			}

			blob = new File([_response], { name: _options.url.replace(/^.+?\/([\w\-\.]+)$/, '$1').toLowerCase() });
			Moxie.compFactory.add(blob.uid, blob);
			return blob.toObject();
		}

		public function getStatus() : int
		{
			return _status;
		}

		public function abort() : void
		{
			if (!_conn) {
				// mark as aborted in order to be handled as soon as possible
				_readyState = XMLHttpRequest.ABORTED;
				return;
			}

			if (_conn is FileReference) {
				_conn.cancel();
			} else if (_conn is URLStream && _conn.connected) {
				_conn.close();
			}

			removeEventListeners(_conn);
			_conn = null;
		}

		private function onOpen() : void {
			dispatchEvent(new Event(Event.OPEN));
		}

		private function onProgress(e:ProgressEvent) : void {
			dispatchEvent(new OProgressEvent(OProgressEvent.PROGRESS, e.bytesLoaded, e.bytesTotal));
		}

		private function onUploadProgress(e:ProgressEvent):void {
			dispatchEvent(e);
		}

		private function onUploadComplete(e:*) : void {
			if (_onCompleteTimeout) {
				clearTimeout(_onCompleteTimeout);
			}

			// if status still equal to zero, than we are ok
			if (_status == 0) {
				_status = 200;
			}

			// save response
			_response = new ByteArray;

			if (_conn is FileReference && e.hasOwnProperty('data')) {
				_response.writeUTFBytes(e.data);
			} else if (_conn is URLStream) {
				_conn.readBytes(_response);
			}

			removeEventListeners(e.target);
			_readyState = XMLHttpRequest.DONE;
			dispatchEvent(new Event(Event.COMPLETE));
		}

		private function onComplete(e:*) : void {
			// give upload complete event a chance to fire
			_onCompleteTimeout = setTimeout(onUploadComplete, 500, e);
		}

		// The httpStatus event is dispatched only for upload failures.
		private function onStatus(e:HTTPStatusEvent) : void {
			_status = e.status;
		}

		private function onIOError(e:IOErrorEvent) : void {
			if (_status == 0) { // httpStatus might have already set status to some failure code (according to livedocs)
				_status = 404; // assume that request succeeded, but url was wrong
			}
			onUploadComplete(e);
		}

		private function onError(e:*) : void {
			removeEventListeners(e.target);
			_readyState = XMLHttpRequest.DONE;
			dispatchEvent(new OErrorEvent(OErrorEvent.ERROR));
		}

		private function removeEventListeners(target:*) : void {
			// some of these event handlers may not even be attached, but we try to remove them all for simplicity
			target.removeEventListener(Event.OPEN, onOpen);
			target.removeEventListener(ProgressEvent.PROGRESS, onProgress);
			target.removeEventListener(ProgressEvent.PROGRESS, onUploadProgress);
			target.removeEventListener(Event.COMPLETE, onComplete);
			target.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadComplete);
			target.removeEventListener(HTTPStatusEvent.HTTP_STATUS, onStatus);
			target.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
			target.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
		}

		private function _uploadFileRef(blob:Blob) : void
		{
			var request:URLRequest;

			request = new URLRequest();
			request.method = URLRequestMethod.POST;
			request.url = _options.url;
			request.data = _postData;

			_conn = blob.getFileRef();
			_readyState = XMLHttpRequest.OPENED;

			_conn.addEventListener(Event.COMPLETE, onComplete);
			_conn.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadComplete);
			_conn.addEventListener(ProgressEvent.PROGRESS, onUploadProgress);
			_conn.addEventListener(HTTPStatusEvent.HTTP_STATUS, onStatus);
			_conn.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			_conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);

			// _conn.addEventListener(Event.OPEN, onOpen); doesn't fire, ideas?
			onOpen(); // trigger it manually

			_readyState = XMLHttpRequest.LOADING;
			_conn.upload(request, _blobFieldName, false);
		}

		private function _preloadBlob(blob:*, callback:Function) : void
		{
			var fr:FileReader = new FileReader;

			fr.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent) : void {
				if (_readyState == XMLHttpRequest.ABORTED) {
					fr.abort();
				}
			});

			fr.addEventListener(Event.COMPLETE, function() : void {
				fr.removeAllEventsListeners();
				if (_readyState == XMLHttpRequest.ABORTED) {
					fr.abort();
					return;
				}
				callback(fr.result);
			});

			//_notCachedSize = blob.realSize - blob.cachedSize;

			fr.readAsByteArray(blob);
		}

		private function _doURLStreamRequest(ba:ByteArray = null) : void
		{
			var request:URLRequest,
			progress:URLStreamProgress,
			start:Date, end:Date; // we are going to measure upload time for each piece of data and make correction to the progress watch

			request = new URLRequest(_options.url);

			// set custom headers if required
			if (!Utils.isEmptyObj(_options.headers)) {
				for (var name:String in _options.headers) {
					request.requestHeaders.push(new URLRequestHeader(name, _options.headers[name]));
				}
			}

			// only GET/POST is supported at the moment
			if (['POST', 'GET'].indexOf(_options.method) === -1) {
				dispatchEvent(new OErrorEvent(OErrorEvent.ERROR, RuntimeError.SYNTAX_ERR));
				return;
			}

			request.method = _methods[_options.method];

			if (_multipart) {
				request.data = _formatAsMultipart(ba, request);
			} else if (ba) {
				request.data = ba;
			}

			_conn = new URLStream;
			_readyState = XMLHttpRequest.OPENED;

			// _conn.addEventListener(Event.OPEN, onOpen); // doesn't trigger (I remember it did), maybe adobe abandoned it?..
			_conn.addEventListener(ProgressEvent.PROGRESS, onProgress);
			_conn.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			_conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			_conn.addEventListener(HTTPStatusEvent.HTTP_STATUS, onStatus);

			if (ba) {
				progress = new URLStreamProgress({ url: _options.url });

				progress.addEventListener(ProgressEvent.PROGRESS, onUploadProgress);

				progress.addEventListener(Event.OPEN, function() : void {
					start = new Date;

					onOpen(); // trigger it manually
					_conn.load(request);
				});

				_conn.addEventListener(Event.COMPLETE, function(e:Event) : void {
					progress.stop();
					progress.removeAllEventsListeners();

					// correlate upload speed
					end = new Date;
					URLStreamProgress.calculateSpeed(request.data.length, end.time - start.time);

					onComplete(e);
				});

				progress.start(request.data.length);
			}
			else {
				_conn.addEventListener(Event.COMPLETE, onComplete);

				onOpen(); // trigger it manually
				_conn.load(request);
			}

			_readyState = XMLHttpRequest.LOADING;
		}

		private function _formatAsMultipart(ba:ByteArray, request:URLRequest) : ByteArray
		{
			var boundary:String = '----moxieboundary' + new Date().getTime(),
				dashdash:String = '--', crlf:String = '\r\n',
				tmpBa:ByteArray;

			tmpBa = new ByteArray;

			request.requestHeaders.push(new URLRequestHeader("Content-Type", 'multipart/form-data; boundary=' + boundary));

			// recursively append mutltipart parameters
			(function eachPostData(key:String, postData:*) : void {
				for (var name:String in postData) {
					if (postData[name] is Array) {
						eachPostData(name, postData[name]); // name will be common for all elements of the array
					} else {
						tmpBa.writeUTFBytes(
							dashdash + boundary + crlf +
							'Content-Disposition: form-data; name="' + (key || name) + '"' + crlf + crlf +
							postData[name] + crlf
						);
					}
				}
			}(null, _postData)); // self-invoke with global _postData object

			// append file if available
			if (ba) {
				tmpBa.writeUTFBytes(
					dashdash + boundary + crlf +
					'Content-Disposition: form-data; name="' + _blobFieldName + '"; filename="' + _blobName + '"' + crlf +
					'Content-Type: ' + _options.mimeType + crlf + crlf
				);

				tmpBa.writeBytes(ba, 0, ba.length);
				ba.clear();

				tmpBa.writeUTFBytes(crlf);
			}

			// wrap it up
			tmpBa.writeUTFBytes(dashdash + boundary + dashdash + crlf);

			return tmpBa;
		}

	}
}

package com
{
	import com.errors.RuntimeError;
	import com.utils.URLStreamProgress;

	import flash.events.DataEvent;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.FileReference;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.net.URLRequestMethod;
	import flash.net.URLStream;
	import flash.utils.ByteArray;
	import flash.utils.clearTimeout;
	import flash.utils.setTimeout;

	import mxi.Utils;
	import mxi.events.OErrorEvent;
	import mxi.events.OProgressEvent;

	public class XMLHttpRequest extends EventDispatcher
	{
		// events dispatched by this class
		public static var dispatches:Object = {
			"LoadStart": Event.OPEN,
			"Progress": OProgressEvent.PROGRESS,
			"UploadProgress": ProgressEvent.PROGRESS,
			"Load": Event.COMPLETE,
			"Error": OErrorEvent.ERROR
		};

		public static const UNSENT:uint = 0;
		public static const OPENED:uint = 1;
		public static const HEADERS_RECEIVED:uint = 2;
		public static const LOADING:uint = 3;
		public static const DONE:uint = 4;
		public static const ABORTED:uint = 5;

		private var _methods:Object = {
			'GET': URLRequestMethod.GET,
			'POST': URLRequestMethod.POST
		};

		private var _options:Object;

		private var _multipart:Boolean = false;

		private var _blob:*;

		private var _blobName:String;

		private var _blobFieldName:String = 'Filedata';

		private var _postData:Array = [];

		private var _conn:*;

		private var _response:ByteArray;

		private var _status:int;

		private var _readyState:uint = XMLHttpRequest.UNSENT;

		private var _onCompleteTimeout:uint = 0;

		private var _notCachedSize:uint = 0;

		public function append(name:String, value:*) : void
		{
			_multipart = true;

			if (name == 'Filename') { // Flash will add this by itself, so we need to omit potential duplicate
				return;
			}

			_postData.push([name, value]);
		}

		public function appendBlob(name:String, blob:*, fileName:String = null) : void
		{
			_multipart = true;

			_blobName = fileName || 'blob' + new Date().getTime();

			if (typeof blob === 'string') {
				blob = Moxie.compFactory.get(blob);
				if (!fileName && blob is File && blob.hasOwnProperty('name')) {
					_blobName = blob.name;
				}
			}

			_blob = blob;
			_blobFieldName = name;
		}

		public function send(meta:Object, blob:* = null) : void
		{
			if (_blob) {
				blob = _blob;
			}

			meta.method = meta.method.toUpperCase();
			_options = meta;

			if (typeof blob === 'string') {
				blob = Moxie.compFactory.get(blob);
				if (blob is File && blob.hasOwnProperty('name')) {
					_blobName = blob.name;
				}
			}

			if (blob && _options.method == 'POST') {
				if (_options.transport == 'client' && _multipart && blob.isFileRef() && Utils.isEmptyObj(_options.headers)) {
					_uploadFileRef(blob);
				} else {
					_preloadBlob(blob, _doURLStreamRequest);
				}
			} else {
				_doURLStreamRequest();
			}
		}

		public function getResponseAsBlob() : Object
		{
			var blob:Blob;

			if (!_response) {
				return null;
			}

			blob = new File([_response], { name: _options.url.replace(/^.+?\/([\w\-\.]+)$/, '$1').toLowerCase() });
			Moxie.compFactory.add(blob.uid, blob);
			return blob.toObject();
		}

		public function getStatus() : int
		{
			return _status;
		}

		public function abort() : void
		{
			if (!_conn) {
				// mark as aborted in order to be handled as soon as possible
				_readyState = XMLHttpRequest.ABORTED;
				return;
			}

			if (_conn is FileReference) {
				_conn.cancel();
			} else if (_conn is URLStream && _conn.connected) {
				_conn.close();
			}

			removeEventListeners(_conn);
			_conn = null;
		}

		private function onOpen() : void {
			dispatchEvent(new Event(Event.OPEN));
		}

		private function onProgress(e:ProgressEvent) : void {
			dispatchEvent(new OProgressEvent(OProgressEvent.PROGRESS, e.bytesLoaded, e.bytesTotal));
		}

		private function onUploadProgress(e:ProgressEvent):void {
			dispatchEvent(e);
		}

		private function onUploadComplete(e:*) : void {
			if (_onCompleteTimeout) {
				clearTimeout(_onCompleteTimeout);
			}

			// if status still equal to zero, than we are ok
			if (_status == 0) {
				_status = 200;
			}

			// save response
			_response = new ByteArray;

			if (_conn is FileReference && e.hasOwnProperty('data')) {
				_response.writeUTFBytes(e.data);
			} else if (_conn is URLStream) {
				_conn.readBytes(_response);
			}

			removeEventListeners(e.target);
			_readyState = XMLHttpRequest.DONE;
			dispatchEvent(new Event(Event.COMPLETE));
		}

		private function onComplete(e:*) : void {
			// give upload complete event a chance to fire
			_onCompleteTimeout = setTimeout(onUploadComplete, 500, e);
		}

		// The httpStatus event is dispatched only for upload failures.
		private function onStatus(e:HTTPStatusEvent) : void {
			_status = e.status;
		}

		private function onIOError(e:IOErrorEvent) : void {
			if (_status == 0) { // httpStatus might have already set status to some failure code (according to livedocs)
				_status = 404; // assume that request succeeded, but url was wrong
			}
			onUploadComplete(e);
		}

		private function onError(e:*) : void {
			removeEventListeners(e.target);
			_readyState = XMLHttpRequest.DONE;
			dispatchEvent(new OErrorEvent(OErrorEvent.ERROR));
		}

		private function removeEventListeners(target:*) : void {
			// some of these event handlers may not even be attached, but we try to remove them all for simplicity
			target.removeEventListener(Event.OPEN, onOpen);
			target.removeEventListener(ProgressEvent.PROGRESS, onProgress);
			target.removeEventListener(ProgressEvent.PROGRESS, onUploadProgress);
			target.removeEventListener(Event.COMPLETE, onComplete);
			target.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadComplete);
			target.removeEventListener(HTTPStatusEvent.HTTP_STATUS, onStatus);
			target.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
			target.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
		}

		private function _uploadFileRef(blob:Blob) : void
		{
			var request:URLRequest;

			request = new URLRequest();
			request.method = URLRequestMethod.POST;
			request.url = _options.url;
			var qs:Array = [];
			for (var i:int=0; i < _postData.length; i++) {
				qs.push(escape(_postData[i][0]) + '=' +  escape(_postData[i][1]));
			}
			request.data = qs.join('&')

			_conn = blob.getFileRef();
			_readyState = XMLHttpRequest.OPENED;

			_conn.addEventListener(Event.COMPLETE, onComplete);
			_conn.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, onUploadComplete);
			_conn.addEventListener(ProgressEvent.PROGRESS, onUploadProgress);
			_conn.addEventListener(HTTPStatusEvent.HTTP_STATUS, onStatus);
			_conn.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			_conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);

			// _conn.addEventListener(Event.OPEN, onOpen); doesn't fire, ideas?
			onOpen(); // trigger it manually

			_readyState = XMLHttpRequest.LOADING;
			_conn.upload(request, _blobFieldName, false);
		}

		private function _preloadBlob(blob:*, callback:Function) : void
		{
			var fr:FileReader = new FileReader;

			fr.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent) : void {
				if (_readyState == XMLHttpRequest.ABORTED) {
					fr.abort();
				}
			});

			fr.addEventListener(Event.COMPLETE, function() : void {
				fr.removeAllEventsListeners();
				if (_readyState == XMLHttpRequest.ABORTED) {
					fr.abort();
					return;
				}
				callback(fr.result);
			});

			//_notCachedSize = blob.realSize - blob.cachedSize;

			fr.readAsByteArray(blob);
		}

		private function _doURLStreamRequest(ba:ByteArray = null) : void
		{
			var request:URLRequest,
			progress:URLStreamProgress,
			start:Date, end:Date; // we are going to measure upload time for each piece of data and make correction to the progress watch

			request = new URLRequest(_options.url);

			// set custom headers if required
			if (!Utils.isEmptyObj(_options.headers)) {
				for (var name:String in _options.headers) {
					request.requestHeaders.push(new URLRequestHeader(name, _options.headers[name]));
				}
			}

			// only GET/POST is supported at the moment
			if (['POST', 'GET'].indexOf(_options.method) === -1) {
				dispatchEvent(new OErrorEvent(OErrorEvent.ERROR, RuntimeError.SYNTAX_ERR));
				return;
			}

			request.method = _methods[_options.method];

			if (_multipart) {
				request.data = _formatAsMultipart(ba, request);
			} else if (ba) {
				request.data = ba;
			}

			_conn = new URLStream;
			_readyState = XMLHttpRequest.OPENED;

			// _conn.addEventListener(Event.OPEN, onOpen); // doesn't trigger (I remember it did), maybe adobe abandoned it?..
			_conn.addEventListener(ProgressEvent.PROGRESS, onProgress);
			_conn.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			_conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			_conn.addEventListener(HTTPStatusEvent.HTTP_STATUS, onStatus);

			if (ba) {
				progress = new URLStreamProgress({ url: _options.url });

				progress.addEventListener(ProgressEvent.PROGRESS, onUploadProgress);

				progress.addEventListener(Event.OPEN, function() : void {
					start = new Date;

					onOpen(); // trigger it manually
					_conn.load(request);
				});

				_conn.addEventListener(Event.COMPLETE, function(e:Event) : void {
					progress.stop();
					progress.removeAllEventsListeners();

					// correlate upload speed
					end = new Date;
					URLStreamProgress.calculateSpeed(request.data.length, end.time - start.time);

					onComplete(e);
				});

				progress.start(request.data.length);
			}
			else {
				_conn.addEventListener(Event.COMPLETE, onComplete);

				onOpen(); // trigger it manually
				_conn.load(request);
			}

			_readyState = XMLHttpRequest.LOADING;
		}

		private function _formatAsMultipart(ba:ByteArray, request:URLRequest) : ByteArray
		{
			var boundary:String = '----moxieboundary' + new Date().getTime(),
				dashdash:String = '--', crlf:String = '\r\n',
				tmpBa:ByteArray;

			tmpBa = new ByteArray;

			request.requestHeaders.push(new URLRequestHeader("Content-Type", 'multipart/form-data; boundary=' + boundary));

			for (var i:int=0; i < _postData.length; i++) {
				var name:String = _postData[i][0];
				var value:String = _postData[i][1];
				tmpBa.writeUTFBytes(
					dashdash + boundary + crlf +
					'Content-Disposition: form-data; name="' + name + '"' + crlf + crlf +
					value + crlf
				);
			}

			// append file if available
			if (ba) {
				tmpBa.writeUTFBytes(
					dashdash + boundary + crlf +
					'Content-Disposition: form-data; name="' + _blobFieldName + '"; filename="' + _blobName + '"' + crlf +
					'Content-Type: ' + _options.mimeType + crlf + crlf
				);

				tmpBa.writeBytes(ba, 0, ba.length);
				ba.clear();

				tmpBa.writeUTFBytes(crlf);
			}

			// wrap it up
			tmpBa.writeUTFBytes(dashdash + boundary + dashdash + crlf);

			return tmpBa;
		}

	}
}

Stop using UrlVariables since it is un-ordered.

UrlVariables will output the params in random order. 99% of the time this
doesn't matter but occasionally it does. This removes the use of UrlVariables.
It is now just a simple list of params and when doing a UrlRequest I encode it
myself.

This has the side benefit of getting rid of the recursive array logic. Params
are not just flat and if someone want to encode an array they can do so without
us caring since we don't have to worry about keys overwritting each other.


class com Preloader extends MovieClip{

	private var target_mc:MovieClip;
        private var progress_mc:MovieClip;
        private var pct_str:String;
        private var dispatchEvent:Function;
        public var addEventListener:Function;
        public var removeEventListener:Function;

        function Preloader()
	{
        mx.events.EventDispatcher.initialize(this);
        }

	public function startPreload(t:MovieClip)
	{
	target_mc = t;
        this.gotoAndPlay("IN");
	}

	private function onAnimateIn()
	{
	this.stop();
        this.onEnterFrame = this.checkLoadProgress;
	}

	private function checkLoadProgress()
	{
	var bl = target_mc.getBytesLoaded();
        var bt = target_mc.getBytesTotal();
        var pct = bl / bt;
        var cf = progress_mc._currentframe;
        var tf = progress_mc._totalframes;
        var f = Math.ceil(tf * pct);
		if (f > cf)
		{
		progress_mc.play();
		}
		else
		{
		progress_mc.stop();
		}

		this.pct_str = (Math.round(cf / tf * 100)).toString();

        if (bt > 20 && bl == bt && cf == tf && progress_mc)
		{
		onPreloadComplete();
		}
	}

	private function onPreloadComplete()
	{
	this.onEnterFrame = null;
        this.gotoAndPlay("OUT");
	}

	private function onAnimateOut()
	{
	this.stop();
        dispatchEvent({target:this, type:'onPreloaderOut'});
	}
}

class Preloader extends MovieClip{

	private var target_mc:MovieClip;
        private var progress_mc:MovieClip;
        private var pct_str:String;
        private var dispatchEvent:Function;
        public var addEventListener:Function;
        public var removeEventListener:Function;

        function Preloader()
	{
        mx.events.EventDispatcher.initialize(this);
        }

	public function startPreload(t:MovieClip)
	{
	target_mc = t;
        this.gotoAndPlay("IN");
	}

	private function onAnimateIn()
	{
	this.stop();
        this.onEnterFrame = this.checkLoadProgress;
	}

	private function checkLoadProgress()
	{
	var bl = target_mc.getBytesLoaded();
        var bt = target_mc.getBytesTotal();
        var pct = bl / bt;
        var cf = progress_mc._currentframe;
        var tf = progress_mc._totalframes;
        var f = Math.ceil(tf * pct);
		if (f > cf)
		{
		progress_mc.play();
		}
		else
		{
		progress_mc.stop();
		}

		this.pct_str = (Math.round(cf / tf * 100)).toString();

        if (bt > 20 && bl == bt && cf == tf && progress_mc)
		{
		onPreloadComplete();
		}
	}

	private function onPreloadComplete()
	{
	this.onEnterFrame = null;
        this.gotoAndPlay("OUT");
	}

	private function onAnimateOut()
	{
	this.stop();
        dispatchEvent({target:this, type:'onPreloaderOut'});
	}
}

Update Preloader.as

Update Preloader.as


package
{
	import com.jonas.net.Multipart;

	import flash.display.Bitmap;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.net.URLLoader;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.utils.ByteArray;

	[SWF(width="600", height="400", frameRate="30", backgroundColor="#CCCCCC")]
	public class Demo extends Sprite
	{
		[Embed(source="../assets/icon480.png")]
		public var iconClass:Class;
		public var iconBitmap:Bitmap;

		public var textField:TextField;

		private var url:String = "http://labs.positronic.fr/flash/multipart/upload.php";

		public function Demo()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;

			var container:Sprite = new Sprite();
			container.addEventListener(MouseEvent.CLICK, onClick);
			container.buttonMode = true;
			container.y = 20;
			addChild(container);

			iconBitmap = new iconClass() as Bitmap;
			iconBitmap.scaleX = iconBitmap.scaleY = 0.5;
			container.addChild(iconBitmap);

			textField = new TextField();
			textField.width = 400;
			textField.multiline = textField.wordWrap = true;
			textField.autoSize = TextFieldAutoSize.LEFT;
			textField.x = container.width+20;
			textField.y = container.y;
			addChild(textField);
			textField.text = "Click to upload";

			if(loaderInfo.parameters["url"] != null){
				url = loaderInfo.parameters["url"];
			}
		}

		protected function onClick(event:Event):void
		{
			textField.text = "loading...";

			// Create an image ByteArray with Thibault Imbert's JPEGEncoder
			// http://www.bytearray.org/wp-content/projects/fastjpeg/JPEGEncoder.as
			var jpg:JPEGEncoder = new JPEGEncoder(80);
			var image:ByteArray = jpg.encode(iconBitmap.bitmapData);

			// Create an ascii text ByteArray
			var text:ByteArray = new ByteArray();
			text.writeMultiByte("Hello", "ascii");

			// Instanciate Multipart with url
			var form:Multipart = new Multipart(url);

			// Add fields
			form.addField("field1", "hello");
			form.addField("field2", "world");

			// Add files
			form.addFile("file1", text, "text/plain", "test.txt");
			form.addFile("file2", image, "image/jpeg", "test.jpg");

			// Load request
			var loader:URLLoader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, onComplete);
			try {
				loader.load(form.request);
			} catch (error: Error) {
				textField.text = "Unable to load request : "+error.message;
			}
		}

		protected function onComplete(event:Event):void
		{
			textField.text = event.target["data"];
		}
	}
}

package
{
	import com.jonas.net.Multipart;

	import flash.display.Bitmap;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.net.URLLoader;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.utils.ByteArray;

	[SWF(width="600", height="400", frameRate="30", backgroundColor="#CCCCCC")]
	public class Demo extends Sprite
	{
		[Embed(source="../assets/icon480.png")]
		public var iconClass:Class;
		public var iconBitmap:Bitmap;

		public var textField:TextField;

		private var url:String = "http://labs.positronic.fr/flash/multipart/upload.php";

		public function Demo()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;

			var container:Sprite = new Sprite();
			container.addEventListener(MouseEvent.CLICK, onClick);
			container.buttonMode = true;
			container.y = 20;
			addChild(container);

			iconBitmap = new iconClass() as Bitmap;
			iconBitmap.scaleX = iconBitmap.scaleY = 0.5;
			container.addChild(iconBitmap);

			textField = new TextField();
			textField.width = 400;
			textField.multiline = textField.wordWrap = true;
			textField.autoSize = TextFieldAutoSize.LEFT;
			textField.x = container.width+20;
			textField.y = container.y;
			addChild(textField);
			textField.text = "Click to upload";

			if(loaderInfo.parameters["url"] != null){
				url = loaderInfo.parameters["url"];
			}
		}

		protected function onClick(event:Event):void
		{
			textField.text = "loading...";

			// Create an image ByteArray with Thibault Imbert's JPEGEncoder
			// http://www.bytearray.org/wp-content/projects/fastjpeg/JPEGEncoder.as
			var jpg:JPEGEncoder = new JPEGEncoder(80);
			var image:ByteArray = jpg.encode(iconBitmap.bitmapData);

			// Create an ascii text ByteArray
			var text:ByteArray = new ByteArray();
			text.writeMultiByte("Hello", "ascii");

			// Instanciate Multipart with url
			var form:Multipart = new Multipart(url);

			// Add fields
			form.addField("field1", "hello");
			form.addField("field2", "world");
			form.addField("field3", "Voici des caractères accentués pour tester l'encodage.");

			// Add files
			form.addFile("file1", text, "text/plain", "test.txt");
			form.addFile("file2", image, "image/jpeg", "test.jpg");

			// Load request
			var loader:URLLoader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, onComplete);
			try {
				loader.load(form.request);
			} catch (error: Error) {
				textField.text = "Unable to load request : "+error.message;
			}
		}

		protected function onComplete(event:Event):void
		{
			textField.text = event.target["data"];
		}
	}
}

Add accented characters to test encoding


/* ***** BEGIN LICENSE BLOCK *****
 *
 * This file is part of Weave.
 *
 * The Initial Developer of Weave is the Institute for Visualization
 * and Perception Research at the University of Massachusetts Lowell.
 * Portions created by the Initial Developer are Copyright (C) 2008-2015
 * the Initial Developer. All Rights Reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * ***** END LICENSE BLOCK ***** */

package weavejs.data.column
{
	import weave.api.data.IWeaveTreeNode;

	import weavejs.WeaveAPI;
	import weavejs.api.core.ILinkableHashMap;
	import weavejs.api.data.IAttributeColumn;
	import weavejs.api.data.IColumnWrapper;
	import weavejs.api.data.IDataSource;
	import weavejs.api.data.IQualifiedKey;
	import weavejs.api.data.IWeaveTreeNode;
	import weavejs.core.CallbackCollection;
	import weavejs.core.LinkableString;
	import weavejs.core.LinkableVariable;
	import weavejs.core.LinkableWatcher;
	import weavejs.data.hierarchy.GlobalColumnDataSource;

	/**
	 * This provides a wrapper for a referenced column.
	 *
	 * @author adufilie
	 */
	public class ReferencedColumn extends CallbackCollection implements IColumnWrapper
	{
		public function ReferencedColumn()
		{
			super();
		}

		private var _initialized:Boolean = false;

		private var _dataSource:IDataSource;

		private function updateDataSource():void
		{
			var root:ILinkableHashMap = Weave.getRoot(this);
			if (!root)
				return;

			if (!_initialized)
			{
				root.childListCallbacks.addImmediateCallback(this, updateDataSource);
				_initialized = true;
			}

			var ds:IDataSource = root.getObject(dataSourceName.value) as IDataSource;
			if (!ds)
				ds = GlobalColumnDataSource.getInstance(root);
			if (_dataSource != ds)
			{
				_dataSource = ds;
				triggerCallbacks();
			}
		}

		/**
		 * This is the name of an IDataSource in the top level session state.
		 */
		public const dataSourceName:LinkableString = Weave.linkableChild(this, LinkableString, updateDataSource);

		/**
		 * This holds the metadata used to identify a column.
		 */
		public const metadata:LinkableVariable = Weave.linkableChild(this, LinkableVariable);

		public function getDataSource():IDataSource
		{
			return _dataSource;
		}

		public function getHierarchyNode():IWeaveTreeNode
		{
			if (!_dataSource)
				return null;

			var meta:Object = metadata.getSessionState();
			return _dataSource.findHierarchyNode(meta);
		}

		/**
		 * Updates the session state to refer to a new column.
		 */
		public function setColumnReference(dataSource:IDataSource, metadata:Object):void
		{
			delayCallbacks();
			dataSourceName.value = Weave.getRoot(this).getName(dataSource);
			this.metadata.setSessionState(metadata);
			resumeCallbacks();
		}

		/**
		 * The trigger counter value at the last time the internal column was retrieved.
		 */
		private var _prevTriggerCounter:uint = 0;
		/**
		 * the internal referenced column
		 */
		private var _internalColumn:IAttributeColumn = null;

		private var _columnWatcher:LinkableWatcher = Weave.linkableChild(this, LinkableWatcher);

		public function getInternalColumn():IAttributeColumn
		{
			if (_prevTriggerCounter != triggerCounter)
			{
				if (Weave.wasDisposed(_dataSource))
					_dataSource = null;

				_columnWatcher.target = _internalColumn = WeaveAPI.AttributeColumnCache.getColumn(_dataSource, metadata.state);

				_prevTriggerCounter = triggerCounter;
			}
			return _internalColumn;
		}

		/************************************
		 * Begin IAttributeColumn interface
		 ************************************/

		public function getMetadata(attributeName:String):String
		{
			if (_prevTriggerCounter != triggerCounter)
				getInternalColumn();
			return _internalColumn ? _internalColumn.getMetadata(attributeName) : null;
		}

		public function getMetadataPropertyNames():Array
		{
			if (_prevTriggerCounter != triggerCounter)
				getInternalColumn();
			return _internalColumn ? _internalColumn.getMetadataPropertyNames() : [];
		}

		/**
		 * @return the keys associated with this column.
		 */
		public function get keys():Array
		{
			if (_prevTriggerCounter != triggerCounter)
				getInternalColumn();
			return _internalColumn ? _internalColumn.keys : [];
		}

		/**
		 * @param key A key to test.
		 * @return true if the key exists in this IKeySet.
		 */
		public function containsKey(key:IQualifiedKey):Boolean
		{
			if (_prevTriggerCounter != triggerCounter)
				getInternalColumn();
			return _internalColumn && _internalColumn.containsKey(key);
		}

		/**
		 * getValueFromKey
		 * @param key A key of the type specified by keyType.
		 * @return The value associated with the given key.
		 */
		public function getValueFromKey(key:IQualifiedKey, dataType:Class = null):*
		{
			if (_prevTriggerCounter != triggerCounter)
				getInternalColumn();
			return _internalColumn ? _internalColumn.getValueFromKey(key, dataType) : undefined;
		}
	}
}

/* ***** BEGIN LICENSE BLOCK *****
 *
 * This file is part of Weave.
 *
 * The Initial Developer of Weave is the Institute for Visualization
 * and Perception Research at the University of Massachusetts Lowell.
 * Portions created by the Initial Developer are Copyright (C) 2008-2015
 * the Initial Developer. All Rights Reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * ***** END LICENSE BLOCK ***** */

package weavejs.data.column
{
	import weavejs.WeaveAPI;
	import weavejs.api.core.ILinkableHashMap;
	import weavejs.api.data.IAttributeColumn;
	import weavejs.api.data.IColumnWrapper;
	import weavejs.api.data.IDataSource;
	import weavejs.api.data.IQualifiedKey;
	import weavejs.api.data.IWeaveTreeNode;
	import weavejs.core.CallbackCollection;
	import weavejs.core.LinkableString;
	import weavejs.core.LinkableVariable;
	import weavejs.core.LinkableWatcher;
	import weavejs.data.hierarchy.GlobalColumnDataSource;

	/**
	 * This provides a wrapper for a referenced column.
	 *
	 * @author adufilie
	 */
	public class ReferencedColumn extends CallbackCollection implements IColumnWrapper
	{
		public function ReferencedColumn()
		{
			super();
		}

		private var _initialized:Boolean = false;

		private var _dataSource:IDataSource;

		private function updateDataSource():void
		{
			var root:ILinkableHashMap = Weave.getRoot(this);
			if (!root)
				return;

			if (!_initialized)
			{
				root.childListCallbacks.addImmediateCallback(this, updateDataSource);
				_initialized = true;
			}

			var ds:IDataSource = root.getObject(dataSourceName.value) as IDataSource;
			if (!ds)
				ds = GlobalColumnDataSource.getInstance(root);
			if (_dataSource != ds)
			{
				_dataSource = ds;
				triggerCallbacks();
			}
		}

		/**
		 * This is the name of an IDataSource in the top level session state.
		 */
		public const dataSourceName:LinkableString = Weave.linkableChild(this, LinkableString, updateDataSource);

		/**
		 * This holds the metadata used to identify a column.
		 */
		public const metadata:LinkableVariable = Weave.linkableChild(this, LinkableVariable);

		public function getDataSource():IDataSource
		{
			return _dataSource;
		}

		public function getHierarchyNode():IWeaveTreeNode
		{
			if (!_dataSource)
				return null;

			var meta:Object = metadata.getSessionState();
			return _dataSource.findHierarchyNode(meta);
		}

		/**
		 * Updates the session state to refer to a new column.
		 */
		public function setColumnReference(dataSource:IDataSource, metadata:Object):void
		{
			delayCallbacks();
			dataSourceName.value = Weave.getRoot(this).getName(dataSource);
			this.metadata.setSessionState(metadata);
			resumeCallbacks();
		}

		/**
		 * The trigger counter value at the last time the internal column was retrieved.
		 */
		private var _prevTriggerCounter:uint = 0;
		/**
		 * the internal referenced column
		 */
		private var _internalColumn:IAttributeColumn = null;

		private var _columnWatcher:LinkableWatcher = Weave.linkableChild(this, LinkableWatcher);

		public function getInternalColumn():IAttributeColumn
		{
			if (_prevTriggerCounter != triggerCounter)
			{
				if (Weave.wasDisposed(_dataSource))
					_dataSource = null;

				_columnWatcher.target = _internalColumn = WeaveAPI.AttributeColumnCache.getColumn(_dataSource, metadata.state);

				_prevTriggerCounter = triggerCounter;
			}
			return _internalColumn;
		}

		/************************************
		 * Begin IAttributeColumn interface
		 ************************************/

		public function getMetadata(attributeName:String):String
		{
			if (_prevTriggerCounter != triggerCounter)
				getInternalColumn();
			return _internalColumn ? _internalColumn.getMetadata(attributeName) : null;
		}

		public function getMetadataPropertyNames():Array
		{
			if (_prevTriggerCounter != triggerCounter)
				getInternalColumn();
			return _internalColumn ? _internalColumn.getMetadataPropertyNames() : [];
		}

		/**
		 * @return the keys associated with this column.
		 */
		public function get keys():Array
		{
			if (_prevTriggerCounter != triggerCounter)
				getInternalColumn();
			return _internalColumn ? _internalColumn.keys : [];
		}

		/**
		 * @param key A key to test.
		 * @return true if the key exists in this IKeySet.
		 */
		public function containsKey(key:IQualifiedKey):Boolean
		{
			if (_prevTriggerCounter != triggerCounter)
				getInternalColumn();
			return _internalColumn && _internalColumn.containsKey(key);
		}

		/**
		 * getValueFromKey
		 * @param key A key of the type specified by keyType.
		 * @return The value associated with the given key.
		 */
		public function getValueFromKey(key:IQualifiedKey, dataType:Class = null):*
		{
			if (_prevTriggerCounter != triggerCounter)
				getInternalColumn();
			return _internalColumn ? _internalColumn.getValueFromKey(key, dataType) : undefined;
		}
	}
}

remove incorrect import

remove incorrect import


package goplayer
{
  import flash.display.Sprite
  import flash.events.Event
  import flash.utils.getTimer

  public class BufferingIndicator extends Component
  {
    private static const dotCount : uint = 10
    private static const period : Duration = Duration.seconds(1)

    private const canvas : Sprite = new Sprite

    private var elapsedTime : Duration = Duration.milliseconds(0)
    private var lastState : uint = 0

    public function BufferingIndicator()
    {
      mouseEnabled = false
      mouseChildren = false

      addChild(new Background(0x000000, 0.5))
      addChild(canvas)

      addEventListener(Event.ENTER_FRAME, handleEnterFrame)
    }

    private function handleEnterFrame(event : Event) : void
    {
      elapsedTime = Duration.milliseconds(getTimer())

      if (currentState != lastState)
        update(), lastState = currentState
    }

    private function get currentState() : uint
    { return Math.floor(elapsedTime.dividedBy(stateLength)) % dotCount }

    private function get stateLength() : Duration
    { return Duration.seconds(period.seconds / dotCount) }

    override public function update() : void
    {
      super.update()

      canvas.graphics.clear()

      for (var i : uint = 0; i < dotCount; ++i)
        drawDot(i)
    }

    private function drawDot(index : uint) : void
    {
      const position : Position = getDotPosition(index)

      canvas.graphics.beginFill(getDotColor(index), getDotAlpha(index))
      canvas.graphics.drawCircle(position.x, position.y, getDotRadius(index))
      canvas.graphics.endFill()
    }

    private function getDotPosition(index : uint) : Position
    {
      return Angle.ratio(-(index / dotCount)).position
        .scaledBy(radius).plus(dimensions.halved)
    }

    private function get radius() : Number
    { return dimensions.innerSquare.width / 16 }

    private function getDotColor(index : uint) : uint
    { return 0xffffff }

    private function getDotAlpha(index : uint) : Number
    { return (dotCount - modulated(index)) / dotCount }

    private function getDotRadius(index : uint) : Number
    { return radius / 4 }

    private function modulated(index : uint) : uint
    { return (index + currentState) % dotCount }
  }
}

package goplayer
{
  import flash.display.Sprite
  import flash.events.Event
  import flash.utils.getTimer

  public class BufferingIndicator extends Component
  {
    private static const dotCount : uint = 10
    private static const period : Duration = Duration.seconds(1)

    private const canvas : Sprite = new Sprite

    private var elapsedTime : Duration = Duration.milliseconds(0)
    private var lastState : uint = 0

    public function BufferingIndicator()
    {
      mouseEnabled = false
      mouseChildren = false

      addChild(canvas)

      addEventListener(Event.ENTER_FRAME, handleEnterFrame)
    }

    private function handleEnterFrame(event : Event) : void
    {
      elapsedTime = Duration.milliseconds(getTimer())

      if (currentState != lastState)
        update(), lastState = currentState
    }

    private function get currentState() : uint
    { return Math.floor(elapsedTime.dividedBy(stateLength)) % dotCount }

    private function get stateLength() : Duration
    { return Duration.seconds(period.seconds / dotCount) }

    override public function update() : void
    {
      super.update()

      canvas.graphics.clear()

      for (var i : uint = 0; i < dotCount; ++i)
        drawDot(i)
    }

    private function drawDot(index : uint) : void
    {
      const position : Position = getDotPosition(index)

      canvas.graphics.beginFill(getDotColor(index), getDotAlpha(index))
      canvas.graphics.drawCircle(position.x, position.y, getDotRadius(index))
      canvas.graphics.endFill()
    }

    private function getDotPosition(index : uint) : Position
    {
      return Angle.ratio(-(index / dotCount)).position
        .scaledBy(radius).plus(dimensions.halved)
    }

    private function get radius() : Number
    { return dimensions.innerSquare.width / 16 }

    private function getDotColor(index : uint) : uint
    { return 0xffffff }

    private function getDotAlpha(index : uint) : Number
    { return (dotCount - modulated(index)) / dotCount }

    private function getDotRadius(index : uint) : Number
    { return radius / 4 }

    private function modulated(index : uint) : uint
    { return (index + currentState) % dotCount }
  }
}

Remove background of buffering indicator.


////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008-2013 Esri. All Rights Reserved.
//
// 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 com.esri.builder.eventbus
{

import flash.events.EventDispatcher;

/**
 * The EventBus allows centralized application communication.
 * It uses the singleton design pattern to make sure one event bus
 * is available globally.
 */
public class EventBus extends EventDispatcher
{
    /** Application event bus instance */
    public static const instance:EventBus = new EventBus();

    /**
     * Normally the EventBus is not instantiated via the <b>new</b> method directly.
     * The constructor helps enforce only one EventBus available for the application
     * (singleton) so that it assures the communication only via a single event bus.
     */
    public function EventBus()
    {
    }
}
}

////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008-2013 Esri. All Rights Reserved.
//
// 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 com.esri.builder.eventbus
{

import flash.events.EventDispatcher;

/**
 * The EventBus allows centralized application communication.
 */
public class EventBus extends EventDispatcher
{
    /** Application event bus instance */
    public static const instance:EventBus = new EventBus();

    /**
     * Normally the EventBus is not instantiated via the <b>new</b> method directly.
     */
    public function EventBus()
    {
    }
}
}

Clean up EventBus comments.

Clean up EventBus comments.


/*****************************************************
*
*  Copyright 2009 Adobe Systems Incorporated.  All Rights Reserved.
*
*****************************************************
*  The contents of this file are subject to the Mozilla Public License
*  Version 1.1 (the "License"); you may not use this file except in
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
*  Software distributed under the License is distributed on an "AS IS"
*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
*  License for the specific language governing rights and limitations
*  under the License.
*
*
*  The Initial Developer of the Original Code is Adobe Systems Incorporated.
*  Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems
*  Incorporated. All Rights Reserved.
*
*****************************************************/
package com.kaltura.hls
{
	import __AS3__.vec.Vector;

	import org.osmf.media.MediaResourceBase;
	import org.osmf.net.NetLoader;
	import org.osmf.net.rtmpstreaming.RTMPDynamicStreamingNetLoader;
	import org.osmf.traits.LoaderBase;
	import org.osmf.elements.VideoElement;
	import flash.geom.Point;
	import flash.display.Stage;
	import flash.utils.Timer;
    import flash.events.TimerEvent;

	CONFIG::FLASH_10_1
	{
	import flash.events.DRMAuthenticateEvent;
	import flash.events.DRMErrorEvent;
	import flash.events.DRMStatusEvent;
	import flash.net.drm.DRMContentData;
	import flash.system.SystemUpdaterType;
	import flash.system.SystemUpdater;
	import org.osmf.net.drm.NetStreamDRMTrait;
	import org.osmf.net.httpstreaming.HTTPStreamingNetLoader;
	}

	public class HLSVideoElement extends VideoElement
	{
		public var cropHackTimer:Timer;

		public function HLSVideoElement(resource:MediaResourceBase=null, loader:NetLoader=null)
		{
			super(resource, loader);

			cropHackTimer = new Timer(250, 0);
			cropHackTimer.addEventListener(TimerEvent.TIMER, onCropHackTimer);
			cropHackTimer.start();
		}

		protected function onCropHackTimer(te:TimerEvent):void
		{
			// Sweet hax to adjust the zoom of the stage video.
			if(stage.stageVideos.length > 0)
			{
				stage.stageVideos[0].zoom = new Point(HLSManifestParser.FORCE_CROP_WORKAROUND_ZOOM_X, HLSManifestParser.FORCE_CROP_WORKAROUND_ZOOM_Y);
				stage.stageVideos[0].pan = new Point(HLSManifestParser.FORCE_CROP_WORKAROUND_PAN_X, FORCE_CROP_WORKAROUND_PAN_Y);
			}
		}

	    /**
	     * @private
		 */
		override protected function processReadyState():void
		{
			super.processReadyState();
		}
	}
}

/*****************************************************
*
*  Copyright 2009 Adobe Systems Incorporated.  All Rights Reserved.
*
*****************************************************
*  The contents of this file are subject to the Mozilla Public License
*  Version 1.1 (the "License"); you may not use this file except in
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
*  Software distributed under the License is distributed on an "AS IS"
*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
*  License for the specific language governing rights and limitations
*  under the License.
*
*
*  The Initial Developer of the Original Code is Adobe Systems Incorporated.
*  Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems
*  Incorporated. All Rights Reserved.
*
*****************************************************/
package com.kaltura.hls
{
	import __AS3__.vec.Vector;

	import org.osmf.media.MediaResourceBase;
	import org.osmf.net.NetLoader;
	import org.osmf.net.rtmpstreaming.RTMPDynamicStreamingNetLoader;
	import org.osmf.traits.LoaderBase;
	import org.osmf.elements.VideoElement;
	import flash.geom.Point;
	import flash.display.Stage;
	import flash.display.DisplayObject;
	import flash.utils.Timer;
    import flash.events.TimerEvent;
    import com.kaltura.hls.manifest.HLSManifestParser;

	CONFIG::FLASH_10_1
	{
	import flash.events.DRMAuthenticateEvent;
	import flash.events.DRMErrorEvent;
	import flash.events.DRMStatusEvent;
	import flash.net.drm.DRMContentData;
	import flash.system.SystemUpdaterType;
	import flash.system.SystemUpdater;
	import org.osmf.net.drm.NetStreamDRMTrait;
	import org.osmf.net.httpstreaming.HTTPStreamingNetLoader;
	}

	public class HLSVideoElement extends VideoElement
	{
		public var cropHackTimer:Timer;

		public function HLSVideoElement(resource:MediaResourceBase=null, loader:NetLoader=null)
		{
			super(resource, loader);

			cropHackTimer = new Timer(250, 0);
			cropHackTimer.addEventListener(TimerEvent.TIMER, onCropHackTimer);
			cropHackTimer.start();
		}

		protected function onCropHackTimer(te:TimerEvent):void
		{
			// Sweet hax to adjust the zoom of the stage video.
			var containerDO:DisplayObject = container as DisplayObject;
			if(!containerDO)
				return;

			var stage:Stage = containerDO.stage;
			if(stage.stageVideos.length > 0)
			{
				stage.stageVideos[0].zoom = new Point(HLSManifestParser.FORCE_CROP_WORKAROUND_ZOOM_X, HLSManifestParser.FORCE_CROP_WORKAROUND_ZOOM_Y);
				stage.stageVideos[0].pan = new Point(HLSManifestParser.FORCE_CROP_WORKAROUND_PAN_X, HLSManifestParser.FORCE_CROP_WORKAROUND_PAN_Y);
			}
		}

	    /**
	     * @private
		 */
		override protected function processReadyState():void
		{
			super.processReadyState();
		}
	}
}

Fix some dumb mistakes!

Fix some dumb mistakes!


package {
  import flash.display.Sprite;
  import flash.external.ExternalInterface;
  import flash.system.Security;
  import flash.net.NetStream;
  import flash.events.*;

  import org.osmf.containers.MediaContainer;
  import org.osmf.elements.VideoElement;
  import org.osmf.net.NetStreamLoadTrait;

  import org.osmf.media.DefaultMediaFactory;
  import org.osmf.media.MediaElement;
  import org.osmf.media.MediaPlayer;
  import org.osmf.media.URLResource;

  import org.osmf.events.TimeEvent;
  import org.osmf.events.BufferEvent;
  import org.osmf.events.MediaElementEvent;
  import org.osmf.events.LoadEvent;

  import org.osmf.traits.MediaTraitType;

  [SWF(width="640", height="360")]
  public class RTMP extends Sprite {
    private var playbackId:String;
    private var mediaFactory:DefaultMediaFactory;
    private var mediaContainer:MediaContainer;
    private var mediaPlayer:MediaPlayer;
    private var netStream:NetStream;
    private var mediaElement:MediaElement;
    private var netStreamLoadTrait:NetStreamLoadTrait;
    private var playbackState:String = "IDLE";

    public function RTMP() {
      Security.allowDomain('*');
      Security.allowInsecureDomain('*');
      playbackId = this.root.loaderInfo.parameters.playbackId;
      mediaFactory = new DefaultMediaFactory();
      mediaContainer = new MediaContainer();
      setupCallbacks();
      setupGetters();
      ExternalInterface.call('console.log', 'clappr rtmp 0.4-alpha');
      _triggerEvent('flashready');
    }

    private function setupCallbacks():void {
      ExternalInterface.addCallback("playerLoad", playerLoad);
      ExternalInterface.addCallback("playerPlay", playerPlay);
      ExternalInterface.addCallback("playerPause", playerPause);
      ExternalInterface.addCallback("playerStop", playerStop);
      ExternalInterface.addCallback("playerSeek", playerSeek);
    }

    private function setupGetters():void {
      ExternalInterface.addCallback("getState", getState);
      ExternalInterface.addCallback("getPosition", getPosition);
      ExternalInterface.addCallback("getDuration", getDuration);
    }

    private function playerLoad(url:String):void {
      mediaElement = mediaFactory.createMediaElement(new URLResource(url));
      mediaElement.addEventListener(MediaElementEvent.TRAIT_ADD, onTraitAdd);
      mediaPlayer = new MediaPlayer(mediaElement);
      mediaPlayer.autoPlay = false;
      mediaPlayer.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, onTimeUpdated);
      mediaPlayer.addEventListener(TimeEvent.DURATION_CHANGE, onTimeUpdated);
      mediaContainer.addMediaElement(mediaElement);
      addChild(mediaContainer);
    }

    private function onTraitAdd(event:MediaElementEvent):void {
      if (mediaElement.hasTrait(MediaTraitType.LOAD)) {
        netStreamLoadTrait = mediaElement.getTrait(MediaTraitType.LOAD) as NetStreamLoadTrait;
        netStreamLoadTrait.addEventListener(LoadEvent.LOAD_STATE_CHANGE, onLoaded);
      }
    }

    private function onLoaded(event:LoadEvent):void {
      netStream = netStreamLoadTrait.netStream;
      netStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
    }

    private function netStatusHandler(event:NetStatusEvent):void {
      ExternalInterface.call('console.log', 'new event:' + event.info.code);
      if (event.info.code === "NetStream.Buffer.Full") {
        playbackState = "PLAYING";
      } else if (isBuffering(event.info.code)) {
        playbackState = "PLAYING_BUFFERING";
      } else if (event.info.code == "NetStream.Play.Stop") {
        playbackState = "ENDED";
      } else if (event.info.code == "NetStream.Buffer.Empty") {
        playbackState = "BUFFERING";
      }
      _triggerEvent('statechanged');
    }

    private function isBuffering(code:String):Boolean {
      return Boolean(code == "NetStream.Buffer.Empty" && playbackState != "ENDED" ||
              code == "NetStream.SeekStart.Notify" ||
              code == "NetStream.Play.Start");
    }

    private function playerPlay():void {
      mediaPlayer.play();
    }

    private function playerPause():void {
      mediaPlayer.pause();
    }

    private function playerSeek(seconds:Number):void {
      mediaPlayer.seek(seconds);
    }

    private function playerStop():void {
      mediaPlayer.stop();
    }

    private function onTimeUpdated(event:TimeEvent):void {
      _triggerEvent('progress');
      _triggerEvent('timeupdate');
    }

    private function getPosition():Number {
      return mediaPlayer.currentTime;
    }

    private function getDuration():Number {
      return mediaPlayer.duration;
    }

    private function _triggerEvent(name: String):void {
      ExternalInterface.call('Clappr.Mediator.trigger("' + playbackId + ':' + name +'")');
    }

    private function getState():String {
      return playbackState;
    }
  }
}

package {
  import flash.display.Sprite;
  import flash.external.ExternalInterface;
  import flash.system.Security;
  import flash.net.NetStream;
  import flash.events.*;

  import org.osmf.containers.MediaContainer;
  import org.osmf.elements.VideoElement;
  import org.osmf.net.NetStreamLoadTrait;

  import org.osmf.media.DefaultMediaFactory;
  import org.osmf.media.MediaElement;
  import org.osmf.media.MediaPlayer;
  import org.osmf.media.URLResource;

  import org.osmf.events.TimeEvent;
  import org.osmf.events.BufferEvent;
  import org.osmf.events.MediaElementEvent;
  import org.osmf.events.LoadEvent;

  import org.osmf.traits.MediaTraitType;

  [SWF(width="640", height="360")]
  public class RTMP extends Sprite {
    private var playbackId:String;
    private var mediaFactory:DefaultMediaFactory;
    private var mediaContainer:MediaContainer;
    private var mediaPlayer:MediaPlayer;
    private var netStream:NetStream;
    private var mediaElement:MediaElement;
    private var netStreamLoadTrait:NetStreamLoadTrait;
    private var playbackState:String = "IDLE";

    public function RTMP() {
      Security.allowDomain('*');
      Security.allowInsecureDomain('*');
      playbackId = this.root.loaderInfo.parameters.playbackId;
      mediaFactory = new DefaultMediaFactory();
      mediaContainer = new MediaContainer();
      setupCallbacks();
      setupGetters();
      ExternalInterface.call('console.log', 'clappr rtmp 0.6-alpha');
      _triggerEvent('flashready');
    }

    private function setupCallbacks():void {
      ExternalInterface.addCallback("playerLoad", playerLoad);
      ExternalInterface.addCallback("playerPlay", playerPlay);
      ExternalInterface.addCallback("playerPause", playerPause);
      ExternalInterface.addCallback("playerStop", playerStop);
      ExternalInterface.addCallback("playerSeek", playerSeek);
      ExternalInterface.addCallback("playerVolume", playerVolume);
    }

    private function setupGetters():void {
      ExternalInterface.addCallback("getState", getState);
      ExternalInterface.addCallback("getPosition", getPosition);
      ExternalInterface.addCallback("getDuration", getDuration);
    }

    private function playerLoad(url:String):void {
      mediaElement = mediaFactory.createMediaElement(new URLResource(url));
      mediaElement.addEventListener(MediaElementEvent.TRAIT_ADD, onTraitAdd);
      mediaPlayer = new MediaPlayer(mediaElement);
      mediaPlayer.autoPlay = false;
      mediaPlayer.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, onTimeUpdated);
      mediaPlayer.addEventListener(TimeEvent.DURATION_CHANGE, onTimeUpdated);
      mediaContainer.addMediaElement(mediaElement);
      addChild(mediaContainer);
    }

    private function onTraitAdd(event:MediaElementEvent):void {
      if (mediaElement.hasTrait(MediaTraitType.LOAD)) {
        netStreamLoadTrait = mediaElement.getTrait(MediaTraitType.LOAD) as NetStreamLoadTrait;
        netStreamLoadTrait.addEventListener(LoadEvent.LOAD_STATE_CHANGE, onLoaded);
      }
    }

    private function onLoaded(event:LoadEvent):void {
      netStream = netStreamLoadTrait.netStream;
      netStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
    }

    private function netStatusHandler(event:NetStatusEvent):void {
      ExternalInterface.call('console.log', 'new event:' + event.info.code);
      if (event.info.code === "NetStream.Buffer.Full") {
        playbackState = "PLAYING";
      } else if (isBuffering(event.info.code)) {
        playbackState = "PLAYING_BUFFERING";
      } else if (event.info.code == "NetStream.Play.Stop") {
        playbackState = "ENDED";
      } else if (event.info.code == "NetStream.Buffer.Empty") {
        playbackState = "BUFFERING";
      }
      _triggerEvent('statechanged');
    }

    private function isBuffering(code:String):Boolean {
      return Boolean(code == "NetStream.Buffer.Empty" && playbackState != "ENDED" ||
              code == "NetStream.SeekStart.Notify" ||
              code == "NetStream.Play.Start");
    }

    private function playerPlay():void {
      mediaPlayer.play();
    }

    private function playerPause():void {
      mediaPlayer.pause();
    }

    private function playerSeek(seconds:Number):void {
      mediaPlayer.seek(seconds);
    }

    private function playerStop():void {
      mediaPlayer.stop();
    }

    private function playerVolume(level:Number):void {
      mediaPlayer.volume = level;
    }

   private function getState():String {
      return playbackState;
    }

    private function getPosition():Number {
      return mediaPlayer.currentTime;
    }

    private function getDuration():Number {
      return mediaPlayer.duration;
    }

    private function onTimeUpdated(event:TimeEvent):void {
      _triggerEvent('progress');
      _triggerEvent('timeupdate');
    }

    private function _triggerEvent(name: String):void {
      ExternalInterface.call('Clappr.Mediator.trigger("' + playbackId + ':' + name +'")');
    }
  }
}

support setVolume() calls (refs #4)

RTMP.as: support setVolume() calls (refs #4)


package com.axis.mjpgplayer {

  import flash.events.ErrorEvent;
  import flash.events.Event;
  import flash.events.EventDispatcher;
  import flash.events.IOErrorEvent;
  import flash.events.ProgressEvent;
  import flash.events.SecurityErrorEvent;
  import flash.net.Socket;
  import flash.utils.ByteArray;
  import flash.system.Security;
  import flash.external.ExternalInterface;
  import flash.display.LoaderInfo;

  import mx.utils.URLUtil;

  [Event(name="image",type="flash.events.Event")]
  [Event(name="connect",type="flash.events.Event")]
  [Event(name="error",type="flash.events.Event")]

  /**
   * Error codes:
   * 0: Socket or security errors
   * 1: Client side validation errors
   * 2: Server HTTP response errors
   * 3: Valid HTTP response (200), but invalid content
   */

  public class IPCam extends EventDispatcher {

    private var jsEventCallbackName:String = "console.log";
    private var socket:Socket = null;
    private var buffer:ByteArray = null;
    private var dataBuffer:ByteArray = null;
    private var url:String = "";
    private var params:Object;
    private var parseHeaders:Boolean = true;
    private var headers:Vector.<String> = new Vector.<String>();
    private var clen:int;
    private var parseSubheaders:Boolean = true;

    public var image:ByteArray = null;

    public function IPCam() {
      // Set up JS API
      ExternalInterface.marshallExceptions = true;
      ExternalInterface.addCallback("play", connect);
      ExternalInterface.addCallback("pause", disconnect);
      ExternalInterface.addCallback("stop", stop);
      ExternalInterface.addCallback("setEventCallbackName", setJsEventCallbackName);

      Security.allowDomain("*");
      Security.allowInsecureDomain("*");

      buffer = new ByteArray();
      dataBuffer = new ByteArray();
      socket = new Socket();
      socket.timeout = 5000;
      socket.addEventListener(Event.CONNECT, onSockConn);
      socket.addEventListener(Event.CLOSE, onClose);
      socket.addEventListener(ProgressEvent.SOCKET_DATA, onSockData);
      socket.addEventListener(IOErrorEvent.IO_ERROR, onError);
      socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
    }

    public function sendLoadedEvent():void {
      // Tell the external JS environent that we are ready to accept API calls
      ExternalInterface.call(jsEventCallbackName, 'loaded');
    }

    public function setJsEventCallbackName(jsEventCallbackName:String):void {
      this.jsEventCallbackName = jsEventCallbackName;
    }

    public function getJsEventCallbackName():String {
      return jsEventCallbackName;
    }

    private function onError(e:ErrorEvent):void {
      sendError(0, e.text);
    }

    private function sendError(errNo:Number = 0, msg:String = ""):void {
      disconnect();
      dispatchEvent(new Event("error"));
      ExternalInterface.call(jsEventCallbackName, "error", errNo, msg);
    }

    public function disconnect():void {
      if (socket.connected) {
        socket.close();
      }
      image = null;
      buffer = null;
      dispatchEvent(new Event("disconnect"));
    }

    public function stop():void {
      disconnect();
      dispatchEvent(new Event("clear"));
    }

    public function connect(url:String = null):void {
      disconnect();

      if (url != null) {
        this.url = url;
      }

      if (!URLUtil.isHttpURL(this.url)) {
        sendError(1, "Invalid Url");
        return;
      }

      socket.connect(URLUtil.getServerName(this.url), 80);
    }

    private function onSockConn(event:Event):void {
      buffer = new ByteArray();
      dataBuffer = new ByteArray();

      parseHeaders = true;
      socket.writeUTFBytes("GET " + url + " HTTP/1.0\r\n");
      socket.writeUTFBytes("Host: " + URLUtil.getServerName(url) + "\r\n");
      socket.writeUTFBytes("\r\n");
    }

    private function onClose(e:Event):void {
      // Security error is thrown if this line is excluded
      socket.close();
    }

    private function readline():Boolean {
      while (dataBuffer.bytesAvailable > 0) {
        var t:String = dataBuffer.readMultiByte(1, "us-ascii");
        if (t == "\r") {
          continue;
        }
        if (t == "\n") {
          return true;
        }
        buffer.writeMultiByte(t, "us-ascii");
      }

      return false;
    }

    private function getContentType():String {
      var content:String = "content-type: ";
      for each (var str:String in headers) {
        if (str.toLowerCase().indexOf(content) >= 0) {
          return str.substr(str.indexOf(content) + content.length).toLowerCase();
        }
      }
      return "";
    }

    private function onParseHeaders():Boolean {
      headers.length = 0;
      while (1) {
        var wholeLine:Boolean = readline();

        if (!wholeLine) {
          return false;
        }

        if (buffer.length == 0) {
          parseHeaders = false;
          break;
        }

        headers.push(buffer.toString());
        buffer.clear();
      }
      try {
        var arr:Array = headers[0].split(" ");
        if (arr[1] == "200") {
          var content:String = getContentType();
          if (content.indexOf("multipart/x-mixed-replace") >= 0) {
            dispatchEvent(new Event("connect"));
            return true;
          }
          throw(new Error("error"));
        } else {
          sendError(2, "Server retured error code " + arr[1] + ".");
        }
      }
      catch (e:Error) {
        sendError(3, "Invalid response received.");
      }
      return false;
    }

    private function onSockData(event:ProgressEvent):void {
      socket.readBytes(dataBuffer, dataBuffer.length);
      if (parseHeaders) {
        if (!onParseHeaders()) {
          return;
        }
      }
      if (parseSubheaders) {
        if (!parseSubHeader()) {
          return;
        }
      }
      findImage();
    }

    private function parseSubHeader():Boolean {
      while (parseSubheaders) {
        var wholeLine:Boolean = readline();

        if (!wholeLine) {
          return false;
        }

        if (buffer.length == 0) {
          break;
        }

        var subHeaders:Array = buffer.toString().split(": ");
        if ((subHeaders[0] as String).toLowerCase() == "content-length") {
          clen = subHeaders[1];
        }
        buffer.clear();
      }

      parseSubheaders = false;

      return true;
    }

    private function findImage():void {
      if (dataBuffer.bytesAvailable < clen + 2) {
        return;
      }

      image = new ByteArray()
      dataBuffer.readBytes(image, 0, clen + 2);
      //dataBuffer.readMultiByte(2, "us-ascii");    // Why is this not needed? Probably the cause of stability issues

      var copy:ByteArray = new ByteArray();
      dataBuffer.readBytes(copy, 0);
      dataBuffer = copy;

      dispatchEvent(new Event("image"));
      clen = 0;
      parseSubheaders = true;
    }

  }

}

package com.axis.mjpgplayer {

  import flash.events.ErrorEvent;
  import flash.events.Event;
  import flash.events.EventDispatcher;
  import flash.events.IOErrorEvent;
  import flash.events.ProgressEvent;
  import flash.events.SecurityErrorEvent;
  import flash.net.Socket;
  import flash.utils.ByteArray;
  import flash.system.Security;
  import flash.external.ExternalInterface;
  import flash.display.LoaderInfo;

  import mx.utils.URLUtil;

  [Event(name="image",type="flash.events.Event")]
  [Event(name="connect",type="flash.events.Event")]
  [Event(name="error",type="flash.events.Event")]

  /**
   * Error codes:
   * 0: Socket or security errors
   * 1: Client side validation errors
   * 2: Server HTTP response errors
   * 3: Valid HTTP response (200), but invalid content
   */

  public class IPCam extends EventDispatcher {

    private var jsEventCallbackName:String = "console.log";
    private var socket:Socket = null;
    private var buffer:ByteArray = null;
    private var dataBuffer:ByteArray = null;
    private var url:String = "";
    private var params:Object;
    private var parseHeaders:Boolean = true;
    private var headers:Vector.<String> = new Vector.<String>();
    private var clen:int;
    private var parseSubheaders:Boolean = true;

    public var image:ByteArray = null;

    public function IPCam() {
      // Set up JS API
      ExternalInterface.marshallExceptions = true;
      ExternalInterface.addCallback("play", connect);
      ExternalInterface.addCallback("pause", disconnect);
      ExternalInterface.addCallback("stop", stop);
      ExternalInterface.addCallback("setEventCallbackName", setJsEventCallbackName);

      Security.allowDomain("*");
      Security.allowInsecureDomain("*");

      buffer = new ByteArray();
      dataBuffer = new ByteArray();
      socket = new Socket();
      socket.timeout = 5000;
      socket.addEventListener(Event.CONNECT, onSockConn);
      socket.addEventListener(Event.CLOSE, onClose);
      socket.addEventListener(ProgressEvent.SOCKET_DATA, onSockData);
      socket.addEventListener(IOErrorEvent.IO_ERROR, onError);
      socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
    }

    public function sendLoadedEvent():void {
      // Tell the external JS environent that we are ready to accept API calls
      ExternalInterface.call(jsEventCallbackName, 'loaded');
    }

    public function setJsEventCallbackName(jsEventCallbackName:String):void {
      this.jsEventCallbackName = jsEventCallbackName;
    }

    public function getJsEventCallbackName():String {
      return jsEventCallbackName;
    }

    private function onError(e:ErrorEvent):void {
      sendError(0, e.text);
    }

    private function sendError(errNo:Number = 0, msg:String = ""):void {
      disconnect();
      dispatchEvent(new Event("error"));
      ExternalInterface.call(jsEventCallbackName, "error", errNo, msg);
    }

    public function disconnect():void {
      if (socket.connected) {
        socket.close();
      }
      image = null;
      buffer = null;
      dispatchEvent(new Event("disconnect"));
    }

    public function stop():void {
      disconnect();
      dispatchEvent(new Event("clear"));
    }

    public function connect(url:String = null):void {
      disconnect();

      if (url != null) {
        this.url = url;
      }

      if (!URLUtil.isHttpURL(this.url)) {
        sendError(1, "Invalid Url");
        return;
      }

      socket.connect(URLUtil.getServerName(this.url), 80);
    }

    private function onSockConn(event:Event):void {
      buffer = new ByteArray();
      dataBuffer = new ByteArray();

      parseHeaders = true;
      socket.writeUTFBytes("GET " + url + " HTTP/1.0\r\n");
      socket.writeUTFBytes("Host: " + URLUtil.getServerName(url) + "\r\n");
      socket.writeUTFBytes("\r\n");
    }

    private function onClose(e:Event):void {
      // Security error is thrown if this line is excluded
      socket.close();
      ExternalInterface.call(jsEventCallbackName, "stopped");
    }

    private function readline():Boolean {
      while (dataBuffer.bytesAvailable > 0) {
        var t:String = dataBuffer.readMultiByte(1, "us-ascii");
        if (t == "\r") {
          continue;
        }
        if (t == "\n") {
          return true;
        }
        buffer.writeMultiByte(t, "us-ascii");
      }

      return false;
    }

    private function getContentType():String {
      var content:String = "content-type: ";
      for each (var str:String in headers) {
        if (str.toLowerCase().indexOf(content) >= 0) {
          return str.substr(str.indexOf(content) + content.length).toLowerCase();
        }
      }
      return "";
    }

    private function onParseHeaders():Boolean {
      headers.length = 0;
      while (1) {
        var wholeLine:Boolean = readline();

        if (!wholeLine) {
          return false;
        }

        if (buffer.length == 0) {
          parseHeaders = false;
          break;
        }

        headers.push(buffer.toString());
        buffer.clear();
      }
      try {
        var arr:Array = headers[0].split(" ");
        if (arr[1] == "200") {
          var content:String = getContentType();
          if (content.indexOf("multipart/x-mixed-replace") >= 0) {
            dispatchEvent(new Event("connect"));
            return true;
          }
          throw(new Error("error"));
        } else {
          sendError(2, "Server retured error code " + arr[1] + ".");
        }
      }
      catch (e:Error) {
        sendError(3, "Invalid response received.");
      }
      return false;
    }

    private function onSockData(event:ProgressEvent):void {
      socket.readBytes(dataBuffer, dataBuffer.length);
      if (parseHeaders) {
        if (!onParseHeaders()) {
          return;
        }
      }
      if (parseSubheaders) {
        if (!parseSubHeader()) {
          return;
        }
      }
      findImage();
    }

    private function parseSubHeader():Boolean {
      while (parseSubheaders) {
        var wholeLine:Boolean = readline();

        if (!wholeLine) {
          return false;
        }

        if (buffer.length == 0) {
          break;
        }

        var subHeaders:Array = buffer.toString().split(": ");
        if ((subHeaders[0] as String).toLowerCase() == "content-length") {
          clen = subHeaders[1];
        }
        buffer.clear();
      }

      parseSubheaders = false;

      return true;
    }

    private function findImage():void {
      if (dataBuffer.bytesAvailable < clen + 2) {
        return;
      }

      image = new ByteArray()
      dataBuffer.readBytes(image, 0, clen + 2);
      //dataBuffer.readMultiByte(2, "us-ascii");    // Why is this not needed? Probably the cause of stability issues

      var copy:ByteArray = new ByteArray();
      dataBuffer.readBytes(copy, 0);
      dataBuffer = copy;

      dispatchEvent(new Event("image"));
      clen = 0;
      parseSubheaders = true;
    }

  }

}

Throw event when stream closes

Throw event when stream closes

Solves: Trouble 52041
Solves: Trouble 52371
Change-Id: I66c55ad90e348539957db66c23c72bf97ad78141
Reference: 4a371182d378ce08461de7e0c09e03d07097467b
Reviewed-on: https://gittools.se.axis.com/gerrit/1281
Reviewed-by: Torbjörn Weiland <68336512b8660a5323e02a03889821bdb71c67bf@axis.com>
Tested-by: Oskar Gustafsson <689c75baac3ff14f6707b8f6bb8ba99347d04a10@axis.com>
Reviewed-by: Oskar Gustafsson <689c75baac3ff14f6707b8f6bb8ba99347d04a10@axis.com>
Daily-Built: Oskar Gustafsson <689c75baac3ff14f6707b8f6bb8ba99347d04a10@axis.com>


/* Vivified
 * Copyright (C) 2007 Benjamin Otte <otte@gnome.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

/*** general objects ***/

Wrap = function () {};
Wrap.prototype = {};
Wrap.prototype.toString = Native.wrap_toString;

Frame = function () extends Wrap {};
Frame.prototype = new Wrap ();
Frame.prototype.addProperty ("code", Native.frame_code_get, null);
Frame.prototype.addProperty ("name", Native.frame_name_get, null);
Frame.prototype.addProperty ("next", Native.frame_next_get, null);

/*** breakpoints ***/

Breakpoint = function () extends Native.Breakpoint {
  super ();
  Breakpoint.list.push (this);
};
Breakpoint.list = new Array ();
Breakpoint.prototype.addProperty ("active", Native.breakpoint_active_get, Native.breakpoint_active_set);

/*** information about the player ***/

Player = {};
Player.addProperty ("filename", Native.player_filename_get, Native.player_filename_set);
Player.addProperty ("frame", Native.player_frame_get, null);
Player.addProperty ("global", Native.player_global_get, null);
Player.addProperty ("variables", Native.player_variables_get, Native.player_variables_set);

/*** commands available for debugging ***/

Commands = new Object ();
Commands.print = Native.print;
Commands.error = Native.error;
Commands.r = Native.run;
Commands.run = Native.run;
Commands.halt = Native.stop;
Commands.stop = Native.stop;
Commands.s = Native.step;
Commands.step = Native.step;
Commands.reset = Native.reset;
Commands.restart = function () {
  Commands.reset ();
  Commands.run ();
};
Commands.quit = Native.quit;
/* can't use "break" as a function name, it's a keyword in JS */
Commands.add = function (name) {
  if (name == undefined) {
    Commands.error ("add command requires a function name");
    return undefined;
  }
  var ret = new Breakpoint ();
  ret.onStartFrame = function (frame) {
    if (frame.name != name)
      return false;

    Commands.print ("Breakpoint: function " + name + " called");
    Commands.print ("  " + frame);
    return true;
  };
  ret.toString = function () {
    return "function call " + name;
  };
  return ret;
};
Commands.list = function () {
  var a = Breakpoint.list;
  var i;
  for (i = 0; i < a.length; i++) {
    Commands.print (i + ": " + a[i]);
  }
};
Commands.del = function (id) {
  var a = Breakpoint.list;
  if (id == undefined) {
    while (a[0])
      Commands.del (0);
  }
  var b = a[id];
  a.splice (id, 1);
  b.active = false;
};
Commands.delete = Commands.del;
Commands.where = function () {
  var frame = Player.frame;
  if (frame == undefined) {
    Commands.print ("---");
    return;
  }
  var i = 0;
  while (frame) {
    Commands.print (i++ + ": " + frame);
    frame = frame.next;
  }
};
Commands.backtrace = Commands.where;
Commands.bt = Commands.where;
Commands.watch = function () {
  var object;
  var name;
  if (arguments.length == 1) {
    name = arguments[0];
  } else if (arguments.length == 2) {
    object = arguments[0];
    name = arguments[1];
  } else {
    Commands.error ("usage: watch [object] name");
    return;
  }
  var ret = new Breakpoint ();
  ret.onSetVariable = function (o, variable, value) {
    if (object && o != object)
      return false;
    if (variable != name)
      return;

    if (object) {
      Commands.print ("Breakpoint: variable " + name + " on " + object);
    } else {
      Commands.print ("Breakpoint: variable " + name);
    }
    Commands.print ("  " + Player.frame);
    return true;
  };
  ret.toString = function () {
    var s = "watch " + name;
    if (object)
      s += " on " + object;
    return s;
  };
  return ret;
};

/* Vivified
 * Copyright (C) 2007 Benjamin Otte <otte@gnome.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

/*** general objects ***/

Wrap = function () {};
Wrap.prototype = {};
Wrap.prototype.toString = Native.wrap_toString;

Frame = function () extends Wrap {};
Frame.prototype = new Wrap ();
Frame.prototype.addProperty ("code", Native.frame_code_get, null);
Frame.prototype.addProperty ("name", Native.frame_name_get, null);
Frame.prototype.addProperty ("next", Native.frame_next_get, null);

/*** breakpoints ***/

Breakpoint = function () extends Native.Breakpoint {
  super ();
  Breakpoint.list.push (this);
};
Breakpoint.list = new Array ();
Breakpoint.prototype.addProperty ("active", Native.breakpoint_active_get, Native.breakpoint_active_set);
Breakpoint.prototype.toString = function () {
  return "user-defined breakpoint";
};

/*** information about the player ***/

Player = {};
Player.addProperty ("filename", Native.player_filename_get, Native.player_filename_set);
Player.addProperty ("frame", Native.player_frame_get, null);
Player.addProperty ("global", Native.player_global_get, null);
Player.addProperty ("variables", Native.player_variables_get, Native.player_variables_set);

/*** commands available for debugging ***/

Commands = new Object ();
Commands.print = Native.print;
Commands.error = Native.error;
Commands.r = Native.run;
Commands.run = Native.run;
Commands.halt = Native.stop;
Commands.stop = Native.stop;
Commands.s = Native.step;
Commands.step = Native.step;
Commands.reset = Native.reset;
Commands.restart = function () {
  Commands.reset ();
  Commands.run ();
};
Commands.quit = Native.quit;
/* can't use "break" as a function name, it's a keyword in JS */
Commands.add = function (name) {
  if (name == undefined) {
    Commands.error ("add command requires a function name");
    return undefined;
  }
  var ret = new Breakpoint ();
  ret.onStartFrame = function (frame) {
    if (frame.name != name)
      return false;

    Commands.print ("Breakpoint: function " + name + " called");
    Commands.print ("  " + frame);
    return true;
  };
  ret.toString = function () {
    return "function call " + name;
  };
  return ret;
};
Commands.list = function () {
  var a = Breakpoint.list;
  var i;
  for (i = 0; i < a.length; i++) {
    Commands.print (i + ": " + a[i]);
  }
};
Commands.del = function (id) {
  var a = Breakpoint.list;
  if (id == undefined) {
    while (a[0])
      Commands.del (0);
  }
  var b = a[id];
  a.splice (id, 1);
  b.active = false;
};
Commands.delete = Commands.del;
Commands.where = function () {
  var frame = Player.frame;
  if (frame == undefined) {
    Commands.print ("---");
    return;
  }
  var i = 0;
  while (frame) {
    Commands.print (i++ + ": " + frame);
    frame = frame.next;
  }
};
Commands.backtrace = Commands.where;
Commands.bt = Commands.where;
Commands.watch = function () {
  var object;
  var name;
  if (arguments.length == 1) {
    name = arguments[0];
  } else if (arguments.length == 2) {
    object = arguments[0];
    name = arguments[1];
  } else {
    Commands.error ("usage: watch [object] name");
    return;
  }
  var ret = new Breakpoint ();
  ret.onSetVariable = function (o, variable, value) {
    if (object && o != object)
      return false;
    if (variable != name)
      return;

    if (object) {
      Commands.print ("Breakpoint: variable " + name + " on " + object);
    } else {
      Commands.print ("Breakpoint: variable " + name);
    }
    Commands.print ("  " + Player.frame);
    return true;
  };
  ret.toString = function () {
    var s = "watch " + name;
    if (object)
      s += " on " + object;
    return s;
  };
  return ret;
};

add toString () for breakpoints

add toString () for breakpoints