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

package weave.data.AttributeColumns
{
	import flash.system.Capabilities;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;

	import weave.api.data.ColumnMetadata;
	import weave.api.data.DataType;
	import weave.api.data.IPrimitiveColumn;
	import weave.api.data.IQualifiedKey;
	import weave.api.reportError;
	import weave.compiler.Compiler;
	import weave.compiler.StandardLib;
	import weave.flascc.date_format;
	import weave.flascc.date_parse;
	import weave.flascc.dates_detect;

	/**
	 * @author adufilie
	 */
	public class DateColumn extends AbstractAttributeColumn implements IPrimitiveColumn
	{
		public function DateColumn(metadata:Object = null)
		{
			super(metadata);
		}

		private const _uniqueKeys:Array = new Array();
		private var _keyToDate:Dictionary = new Dictionary();

		// temp variables for async task
		private var _i:int;
		private var _keys:Vector.<IQualifiedKey>;
		private var _dates:Vector.<String>;
		private var _reportedError:Boolean;

		// variables that do not get reset after async task
		private static const compiler:Compiler = new Compiler();
		private var _stringToNumberFunction:Function = null;
		private var _numberToStringFunction:Function = null;
		private var _dateFormat:String = null;
		private var _useFlascc:Boolean = true;

		/**
		 * @inheritDoc
		 */
		override public function getMetadata(propertyName:String):String
		{
			if (propertyName == ColumnMetadata.DATA_TYPE)
				return DataType.DATE;
			if (propertyName == ColumnMetadata.DATE_FORMAT)
				return _dateFormat;
			return super.getMetadata(propertyName);
		}

		/**
		 * @inheritDoc
		 */
		override public function get keys():Array
		{
			return _uniqueKeys;
		}

		/**
		 * @inheritDoc
		 */
		override public function containsKey(key:IQualifiedKey):Boolean
		{
			return _keyToDate[key] != undefined;
		}

		public function setRecords(keys:Vector.<IQualifiedKey>, dates:Vector.<String>):void
		{
			if (keys.length > dates.length)
			{
				reportError("Array lengths differ");
				return;
			}

			// read dateFormat metadata
			_dateFormat = getMetadata(ColumnMetadata.DATE_FORMAT);
			if (!_dateFormat)
			{
				var possibleFormats:Array = detectDateFormats(dates);
				StandardLib.sortOn(possibleFormats, 'length');
				_dateFormat = possibleFormats.pop();
			}

			_dateFormat = convertDateFormat_as_to_c(_dateFormat);
			//_useFlascc = _dateFormat && _dateFormat.indexOf('%') >= 0;

			// compile the number format function from the metadata
			_stringToNumberFunction = null;
			var numberFormat:String = getMetadata(ColumnMetadata.NUMBER);
			if (numberFormat)
			{
				try
				{
					_stringToNumberFunction = compiler.compileToFunction(numberFormat, null, errorHandler, false, [ColumnMetadata.STRING]);
				}
				catch (e:Error)
				{
					reportError(e);
				}
			}

			// compile the string format function from the metadata
			_numberToStringFunction = null;
			var stringFormat:String = getMetadata(ColumnMetadata.STRING);
			if (stringFormat)
			{
				try
				{
					_numberToStringFunction = compiler.compileToFunction(stringFormat, null, errorHandler, false, [ColumnMetadata.NUMBER]);
				}
				catch (e:Error)
				{
					reportError(e);
				}
			}

			_i = 0;
			_keys = keys;
			_dates = dates;
			_keyToDate = new Dictionary();
			_uniqueKeys.length = 0;
			_reportedError = false;

			// high priority because not much can be done without data
			WeaveAPI.StageUtils.startTask(this, _asyncIterate, WeaveAPI.TASK_PRIORITY_HIGH, _asyncComplete);
		}

		private function errorHandler(e:*):void
		{
			return; // do nothing
		}

		private function _asyncComplete():void
		{
			_keys = null;
			_dates = null;

			triggerCallbacks();
		}

		private function parseDate(string:String):Date
		{
			if (_useFlascc)
				return weave.flascc.date_parse(string, _dateFormat);
			return StandardLib.parseDate(string, _dateFormat);
		}

		private function formatDate(value:Object):String
		{
			if (_useFlascc)
				return weave.flascc.date_format(value as Date || new Date(value), _dateFormat);
			return StandardLib.formatDate(value, _dateFormat);
		}

		private function _asyncIterate(stopTime:int):Number
		{
			for (; _i < _keys.length; _i++)
			{
				if (getTimer() > stopTime)
					return _i / _keys.length;

				// get values for this iteration
				var key:IQualifiedKey = _keys[_i];
				var string:String = _dates[_i];
				var date:Date;
				if (_stringToNumberFunction != null)
				{
					var number:Number = _stringToNumberFunction(string);
					if (_numberToStringFunction != null)
					{
						string = _numberToStringFunction(number);
						if (!string)
							continue;
						date = parseDate(string);
					}
					else
					{
						if (!isFinite(number))
							continue;
						date = new Date(number);
					}
				}
				else
				{
					try
					{
						if (!string)
							continue;
						date = parseDate(string);
					}
					catch (e:Error)
					{
						if (!_reportedError)
						{
							_reportedError = true;
							var err:String = StandardLib.substitute(
								'Warning: Unable to parse this value as a date: "{0}"'
								+ ' (only the first error for this column is reported).'
								+ ' Attribute column: {1}',
								string,
								Compiler.stringify(_metadata)
							);
							reportError(err);
						}
						continue;
					}
				}

				// keep track of unique keys
				if (_keyToDate[key] === undefined)
				{
					_uniqueKeys.push(key);
					// save key-to-data mapping
					_keyToDate[key] = date;
				}
				else if (!_reportedError)
				{
					_reportedError = true;
					var fmt:String = 'Warning: Key column values are not unique.  Record dropped due to duplicate key ({0}) (only reported for first duplicate).  Attribute column: {1}';
					var str:String = StandardLib.substitute(fmt, key.localName, Compiler.stringify(_metadata));
					if (Capabilities.isDebugger)
						reportError(str);
				}
			}
			return 1;
		}

		/**
		 * @inheritDoc
		 */
		public function deriveStringFromNumber(number:Number):String
		{
			if (_numberToStringFunction != null)
				return _numberToStringFunction(number);

			if (_dateFormat)
				return formatDate(number);

			return new Date(number).toString();
		}

		/**
		 * @inheritDoc
		 */
		override public function getValueFromKey(key:IQualifiedKey, dataType:Class = null):*
		{
			var number:Number;
			var string:String;
			var date:Date;

			if (dataType == Number)
			{
				number = _keyToDate[key];
				return number;
			}

			if (dataType == String)
			{
				if (_numberToStringFunction != null)
				{
					number = _keyToDate[key];
					return _numberToStringFunction(number);
				}

				date = _keyToDate[key];

				if (!date)
					return '';

				if (_dateFormat)
					string = formatDate(date);
				else
					string = date.toString();

				return string;
			}

			date = _keyToDate[key];

			if (dataType)
				return date as DataType;

			return date;
		}

		override public function toString():String
		{
			return debugId(this) + '{recordCount: '+keys.length+', keyType: "'+getMetadata('keyType')+'", title: "'+getMetadata('title')+'"}';
		}

		private static function convertDateFormat_as_to_c(format:String):String
		{
			if (!format || format.indexOf('%') >= 0)
				return format;
			return StandardLib.replace.apply(null, [format].concat(dateFormat_replacements_as_to_c));
		}

		private static const dateFormat_replacements_as_to_c:Array = [
			'YYYY','%Y',
			'YY','%y',
			'MMMM','%B',
			'MMM','%b',
			'MM','%m',
			'M','%-m',
			'DD','%d',
			'D','%-d',
			'E','%u',
			'A','%p',
			'JJ','%H',
			'J','%-H',
			'LL','%I',
			'L','%-I',
			'EEEE','%A', // note this %A is after the A was replaced above
			'EEE','%a',
			'NN','%M', // note these %M appears after the M's were replaced above
			'N','%-M',
			'SS','%S'
			//,'S','%-S'
		];

		public static function detectDateFormats(dates:*):Array
		{
			return weave.flascc.dates_detect(dates, DATE_FORMAT_AUTO_DETECT);
		}

		public static const DATE_FORMAT_ADDITIONAL_SUGGESTIONS:Array = [
			"%Y"
		];
		public static const DATE_FORMAT_AUTO_DETECT:Array = [
			'%d-%b-%y',
			'%b-%d-%y',
			'%d-%b-%Y',
			'%b-%d-%Y',
			'%Y-%b-%d',

			'%d/%b/%y',
			'%b/%d/%y',
			'%d/%b/%Y',
			'%b/%d/%Y',
			'%Y/%b/%d',

			'%d.%b.%y',
			'%b.%d.%y',
			'%d.%b.%Y',
			'%b.%d.%Y',
			'%Y.%b.%d',

			'%d-%m-%y',
			'%m-%d-%y',
			'%d-%m-%Y',
			'%m-%d-%Y',
			'%Y-%m-%d',

			'%d/%m/%y',
			'%m/%d/%y',
			'%d/%m/%Y',
			'%m/%d/%Y',
			'%Y/%m/%d',

			'%d.%m.%y',
			'%m.%d.%y',
			'%d.%m.%Y',
			'%m.%d.%Y',
			'%Y.%m.%d',

			'%H:%M',
			'%H:%M:%S',
			'%a, %d %b %Y %H:%M:%S %z', // RFC_822

			// ISO_8601   http://www.thelinuxdaily.com/2014/03/c-function-to-validate-iso-8601-date-formats-using-strptime/
			"%Y-%m-%d",
			"%y-%m-%d",
			"%Y-%m-%d %T",
			"%y-%m-%d %T",
			"%Y-%m-%dT%T",
			"%y-%m-%dT%T",
			"%Y-%m-%dT%TZ",
			"%y-%m-%dT%TZ",
			"%Y-%m-%d %TZ",
			"%y-%m-%d %TZ",
			"%Y%m%dT%TZ",
			"%y%m%dT%TZ",
			"%Y%m%d %TZ",
			"%y%m%d %TZ",

			"%Y-%b-%d %T",
			"%Y-%b-%d %H:%M:%S",
			"%d-%b-%Y %T",
			"%d-%b-%Y %H:%M:%S"

			/*
			//https://code.google.com/p/datejs/source/browse/trunk/src/globalization/en-US.js
			'M/d/yyyy',
			'dddd, MMMM dd, yyyy',
			"M/d/yyyy",
			"dddd, MMMM dd, yyyy",
			"h:mm tt",
			"h:mm:ss tt",
			"dddd, MMMM dd, yyyy h:mm:ss tt",
			"yyyy-MM-ddTHH:mm:ss",
			"yyyy-MM-dd HH:mm:ssZ",
			"ddd, dd MMM yyyy HH:mm:ss GMT",
			"MMMM dd",
			"MMMM, yyyy",

			//http://www.java2s.com/Code/Android/Date-Type/parseDateforlistofpossibleformats.htm
			"EEE, dd MMM yyyy HH:mm:ss z", // RFC_822
			"EEE, dd MMM yyyy HH:mm zzzz",
			"yyyy-MM-dd'T'HH:mm:ssZ",
			"yyyy-MM-dd'T'HH:mm:ss.SSSzzzz", // Blogger Atom feed has millisecs also
			"yyyy-MM-dd'T'HH:mm:sszzzz",
			"yyyy-MM-dd'T'HH:mm:ss z",
			"yyyy-MM-dd'T'HH:mm:ssz", // ISO_8601
			"yyyy-MM-dd'T'HH:mm:ss",
			"yyyy-MM-dd'T'HHmmss.SSSz",

			//http://stackoverflow.com/a/21737848
			"M/d/yyyy", "MM/dd/yyyy",
			"d/M/yyyy", "dd/MM/yyyy",
			"yyyy/M/d", "yyyy/MM/dd",
			"M-d-yyyy", "MM-dd-yyyy",
			"d-M-yyyy", "dd-MM-yyyy",
			"yyyy-M-d", "yyyy-MM-dd",
			"M.d.yyyy", "MM.dd.yyyy",
			"d.M.yyyy", "dd.MM.yyyy",
			"yyyy.M.d", "yyyy.MM.dd",
			"M,d,yyyy", "MM,dd,yyyy",
			"d,M,yyyy", "dd,MM,yyyy",
			"yyyy,M,d", "yyyy,MM,dd",
			"M d yyyy", "MM dd yyyy",
			"d M yyyy", "dd MM yyyy",
			"yyyy M d", "yyyy MM dd" */
		];
	}
}

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

