/*
* 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
{
	import com.snowplowanalytics.snowplow.tracker.emitter.Emitter;
	import com.snowplowanalytics.snowplow.tracker.payload.IPayload;
	import com.snowplowanalytics.snowplow.tracker.payload.SchemaPayload;
	import com.snowplowanalytics.snowplow.tracker.payload.TrackerPayload;
	import com.snowplowanalytics.snowplow.tracker.util.Preconditions;

	import flash.display.Stage;
	import flash.external.ExternalInterface;
	import flash.net.LocalConnection;
	import flash.net.SharedObject;
	import flash.system.Capabilities;

	public class Tracker
	{
		private var base64Encoded:Boolean = true;
		private var emitter:Emitter;
		private var platform:String;
		private var appId:String;
		private var namespace:String;
		private var trackerVersion:String;
		private var subject:Subject;
		private var stage:Stage;

		private var playerType:String;
		private var playerVersion:String;
		private var isDebugger:Boolean;
		private var hasLocalStorage:Boolean;
		private var hasScriptAccess:Boolean;

		/**
		 * @param emitter Emitter to which events will be sent
		 * @param subject Subject to be tracked
		 * @param namespace Identifier for the Tracker instance
		 * @param appId Application ID
		 * @param stage The flash stage object.  used for adding stage info to payload.
		 * @param base64Encoded Whether JSONs in the payload should be base-64 encoded
		 */
		function Tracker(emitter:Emitter, namespace:String, appId:String, subject:Subject = null, stage:Stage = null, base64Encoded:Boolean = true) {
				this.emitter = emitter;
				this.appId = appId;
				this.base64Encoded = base64Encoded;
				this.namespace = namespace;
				this.subject = subject;
				this.trackerVersion = Version.TRACKER;
				this.platform = DevicePlatform.WEB;
				this.stage = stage;

				this.playerType = Capabilities.playerType;
				this.playerVersion = Capabilities.version;
				this.isDebugger = Capabilities.isDebugger;

				try
				{
					SharedObject.getLocal("test");
					this.hasLocalStorage = true;
				}
				catch (e:Error)
				{
					this.hasLocalStorage = false;
				}
				this.hasScriptAccess = ExternalInterface.available;
			}

		/**
		 * @param payload Payload builder
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 * @return A completed Payload
		 */
		protected function completePayload(payload:IPayload,
										   context:Array,
										   timestamp:Number):IPayload {

				payload.add(Parameter.PLATFORM, this.platform);
				payload.add(Parameter.APPID, this.appId);
				payload.add(Parameter.NAMESPACE, this.namespace);
				payload.add(Parameter.TRACKER_VERSION, this.trackerVersion);
				payload.add(Parameter.EID, Util.getEventId());

				// If timestamp is set to 0, generate one
				payload.add(Parameter.TIMESTAMP,
					(timestamp == 0 ? Util.getTimestamp() : String(timestamp)));

				// Add flash information
				if (context == null) {
					context = [];
				}

				var flashData:TrackerPayload = new TrackerPayload();
				flashData.add(Parameter.FLASH_PLAYER_TYPE, playerType);
				flashData.add(Parameter.FLASH_VERSION, playerVersion);
				flashData.add(Parameter.FLASH_IS_DEBUGGER, isDebugger);
				flashData.add(Parameter.FLASH_HAS_LOCAL_STORAGE, hasLocalStorage);
				flashData.add(Parameter.FLASH_HAS_SCRIPT_ACCESS, hasScriptAccess);
				if (stage != null) {
					flashData.add(Parameter.FLASH_STAGE_SIZE, { "width": stage.stageWidth, "height": stage.stageHeight});
				}

				var flashPayload:SchemaPayload = new SchemaPayload();
				flashPayload.setSchema(Constants.SCHEMA_FLASH);
				flashPayload.setData(flashData.getMap());

				context.push(flashPayload);

				// Encodes context data
				if (context != null && context.length > 0) {
					var envelope:SchemaPayload = new SchemaPayload();
					envelope.setSchema(Constants.SCHEMA_CONTEXTS);

					// We can do better here, rather than re-iterate through the list
					var contextDataList:Array = [];
					for each(var schemaPayload:SchemaPayload in context) {
						contextDataList.push(schemaPayload.getMap());
					}

					envelope.setData(contextDataList);
					payload.addMap(envelope.getMap(), this.base64Encoded, Parameter.CONTEXT_ENCODED, Parameter.CONTEXT);
				}

				if (this.subject != null) {
					payload.addMap(Util.copyObject(subject.getSubject(), true));
				}

				return payload;
			}

		public function setPlatform(platform:String):void {
			this.platform = platform;
		}

		public function getPlatform():String {
			return this.platform;
		}

		protected function setTrackerVersion(version:String):void {
			this.trackerVersion = version;
		}

		private function addTrackerPayload(payload:IPayload):void {
			this.emitter.addToBuffer(payload);
		}

		public function setSubject(subject:Subject):void {
			this.subject = subject;
		}

		public function getSubject():Subject {
			return this.subject;
		}

		/**
		 * @param pageUrl URL of the viewed page
		 * @param pageTitle Title of the viewed page
		 * @param referrer Referrer of the page
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
//		public function trackPageView(pageUrl:String, pageTitle:String, referrer:String, context:Array = null, timestamp:Number = 0):void
//		{
//			// Precondition checks
//			Preconditions.checkNotNull(pageUrl);
//			Preconditions.checkArgument(!Util.isNullOrEmpty(pageUrl), "pageUrl cannot be empty");
//			Preconditions.checkArgument(!Util.isNullOrEmpty(pageTitle), "pageTitle cannot be empty");
//			Preconditions.checkArgument(!Util.isNullOrEmpty(referrer), "referrer cannot be empty");
//
//			var payload:IPayload = new TrackerPayload();
//			payload.add(Parameter.EVENT, Constants.EVENT_PAGE_VIEW);
//			payload.add(Parameter.PAGE_URL, pageUrl);
//			payload.add(Parameter.PAGE_TITLE, pageTitle);
//			payload.add(Parameter.PAGE_REFR, referrer);
//
//			completePayload(payload, context, timestamp);
//
//			addTrackerPayload(payload);
//		}

		/**
		 * @param category Category of the event
		 * @param action The event itself
		 * @param label Refer to the object the action is performed on
		 * @param property Property associated with either the action or the object
		 * @param value A value associated with the user action
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackStructuredEvent(category:String,
			action:String,
			label:String,
			property:String,
			value:int,
			context:Array = null,
			timestamp:Number = 0):void
		{
			// Precondition checks
			Preconditions.checkNotNull(label);
			Preconditions.checkNotNull(property);
			Preconditions.checkArgument(!Util.isNullOrEmpty(label), "label cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(property), "property cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(category), "category cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(action), "action cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_STRUCTURED);
			payload.add(Parameter.SE_CATEGORY, category);
			payload.add(Parameter.SE_ACTION, action);
			payload.add(Parameter.SE_LABEL, label);
			payload.add(Parameter.SE_PROPERTY, property);
			payload.add(Parameter.SE_VALUE, String(value));

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 *
		 * @param eventData The properties of the event. Has two field:
		 *                   A "data" field containing the event properties and
		 *                   A "schema" field identifying the schema against which the data is validated
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackUnstructuredEvent(eventData:SchemaPayload, context:Array = null,
			timestamp:Number = 0):void
		{
			var envelope:SchemaPayload = new SchemaPayload();
			envelope.setSchema(Constants.SCHEMA_UNSTRUCT_EVENT);
			envelope.setData(eventData.getMap());

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_UNSTRUCTURED);
			payload.addMap(envelope.getMap(), base64Encoded,
				Parameter.UNSTRUCTURED_ENCODED, Parameter.UNSTRUCTURED);

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 * This is an internal method called by track_ecommerce_transaction. It is not for public use.
		 * @param order_id Order ID
		 * @param sku Item SKU
		 * @param price Item price
		 * @param quantity Item quantity
		 * @param name Item name
		 * @param category Item category
		 * @param currency The currency the price is expressed in
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		protected function trackEcommerceTransactionItem(order_id:String, sku:String, price:Number,
			quantity:int, name:String, category:String,
			currency:String, context:Array,
			timestamp:Number):void
		{
			// Precondition checks
			Preconditions.checkNotNull(name);
			Preconditions.checkNotNull(category);
			Preconditions.checkNotNull(currency);
			Preconditions.checkArgument(!Util.isNullOrEmpty(order_id), "order_id cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(sku), "sku cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(name), "name cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(category), "category cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(currency), "currency cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_ECOMM_ITEM);
			payload.add(Parameter.TI_ITEM_ID, order_id);
			payload.add(Parameter.TI_ITEM_SKU, sku);
			payload.add(Parameter.TI_ITEM_NAME, name);
			payload.add(Parameter.TI_ITEM_CATEGORY, category);
			payload.add(Parameter.TI_ITEM_PRICE, String(price));
			payload.add(Parameter.TI_ITEM_QUANTITY, String(quantity));
			payload.add(Parameter.TI_ITEM_CURRENCY, currency);

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 * @param order_id ID of the eCommerce transaction
		 * @param total_value Total transaction value
		 * @param affiliation Transaction affiliation
		 * @param tax_value Transaction tax value
		 * @param shipping Delivery cost charged
		 * @param city Delivery address city
		 * @param state Delivery address state
		 * @param country Delivery address country
		 * @param currency The currency the price is expressed in
		 * @param items The items in the transaction
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackEcommerceTransaction(order_id:String, total_value:Number, affiliation:String,
			tax_value:Number, shipping:Number, city:String,
			state:String, country:String, currency:String,
			items:Array, context:Array = null,
			timestamp:Number = 0):void
		{
			// Precondition checks
			Preconditions.checkNotNull(affiliation);
			Preconditions.checkNotNull(city);
			Preconditions.checkNotNull(state);
			Preconditions.checkNotNull(country);
			Preconditions.checkNotNull(currency);
			Preconditions.checkArgument(!Util.isNullOrEmpty(order_id), "order_id cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(affiliation), "affiliation cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(city), "city cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(state), "state cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(country), "country cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(currency), "currency cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_ECOMM);
			payload.add(Parameter.TR_ID, order_id);
			payload.add(Parameter.TR_TOTAL, String(total_value));
			payload.add(Parameter.TR_AFFILIATION, affiliation);
			payload.add(Parameter.TR_TAX, String(tax_value));
			payload.add(Parameter.TR_SHIPPING, String(shipping));
			payload.add(Parameter.TR_CITY, city);
			payload.add(Parameter.TR_STATE, state);
			payload.add(Parameter.TR_COUNTRY, country);
			payload.add(Parameter.TR_CURRENCY, currency);

			completePayload(payload, context, timestamp);

			for each(var item:TransactionItem in items) {
				trackEcommerceTransactionItem(
					String(item.get(Parameter.TI_ITEM_ID)),
					String(item.get(Parameter.TI_ITEM_SKU)),
					Number(item.get(Parameter.TI_ITEM_PRICE)),
					parseInt(item.get(Parameter.TI_ITEM_QUANTITY)),
					String(item.get(Parameter.TI_ITEM_NAME)),
					String(item.get(Parameter.TI_ITEM_CATEGORY)),
					String(item.get(Parameter.TI_ITEM_CURRENCY)),
					item.get(Parameter.CONTEXT),
					timestamp);
			}

			addTrackerPayload(payload);
		}

		/**
		 * @param name The name of the screen view event
		 * @param id Screen view ID
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackScreenView(name:String, id:String, context:Array,
			timestamp:Number):void {
				Preconditions.checkArgument(name != null || id != null);
				var trackerPayload:TrackerPayload = new TrackerPayload();

				trackerPayload.add(Parameter.SV_NAME, name);
				trackerPayload.add(Parameter.SV_ID, id);

				var payload:SchemaPayload = new SchemaPayload();
				payload.setSchema(Constants.SCHEMA_SCREEN_VIEW);
				payload.setData(trackerPayload);

				trackUnstructuredEvent(payload, context, timestamp);
			}
	}
}

/*
* 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
{
	import com.adobe.net.URI;
	import com.snowplowanalytics.snowplow.tracker.emitter.Emitter;
	import com.snowplowanalytics.snowplow.tracker.payload.IPayload;
	import com.snowplowanalytics.snowplow.tracker.payload.SchemaPayload;
	import com.snowplowanalytics.snowplow.tracker.payload.TrackerPayload;
	import com.snowplowanalytics.snowplow.tracker.util.Preconditions;

	import flash.display.Stage;
	import flash.external.ExternalInterface;
	import flash.net.LocalConnection;
	import flash.net.SharedObject;
	import flash.system.Capabilities;

	public class Tracker
	{
		private var base64Encoded:Boolean = true;
		private var emitter:Emitter;
		private var platform:String;
		private var appId:String;
		private var namespace:String;
		private var trackerVersion:String;
		private var subject:Subject;
		private var stage:Stage;

		private var playerType:String;
		private var playerVersion:String;
		private var isDebugger:Boolean;
		private var hasLocalStorage:Boolean;
		private var hasScriptAccess:Boolean;

		private var pageUrl:String = "UNKNOWN";
		private var pageTitle:String = "UNKNOWN";
		private var referrer:String = "UNKNOWN";

		/**
		 * @param emitter Emitter to which events will be sent
		 * @param subject Subject to be tracked
		 * @param namespace Identifier for the Tracker instance
		 * @param appId Application ID
		 * @param stage The flash stage object.  used for adding stage info to payload.
		 * @param base64Encoded Whether JSONs in the payload should be base-64 encoded
		 */
		function Tracker(emitter:Emitter, namespace:String, appId:String, subject:Subject = null, stage:Stage = null, base64Encoded:Boolean = true) {
			this.emitter = emitter;
			this.appId = appId;
			this.base64Encoded = base64Encoded;
			this.namespace = namespace;
			this.subject = subject;
			this.trackerVersion = Version.TRACKER;
			this.platform = DevicePlatform.WEB;
			this.stage = stage;

			this.playerType = Capabilities.playerType;
			this.playerVersion = Capabilities.version;
			this.isDebugger = Capabilities.isDebugger;

			try
			{
				SharedObject.getLocal("test");
				this.hasLocalStorage = true;
			}
			catch (e:Error)
			{
				this.hasLocalStorage = false;
			}
			this.hasScriptAccess = ExternalInterface.available;

			if (this.hasScriptAccess) {
				try { pageUrl = ExternalInterface.call("function getPageUrl() { return document.location.href; }"); } catch(e:Error) { pageUrl = "UNKNOWN"; }
				try { pageTitle = ExternalInterface.call("function getPageTitle() { return document.title; }"); } catch(e:Error) { pageTitle = "UNKNOWN"; }
				try { referrer = ExternalInterface.call("function getReferrer() { return document.referrer; }"); } catch(e:Error) { referrer = "UNKNOWN"; }
			}
		}

		/**
		 * @param payload Payload builder
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 * @return A completed Payload
		 */
		protected function completePayload(payload:IPayload,
										   context:Array,
										   timestamp:Number):IPayload {

				payload.add(Parameter.PLATFORM, this.platform);
				payload.add(Parameter.APPID, this.appId);
				payload.add(Parameter.NAMESPACE, this.namespace);
				payload.add(Parameter.TRACKER_VERSION, this.trackerVersion);
				payload.add(Parameter.EID, Util.getEventId());

				//Add page data
				payload.add(Parameter.PAGE_URL, pageUrl);
				payload.add(Parameter.PAGE_TITLE, pageTitle);
				payload.add(Parameter.PAGE_REFR, referrer);

				// If timestamp is set to 0, generate one
				payload.add(Parameter.TIMESTAMP,
					(timestamp == 0 ? Util.getTimestamp() : String(timestamp)));

				// Add flash information
				if (context == null) {
					context = [];
				}

				var flashData:TrackerPayload = new TrackerPayload();
				flashData.add(Parameter.FLASH_PLAYER_TYPE, playerType);
				flashData.add(Parameter.FLASH_VERSION, playerVersion);
				flashData.add(Parameter.FLASH_IS_DEBUGGER, isDebugger);
				flashData.add(Parameter.FLASH_HAS_LOCAL_STORAGE, hasLocalStorage);
				flashData.add(Parameter.FLASH_HAS_SCRIPT_ACCESS, hasScriptAccess);
				if (stage != null) {
					flashData.add(Parameter.FLASH_STAGE_SIZE, { "width": stage.stageWidth, "height": stage.stageHeight});
				}

				var flashPayload:SchemaPayload = new SchemaPayload();
				flashPayload.setSchema(Constants.SCHEMA_FLASH);
				flashPayload.setData(flashData.getMap());

				context.push(flashPayload);

				// Encodes context data
				if (context != null && context.length > 0) {
					var envelope:SchemaPayload = new SchemaPayload();
					envelope.setSchema(Constants.SCHEMA_CONTEXTS);

					// We can do better here, rather than re-iterate through the list
					var contextDataList:Array = [];
					for each(var schemaPayload:SchemaPayload in context) {
						contextDataList.push(schemaPayload.getMap());
					}

					envelope.setData(contextDataList);
					payload.addMap(envelope.getMap(), this.base64Encoded, Parameter.CONTEXT_ENCODED, Parameter.CONTEXT);
				}

				if (this.subject != null) {
					payload.addMap(Util.copyObject(subject.getSubject(), true));
				}

				return payload;
			}

		public function setPlatform(platform:String):void {
			this.platform = platform;
		}

		public function getPlatform():String {
			return this.platform;
		}

		protected function setTrackerVersion(version:String):void {
			this.trackerVersion = version;
		}

		private function addTrackerPayload(payload:IPayload):void {
			this.emitter.addToBuffer(payload);
		}

		public function setSubject(subject:Subject):void {
			this.subject = subject;
		}

		public function getSubject():Subject {
			return this.subject;
		}

		/**
		 * @param pageUrl URL of the viewed page
		 * @param pageTitle Title of the viewed page
		 * @param referrer Referrer of the page
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
//		public function trackPageView(pageUrl:String, pageTitle:String, referrer:String, context:Array = null, timestamp:Number = 0):void
//		{
//			// Precondition checks
//			Preconditions.checkNotNull(pageUrl);
//			Preconditions.checkArgument(!Util.isNullOrEmpty(pageUrl), "pageUrl cannot be empty");
//			Preconditions.checkArgument(!Util.isNullOrEmpty(pageTitle), "pageTitle cannot be empty");
//			Preconditions.checkArgument(!Util.isNullOrEmpty(referrer), "referrer cannot be empty");
//
//			var payload:IPayload = new TrackerPayload();
//			payload.add(Parameter.EVENT, Constants.EVENT_PAGE_VIEW);
//			payload.add(Parameter.PAGE_URL, pageUrl);
//			payload.add(Parameter.PAGE_TITLE, pageTitle);
//			payload.add(Parameter.PAGE_REFR, referrer);
//
//			completePayload(payload, context, timestamp);
//
//			addTrackerPayload(payload);
//		}

		/**
		 * @param category Category of the event
		 * @param action The event itself
		 * @param label Refer to the object the action is performed on
		 * @param property Property associated with either the action or the object
		 * @param value A value associated with the user action
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackStructuredEvent(category:String,
			action:String,
			label:String,
			property:String,
			value:int,
			context:Array = null,
			timestamp:Number = 0):void
		{
			// Precondition checks
			Preconditions.checkNotNull(label);
			Preconditions.checkNotNull(property);
			Preconditions.checkArgument(!Util.isNullOrEmpty(label), "label cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(property), "property cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(category), "category cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(action), "action cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_STRUCTURED);
			payload.add(Parameter.SE_CATEGORY, category);
			payload.add(Parameter.SE_ACTION, action);
			payload.add(Parameter.SE_LABEL, label);
			payload.add(Parameter.SE_PROPERTY, property);
			payload.add(Parameter.SE_VALUE, String(value));

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 *
		 * @param eventData The properties of the event. Has two field:
		 *                   A "data" field containing the event properties and
		 *                   A "schema" field identifying the schema against which the data is validated
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackUnstructuredEvent(eventData:SchemaPayload, context:Array = null,
			timestamp:Number = 0):void
		{
			var envelope:SchemaPayload = new SchemaPayload();
			envelope.setSchema(Constants.SCHEMA_UNSTRUCT_EVENT);
			envelope.setData(eventData.getMap());

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_UNSTRUCTURED);
			payload.addMap(envelope.getMap(), base64Encoded,
				Parameter.UNSTRUCTURED_ENCODED, Parameter.UNSTRUCTURED);

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 * This is an internal method called by track_ecommerce_transaction. It is not for public use.
		 * @param order_id Order ID
		 * @param sku Item SKU
		 * @param price Item price
		 * @param quantity Item quantity
		 * @param name Item name
		 * @param category Item category
		 * @param currency The currency the price is expressed in
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		protected function trackEcommerceTransactionItem(order_id:String, sku:String, price:Number,
			quantity:int, name:String, category:String,
			currency:String, context:Array,
			timestamp:Number):void
		{
			// Precondition checks
			Preconditions.checkNotNull(name);
			Preconditions.checkNotNull(category);
			Preconditions.checkNotNull(currency);
			Preconditions.checkArgument(!Util.isNullOrEmpty(order_id), "order_id cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(sku), "sku cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(name), "name cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(category), "category cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(currency), "currency cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_ECOMM_ITEM);
			payload.add(Parameter.TI_ITEM_ID, order_id);
			payload.add(Parameter.TI_ITEM_SKU, sku);
			payload.add(Parameter.TI_ITEM_NAME, name);
			payload.add(Parameter.TI_ITEM_CATEGORY, category);
			payload.add(Parameter.TI_ITEM_PRICE, String(price));
			payload.add(Parameter.TI_ITEM_QUANTITY, String(quantity));
			payload.add(Parameter.TI_ITEM_CURRENCY, currency);

			completePayload(payload, context, timestamp);

			addTrackerPayload(payload);
		}

		/**
		 * @param order_id ID of the eCommerce transaction
		 * @param total_value Total transaction value
		 * @param affiliation Transaction affiliation
		 * @param tax_value Transaction tax value
		 * @param shipping Delivery cost charged
		 * @param city Delivery address city
		 * @param state Delivery address state
		 * @param country Delivery address country
		 * @param currency The currency the price is expressed in
		 * @param items The items in the transaction
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackEcommerceTransaction(order_id:String, total_value:Number, affiliation:String,
			tax_value:Number, shipping:Number, city:String,
			state:String, country:String, currency:String,
			items:Array, context:Array = null,
			timestamp:Number = 0):void
		{
			// Precondition checks
			Preconditions.checkNotNull(affiliation);
			Preconditions.checkNotNull(city);
			Preconditions.checkNotNull(state);
			Preconditions.checkNotNull(country);
			Preconditions.checkNotNull(currency);
			Preconditions.checkArgument(!Util.isNullOrEmpty(order_id), "order_id cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(affiliation), "affiliation cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(city), "city cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(state), "state cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(country), "country cannot be empty");
			Preconditions.checkArgument(!Util.isNullOrEmpty(currency), "currency cannot be empty");

			var payload:IPayload = new TrackerPayload();
			payload.add(Parameter.EVENT, Constants.EVENT_ECOMM);
			payload.add(Parameter.TR_ID, order_id);
			payload.add(Parameter.TR_TOTAL, String(total_value));
			payload.add(Parameter.TR_AFFILIATION, affiliation);
			payload.add(Parameter.TR_TAX, String(tax_value));
			payload.add(Parameter.TR_SHIPPING, String(shipping));
			payload.add(Parameter.TR_CITY, city);
			payload.add(Parameter.TR_STATE, state);
			payload.add(Parameter.TR_COUNTRY, country);
			payload.add(Parameter.TR_CURRENCY, currency);

			completePayload(payload, context, timestamp);

			for each(var item:TransactionItem in items) {
				trackEcommerceTransactionItem(
					String(item.get(Parameter.TI_ITEM_ID)),
					String(item.get(Parameter.TI_ITEM_SKU)),
					Number(item.get(Parameter.TI_ITEM_PRICE)),
					parseInt(item.get(Parameter.TI_ITEM_QUANTITY)),
					String(item.get(Parameter.TI_ITEM_NAME)),
					String(item.get(Parameter.TI_ITEM_CATEGORY)),
					String(item.get(Parameter.TI_ITEM_CURRENCY)),
					item.get(Parameter.CONTEXT),
					timestamp);
			}

			addTrackerPayload(payload);
		}

		/**
		 * @param name The name of the screen view event
		 * @param id Screen view ID
		 * @param context Custom context for the event
		 * @param timestamp Optional user-provided timestamp for the event
		 */
		public function trackScreenView(name:String, id:String, context:Array,
			timestamp:Number):void {
				Preconditions.checkArgument(name != null || id != null);
				var trackerPayload:TrackerPayload = new TrackerPayload();

				trackerPayload.add(Parameter.SV_NAME, name);
				trackerPayload.add(Parameter.SV_ID, id);

				var payload:SchemaPayload = new SchemaPayload();
				payload.setSchema(Constants.SCHEMA_SCREEN_VIEW);
				payload.setData(trackerPayload);

				trackUnstructuredEvent(payload, context, timestamp);
			}
	}
}

