package aerys.minko.type.binding
{
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.Signal;
	import aerys.minko.type.enum.DataProviderUsage;

	import flash.utils.Dictionary;

	public final class DataBindings
	{
		private var _owner						: ISceneNode;

		private var _providers					: Vector.<IDataProvider>;
		private var _bindingNames				: Vector.<String>;

		private var _bindingNameToValue			: Object;
		private var _bindingNameToChangedSignal	: Object;

		private var _bindingNameToProvider		: Object;
		private var _providerToBindingNames		: Dictionary;

		private var _consumers					: Vector.<IDataBindingsConsumer>;

		public function get owner() : ISceneNode
		{
			return _owner;
		}

		public function get numProviders() : uint
		{
			return _providers.length;
		}

		public function get numProperties() : uint
		{
			return _bindingNames.length;
		}

		public function DataBindings(owner : ISceneNode)
		{
			_owner = owner;

			_providers = new <IDataProvider>[];
			_bindingNames = new <String>[];
			_bindingNameToValue = {};
			_bindingNameToChangedSignal = {};
			_bindingNameToProvider = {};
			_providerToBindingNames = new Dictionary(true);
			_consumers = new <IDataBindingsConsumer>[];
		}

		public function contains(dataProvider : IDataProvider) : Boolean
		{
			return _providers.indexOf(dataProvider) != -1;
		}

		public function addProvider(provider : IDataProvider) : void
		{
			if (_providerToBindingNames[provider])
				throw new Error('This provider is already bound.');

			var dataDescriptor	: Object	= provider.dataDescriptor;

			provider.propertyChanged.add(propertyChangedHandler);
			if (provider is IDynamicDataProvider)
			{
				var dynamicProvider : IDynamicDataProvider = provider as IDynamicDataProvider;

				dynamicProvider.propertyAdded.add(addBinding);
				dynamicProvider.propertyRemoved.add(removeBinding);
			}

			_providerToBindingNames[provider] = new <String>[];
			_providers.push(provider);

			for (var propertyName : String in dataDescriptor)
				addBinding(
					provider,
					propertyName,
					dataDescriptor[propertyName],
					provider[propertyName]
				);
		}

		private function addBinding(provider		: IDataProvider,
									propertyName	: String,
									bindingName		: String,
									value			: Object) : void
		{
			var providerBindingNames	: Vector.<String> 	= _providerToBindingNames[provider];

			if (_bindingNames.indexOf(bindingName) != -1)
				throw new Error(
					'Another data provider is already declaring the \'' + bindingName
					+ '\' property.'
				);

			_bindingNameToProvider[bindingName] = provider;
			_bindingNameToValue[bindingName] = value;

			providerBindingNames.push(bindingName);
			_bindingNames.push(bindingName);

            var numConsumers : uint = _consumers.length;
            for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
                _consumers[consumerId].setProperty(bindingName, value);

			if (_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName].execute(this, bindingName, null, value);
		}

		public function removeProvider(provider : IDataProvider) : void
		{
			var providerBindingsNames 	: Vector.<String> 	= _providerToBindingNames[provider];

			if (providerBindingsNames == null)
				throw new ArgumentError('Unknown provider.');

			var numProviders : uint = _providers.length - 1;

			provider.propertyChanged.remove(propertyChangedHandler);
			if (provider is IDynamicDataProvider)
			{
				var dynamicProvider : IDynamicDataProvider = provider as IDynamicDataProvider;

				dynamicProvider.propertyAdded.remove(addBinding);
				dynamicProvider.propertyRemoved.remove(removeBinding);
			}

			_providers[_providers.indexOf(provider)] = _providers[numProviders];
			_providers.length = numProviders;
			delete _providerToBindingNames[provider];

			var dataDescriptor : Object = provider.dataDescriptor;

			for (var propertyName : String in dataDescriptor)
				removeBinding(
					provider,
					propertyName,
					dataDescriptor[propertyName],
					provider[propertyName]
				);
		}

		public function removeBinding(provider		: IDataProvider,
									  propertyName	: String,
									  bindingName	: String,
									  value			: Object) : void
		{
			var numBindings 	: uint 		= _bindingNames.length - 1;
			var changedSignal 	: Signal 	= _bindingNameToChangedSignal[bindingName] as Signal;

			delete _bindingNameToValue[bindingName];
			delete _bindingNameToProvider[bindingName];

			_bindingNames[_bindingNames.indexOf(bindingName)] = _bindingNames[numBindings];
			_bindingNames.length = numBindings;

            var numConsumers : uint = _consumers.length;
            for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
                _consumers[consumerId].setProperty(bindingName, null);

			if (changedSignal != null)
				changedSignal.execute(this, bindingName, value, null);
		}

		public function removeAllProviders() : void
		{
			var numProviders : uint = this.numProviders;

			for (var providerId : int = numProviders - 1; providerId >= 0; --providerId)
				removeProvider(getProviderAt(providerId));
		}

		public function hasCallback(bindingName	: String,
									callback	: Function) : Boolean
		{
			var signal : Signal = _bindingNameToChangedSignal[bindingName];

			return signal != null && signal.hasCallback(callback);
		}

		public function addCallback(bindingName : String,
									callback	: Function) : void
		{
			_bindingNameToChangedSignal[bindingName] ||= new Signal(
				'DataBindings.changed[' + bindingName + ']'
			);

			Signal(_bindingNameToChangedSignal[bindingName]).add(callback);
		}

		public function removeCallback(bindingName	: String,
									   callback		: Function) : void
		{
			var signal : Signal = _bindingNameToChangedSignal[bindingName];

			if (!signal)
				throw new ArgumentError('Unkown property \'' + bindingName + '\'.');

			signal.remove(callback);

			if (signal.numCallbacks == 0)
				delete _bindingNameToChangedSignal[bindingName];
		}

		public function getProviderAt(index : uint) : IDataProvider
		{
			return _providers[index];
		}

		public function getProviderByBindingName(bindingName : String) : IDataProvider
		{
			if (_bindingNameToProvider[bindingName] == null)
				throw new ArgumentError('Unkown property \'' + bindingName + '\'.');

			return _bindingNameToProvider[bindingName];
		}

		public function propertyExists(bindingName : String) : Boolean
		{
			return _bindingNameToValue.hasOwnProperty(bindingName);
		}

		public function getProperty(bindingName : String) : *
		{
			if (_bindingNames.indexOf(bindingName) < 0)
				throw new Error('The property \'' + bindingName + '\' does not exist.');

			return _bindingNameToValue[bindingName];
		}

		public function getPropertyName(bindingIndex : uint) : String
		{
			if (bindingIndex > numProperties)
				throw new ArgumentError('No such binding');

			return _bindingNames[bindingIndex];
		}

		public function copySharedProvidersFrom(source : DataBindings) : void
		{
			var numProviders : uint = source._providers.length;

			for (var providerId : uint = 0; providerId < numProviders; ++providerId)
			{
				var provider : IDataProvider = source._providers[providerId];

				if (provider.usage == DataProviderUsage.SHARED)
					addProvider(provider);
			}
		}

        public function hasProvider(provider : DataProvider) : Boolean
        {
			for each (var accessibleProvider : DataProvider in _providers)
			{
				if (accessibleProvider == provider)
					return true;
			}
			return false;
		}

		private function propertyChangedHandler(source 			: IDataProvider,
												propertyName 	: String,
												bindingName		: String,
												value			: Object) : void
		{
			if (propertyName == null)
				throw new Error('DataProviders must change only one property at a time.');

			var oldValue : Object = _bindingNameToValue[bindingName];

			_bindingNameToValue[bindingName] = value;

			var numConsumers : uint = _consumers.length;
			for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
				_consumers[consumerId].setProperty(bindingName, value);

			if (_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName].execute(
					this,
					bindingName,
					oldValue,
					value
				);
		}

		public function addConsumer(consumer : IDataBindingsConsumer) : void
		{
			_consumers.push(consumer);

			var numProperties : uint = this.numProperties;
			for (var propertyId : uint = 0; propertyId < numProperties; ++propertyId)
			{
				var bindingName : String = _bindingNames[propertyId];

				consumer.setProperty(bindingName, _bindingNameToValue[bindingName]);
			}
		}

		public function removeConsumer(consumer : IDataBindingsConsumer) : void
		{
			var numConsumers : uint = _consumers.length - 1;
			var index : int = _consumers.indexOf(consumer);

			if (index < 0)
				throw new Error('This consumer does not exist.');

			_consumers[index] = _consumers[numConsumers];
			_consumers.length = numConsumers;
		}
	}
}