package weave.data.AttributeColumns
{
	import flash.system.Capabilities;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;

	import weave.api.data.ColumnMetadata;
	import weave.api.data.DataType;
	import weave.api.data.IPrimitiveColumn;
	import weave.api.data.IQualifiedKey;
	import weave.api.reportError;
	import weave.compiler.Compiler;
	import weave.compiler.StandardLib;
	import weave.flascc.date_format;
	import weave.flascc.date_parse;
	import weave.flascc.dates_detect;

	/**
	 * @author adufilie
	 */
	public class DateColumn extends AbstractAttributeColumn implements IPrimitiveColumn
	{
		public function DateColumn(metadata:Object = null)
		{
			super(metadata);
		}

		private const _uniqueKeys:Array = new Array();
		private var _keyToDate:Dictionary = new Dictionary();

		// temp variables for async task
		private var _i:int;
		private var _keys:Vector.<IQualifiedKey>;
		private var _dates:Vector.<String>;
		private var _reportedError:Boolean;

		// variables that do not get reset after async task
		private static const compiler:Compiler = new Compiler();
		private var _stringToNumberFunction:Function = null;
		private var _numberToStringFunction:Function = null;
		private var _dateFormat:String = null;
		private var _useFlascc:Boolean = true;

		/**
		 * @inheritDoc
		 */
		override public function getMetadata(propertyName:String):String
		{
			if (propertyName == ColumnMetadata.DATA_TYPE)
				return DataType.DATE;
			if (propertyName == ColumnMetadata.DATE_FORMAT)
				return _dateFormat;
			return super.getMetadata(propertyName);
		}

		/**
		 * @inheritDoc
		 */
		override public function get keys():Array
		{
			return _uniqueKeys;
		}

		/**
		 * @inheritDoc
		 */
		override public function containsKey(key:IQualifiedKey):Boolean
		{
			return _keyToDate[key] != undefined;
		}

		public function setRecords(keys:Vector.<IQualifiedKey>, dates:Vector.<String>):void
		{
			if (keys.length > dates.length)
			{
				reportError("Array lengths differ");
				return;
			}

			// read dateFormat metadata
			_dateFormat = getMetadata(ColumnMetadata.DATE_FORMAT);
			if (!_dateFormat)
			{
				var possibleFormats:Array = detectDateFormats(dates);
				StandardLib.sortOn(possibleFormats, 'length');
				_dateFormat = possibleFormats.pop();
			}

			_dateFormat = convertDateFormat_as_to_c(_dateFormat);
			//_useFlascc = _dateFormat && _dateFormat.indexOf('%') >= 0;

			// compile the number format function from the metadata
			_stringToNumberFunction = null;
			var numberFormat:String = getMetadata(ColumnMetadata.NUMBER);
			if (numberFormat)
			{
				try
				{
					_stringToNumberFunction = compiler.compileToFunction(numberFormat, null, errorHandler, false, [ColumnMetadata.STRING]);
				}
				catch (e:Error)
				{
					reportError(e);
				}
			}

			// compile the string format function from the metadata
			_numberToStringFunction = null;
			var stringFormat:String = getMetadata(ColumnMetadata.STRING);
			if (stringFormat)
			{
				try
				{
					_numberToStringFunction = compiler.compileToFunction(stringFormat, null, errorHandler, false, [ColumnMetadata.NUMBER]);
				}
				catch (e:Error)
				{
					reportError(e);
				}
			}

			_i = 0;
			_keys = keys;
			_dates = dates;
			_keyToDate = new Dictionary();
			_uniqueKeys.length = 0;
			_reportedError = false;

			// high priority because not much can be done without data
			WeaveAPI.StageUtils.startTask(this, _asyncIterate, WeaveAPI.TASK_PRIORITY_HIGH, _asyncComplete);
		}

		private function errorHandler(e:*):void
		{
			return; // do nothing
		}

		private function _asyncComplete():void
		{
			_keys = null;
			_dates = null;

			triggerCallbacks();
		}

		private function parseDate(string:String):Date
		{
			if (_useFlascc)
				return weave.flascc.date_parse(string, _dateFormat);
			return StandardLib.parseDate(string, _dateFormat);
		}

		private function formatDate(value:Object):String
		{
			if (_useFlascc)
				return weave.flascc.date_format(value as Date || new Date(value), _dateFormat);
			return StandardLib.formatDate(value, _dateFormat);
		}

		private function _asyncIterate(stopTime:int):Number
		{
			for (; _i < _keys.length; _i++)
			{
				if (getTimer() > stopTime)
					return _i / _keys.length;

				// get values for this iteration
				var key:IQualifiedKey = _keys[_i];
				var string:String = _dates[_i];
				var date:Date;
				if (_stringToNumberFunction != null)
				{
					var number:Number = _stringToNumberFunction(string);
					if (_numberToStringFunction != null)
					{
						string = _numberToStringFunction(number);
						if (!string)
							continue;
						date = parseDate(string);
					}
					else
					{
						if (!isFinite(number))
							continue;
						date = new Date(number);
					}
				}
				else
				{
					try
					{
						if (!string)
							continue;
						date = parseDate(string);
					}
					catch (e:Error)
					{
						if (!_reportedError)
						{
							_reportedError = true;
							var err:String = StandardLib.substitute(
								'Warning: Unable to parse this value as a date: "{0}"'
								+ ' (only the first error for this column is reported).'
								+ ' Attribute column: {1}',
								string,
								Compiler.stringify(_metadata)
							);
							reportError(err);
						}
						continue;
					}
				}

				// keep track of unique keys
				if (_keyToDate[key] === undefined)
				{
					_uniqueKeys.push(key);
					// save key-to-data mapping
					_keyToDate[key] = date;
				}
				else if (!_reportedError)
				{
					_reportedError = true;
					var fmt:String = 'Warning: Key column values are not unique.  Record dropped due to duplicate key ({0}) (only reported for first duplicate).  Attribute column: {1}';
					var str:String = StandardLib.substitute(fmt, key.localName, Compiler.stringify(_metadata));
					if (Capabilities.isDebugger)
						reportError(str);
				}
			}
			return 1;
		}

		/**
		 * @inheritDoc
		 */
		public function deriveStringFromNumber(number:Number):String
		{
			if (_numberToStringFunction != null)
				return _numberToStringFunction(number);

			if (_dateFormat)
				return formatDate(number);

			return new Date(number).toString();
		}

		/**
		 * @inheritDoc
		 */
		override public function getValueFromKey(key:IQualifiedKey, dataType:Class = null):*
		{
			var number:Number;
			var string:String;
			var date:Date;

			if (dataType == Number)
			{
				number = _keyToDate[key];
				return number;
			}

			if (dataType == String)
			{
				if (_numberToStringFunction != null)
				{
					number = _keyToDate[key];
					return _numberToStringFunction(number);
				}

				date = _keyToDate[key];

				if (!date)
					return '';

				if (_dateFormat)
					string = formatDate(date);
				else
					string = date.toString();

				return string;
			}

			date = _keyToDate[key];

			if (dataType)
				return date as DataType;

			return date;
		}

		override public function toString():String
		{
			return debugId(this) + '{recordCount: '+keys.length+', keyType: "'+getMetadata('keyType')+'", title: "'+getMetadata('title')+'"}';
		}

		private static function convertDateFormat_as_to_c(format:String):String
		{
			if (!format || format.indexOf('%') >= 0)
				return format;
			return StandardLib.replace.apply(null, [format].concat(dateFormat_replacements_as_to_c));
		}

		private static const dateFormat_replacements_as_to_c:Array = [
			'YYYY','%Y',
			'YY','%y',
			'MMMM','%B',
			'MMM','%b',
			'MM','%m',
			'M','%-m',
			'DD','%d',
			'D','%-d',
			'E','%u',
			'A','%p',
			'JJ','%H',
			'J','%-H',
			'LL','%I',
			'L','%-I',
			'EEEE','%A', // note this %A is after the A was replaced above
			'EEE','%a',
			'NN','%M', // note these %M appears after the M's were replaced above
			'N','%-M',
			'SS','%S'
			//,'S','%-S'
		];

		public static function detectDateFormats(dates:*):Array
		{
			return weave.flascc.dates_detect(dates, DATE_FORMAT_AUTO_DETECT);
		}

		public static const DATE_FORMAT_ADDITIONAL_SUGGESTIONS:Array = [
			"%Y"
		];
		public static const DATE_FORMAT_AUTO_DETECT:Array = [
			'%d-%b-%y',
			'%b-%d-%y',
			'%d-%b-%Y',
			'%b-%d-%Y',
			'%Y-%b-%d',

			'%d/%b/%y',
			'%b/%d/%y',
			'%d/%b/%Y',
			'%b/%d/%Y',
			'%Y/%b/%d',

			'%d.%b.%y',
			'%b.%d.%y',
			'%d.%b.%Y',
			'%b.%d.%Y',
			'%Y.%b.%d',

			'%d-%m-%y',
			'%m-%d-%y',
			'%d-%m-%Y',
			'%m-%d-%Y',
			'%Y-%m-%d',

			'%d/%m/%y',
			'%m/%d/%y',
			'%d/%m/%Y',
			'%m/%d/%Y',
			'%Y/%m/%d',

			'%d.%m.%y',
			'%m.%d.%y',
			'%d.%m.%Y',
			'%m.%d.%Y',
			'%Y.%m.%d',

			'%H:%M',
			'%H:%M:%S',
			'%a, %d %b %Y %H:%M:%S %z', // RFC_822

			// ISO_8601   http://www.thelinuxdaily.com/2014/03/c-function-to-validate-iso-8601-date-formats-using-strptime/
			"%Y-%m-%d",
			"%y-%m-%d",
			"%Y-%m-%d %T",
			"%y-%m-%d %T",
			"%Y-%m-%dT%T",
			"%y-%m-%dT%T",
			"%Y-%m-%dT%TZ",
			"%y-%m-%dT%TZ",
			"%Y-%m-%d %TZ",
			"%y-%m-%d %TZ",
			"%Y%m%dT%TZ",
			"%y%m%dT%TZ",
			"%Y%m%d %TZ",
			"%y%m%d %TZ",

			"%Y-%b-%d %T",
			"%Y-%b-%d %H:%M:%S",
			"%d-%b-%Y %T",
			"%d-%b-%Y %H:%M:%S",
			"%d-%b-%Y %H:%M:%S.%Q",

			/*
			//https://code.google.com/p/datejs/source/browse/trunk/src/globalization/en-US.js
			'M/d/yyyy',
			'dddd, MMMM dd, yyyy',
			"M/d/yyyy",
			"dddd, MMMM dd, yyyy",
			"h:mm tt",
			"h:mm:ss tt",
			"dddd, MMMM dd, yyyy h:mm:ss tt",
			"yyyy-MM-ddTHH:mm:ss",
			"yyyy-MM-dd HH:mm:ssZ",
			"ddd, dd MMM yyyy HH:mm:ss GMT",
			"MMMM dd",
			"MMMM, yyyy",

			//http://www.java2s.com/Code/Android/Date-Type/parseDateforlistofpossibleformats.htm
			"EEE, dd MMM yyyy HH:mm:ss z", // RFC_822
			"EEE, dd MMM yyyy HH:mm zzzz",
			"yyyy-MM-dd'T'HH:mm:ssZ",
			"yyyy-MM-dd'T'HH:mm:ss.SSSzzzz", // Blogger Atom feed has millisecs also
			"yyyy-MM-dd'T'HH:mm:sszzzz",
			"yyyy-MM-dd'T'HH:mm:ss z",
			"yyyy-MM-dd'T'HH:mm:ssz", // ISO_8601
			"yyyy-MM-dd'T'HH:mm:ss",
			"yyyy-MM-dd'T'HHmmss.SSSz",

			//http://stackoverflow.com/a/21737848
			"M/d/yyyy", "MM/dd/yyyy",
			"d/M/yyyy", "dd/MM/yyyy",
			"yyyy/M/d", "yyyy/MM/dd",
			"M-d-yyyy", "MM-dd-yyyy",
			"d-M-yyyy", "dd-MM-yyyy",
			"yyyy-M-d", "yyyy-MM-dd",
			"M.d.yyyy", "MM.dd.yyyy",
			"d.M.yyyy", "dd.MM.yyyy",
			"yyyy.M.d", "yyyy.MM.dd",
			"M,d,yyyy", "MM,dd,yyyy",
			"d,M,yyyy", "dd,MM,yyyy",
			"yyyy,M,d", "yyyy,MM,dd",
			"M d yyyy", "MM dd yyyy",
			"d M yyyy", "dd MM yyyy",
			"yyyy M d", "yyyy MM dd" */
		];
	}
}