Send page URL and refr URL fields with all events, not just page views. resolves #16


/*
 * FLINT PARTICLE SYSTEM
 * .....................
 *
 * Author: Richard Lord
 * Copyright (c) Richard Lord 2008-2011
 * http://flintparticles.org
 *
 *
 * Licence Agreement
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.flintparticles.twoD.actions
{
	import org.flintparticles.common.actions.ActionBase;
	import org.flintparticles.common.emitters.Emitter;
	import org.flintparticles.common.initializers.Initializer;
	import org.flintparticles.common.particles.Particle;
	import org.flintparticles.twoD.particles.Particle2D;
	import org.flintparticles.twoD.zones.Zone2D;

	import flash.geom.Point;

	[DefaultProperty("zone")]

	/**
	 * The TweenToCurrentPosition action adjusts the particle's position between two
	 * locations as it ages. The start location is a random point within the specified
	 * zone, and the end location is the particle's position when it is created or added
	 * to the emitter. The current position is relative to the particle's energy,
	 * which changes as the particle ages in accordance with the energy easing
	 * function used. This action should be used in conjunction with the Age action.
	 */

	public class TweenToCurrentPosition extends ActionBase implements Initializer
	{
		private var _zone : Zone2D;

		/**
		 * The constructor creates a TweenToCurrentPosition action for use by an emitter.
		 * To add a TweenToCurrentPosition to all particles created by an emitter, use the
		 * emitter's addAction method.
		 *
		 * @see org.flintparticles.common.emitters.Emitter#addAction()
		 *
		 * @param zone The zone for the particle's position when its energy is 0.
		 */
		public function TweenToCurrentPosition( zone : Zone2D )
		{
			_zone = zone;
			priority = -10;
		}

		/**
		 * The zone for the particle's position when its energy is 0.
		 */
		public function get zone() : Zone2D
		{
			return _zone;
		}

		public function set zone( value : Zone2D ) : void
		{
			_zone = value;
		}

		/**
		 *
		 */
		override public function addedToEmitter( emitter : Emitter ) : void
		{
			if ( !emitter.hasInitializer( this ) )
			{
				emitter.addInitializer( this );
			}
		}

		override public function removedFromEmitter( emitter : Emitter ) : void
		{
			emitter.removeInitializer( this );
		}

		/**
		 *
		 */
		public function initialize( emitter : Emitter, particle : Particle ) : void
		{
			var p : Particle2D = Particle2D( particle );
			var pt : Point = _zone.getLocation();
			var data : TweenToPositionData = new TweenToPositionData( pt.x, pt.y, p.x, p.y );
			p.dictionary[this] = data;
		}

		/**
		 * Calculates the current position of the particle based on it's energy.
		 *
		 *
This method is called by the emitter and need not be called by the
		 * user.

		 *
		 * @param emitter The Emitter that created the particle.
		 * @param particle The particle to be updated.
		 * @param time The duration of the frame - used for time based updates.
		 *
		 * @see org.flintparticles.common.actions.Action#update()
		 */
		override public function update( emitter : Emitter, particle : Particle, time : Number ) : void
		{
			var p : Particle2D = Particle2D( particle );
			if ( !p.dictionary[this] )
			{
				initialize( emitter, particle );
			}
			var data : TweenToPositionData = p.dictionary[this];
			p.x = data.endX + data.diffX * p.energy;
			p.y = data.endY + data.diffY * p.energy;
		}
	}
}

