/*
* Copyright (c) 2015 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/

package com.snowplowanalytics.snowplow.tracker.emitter
{
	import com.adobe.net.URI;
	import com.snowplowanalytics.snowplow.tracker.Constants;
	import com.snowplowanalytics.snowplow.tracker.Util;
	import com.snowplowanalytics.snowplow.tracker.event.EmitterEvent;
	import com.snowplowanalytics.snowplow.tracker.payload.IPayload;
	import com.snowplowanalytics.snowplow.tracker.payload.SchemaPayload;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;

	public class Emitter extends EventDispatcher
	{
		private var _uri:URI;
		private var _buffer:Array = [];

		protected var bufferSize:int = BufferOption.DEFAULT;
		protected var httpMethod:String = URLRequestMethod.GET;

		/**
		 * Create an Emitter instance with a collector URL and HttpMethod to send requests.
		 * @param URI The collector URL. Don't include "http://" - this is done automatically.
		 * @param httpMethod The HTTP request method. If GET, BufferOption is set to Instant.
		 * @param protocol The protocol for the call. Either http or https. Defaults to https.
		 */
		public function Emitter(uri:String, httpMethod:String = URLRequestMethod.GET, protocol:String = "https") {

			if (httpMethod == URLRequestMethod.GET) {
				_uri = new URI(protocol + "://" + uri + "/i");
			} else { // POST
				_uri = new URI(protocol + "://" + uri + "/" + Constants.PROTOCOL_VENDOR + "/" + Constants.PROTOCOL_VERSION);
			}

			this.httpMethod = httpMethod;
		}

		/**
		 * Sets whether the buffer should send events instantly or after the buffer has reached
		 * it's limit. By default, this is set to BufferOption Default.
		 * @param option Set the BufferOption enum to Instant send events upon creation.
		 */
		public function setBufferSize(option:int):void {
			this.bufferSize = option;
		}

		/**
		 * Add event payloads to the emitter's buffer
		 * @param payload Payload to be added
		 */
		public function addToBuffer(payload:IPayload):void {
			_buffer.push(payload);
			if (_buffer.length == bufferSize)
				flushBuffer();
		}

		/**
		 * Sends all events in the buffer to the collector.
		 */
		public function checkBufferComplete(successCount:int, totalCount:int, totalPayloads:int, unsentPayloads:Array):void {
			if (totalCount == totalPayloads)
			{
				if (unsentPayloads.length == 0)
				{
					dispatchEvent(new EmitterEvent(EmitterEvent.SUCCESS, successCount));
				}
				else
				{
					dispatchEvent(new EmitterEvent(EmitterEvent.FAILURE, successCount, unsentPayloads, "Not all items in buffer were sent"));
				}
			}
		}

		/**
		 * Sends all events in the buffer to the collector.
		 */
		public function flushBuffer():void {
			if (_buffer.length == 0) {
				trace("Buffer is empty, exiting flush operation.");
				return;
			}

			if (httpMethod == URLRequestMethod.GET) {
				var successCount:int = 0;
				var totalCount:int = 0;
				var totalPayloads:int = _buffer.length;
				var unsentPayloads:Array = [];

				for each (var getPayload:IPayload in _buffer) {
					sendGetData(getPayload,
						function onGetSuccess (data:*):void {
							successCount++;
							totalCount++;
							checkBufferComplete(successCount, totalCount, totalPayloads, unsentPayloads);
						},
						function onGetError ():void {
							totalCount++;
							unsentPayloads.push(payload);
							checkBufferComplete(successCount, totalCount, totalPayloads, unsentPayloads);
						}
					);
				}
			} else if (httpMethod == URLRequestMethod.POST) {
				var unsentPayload:Array = [];

				var postPayload:SchemaPayload = new SchemaPayload();
				postPayload.setSchema(Constants.SCHEMA_PAYLOAD_DATA);

				var eventMaps:Array = [];
				for each (var payload:IPayload in _buffer) {
					eventMaps.push(payload.getMap());
				}

				postPayload.setData(eventMaps);

				sendPostData(postPayload,
					function onPostSuccess (data:*):void
					{
						dispatchEvent(new EmitterEvent(EmitterEvent.SUCCESS, _buffer.length));
					},
					function onPostError (event:Event):void
					{
						unsentPayload.push(postPayload);
						dispatchEvent(new EmitterEvent(EmitterEvent.FAILURE, 0, unsentPayloads, event.toString()));
					}
				);
			}

			// Empties current buffer
			Util.clearArray(_buffer);
		}

		protected function sendPostData(payload:IPayload, successCallback:Function, errorCallback:Function):void
		{
			Util.getResponse(_uri.toString(),
				successCallback,
				errorCallback,
				URLRequestMethod.POST,
				payload.toString());
		}

		protected function sendGetData(payload:IPayload, successCallback:Function, errorCallback:Function):void
		{
			var hashMap:Object = payload.getMap();
			_uri.setQueryByMap(hashMap);

			Util.getResponse(_uri.toString(),
				successCallback,
				errorCallback);
		}
	}
}

/*
* Copyright (c) 2015 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/

package com.snowplowanalytics.snowplow.tracker.emitter
{
	import com.adobe.net.URI;
	import com.snowplowanalytics.snowplow.tracker.Constants;
	import com.snowplowanalytics.snowplow.tracker.Util;
	import com.snowplowanalytics.snowplow.tracker.event.EmitterEvent;
	import com.snowplowanalytics.snowplow.tracker.payload.IPayload;
	import com.snowplowanalytics.snowplow.tracker.payload.SchemaPayload;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;

	public class Emitter extends EventDispatcher
	{
		private var _uri:URI;
		private var _buffer:Array = [];

		protected var bufferSize:int = BufferOption.DEFAULT;
		protected var httpMethod:String = URLRequestMethod.GET;

		/**
		 * Create an Emitter instance with a collector URL and HttpMethod to send requests.
		 * @param URI The collector URL. Don't include "http://" - this is done automatically.
		 * @param httpMethod The HTTP request method. If GET, BufferOption is set to Instant.
		 * @param protocol The protocol for the call. Either http or https. Defaults to http.
		 */
		public function Emitter(uri:String, httpMethod:String = URLRequestMethod.GET, protocol:String = "http") {

			if (httpMethod == URLRequestMethod.GET) {
				_uri = new URI(protocol + "://" + uri + "/i");
			} else { // POST
				_uri = new URI(protocol + "://" + uri + "/" + Constants.PROTOCOL_VENDOR + "/" + Constants.PROTOCOL_VERSION);
			}

			this.httpMethod = httpMethod;
		}

		/**
		 * Sets whether the buffer should send events instantly or after the buffer has reached
		 * it's limit. By default, this is set to BufferOption Default.
		 * @param option Set the BufferOption enum to Instant send events upon creation.
		 */
		public function setBufferSize(option:int):void {
			this.bufferSize = option;
		}

		/**
		 * Add event payloads to the emitter's buffer
		 * @param payload Payload to be added
		 */
		public function addToBuffer(payload:IPayload):void {
			_buffer.push(payload);
			if (_buffer.length == bufferSize)
				flushBuffer();
		}

		/**
		 * Sends all events in the buffer to the collector.
		 */
		public function checkBufferComplete(successCount:int, totalCount:int, totalPayloads:int, unsentPayloads:Array):void {
			if (totalCount == totalPayloads)
			{
				if (unsentPayloads.length == 0)
				{
					dispatchEvent(new EmitterEvent(EmitterEvent.SUCCESS, successCount));
				}
				else
				{
					dispatchEvent(new EmitterEvent(EmitterEvent.FAILURE, successCount, unsentPayloads, "Not all items in buffer were sent"));
				}
			}
		}

		/**
		 * Sends all events in the buffer to the collector.
		 */
		public function flushBuffer():void {
			if (_buffer.length == 0) {
				trace("Buffer is empty, exiting flush operation.");
				return;
			}

			if (httpMethod == URLRequestMethod.GET) {
				var successCount:int = 0;
				var totalCount:int = 0;
				var totalPayloads:int = _buffer.length;
				var unsentPayloads:Array = [];

				for each (var getPayload:IPayload in _buffer) {
					sendGetData(getPayload,
						function onGetSuccess (data:*):void {
							successCount++;
							totalCount++;
							checkBufferComplete(successCount, totalCount, totalPayloads, unsentPayloads);
						},
						function onGetError ():void {
							totalCount++;
							unsentPayloads.push(payload);
							checkBufferComplete(successCount, totalCount, totalPayloads, unsentPayloads);
						}
					);
				}
			} else if (httpMethod == URLRequestMethod.POST) {
				var unsentPayload:Array = [];

				var postPayload:SchemaPayload = new SchemaPayload();
				postPayload.setSchema(Constants.SCHEMA_PAYLOAD_DATA);

				var eventMaps:Array = [];
				for each (var payload:IPayload in _buffer) {
					eventMaps.push(payload.getMap());
				}

				postPayload.setData(eventMaps);

				sendPostData(postPayload,
					function onPostSuccess (data:*):void
					{
						dispatchEvent(new EmitterEvent(EmitterEvent.SUCCESS, _buffer.length));
					},
					function onPostError (event:Event):void
					{
						unsentPayload.push(postPayload);
						dispatchEvent(new EmitterEvent(EmitterEvent.FAILURE, 0, unsentPayloads, event.toString()));
					}
				);
			}

			// Empties current buffer
			Util.clearArray(_buffer);
		}

		protected function sendPostData(payload:IPayload, successCallback:Function, errorCallback:Function):void
		{
			Util.getResponse(_uri.toString(),
				successCallback,
				errorCallback,
				URLRequestMethod.POST,
				payload.toString());
		}

		protected function sendGetData(payload:IPayload, successCallback:Function, errorCallback:Function):void
		{
			var hashMap:Object = payload.getMap();
			_uri.setQueryByMap(hashMap);

			Util.getResponse(_uri.toString(),
				successCallback,
				errorCallback);
		}
	}
}