Add a millisecond-resolution date format to the autodetection list.


////////////////////////////////////////////////////////////////////////////////
//
//  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.staticControls.beads
{
    import flash.display.DisplayObject;
    import flash.events.Event;

    import org.apache.flex.core.IBead;
    import org.apache.flex.core.IItemRendererClassFactory;
    import org.apache.flex.core.IItemRendererParent;
    import org.apache.flex.core.ISelectionModel;
    import org.apache.flex.core.IStrand;

	public class TextItemRendererFactoryForArrayData implements IBead
	{
		public function TextItemRendererFactoryForArrayData()
		{

		}

		private var selectionModel:ISelectionModel;

		private var _strand:IStrand;

		public function set strand(value:IStrand):void
		{
			_strand = value;
			selectionModel = value.getBeadByType(ISelectionModel) as ISelectionModel;
			var listBead:IListBead = value.getBeadByType(IListBead) as IListBead;
			dataGroup = listBead.dataGroup;
			selectionModel.addEventListener("dataProviderChange", dataProviderChangeHandler);
			dataProviderChangeHandler(null);
		}

        public var itemRendererFactory:IItemRendererClassFactory;

		public var dataGroup:IItemRendererParent;

		private function dataProviderChangeHandler(event:Event):void
		{
			var dp:Array = selectionModel.dataProvider as Array;
			var n:int = dp.length;
			for (var i:int = 0; i < n; i++)
			{
				var tf:ITextItemRenderer = itemRendererFactory.createItemRenderer(dataGroup) as ITextItemRenderer;
                tf.index = i;
				dataGroup.addChild(tf as DisplayObject);
				tf.text = dp[i];
			}
		}

	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  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.staticControls.beads
{
    import flash.display.DisplayObject;
    import flash.events.Event;

    import org.apache.flex.core.IBead;
    import org.apache.flex.core.IItemRendererClassFactory;
    import org.apache.flex.core.IItemRendererParent;
    import org.apache.flex.core.ISelectionModel;
    import org.apache.flex.core.IStrand;

	public class TextItemRendererFactoryForArrayData implements IBead
	{
		public function TextItemRendererFactoryForArrayData()
		{

		}

		private var selectionModel:ISelectionModel;

		private var _strand:IStrand;

		public function set strand(value:IStrand):void
		{
			_strand = value;
			selectionModel = value.getBeadByType(ISelectionModel) as ISelectionModel;
			var listBead:IListBead = value.getBeadByType(IListBead) as IListBead;
			dataGroup = listBead.dataGroup;
			selectionModel.addEventListener("dataProviderChanged", dataProviderChangeHandler);
			dataProviderChangeHandler(null);
		}

        public var itemRendererFactory:IItemRendererClassFactory;

		public var dataGroup:IItemRendererParent;

		private function dataProviderChangeHandler(event:Event):void
		{
			var dp:Array = selectionModel.dataProvider as Array;
			if (!dp)
				return;
			var n:int = dp.length;
			for (var i:int = 0; i < n; i++)
			{
				var tf:ITextItemRenderer = itemRendererFactory.createItemRenderer(dataGroup) as ITextItemRenderer;
                tf.index = i;
				dataGroup.addChild(tf as DisplayObject);
				tf.text = dp[i];
			}
		}

	}
}

handle null dataprovider

handle null dataprovider


package com.axis.audioclient {
  import com.axis.audioclient.IAudioClient;
  import com.axis.codec.g711;
  import com.axis.ErrorManager;
  import com.axis.http.auth;
  import com.axis.http.request;
  import com.axis.http.url;
  import com.axis.Logger;

  import flash.events.ErrorEvent;
  import flash.events.Event;
  import flash.events.IOErrorEvent;
  import flash.events.ProgressEvent;
  import flash.events.SampleDataEvent;
  import flash.events.SecurityErrorEvent;
  import flash.events.StatusEvent;
  import flash.external.ExternalInterface;
  import flash.media.Microphone;
  import flash.media.SoundCodec;
  import flash.net.Socket;
  import flash.utils.ByteArray;

  public class AxisTransmit implements IAudioClient {
    private static const EVENT_AUDIO_TRANSMIT_STARTED:String = "audioTransmitStarted";
    private static const EVENT_AUDIO_TRANSMIT_STOPPED:String = "audioTransmitStopped";

    private var urlParsed:Object = {};
    private var conn:Socket = new Socket();

    private var authState:String = 'none';
    private var authOpts:Object = {};

    private var savedUrl:String = null;

    private var mic:Microphone = Microphone.getMicrophone();
    private var _microphoneVolume:Number;

    private var currentState:String = "stopped";

    public function AxisTransmit() {
      /* Set default microphone volume */
      this.microphoneVolume = 50;
    }

    private function onMicStatus(event:StatusEvent):void {
      if (conn.connected) {
        authState = 'none';
        conn.close();
      }

      if ('Microphone.Muted' === event.code) {
        ErrorManager.dispatchError(816);
        return;
      }

      if (urlParsed.host && urlParsed.port) {
        conn.connect(urlParsed.host, urlParsed.port);
      }
    }

    public function start(iurl:String = null):void {
      if (conn.connected) {
        ErrorManager.dispatchError(817);
        return;
      }

      var currentUrl:String = (iurl) ? iurl : savedUrl;

      if (!currentUrl) {
        ErrorManager.dispatchError(818);
        return;
      }

      this.savedUrl = currentUrl;

      var mic:Microphone = Microphone.getMicrophone();

      if (null === mic) {
        ErrorManager.dispatchError(819);
        return;
      }

      mic.rate = 16;
      mic.setSilenceLevel(0, -1);
      mic.addEventListener(StatusEvent.STATUS, onMicStatus);
      mic.addEventListener(SampleDataEvent.SAMPLE_DATA, onMicSampleData);

      this.urlParsed = url.parse(currentUrl);

      conn = new Socket();
      conn.addEventListener(Event.CONNECT, onConnected);
      conn.addEventListener(Event.CLOSE, onClosed);
      conn.addEventListener(ProgressEvent.SOCKET_DATA, onRequestData);
      conn.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
      conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);

      if (true === mic.muted) {
        ErrorManager.dispatchError(816);
        return;
      }

      conn.connect(urlParsed.host, urlParsed.port);
    }

    public function stop():void {
      if (!conn.connected) {
        ErrorManager.dispatchError(813);
        this.callAPI(EVENT_AUDIO_TRANSMIT_STOPPED);
        return;
      }

      if (mic) {
        mic.removeEventListener(StatusEvent.STATUS, onMicStatus);
        mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, onMicSampleData);
      }
      conn.close();
    }

    private function writeAuthorizationHeader():void {
      var a:String = '';
      switch (authState) {
        case "basic":
          a = auth.basic(this.urlParsed.user, this.urlParsed.pass) + "\r\n";
          break;

        case "digest":
          a = auth.digest(
            this.urlParsed.user,
            this.urlParsed.pass,
            "POST",
            authOpts.digestRealm,
            urlParsed.urlpath,
            authOpts.qop,
            authOpts.nonce,
            1
          );
          break;

        default:
        case "none":
          return;
      }

      conn.writeUTFBytes('Authorization: ' + a + "\r\n");
    }

    private function onConnected(event:Event):void {
      conn.writeUTFBytes("POST " + this.urlParsed.urlpath + " HTTP/1.0\r\n");
      conn.writeUTFBytes("Content-Type: audio/axis-mulaw-128\r\n");
      conn.writeUTFBytes("Content-Length: 9999999\r\n");
      conn.writeUTFBytes("Connection: Keep-Alive\r\n");
      conn.writeUTFBytes("Cache-Control: no-cache\r\n");
      writeAuthorizationHeader();
      conn.writeUTFBytes("\r\n");
    }

    public function onClosed(event:Event):void {
      if ("playing" === this.currentState) {
        this.currentState = "stopped";
        this.callAPI(EVENT_AUDIO_TRANSMIT_STOPPED);
      }
    }

    private function onMicSampleData(event:SampleDataEvent):void {
      if (!conn.connected) {
        return;
      }

      if ("stopped" === this.currentState) {
        this.currentState = "playing";
        this.callAPI(EVENT_AUDIO_TRANSMIT_STARTED);
      }

      while (event.data.bytesAvailable) {
        var encoded:uint = g711.linearToMulaw(event.data.readFloat());
        conn.writeByte(encoded);
      }

      conn.flush();
    }

    private function onRequestData(event:ProgressEvent):void {
      var data:ByteArray = new ByteArray();
      var parsed:* = request.readHeaders(conn, data);

      if (false === parsed) {
        return;
      }

      if (401 === parsed.code) {
        /* Unauthorized, change authState and (possibly) try again */
        authOpts = parsed.headers['www-authenticate'];
        var newAuthState:String = auth.nextMethod(authState, authOpts);
        if (authState === newAuthState) {
          ErrorManager.dispatchError(parsed.code);
          return;
        }

        Logger.log('AxisTransmit: switching http-authorization from ' + authState + ' to ' + newAuthState);
        authState = newAuthState;
        conn.close();
        conn.connect(this.urlParsed.host, this.urlParsed.port);
        return;
      }
    }

    private function onIOError(event:IOErrorEvent):void {
      ErrorManager.dispatchError(732, [event.text]);
    }

    private function onSecurityError(event:SecurityErrorEvent):void {
      ErrorManager.dispatchError(731, [event.text]);
    }

    public function get microphoneVolume():Number {
      return _microphoneVolume;
    }

    public function set microphoneVolume(volume:Number):void {
      if (null === mic) {
        ErrorManager.dispatchError(819);
        return;
      }

      _microphoneVolume = volume;
      mic.gain = volume;

      if (volume && savedUrl)
        start();
    }

    public function muteMicrophone():void {
      if (null === mic) {
        ErrorManager.dispatchError(819);
        return;
      }

      mic.gain = 0;
      stop();
    }

    public function unmuteMicrophone():void {
      if (null === mic) {
        ErrorManager.dispatchError(819);
        return;
      }

      if (mic.gain !== 0)
        return;

      mic.gain = this.microphoneVolume;
      start();
    }

    private function callAPI(eventName:String, data:Object = null):void {
      var functionName:String = "LocomoteMap['" + Player.locomoteID + "'].__playerEvent";
      if (data) {
        ExternalInterface.call(functionName, eventName, data);
      } else {
        ExternalInterface.call(functionName, eventName);
      }
    }
  }
}