class TweenToPositionData
{
	public var diffX : Number;
	public var diffY : Number;
	public var endX : Number;
	public var endY : Number;

	public function TweenToPositionData( startX : Number, startY : Number, endX : Number, endY : Number )
	{
		this.diffX = startX - endX;
		this.diffY = startY - endY;
		this.endX = endX;
		this.endY = endY;
	}
}

/*
 * FLINT PARTICLE SYSTEM
 * .....................
 *
 * Author: Richard Lord
 * Copyright (c) Richard Lord 2008-2011
 * http://flintparticles.org
 *
 *
 * Licence Agreement
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.flintparticles.twoD.actions
{
	import org.flintparticles.common.actions.ActionBase;
	import org.flintparticles.common.emitters.Emitter;
	import org.flintparticles.common.initializers.Initializer;
	import org.flintparticles.common.particles.Particle;
	import org.flintparticles.twoD.particles.Particle2D;
	import org.flintparticles.twoD.zones.Zone2D;

	import flash.geom.Point;

	[DefaultProperty("zone")]

	/**
	 * The TweenToCurrentPosition action adjusts the particle's position between two
	 * locations as it ages. The start location is a random point within the specified
	 * zone, and the end location is the particle's position when it is created or added
	 * to the emitter. The current position is relative to the particle's energy,
	 * which changes as the particle ages in accordance with the energy easing
	 * function used. This action should be used in conjunction with the Age action.
	 */

	public class TweenToCurrentPosition extends ActionBase implements Initializer
	{
		private var _zone : Zone2D;

		/**
		 * The constructor creates a TweenToCurrentPosition action for use by an emitter.
		 * To add a TweenToCurrentPosition to all particles created by an emitter, use the
		 * emitter's addAction method.
		 *
		 * @see org.flintparticles.common.emitters.Emitter#addAction()
		 *
		 * @param zone The zone for the particle's position when its energy is 0.
		 */
		public function TweenToCurrentPosition( zone : Zone2D = null )
		{
			_zone = zone;
			priority = -10;
		}

		/**
		 * The zone for the particle's position when its energy is 0.
		 */
		public function get zone() : Zone2D
		{
			return _zone;
		}

		public function set zone( value : Zone2D ) : void
		{
			_zone = value;
		}

		/**
		 *
		 */
		override public function addedToEmitter( emitter : Emitter ) : void
		{
			if ( !emitter.hasInitializer( this ) )
			{
				emitter.addInitializer( this );
			}
		}

		override public function removedFromEmitter( emitter : Emitter ) : void
		{
			emitter.removeInitializer( this );
		}

		/**
		 *
		 */
		public function initialize( emitter : Emitter, particle : Particle ) : void
		{
			var p : Particle2D = Particle2D( particle );
			var pt : Point = _zone.getLocation();
			var data : TweenToPositionData = new TweenToPositionData( pt.x, pt.y, p.x, p.y );
			p.dictionary[this] = data;
		}

		/**
		 * Calculates the current position of the particle based on it's energy.
		 *
		 *
This method is called by the emitter and need not be called by the
		 * user.

		 *
		 * @param emitter The Emitter that created the particle.
		 * @param particle The particle to be updated.
		 * @param time The duration of the frame - used for time based updates.
		 *
		 * @see org.flintparticles.common.actions.Action#update()
		 */
		override public function update( emitter : Emitter, particle : Particle, time : Number ) : void
		{
			var p : Particle2D = Particle2D( particle );
			if ( !p.dictionary[this] )
			{
				initialize( emitter, particle );
			}
			var data : TweenToPositionData = p.dictionary[this];
			p.x = data.endX + data.diffX * p.energy;
			p.y = data.endY + data.diffY * p.energy;
		}
	}
}