change Emitter default protocol back to HTTP


package com.mangui.HLS.muxing {

	import com.mangui.HLS.muxing.*;
	import com.mangui.HLS.utils.Log;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	/** Representation of an MPEG transport stream. **/
	public class TS extends EventDispatcher {

		/** TS Sync byte. **/
		public static const SYNCBYTE:uint = 0x47;
		/** TS Packet size in byte. **/
		public static const PACKETSIZE:uint = 188;
		/** Identifier for read complete event **/
		public static const READCOMPLETE:String = "readComplete"
		private static const COUNT:uint = 5000;

		/** Packet ID of the AAC audio stream. **/
		private var _aacId:Number = 257;
		/** List with audio frames. **/
		public var audioTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AAC. **/
		private var _audioPES:Vector.<PES> = new Vector.<PES>();
		/** Packet ID of the video stream. **/
		private var _avcId:Number = 256;
		/** PES packet that contains the first keyframe. **/
		private var _firstKey:Number = -1;
		/** Packet ID of the MP3 audio stream. **/
		private var _mp3Id:Number = -1;
		/** Packet ID of the PAT (is always 0). **/
		private var _patId:Number = 0;
		/** Packet ID of the Program Map Table. **/
		private var _pmtId:Number = -1;
		/** List with video frames. **/
		/** Packet ID of the SDT (is always 17). **/
		private var _sdtId:Number = 17;
		public var videoTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AVC. **/
		private var _videoPES:Vector.<PES> = new Vector.<PES>();
		/** Timer for reading packets **/
		public var _timer:Timer;
		/** Byte data to be read **/
		private var _data:ByteArray;

		/** Transmux the M2TS file into an FLV file. **/
		public function TS(data:ByteArray) {
			// Extract the elementary streams.
			_data = data;
			_timer = new Timer(0,0);
			_timer.addEventListener(TimerEvent.TIMER, _readData);
		};

		/** Read a small chunk of packets each time to avoid blocking **/
		private function _readData(e:Event):void {
			var i:uint = 0;
			while(_data.bytesAvailable && i < COUNT) {
				_readPacket();
				i++;

			}
			if (!_data.bytesAvailable) {
				_timer.stop();
				_extractFrames();
			}
		}

		/** start the timer in order to start reading data **/
		public function startReading():void {;
			_timer.start();
		}

		/** setup the video and audio tag vectors from the read data **/
		private function _extractFrames():void {
			if (_videoPES.length == 0 && _audioPES.length == 0 ) {
				throw new Error("No AAC audio and no AVC video stream found.");
			}
			// Extract the ADTS or MPEG audio frames.
			if(_aacId > 0) {
				_readADTS();
			} else {
				_readMPEG();
			}
			// Extract the NALU video frames.
			_readNALU();
			dispatchEvent(new Event(TS.READCOMPLETE));
		}

		/** Get audio configuration data. **/
		public function getADIF():ByteArray {
			if(_aacId > 0) {
				return AAC.getADIF(_audioPES[0].data,_audioPES[0].payload);
			} else {
				return new ByteArray();
			}
		};

		/** Get video configuration data. **/
		public function getAVCC():ByteArray {
			if(_firstKey == -1) {
				return new ByteArray();
			}
			return AVC.getAVCC(_videoPES[_firstKey].data,_videoPES[_firstKey].payload);
		};

		/** Read ADTS frames from audio PES streams. **/
		private function _readADTS():void {
			var frames:Array;
			var overflow:Number = 0;
			var tag:Tag;
			var stamp:Number;
			for(var i:Number=0; i<_audioPES.length; i++) {
				// Parse the PES headers.
				_audioPES[i].parse();
				// Correct for Segmenter's "optimize", which cuts frames in half.
				if(overflow > 0) {
					_audioPES[i-1].data.position = _audioPES[i-1].data.length;
					_audioPES[i-1].data.writeBytes(_audioPES[i].data,_audioPES[i].payload,overflow);
					_audioPES[i].payload += overflow;
				}
				// Store ADTS frames in array.
				frames = AAC.getFrames(_audioPES[i].data,_audioPES[i].payload);
				for(var j:Number=0; j< frames.length; j++) {
					// Increment the timestamp of subsequent frames.
					stamp = Math.round(_audioPES[i].pts + j * 1024 * 1000 / frames[j].rate);
					tag = new Tag(Tag.AAC_RAW, stamp, stamp, false);
					if(i == _audioPES.length-1 && j == frames.length - 1) {
						tag.push(_audioPES[i].data, frames[j].start, _audioPES[i].data.length - frames[j].start);
					} else {
						tag.push(_audioPES[i].data, frames[j].start, frames[j].length);
					}
					audioTags.push(tag);
				}
				// Correct for Segmenter's "optimize", which cuts frames in half.
				overflow = frames[frames.length-1].start +
					frames[frames.length-1].length - _audioPES[i].data.length;
			}
		};

		/** Read MPEG data from audio PES streams. **/
		private function _readMPEG():void {
			var tag:Tag;
			for(var i:Number=0; i<_audioPES.length; i++) {
				_audioPES[i].parse();
				tag = new Tag(Tag.MP3_RAW, _audioPES[i].pts,_audioPES[i].dts, false);
				tag.push(_audioPES[i].data, _audioPES[i].payload, _audioPES[i].data.length-_audioPES[i].payload);
				audioTags.push(tag);
			}
		};

		/** Read NALU frames from video PES streams. **/
		private function _readNALU():void {
			var overflow:Number;
			var units:Array;
			var last:Number;
			for(var i:Number=0; i<_videoPES.length; i++) {
				// Parse the PES headers and NAL units.
				try {
					_videoPES[i].parse();
				} catch (error:Error) {
					Log.txt(error.message);
					continue;
				}
				units = AVC.getNALU(_videoPES[i].data,_videoPES[i].payload);
				// If there's no NAL unit, push all data in the previous tag.
				if(!units.length) {
					videoTags[videoTags.length-1].push(_videoPES[i].data, _videoPES[i].payload,
						_videoPES[i].data.length - _videoPES[i].payload);
					continue;
				}
				// If NAL units are offset, push preceding data into the previous tag.
				overflow = units[0].start - units[0].header - _videoPES[i].payload;
				if(overflow) {
					videoTags[videoTags.length-1].push(_videoPES[i].data,_videoPES[i].payload,overflow);
				}
				videoTags.push(new Tag(Tag.AVC_NALU,_videoPES[i].pts,_videoPES[i].dts,false));
				// Only push NAL units 1 to 5 into tag.
				for(var j:Number = 0; j < units.length; j++) {
					if (units[j].type < 6) {
						videoTags[videoTags.length-1].push(_videoPES[i].data,units[j].start,units[j].length);
						// Unit type 5 indicates a keyframe.
						if(units[j].type == 5) {
							videoTags[videoTags.length-1].keyframe = true;
							if(_firstKey == -1) {
								_firstKey = i;
							}
						}
					}
				}
			}
		};

		/** Read TS packet. **/
		private function _readPacket():void {
			// Each packet is 188 bytes.
			var todo:uint = TS.PACKETSIZE;
			// Sync byte.
			if(_data.readByte() != TS.SYNCBYTE) {
				throw new Error("Could not parse TS file: sync byte not found.");
			}
			todo--;
			// Payload unit start indicator.
			var stt:uint = (_data.readUnsignedByte() & 64) >> 6;
			_data.position--;

			// Packet ID (last 13 bits of UI16).
			var pid:uint = _data.readUnsignedShort() & 8191;
			// Check for adaptation field.
			todo -=2;
			var atf:uint = (_data.readByte() & 48) >> 4;
			todo --;
			// Read adaptation field if available.
			if(atf > 1) {
				// Length of adaptation field.
				var len:uint = _data.readUnsignedByte();
				todo--;
				// Random access indicator (keyframe).
				//var rai:uint = data.readUnsignedByte() & 64;
				_data.position += len;
				todo -= len;
				// Return if there's only adaptation field.
				if(atf == 2 || len == 183) {
					_data.position += todo;
					return;
				}
			}

			var pes:ByteArray = new ByteArray();
			// Parse the PES, split by Packet ID.
			switch (pid) {
				case _patId:
					todo -= _readPAT();
					break;
				case _pmtId:
					todo -= _readPMT();
					break;
				case _aacId:
				case _mp3Id:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_audioPES.push(new PES(pes,true));
					} else if (_audioPES.length) {
						_audioPES[_audioPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS audio packet with id "+pid);
					}
					break;
				case _avcId:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_videoPES.push(new PES(pes,false));
					} else if (_videoPES.length) {
						_videoPES[_videoPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS video packet with id "+pid);
					}
					break;
				case _sdtId:
						break;
				default:
					// Ignored other packet IDs
					Log.txt("Discarding unassignable TS packets with id "+pid);
					break;
			}
			// Jump to the next packet.
			_data.position += todo;
		};

		/** Read the Program Association Table. **/
		private function _readPAT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			if(_data.readUnsignedByte() > 13) {
				throw new Error("Multiple PMT/NIT entries are not supported.");
			}
			// Grab the PMT ID.
			_data.position += 7;
			_pmtId = _data.readUnsignedShort() & 8191;
			return 13;
		};

		/** Read the Program Map Table. **/
		private function _readPMT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			var len:uint = _data.readByte();
			var read:uint = 13;
			_data.position += 8;
			var pil:Number = _data.readByte();
			_data.position += pil;
			read += pil;
			// Loop through the streams in the PMT.
			while(read < len) {
				var typ:uint = _data.readByte();
				var sid:uint = _data.readUnsignedShort() & 8191;
				if(typ == 0x0F) {
					_aacId = sid;
				} else if (typ == 0x1B) {
					_avcId = sid;
				} else if (typ == 0x03) {
					_mp3Id = sid;
				}
				// Possible section length.
				_data.position++;
				var sel:uint = _data.readByte() & 0x0F;
				_data.position += sel;
				read += sel + 5;
			}
			return len;
		};

	}

}