package com.axis.audioclient {
  import com.axis.audioclient.IAudioClient;
  import com.axis.codec.g711;
  import com.axis.ErrorManager;
  import com.axis.http.auth;
  import com.axis.http.request;
  import com.axis.http.url;
  import com.axis.Logger;

  import flash.events.ErrorEvent;
  import flash.events.Event;
  import flash.events.IOErrorEvent;
  import flash.events.ProgressEvent;
  import flash.events.SampleDataEvent;
  import flash.events.SecurityErrorEvent;
  import flash.events.StatusEvent;
  import flash.external.ExternalInterface;
  import flash.media.Microphone;
  import flash.media.SoundCodec;
  import flash.net.Socket;
  import flash.utils.ByteArray;

  public class AxisTransmit implements IAudioClient {
    private static const EVENT_AUDIO_TRANSMIT_STARTED:String = 'audioTransmitStarted';
    private static const EVENT_AUDIO_TRANSMIT_STOPPED:String = 'audioTransmitStopped';

    private var urlParsed:Object = {};
    private var conn:Socket = new Socket();

    private var authState:String = 'none';
    private var authOpts:Object = {};

    private var savedUrl:String = null;

    private var mic:Microphone = Microphone.getMicrophone();
    private var _microphoneVolume:Number;

    private var currentState:String = 'stopped';

    public function AxisTransmit() {
      /* Set default microphone volume */
      this.microphoneVolume = 50;
    }

    private function onMicStatus(event:StatusEvent):void {
      if (conn.connected) {
        authState = 'none';
        conn.close();
      }

      if ('Microphone.Muted' === event.code) {
        ErrorManager.dispatchError(816);
        return;
      }

      if (urlParsed.host && urlParsed.port) {
        conn.connect(urlParsed.host, urlParsed.port);
      }
    }

    public function start(iurl:String = null):void {
      if (conn.connected) {
        ErrorManager.dispatchError(817);
        return;
      }

      var currentUrl:String = (iurl) ? iurl : savedUrl;

      if (!currentUrl) {
        ErrorManager.dispatchError(818);
        return;
      }

      this.savedUrl = currentUrl;

      var mic:Microphone = Microphone.getMicrophone();

      if (null === mic) {
        ErrorManager.dispatchError(819);
        return;
      }

      mic.rate = 16;
      mic.setSilenceLevel(0, -1);
      mic.addEventListener(StatusEvent.STATUS, onMicStatus);
      mic.addEventListener(SampleDataEvent.SAMPLE_DATA, onMicSampleData);

      this.urlParsed = url.parse(currentUrl);

      conn = new Socket();
      conn.addEventListener(Event.CONNECT, onConnected);
      conn.addEventListener(Event.CLOSE, onClosed);
      conn.addEventListener(ProgressEvent.SOCKET_DATA, onRequestData);
      conn.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
      conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);

      if (true === mic.muted) {
        ErrorManager.dispatchError(816);
        return;
      }

      conn.connect(urlParsed.host, urlParsed.port);
    }

    public function stop():void {
      if (!conn.connected) {
        ErrorManager.dispatchError(813);
        return;
      }

      if (mic) {
        mic.removeEventListener(StatusEvent.STATUS, onMicStatus);
        mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, onMicSampleData);
      }
      this.currentState = 'stopped';
      this.callAPI(EVENT_AUDIO_TRANSMIT_STOPPED);
      conn.close();
    }

    private function writeAuthorizationHeader():void {
      var a:String = '';
      switch (authState) {
        case "basic":
          a = auth.basic(this.urlParsed.user, this.urlParsed.pass) + "\r\n";
          break;

        case "digest":
          a = auth.digest(
            this.urlParsed.user,
            this.urlParsed.pass,
            "POST",
            authOpts.digestRealm,
            urlParsed.urlpath,
            authOpts.qop,
            authOpts.nonce,
            1
          );
          break;

        default:
        case "none":
          return;
      }

      conn.writeUTFBytes('Authorization: ' + a + "\r\n");
    }

    private function onConnected(event:Event):void {
      conn.writeUTFBytes("POST " + this.urlParsed.urlpath + " HTTP/1.0\r\n");
      conn.writeUTFBytes("Content-Type: audio/axis-mulaw-128\r\n");
      conn.writeUTFBytes("Content-Length: 9999999\r\n");
      conn.writeUTFBytes("Connection: Keep-Alive\r\n");
      conn.writeUTFBytes("Cache-Control: no-cache\r\n");
      writeAuthorizationHeader();
      conn.writeUTFBytes("\r\n");
    }

    public function onClosed(event:Event):void {
      if ('playing' === this.currentState) {
        this.currentState = 'stopped';
        this.callAPI(EVENT_AUDIO_TRANSMIT_STOPPED);
      }
    }

    private function onMicSampleData(event:SampleDataEvent):void {
      if (!conn.connected) {
        return;
      }

      if ('stopped' === this.currentState) {
        this.currentState = 'playing';
        this.callAPI(EVENT_AUDIO_TRANSMIT_STARTED);
      }

      while (event.data.bytesAvailable) {
        var encoded:uint = g711.linearToMulaw(event.data.readFloat());
        conn.writeByte(encoded);
      }

      conn.flush();
    }

    private function onRequestData(event:ProgressEvent):void {
      var data:ByteArray = new ByteArray();
      var parsed:* = request.readHeaders(conn, data);

      if (false === parsed) {
        return;
      }

      if (401 === parsed.code) {
        /* Unauthorized, change authState and (possibly) try again */
        authOpts = parsed.headers['www-authenticate'];
        var newAuthState:String = auth.nextMethod(authState, authOpts);
        if (authState === newAuthState) {
          ErrorManager.dispatchError(parsed.code);
          return;
        }

        Logger.log('AxisTransmit: switching http-authorization from ' + authState + ' to ' + newAuthState);
        authState = newAuthState;
        conn.close();
        conn.connect(this.urlParsed.host, this.urlParsed.port);
        return;
      }
    }

    private function onIOError(event:IOErrorEvent):void {
      ErrorManager.dispatchError(732, [event.text]);
    }

    private function onSecurityError(event:SecurityErrorEvent):void {
      ErrorManager.dispatchError(731, [event.text]);
    }

    public function get microphoneVolume():Number {
      return _microphoneVolume;
    }

    public function set microphoneVolume(volume:Number):void {
      if (null === mic) {
        ErrorManager.dispatchError(819);
        return;
      }

      _microphoneVolume = volume;
      mic.gain = volume;

      if (volume && savedUrl)
        start();
    }

    public function muteMicrophone():void {
      if (null === mic) {
        ErrorManager.dispatchError(819);
        return;
      }

      mic.gain = 0;
      stop();
    }

    public function unmuteMicrophone():void {
      if (null === mic) {
        ErrorManager.dispatchError(819);
        return;
      }

      if (mic.gain !== 0)
        return;

      mic.gain = this.microphoneVolume;
      start();
    }

    private function callAPI(eventName:String, data:Object = null):void {
      var functionName:String = "LocomoteMap['" + Player.locomoteID + "'].__playerEvent";
      if (data) {
        ExternalInterface.call(functionName, eventName, data);
      } else {
        ExternalInterface.call(functionName, eventName);
      }
    }
  }
}