class TweenToPositionData
{
	public var diffX : Number;
	public var diffY : Number;
	public var endX : Number;
	public var endY : Number;

	public function TweenToPositionData( startX : Number, startY : Number, endX : Number, endY : Number )
	{
		this.diffX = startX - endX;
		this.diffY = startY - endY;
		this.endX = endX;
		this.endY = endY;
	}
}

Add default parameter to support MXML.

Add default parameter to support MXML.


//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.util {

import flash.geom.Point;
import flash.geom.Matrix;

/**
 * Merely a typed container for two Points.
 */
public class Line
{
    public static const INTERSECTION_NORTH :int = 1;
    public static const INTERSECTION_SOUTH :int = 2;
    public static const DOES_NOT_INTERSECT :int = 3;

    public var start :Point;
    public var stop :Point;

    public function Line (start :Point, stop :Point)
    {
        this.start = start;
        this.stop = stop;
    }

    public function isIntersected (line :Line) :Boolean
    {
        return getIntersectionType(line) != DOES_NOT_INTERSECT;
    }

    /**
     * Tests if the given line intersects this line. This method rotates both lines so that the
     * start point of this line is on the left, at (0, 0).  If the lines do intersect, it then
     * returns INTERSECTION_NORTH if the end point of line is north of this line,
     * INTERSECTION_SOUTH otherwise.
     *
     * Intersections are inclusive.  If one or both points lands on this line, interects will not
     * return DOES_NOT_INTERSECT.
     */
    public function getIntersectionType (line :Line) :int
    {
        // rotate so that this line is horizontal, with the start on the left, at (0, 0)
        var trans :Matrix = new Matrix();
        trans.translate(-start.x, -start.y);
        trans.rotate(-Math.atan2(stop.y - start.y, stop.x - start.x));
        var thisLineStop :Point = trans.transformPoint(stop);
        var thatLineStart :Point = trans.transformPoint(line.start);
        var thatLineStop :Point = trans.transformPoint(line.stop);
        if (thatLineStart.y >= 0 && thatLineStop.y <= 0) {
            var interp :Point = Point.interpolate(thatLineStart, thatLineStop, thatLineStop.y /
                (thatLineStop.y + (-thatLineStart.y)));
            if (interp.x >= 0 && interp.x <= thisLineStop.x) {
                return INTERSECTION_NORTH;
            } else {
                return DOES_NOT_INTERSECT;
            }
        } else if (thatLineStart.y <= 0 && thatLineStop.y >= 0) {
            interp = Point.interpolate(thatLineStop, thatLineStart, thatLineStart.y /
                (thatLineStart.y + (-thatLineStop.y)));
            if (interp.x >= 0 && interp.x <= thisLineStop.x) {
                return INTERSECTION_SOUTH;
            } else {
                return DOES_NOT_INTERSECT;
            }
        } else {
            return DOES_NOT_INTERSECT;
        }
    }
}
}