package aerys.minko.type.binding
{
	import flash.utils.Dictionary;

	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.Signal;
	import aerys.minko.type.enum.DataProviderUsage;

	public final class DataBindings
	{
		private var _owner						: ISceneNode;

		private var _providers					: Vector.<IDataProvider>;
		private var _bindingNames				: Vector.<String>;

		private var _bindingNameToValue			: Object;
		private var _bindingNameToChangedSignal	: Object;

		private var _bindingNameToProvider		: Object;
		private var _providerToBindingNames		: Dictionary;

		private var _consumers					: Vector.<IDataBindingsConsumer>;

		public function get owner() : ISceneNode
		{
			return _owner;
		}

		public function get numProviders() : uint
		{
			return _providers.length;
		}

		public function get numProperties() : uint
		{
			return _bindingNames.length;
		}

		public function DataBindings(owner : ISceneNode)
		{
			_owner = owner;

			_providers = new <IDataProvider>[];
			_bindingNames = new <String>[];
			_bindingNameToValue = {};
			_bindingNameToChangedSignal = {};
			_bindingNameToProvider = {};
			_providerToBindingNames = new Dictionary(true);
			_consumers = new <IDataBindingsConsumer>[];
		}

		public function contains(dataProvider : IDataProvider) : Boolean
		{
			return _providers.indexOf(dataProvider) != -1;
		}

		public function addProvider(provider : IDataProvider) : void
		{
			if (_providerToBindingNames[provider])
				throw new Error('This provider is already bound.');

			var dataDescriptor	: Object	= provider.dataDescriptor;

			provider.propertyChanged.add(propertyChangedHandler);
			if (provider is IDynamicDataProvider)
			{
				var dynamicProvider : IDynamicDataProvider = provider as IDynamicDataProvider;

				dynamicProvider.propertyAdded.add(addBinding);
				dynamicProvider.propertyRemoved.add(removeBinding);
			}

			_providerToBindingNames[provider] = new <String>[];
			_providers.push(provider);

			for (var propertyName : String in dataDescriptor)
				addBinding(
					provider,
					propertyName,
					dataDescriptor[propertyName],
					provider[propertyName]
				);
		}

		private function addBinding(provider		: IDataProvider,
									propertyName	: String,
									bindingName		: String,
									value			: Object) : void
		{
			var providerBindingNames	: Vector.<String> 	= _providerToBindingNames[provider];

			if (_bindingNames.indexOf(bindingName) != -1)
				throw new Error(
					'Another data provider is already declaring the \'' + bindingName
					+ '\' property.'
				);

			_bindingNameToProvider[bindingName] = provider;
			_bindingNameToValue[bindingName] = value;

			providerBindingNames.push(bindingName);
			_bindingNames.push(bindingName);

            var numConsumers : uint = _consumers.length;
            for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
                _consumers[consumerId].setProperty(bindingName, value);

			if (_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName].execute(this, bindingName, null, value);
		}

		public function removeProvider(provider : IDataProvider) : void
		{
			var providerBindingsNames 	: Vector.<String> 	= _providerToBindingNames[provider];

			if (providerBindingsNames == null)
				throw new ArgumentError('Unknown provider.');

			var numProviders : uint = _providers.length - 1;

			provider.propertyChanged.remove(propertyChangedHandler);
			if (provider is IDynamicDataProvider)
			{
				var dynamicProvider : IDynamicDataProvider = provider as IDynamicDataProvider;

				dynamicProvider.propertyAdded.remove(addBinding);
				dynamicProvider.propertyRemoved.remove(removeBinding);
			}

			_providers[_providers.indexOf(provider)] = _providers[numProviders];
			_providers.length = numProviders;
			delete _providerToBindingNames[provider];

			var dataDescriptor : Object = provider.dataDescriptor;

			for (var propertyName : String in dataDescriptor)
				removeBinding(
					provider,
					propertyName,
					dataDescriptor[propertyName],
					provider[propertyName]
				);
		}

		public function removeBinding(provider		: IDataProvider,
									  propertyName	: String,
									  bindingName	: String,
									  value			: Object) : void
		{
			var numBindings 	: uint 		= _bindingNames.length - 1;
			var changedSignal 	: Signal 	= _bindingNameToChangedSignal[bindingName] as Signal;

			delete _bindingNameToValue[bindingName];
			delete _bindingNameToProvider[bindingName];

			_bindingNames[_bindingNames.indexOf(bindingName)] = _bindingNames[numBindings];
			_bindingNames.length = numBindings;

            var numConsumers : uint = _consumers.length;
            for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
                _consumers[consumerId].setProperty(bindingName, null);

			if (changedSignal != null)
				changedSignal.execute(this, bindingName, value, null);
		}

		public function removeAllProviders() : void
		{
			var numProviders : uint = this.numProviders;

			for (var providerId : int = numProviders - 1; providerId >= 0; --providerId)
				removeProvider(getProviderAt(providerId));
		}

		public function hasCallback(bindingName	: String,
									callback	: Function) : Boolean
		{
			var signal : Signal = _bindingNameToChangedSignal[bindingName];

			return signal != null && signal.hasCallback(callback);
		}

		public function addCallback(bindingName : String,
									callback	: Function) : void
		{
			_bindingNameToChangedSignal[bindingName] ||= new Signal(
				'DataBindings.changed[' + bindingName + ']'
			);

			Signal(_bindingNameToChangedSignal[bindingName]).add(callback);
		}

		public function removeCallback(bindingName	: String,
									   callback		: Function) : void
		{
			var signal : Signal = _bindingNameToChangedSignal[bindingName];

			if (!signal)
				throw new ArgumentError('Unkown property \'' + bindingName + '\'.');

			signal.remove(callback);

			if (signal.numCallbacks == 0)
				delete _bindingNameToChangedSignal[bindingName];
		}

		public function getProviderAt(index : uint) : IDataProvider
		{
			return _providers[index];
		}

		public function getProviderByBindingName(bindingName : String) : IDataProvider
		{
			if (_bindingNameToProvider[bindingName] == null)
				throw new ArgumentError('Unkown property \'' + bindingName + '\'.');

			return _bindingNameToProvider[bindingName];
		}

		public function propertyExists(bindingName : String) : Boolean
		{
			return _bindingNameToValue.hasOwnProperty(bindingName);
		}

		public function getProperty(bindingName : String) : *
		{
			if (_bindingNames.indexOf(bindingName) < 0)
				throw new Error('The property \'' + bindingName + '\' does not exist.');

			return _bindingNameToValue[bindingName];
		}

		public function getPropertyName(bindingIndex : uint) : String
		{
			if (bindingIndex > numProperties)
				throw new ArgumentError('No such binding');

			return _bindingNames[bindingIndex];
		}

		public function copySharedProvidersFrom(source : DataBindings) : void
		{
			var numProviders : uint = source._providers.length;

			for (var providerId : uint = 0; providerId < numProviders; ++providerId)
			{
				var provider : IDataProvider = source._providers[providerId];

				if (provider.usage == DataProviderUsage.SHARED)
					addProvider(provider);
			}
		}

        public function hasProvider(provider : IDataProvider) : Boolean
        {
			for each (var accessibleProvider : IDataProvider in _providers)
			{
				if (accessibleProvider == provider)
					return true;
			}
			return false;
		}

		private function propertyChangedHandler(source 			: IDataProvider,
												propertyName 	: String,
												bindingName		: String,
												value			: Object) : void
		{
			if (propertyName == null)
				throw new Error('DataProviders must change only one property at a time.');

			var oldValue : Object = _bindingNameToValue[bindingName];

			_bindingNameToValue[bindingName] = value;

			var numConsumers : uint = _consumers.length;
			for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
				_consumers[consumerId].setProperty(bindingName, value);

			if (_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName].execute(
					this,
					bindingName,
					oldValue,
					value
				);
		}

		public function addConsumer(consumer : IDataBindingsConsumer) : void
		{
			_consumers.push(consumer);

			var numProperties : uint = this.numProperties;
			for (var propertyId : uint = 0; propertyId < numProperties; ++propertyId)
			{
				var bindingName : String = _bindingNames[propertyId];

				consumer.setProperty(bindingName, _bindingNameToValue[bindingName]);
			}
		}

		public function removeConsumer(consumer : IDataBindingsConsumer) : void
		{
			var numConsumers : uint = _consumers.length - 1;
			var index : int = _consumers.indexOf(consumer);

			if (index < 0)
				throw new Error('This consumer does not exist.');

			_consumers[index] = _consumers[numConsumers];
			_consumers.length = numConsumers;
		}
	}
}

Fix hasProvider function

Fix hasProvider function


package jp.promotal.ssplayer.data {

	import jp.promotal.ssplayer.utils.SSInterpolation;

	public class SSAttribute {

		private var _xml:XML;

		private var _tag:String;
		public function get tag():String {
			return this._tag;
		}
		public function set tag(value:String):void {
			this._tag = value;
			if (defaultValue(value) === undefined) {
				trace("SSAttribute.tag(): Attribute not supported:", value);
			}
		}

		public static function defaultValue(tag:String):* {
			switch (tag) {
				case "CELL":
					return null;
				case "HIDE":
					return 0;
				case "POSX":
					return 0;
				case "POSY":
					return 0;
				case "ROTZ":
					return 0;
				case "SCLX":
					return 1;
				case "SCLY":
					return 1;
				case "ALPH":
					return 1;
				default:
					return undefined;
			}

		}

		private var _keyFrames:Array;
		public function get keyFrames():Array {
			return this._keyFrames;
		}

		private var _frames:Array;
		public function get frames():Array {
			if (this._frames) {
				return this._frames;
			}
			this._frames = [];
			for each (var keyFrame:SSKeyFrame in this.keyFrames) {
				this._frames[keyFrame.time] = keyFrame;
			}
			for (var i:int = 0; i < this._frames.length; i++) {
				var startFrame:SSFrame = this._frames[i];
				if (startFrame) {
					var endFrame:SSFrame = null;
					for (var j:int = i + 1; j < this._frames.length; j++) {
						if (this._frames[j]) {
							endFrame = this._frames[j];
							break;
						}
					}
					if (endFrame) {
						for (var k:int = i + 1; k < j; k++) {
							var frame:SSFrame = new SSFrame();
							frame.value = SSInterpolation.interpolate(startFrame.ipType, i, j, k, startFrame.value, endFrame.value);
							this._frames[k] = frame;
						}
						i = j - 1;
					}
				}
			}
			return this._frames;
		}

		public function valueAt(time:Number):* {
			var prevKeyFrame:SSKeyFrame = null;
			var nextKeyFrame:SSKeyFrame = null;
			for each (var keyFrame:SSKeyFrame in this.keyFrames) {
				if (keyFrame.time <= time) {
					if (!prevKeyFrame || keyFrame.time > prevKeyFrame.time) {
						prevKeyFrame = keyFrame;
					}
				} else {
					if (!nextKeyFrame || keyFrame.time < nextKeyFrame.time) {
						nextKeyFrame = keyFrame;
					}
				}
			}
			if (!prevKeyFrame) {
				return nextKeyFrame ? nextKeyFrame.value : defaultValue(this.tag);
			}
			if (!nextKeyFrame) {
				return prevKeyFrame.value;
			}
			return SSInterpolation.interpolate(prevKeyFrame.ipType, prevKeyFrame.time, nextKeyFrame.time, time, prevKeyFrame.value, nextKeyFrame.value);
		}

		public function SSAttribute() {
			super();
			this._keyFrames = [];
		}

		public static function empty(tag:String):SSAttribute {
			var result:SSAttribute = new SSAttribute();
			result.tag = tag;
			return result;
		}

		public static function fromXML(xml:XML):SSAttribute {
			var result:SSAttribute = new SSAttribute();
			if (SSProject.DEBUG) {
				result._xml = xml;
			}
			result.tag = xml.@tag;
			for each (var key:XML in xml.key) {
				var keyFrame:SSKeyFrame = SSKeyFrame.fromXML(key);
				result._keyFrames.push(keyFrame);
			}
			return result;
		}
	}
}

package jp.promotal.ssplayer.data {

	import jp.promotal.ssplayer.utils.SSInterpolation;

	public class SSAttribute {

		private var _xml:XML;

		private var _tag:String;
		public function get tag():String {
			return this._tag;
		}
		public function set tag(value:String):void {
			this._tag = value;
			if (defaultValue(value) === undefined) {
				trace("[SSProject] SSAttribute.tag(): Attribute not supported:", value);
			}
		}

		public static function defaultValue(tag:String):* {
			switch (tag) {
				case "CELL":
					return null;
				case "HIDE":
					return 0;
				case "POSX":
					return 0;
				case "POSY":
					return 0;
				case "ROTZ":
					return 0;
				case "SCLX":
					return 1;
				case "SCLY":
					return 1;
				case "ALPH":
					return 1;
				default:
					return undefined;
			}

		}

		private var _keyFrames:Array;
		public function get keyFrames():Array {
			return this._keyFrames;
		}

		private var _frames:Array;
		public function get frames():Array {
			if (this._frames) {
				return this._frames;
			}
			this._frames = [];
			for each (var keyFrame:SSKeyFrame in this.keyFrames) {
				this._frames[keyFrame.time] = keyFrame;
			}
			for (var i:int = 0; i < this._frames.length; i++) {
				var startFrame:SSFrame = this._frames[i];
				if (startFrame) {
					var endFrame:SSFrame = null;
					for (var j:int = i + 1; j < this._frames.length; j++) {
						if (this._frames[j]) {
							endFrame = this._frames[j];
							break;
						}
					}
					if (endFrame) {
						for (var k:int = i + 1; k < j; k++) {
							var frame:SSFrame = new SSFrame();
							frame.value = SSInterpolation.interpolate(startFrame.ipType, i, j, k, startFrame.value, endFrame.value);
							this._frames[k] = frame;
						}
						i = j - 1;
					}
				}
			}
			return this._frames;
		}

		public function valueAt(time:Number):* {
			var prevKeyFrame:SSKeyFrame = null;
			var nextKeyFrame:SSKeyFrame = null;
			for each (var keyFrame:SSKeyFrame in this.keyFrames) {
				if (keyFrame.time <= time) {
					if (!prevKeyFrame || keyFrame.time > prevKeyFrame.time) {
						prevKeyFrame = keyFrame;
					}
				} else {
					if (!nextKeyFrame || keyFrame.time < nextKeyFrame.time) {
						nextKeyFrame = keyFrame;
					}
				}
			}
			if (!prevKeyFrame) {
				return nextKeyFrame ? nextKeyFrame.value : defaultValue(this.tag);
			}
			if (!nextKeyFrame) {
				return prevKeyFrame.value;
			}
			return SSInterpolation.interpolate(prevKeyFrame.ipType, prevKeyFrame.time, nextKeyFrame.time, time, prevKeyFrame.value, nextKeyFrame.value);
		}

		public function SSAttribute() {
			super();
			this._keyFrames = [];
		}

		public static function empty(tag:String):SSAttribute {
			var result:SSAttribute = new SSAttribute();
			result.tag = tag;
			return result;
		}

		public static function fromXML(xml:XML):SSAttribute {
			var result:SSAttribute = new SSAttribute();
			if (SSProject.DEBUG) {
				result._xml = xml;
			}
			result.tag = xml.@tag;
			for each (var key:XML in xml.key) {
				var keyFrame:SSKeyFrame = SSKeyFrame.fromXML(key);
				result._keyFrames.push(keyFrame);
			}
			return result;
		}
	}
}