package com.mangui.HLS.muxing {

	import com.mangui.HLS.muxing.*;
	import com.mangui.HLS.utils.Log;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.ByteArray;
	import flash.utils.Timer;

	/** Representation of an MPEG transport stream. **/
	public class TS extends EventDispatcher {

		/** TS Sync byte. **/
		public static const SYNCBYTE:uint = 0x47;
		/** TS Packet size in byte. **/
		public static const PACKETSIZE:uint = 188;
		/** Identifier for read complete event **/
		public static const READCOMPLETE:String = "readComplete"
		private static const COUNT:uint = 5000;

		/** Packet ID of the AAC audio stream. **/
		private var _aacId:Number = -1;
		/** List with audio frames. **/
		public var audioTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AAC. **/
		private var _audioPES:Vector.<PES> = new Vector.<PES>();
		/** Packet ID of the video stream. **/
		private var _avcId:Number = -1;
		/** PES packet that contains the first keyframe. **/
		private var _firstKey:Number = -1;
		/** Packet ID of the MP3 audio stream. **/
		private var _mp3Id:Number = -1;
		/** Packet ID of the PAT (is always 0). **/
		private var _patId:Number = 0;
		/** Packet ID of the Program Map Table. **/
		private var _pmtId:Number = -1;
		/** List with video frames. **/
		/** Packet ID of the SDT (is always 17). **/
		private var _sdtId:Number = 17;
		public var videoTags:Vector.<Tag> = new Vector.<Tag>();
		/** List of packetized elementary streams with AVC. **/
		private var _videoPES:Vector.<PES> = new Vector.<PES>();
		/** Timer for reading packets **/
		public var _timer:Timer;
		/** Byte data to be read **/
		private var _data:ByteArray;

		/** Transmux the M2TS file into an FLV file. **/
		public function TS(data:ByteArray) {
			// Extract the elementary streams.
			_data = data;
			_timer = new Timer(0,0);
			_timer.addEventListener(TimerEvent.TIMER, _readData);
		};

		/** Read a small chunk of packets each time to avoid blocking **/
		private function _readData(e:Event):void {
			var i:uint = 0;
			while(_data.bytesAvailable && i < COUNT) {
				_readPacket();
				i++;

			}
			if (!_data.bytesAvailable) {
				_timer.stop();
				_extractFrames();
			}
		}

		/** start the timer in order to start reading data **/
		public function startReading():void {;
			_timer.start();
		}

		/** setup the video and audio tag vectors from the read data **/
		private function _extractFrames():void {
			if (_videoPES.length == 0 && _audioPES.length == 0 ) {
				throw new Error("No AAC audio and no AVC video stream found.");
			}
			// Extract the ADTS or MPEG audio frames.
			if(_aacId > 0) {
				_readADTS();
			} else {
				_readMPEG();
			}
			// Extract the NALU video frames.
			_readNALU();
			dispatchEvent(new Event(TS.READCOMPLETE));
		}

		/** Get audio configuration data. **/
		public function getADIF():ByteArray {
			if(_aacId > 0) {
				return AAC.getADIF(_audioPES[0].data,_audioPES[0].payload);
			} else {
				return new ByteArray();
			}
		};

		/** Get video configuration data. **/
		public function getAVCC():ByteArray {
			if(_firstKey == -1) {
				return new ByteArray();
			}
			return AVC.getAVCC(_videoPES[_firstKey].data,_videoPES[_firstKey].payload);
		};

		/** Read ADTS frames from audio PES streams. **/
		private function _readADTS():void {
			var frames:Array;
			var overflow:Number = 0;
			var tag:Tag;
			var stamp:Number;
			for(var i:Number=0; i<_audioPES.length; i++) {
				// Parse the PES headers.
				_audioPES[i].parse();
				// Correct for Segmenter's "optimize", which cuts frames in half.
				if(overflow > 0) {
					_audioPES[i-1].data.position = _audioPES[i-1].data.length;
					_audioPES[i-1].data.writeBytes(_audioPES[i].data,_audioPES[i].payload,overflow);
					_audioPES[i].payload += overflow;
				}
				// Store ADTS frames in array.
				frames = AAC.getFrames(_audioPES[i].data,_audioPES[i].payload);
				for(var j:Number=0; j< frames.length; j++) {
					// Increment the timestamp of subsequent frames.
					stamp = Math.round(_audioPES[i].pts + j * 1024 * 1000 / frames[j].rate);
					tag = new Tag(Tag.AAC_RAW, stamp, stamp, false);
					if(i == _audioPES.length-1 && j == frames.length - 1) {
						tag.push(_audioPES[i].data, frames[j].start, _audioPES[i].data.length - frames[j].start);
					} else {
						tag.push(_audioPES[i].data, frames[j].start, frames[j].length);
					}
					audioTags.push(tag);
				}
				// Correct for Segmenter's "optimize", which cuts frames in half.
				overflow = frames[frames.length-1].start +
					frames[frames.length-1].length - _audioPES[i].data.length;
			}
		};

		/** Read MPEG data from audio PES streams. **/
		private function _readMPEG():void {
			var tag:Tag;
			for(var i:Number=0; i<_audioPES.length; i++) {
				_audioPES[i].parse();
				tag = new Tag(Tag.MP3_RAW, _audioPES[i].pts,_audioPES[i].dts, false);
				tag.push(_audioPES[i].data, _audioPES[i].payload, _audioPES[i].data.length-_audioPES[i].payload);
				audioTags.push(tag);
			}
		};

		/** Read NALU frames from video PES streams. **/
		private function _readNALU():void {
			var overflow:Number;
			var units:Array;
			var last:Number;
			for(var i:Number=0; i<_videoPES.length; i++) {
				// Parse the PES headers and NAL units.
				try {
					_videoPES[i].parse();
				} catch (error:Error) {
					Log.txt(error.message);
					continue;
				}
				units = AVC.getNALU(_videoPES[i].data,_videoPES[i].payload);
				// If there's no NAL unit, push all data in the previous tag.
				if(!units.length) {
					videoTags[videoTags.length-1].push(_videoPES[i].data, _videoPES[i].payload,
						_videoPES[i].data.length - _videoPES[i].payload);
					continue;
				}
				// If NAL units are offset, push preceding data into the previous tag.
				overflow = units[0].start - units[0].header - _videoPES[i].payload;
				if(overflow) {
					videoTags[videoTags.length-1].push(_videoPES[i].data,_videoPES[i].payload,overflow);
				}
				videoTags.push(new Tag(Tag.AVC_NALU,_videoPES[i].pts,_videoPES[i].dts,false));
				// Only push NAL units 1 to 5 into tag.
				for(var j:Number = 0; j < units.length; j++) {
					if (units[j].type < 6) {
						videoTags[videoTags.length-1].push(_videoPES[i].data,units[j].start,units[j].length);
						// Unit type 5 indicates a keyframe.
						if(units[j].type == 5) {
							videoTags[videoTags.length-1].keyframe = true;
							if(_firstKey == -1) {
								_firstKey = i;
							}
						}
					}
				}
			}
		};

		/** Read TS packet. **/
		private function _readPacket():void {
			// Each packet is 188 bytes.
			var todo:uint = TS.PACKETSIZE;
			// Sync byte.
			if(_data.readByte() != TS.SYNCBYTE) {
				throw new Error("Could not parse TS file: sync byte not found.");
			}
			todo--;
			// Payload unit start indicator.
			var stt:uint = (_data.readUnsignedByte() & 64) >> 6;
			_data.position--;

			// Packet ID (last 13 bits of UI16).
			var pid:uint = _data.readUnsignedShort() & 8191;
			// Check for adaptation field.
			todo -=2;
			var atf:uint = (_data.readByte() & 48) >> 4;
			todo --;
			// Read adaptation field if available.
			if(atf > 1) {
				// Length of adaptation field.
				var len:uint = _data.readUnsignedByte();
				todo--;
				// Random access indicator (keyframe).
				//var rai:uint = data.readUnsignedByte() & 64;
				_data.position += len;
				todo -= len;
				// Return if there's only adaptation field.
				if(atf == 2 || len == 183) {
					_data.position += todo;
					return;
				}
			}

			var pes:ByteArray = new ByteArray();
			// Parse the PES, split by Packet ID.
			switch (pid) {
				case _patId:
					todo -= _readPAT();
					break;
				case _pmtId:
					todo -= _readPMT();
					break;
				case _aacId:
				case _mp3Id:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_audioPES.push(new PES(pes,true));
					} else if (_audioPES.length) {
						_audioPES[_audioPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS audio packet with id "+pid);
					}
					break;
				case _avcId:
					if(stt) {
						pes.writeBytes(_data,_data.position,todo);
						_videoPES.push(new PES(pes,false));
					} else if (_videoPES.length) {
						_videoPES[_videoPES.length-1].data.writeBytes(_data,_data.position,todo);
					} else {
						Log.txt("Discarding TS video packet with id "+pid);
					}
					break;
				case _sdtId:
						break;
				default:
					// Ignored other packet IDs
					Log.txt("Discarding unassignable TS packets with id "+pid);
					break;
			}
			// Jump to the next packet.
			_data.position += todo;
		};

		/** Read the Program Association Table. **/
		private function _readPAT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			if(_data.readUnsignedByte() > 13) {
				throw new Error("Multiple PMT/NIT entries are not supported.");
			}
			// Grab the PMT ID.
			_data.position += 7;
			_pmtId = _data.readUnsignedShort() & 8191;
			return 13;
		};

		/** Read the Program Map Table. **/
		private function _readPMT():Number {
			// Check the section length for a single PMT.
			_data.position += 3;
			var len:uint = _data.readByte();
			var read:uint = 13;
			_data.position += 8;
			var pil:Number = _data.readByte();
			_data.position += pil;
			read += pil;
			// Loop through the streams in the PMT.
			while(read < len) {
				var typ:uint = _data.readByte();
				var sid:uint = _data.readUnsignedShort() & 8191;
				if(typ == 0x0F) {
					_aacId = sid;
				} else if (typ == 0x1B) {
					_avcId = sid;
				} else if (typ == 0x03) {
					_mp3Id = sid;
				}
				// Possible section length.
				_data.position++;
				var sel:uint = _data.readByte() & 0x0F;
				_data.position += sel;
				read += sel + 5;
			}
			return len;
		};

	}

}