//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.util {

import flash.geom.Point;
import flash.geom.Matrix;

/**
 * Merely a typed container for two Points.
 */
public class Line implements Equalable
{
    public static const INTERSECTION_NORTH :int = 1;
    public static const INTERSECTION_SOUTH :int = 2;
    public static const DOES_NOT_INTERSECT :int = 3;

    public var start :Point;
    public var stop :Point;

    public function Line (start :Point, stop :Point)
    {
        this.start = start;
        this.stop = stop;
    }

    public function isIntersected (line :Line) :Boolean
    {
        return getIntersectionType(line) != DOES_NOT_INTERSECT;
    }

    /**
     * Tests if the given line intersects this line. This method rotates both lines so that the
     * start point of this line is on the left, at (0, 0).  If the lines do intersect, it then
     * returns INTERSECTION_NORTH if the end point of line is north of this line,
     * INTERSECTION_SOUTH otherwise.
     *
     * Intersections are inclusive.  If one or both points lands on this line, interects will not
     * return DOES_NOT_INTERSECT.
     */
    public function getIntersectionType (line :Line) :int
    {
        // rotate so that this line is horizontal, with the start on the left, at (0, 0)
        var trans :Matrix = new Matrix();
        trans.translate(-start.x, -start.y);
        trans.rotate(-Math.atan2(stop.y - start.y, stop.x - start.x));
        var thisLineStop :Point = trans.transformPoint(stop);
        var thatLineStart :Point = trans.transformPoint(line.start);
        var thatLineStop :Point = trans.transformPoint(line.stop);
        if (thatLineStart.y >= 0 && thatLineStop.y <= 0) {
            var interp :Point = Point.interpolate(thatLineStart, thatLineStop, thatLineStop.y /
                (thatLineStop.y + (-thatLineStart.y)));
            if (interp.x >= 0 && interp.x <= thisLineStop.x) {
                return INTERSECTION_NORTH;
            } else {
                return DOES_NOT_INTERSECT;
            }
        } else if (thatLineStart.y <= 0 && thatLineStop.y >= 0) {
            interp = Point.interpolate(thatLineStop, thatLineStart, thatLineStart.y /
                (thatLineStart.y + (-thatLineStop.y)));
            if (interp.x >= 0 && interp.x <= thisLineStop.x) {
                return INTERSECTION_SOUTH;
            } else {
                return DOES_NOT_INTERSECT;
            }
        } else {
            return DOES_NOT_INTERSECT;
        }
    }