tag warning message

tag warning message


package aerys.minko.type.animation.timeline
{
	import aerys.minko.ns.minko_animation;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.math.Matrix4x4;

	use namespace minko_animation;

	public final class MatrixSegmentTimeline extends AbstractTimeline
	{
		private var _timeTable	: Vector.<uint>			= null;
		private var _matrices	: Vector.<Matrix4x4>	= null;

		public function MatrixSegmentTimeline(propertyPath		: String,
											  timeTable 		: Vector.<uint>,
											  values			: Vector.<Matrix4x4>)
		{
			super(propertyPath, timeTable[int(timeTable.length - 1)]);

			_timeTable = timeTable;
			_matrices = values;
		}

		override public function updateAt(t : int, target : Object) : void
		{
			super.updateAt(t, target);

			var reverse		: Boolean	= t < 0;
			var timeId		: uint 		= getIndexForTime(reverse ? duration - t : t);
			var timeCount	: uint 		= _timeTable.length;

			// change matrix value
			var out : Matrix4x4 = currentTarget[propertyName];

			if (!out)
			{
				throw new Error(
					"'" + propertyName
					+ "' could not be found in '"
					+ currentTarget + "'."
				);
			}

			if (timeId == 0)
				out.copyFrom(_matrices[0]);
			else
				out.copyFrom(_matrices[int(timeId - 1)]);
		}

		private function getIndexForTime(t : uint) : uint
		{
			// use a dichotomy to find the current frame in the time table.
			var timeCount 		: uint = _timeTable.length;
			var bottomTimeId	: uint = 0;
			var upperTimeId		: uint = timeCount;
			var timeId			: uint;

			while (upperTimeId - bottomTimeId > 1)
			{
				timeId = (bottomTimeId + upperTimeId) >> 1;

				if (_timeTable[timeId] > t)
					upperTimeId = timeId;
				else
					bottomTimeId = timeId;
			}

			return upperTimeId;
		}

		override public function clone() : ITimeline
		{
			return new MatrixTimeline(
				propertyPath,
				_timeTable.slice(),
				_matrices.slice()
			);
		}
	}
}

package aerys.minko.type.animation.timeline
{
	import aerys.minko.ns.minko_animation;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.math.Matrix4x4;

	use namespace minko_animation;

	public final class MatrixSegmentTimeline extends AbstractTimeline
	{
		private var _timeTable	: Vector.<uint>			= null;
		private var _matrices	: Vector.<Matrix4x4>	= null;

		public function MatrixSegmentTimeline(propertyPath		: String,
											  timeTable 		: Vector.<uint>,
											  values			: Vector.<Matrix4x4>)
		{
			super(propertyPath, timeTable[uint(timeTable.length - 1)]);

			_timeTable = timeTable;
			_matrices = values;
		}

		override public function updateAt(t : int, target : Object) : void
		{
			super.updateAt(t, target);

			var reverse		: Boolean	= t < 0;
			var timeId		: uint 		= getIndexForTime(reverse ? duration - t : t);
			var timeCount	: uint 		= _timeTable.length;

			// change matrix value
			var out : Matrix4x4 = currentTarget[propertyName];

			if (!out)
			{
				throw new Error(
					"'" + propertyName
					+ "' could not be found in '"
					+ currentTarget + "'."
				);
			}

			if (timeId == 0)
				out.copyFrom(_matrices[0]);
			else
				out.copyFrom(_matrices[int(timeId - 1)]);
		}

		private function getIndexForTime(t : uint) : uint
		{
			// use a dichotomy to find the current frame in the time table.
			var timeCount 		: uint = _timeTable.length;
			var bottomTimeId	: uint = 0;
			var upperTimeId		: uint = timeCount;
			var timeId			: uint;

			while (upperTimeId - bottomTimeId > 1)
			{
				timeId = (bottomTimeId + upperTimeId) >> 1;

				if (_timeTable[timeId] > t)
					upperTimeId = timeId;
				else
					bottomTimeId = timeId;
			}

			return upperTimeId;
		}

		override public function clone() : ITimeline
		{
			return new MatrixTimeline(
				propertyPath,
				_timeTable.slice(),
				_matrices.slice()
			);
		}
	}
}

fix array access cast to uint instead of int in MatrixSegmentTimeline