Fix AxisTransmit events

Fix AxisTransmit events


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

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

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

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

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

		private var _bindings			: Object							= null;

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

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

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

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

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

		private var _colorMask			: uint								= 0;
		private var _colorMaskR			: Boolean							= true;
		private var _colorMaskG			: Boolean							= true;
		private var _colorMaskB			: Boolean							= true;
		private var _colorMaskA			: Boolean							= true;

		private var _enabled			: Boolean							= true;

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

		private var _bindingsConsumer	: DrawCallBindingsConsumer;

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

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

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

		public function get colorMask() : uint
		{
			return _colorMask;
		}
		public function set colorMask(value : uint) : void
		{
			_colorMask = value;
			_colorMaskR = (value & ColorMask.RED) != 0;
			_colorMaskG = (value & ColorMask.GREEN) != 0;
			_colorMaskB = (value & ColorMask.BLUE) != 0;
			_colorMaskA = (value & ColorMask.ALPHA) != 0;
		}

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

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

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

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

			return _depth;
		}

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

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

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

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

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

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

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

			triangleCulling		= TriangleCulling.FRONT;
			blending			= Blending.NORMAL;
			colorMask			= ColorMask.RGBA;
		}

		 /**
		  * Ask geometry to compute additional vertex data if needed for this drawcall.
		  */
		public function updateGeometry(geometry : Geometry) : void
		{
			var vertexFormat    : VertexFormat	= geometry.format;
            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
		{
			if (!_enabled)
				return 0;

			context.setColorMask(_colorMaskR, _colorMaskG, _colorMaskB, _colorMaskA)
				   .setProgramConstantsFromVector(PROGRAM_TYPE_VERTEX, 0, _vsConstants)
				   .setProgramConstantsFromVector(PROGRAM_TYPE_FRAGMENT, 0, _fsConstants);

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

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

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

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

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

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

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

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

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

			_invalidDepth = true;
		}
	}
}

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

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

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

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

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

		private var _bindings			: Object							= null;

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

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

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

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

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

		private var _colorMask			: uint								= 0;
		private var _colorMaskR			: Boolean							= true;
		private var _colorMaskG			: Boolean							= true;
		private var _colorMaskB			: Boolean							= true;
		private var _colorMaskA			: Boolean							= true;

		private var _enabled			: Boolean							= true;

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

		private var _bindingsConsumer	: DrawCallBindingsConsumer;

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

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

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

		public function get colorMask() : uint
		{
			return _colorMask;
		}
		public function set colorMask(value : uint) : void
		{
			_colorMask = value;
			_colorMaskR = (value & ColorMask.RED) != 0;
			_colorMaskG = (value & ColorMask.GREEN) != 0;
			_colorMaskB = (value & ColorMask.BLUE) != 0;
			_colorMaskA = (value & ColorMask.ALPHA) != 0;
		}

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

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

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

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

			return _depth;
		}

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

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

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

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

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

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

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

			triangleCulling		= TriangleCulling.FRONT;
			blending			= Blending.NORMAL;
			colorMask			= ColorMask.RGBA;
		}

		 /**
		  * Ask geometry to compute additional vertex data if needed for this drawcall.
		  */
		public function updateGeometry(geometry : Geometry) : void
		{
			var vertexFormat    : VertexFormat	= geometry.format;
            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.setColorMask(_colorMaskR, _colorMaskG, _colorMaskB, _colorMaskA)
				   .setProgramConstantsFromVector(PROGRAM_TYPE_VERTEX, 0, _vsConstants)
				   .setProgramConstantsFromVector(PROGRAM_TYPE_FRAGMENT, 0, _fsConstants);

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

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

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

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

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

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

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

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

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

			_invalidDepth = true;
		}
	}
}