    // from interface Equalable
    public function equals (o :Object) :Boolean
    {
        var other :Line = o as Line;
        if (other == null) {
            return false;
        }

        if (start == null || other.start == null) {
            if (start != other.start) {
                // not both null
                return false;
            }
        }
        if (stop == null || other.stop == null) {
            if (stop != other.stop) {
                // not both null
                return false;
            }
        }

        return (start == null || start.equals(other.start)) &&
               (stop == null || stop.equals(other.stop));
    }
}
}

implement equalable

implement equalable

git-svn-id: a1a4b28b82a3276cc491891159dd9963a0a72fae@4700 542714f4-19e9-0310-aa3c-eee0fc999fb1


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

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

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

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

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

		private var _bindings			: Object							= null;

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

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

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

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

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

		private var _enabled			: Boolean							= true;

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

		private var _bindingsConsumer	: DrawCallBindingsConsumer;

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

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

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

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

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

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

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

			return _depth;
		}

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

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

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

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

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

            _bindingsConsumer = null;
		}

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

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

			triangleCulling		= TriangleCulling.FRONT;
			blending			= Blending.OPAQUE;
		}

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

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

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

			updateGeometry(geometry);

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

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

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

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

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

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

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

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

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

				_invalidDepth = true;
			}
		}

		public function apply(context : Context3DResource, previous : DrawCall) : uint
		{
			context.setProgramConstantsFromVector(PROGRAM_TYPE_VERTEX, 0, _vsConstants)
				   .setProgramConstantsFromVector(PROGRAM_TYPE_FRAGMENT, 0, _fsConstants);

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

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

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

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

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

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

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

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

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

			_invalidDepth = true;
		}
	}
}

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

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

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

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

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

		private var _bindings			: Object							= null;

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

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

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

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

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

		private var _enabled			: Boolean							= true;

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

		private var _bindingsConsumer	: DrawCallBindingsConsumer;

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

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

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

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

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

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

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

			return _depth;
		}

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

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

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

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

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

            _bindingsConsumer = null;
		}

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

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

			triangleCulling		= TriangleCulling.FRONT;
			blending			= Blending.OPAQUE;
		}

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

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

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

			updateGeometry(geometry);

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

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

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

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

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

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

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

			if (computeDepth)
			{
                if (sceneBindings.propertyExists('worldToScreen'))
                    _worldToScreen = sceneBindings.getProperty('worldToScreen');
				sceneBindings.addCallback('worldToScreen', transformChangedHandler);

                if (meshBindings.propertyExists('localToWorld'))
                    _localToWorld = meshBindings.getProperty('localToWorld');
				meshBindings.addCallback('localToWorld', transformChangedHandler);

				_invalidDepth = true;
			}
		}

		public function apply(context : Context3DResource, previous : DrawCall) : uint
		{
			context.setProgramConstantsFromVector(PROGRAM_TYPE_VERTEX, 0, _vsConstants)
				   .setProgramConstantsFromVector(PROGRAM_TYPE_FRAGMENT, 0, _fsConstants);

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

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

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

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

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

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

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

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

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

			_invalidDepth = true;
		}
	}
}