/* 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.stream {
    import org.mangui.hls.controller.BufferThresholdController;
    import org.mangui.hls.loader.FragmentLoader;
    import org.mangui.hls.event.HLSPlayMetrics;
    import org.mangui.hls.event.HLSError;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.constant.HLSSeekStates;
    import org.mangui.hls.constant.HLSPlayStates;
    import org.mangui.hls.flv.FLVTag;
    import org.mangui.hls.HLS;
    import org.mangui.hls.HLSSettings;
    import org.mangui.hls.utils.Hex;

    import flash.events.Event;
    import flash.events.NetStatusEvent;
    import flash.events.TimerEvent;
    import flash.net.*;
    import flash.utils.*;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Class that overrides standard flash.net.NetStream class, keeps the buffer filled, handles seek and play state
     *
     * play state transition :
     * 				FROM								TO								condition
     *  HLSPlayStates.IDLE              	HLSPlayStates.PLAYING_BUFFERING     play()/play2()/seek() called
     *  HLSPlayStates.PLAYING_BUFFERING  	HLSPlayStates.PLAYING  				buflen > minBufferLength
     *  HLSPlayStates.PAUSED_BUFFERING  	HLSPlayStates.PAUSED  				buflen > minBufferLength
     *  HLSPlayStates.PLAYING  				HLSPlayStates.PLAYING_BUFFERING  	buflen < lowBufferLength
     *  HLSPlayStates.PAUSED  				HLSPlayStates.PAUSED_BUFFERING  	buflen < lowBufferLength
     */
    public class HLSNetStream extends NetStream {
        /** Reference to the framework controller. **/
        private var _hls : HLS;
        /** reference to buffer threshold controller */
        private var _bufferThresholdController : BufferThresholdController;
        /** FLV tags buffer vector **/
        private var _flvTagBuffer : Vector.<FLVTag>;
        /** FLV tags buffer duration **/
        private var _flvTagBufferDuration : Number;
        /** The fragment loader. **/
        private var _fragmentLoader : FragmentLoader;
        /** means that last fragment of a VOD playlist has been loaded */
        private var _reached_vod_end : Boolean;
        /** Timer used to check buffer and position. **/
        private var _timer : Timer;
        /** Current playback state. **/
        private var _playbackState : String;
        /** Current seek state. **/
        private var _seekState : String;
        /** current playback level **/
        private var _playbackLevel : int;
        /** Netstream client proxy */
        private var _client : HLSNetStreamClient;

        /** Create the buffer. **/
        public function HLSNetStream(connection : NetConnection, hls : HLS, fragmentLoader : FragmentLoader) : void {
            super(connection);
            super.bufferTime = 0.1;
            _flvTagBufferDuration = 0;
            _hls = hls;
            _bufferThresholdController = new BufferThresholdController(hls);
            _fragmentLoader = fragmentLoader;
            _hls.addEventListener(HLSEvent.LAST_VOD_FRAGMENT_LOADED, _lastVODFragmentLoadedHandler);
            _playbackState = HLSPlayStates.IDLE;
            _seekState = HLSSeekStates.IDLE;
            _timer = new Timer(100, 0);
            _timer.addEventListener(TimerEvent.TIMER, _checkBuffer);
            _client = new HLSNetStreamClient();
            _client.registerCallback("onHLSFragmentChange", onHLSFragmentChange);
            _client.registerCallback("onID3Data", onID3Data);
            super.client = _client;
        };

        public function onHLSFragmentChange(level : int, seqnum : int, cc : int, audio_only : Boolean, program_date : Number, width : int, height : int, ... tags) : void {
            CONFIG::LOGGING {
                Log.debug("playing fragment(level/sn/cc):" + level + "/" + seqnum + "/" + cc);
            }
            _playbackLevel = level;
            var tag_list : Array = new Array();
            for (var i : uint = 0; i < tags.length; i++) {
                tag_list.push(tags[i]);
                CONFIG::LOGGING {
                    Log.debug("custom tag:" + tags[i]);
                }
            }
            _hls.dispatchEvent(new HLSEvent(HLSEvent.FRAGMENT_PLAYING, new HLSPlayMetrics(level, seqnum, cc, audio_only, program_date, width, height, tag_list)));
        }

        // function is called by SCRIPT in FLV
        public function onID3Data(data : ByteArray) : void {
            var dump : String = "unset";

            // we dump the content as hex to get it to the Javascript in the browser.
            // from lots of searching, we could use base64, but even then, the decode would
            // not be native, so hex actually seems more efficient
            dump = Hex.fromArray(data);

            CONFIG::LOGGING {
                Log.debug("id3:" + dump);
            }
            _hls.dispatchEvent(new HLSEvent(HLSEvent.ID3_UPDATED, dump));
        }

        /** timer function, check/update NetStream state, and append tags if needed **/
        private function _checkBuffer(e : Event) : void {
            var buffer : Number = this.bufferLength;
            // Set playback state. no need to check buffer status if seeking
            if (_seekState != HLSSeekStates.SEEKING) {
                // check low buffer condition
                if (buffer <= 0.1) {
                    if (_reached_vod_end) {
                        // reach end of playlist + playback complete (as buffer is empty).
                        // stop timer, report event and switch to IDLE mode.
                        _timer.stop();
                        CONFIG::LOGGING {
                            Log.debug("reached end of VOD playlist, notify playback complete");
                        }
                        _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYBACK_COMPLETE));
                        _setPlaybackState(HLSPlayStates.IDLE);
                        _setSeekState(HLSSeekStates.IDLE);
                        return;
                    } else {
                        // buffer <= 0.1 and not EOS, pause Netstream
                        super.pause();
                    }
                }
                // if buffer len is below lowBufferLength, get into buffering state
                if (!_reached_vod_end && buffer < _bufferThresholdController.lowBufferLength) {
                    if (_playbackState == HLSPlayStates.PLAYING) {
                        // low buffer condition and play state. switch to play buffering state
                        _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
                    } else if (_playbackState == HLSPlayStates.PAUSED) {
                        // low buffer condition and pause state. switch to paused buffering state
                        _setPlaybackState(HLSPlayStates.PAUSED_BUFFERING);
                    }
                }
                // if buffer len is above minBufferLength, get out of buffering state
                if (buffer >= _bufferThresholdController.minBufferLength || _reached_vod_end) {
                    /* after we reach back threshold value, set it buffer low value to avoid
                     * reporting buffering state to often. using different values for low buffer / min buffer
                     * allow to fine tune this
                     */
                    // no more in low buffer state
                    if (_playbackState == HLSPlayStates.PLAYING_BUFFERING) {
                        CONFIG::LOGGING {
                            Log.debug("resume playback");
                        }
                        super.resume();
                        _setPlaybackState(HLSPlayStates.PLAYING);
                    } else if (_playbackState == HLSPlayStates.PAUSED_BUFFERING) {
                        _setPlaybackState(HLSPlayStates.PAUSED);
                    }
                }
            }
            // in case any data available in our FLV buffer, append into NetStream
            if (_flvTagBuffer.length) {
                if (_seekState == HLSSeekStates.SEEKING) {
                    /* this is our first injection after seek(),
                    let's flush netstream now
                    this is to avoid black screen during seek command */
                    super.close();
                    CONFIG::FLASH_11_1 {
                        try {
                            super.useHardwareDecoder = HLSSettings.useHardwareVideoDecoder;
                        } catch(e : Error) {
                        }
                    }
                    super.play(null);
                    super.appendBytesAction(NetStreamAppendBytesAction.RESET_SEEK);
                    // immediatly pause NetStream, it will be resumed when enough data will be buffered in the NetStream
                    super.pause();
                    // dispatch event to mimic NetStream behaviour
                    dispatchEvent(new NetStatusEvent(NetStatusEvent.NET_STATUS, false, false, {code:"NetStream.Seek.Notify", level:"status"}));
                    _setSeekState(HLSSeekStates.SEEKED);
                }
                // CONFIG::LOGGING {
                // Log.debug("appending data into NetStream");
                // }
                while (0 < _flvTagBuffer.length) {
                    var tagBuffer : FLVTag = _flvTagBuffer.shift();
                    // append data until we drain our _buffer
                    try {
                        if (tagBuffer.type == FLVTag.DISCONTINUITY) {
                            super.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
                            super.appendBytes(FLVTag.getHeader());
                        }
                        super.appendBytes(tagBuffer.data);
                    } catch (error : Error) {
                        var hlsError : HLSError = new HLSError(HLSError.TAG_APPENDING_ERROR, null, tagBuffer.type + ": " + error.message);
                        _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
                    }
                    // Last tag done? Then append sequence end.
                    if (_reached_vod_end && _flvTagBuffer.length == 0) {
                        super.appendBytesAction(NetStreamAppendBytesAction.END_SEQUENCE);
                        super.appendBytes(new ByteArray());
                    }
                }
                // FLV tag buffer drained, reset its duration
                _flvTagBufferDuration = 0;
            }
        };

        /** Return the current playback state. **/
        public function get playbackState() : String {
            return _playbackState;
        };

        /** Return the current seek state. **/
        public function get seekState() : String {
            return _seekState;
        };

        /** Return the current playback quality level **/
        public function get playbackLevel() : int {
            return _playbackLevel;
        };

        /** append tags to NetStream **/
        public function appendTags(tags : Vector.<FLVTag>) : void {
            _flvTagBufferDuration += (tags[tags.length - 1].pts - tags[0].pts) / 1000;
            for each (var tag : FLVTag in tags) {
                _flvTagBuffer.push(tag);
            }
        };

        private function _lastVODFragmentLoadedHandler(event : HLSEvent) : void {
            CONFIG::LOGGING {
                Log.debug("last fragment loaded");
            }
            _reached_vod_end = true;
        }

        /** Change playback state. **/
        private function _setPlaybackState(state : String) : void {
            if (state != _playbackState) {
                CONFIG::LOGGING {
                    Log.debug('[PLAYBACK_STATE] from ' + _playbackState + ' to ' + state);
                }
                _playbackState = state;
                _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYBACK_STATE, _playbackState));
            }
        };

        /** Change seeking state. **/
        private function _setSeekState(state : String) : void {
            if (state != _seekState) {
                CONFIG::LOGGING {
                    Log.debug('[SEEK_STATE] from ' + _seekState + ' to ' + state);
                }
                _seekState = state;
                _hls.dispatchEvent(new HLSEvent(HLSEvent.SEEK_STATE, _seekState));
            }
        };

        override public function play(...args) : void {
            var _playStart : Number;
            if (args.length >= 2) {
                _playStart = Number(args[1]);
            } else {
                _playStart = -1;
            }
            CONFIG::LOGGING {
                Log.info("HLSNetStream:play(" + _playStart + ")");
            }
            seek(_playStart);
            _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
        }

        override public function play2(param : NetStreamPlayOptions) : void {
            CONFIG::LOGGING {
                Log.info("HLSNetStream:play2(" + param.start + ")");
            }
            seek(param.start);
            _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
        }

        /** Pause playback. **/
        override public function pause() : void {
            CONFIG::LOGGING {
                Log.info("HLSNetStream:pause");
            }
            if (_playbackState == HLSPlayStates.PLAYING) {
                super.pause();
                _setPlaybackState(HLSPlayStates.PAUSED);
            } else if (_playbackState == HLSPlayStates.PLAYING_BUFFERING) {
                super.pause();
                _setPlaybackState(HLSPlayStates.PAUSED_BUFFERING);
            }
        };

        /** Resume playback. **/
        override public function resume() : void {
            CONFIG::LOGGING {
                Log.info("HLSNetStream:resume");
            }
            if (_playbackState == HLSPlayStates.PAUSED) {
                super.resume();
                _setPlaybackState(HLSPlayStates.PLAYING);
            } else if (_playbackState == HLSPlayStates.PAUSED_BUFFERING) {
                // dont resume NetStream here, it will be resumed by Timer. this avoids resuming playback while seeking is in progress
                _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
            }
        };

        /** get Buffer Length  **/
        override public function get bufferLength() : Number {
            /* remaining buffer is total duration buffered since beginning minus playback time */
            if (_seekState == HLSSeekStates.SEEKING) {
                return _flvTagBufferDuration;
            } else {
                return super.bufferLength + _flvTagBufferDuration;
            }
        };

        /** Start playing data in the buffer. **/
        override public function seek(position : Number) : void {
            CONFIG::LOGGING {
                Log.info("HLSNetStream:seek(" + position + ")");
            }
            _fragmentLoader.stop();
            _fragmentLoader.seek(position);
            _flvTagBuffer = new Vector.<FLVTag>();
            _flvTagBufferDuration = 0;
            _reached_vod_end = false;

            _setSeekState(HLSSeekStates.SEEKING);
            /* if HLS was in paused state before seeking,
             * switch to paused buffering state
             * otherwise, switch to playing buffering state
             */
            switch(_playbackState) {
                case HLSPlayStates.PAUSED:
                case HLSPlayStates.PAUSED_BUFFERING:
                    _setPlaybackState(HLSPlayStates.PAUSED_BUFFERING);
                    break;
                case HLSPlayStates.PLAYING:
                case HLSPlayStates.PLAYING_BUFFERING:
                    _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
                    break;
                default:
                    break;
            }
            /* always pause NetStream while seeking, even if we are in play state
             * in that case, NetStream will be resumed after first fragment loading
             */
            super.pause();
            _timer.start();
        };

        public override function set client(client : Object) : void {
            _client.delegate = client;
        };

        public override function get client() : Object {
            return _client.delegate;
        }

        /** Stop playback. **/
        override public function close() : void {
            CONFIG::LOGGING {
                Log.info("HLSNetStream:close");
            }
            super.close();
            _timer.stop();
            _fragmentLoader.stop();
            _setPlaybackState(HLSPlayStates.IDLE);
            _setSeekState(HLSSeekStates.IDLE);
        };

        public function dispose_() : void {
            close();
            _bufferThresholdController.dispose();
            _hls.removeEventListener(HLSEvent.LAST_VOD_FRAGMENT_LOADED, _lastVODFragmentLoadedHandler);
        }
    }
}