fix initial pid value, should be undefined (-1)


package aerys.minko.type.enum
{
	import aerys.minko.ns.minko_render;

	import flash.display3D.Context3DBlendFactor;

	/**
	 * The Blending class is an enumeration of the most common values possible
	 * for the Shader.blending property and the BlendingShaderPart.blend() method.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 * @see aerys.minko.type.enum.BlendingSource
	 * @see aerys.minko.type.enum.BlendingDestination
	 * @see aerys.minko.render.shader.Shader
	 * @see aerys.minko.render.shader.part.BlendingShaderPart
	 *
	 */
	public final class Blending
	{
		public static const NORMAL		: uint		= BlendingSource.ONE
													  | BlendingDestination.ZERO;

		public static const ALPHA		: uint		= BlendingSource.SOURCE_ALPHA
													  | BlendingDestination.ONE_MINUS_SOURCE_ALPHA;

		public static const ADDITIVE	: uint		= BlendingSource.SOURCE_ALPHA
													  | BlendingDestination.ONE;

		public static const LIGHT		: uint		= BlendingSource.ZERO
													  | BlendingDestination.SOURCE_COLOR;

		minko_render static const STRINGS : Vector.<String> = new <String>[
			Context3DBlendFactor.DESTINATION_ALPHA,
			Context3DBlendFactor.DESTINATION_COLOR,
			Context3DBlendFactor.ONE,
			Context3DBlendFactor.ONE_MINUS_DESTINATION_ALPHA,
			Context3DBlendFactor.ONE_MINUS_DESTINATION_COLOR,
			Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA,
			Context3DBlendFactor.SOURCE_ALPHA,
			Context3DBlendFactor.SOURCE_COLOR,
			Context3DBlendFactor.ZERO
		];
	}
}

package aerys.minko.type.enum
{
	import aerys.minko.ns.minko_render;

	import flash.display3D.Context3DBlendFactor;

	/**
	 * The Blending class is an enumeration of the most common values possible
	 * for the Shader.blending property and the BlendingShaderPart.blend() method.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 * @see aerys.minko.type.enum.BlendingSource
	 * @see aerys.minko.type.enum.BlendingDestination
	 * @see aerys.minko.render.shader.Shader
	 * @see aerys.minko.render.shader.part.BlendingShaderPart
	 *
	 */
	public final class Blending
	{
		public static const OPAQUE		: uint		= BlendingSource.ONE
													  | BlendingDestination.ZERO;

		public static const ALPHA		: uint		= BlendingSource.SOURCE_ALPHA
													  | BlendingDestination.ONE_MINUS_SOURCE_ALPHA;

//		public static const ADDITIVE	: uint		= BlendingSource.SOURCE_ALPHA
//													  | BlendingDestination.ONE;

        public static const ADDITIVE	: uint		= BlendingSource.DESTINATION_ALPHA
            | BlendingDestination.ONE;

		public static const LIGHT		: uint		= BlendingSource.ZERO
													  | BlendingDestination.SOURCE_COLOR;

		minko_render static const STRINGS : Vector.<String> = new <String>[
			Context3DBlendFactor.DESTINATION_ALPHA,
			Context3DBlendFactor.DESTINATION_COLOR,
			Context3DBlendFactor.ONE,
			Context3DBlendFactor.ONE_MINUS_DESTINATION_ALPHA,
			Context3DBlendFactor.ONE_MINUS_DESTINATION_COLOR,
			Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA,
			Context3DBlendFactor.SOURCE_ALPHA,
			Context3DBlendFactor.SOURCE_COLOR,
			Context3DBlendFactor.ZERO
		];
	}
}

rename Blending.NORMAL to Blending.OPAQUE


/* 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/. */
package org.hola {
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.external.ExternalInterface;
    import flash.net.URLRequest;
    import flash.net.URLStream;
    import flash.utils.ByteArray;
    import flash.utils.getTimer;
    import flash.utils.Timer;
    import org.hola.ZErr;
    import org.hola.Base64;
    import org.mangui.hls.loader.FragmentLoader;

    public dynamic class JSURLStream extends URLStream {
        private var _connected : Boolean;
        private var _resource : ByteArray = new ByteArray();
        public var holaManaged:Boolean = false;
        public static var jsApiInited:Boolean = false;
        public static var reqCount:Number = 0;
        public var req_id:String;
        public static var reqs:Object = {};

        public function JSURLStream(){
            holaManaged = FragmentLoader.g_hls_mode;
            addEventListener(Event.OPEN, onOpen);
            ExternalInterface.marshallExceptions = true;
            super();

            // Connect calls to JS.
            if (ExternalInterface.available && !jsApiInited){
                ZErr.log('JSURLStream init api');
                jsApiInited = true;
                ExternalInterface.addCallback('hola_onFragmentData',
                    hola_onFragmentData);
            }
        }

        protected function _trigger(cb:String, data:Object) : void {
            if (!ExternalInterface.available) {
                // XXX arik: need ZErr.throw
                ZErr.log('invalid trigger');
                throw new Error('invalid trigger');
            }
            ExternalInterface.call('window.hola_'+cb,
                {objectID: ExternalInterface.objectID, data: data});
        }

        override public function get connected() : Boolean {
            if (!holaManaged)
                return super.connected;
            return _connected;
        }

        override public function get bytesAvailable() : uint {
            if (!holaManaged)
                return super.bytesAvailable;
            return _resource.bytesAvailable;
        }

        override public function readByte() : int {
            if (!holaManaged)
                return super.readByte();
            return _resource.readByte();
        }

        override public function readUnsignedShort() : uint {
            if (!holaManaged)
                return super.readUnsignedShort();
            return _resource.readUnsignedShort();
        }

        override public function readBytes(bytes : ByteArray, offset : uint = 0, length : uint = 0) : void {
            if (!holaManaged)
                return super.readBytes(bytes, offset, length);
            _resource.readBytes(bytes, offset, length);
        }

        override public function close() : void {
            if (holaManaged && reqs[req_id])
                _trigger('abortFragment', {req_id: req_id});
            if (super.connected)
                super.close();
            _connected = false;
        }

        override public function load(request : URLRequest) : void {
            // XXX arik: cleanup previous if hola mode changed
            holaManaged = FragmentLoader.g_hls_mode;
            reqCount++;
            req_id = 'req'+reqCount;
            if (!holaManaged)
                return super.load(request);
            reqs[req_id] = this;
            _resource = new ByteArray();
            _trigger('requestFragment', {url: request.url, req_id: req_id});
            this.dispatchEvent(new Event(Event.OPEN));
        }

        private function onOpen(event : Event) : void { _connected = true; }

        protected static function hola_onFragmentData(o:Object):void{
            var stream:JSURLStream;
            try {
                stream = reqs[o.req_id];
                if (!stream)
                    throw new Error('req_id not found '+o.req_id);
                if (o.error)
                {
                    delete reqs[o.req_id];
                    return stream.resourceLoadingError();
                }
                if (o.data)
                {
                    var data:ByteArray = Base64.decode_str(o.data);
                    data.position = 0;
                    if (stream._resource)
                    {
                        var prev:uint = stream._resource.position;
                        data.readBytes(stream._resource,
                            stream._resource.length);
                        stream._resource.position = prev;
                    }
                    else
                        stream._resource = data;
                    // XXX arik: get finalLength from js
                    var finalLength:uint = stream._resource.length;
                    stream.dispatchEvent(new ProgressEvent(
                        ProgressEvent.PROGRESS, false, false,
                        stream._resource.length, finalLength));
                }
                if (o.status)
                {
                    delete reqs[o.req_id];
                    // XXX arik: dispatch httpStatus/httpResponseStatus
                    stream.resourceLoadingSuccess();
                }
            } catch(err:Error){
                ZErr.log('Error in hola_onFragmentData', ''+err,
                    ''+err.getStackTrace());
                delete reqs[o.req_id];
                if (stream)
                    stream.resourceLoadingError();
                throw err;
            }
        }

        protected function resourceLoadingError() : void {
            this.dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
        }

        protected function resourceLoadingSuccess() : void {
            this.dispatchEvent(new Event(Event.COMPLETE));
        }
    }
}