fix DrawCall.depth update by making sure the reference to localToWorld and worldToScreen are init. directly from the bindings if available when DrawCall.setBindings() is called


/* 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.mangui.hls {
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.constant.HLSMaxLevelCappingMode;

    public final class HLSSettings extends Object {
        /**
         * Limit levels usable in auto-quality by the stage dimensions (width and height).
         *      true - level width and height (defined in m3u8 playlist) will be compared with the player width and height (stage.stageWidth and stage.stageHeight).
         *             Max level will be set depending on the maxLevelCappingMode option.
         *      false - levels will not be limited. All available levels could be used in auto-quality mode taking only bandwidth into consideration.
         *
         * Note: this setting is ignored in manual mode so all the levels could be selected manually.
         *
         * Default is false
         */
        public static var capLevelToStage : Boolean = false;
        /**
         * Defines the max level capping mode to the one available in HLSMaxLevelCappingMode
         *      HLSMaxLevelCappingMode.DOWNSCALE - max capped level should be the one with the dimensions equal or greater than the stage dimensions (so the video will be downscaled)
         *      HLSMaxLevelCappingMode.UPSCALE - max capped level should be the one with the dimensions equal or lower than the stage dimensions (so the video will be upscaled)
         *
         * Default is HLSMaxLevelCappingMode.DOWNSCALE
         */
        public static var maxLevelCappingMode : String = HLSMaxLevelCappingMode.DOWNSCALE;
        // // // // // // /////////////////////////////////
        //
        // org.mangui.hls.stream.HLSNetStream
        //
        // // // // // // /////////////////////////////////
        /**
         * Defines minimum buffer length in seconds before playback can start, after seeking or buffer stalling.
         *
         * Default is -1 = auto
         */
        public static var minBufferLength : Number = -1;
        /**
         * Defines maximum buffer length in seconds.
         * (0 means infinite buffering)
         *
         * Default is 300.
         */
        public static var maxBufferLength : Number = 300;
        /**
         * Defines maximum back buffer length in seconds.
         * (0 means infinite back buffering)
         *
         * Default is 30.
         */
        public static var maxBackBufferLength : Number = 30;
        /**
         * Defines low buffer length in seconds.
         * When crossing down this threshold, HLS will switch to buffering state.
         *
         * Default is 3.
         */
        public static var lowBufferLength : Number = 3;
        /**
         * Defines seek mode to one form available in HLSSeekMode class:
         *      HLSSeekMode.ACCURATE_SEEK - accurate seeking to exact requested position
         *      HLSSeekMode.KEYFRAME_SEEK - key-frame based seeking (seek to nearest key frame before requested seek position)
         *      HLSSeekMode.SEGMENT_SEEK - segment based seeking (seek to beginning of segment containing requested seek position)
         *
         * Default is HLSSeekMode.ACCURATE_SEEK.
         */
        public static var seekMode : String = HLSSeekMode.ACCURATE_SEEK;
        /** max nb of retries for Key Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var keyLoadMaxRetry : int = -1;
        /** keyLoadMaxRetryTimeout

         * Maximum key retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on key request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until keyLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var keyLoadMaxRetryTimeout : Number = 64000;
        /** max nb of retries for Fragment Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var fragmentLoadMaxRetry : int = -1;
        /** fragmentLoadMaxRetryTimeout

         * Maximum Fragment retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until fragmentLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var fragmentLoadMaxRetryTimeout : Number = 64000;
        /**
         * If set to true, live playlist will be flushed from URL cache before reloading
         * (this is to workaround some cache issues with some combination of Flash Player / IE version)
         *
         * Default is false
         */
        public static var flushLiveURLCache : Boolean = false;
        /** max nb of retries for Manifest Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var manifestLoadMaxRetry : int = -1;
        /** manifestLoadMaxRetryTimeout

         * Maximum Manifest retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until manifestLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var manifestLoadMaxRetryTimeout : Number = 64000;
        /**
         * If greater than 0, specifies the preferred bitrate.
         * If -1, and startFromLevel is not specified, automatic start level selection will be used.
         * This parameter, if set, will take priority over startFromLevel.
         */
        public static var startFromBitrate : Number = -1;
        /** start level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
         */
        public static var startFromLevel : Number = -1;
        /** seek level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, keep previous level matching previous download bandwidth
         */
        public static var seekFromLevel : Number = -1;
        /** use hardware video decoder :
         *  it will set NetStream.useHardwareDecoder
         *  refer to http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#useHardwareDecoder
         */
        public static var useHardwareVideoDecoder : Boolean = false;
        /**
         * Defines whether INFO level log messages will will appear in the console
         * Default is true.
         */
        public static var logInfo : Boolean = true;
        /**
         * Defines whether DEBUG level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug : Boolean = false;
        /**
         * Defines whether DEBUG2 level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug2 : Boolean = false;
        /**
         * Defines whether WARN level log messages will will appear in the console
         * Default is true.
         */
        public static var logWarn : Boolean = true;
        /**
         * Defines whether ERROR level log messages will will appear in the console
         * Default is true.
         */
        public static var logError : Boolean = true;
    }
}