/* 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.stream {
    import org.mangui.hls.controller.BufferThresholdController;
    import org.mangui.hls.loader.FragmentLoader;
    import org.mangui.hls.event.HLSPlayMetrics;
    import org.mangui.hls.event.HLSError;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.constant.HLSSeekStates;
    import org.mangui.hls.constant.HLSPlayStates;
    import org.mangui.hls.flv.FLVTag;
    import org.mangui.hls.HLS;
    import org.mangui.hls.HLSSettings;
    import org.mangui.hls.utils.Hex;

    import flash.events.Event;
    import flash.events.NetStatusEvent;
    import flash.events.TimerEvent;
    import flash.net.*;
    import flash.utils.*;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Class that overrides standard flash.net.NetStream class, keeps the buffer filled, handles seek and play state
     *
     * play state transition :
     * 				FROM								TO								condition
     *  HLSPlayStates.IDLE              	HLSPlayStates.PLAYING_BUFFERING     play()/play2()/seek() called
     *  HLSPlayStates.PLAYING_BUFFERING  	HLSPlayStates.PLAYING  				buflen > minBufferLength
     *  HLSPlayStates.PAUSED_BUFFERING  	HLSPlayStates.PAUSED  				buflen > minBufferLength
     *  HLSPlayStates.PLAYING  				HLSPlayStates.PLAYING_BUFFERING  	buflen < lowBufferLength
     *  HLSPlayStates.PAUSED  				HLSPlayStates.PAUSED_BUFFERING  	buflen < lowBufferLength
     */
    public class HLSNetStream extends NetStream {
        /** Reference to the framework controller. **/
        private var _hls : HLS;
        /** reference to buffer threshold controller */
        private var _bufferThresholdController : BufferThresholdController;
        /** FLV tags buffer vector **/
        private var _flvTagBuffer : Vector.<FLVTag>;
        /** FLV tags buffer duration **/
        private var _flvTagBufferDuration : Number;
        /** The fragment loader. **/
        private var _fragmentLoader : FragmentLoader;
        /** means that last fragment of a VOD playlist has been loaded */
        private var _reached_vod_end : Boolean;
        /** Timer used to check buffer and position. **/
        private var _timer : Timer;
        /** Current playback state. **/
        private var _playbackState : String;
        /** Current seek state. **/
        private var _seekState : String;
        /** current playback level **/
        private var _playbackLevel : int;
        /** Netstream client proxy */
        private var _client : HLSNetStreamClient;

        /** Create the buffer. **/
        public function HLSNetStream(connection : NetConnection, hls : HLS, fragmentLoader : FragmentLoader) : void {
            super(connection);
            super.bufferTime = 0.1;
            _flvTagBufferDuration = 0;
            _hls = hls;
            _bufferThresholdController = new BufferThresholdController(hls);
            _fragmentLoader = fragmentLoader;
            _hls.addEventListener(HLSEvent.LAST_VOD_FRAGMENT_LOADED, _lastVODFragmentLoadedHandler);
            _playbackState = HLSPlayStates.IDLE;
            _seekState = HLSSeekStates.IDLE;
            _timer = new Timer(100, 0);
            _timer.addEventListener(TimerEvent.TIMER, _checkBuffer);
            _client = new HLSNetStreamClient();
            _client.registerCallback("onHLSFragmentChange", onHLSFragmentChange);
            _client.registerCallback("onID3Data", onID3Data);
            super.client = _client;
        };

        public function onHLSFragmentChange(level : int, seqnum : int, cc : int, audio_only : Boolean, program_date : Number, width : int, height : int, ... tags) : void {
            CONFIG::LOGGING {
                Log.debug("playing fragment(level/sn/cc):" + level + "/" + seqnum + "/" + cc);
            }
            _playbackLevel = level;
            var tag_list : Array = new Array();
            for (var i : uint = 0; i < tags.length; i++) {
                tag_list.push(tags[i]);
                CONFIG::LOGGING {
                    Log.debug("custom tag:" + tags[i]);
                }
            }
            _hls.dispatchEvent(new HLSEvent(HLSEvent.FRAGMENT_PLAYING, new HLSPlayMetrics(level, seqnum, cc, audio_only, program_date, width, height, tag_list)));
        }

        // function is called by SCRIPT in FLV
        public function onID3Data(data : ByteArray) : void {
            var dump : String = "unset";

            // we dump the content as hex to get it to the Javascript in the browser.
            // from lots of searching, we could use base64, but even then, the decode would
            // not be native, so hex actually seems more efficient
            dump = Hex.fromArray(data);

            CONFIG::LOGGING {
                Log.debug("id3:" + dump);
            }
            _hls.dispatchEvent(new HLSEvent(HLSEvent.ID3_UPDATED, dump));
        }

        /** timer function, check/update NetStream state, and append tags if needed **/
        private function _checkBuffer(e : Event) : void {
            var buffer : Number = this.bufferLength;
            // Set playback state. no need to check buffer status if seeking
            if (_seekState != HLSSeekStates.SEEKING) {
                // check low buffer condition
                if (buffer <= 0.1) {
                    if (_reached_vod_end) {
                        // reach end of playlist + playback complete (as buffer is empty).
                        // stop timer, report event and switch to IDLE mode.
                        _timer.stop();
                        CONFIG::LOGGING {
                            Log.debug("reached end of VOD playlist, notify playback complete");
                        }
                        _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYBACK_COMPLETE));
                        _setPlaybackState(HLSPlayStates.IDLE);
                        _setSeekState(HLSSeekStates.IDLE);
                        return;
                    } else {
                        // buffer <= 0.1 and not EOS, pause playback
                        super.pause();
                    }
                }
                // if buffer len is below lowBufferLength, get into buffering state
                if (!_reached_vod_end && buffer < _bufferThresholdController.lowBufferLength) {
                    if (_playbackState == HLSPlayStates.PLAYING) {
                        // low buffer condition and play state. switch to play buffering state
                        _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
                    } else if (_playbackState == HLSPlayStates.PAUSED) {
                        // low buffer condition and pause state. switch to paused buffering state
                        _setPlaybackState(HLSPlayStates.PAUSED_BUFFERING);
                    }
                }
                // if buffer len is above minBufferLength, get out of buffering state
                if (buffer >= _bufferThresholdController.minBufferLength || _reached_vod_end) {
                    if (_playbackState == HLSPlayStates.PLAYING_BUFFERING) {
                        CONFIG::LOGGING {
                            Log.debug("resume playback");
                        }
                        // resume playback in case it was paused, this can happen if buffer was in really low condition (less than 0.1s)
                        super.resume();
                        _setPlaybackState(HLSPlayStates.PLAYING);
                    } else if (_playbackState == HLSPlayStates.PAUSED_BUFFERING) {
                        _setPlaybackState(HLSPlayStates.PAUSED);
                    }
                }
            }
            // in case any data available in our FLV buffer, append into NetStream
            if (_flvTagBuffer.length) {
                if (_seekState == HLSSeekStates.SEEKING) {
                    /* this is our first injection after seek(),
                    let's flush netstream now
                    this is to avoid black screen during seek command */
                    super.close();
                    CONFIG::FLASH_11_1 {
                        try {
                            super.useHardwareDecoder = HLSSettings.useHardwareVideoDecoder;
                        } catch(e : Error) {
                        }
                    }
                    super.play(null);
                    super.appendBytesAction(NetStreamAppendBytesAction.RESET_SEEK);
                    // immediatly pause NetStream, it will be resumed when enough data will be buffered in the NetStream
                    super.pause();
                    // dispatch event to mimic NetStream behaviour
                    dispatchEvent(new NetStatusEvent(NetStatusEvent.NET_STATUS, false, false, {code:"NetStream.Seek.Notify", level:"status"}));
                    _setSeekState(HLSSeekStates.SEEKED);
                }
                // CONFIG::LOGGING {
                // Log.debug("appending data into NetStream");
                // }
                while (0 < _flvTagBuffer.length) {
                    var tagBuffer : FLVTag = _flvTagBuffer.shift();
                    // append data until we drain our _buffer
                    try {
                        if (tagBuffer.type == FLVTag.DISCONTINUITY) {
                            super.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
                            super.appendBytes(FLVTag.getHeader());
                        }
                        super.appendBytes(tagBuffer.data);
                    } catch (error : Error) {
                        var hlsError : HLSError = new HLSError(HLSError.TAG_APPENDING_ERROR, null, tagBuffer.type + ": " + error.message);
                        _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
                    }
                    // Last tag done? Then append sequence end.
                    if (_reached_vod_end && _flvTagBuffer.length == 0) {
                        super.appendBytesAction(NetStreamAppendBytesAction.END_SEQUENCE);
                        super.appendBytes(new ByteArray());
                    }
                }
                // FLV tag buffer drained, reset its duration
                _flvTagBufferDuration = 0;
            }
        };

        /** Return the current playback state. **/
        public function get playbackState() : String {
            return _playbackState;
        };

        /** Return the current seek state. **/
        public function get seekState() : String {
            return _seekState;
        };

        /** Return the current playback quality level **/
        public function get playbackLevel() : int {
            return _playbackLevel;
        };

        /** append tags to NetStream **/
        public function appendTags(tags : Vector.<FLVTag>) : void {
            _flvTagBufferDuration += (tags[tags.length - 1].pts - tags[0].pts) / 1000;
            for each (var tag : FLVTag in tags) {
                _flvTagBuffer.push(tag);
            }
        };

        private function _lastVODFragmentLoadedHandler(event : HLSEvent) : void {
            CONFIG::LOGGING {
                Log.debug("last fragment loaded");
            }
            _reached_vod_end = true;
        }

        /** Change playback state. **/
        private function _setPlaybackState(state : String) : void {
            if (state != _playbackState) {
                CONFIG::LOGGING {
                    Log.debug('[PLAYBACK_STATE] from ' + _playbackState + ' to ' + state);
                }
                _playbackState = state;
                _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYBACK_STATE, _playbackState));
            }
        };

        /** Change seeking state. **/
        private function _setSeekState(state : String) : void {
            if (state != _seekState) {
                CONFIG::LOGGING {
                    Log.debug('[SEEK_STATE] from ' + _seekState + ' to ' + state);
                }
                _seekState = state;
                _hls.dispatchEvent(new HLSEvent(HLSEvent.SEEK_STATE, _seekState));
            }
        };

        override public function play(...args) : void {
            var _playStart : Number;
            if (args.length >= 2) {
                _playStart = Number(args[1]);
            } else {
                _playStart = -1;
            }
            CONFIG::LOGGING {
                Log.info("HLSNetStream:play(" + _playStart + ")");
            }
            seek(_playStart);
            _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
        }

        override public function play2(param : NetStreamPlayOptions) : void {
            CONFIG::LOGGING {
                Log.info("HLSNetStream:play2(" + param.start + ")");
            }
            seek(param.start);
            _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
        }

        /** Pause playback. **/
        override public function pause() : void {
            CONFIG::LOGGING {
                Log.info("HLSNetStream:pause");
            }
            if (_playbackState == HLSPlayStates.PLAYING) {
                super.pause();
                _setPlaybackState(HLSPlayStates.PAUSED);
            } else if (_playbackState == HLSPlayStates.PLAYING_BUFFERING) {
                super.pause();
                _setPlaybackState(HLSPlayStates.PAUSED_BUFFERING);
            }
        };

        /** Resume playback. **/
        override public function resume() : void {
            CONFIG::LOGGING {
                Log.info("HLSNetStream:resume");
            }
            if (_playbackState == HLSPlayStates.PAUSED) {
                super.resume();
                _setPlaybackState(HLSPlayStates.PLAYING);
            } else if (_playbackState == HLSPlayStates.PAUSED_BUFFERING) {
                // dont resume NetStream here, it will be resumed by Timer. this avoids resuming playback while seeking is in progress
                _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
            }
        };

        /** get Buffer Length  **/
        override public function get bufferLength() : Number {
            /* remaining buffer is total duration buffered since beginning minus playback time */
            if (_seekState == HLSSeekStates.SEEKING) {
                return _flvTagBufferDuration;
            } else {
                return super.bufferLength + _flvTagBufferDuration;
            }
        };

        /** Start playing data in the buffer. **/
        override public function seek(position : Number) : void {
            CONFIG::LOGGING {
                Log.info("HLSNetStream:seek(" + position + ")");
            }
            _fragmentLoader.stop();
            _fragmentLoader.seek(position);
            _flvTagBuffer = new Vector.<FLVTag>();
            _flvTagBufferDuration = 0;
            _reached_vod_end = false;

            _setSeekState(HLSSeekStates.SEEKING);
            /* if HLS was in paused state before seeking,
             * switch to paused buffering state
             * otherwise, switch to playing buffering state
             */
            switch(_playbackState) {
                case HLSPlayStates.PAUSED:
                case HLSPlayStates.PAUSED_BUFFERING:
                    _setPlaybackState(HLSPlayStates.PAUSED_BUFFERING);
                    break;
                case HLSPlayStates.PLAYING:
                case HLSPlayStates.PLAYING_BUFFERING:
                    _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
                    break;
                default:
                    break;
            }
            /* always pause NetStream while seeking, even if we are in play state
             * in that case, NetStream will be resumed after first fragment loading
             */
            super.pause();
            _timer.start();
        };

        public override function set client(client : Object) : void {
            _client.delegate = client;
        };

        public override function get client() : Object {
            return _client.delegate;
        }

        /** Stop playback. **/
        override public function close() : void {
            CONFIG::LOGGING {
                Log.info("HLSNetStream:close");
            }
            super.close();
            _timer.stop();
            _fragmentLoader.stop();
            _setPlaybackState(HLSPlayStates.IDLE);
            _setSeekState(HLSSeekStates.IDLE);
        };

        public function dispose_() : void {
            close();
            _bufferThresholdController.dispose();
            _hls.removeEventListener(HLSEvent.LAST_VOD_FRAGMENT_LOADED, _lastVODFragmentLoadedHandler);
        }
    }
}

update comments

HLSNetStream.as: update comments


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

package flash.display {
import flash.geom.Point;
import flash.text.TextSnapshot;

[native(cls='DisplayObjectContainerClass')]
public class DisplayObjectContainer extends InteractiveObject {
  public function DisplayObjectContainer() {}
  public native function get numChildren():int;
  public native function get textSnapshot():TextSnapshot;
  public native function get tabChildren():Boolean;
  public native function set tabChildren(enable:Boolean):void;
  public native function get mouseChildren():Boolean;
  public native function set mouseChildren(enable:Boolean):void;
  public native function addChild(child:DisplayObject):DisplayObject;
  public native function addChildAt(child:DisplayObject, index:int):DisplayObject;
  public native function removeChild(child:DisplayObject):DisplayObject;
  public native function removeChildAt(index:int):DisplayObject;
  public native function getChildIndex(child:DisplayObject):int;
  public native function setChildIndex(child:DisplayObject, index:int):void;
  public native function getChildAt(index:int):DisplayObject;
  public native function getChildByName(name:String):DisplayObject;
  public native function getObjectsUnderPoint(point:Point):Array;
  public native function areInaccessibleObjectsUnderPoint(point:Point):Boolean;
  public native function contains(child:DisplayObject):Boolean;
  public native function swapChildrenAt(index1:int, index2:int):void;
  public native function swapChildren(child1:DisplayObject, child2:DisplayObject):void;
  public native function removeChildren(beginIndex:int = 0, endIndex:int = 2147483647):void;
}
}

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