remove test of _enabled because it should now be done before calling DrawCall.apply()


package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.material.Material;
	import aerys.minko.render.material.basic.BasicMaterial;
	import aerys.minko.scene.controller.mesh.MeshVisibilityController;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.DataProvider;
	import aerys.minko.type.enum.DataProviderUsage;
	import aerys.minko.type.enum.FrustumCulling;
	import aerys.minko.type.math.Ray;

	use namespace minko_scene;

	/**
	 * Mesh objects are a visible instance of a Geometry rendered using a specific
	 * Effect with specific rendering properties.
	 *
	 *

	 * Those rendering properties are stored in a DataBindings object so they can
	 * be directly used by the shaders in the rendering API.
	 *

	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class Mesh extends AbstractSceneNode
	{
		public static const DEFAULT_MATERIAL	: Material	= new BasicMaterial();

		private var _geometry			: Geometry;
		private var _properties			: DataProvider;
		private var _material			: Material;
		private var _bindings			: DataBindings;

		private var _visibility			: MeshVisibilityController;

		private var _frame				: uint;

		private var _cloned				: Signal;
		private var _materialChanged	: Signal;
		private var _frameChanged		: Signal;
		private var _geometryChanged	: Signal;

		/**
		 * A DataProvider object already bound to the Mesh bindings.
		 *
		 *

		 * // set the "diffuseColor" property to 0x0000ffff
		 * mesh.properties.diffuseColor = 0x0000ffff;
		 *
		 * // animate the "diffuseColor" property
		 * mesh.addController(
		 *   new AnimationController(
		 * 	  new <ITimeline>[new ColorTimeline(
		 * 	    "dataProvider.diffuseColor",
		 * 	    5000,
		 * 	    new <uint>[0xffffffff, 0xffffff00, 0xffffffff]
		 *    )]
		 *   )
		 * );
		 *

		 *
		 * @return
		 *
		 */
		public function get properties() : DataProvider
		{
			return _properties;
		}

		public function set properties(value : DataProvider) : void
		{
			if (_properties != value)
			{
				if (_properties)
					_bindings.removeProvider(_properties);

				_properties = value;

				if (value)
					_bindings.addProvider(value);
			}
		}

		public function get material() : Material
		{
			return _material;
		}
		public function set material(value : Material) : void
		{
			if (_material != value)
			{
				if (_material)
					_bindings.removeProvider(_material);

				var oldMaterial : Material = _material;

				_material = value;

				if (value)
					_bindings.addProvider(value);

				_materialChanged.execute(this, oldMaterial, value);
			}
		}

		/**
		 * The rendering properties provided to the shaders to customize
		 * how the mesh will appear on screen.
		 *
		 * @return
		 *
		 */
		public function get bindings() : DataBindings
		{
			return _bindings;
		}

		/**
		 * The Geometry of the mesh.
		 * @return
		 *
		 */
		public function get geometry() : Geometry
		{
			return _geometry;
		}

		public function set geometry(value : Geometry) : void
		{
			if (_geometry != value)
			{
				var oldGeometry	: Geometry = _geometry;

				if (oldGeometry)
					oldGeometry.changed.remove(geometryChangedHandler);

				_geometry = value;

				if (value)
					_geometry.changed.add(geometryChangedHandler);

				_geometryChanged.execute(this, oldGeometry, value);
			}
		}

        /**
         * Whether the mesh in inside the camera frustum or not.
         * @return
         *
         */
        public function get insideFrustum() : Boolean
        {
            return _visibility.insideFrustum;
        }

        override public function get computedVisibility() : Boolean
        {
            return scene ? _visibility.computedVisibility : super.computedVisibility;
        }

		public function get frustumCulling() : uint
		{
			return _visibility.frustumCulling;
		}
		public function set frustumCulling(value : uint) : void
		{
			_visibility.frustumCulling = value;
		}

		public function get cloned() : Signal
		{
			return _cloned;
		}

		public function get materialChanged() : Signal
		{
			return _materialChanged;
		}

		public function get frameChanged() : Signal
		{
			return _frameChanged;
		}

		public function get geometryChanged() : Signal
		{
			return _geometryChanged;
		}

		public function get frame() : uint
		{
			return _frame;
		}
		public function set frame(value : uint) : void
		{
			if (_frame != value)
			{
				var oldFrame : uint = _frame;

				_frame = value;
				_frameChanged.execute(this, oldFrame, value);
			}
		}

		public function Mesh(geometry	: Geometry	= null,
							 material	: Material	= null,
							 name		: String	= null)
		{
			super();

			initialize(geometry, material, name);
		}

		private function initialize(geometry	: Geometry,
									material	: Material,
									name		: String) : void
		{
			if (name)
				this.name = name;

			_cloned = new Signal('Mesh.clones');
			_materialChanged = new Signal('Mesh.materialChanged');
			_frameChanged = new Signal('Mesh.frameChanged');
			_geometryChanged = new Signal('Mesh.geometryChanged');

			_bindings = new DataBindings(this);
			this.properties = new DataProvider(properties, 'meshProperties', DataProviderUsage.EXCLUSIVE);

			this.geometry = geometry;
			this.material = material || DEFAULT_MATERIAL;

			_visibility = new MeshVisibilityController();
			_visibility.frustumCulling = FrustumCulling.ENABLED;
			addController(_visibility);
		}

		public function cast(ray : Ray, maxDistance : Number = Number.POSITIVE_INFINITY) : Number
		{
			return _geometry.boundingBox.testRay(
				ray,
				worldToLocal,
				maxDistance
			);
		}