/* 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.mangui.hls {
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.constant.HLSMaxLevelCappingMode;

    public final class HLSSettings extends Object {
        /**
         * Limit levels usable in auto-quality by the stage dimensions (width and height).
         *      true - level width and height (defined in m3u8 playlist) will be compared with the player width and height (stage.stageWidth and stage.stageHeight).
         *             Max level will be set depending on the maxLevelCappingMode option.
         *      false - levels will not be limited. All available levels could be used in auto-quality mode taking only bandwidth into consideration.
         *
         * Note: this setting is ignored in manual mode so all the levels could be selected manually.
         *
         * Default is false
         */
        public static var capLevelToStage : Boolean = false;
        /**
         * Defines the max level capping mode to the one available in HLSMaxLevelCappingMode
         *      HLSMaxLevelCappingMode.DOWNSCALE - max capped level should be the one with the dimensions equal or greater than the stage dimensions (so the video will be downscaled)
         *      HLSMaxLevelCappingMode.UPSCALE - max capped level should be the one with the dimensions equal or lower than the stage dimensions (so the video will be upscaled)
         *
         * Default is HLSMaxLevelCappingMode.DOWNSCALE
         */
        public static var maxLevelCappingMode : String = HLSMaxLevelCappingMode.DOWNSCALE;
        // // // // // // /////////////////////////////////
        //
        // org.mangui.hls.stream.HLSNetStream
        //
        // // // // // // /////////////////////////////////
        /**
         * Defines minimum buffer length in seconds before playback can start, after seeking or buffer stalling.
         *
         * Default is -1 = auto
         */
        public static var minBufferLength : Number = -1;
        /**
         * Defines maximum buffer length in seconds.
         * (0 means infinite buffering)
         *
         * Default is 120.
         */
        public static var maxBufferLength : Number = 120;
        /**
         * Defines maximum back buffer length in seconds.
         * (0 means infinite back buffering)
         *
         * Default is 30.
         */
        public static var maxBackBufferLength : Number = 30;
        /**
         * Defines low buffer length in seconds.
         * When crossing down this threshold, HLS will switch to buffering state.
         *
         * Default is 3.
         */
        public static var lowBufferLength : Number = 3;
        /**
         * Defines seek mode to one form available in HLSSeekMode class:
         *      HLSSeekMode.ACCURATE_SEEK - accurate seeking to exact requested position
         *      HLSSeekMode.KEYFRAME_SEEK - key-frame based seeking (seek to nearest key frame before requested seek position)
         *      HLSSeekMode.SEGMENT_SEEK - segment based seeking (seek to beginning of segment containing requested seek position)
         *
         * Default is HLSSeekMode.ACCURATE_SEEK.
         */
        public static var seekMode : String = HLSSeekMode.ACCURATE_SEEK;
        /** max nb of retries for Key Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var keyLoadMaxRetry : int = -1;
        /** keyLoadMaxRetryTimeout

         * Maximum key retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on key request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until keyLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var keyLoadMaxRetryTimeout : Number = 64000;
        /** max nb of retries for Fragment Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var fragmentLoadMaxRetry : int = -1;
        /** fragmentLoadMaxRetryTimeout

         * Maximum Fragment retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until fragmentLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var fragmentLoadMaxRetryTimeout : Number = 64000;
        /**
         * If set to true, live playlist will be flushed from URL cache before reloading
         * (this is to workaround some cache issues with some combination of Flash Player / IE version)
         *
         * Default is false
         */
        public static var flushLiveURLCache : Boolean = false;
        /** max nb of retries for Manifest Loading in case I/O errors are met,
         *      0, means no retry, error will be triggered automatically
         *     -1 means infinite retry
         */
        public static var manifestLoadMaxRetry : int = -1;
        /** manifestLoadMaxRetryTimeout

         * Maximum Manifest retry timeout (in milliseconds) in case I/O errors are met.
         * Every fail on fragment request, player will exponentially increase the timeout to try again.
         * It starts waiting 1 second (1000ms), than 2, 4, 8, 16, until manifestLoadMaxRetryTimeout is reached.
         *
         * Default is 64000.
         */
        public static var manifestLoadMaxRetryTimeout : Number = 64000;
        /**
         * If greater than 0, specifies the preferred bitrate.
         * If -1, and startFromLevel is not specified, automatic start level selection will be used.
         * This parameter, if set, will take priority over startFromLevel.
         */
        public static var startFromBitrate : Number = -1;
        /** start level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
         */
        public static var startFromLevel : Number = -1;
        /** seek level :
         *  from 0 to 1 : indicates the "normalized" preferred bitrate. As such, if it is 0.5, the closest to the middle bitrate will be selected and used first.
         * -1 : automatic start level selection, keep previous level matching previous download bandwidth
         */
        public static var seekFromLevel : Number = -1;
        /** use hardware video decoder :
         *  it will set NetStream.useHardwareDecoder
         *  refer to http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#useHardwareDecoder
         */
        public static var useHardwareVideoDecoder : Boolean = false;
        /**
         * Defines whether INFO level log messages will will appear in the console
         * Default is true.
         */
        public static var logInfo : Boolean = true;
        /**
         * Defines whether DEBUG level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug : Boolean = false;
        /**
         * Defines whether DEBUG2 level log messages will will appear in the console
         * Default is false.
         */
        public static var logDebug2 : Boolean = false;
        /**
         * Defines whether WARN level log messages will will appear in the console
         * Default is true.
         */
        public static var logWarn : Boolean = true;
        /**
         * Defines whether ERROR level log messages will will appear in the console
         * Default is true.
         */
        public static var logError : Boolean = true;
    }
}

set max buffer len to 120s

set max buffer len to 120s