package flash.display {
import flash.geom.Point;
import flash.text.TextSnapshot;

[native(cls='ContainerClass')]
public class DisplayObjectContainer extends InteractiveObject {
  public function DisplayObjectContainer() {}
  public native function get numChildren():int;
  public native function get textSnapshot():TextSnapshot;
  public native function get tabChildren():Boolean;
  public native function set tabChildren(enable:Boolean):void;
  public native function get mouseChildren():Boolean;
  public native function set mouseChildren(enable:Boolean):void;
  public native function addChild(child:DisplayObject):DisplayObject;
  public native function addChildAt(child:DisplayObject, index:int):DisplayObject;
  public native function removeChild(child:DisplayObject):DisplayObject;
  public native function removeChildAt(index:int):DisplayObject;
  public native function getChildIndex(child:DisplayObject):int;
  public native function setChildIndex(child:DisplayObject, index:int):void;
  public native function getChildAt(index:int):DisplayObject;
  public native function getChildByName(name:String):DisplayObject;
  public native function getObjectsUnderPoint(point:Point):Array;
  public native function areInaccessibleObjectsUnderPoint(point:Point):Boolean;
  public native function contains(child:DisplayObject):Boolean;
  public native function swapChildrenAt(index1:int, index2:int):void;
  public native function swapChildren(child1:DisplayObject, child2:DisplayObject):void;
  public native function removeChildren(beginIndex:int = 0, endIndex:int = 2147483647):void;
}
}

Fix metadata for DisplayObjectContainer

Fix metadata for DisplayObjectContainer


// Urho3D editor

#include "Scripts/Editor/EditorHierarchyWindow.as"
#include "Scripts/Editor/EditorView.as"
#include "Scripts/Editor/EditorScene.as"
#include "Scripts/Editor/EditorActions.as"
#include "Scripts/Editor/EditorUIElement.as"
#include "Scripts/Editor/EditorGizmo.as"
#include "Scripts/Editor/EditorMaterial.as"
#include "Scripts/Editor/EditorSettings.as"
#include "Scripts/Editor/EditorPreferences.as"
#include "Scripts/Editor/EditorToolBar.as"
#include "Scripts/Editor/EditorSecondaryToolbar.as"
#include "Scripts/Editor/EditorUI.as"
#include "Scripts/Editor/EditorImport.as"
#include "Scripts/Editor/EditorResourceBrowser.as"
#include "Scripts/Editor/EditorSpawn.as"

String configFileName;

bool instancingSetting = true;
int shadowQualitySetting = 2;

void Start()
{
    // Assign the value ASAP because configFileName is needed on exit, including exit on error
    configFileName = fileSystem.GetAppPreferencesDir("urho3d", "Editor") + "Config.xml";

    if (engine.headless)
    {
        ErrorDialog("Urho3D Editor", "Headless mode is not supported. The program will now exit.");
        engine.Exit();
        return;
    }

    // Use the first frame to setup when the resolution is initialized
    SubscribeToEvent("Update", "FirstFrame");

    SubscribeToEvent(input, "ExitRequested", "HandleExitRequested");

    // Disable Editor auto exit, check first if it is OK to exit
    engine.autoExit = false;
    // Enable console commands from the editor script
    script.defaultScriptFile = scriptFile;
    // Enable automatic resource reloading
    cache.autoReloadResources = true;
    // Return resources which exist but failed to load due to error, so that we will not lose resource refs
    cache.returnFailedResources = true;
    // Use OS mouse without grabbing it
    input.mouseVisible = true;
    // Use system clipboard to allow transport of text in & out from the editor
    ui.useSystemClipboard = true;
}

void FirstFrame()
{
    // Create root scene node
    CreateScene();
    // Load editor settings and preferences
    LoadConfig();
    // Create user interface for the editor
    CreateUI();
    // Create root UI element where all 'editable' UI elements would be parented to
    CreateRootUIElement();
    // Load the initial scene if provided
    ParseArguments();
    // Switch to real frame handler after initialization
    SubscribeToEvent("Update", "HandleUpdate");
}

void Stop()
{
    SaveConfig();
}

void ParseArguments()
{
    Array<String> arguments = GetArguments();
    bool loaded = false;

    // Scan for a scene to load
    for (uint i = 1; i < arguments.length; ++i)
    {
        if (arguments[i].ToLower() == "-scene")
        {
            if (++i < arguments.length)
            {
                loaded = LoadScene(arguments[i]);
                break;
            }
        }
    }

    if (!loaded)
        ResetScene();
}

void HandleUpdate(StringHash eventType, VariantMap& eventData)
{
    float timeStep = eventData["TimeStep"].GetFloat();

    DoResourceBrowserWork();
    UpdateView(timeStep);
    UpdateViewports(timeStep);
    UpdateStats(timeStep);
    UpdateScene(timeStep);
    UpdateTestAnimation(timeStep);
    UpdateGizmo();
    UpdateDirtyUI();
}

void LoadConfig()
{
    if (!fileSystem.FileExists(configFileName))
        return;

    XMLFile config;
    config.Load(File(configFileName, FILE_READ));

    XMLElement configElem = config.root;
    if (configElem.isNull)
        return;

    XMLElement cameraElem = configElem.GetChild("camera");
    XMLElement objectElem = configElem.GetChild("object");
    XMLElement renderingElem = configElem.GetChild("rendering");
    XMLElement uiElem = configElem.GetChild("ui");
    XMLElement hierarchyElem = configElem.GetChild("hierarchy");
    XMLElement inspectorElem = configElem.GetChild("attributeinspector");
    XMLElement viewElem = configElem.GetChild("view");
    XMLElement resourcesElem = configElem.GetChild("resources");
    XMLElement consoleElem = configElem.GetChild("console");

    if (!cameraElem.isNull)
    {
        if (cameraElem.HasAttribute("nearclip")) viewNearClip = cameraElem.GetFloat("nearclip");
        if (cameraElem.HasAttribute("farclip")) viewFarClip = cameraElem.GetFloat("farclip");
        if (cameraElem.HasAttribute("fov")) viewFov = cameraElem.GetFloat("fov");
        if (cameraElem.HasAttribute("speed")) cameraBaseSpeed = cameraElem.GetFloat("speed");
        if (cameraElem.HasAttribute("limitrotation")) limitRotation = cameraElem.GetBool("limitrotation");
        if (cameraElem.HasAttribute("mousewheelcameraposition")) mouseWheelCameraPosition = cameraElem.GetBool("mousewheelcameraposition");
        if (cameraElem.HasAttribute("viewportmode")) viewportMode = cameraElem.GetUInt("viewportmode");
        UpdateViewParameters();
        if (cameraElem.HasAttribute("mouseorbitmode")) mouseOrbitMode = cameraElem.GetInt("mouseorbitmode");
        UpdateViewParameters();
    }

    if (!objectElem.isNull)
    {
        if (objectElem.HasAttribute("newnodedistance")) newNodeDistance = objectElem.GetFloat("newnodedistance");
        if (objectElem.HasAttribute("movestep")) moveStep = objectElem.GetFloat("movestep");
        if (objectElem.HasAttribute("rotatestep")) rotateStep = objectElem.GetFloat("rotatestep");
        if (objectElem.HasAttribute("scalestep")) scaleStep = objectElem.GetFloat("scalestep");
        if (objectElem.HasAttribute("movesnap")) moveSnap = objectElem.GetBool("movesnap");
        if (objectElem.HasAttribute("rotatesnap")) rotateSnap = objectElem.GetBool("rotatesnap");
        if (objectElem.HasAttribute("scalesnap")) scaleSnap = objectElem.GetBool("scalesnap");
        if (objectElem.HasAttribute("applymateriallist")) applyMaterialList = objectElem.GetBool("applymateriallist");
        if (objectElem.HasAttribute("importoptions")) importOptions = objectElem.GetAttribute("importoptions");
        if (objectElem.HasAttribute("pickmode")) pickMode = objectElem.GetInt("pickmode");
        if (objectElem.HasAttribute("axismode")) axisMode = AxisMode(objectElem.GetInt("axismode"));
        if (objectElem.HasAttribute("revertonpause")) revertOnPause = objectElem.GetBool("revertonpause");
    }

    if (!resourcesElem.isNull)
    {
        if (resourcesElem.HasAttribute("rememberresourcepath")) rememberResourcePath = resourcesElem.GetBool("rememberresourcepath");
        if (rememberResourcePath && resourcesElem.HasAttribute("resourcepath"))
        {
            String newResourcePath = resourcesElem.GetAttribute("resourcepath");
            if (fileSystem.DirExists(newResourcePath))
                SetResourcePath(resourcesElem.GetAttribute("resourcepath"), false);
        }
        if (resourcesElem.HasAttribute("importpath"))
        {
            String newImportPath = resourcesElem.GetAttribute("importpath");
            if (fileSystem.DirExists(newImportPath))
                uiImportPath = newImportPath;
        }
        if (resourcesElem.HasAttribute("recentscenes"))
        {
            uiRecentScenes = resourcesElem.GetAttribute("recentscenes").Split(';');
        }
    }

    if (!renderingElem.isNull)
    {
        if (renderingElem.HasAttribute("texturequality")) renderer.textureQuality = renderingElem.GetInt("texturequality");
        if (renderingElem.HasAttribute("materialquality")) renderer.materialQuality = renderingElem.GetInt("materialquality");
        if (renderingElem.HasAttribute("shadowresolution")) SetShadowResolution(renderingElem.GetInt("shadowresolution"));
        if (renderingElem.HasAttribute("shadowquality")) renderer.shadowQuality = shadowQualitySetting = renderingElem.GetInt("shadowquality");
        if (renderingElem.HasAttribute("maxoccludertriangles")) renderer.maxOccluderTriangles = renderingElem.GetInt("maxoccludertriangles");
        if (renderingElem.HasAttribute("specularlighting")) renderer.specularLighting = renderingElem.GetBool("specularlighting");
        if (renderingElem.HasAttribute("dynamicinstancing")) renderer.dynamicInstancing = instancingSetting = renderingElem.GetBool("dynamicinstancing");
        if (renderingElem.HasAttribute("framelimiter")) engine.maxFps = renderingElem.GetBool("framelimiter") ? 200 : 0;
    }

    if (!uiElem.isNull)
    {
        if (uiElem.HasAttribute("minopacity")) uiMinOpacity = uiElem.GetFloat("minopacity");
        if (uiElem.HasAttribute("maxopacity")) uiMaxOpacity = uiElem.GetFloat("maxopacity");
    }

    if (!hierarchyElem.isNull)
    {
        if (hierarchyElem.HasAttribute("showinternaluielement")) showInternalUIElement = hierarchyElem.GetBool("showinternaluielement");
        if (hierarchyElem.HasAttribute("showtemporaryobject")) showTemporaryObject = hierarchyElem.GetBool("showtemporaryobject");
        if (inspectorElem.HasAttribute("nodecolor")) nodeTextColor = inspectorElem.GetColor("nodecolor");
        if (inspectorElem.HasAttribute("componentcolor")) componentTextColor = inspectorElem.GetColor("componentcolor");
    }

    if (!inspectorElem.isNull)
    {
        if (inspectorElem.HasAttribute("originalcolor")) normalTextColor = inspectorElem.GetColor("originalcolor");
        if (inspectorElem.HasAttribute("modifiedcolor")) modifiedTextColor = inspectorElem.GetColor("modifiedcolor");
        if (inspectorElem.HasAttribute("noneditablecolor")) nonEditableTextColor = inspectorElem.GetColor("noneditablecolor");
        if (inspectorElem.HasAttribute("shownoneditable")) showNonEditableAttribute = inspectorElem.GetBool("shownoneditable");
    }

    if (!viewElem.isNull)
    {
        if (viewElem.HasAttribute("defaultzoneambientcolor")) renderer.defaultZone.ambientColor = viewElem.GetColor("defaultzoneambientcolor");
        if (viewElem.HasAttribute("defaultzonefogcolor")) renderer.defaultZone.fogColor = viewElem.GetColor("defaultzonefogcolor");
        if (viewElem.HasAttribute("defaultzonefogstart")) renderer.defaultZone.fogStart = viewElem.GetInt("defaultzonefogstart");
        if (viewElem.HasAttribute("defaultzonefogend")) renderer.defaultZone.fogEnd = viewElem.GetInt("defaultzonefogend");
        if (viewElem.HasAttribute("showgrid")) showGrid = viewElem.GetBool("showgrid");
        if (viewElem.HasAttribute("grid2dmode")) grid2DMode = viewElem.GetBool("grid2dmode");
        if (viewElem.HasAttribute("gridsize")) gridSize = viewElem.GetInt("gridsize");
        if (viewElem.HasAttribute("gridsubdivisions")) gridSubdivisions = viewElem.GetInt("gridsubdivisions");
        if (viewElem.HasAttribute("gridscale")) gridScale = viewElem.GetFloat("gridscale");
        if (viewElem.HasAttribute("gridcolor")) gridColor = viewElem.GetColor("gridcolor");
        if (viewElem.HasAttribute("gridsubdivisioncolor")) gridSubdivisionColor = viewElem.GetColor("gridsubdivisioncolor");
    }

    if (!consoleElem.isNull)
    {
        // Console does not exist yet at this point, so store the string in a global variable
        if (consoleElem.HasAttribute("commandinterpreter")) consoleCommandInterpreter = consoleElem.GetAttribute("commandinterpreter");
    }
}