//        override protected function visibilityChangedHandler(node       : AbstractSceneNode,
//                                                             visibility : Boolean) : void
//        {
//            // nothing
//        }

		override minko_scene function cloneNode() : AbstractSceneNode
		{
			var clone : Mesh = new Mesh();

			clone.name 		= name;
			clone.geometry 	= _geometry;

			clone.properties = DataProvider(_properties.clone());
			clone._bindings.copySharedProvidersFrom(_bindings);

			clone.transform.copyFrom(transform);
			clone.material = _material;

			this.cloned.execute(this, clone);

			return clone;
		}

		private function geometryChangedHandler(geometry : Geometry) : void
		{
			_geometryChanged.execute(this, _geometry, _geometry);
		}
	}
}

package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.geometry.Geometry;
	import aerys.minko.render.material.Material;
	import aerys.minko.render.material.basic.BasicMaterial;
	import aerys.minko.scene.controller.mesh.MeshVisibilityController;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.DataProvider;
	import aerys.minko.type.enum.DataProviderUsage;
	import aerys.minko.type.enum.FrustumCulling;
	import aerys.minko.type.math.Ray;

	use namespace minko_scene;

	/**
	 * Mesh objects are a visible instance of a Geometry rendered using a specific
	 * Effect with specific rendering properties.
	 *
	 *

	 * Those rendering properties are stored in a DataBindings object so they can
	 * be directly used by the shaders in the rendering API.
	 *

	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class Mesh extends AbstractSceneNode
	{
		public static const DEFAULT_MATERIAL	: Material	= new BasicMaterial();

		private var _geometry			: Geometry;
		private var _properties			: DataProvider;
		private var _material			: Material;
		private var _bindings			: DataBindings;

		private var _visibility			: MeshVisibilityController;

		private var _frame				: uint;

		private var _cloned				: Signal;
		private var _materialChanged	: Signal;
		private var _frameChanged		: Signal;
		private var _geometryChanged	: Signal;

		/**
		 * A DataProvider object already bound to the Mesh bindings.
		 *
		 *

		 * // set the "diffuseColor" property to 0x0000ffff
		 * mesh.properties.diffuseColor = 0x0000ffff;
		 *
		 * // animate the "diffuseColor" property
		 * mesh.addController(
		 *   new AnimationController(
		 * 	  new <ITimeline>[new ColorTimeline(
		 * 	    "dataProvider.diffuseColor",
		 * 	    5000,
		 * 	    new <uint>[0xffffffff, 0xffffff00, 0xffffffff]
		 *    )]
		 *   )
		 * );
		 *

		 *
		 * @return
		 *
		 */
		public function get properties() : DataProvider
		{
			return _properties;
		}

		public function set properties(value : DataProvider) : void
		{
			if (_properties != value)
			{
				if (_properties)
					_bindings.removeProvider(_properties);

				_properties = value;

				if (value)
					_bindings.addProvider(value);
			}
		}

		public function get material() : Material
		{
			return _material;
		}
		public function set material(value : Material) : void
		{
			if (_material != value)
			{
				if (_material)
					_bindings.removeProvider(_material);

				var oldMaterial : Material = _material;

				_material = value;

				if (value)
					_bindings.addProvider(value);

				_materialChanged.execute(this, oldMaterial, value);
			}
		}

		/**
		 * The rendering properties provided to the shaders to customize
		 * how the mesh will appear on screen.
		 *
		 * @return
		 *
		 */
		public function get bindings() : DataBindings
		{
			return _bindings;
		}

		/**
		 * The Geometry of the mesh.
		 * @return
		 *
		 */
		public function get geometry() : Geometry
		{
			return _geometry;
		}

		public function set geometry(value : Geometry) : void
		{
			if (_geometry != value)
			{
				var oldGeometry	: Geometry = _geometry;

				if (oldGeometry)
					oldGeometry.changed.remove(geometryChangedHandler);

				_geometry = value;

				if (value)
					_geometry.changed.add(geometryChangedHandler);

				_geometryChanged.execute(this, oldGeometry, value);
			}
		}

        /**
         * Whether the mesh in inside the camera frustum or not.
         * @return
         *
         */
        public function get insideFrustum() : Boolean
        {
            return _visibility.insideFrustum;
        }

        override public function get computedVisibility() : Boolean
        {
            return scene ? _visibility.computedVisibility : super.computedVisibility;
        }

		public function get frustumCulling() : uint
		{
			return _visibility.frustumCulling;
		}
		public function set frustumCulling(value : uint) : void
		{
			_visibility.frustumCulling = value;
		}

		public function get cloned() : Signal
		{
			return _cloned;
		}

		public function get materialChanged() : Signal
		{
			return _materialChanged;
		}

		public function get frameChanged() : Signal
		{
			return _frameChanged;
		}

		public function get geometryChanged() : Signal
		{
			return _geometryChanged;
		}

		public function get frame() : uint
		{
			return _frame;
		}
		public function set frame(value : uint) : void
		{
			if (_frame != value)
			{
				var oldFrame : uint = _frame;

				_frame = value;
				_frameChanged.execute(this, oldFrame, value);
			}
		}

		public function Mesh(geometry	: Geometry	= null,
							 material	: Material	= null,
							 name		: String	= null)
		{
			super();

			initialize(geometry, material, name);
		}

		private function initialize(geometry	: Geometry,
									material	: Material,
									name		: String) : void
		{
			if (name)
				this.name = name;

			_cloned = new Signal('Mesh.cloned');
			_materialChanged = new Signal('Mesh.materialChanged');
			_frameChanged = new Signal('Mesh.frameChanged');
			_geometryChanged = new Signal('Mesh.geometryChanged');

			_bindings = new DataBindings(this);
			this.properties = new DataProvider(properties, 'meshProperties', DataProviderUsage.EXCLUSIVE);

			this.geometry = geometry;
			this.material = material || DEFAULT_MATERIAL;

			_visibility = new MeshVisibilityController();
			_visibility.frustumCulling = FrustumCulling.ENABLED;
			addController(_visibility);
		}

		public function cast(ray : Ray, maxDistance : Number = Number.POSITIVE_INFINITY) : Number
		{
			return _geometry.boundingBox.testRay(
				ray,
				worldToLocal,
				maxDistance
			);
		}

//        override protected function visibilityChangedHandler(node       : AbstractSceneNode,
//                                                             visibility : Boolean) : void
//        {
//            // nothing
//        }

		override minko_scene function cloneNode() : AbstractSceneNode
		{
			var clone : Mesh = new Mesh();

			clone.name 		= name;
			clone.geometry 	= _geometry;

			clone.properties = DataProvider(_properties.clone());
			clone._bindings.copySharedProvidersFrom(_bindings);

			clone.transform.copyFrom(transform);
			clone.material = _material;

			this.cloned.execute(this, clone);

			return clone;
		}

		private function geometryChangedHandler(geometry : Geometry) : void
		{
			_geometryChanged.execute(this, _geometry, _geometry);
		}
	}
}

fix typo

fix typo


// vim: tabstop=4 shiftwidth=4

// Copyright (c) 2010 , NetEase.com,Inc. All rights reserved.
//
// Author: Yang Bo (pop.atry@gmail.com)
//
// Use, modification and distribution are subject to the "New BSD License"
// as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.

package com.netease.protobuf {
	import flash.net.*;
	import flash.utils.*;
	import flash.events.*;
	public final class SimpleWebRPC {
		private var urlPrefix:String
		public function SimpleWebRPC(urlPrefix:String) {
			this.urlPrefix = urlPrefix;
		}
		private static const REF:Dictionary = new Dictionary();
		public function send(qualifiedMethodName:String,
							 input:IExternalizable,
							 callback:Function,
							 outputType:Class):void {

			const loader:URLLoader = new URLLoader
			REF[loader] = true;
			loader.dataFormat = URLLoaderDataFormat.BINARY
			loader.addEventListener(Event.COMPLETE, function(event:Event):void {
				delete REF[loader]
				const output:IExternalizable = new outputType
				output.readExternal(loader.data)
				callback(output)
			})
			function errorEventHandler(event:Event):void {
				delete REF[loader]
				callback(event)
			}
			loader.addEventListener(IOErrorEvent.IO_ERROR, errorEventHandler)
			loader.addEventListener(
					SecurityErrorEvent.SECURITY_ERROR, errorEventHandler)
			const request:URLRequest = new URLRequest(
				urlPrefix + qualifiedMethodName)
			request.method = URLRequestMethod.POST
			const requestContent:ByteArray = new ByteArray
			input.writeExternal(requestContent)
			request.data = requestContent
			request.contentType = "application/x-protobuf"
			loader.load(request)
		}
	}
}