/* 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/. */
package org.hola {
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.external.ExternalInterface;
    import flash.net.URLRequest;
    import flash.net.URLStream;
    import flash.utils.ByteArray;
    import flash.utils.getTimer;
    import flash.utils.Timer;
    import org.hola.ZErr;
    import org.hola.Base64;
    import org.hola.WorkerUtils;
    import org.hola.HEvent;
    import org.mangui.hls.loader.FragmentLoader;

    public dynamic class JSURLStream extends URLStream {
        private var _connected : Boolean;
        private var _resource : ByteArray = new ByteArray();
        private var _curr_data : Object;
        public var holaManaged:Boolean = false;
        public static var jsApiInited:Boolean = false;
        public static var reqCount:Number = 0;
        public var req_id:String;
        public static var reqs:Object = {};

        public function JSURLStream(){
            holaManaged = FragmentLoader.g_hls_mode;
            addEventListener(Event.OPEN, onOpen);
            ExternalInterface.marshallExceptions = true;
            super();
            // Connect calls to JS.
            if (ExternalInterface.available && !jsApiInited){
                ZErr.log('JSURLStream init api');
                jsApiInited = true;
                ExternalInterface.addCallback('hola_onFragmentData',
                    hola_onFragmentData);
            }
        }

        protected function _trigger(cb:String, data:Object) : void {
            if (!ExternalInterface.available) {
                // XXX arik: need ZErr.throw
                ZErr.log('invalid trigger');
                throw new Error('invalid trigger');
            }
            ExternalInterface.call('window.hola_'+cb,
                {objectID: ExternalInterface.objectID, data: data});
        }

        override public function get connected() : Boolean {
            if (!holaManaged)
                return super.connected;
            return _connected;
        }

        override public function get bytesAvailable() : uint {
            if (!holaManaged)
                return super.bytesAvailable;
            return _resource.bytesAvailable;
        }

        override public function readByte() : int {
            if (!holaManaged)
                return super.readByte();
            return _resource.readByte();
        }

        override public function readUnsignedShort() : uint {
            if (!holaManaged)
                return super.readUnsignedShort();
            return _resource.readUnsignedShort();
        }

        override public function readBytes(bytes : ByteArray,
            offset : uint = 0, length : uint = 0) : void
        {
            if (!holaManaged)
                return super.readBytes(bytes, offset, length);
            _resource.readBytes(bytes, offset, length);
        }

        override public function close() : void {
            if (holaManaged)
            {
                if (reqs[req_id])
                    _trigger('abortFragment', {req_id: req_id});
                WorkerUtils.removeEventListener(HEvent.WORKER_MESSAGE, onmsg);
            }
            if (super.connected)
                super.close();
            _connected = false;
        }

        override public function load(request : URLRequest) : void {
            // XXX arik: cleanup previous if hola mode changed
            holaManaged = FragmentLoader.g_hls_mode;
            reqCount++;
            req_id = 'req'+reqCount;
            ZErr.log("load "+req_id);
            if (!holaManaged)
                return super.load(request);
            WorkerUtils.addEventListener(HEvent.WORKER_MESSAGE, onmsg);
            reqs[req_id] = this;
            _resource = new ByteArray();
            _trigger('requestFragment', {url: request.url, req_id: req_id});
            this.dispatchEvent(new Event(Event.OPEN));
        }

        private function onOpen(event : Event) : void { _connected = true; }

        private function decode(str : String) : void {
            var data : ByteArray;
            if (!WorkerUtils.worker)
            {
                if (str)
                    data = Base64.decode_str(str);
                return on_decoded_data(data);
            }
            data = new ByteArray();
            data.shareable = true;
            data.writeUTFBytes(str);
            WorkerUtils.send({cmd: "b64.decode", id: req_id});
            WorkerUtils.send(data);
        }

        private function onmsg(e : HEvent) : void {
            var msg : Object = e.data;
            if (!req_id || req_id!=msg.id || msg.cmd!="b64.decode")
                return;
            var data : ByteArray = WorkerUtils.recv();
            on_decoded_data(data);
        }

        private function on_decoded_data(data : ByteArray) : void {
            if (data)
            {
                data.position = 0;
                if (_resource)
                {
                    var prev:uint = _resource.position;
                    data.readBytes(_resource, _resource.length);
                    _resource.position = prev;
                }
                else
                    _resource = data;
                // XXX arik: get finalLength from js
                var finalLength:uint = _resource.length;
                dispatchEvent(new ProgressEvent( ProgressEvent.PROGRESS, false,
                    false, _resource.length, finalLength));
            }
            // XXX arik: dispatch httpStatus/httpResponseStatus
            if (_curr_data.status)
                resourceLoadingSuccess();
        }

        private function on_fragment_data(o : Object) : void {
            _curr_data = o;
            if (o.error)
                return resourceLoadingError();
            decode(o.data);
        }

        protected static function hola_onFragmentData(o:Object):void{
            var stream:JSURLStream;
            try {
                if (!(stream = reqs[o.req_id]))
                    throw new Error('req_id not found '+o.req_id);
                stream.on_fragment_data(o);
            } catch(err:Error){
                ZErr.log('Error in hola_onFragmentData', ''+err,
                    ''+err.getStackTrace());
                if (stream)
                    stream.resourceLoadingError();
                throw err;
            }
        }

        protected function resourceLoadingError() : void {
            delete reqs[req_id];
            dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
        }

        protected function resourceLoadingSuccess() : void {
            delete reqs[req_id];
            dispatchEvent(new Event(Event.COMPLETE));
        }
    }
}

support base64 decoding in a worker