void SaveConfig()
{
    XMLFile config;
    XMLElement configElem = config.CreateRoot("configuration");
    XMLElement cameraElem = configElem.CreateChild("camera");
    XMLElement objectElem = configElem.CreateChild("object");
    XMLElement renderingElem = configElem.CreateChild("rendering");
    XMLElement uiElem = configElem.CreateChild("ui");
    XMLElement hierarchyElem = configElem.CreateChild("hierarchy");
    XMLElement inspectorElem = configElem.CreateChild("attributeinspector");
    XMLElement viewElem = configElem.CreateChild("view");
    XMLElement resourcesElem = configElem.CreateChild("resources");
    XMLElement consoleElem = configElem.CreateChild("console");

    cameraElem.SetFloat("nearclip", viewNearClip);
    cameraElem.SetFloat("farclip", viewFarClip);
    cameraElem.SetFloat("fov", viewFov);
    cameraElem.SetFloat("speed", cameraBaseSpeed);
    cameraElem.SetBool("limitrotation", limitRotation);
    cameraElem.SetBool("mousewheelcameraposition", mouseWheelCameraPosition);
    cameraElem.SetUInt("viewportmode", viewportMode);
    cameraElem.SetInt("mouseorbitmode", mouseOrbitMode);

    objectElem.SetFloat("newnodedistance", newNodeDistance);
    objectElem.SetFloat("movestep", moveStep);
    objectElem.SetFloat("rotatestep", rotateStep);
    objectElem.SetFloat("scalestep", scaleStep);
    objectElem.SetBool("movesnap", moveSnap);
    objectElem.SetBool("rotatesnap", rotateSnap);
    objectElem.SetBool("scalesnap", scaleSnap);
    objectElem.SetBool("applymateriallist", applyMaterialList);
    objectElem.SetAttribute("importoptions", importOptions);
    objectElem.SetInt("pickmode", pickMode);
    objectElem.SetInt("axismode", axisMode);
    objectElem.SetBool("revertonpause", revertOnPause);

    resourcesElem.SetBool("rememberresourcepath", rememberResourcePath);
    resourcesElem.SetAttribute("resourcepath", sceneResourcePath);
    resourcesElem.SetAttribute("importpath", uiImportPath);
    resourcesElem.SetAttribute("recentscenes", Join(uiRecentScenes, ";"));

    if (renderer !is null && graphics !is null)
    {
        renderingElem.SetInt("texturequality", renderer.textureQuality);
        renderingElem.SetInt("materialquality", renderer.materialQuality);
        renderingElem.SetInt("shadowresolution", GetShadowResolution());
        renderingElem.SetInt("maxoccludertriangles", renderer.maxOccluderTriangles);
        renderingElem.SetBool("specularlighting", renderer.specularLighting);
        // If Shader Model 3 is not supported, save the remembered instancing & quality settings instead of reduced settings
        renderingElem.SetInt("shadowquality", graphics.sm3Support ? renderer.shadowQuality : shadowQualitySetting);
        renderingElem.SetBool("dynamicinstancing", graphics.sm3Support ? renderer.dynamicInstancing : instancingSetting);
    }

    renderingElem.SetBool("framelimiter", engine.maxFps > 0);

    uiElem.SetFloat("minopacity", uiMinOpacity);
    uiElem.SetFloat("maxopacity", uiMaxOpacity);

    hierarchyElem.SetBool("showinternaluielement", showInternalUIElement);
    hierarchyElem.SetBool("showtemporaryobject", showTemporaryObject);
    inspectorElem.SetColor("nodecolor", nodeTextColor);
    inspectorElem.SetColor("componentcolor", componentTextColor);

    inspectorElem.SetColor("originalcolor", normalTextColor);
    inspectorElem.SetColor("modifiedcolor", modifiedTextColor);
    inspectorElem.SetColor("noneditablecolor", nonEditableTextColor);
    inspectorElem.SetBool("shownoneditable", showNonEditableAttribute);

    viewElem.SetBool("showgrid", showGrid);
    viewElem.SetBool("grid2dmode", grid2DMode);
    viewElem.SetColor("defaultzoneambientcolor", renderer.defaultZone.ambientColor);
    viewElem.SetColor("defaultzonefogcolor", renderer.defaultZone.fogColor);
    viewElem.SetFloat("defaultzonefogstart", renderer.defaultZone.fogStart);
    viewElem.SetFloat("defaultzonefogend", renderer.defaultZone.fogEnd);
    viewElem.SetInt("gridsize", gridSize);
    viewElem.SetInt("gridsubdivisions", gridSubdivisions);
    viewElem.SetFloat("gridscale", gridScale);
    viewElem.SetColor("gridcolor", gridColor);
    viewElem.SetColor("gridsubdivisioncolor", gridSubdivisionColor);

    consoleElem.SetAttribute("commandinterpreter", console.commandInterpreter);

    config.Save(File(configFileName, FILE_WRITE));
}

void MakeBackup(const String&in fileName)
{
    fileSystem.Rename(fileName, fileName + ".old");
}

void RemoveBackup(bool success, const String&in fileName)
{
    if (success)
        fileSystem.Delete(fileName + ".old");
}

// Urho3D editor

#include "Scripts/Editor/EditorHierarchyWindow.as"
#include "Scripts/Editor/EditorView.as"
#include "Scripts/Editor/EditorScene.as"
#include "Scripts/Editor/EditorActions.as"
#include "Scripts/Editor/EditorUIElement.as"
#include "Scripts/Editor/EditorGizmo.as"
#include "Scripts/Editor/EditorMaterial.as"
#include "Scripts/Editor/EditorSettings.as"
#include "Scripts/Editor/EditorPreferences.as"
#include "Scripts/Editor/EditorToolBar.as"
#include "Scripts/Editor/EditorSecondaryToolbar.as"
#include "Scripts/Editor/EditorUI.as"
#include "Scripts/Editor/EditorImport.as"
#include "Scripts/Editor/EditorResourceBrowser.as"
#include "Scripts/Editor/EditorSpawn.as"

String configFileName;

bool instancingSetting = true;
int shadowQualitySetting = 2;

void Start()
{
    // Assign the value ASAP because configFileName is needed on exit, including exit on error
    configFileName = fileSystem.GetAppPreferencesDir("urho3d", "Editor") + "Config.xml";

    if (engine.headless)
    {
        ErrorDialog("Urho3D Editor", "Headless mode is not supported. The program will now exit.");
        engine.Exit();
        return;
    }

    // Use the first frame to setup when the resolution is initialized
    SubscribeToEvent("Update", "FirstFrame");

    SubscribeToEvent(input, "ExitRequested", "HandleExitRequested");

    // Disable Editor auto exit, check first if it is OK to exit
    engine.autoExit = false;
    // Enable console commands from the editor script
    script.defaultScriptFile = scriptFile;
    // Enable automatic resource reloading
    cache.autoReloadResources = true;
    // Return resources which exist but failed to load due to error, so that we will not lose resource refs
    cache.returnFailedResources = true;
    // Use OS mouse without grabbing it
    input.mouseVisible = true;
    // Use system clipboard to allow transport of text in & out from the editor
    ui.useSystemClipboard = true;
}

void FirstFrame()
{
    // Create root scene node
    CreateScene();
    // Load editor settings and preferences
    LoadConfig();
    // Create user interface for the editor
    CreateUI();
    // Create root UI element where all 'editable' UI elements would be parented to
    CreateRootUIElement();
    // Load the initial scene if provided
    ParseArguments();
    // Switch to real frame handler after initialization
    SubscribeToEvent("Update", "HandleUpdate");
}

void Stop()
{
    SaveConfig();
}

void ParseArguments()
{
    Array<String> arguments = GetArguments();
    bool loaded = false;

    // Scan for a scene to load
    for (uint i = 1; i < arguments.length; ++i)
    {
        if (arguments[i].ToLower() == "-scene")
        {
            if (++i < arguments.length)
            {
                loaded = LoadScene(arguments[i]);
                break;
            }
        }
    }

    if (!loaded)
        ResetScene();
}

void HandleUpdate(StringHash eventType, VariantMap& eventData)
{
    float timeStep = eventData["TimeStep"].GetFloat();

    DoResourceBrowserWork();
    UpdateView(timeStep);
    UpdateViewports(timeStep);
    UpdateStats(timeStep);
    UpdateScene(timeStep);
    UpdateTestAnimation(timeStep);
    UpdateGizmo();
    UpdateDirtyUI();
}