// vim: tabstop=4 shiftwidth=4

// Copyright (c) 2010 , NetEase.com,Inc. All rights reserved.
//
// Author: Yang Bo (pop.atry@gmail.com)
//
// Use, modification and distribution are subject to the "New BSD License"
// as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.

package com.netease.protobuf {
	import flash.net.*;
	import flash.utils.*;
	import flash.events.*;
	public final class SimpleWebRPC {
		private var urlPrefix:String
		public function SimpleWebRPC(urlPrefix:String) {
			this.urlPrefix = urlPrefix;
		}
		private static const REF:Dictionary = new Dictionary();
		public function send(qualifiedMethodName:String,
							 input:IExternalizable,
							 rpcResult:Function,
							 outputType:Class):void {

			const loader:URLLoader = new URLLoader
			REF[loader] = true;
			loader.dataFormat = URLLoaderDataFormat.BINARY
			loader.addEventListener(Event.COMPLETE, function(event:Event):void {
				delete REF[loader]
				const output:IExternalizable = new outputType
				output.readExternal(loader.data)
				rpcResult(output)
			})
			function errorEventHandler(event:Event):void {
				delete REF[loader]
				rpcResult(event)
			}
			loader.addEventListener(IOErrorEvent.IO_ERROR, errorEventHandler)
			loader.addEventListener(
					SecurityErrorEvent.SECURITY_ERROR, errorEventHandler)
			const request:URLRequest = new URLRequest(
				urlPrefix + qualifiedMethodName)
			request.method = URLRequestMethod.POST
			const requestContent:ByteArray = new ByteArray
			input.writeExternal(requestContent)
			request.data = requestContent
			request.contentType = "application/x-protobuf"
			loader.load(request)
		}
	}
}

把另一处 callback 也改成其他名字

把另一处 callback 也改成其他名字


/*
 * Copyright 2008 Adobe Systems Inc., 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributor(s):
 *   Zwetan Kjukov <zwetan@gmail.com>.
 */

package com.google.analytics.core
{
	import library.ASTUce.framework.TestCase;

    import com.google.analytics.utils.Environment;
    import com.google.analytics.utils.FakeEnvironment;
    import com.google.analytics.utils.Variables;
    import com.google.analytics.utils.Version;
    import com.google.analytics.v4.Configuration;

    public class BrowserInfoTest extends TestCase
    {
        private var _config:Configuration;
        private var _browserInfo0:BrowserInfo;
        private var _env0:Environment;

        public function BrowserInfoTest(name:String="")
        {
            super(name);
        }

        public function testHitId():void {
        	assertEquals(1,1);

        }

        public function setUp():void
        {
            _config = new Configuration();
            _env0 = new FakeEnvironment("",null,"","","","","","",new Version(9,0,115,0),"en-GB","UTF-8","","","",null,800,600,"24");
            _browserInfo0 = new BrowserInfo( _config, _env0 );
        }

        public function testFlashVersion():void
        {
            assertEquals( "9.0 r115", _browserInfo0.utmfl );
        }

        public function testScreenInfo():void
        {
            assertEquals( "800x600", _browserInfo0.utmsr );
            assertEquals( "24-bit", _browserInfo0.utmsc );
        }

        public function testLangInfo():void
        {
            assertEquals( "en-gb", _browserInfo0.utmul );
            assertEquals( "UTF-8", _browserInfo0.utmcs );
        }

        public function testToVariables():void
        {
            var vars:Variables = _browserInfo0.toVariables();

            assertEquals( "UTF-8",    vars.utmcs );
            assertEquals( "800x600",  vars.utmsr );
            assertEquals( "24-bit",   vars.utmsc );
            assertEquals( "en-gb",    vars.utmul );
            assertEquals( "0",        vars.utmje );
            assertEquals( "9.0 r115", vars.utmfl );
        }

        public function testToURLString():void
        {
            var vars:Variables = _browserInfo0.toVariables();

            var varsA:Variables = new Variables();
                varsA.URIencode = true;
                varsA.utmcs = vars.utmcs;
            assertEquals( "utmcs=UTF-8", varsA.toString() );

            var varsB:Variables = new Variables();
                varsB.URIencode = true;
                varsB.utmsr = vars.utmsr;
            assertEquals( "utmsr=800x600", varsB.toString() );

            var varsC:Variables = new Variables();
                varsC.URIencode = true;
                varsC.utmsc = vars.utmsc;
            assertEquals( "utmsc=24-bit", varsC.toString() );

            var varsD:Variables = new Variables();
                varsD.URIencode = true;
                varsD.utmul = vars.utmul;
            assertEquals( "utmul=en-gb", varsD.toString() );

            var varsE:Variables = new Variables();
                varsE.URIencode = true;
                varsE.utmje = vars.utmje;
            assertEquals( "utmje=0", varsE.toString() );

            var varsF:Variables = new Variables();
                varsF.URIencode = true;
                varsF.utmfl = vars.utmfl;
            assertEquals( "utmfl=9.0%20r115", varsF.toString() );
        }

    }
}

/*
 * Copyright 2008 Adobe Systems Inc., 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributor(s):
 *   Zwetan Kjukov <zwetan@gmail.com>.
 */

package com.google.analytics.core
{
	import com.google.analytics.utils.Environment;
	import com.google.analytics.utils.FakeEnvironment;
	import com.google.analytics.utils.Variables;
	import com.google.analytics.v4.Configuration;

	import core.version;

	import library.ASTUce.framework.TestCase;

    public class BrowserInfoTest extends TestCase
    {
        private var _config:Configuration;
        private var _browserInfo0:BrowserInfo;
        private var _env0:Environment;

        public function BrowserInfoTest(name:String="")
        {
            super(name);
        }

        public function testHitId():void {
        	assertEquals(1,1);

        }

        public function setUp():void
        {
            _config = new Configuration();
            _env0 = new FakeEnvironment("",null,"","","","","","",new version(9,0,115,0),"en-GB","UTF-8","","","",null,800,600,"24");
            _browserInfo0 = new BrowserInfo( _config, _env0 );
        }

        public function testFlashVersion():void
        {
            assertEquals( "9.0 r115", _browserInfo0.utmfl );
        }

        public function testScreenInfo():void
        {
            assertEquals( "800x600", _browserInfo0.utmsr );
            assertEquals( "24-bit", _browserInfo0.utmsc );
        }

        public function testLangInfo():void
        {
            assertEquals( "en-gb", _browserInfo0.utmul );
            assertEquals( "UTF-8", _browserInfo0.utmcs );
        }

        public function testToVariables():void
        {
            var vars:Variables = _browserInfo0.toVariables();

            assertEquals( "UTF-8",    vars.utmcs );
            assertEquals( "800x600",  vars.utmsr );
            assertEquals( "24-bit",   vars.utmsc );
            assertEquals( "en-gb",    vars.utmul );
            assertEquals( "0",        vars.utmje );
            assertEquals( "9.0 r115", vars.utmfl );
        }

        public function testToURLString():void
        {
            var vars:Variables = _browserInfo0.toVariables();

            var varsA:Variables = new Variables();
                varsA.URIencode = true;
                varsA.utmcs = vars.utmcs;
            assertEquals( "utmcs=UTF-8", varsA.toString() );

            var varsB:Variables = new Variables();
                varsB.URIencode = true;
                varsB.utmsr = vars.utmsr;
            assertEquals( "utmsr=800x600", varsB.toString() );

            var varsC:Variables = new Variables();
                varsC.URIencode = true;
                varsC.utmsc = vars.utmsc;
            assertEquals( "utmsc=24-bit", varsC.toString() );

            var varsD:Variables = new Variables();
                varsD.URIencode = true;
                varsD.utmul = vars.utmul;
            assertEquals( "utmul=en-gb", varsD.toString() );

            var varsE:Variables = new Variables();
                varsE.URIencode = true;
                varsE.utmje = vars.utmje;
            assertEquals( "utmje=0", varsE.toString() );

            var varsF:Variables = new Variables();
                varsF.URIencode = true;
                varsF.utmfl = vars.utmfl;
            assertEquals( "utmfl=9.0%20r115", varsF.toString() );
        }

    }
}

replace class Version by core.version

replace class Version by core.version