support base64 decoding in a worker


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

	/**
	 *  The LayoutChangeNotifier notifies layouts when a property
     *  it is watching changes.
	 *
	 *  @langversion 3.0
	 *  @playerversion Flash 10.2
	 *  @playerversion AIR 2.6
	 *  @productversion FlexJS 0.0
	 */
	public class LayoutChangeNotifier implements IBead
	{
		/**
		 *  constructor.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function LayoutChangeNotifier()
		{
		}

		private var _strand:IStrand;

		/**
		 *  @copy org.apache.flex.core.IBead#strand
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function set strand(value:IStrand):void
		{
			_strand = value;
		}

        private var _value:* = undefined;

		/**
		 *  The number of tiles to fit horizontally into the layout.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function set watchedProperty(value:Object):void
		{
			if (_value !== value)
            {
                _value = value;
                if (_strand is IBeadView)
                    IBeadView(_strand).host.dispatchEvent(new Event("layoutNeeded"));
                else
                    IEventDispatcher(_strand).dispatchEvent(new Event("layoutNeeded"));
            }
		}

	}
}

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

	/**
	 *  The LayoutChangeNotifier notifies layouts when a property
     *  it is watching changes.
	 *
	 *  @langversion 3.0
	 *  @playerversion Flash 10.2
	 *  @playerversion AIR 2.6
	 *  @productversion FlexJS 0.0
	 */
	public class LayoutChangeNotifier implements IBead
	{
		/**
		 *  constructor.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function LayoutChangeNotifier()
		{
		}

		private var _strand:IStrand;

		/**
		 *  @copy org.apache.flex.core.IBead#strand
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function set strand(value:IStrand):void
		{
			_strand = value;
		}

        private var _value:* = undefined;

        /**
         *  The value of the property being watched.  This is usually
         *  a data binding expression.
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.6
         *  @productversion FlexJS 0.0
         */
        public function set initialValue(value:Object):void
        {
            _value = value;
        }

		/**
		 *  The value of the property being watched.  This is usually
         *  a data binding expression.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public function set watchedProperty(value:Object):void
		{
			if (_value !== value)
            {
                _value = value;
                if (_strand is IBeadView)
                    IBeadView(_strand).host.dispatchEvent(new Event("layoutNeeded"));
                else
                    IEventDispatcher(_strand).dispatchEvent(new Event("layoutNeeded"));
            }
		}

	}
}

add initial value to prevent a few unnecessary layout passes


package aerys.minko.type.math
{
	import aerys.minko.ns.minko_math;
	import aerys.minko.type.Factory;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.IWatchable;

	import flash.geom.Vector3D;

	public class Vector4 implements IWatchable
	{
		use namespace minko_math;

		public static const X_AXIS				: Vector4	= new Vector4(1., 0., 0.);
		public static const Y_AXIS				: Vector4	= new Vector4(0., 1., 0.);
		public static const Z_AXIS				: Vector4	= new Vector4(0., 0., 1.);
		public static const ZERO				: Vector4	= new Vector4(0., 0., 0., 0.);
		public static const ONE					: Vector4	= new Vector4(1., 1., 1., 1.);
		public static const FORWARD				: Vector4	= Z_AXIS;
		public static const BACKWARD			: Vector4	= new Vector4(0., 0., -1.);
		public static const LEFT				: Vector4	= new Vector4(-1, 0., 0.);
		public static const RIGHT				: Vector4	= new Vector4(1, 0., 0.);
		public static const UP					: Vector4	= Y_AXIS;
		public static const DOWN				: Vector4	= new Vector4(0, -1., 0.);

		private static const FACTORY			: Factory	= Factory.getFactory(Vector4);

		private static const UPDATE_NONE		: uint		= 0;
		private static const UPDATE_LENGTH		: uint		= 1;
		private static const UPDATE_LENGTH_SQ	: uint		= 2;
		private static const UPDATE_ALL			: uint		= 3; // UPDATE_LENGTH | UPDATE_LENGTH_SQ;

		private static const DATA_DESCRIPTOR	: Object	= {
			'x'	: 'x',
			'y'	: 'y',
			'z'	: 'z',
			'w'	: 'w'
		}

		minko_math var _vector	: Vector3D	= new Vector3D();

		private var _update		: uint		= 0;

		private var _length		: Number	= 0.;
		private var _lengthSq	: Number	= 0.;

		private var _locked		: Boolean	= false;
		private var _changed	: Signal	= new Signal('Vector4.changed');

		public function get x()	: Number
		{
			return _vector.x;
		}
		public function set x(value : Number) : void
		{
			if (value != _vector.x)
			{
				_vector.x = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get y()	: Number
		{
			return _vector.y;
		}
		public function set y(value : Number) : void
		{
			if (value != _vector.y)
			{
				_vector.y = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get z() : Number
		{
			return _vector.z;
		}
		public function set z(value : Number) : void
		{
			if (value != _vector.z)
			{
				_vector.z = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get w()	: Number
		{
			return _vector.w;
		}
		public function set w(value : Number) : void
		{
			if (value != _vector.w)
			{
				_vector.w = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get lengthSquared()	: Number
		{
			if (_update & UPDATE_LENGTH_SQ)
			{
				_update ^= UPDATE_LENGTH_SQ;
				_lengthSq = _vector.x * _vector.x + _vector.y * _vector.y
							+ _vector.z * _vector.z;
			}

			return _lengthSq;
		}

		public function get length() : Number
		{
			if (_update & UPDATE_LENGTH)
			{
				_update ^= UPDATE_LENGTH;
				_length = Math.sqrt(lengthSquared);
			}

			return _length;
		}

		public function get locked() : Boolean
		{
			return _locked;
		}

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

		public function Vector4(x 	: Number	= 0.,
								y	: Number	= 0.,
								z	: Number	= 0.,
								w 	: Number	= 1.)
		{
			_vector.x = x;
			_vector.y = y;
			_vector.z = z;
			_vector.w = w;

			_update = UPDATE_ALL;
		}

		public static function add(u 	: Vector4,
								   v 	: Vector4,
								   out	: Vector4 = null) : Vector4
		{
			out = copy(u, out);

			return out.incrementBy(v);
		}

		public static function subtract(u 	: Vector4,
										v 	: Vector4,
										out : Vector4 = null) : Vector4
		{
			out = copy(u, out);

			return out.decrementBy(v);
		}

		public static function dotProduct(u : Vector4, v : Vector4) : Number
		{
			return u._vector.dotProduct(v._vector);
		}

		public static function crossProduct(u : Vector4, v : Vector4) : Vector4
		{
			return new Vector4(
				u._vector.y * v._vector.z - v._vector.y * u._vector.z,
				u._vector.z * v._vector.x - v._vector.z * u._vector.x,
				u._vector.x * v._vector.y - v._vector.x * u._vector.y
			);
		}

		public function compareTo(v : Vector4, allFour : Boolean = false) : Boolean
		{
			return _vector.x == v._vector.x
				   && _vector.y == v._vector.y
				   && _vector.z == v._vector.z
				   && (!allFour || (_vector.w == v._vector.w));
		}

		public static function distance(u : Vector4, v : Vector4) : Number
		{
			return Vector3D.distance(u._vector, v._vector);
		}

		public static function copy(source : Vector4, target : Vector4 = null) : Vector4
		{
			target ||= FACTORY.create() as Vector4;
			target.set(source.x, source.y, source.z, source.w);

			return target;
		}

		public function incrementBy(vector : Vector4) : Vector4
		{
			_vector.incrementBy(vector._vector);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function decrementBy(vector : Vector4) : Vector4
		{
			_vector.decrementBy(vector._vector);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function scaleBy(scale : Number) : Vector4
		{
			_vector.scaleBy(scale);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function project() : Vector4
		{
			_vector.x /= _vector.w;
			_vector.y /= _vector.w;
			_vector.z /= _vector.w;

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function normalize() : Vector4
		{
			var l : Number = length;

			if (l != 0.)
			{
				_vector.x /= l;
				_vector.y /= l;
				_vector.z /= l;

				_length = 1.;
				_lengthSq = 1.;

				_update = UPDATE_NONE;
				_changed.execute(this);
			}

			return this;
		}

		public function crossProduct(vector : Vector4) : Vector4
		{
			var x : Number = _vector.x;
			var y : Number = _vector.y;
			var z : Number = _vector.z;

			_vector.x = y * vector._vector.z - vector._vector.y * z;
			_vector.y = z * vector._vector.x - vector._vector.z * x;
			_vector.z = x * vector._vector.y - vector._vector.x * y;

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function getVector3D() : Vector3D
		{
			return _vector.clone();
		}

		public function set(x : Number,
							y : Number,
							z : Number,
							w : Number = 1.) : Vector4
		{
			if (x != _vector.x || y != _vector.y || z != _vector.z || w != _vector.w)
			{
				_vector.x = x;
				_vector.y = y;
				_vector.z = z;
				_vector.w = w;

				_update = UPDATE_ALL;
				if (!_locked)
					_changed.execute(this);
			}

			return this;
		}

		public function toString() : String
		{
			return "(" + _vector.x + ", " + _vector.y + ", " + _vector.z + ", "
				   + _vector.w + ")";
		}

		public static function scale(v : Vector4, s : Number, out : Vector4 = null) : Vector4
		{
			out ||= FACTORY.create() as Vector4;
			out.set(v._vector.x * s, v._vector.y * s, v._vector.z * s, v._vector.w * s);

			return out;
		}

		public function toVector3D(out : Vector3D = null) : Vector3D
		{
			if (!out)
				return _vector.clone();

			out.x = _vector.x;
			out.y = _vector.y;
			out.z = _vector.z;
			out.w = _vector.w;

			return out;
		}

		public static function normalize(v : Vector4, out : Vector4 = null) : Vector4
		{
			out = copy(v, out);

			return out.normalize();
		}

		public function lock() : void
		{
			_locked = true;
		}

		public function unlock() : void
		{
			_locked = false;
			_changed.execute(this);
		}

		public function clone() : Vector4
		{
			return new Vector4(_vector.x, _vector.y, _vector.z, _vector.w);
		}
	}
}

package aerys.minko.type.math
{
	import aerys.minko.ns.minko_math;
	import aerys.minko.type.Factory;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.IWatchable;

	import flash.geom.Vector3D;

	public class Vector4 implements IWatchable
	{
		use namespace minko_math;

		public static const X_AXIS				: Vector4	= new Vector4(1., 0., 0.);
		public static const Y_AXIS				: Vector4	= new Vector4(0., 1., 0.);
		public static const Z_AXIS				: Vector4	= new Vector4(0., 0., 1.);
		public static const ZERO				: Vector4	= new Vector4(0., 0., 0., 0.);
		public static const ONE					: Vector4	= new Vector4(1., 1., 1., 1.);
		public static const FORWARD				: Vector4	= Z_AXIS;
		public static const BACKWARD			: Vector4	= new Vector4(0., 0., -1.);
		public static const LEFT				: Vector4	= new Vector4(-1, 0., 0.);
		public static const RIGHT				: Vector4	= new Vector4(1, 0., 0.);
		public static const UP					: Vector4	= Y_AXIS;
		public static const DOWN				: Vector4	= new Vector4(0, -1., 0.);

		private static const FACTORY			: Factory	= Factory.getFactory(Vector4);

		private static const UPDATE_NONE		: uint		= 0;
		private static const UPDATE_LENGTH		: uint		= 1;
		private static const UPDATE_LENGTH_SQ	: uint		= 2;
		private static const UPDATE_ALL			: uint		= 3; // UPDATE_LENGTH | UPDATE_LENGTH_SQ;

		private static const DATA_DESCRIPTOR	: Object	= {
			'x'	: 'x',
			'y'	: 'y',
			'z'	: 'z',
			'w'	: 'w'
		}

		minko_math var _vector	: Vector3D	= new Vector3D();

		private var _update		: uint		= 0;

		private var _length		: Number	= 0.;
		private var _lengthSq	: Number	= 0.;

		private var _locked		: Boolean	= false;
		private var _changed	: Signal	= new Signal('Vector4.changed');

		public function get x()	: Number
		{
			return _vector.x;
		}
		public function set x(value : Number) : void
		{
			if (value != _vector.x)
			{
				_vector.x = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get y()	: Number
		{
			return _vector.y;
		}
		public function set y(value : Number) : void
		{
			if (value != _vector.y)
			{
				_vector.y = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get z() : Number
		{
			return _vector.z;
		}
		public function set z(value : Number) : void
		{
			if (value != _vector.z)
			{
				_vector.z = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get w()	: Number
		{
			return _vector.w;
		}
		public function set w(value : Number) : void
		{
			if (value != _vector.w)
			{
				_vector.w = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get lengthSquared()	: Number
		{
			if (_update & UPDATE_LENGTH_SQ)
			{
				_update ^= UPDATE_LENGTH_SQ;
				_lengthSq = _vector.x * _vector.x + _vector.y * _vector.y
							+ _vector.z * _vector.z;
			}

			return _lengthSq;
		}

		public function get length() : Number
		{
			if (_update & UPDATE_LENGTH)
			{
				_update ^= UPDATE_LENGTH;
				_length = Math.sqrt(lengthSquared);
			}

			return _length;
		}

		public function get locked() : Boolean
		{
			return _locked;
		}

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

		public function Vector4(x 	: Number	= 0.,
								y	: Number	= 0.,
								z	: Number	= 0.,
								w 	: Number	= 1.)
		{
			_vector.x = x;
			_vector.y = y;
			_vector.z = z;
			_vector.w = w;

			_update = UPDATE_ALL;
		}

		public static function add(u 	: Vector4,
								   v 	: Vector4,
								   out	: Vector4 = null) : Vector4
		{
			out = copy(u, out);

			return out.incrementBy(v);
		}

		public static function subtract(u 	: Vector4,
										v 	: Vector4,
										out : Vector4 = null) : Vector4
		{
			out = copy(u, out);

			return out.decrementBy(v);
		}

		public static function dotProduct(u : Vector4, v : Vector4) : Number
		{
			return u._vector.dotProduct(v._vector);
		}

		public static function crossProduct(u : Vector4, v : Vector4) : Vector4
		{
			return new Vector4(
				u._vector.y * v._vector.z - v._vector.y * u._vector.z,
				u._vector.z * v._vector.x - v._vector.z * u._vector.x,
				u._vector.x * v._vector.y - v._vector.x * u._vector.y
			);
		}

		public function compareTo(v : Vector4, allFour : Boolean = false) : Boolean
		{
			return _vector.x == v._vector.x
				   && _vector.y == v._vector.y
				   && _vector.z == v._vector.z
				   && (!allFour || (_vector.w == v._vector.w));
		}

		public static function distance(u : Vector4, v : Vector4) : Number
		{
			return Vector3D.distance(u._vector, v._vector);
		}

		public function copyFrom(source : Vector4) : Vector4
		{
			return set(source.x, source.y, source.z, source.w);
		}

		public function incrementBy(vector : Vector4) : Vector4
		{
			_vector.incrementBy(vector._vector);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function decrementBy(vector : Vector4) : Vector4
		{
			_vector.decrementBy(vector._vector);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function scaleBy(scale : Number) : Vector4
		{
			_vector.scaleBy(scale);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function lerp(target : Vector4, ratio : Number) : Vector4
		{
			if (ratio == 1.)
				return copyFrom(target);

			if (ratio != 0.)
			{
				var x : Number = _vector.x;
				var y : Number = _vector.y;
				var z : Number = _vector.z;
				var w : Number = _vector.w;

				set(
					x + (target._vector.x - x) * ratio,
					y + (target._vector.y - y) * ratio,
					z + (target._vector.z - z) * ratio,
					w + (target._vector.w - w) * ratio
				);
			}

			return this;
		}

		public function equals(v : Vector4, allFour : Boolean = false, tolerance : Number = 0.) : Boolean
		{
			return tolerance
				? _vector.nearEquals(v._vector, tolerance, allFour);
				: _vector.equals(v._vector, allFour)
		}

		public function project() : Vector4
		{
			_vector.x /= _vector.w;
			_vector.y /= _vector.w;
			_vector.z /= _vector.w;

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function normalize() : Vector4
		{
			var l : Number = length;

			if (l != 0.)
			{
				_vector.x /= l;
				_vector.y /= l;
				_vector.z /= l;

				_length = 1.;
				_lengthSq = 1.;

				_update = UPDATE_NONE;
				_changed.execute(this);
			}

			return this;
		}

		public function crossProduct(vector : Vector4) : Vector4
		{
			var x : Number = _vector.x;
			var y : Number = _vector.y;
			var z : Number = _vector.z;

			_vector.x = y * vector._vector.z - vector._vector.y * z;
			_vector.y = z * vector._vector.x - vector._vector.z * x;
			_vector.z = x * vector._vector.y - vector._vector.x * y;

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function getVector3D() : Vector3D
		{
			return _vector.clone();
		}

		public function set(x : Number,
							y : Number,
							z : Number,
							w : Number = 1.) : Vector4
		{
			if (x != _vector.x || y != _vector.y || z != _vector.z || w != _vector.w)
			{
				_vector.setTo(x, y, z);
				_vector.w = w;

				_update = UPDATE_ALL;
				if (!_locked)
					_changed.execute(this);
			}

			return this;
		}

		public function toString() : String
		{
			return '(' + _vector.x + ', ' + _vector.y + ', ' + _vector.z + ', ' + _vector.w + ')';
		}

		public static function scale(v : Vector4, s : Number, out : Vector4 = null) : Vector4
		{
			out ||= FACTORY.create() as Vector4;
			out.set(v._vector.x * s, v._vector.y * s, v._vector.z * s, v._vector.w * s);

			return out;
		}

		public function toVector3D(out : Vector3D = null) : Vector3D
		{
			if (!out)
				return _vector.clone();

			out.x = _vector.x;
			out.y = _vector.y;
			out.z = _vector.z;
			out.w = _vector.w;

			return out;
		}

		public static function normalize(v : Vector4, out : Vector4 = null) : Vector4
		{
			out = copy(v, out);

			return out.normalize();
		}

		public function lock() : void
		{
			_locked = true;
		}

		public function unlock() : void
		{
			_locked = false;
			_changed.execute(this);
		}

		public function clone() : Vector4
		{
			return new Vector4(_vector.x, _vector.y, _vector.z, _vector.w);
		}
	}
}

add Vector4.lerp() and remove Vector4::copy(), use new Vector4().copyFrom() instead


package aerys.minko.type.math
{
	import aerys.minko.ns.minko_math;
	import aerys.minko.type.Factory;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.IWatchable;

	import flash.geom.Vector3D;

	final public class Vector4 implements IWatchable
	{
		use namespace minko_math;

		public static const X_AXIS				: Vector4	= new Vector4(1., 0., 0.);
		public static const Y_AXIS				: Vector4	= new Vector4(0., 1., 0.);
		public static const Z_AXIS				: Vector4	= new Vector4(0., 0., 1.);
		public static const ZERO				: Vector4	= new Vector4(0., 0., 0., 0.);
		public static const ONE					: Vector4	= new Vector4(1., 1., 1., 1.);
		public static const FORWARD				: Vector4	= Z_AXIS;
		public static const BACKWARD			: Vector4	= new Vector4(0., 0., -1.);
		public static const LEFT				: Vector4	= new Vector4(-1, 0., 0.);
		public static const RIGHT				: Vector4	= new Vector4(1, 0., 0.);
		public static const UP					: Vector4	= Y_AXIS;
		public static const DOWN				: Vector4	= new Vector4(0, -1., 0.);

		private static const FACTORY			: Factory	= Factory.getFactory(Vector4);

		minko_math static const UPDATE_NONE			: uint		= 0;
		minko_math static const UPDATE_LENGTH		: uint		= 1;
		minko_math static const UPDATE_LENGTH_SQ	: uint		= 2;
		minko_math static const UPDATE_ALL			: uint		= 3; // UPDATE_LENGTH | UPDATE_LENGTH_SQ;

		private static const DATA_DESCRIPTOR	: Object	= {
			'x'	: 'x',
			'y'	: 'y',
			'z'	: 'z',
			'w'	: 'w'
		};

		minko_math var _vector	: Vector3D	= new Vector3D();
		minko_math var _update		: uint		= 0;

		private var _length		: Number	= 0.;
		private var _lengthSq	: Number	= 0.;

		private var _locked		: Boolean	= false;
		private var _changed	: Signal	= new Signal('Vector4.changed');

		public function get x()	: Number
		{
			return _vector.x;
		}
		public function set x(value : Number) : void
		{
			if (value != _vector.x)
			{
				_vector.x = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get y()	: Number
		{
			return _vector.y;
		}
		public function set y(value : Number) : void
		{
			if (value != _vector.y)
			{
				_vector.y = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get z() : Number
		{
			return _vector.z;
		}
		public function set z(value : Number) : void
		{
			if (value != _vector.z)
			{
				_vector.z = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get w()	: Number
		{
			return _vector.w;
		}
		public function set w(value : Number) : void
		{
			if (value != _vector.w)
			{
				_vector.w = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get lengthSquared()	: Number
		{
			if (_update & UPDATE_LENGTH_SQ)
			{
				_update ^= UPDATE_LENGTH_SQ;
				_lengthSq = _vector.x * _vector.x + _vector.y * _vector.y
							+ _vector.z * _vector.z;
			}

			return _lengthSq;
		}

		public function get length() : Number
		{
			if (_update & UPDATE_LENGTH)
			{
				_update ^= UPDATE_LENGTH;
				_length = Math.sqrt(lengthSquared);
			}

			return _length;
		}

		public function get locked() : Boolean
		{
			return _locked;
		}

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

		public function Vector4(x 	: Number	= 0.,
								y	: Number	= 0.,
								z	: Number	= 0.,
								w 	: Number	= 1.)
		{
			_vector.x = x;
			_vector.y = y;
			_vector.z = z;
			_vector.w = w;

			_update = UPDATE_ALL;
		}

		public static function add(u 	: Vector4,
								   v 	: Vector4,
								   out	: Vector4 = null) : Vector4
		{
			out ||= new Vector4();
			out.copyFrom(u);

			return out.incrementBy(v);
		}

		public static function subtract(u 	: Vector4,
										v 	: Vector4,
										out : Vector4 = null) : Vector4
		{
			out ||= new Vector4();
			out.copyFrom(u);

			return out.decrementBy(v);
		}

		public static function dotProduct(u : Vector4, v : Vector4) : Number
		{
			return u._vector.dotProduct(v._vector);
		}

		public static function crossProduct(u : Vector4, v : Vector4, out : Vector4 = null) : Vector4
		{
			out ||= FACTORY.create() as Vector4;

			out.set(
				u._vector.y * v._vector.z - v._vector.y * u._vector.z,
				u._vector.z * v._vector.x - v._vector.z * u._vector.x,
				u._vector.x * v._vector.y - v._vector.x * u._vector.y
			);

			return out;
		}

		public static function distance(u : Vector4, v : Vector4) : Number
		{
			return Vector3D.distance(u._vector, v._vector);
		}

		public function copyFrom(source : Vector4) : Vector4
		{
            var sourceVector : Vector3D = source._vector;

			return set(sourceVector.x, sourceVector.y, sourceVector.z, sourceVector.w);
		}

		public function incrementBy(vector : Vector4) : Vector4
		{
			_vector.incrementBy(vector._vector);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function decrementBy(vector : Vector4) : Vector4
		{
			_vector.decrementBy(vector._vector);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function scaleBy(scale : Number) : Vector4
		{
			_vector.scaleBy(scale);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function lerp(target : Vector4, ratio : Number) : Vector4
		{
			if (ratio == 1.)
				return copyFrom(target);

			if (ratio != 0.)
			{
				var x : Number = _vector.x;
				var y : Number = _vector.y;
				var z : Number = _vector.z;
				var w : Number = _vector.w;

				set(
					x + (target._vector.x - x) * ratio,
					y + (target._vector.y - y) * ratio,
					z + (target._vector.z - z) * ratio,
					w + (target._vector.w - w) * ratio
				);
			}

			return this;
		}

		public function equals(v : Vector4, allFour : Boolean = false, tolerance : Number = 0.) : Boolean
		{
			return tolerance
				? _vector.nearEquals(v._vector, tolerance, allFour)
				: _vector.equals(v._vector, allFour);
		}

		public function project() : Vector4
		{
			_vector.x /= _vector.w;
			_vector.y /= _vector.w;
			_vector.z /= _vector.w;

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function normalize() : Vector4
		{
			var l : Number = length;

			if (l != 0.)
			{
				_vector.x /= l;
				_vector.y /= l;
				_vector.z /= l;

				_length = 1.;
				_lengthSq = 1.;

				_update = UPDATE_NONE;
				_changed.execute(this);
			}

			return this;
		}

		public function crossProduct(vector : Vector4) : Vector4
		{
			var x : Number = _vector.x;
			var y : Number = _vector.y;
			var z : Number = _vector.z;

			_vector.x = y * vector._vector.z - vector._vector.y * z;
			_vector.y = z * vector._vector.x - vector._vector.z * x;
			_vector.z = x * vector._vector.y - vector._vector.x * y;

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function getVector3D() : Vector3D
		{
			return _vector.clone();
		}

		public function set(x : Number,
							y : Number,
							z : Number,
							w : Number = 1.) : Vector4
		{
			if (x != _vector.x || y != _vector.y || z != _vector.z || w != _vector.w)
			{
				_vector.setTo(x, y, z);
				_vector.w = w;

				_update = UPDATE_ALL;
				if (!_locked)
					_changed.execute(this);
			}

			return this;
		}

		public function toString() : String
		{
			return '(' + _vector.x + ', ' + _vector.y + ', ' + _vector.z + ', ' + _vector.w + ')';
		}

		public static function scale(v : Vector4, s : Number, out : Vector4 = null) : Vector4
		{
			out ||= FACTORY.create() as Vector4;
			out.set(v._vector.x * s, v._vector.y * s, v._vector.z * s, v._vector.w * s);

			return out;
		}

		public function toVector3D(out : Vector3D = null) : Vector3D
		{
			if (!out)
				return _vector.clone();

			out.x = _vector.x;
			out.y = _vector.y;
			out.z = _vector.z;
			out.w = _vector.w;

			return out;
		}

		public function lock() : void
		{
			_locked = true;
		}

		public function unlock() : void
		{
			_locked = false;
			_changed.execute(this);
		}

		public function clone() : Vector4
		{
			return new Vector4(_vector.x, _vector.y, _vector.z, _vector.w);
		}
	}
}

package aerys.minko.type.math
{
	import aerys.minko.ns.minko_math;
	import aerys.minko.type.Factory;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.IWatchable;

	import flash.geom.Vector3D;

	final public class Vector4 implements IWatchable
	{
		use namespace minko_math;

		public static const X_AXIS				: Vector4	= new Vector4(1., 0., 0.);
		public static const Y_AXIS				: Vector4	= new Vector4(0., 1., 0.);
		public static const Z_AXIS				: Vector4	= new Vector4(0., 0., 1.);
		public static const ZERO				: Vector4	= new Vector4(0., 0., 0., 0.);
		public static const ONE					: Vector4	= new Vector4(1., 1., 1., 1.);
		public static const FORWARD				: Vector4	= Z_AXIS;
		public static const BACKWARD			: Vector4	= new Vector4(0., 0., -1.);
		public static const LEFT				: Vector4	= new Vector4(-1, 0., 0.);
		public static const RIGHT				: Vector4	= new Vector4(1, 0., 0.);
		public static const UP					: Vector4	= Y_AXIS;
		public static const DOWN				: Vector4	= new Vector4(0, -1., 0.);

		private static const FACTORY			: Factory	= Factory.getFactory(Vector4);

		minko_math static const UPDATE_NONE			: uint		= 0;
		minko_math static const UPDATE_LENGTH		: uint		= 1;
		minko_math static const UPDATE_LENGTH_SQ	: uint		= 2;
		minko_math static const UPDATE_ALL			: uint		= 3; // UPDATE_LENGTH | UPDATE_LENGTH_SQ;

		private static const DATA_DESCRIPTOR	: Object	= {
			'x'	: 'x',
			'y'	: 'y',
			'z'	: 'z',
			'w'	: 'w'
		};

		minko_math var _vector	: Vector3D	= new Vector3D();
		minko_math var _update		: uint		= 0;

		private var _length		: Number	= 0.;
		private var _lengthSq	: Number	= 0.;

		private var _locked		: Boolean	= false;
		private var _changed	: Signal	= new Signal('Vector4.changed');

		public function get x()	: Number
		{
			return _vector.x;
		}
		public function set x(value : Number) : void
		{
			if (value != _vector.x)
			{
				_vector.x = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get y()	: Number
		{
			return _vector.y;
		}
		public function set y(value : Number) : void
		{
			if (value != _vector.y)
			{
				_vector.y = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get z() : Number
		{
			return _vector.z;
		}
		public function set z(value : Number) : void
		{
			if (value != _vector.z)
			{
				_vector.z = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get w()	: Number
		{
			return _vector.w;
		}
		public function set w(value : Number) : void
		{
			if (value != _vector.w)
			{
				_vector.w = value;
				_update = UPDATE_ALL;

				if (!_locked)
					_changed.execute(this);
			}
		}

		public function get lengthSquared()	: Number
		{
			if (_update & UPDATE_LENGTH_SQ)
			{
				_update ^= UPDATE_LENGTH_SQ;
				_lengthSq = _vector.x * _vector.x + _vector.y * _vector.y
							+ _vector.z * _vector.z;
			}

			return _lengthSq;
		}

		public function get length() : Number
		{
			if (_update & UPDATE_LENGTH)
			{
				_update ^= UPDATE_LENGTH;
				_length = Math.sqrt(lengthSquared);
			}

			return _length;
		}

		public function get locked() : Boolean
		{
			return _locked;
		}

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

		public function Vector4(x 	: Number	= 0.,
								y	: Number	= 0.,
								z	: Number	= 0.,
								w 	: Number	= 1.)
		{
			_vector.x = x;
			_vector.y = y;
			_vector.z = z;
			_vector.w = w;

			_update = UPDATE_ALL;
		}

		public static function add(u 	: Vector4,
								   v 	: Vector4,
								   out	: Vector4 = null) : Vector4
		{
			out ||= new Vector4();
			out.copyFrom(u);

			return out.incrementBy(v);
		}

		public static function subtract(u 	: Vector4,
										v 	: Vector4,
										out : Vector4 = null) : Vector4
		{
			out ||= new Vector4();
			out.copyFrom(u);

			return out.decrementBy(v);
		}

		public static function dotProduct(u : Vector4, v : Vector4) : Number
		{
			return u._vector.dotProduct(v._vector);
		}

		public static function crossProduct(u : Vector4, v : Vector4, out : Vector4 = null) : Vector4
		{
			out ||= FACTORY.create() as Vector4;

			out.set(
				u._vector.y * v._vector.z - v._vector.y * u._vector.z,
				u._vector.z * v._vector.x - v._vector.z * u._vector.x,
				u._vector.x * v._vector.y - v._vector.x * u._vector.y
			);

			return out;
		}

		public static function distance(u : Vector4, v : Vector4) : Number
		{
			return Vector3D.distance(u._vector, v._vector);
		}

		public function copyFrom(source : Vector4) : Vector4
		{
            var sourceVector : Vector3D = source._vector;

			return set(sourceVector.x, sourceVector.y, sourceVector.z, sourceVector.w);
		}

		public function incrementBy(vector : Vector4) : Vector4
		{
			_vector.incrementBy(vector._vector);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function decrementBy(vector : Vector4) : Vector4
		{
			_vector.decrementBy(vector._vector);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function scaleBy(scale : Number) : Vector4
		{
			_vector.scaleBy(scale);

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function lerp(target : Vector4, ratio : Number) : Vector4
		{
			if (ratio == 1.)
				return copyFrom(target);

			if (ratio != 0.)
			{
				var x : Number = _vector.x;
				var y : Number = _vector.y;
				var z : Number = _vector.z;
				var w : Number = _vector.w;

				set(
					x + (target._vector.x - x) * ratio,
					y + (target._vector.y - y) * ratio,
					z + (target._vector.z - z) * ratio,
					w + (target._vector.w - w) * ratio
				);
			}

			return this;
		}

		public function equals(v : Vector4, allFour : Boolean = false, tolerance : Number = 0.) : Boolean
		{
			return tolerance
				? _vector.nearEquals(v._vector, tolerance, allFour)
				: _vector.equals(v._vector, allFour);
		}

		public function project() : Vector4
		{
			_vector.x /= _vector.w;
			_vector.y /= _vector.w;
			_vector.z /= _vector.w;

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function normalize() : Vector4
		{
			var l : Number = length;

			if (l != 0.)
			{
				_vector.x /= l;
				_vector.y /= l;
				_vector.z /= l;

				_length = 1.;
				_lengthSq = 1.;

				_update = UPDATE_NONE;
				_changed.execute(this);
			}

			return this;
		}

		public function crossProduct(vector : Vector4) : Vector4
		{
			var x : Number = _vector.x;
			var y : Number = _vector.y;
			var z : Number = _vector.z;

			_vector.x = y * vector._vector.z - vector._vector.y * z;
			_vector.y = z * vector._vector.x - vector._vector.z * x;
			_vector.z = x * vector._vector.y - vector._vector.x * y;

			_update = UPDATE_ALL;
			_changed.execute(this);

			return this;
		}

		public function getVector3D() : Vector3D
		{
			return _vector.clone();
		}

		public function set(x : Number,
							y : Number,
							z : Number,
							w : Number = 1.) : Vector4
		{
			if (x != _vector.x || y != _vector.y || z != _vector.z || w != _vector.w)
			{
				_vector.setTo(x, y, z);
				_vector.w = w;

				_update = UPDATE_ALL;
				if (!_locked)
					_changed.execute(this);
			}

			return this;
		}

		public function toString() : String
		{
			return '(' + _vector.x + ', ' + _vector.y + ', ' + _vector.z + ', ' + _vector.w + ')';
		}

		public static function scale(v : Vector4, s : Number, out : Vector4 = null) : Vector4
		{
			out ||= FACTORY.create() as Vector4;
			out.set(v._vector.x * s, v._vector.y * s, v._vector.z * s, v._vector.w * s);

			return out;
		}

		public static function fromVector3D(vector : Vector3D, out : Vector4 = null) : Vector4
		{
			out ||= FACTORY.create() as Vector4;

			out.x = vector.x;
			out.y = vector.y;
			out.z = vector.z;
			out.w = vector.w;

			return out;
		}

		public function toVector3D(out : Vector3D = null) : Vector3D
		{
			if (!out)
				return _vector.clone();

			out.x = _vector.x;
			out.y = _vector.y;
			out.z = _vector.z;
			out.w = _vector.w;

			return out;
		}

		public function lock() : void
		{
			_locked = true;
		}

		public function unlock() : void
		{
			_locked = false;
			_changed.execute(this);
		}

		public function clone() : Vector4
		{
			return new Vector4(_vector.x, _vector.y, _vector.z, _vector.w);
		}
	}
}

Add Vector4.fromVector3D().

Add Vector4.fromVector3D().