void LoadConfig()
{
    if (!fileSystem.FileExists(configFileName))
        return;

    XMLFile config;
    config.Load(File(configFileName, FILE_READ));

    XMLElement configElem = config.root;
    if (configElem.isNull)
        return;

    XMLElement cameraElem = configElem.GetChild("camera");
    XMLElement objectElem = configElem.GetChild("object");
    XMLElement renderingElem = configElem.GetChild("rendering");
    XMLElement uiElem = configElem.GetChild("ui");
    XMLElement hierarchyElem = configElem.GetChild("hierarchy");
    XMLElement inspectorElem = configElem.GetChild("attributeinspector");
    XMLElement viewElem = configElem.GetChild("view");
    XMLElement resourcesElem = configElem.GetChild("resources");
    XMLElement consoleElem = configElem.GetChild("console");

    if (!cameraElem.isNull)
    {
        if (cameraElem.HasAttribute("nearclip")) viewNearClip = cameraElem.GetFloat("nearclip");
        if (cameraElem.HasAttribute("farclip")) viewFarClip = cameraElem.GetFloat("farclip");
        if (cameraElem.HasAttribute("fov")) viewFov = cameraElem.GetFloat("fov");
        if (cameraElem.HasAttribute("speed")) cameraBaseSpeed = cameraElem.GetFloat("speed");
        if (cameraElem.HasAttribute("limitrotation")) limitRotation = cameraElem.GetBool("limitrotation");
        if (cameraElem.HasAttribute("mousewheelcameraposition")) mouseWheelCameraPosition = cameraElem.GetBool("mousewheelcameraposition");
        if (cameraElem.HasAttribute("viewportmode")) viewportMode = cameraElem.GetUInt("viewportmode");
        if (cameraElem.HasAttribute("mouseorbitmode")) mouseOrbitMode = cameraElem.GetInt("mouseorbitmode");
        UpdateViewParameters();
    }

    if (!objectElem.isNull)
    {
        if (objectElem.HasAttribute("newnodedistance")) newNodeDistance = objectElem.GetFloat("newnodedistance");
        if (objectElem.HasAttribute("movestep")) moveStep = objectElem.GetFloat("movestep");
        if (objectElem.HasAttribute("rotatestep")) rotateStep = objectElem.GetFloat("rotatestep");
        if (objectElem.HasAttribute("scalestep")) scaleStep = objectElem.GetFloat("scalestep");
        if (objectElem.HasAttribute("movesnap")) moveSnap = objectElem.GetBool("movesnap");
        if (objectElem.HasAttribute("rotatesnap")) rotateSnap = objectElem.GetBool("rotatesnap");
        if (objectElem.HasAttribute("scalesnap")) scaleSnap = objectElem.GetBool("scalesnap");
        if (objectElem.HasAttribute("applymateriallist")) applyMaterialList = objectElem.GetBool("applymateriallist");
        if (objectElem.HasAttribute("importoptions")) importOptions = objectElem.GetAttribute("importoptions");
        if (objectElem.HasAttribute("pickmode")) pickMode = objectElem.GetInt("pickmode");
        if (objectElem.HasAttribute("axismode")) axisMode = AxisMode(objectElem.GetInt("axismode"));
        if (objectElem.HasAttribute("revertonpause")) revertOnPause = objectElem.GetBool("revertonpause");
    }

    if (!resourcesElem.isNull)
    {
        if (resourcesElem.HasAttribute("rememberresourcepath")) rememberResourcePath = resourcesElem.GetBool("rememberresourcepath");
        if (rememberResourcePath && resourcesElem.HasAttribute("resourcepath"))
        {
            String newResourcePath = resourcesElem.GetAttribute("resourcepath");
            if (fileSystem.DirExists(newResourcePath))
                SetResourcePath(resourcesElem.GetAttribute("resourcepath"), false);
        }
        if (resourcesElem.HasAttribute("importpath"))
        {
            String newImportPath = resourcesElem.GetAttribute("importpath");
            if (fileSystem.DirExists(newImportPath))
                uiImportPath = newImportPath;
        }
        if (resourcesElem.HasAttribute("recentscenes"))
        {
            uiRecentScenes = resourcesElem.GetAttribute("recentscenes").Split(';');
        }
    }

    if (!renderingElem.isNull)
    {
        if (renderingElem.HasAttribute("texturequality")) renderer.textureQuality = renderingElem.GetInt("texturequality");
        if (renderingElem.HasAttribute("materialquality")) renderer.materialQuality = renderingElem.GetInt("materialquality");
        if (renderingElem.HasAttribute("shadowresolution")) SetShadowResolution(renderingElem.GetInt("shadowresolution"));
        if (renderingElem.HasAttribute("shadowquality")) renderer.shadowQuality = shadowQualitySetting = renderingElem.GetInt("shadowquality");
        if (renderingElem.HasAttribute("maxoccludertriangles")) renderer.maxOccluderTriangles = renderingElem.GetInt("maxoccludertriangles");
        if (renderingElem.HasAttribute("specularlighting")) renderer.specularLighting = renderingElem.GetBool("specularlighting");
        if (renderingElem.HasAttribute("dynamicinstancing")) renderer.dynamicInstancing = instancingSetting = renderingElem.GetBool("dynamicinstancing");
        if (renderingElem.HasAttribute("framelimiter")) engine.maxFps = renderingElem.GetBool("framelimiter") ? 200 : 0;
    }

    if (!uiElem.isNull)
    {
        if (uiElem.HasAttribute("minopacity")) uiMinOpacity = uiElem.GetFloat("minopacity");
        if (uiElem.HasAttribute("maxopacity")) uiMaxOpacity = uiElem.GetFloat("maxopacity");
    }

    if (!hierarchyElem.isNull)
    {
        if (hierarchyElem.HasAttribute("showinternaluielement")) showInternalUIElement = hierarchyElem.GetBool("showinternaluielement");
        if (hierarchyElem.HasAttribute("showtemporaryobject")) showTemporaryObject = hierarchyElem.GetBool("showtemporaryobject");
        if (inspectorElem.HasAttribute("nodecolor")) nodeTextColor = inspectorElem.GetColor("nodecolor");
        if (inspectorElem.HasAttribute("componentcolor")) componentTextColor = inspectorElem.GetColor("componentcolor");
    }

    if (!inspectorElem.isNull)
    {
        if (inspectorElem.HasAttribute("originalcolor")) normalTextColor = inspectorElem.GetColor("originalcolor");
        if (inspectorElem.HasAttribute("modifiedcolor")) modifiedTextColor = inspectorElem.GetColor("modifiedcolor");
        if (inspectorElem.HasAttribute("noneditablecolor")) nonEditableTextColor = inspectorElem.GetColor("noneditablecolor");
        if (inspectorElem.HasAttribute("shownoneditable")) showNonEditableAttribute = inspectorElem.GetBool("shownoneditable");
    }

    if (!viewElem.isNull)
    {
        if (viewElem.HasAttribute("defaultzoneambientcolor")) renderer.defaultZone.ambientColor = viewElem.GetColor("defaultzoneambientcolor");
        if (viewElem.HasAttribute("defaultzonefogcolor")) renderer.defaultZone.fogColor = viewElem.GetColor("defaultzonefogcolor");
        if (viewElem.HasAttribute("defaultzonefogstart")) renderer.defaultZone.fogStart = viewElem.GetInt("defaultzonefogstart");
        if (viewElem.HasAttribute("defaultzonefogend")) renderer.defaultZone.fogEnd = viewElem.GetInt("defaultzonefogend");
        if (viewElem.HasAttribute("showgrid")) showGrid = viewElem.GetBool("showgrid");
        if (viewElem.HasAttribute("grid2dmode")) grid2DMode = viewElem.GetBool("grid2dmode");
        if (viewElem.HasAttribute("gridsize")) gridSize = viewElem.GetInt("gridsize");
        if (viewElem.HasAttribute("gridsubdivisions")) gridSubdivisions = viewElem.GetInt("gridsubdivisions");
        if (viewElem.HasAttribute("gridscale")) gridScale = viewElem.GetFloat("gridscale");
        if (viewElem.HasAttribute("gridcolor")) gridColor = viewElem.GetColor("gridcolor");
        if (viewElem.HasAttribute("gridsubdivisioncolor")) gridSubdivisionColor = viewElem.GetColor("gridsubdivisioncolor");
    }

    if (!consoleElem.isNull)
    {
        // Console does not exist yet at this point, so store the string in a global variable
        if (consoleElem.HasAttribute("commandinterpreter")) consoleCommandInterpreter = consoleElem.GetAttribute("commandinterpreter");
    }
}

void SaveConfig()
{
    XMLFile config;
    XMLElement configElem = config.CreateRoot("configuration");
    XMLElement cameraElem = configElem.CreateChild("camera");
    XMLElement objectElem = configElem.CreateChild("object");
    XMLElement renderingElem = configElem.CreateChild("rendering");
    XMLElement uiElem = configElem.CreateChild("ui");
    XMLElement hierarchyElem = configElem.CreateChild("hierarchy");
    XMLElement inspectorElem = configElem.CreateChild("attributeinspector");
    XMLElement viewElem = configElem.CreateChild("view");
    XMLElement resourcesElem = configElem.CreateChild("resources");
    XMLElement consoleElem = configElem.CreateChild("console");

    cameraElem.SetFloat("nearclip", viewNearClip);
    cameraElem.SetFloat("farclip", viewFarClip);
    cameraElem.SetFloat("fov", viewFov);
    cameraElem.SetFloat("speed", cameraBaseSpeed);
    cameraElem.SetBool("limitrotation", limitRotation);
    cameraElem.SetBool("mousewheelcameraposition", mouseWheelCameraPosition);
    cameraElem.SetUInt("viewportmode", viewportMode);
    cameraElem.SetInt("mouseorbitmode", mouseOrbitMode);

    objectElem.SetFloat("newnodedistance", newNodeDistance);
    objectElem.SetFloat("movestep", moveStep);
    objectElem.SetFloat("rotatestep", rotateStep);
    objectElem.SetFloat("scalestep", scaleStep);
    objectElem.SetBool("movesnap", moveSnap);
    objectElem.SetBool("rotatesnap", rotateSnap);
    objectElem.SetBool("scalesnap", scaleSnap);
    objectElem.SetBool("applymateriallist", applyMaterialList);
    objectElem.SetAttribute("importoptions", importOptions);
    objectElem.SetInt("pickmode", pickMode);
    objectElem.SetInt("axismode", axisMode);
    objectElem.SetBool("revertonpause", revertOnPause);

    resourcesElem.SetBool("rememberresourcepath", rememberResourcePath);
    resourcesElem.SetAttribute("resourcepath", sceneResourcePath);
    resourcesElem.SetAttribute("importpath", uiImportPath);
    resourcesElem.SetAttribute("recentscenes", Join(uiRecentScenes, ";"));

    if (renderer !is null && graphics !is null)
    {
        renderingElem.SetInt("texturequality", renderer.textureQuality);
        renderingElem.SetInt("materialquality", renderer.materialQuality);
        renderingElem.SetInt("shadowresolution", GetShadowResolution());
        renderingElem.SetInt("maxoccludertriangles", renderer.maxOccluderTriangles);
        renderingElem.SetBool("specularlighting", renderer.specularLighting);
        // If Shader Model 3 is not supported, save the remembered instancing & quality settings instead of reduced settings
        renderingElem.SetInt("shadowquality", graphics.sm3Support ? renderer.shadowQuality : shadowQualitySetting);
        renderingElem.SetBool("dynamicinstancing", graphics.sm3Support ? renderer.dynamicInstancing : instancingSetting);
    }

    renderingElem.SetBool("framelimiter", engine.maxFps > 0);

    uiElem.SetFloat("minopacity", uiMinOpacity);
    uiElem.SetFloat("maxopacity", uiMaxOpacity);

    hierarchyElem.SetBool("showinternaluielement", showInternalUIElement);
    hierarchyElem.SetBool("showtemporaryobject", showTemporaryObject);
    inspectorElem.SetColor("nodecolor", nodeTextColor);
    inspectorElem.SetColor("componentcolor", componentTextColor);

    inspectorElem.SetColor("originalcolor", normalTextColor);
    inspectorElem.SetColor("modifiedcolor", modifiedTextColor);
    inspectorElem.SetColor("noneditablecolor", nonEditableTextColor);
    inspectorElem.SetBool("shownoneditable", showNonEditableAttribute);

    viewElem.SetBool("showgrid", showGrid);
    viewElem.SetBool("grid2dmode", grid2DMode);
    viewElem.SetColor("defaultzoneambientcolor", renderer.defaultZone.ambientColor);
    viewElem.SetColor("defaultzonefogcolor", renderer.defaultZone.fogColor);
    viewElem.SetFloat("defaultzonefogstart", renderer.defaultZone.fogStart);
    viewElem.SetFloat("defaultzonefogend", renderer.defaultZone.fogEnd);
    viewElem.SetInt("gridsize", gridSize);
    viewElem.SetInt("gridsubdivisions", gridSubdivisions);
    viewElem.SetFloat("gridscale", gridScale);
    viewElem.SetColor("gridcolor", gridColor);
    viewElem.SetColor("gridsubdivisioncolor", gridSubdivisionColor);

    consoleElem.SetAttribute("commandinterpreter", console.commandInterpreter);

    config.Save(File(configFileName, FILE_WRITE));
}

void MakeBackup(const String&in fileName)
{
    fileSystem.Rename(fileName, fileName + ".old");
}

void RemoveBackup(bool success, const String&in fileName)
{
    if (success)
        fileSystem.Delete(fileName + ".old");
}

Remove duplicated call of UpdateViewParameters.[ci skip]