package aerys.minko.type.xpath
{
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.binding.DataBindings;

	import flash.utils.getQualifiedClassName;

	public final class XPathEvaluator
	{
		private var _path 		: String;
		private var _selection	: Vector.<ISceneNode>		= null;
		private var _modifier	: String					= null;
		private var _lexer		: XPathLexer				= null;

		public function get selection() : Vector.<ISceneNode>
		{
			return _selection;
		}

		public function XPathEvaluator(path			: String,
									   selection	: Vector.<ISceneNode>,
									   modifier		: String    = null)
		{
			_modifier = modifier;

			initialize(path, selection);
		}

		private function initialize(path : String, selection : Vector.<ISceneNode>) : void
		{
			_path = path;
			_lexer = new XPathLexer(_path);

			// update root
			var token : String = _lexer.getToken();

			_selection = selection.slice();
			if (token == "/")
			{
				selectRoots();
				_lexer.nextToken(token);
			}

			// parse
			while ((token = _lexer.getToken()) != null)
			{
				switch (token)
				{
					case '//' :
						_lexer.nextToken(token);
						selectDescendants();
						break ;
					case '/' :
						_lexer.nextToken(token);
						selectChildren();
						break ;
					default :
						_lexer.nextToken(token);
						parseNodeType(token);
						break ;
				}
			}
		}

		private function selectChildren(typeName : String = null) : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			if (typeName != null)
				typeName = typeName.toLowerCase();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node is Group)
				{
					var group 		: Group = node as Group;
					var numChildren : uint 	= group.numChildren;

					for (var i : uint = 0; i < numChildren; ++i)
					{
						var child 		: ISceneNode 	= group.getChildAt(i);
						var className	: String		= getQualifiedClassName(child)
						var childType 	: String 		= className.substr(className.lastIndexOf(':') + 1);

						if (typeName == null || childType.toLowerCase() == typeName)
							_selection.push(child);
					}
				}
			}
		}

		private function selectRoots() : void
		{
			var selection	: Vector.<ISceneNode>	= _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			if (_selection.indexOf(node.root) < 0)
				_selection.push(node);
		}

		private function selectDescendants() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				_selection.push(node);
				if (node is Group)
					(node as Group).getDescendantsByType(ISceneNode, _selection);
			}
		}

		private function selectParents() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node.parent)
					_selection.push(node.parent);
				else
					_selection.push(node);
			}
		}

		private function parseNodeType(nodeType : String) : void
		{
			if (nodeType == '.')
			{
				// nothing
			}
			if (nodeType == '..')
				selectParents();
			else if (nodeType == '*')
				selectChildren();
			else
				selectChildren(nodeType);

			// apply predicates
			var token : String = _lexer.getToken();

			while (token == '[')
			{
				_lexer.nextToken(token);
				parsePredicate();

				token = _lexer.getToken();
			}
		}

		private function parsePredicate() : void
		{
			var propertyName	: String	= _lexer.getToken(true);
			var isBinding		: Boolean	= propertyName == '@';

			if (isBinding)
				propertyName = _lexer.getToken(true);

			var index			: int		= parseInt(propertyName);

			filterProperty(_lexer, propertyName, _selection);

			_lexer.checkNextToken(']');
		}

		private function getValueObject(source : Object, chunks : Array) : Object
		{
			if (chunks)
				for each (var chunk : String in chunks)
				source = source[chunk];

			return source;
		}

		private function removeFromSelection(index : uint) : void
		{
			var numNodes : uint = _selection.length - 1;

			_selection[index] = _selection[numNodes];
			_selection.length = numNodes;
		}

		private function filterProperty(lexer : XPathLexer, propertyName : String, selection : Vector.<ISceneNode>) : void
		{
			var isBinding		: Boolean	= propertyName == '@';
			var index			: int		= parseInt(propertyName);

			if (propertyName == 'hasController')
				filterOnController(lexer, selection);
			else if (propertyName == 'hasProperty')
				filterOnProperty(lexer, selection);
			else if (propertyName == 'position')
				filterOnPosition(lexer, selection);
			else if (propertyName == 'last')
				filterLast(lexer, selection);
			else if (propertyName == 'first')
				filterFirst(lexer, selection);
			else if (index.toString() == propertyName)
			{
				if (index < selection.length)
				{
					selection[0] = selection[index];
					selection.length = 1;
				}
				else
					selection.length = 0;
			}
			else
				filterOnValue(lexer, selection, propertyName, isBinding);
		}

		private function filterOnValue(lexer		: XPathLexer,
									   selection	: Vector.<ISceneNode>,
									   propertyName : String,
									   isBinding	: Boolean = false) : void
		{
			var operator	: String	= lexer.getToken(true);
			var chunks		: Array		= [propertyName];

			while (operator == '.')
			{
				chunks.push(lexer.getToken(true));
				operator = lexer.getToken(true);
			}

			var value		: Object	= lexer.getValueToken();
			var numNodes	: uint		= selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node		: ISceneNode	= selection[i];
				var nodeValue 	: Object 		= null;

				if (isBinding && (node['bindings'] is DataBindings))
					nodeValue = (node['bindings'] as DataBindings).getProperty(propertyName);
				else
				{
					try
					{
						nodeValue = getValueObject(node, chunks);
						if (!compare(operator, nodeValue, value))
							removeFromSelection(i);
					}
					catch (e : Error)
					{
						removeFromSelection(i);
					}
				}

			}
		}

		private function filterLast(lexer : XPathLexer, selection : Vector.<ISceneNode>) : void
		{
			lexer.checkNextToken('(');
			lexer.checkNextToken(')');

			if (selection.length)
			{
				selection[0] = selection[uint(selection.length - 1)];
				selection.length = 1;
			}
		}

		private function filterFirst(lexer : XPathLexer, selection : Vector.<ISceneNode>) : void
		{
			lexer.checkNextToken('(');
			lexer.checkNextToken(')');

			selection.length = 1;
		}

		private function filterOnController(lexer : XPathLexer, selection : Vector.<ISceneNode>) : Object
		{
			lexer.checkNextToken('(');
			var controllerName : String = lexer.getToken(true);
			lexer.checkNextToken(')');

			var numNodes	: uint	= selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node			: ISceneNode	= selection[i];
				var numControllers	: uint			= node.numControllers;
				var keepSceneNode	: Boolean		= false;

				for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
				{
					var controllerType : String = getQualifiedClassName(node.getController(controllerId));
					controllerType = controllerType.substr(controllerType.lastIndexOf(':') + 1);

					if (controllerType == controllerName)
					{
						keepSceneNode = true;
						break;
					}
				}

				if (!keepSceneNode)
					removeFromSelection(i);
			}

			return null;
		}

		private function filterOnPosition(lexer : XPathLexer, selection : Vector.<ISceneNode>) : void
		{
			lexer.checkNextToken('(');
			lexer.checkNextToken(')');

			var operator 	: String 	= lexer.getToken(true);
			var value 		: uint 		= uint(parseInt(lexer.getToken(true)));

			switch (operator)
			{
				case '>':
					++value;
				case '>=':
					selection = selection.slice(value);
					break;
				case '<':
					--value;
				case '<=':
					selection = selection.slice(0, value);
					break;
				case '=':
				case '==':
					selection[0] = selection[value];
					selection.length = 1;
				default:
					throw new Error('Unknown comparison operator \'' + operator + '\'');
			}
		}

		private function filterOnProperty(lexer : XPathLexer, selection : Vector.<ISceneNode>) : void
		{
			lexer.checkNextToken('(');

			var chunks		: Array 	= [lexer.getToken(true)];
			var operator	: String 	= lexer.getToken(true);

			while (operator == '.')
			{
				chunks.push(operator);
				operator = lexer.getToken(true);
			}

			if (operator != ')')
				lexer.throwParseError(')', operator);

			var numNodes : uint	= selection.length;
			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				try
				{
					getValueObject(selection[i], chunks);
				}
				catch (e : Error)
				{
					removeFromSelection(i);
				}
			}
		}

		private function compare(operator : String, a : Object, b : Object) : Boolean
		{
			switch (operator)
			{
				case '>' :
					return a > b;
				case '>=' :
					return a >= b;
				case '<' :
					return a >= b;
				case '<=' :
					return a <= b;
				case '=' :
				case '==' :
					return a == b;
				case '~=' :
					var matches	: Array	= String(a).match(b);

					return matches && matches.length != 0;
				default:
					throw new Error('Unknown comparison operator \'' + operator + '\'');
			}
		}
	}
}

package aerys.minko.type.xpath
{
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.binding.DataBindings;

	import flash.utils.getQualifiedClassName;

	public final class XPathEvaluator
	{
		private var _path 		: String;
		private var _selection	: Vector.<ISceneNode>		= null;
		private var _modifier	: String					= null;
		private var _lexer		: XPathLexer				= null;

		public function get selection() : Vector.<ISceneNode>
		{
			return _selection;
		}

		public function XPathEvaluator(path			: String,
									   selection	: Vector.<ISceneNode>,
									   modifier		: String    = null)
		{
			_modifier = modifier;

			initialize(path, selection);
		}

		private function initialize(path : String, selection : Vector.<ISceneNode>) : void
		{
			_path = path;
			_lexer = new XPathLexer(_path);

			// update root
			var token : String = _lexer.getToken();

			_selection = selection.slice();
			if (token == "/")
			{
				selectRoots();
				_lexer.nextToken(token);
			}

			// parse
			while ((token = _lexer.getToken()) != null)
			{
				switch (token)
				{
					case '//' :
						_lexer.nextToken(token);
						selectDescendants();
						break ;
					case '/' :
						_lexer.nextToken(token);
						selectChildren();
						break ;
					default :
						_lexer.nextToken(token);
						parseNodeType(token);
						break ;
				}
			}
		}

		private function selectChildren(typeName : String = null) : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			if (typeName != null)
				typeName = typeName.toLowerCase();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node is Group)
				{
					var group 		: Group = node as Group;
					var numChildren : uint 	= group.numChildren;

					for (var i : uint = 0; i < numChildren; ++i)
					{
						var child 		: ISceneNode 	= group.getChildAt(i);
						var className	: String		= getQualifiedClassName(child)
						var childType 	: String 		= className.substr(className.lastIndexOf(':') + 1);

						if (typeName == null || childType.toLowerCase() == typeName)
							_selection.push(child);
					}
				}
			}
		}

		private function selectRoots() : void
		{
			var selection	: Vector.<ISceneNode>	= _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			if (_selection.indexOf(node.root) < 0)
				_selection.push(node);
		}

		private function selectDescendants() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				_selection.push(node);
				if (node is Group)
					(node as Group).getDescendantsByType(ISceneNode, _selection);
			}
		}

		private function selectParents() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node.parent)
					_selection.push(node.parent);
				else
					_selection.push(node);
			}
		}

		private function parseNodeType(nodeType : String) : void
		{
			if (nodeType == '.')
			{
				// nothing
			}
			if (nodeType == '..')
				selectParents();
			else if (nodeType == '*')
				selectChildren();
			else
				selectChildren(nodeType);

			// apply predicates
			var token : String = _lexer.getToken();

			while (token == '[')
			{
				_lexer.nextToken(token);
				parsePredicate();

				token = _lexer.getToken();
			}
		}

		private function parsePredicate() : void
		{
			var propertyName	: String	= _lexer.getToken(true);
			var isBinding		: Boolean	= propertyName == '@';

			if (isBinding)
				propertyName = _lexer.getToken(true);

			var index			: int		= parseInt(propertyName);

			filterProperty(_lexer, propertyName, _selection);

			_lexer.checkNextToken(']');
		}

		private function getValueObject(source : Object, chunks : Array) : Object
		{
			if (chunks)
				for each (var chunk : String in chunks)
				source = source[chunk];

			return source;
		}

		private function removeFromSelection(index : uint) : void
		{
			var numNodes : uint = _selection.length - 1;

			_selection[index] = _selection[numNodes];
			_selection.length = numNodes;
		}

		private function filterProperty(lexer : XPathLexer, propertyName : String, selection : Vector.<ISceneNode>) : void
		{
			var isBinding		: Boolean	= propertyName == '@';
			var index			: int		= parseInt(propertyName);

			if (propertyName == 'hasController')
				filterOnController(lexer, selection);
			else if (propertyName == 'hasProperty')
				filterOnProperty(lexer, selection);
			else if (propertyName == 'position')
				filterOnPosition(lexer, selection);
			else if (propertyName == 'last')
				filterLast(lexer, selection);
			else if (propertyName == 'first')
				filterFirst(lexer, selection);
			else if (index.toString() == propertyName)
			{
				if (index < selection.length)
				{
					selection[0] = selection[index];
					selection.length = 1;
				}
				else
					selection.length = 0;
			}
			else
				filterOnValue(lexer, selection, propertyName, isBinding);
		}

		private function filterOnValue(lexer		: XPathLexer,
									   selection	: Vector.<ISceneNode>,
									   propertyName : String,
									   isBinding	: Boolean = false) : void
		{
			var operator	: String	= lexer.getToken(true);
			var chunks		: Array		= [propertyName];

			while (operator == '.')
			{
				chunks.push(lexer.getToken(true));
				operator = lexer.getToken(true);
			}

			var value		: Object	= lexer.getValueToken();
			var numNodes	: uint		= selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node		: ISceneNode	= selection[i];
				var nodeValue 	: Object 		= null;

				if (isBinding && (node['bindings'] is DataBindings))
					nodeValue = (node['bindings'] as DataBindings).getProperty(propertyName);
				else
				{
					try
					{
						nodeValue = getValueObject(node, chunks);
						if (!compare(operator, nodeValue, value))
							removeFromSelection(i);
					}
					catch (e : Error)
					{
						removeFromSelection(i);
					}
				}

			}
		}

		private function filterLast(lexer : XPathLexer, selection : Vector.<ISceneNode>) : void
		{
			lexer.checkNextToken('(');
			lexer.checkNextToken(')');

			if (selection.length)
			{
				selection[0] = selection[uint(selection.length - 1)];
				selection.length = 1;
			}
		}

		private function filterFirst(lexer : XPathLexer, selection : Vector.<ISceneNode>) : void
		{
			lexer.checkNextToken('(');
			lexer.checkNextToken(')');

			selection.length = 1;
		}

		private function filterOnController(lexer : XPathLexer, selection : Vector.<ISceneNode>) : Object
		{
			lexer.checkNextToken('(');
			var controllerName : String = lexer.getToken(true);
			lexer.checkNextToken(')');

			var numNodes	: uint	= selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node			: ISceneNode	= selection[i];
				var numControllers	: uint			= node.numControllers;
				var keepSceneNode	: Boolean		= false;

				for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
				{
					var controllerType : String = getQualifiedClassName(node.getController(controllerId));
					controllerType = controllerType.substr(controllerType.lastIndexOf(':') + 1);

					if (controllerType == controllerName)
					{
						keepSceneNode = true;
						break;
					}
				}

				if (!keepSceneNode)
					removeFromSelection(i);
			}

			return null;
		}

		private function filterOnPosition(lexer : XPathLexer, selection : Vector.<ISceneNode>) : void
		{
			lexer.checkNextToken('(');
			lexer.checkNextToken(')');

			var operator 	: String 	= lexer.getToken(true);
			var value 		: uint 		= uint(parseInt(lexer.getToken(true)));

			switch (operator)
			{
				case '>':
					++value;
				case '>=':
					selection = selection.slice(value);
					break;
				case '<':
					--value;
				case '<=':
					selection = selection.slice(0, value);
					break;
				case '=':
				case '==':
					selection[0] = selection[value];
					selection.length = 1;
				default:
					throw new Error('Unknown comparison operator \'' + operator + '\'');
			}
		}

		private function filterOnProperty(lexer : XPathLexer, selection : Vector.<ISceneNode>) : void
		{
			lexer.checkNextToken('(');

			var chunks		: Array 	= [lexer.getToken(true)];
			var operator	: String 	= lexer.getToken(true);

			while (operator == '.')
			{
				//chunks.push(operator);
				chunks.push(lexer.getToken(true));
				operator = lexer.getToken(true);
			}

			if (operator != ')')
				lexer.throwParseError(')', operator);

			var numNodes : uint	= selection.length;
			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				try
				{
					if (getValueObject(selection[i], chunks) == null)
						removeFromSelection(i);
				}
				catch (e : Error)
				{
					removeFromSelection(i);
				}
			}
		}

		private function compare(operator : String, a : Object, b : Object) : Boolean
		{
			switch (operator)
			{
				case '>' :
					return a > b;
				case '>=' :
					return a >= b;
				case '<' :
					return a >= b;
				case '<=' :
					return a <= b;
				case '=' :
				case '==' :
					return a == b;
				case '~=' :
					var matches	: Array	= String(a).match(b);

					return matches && matches.length != 0;
				default:
					throw new Error('Unknown comparison operator \'' + operator + '\'');
			}
		}
	}
}

Fix xpath for hasProperty command

Fix xpath for hasProperty command


package com.axis.httpclient {
  import com.axis.ClientEvent;
  import com.axis.NetStreamClient;
  import com.axis.ErrorManager;
  import com.axis.IClient;
  import com.axis.Logger;

  import flash.utils.*;
  import flash.events.Event;
  import flash.events.EventDispatcher;
  import flash.events.NetStatusEvent;
  import flash.media.Video;
  import flash.net.NetConnection;
  import flash.net.NetStream;

  public class HTTPClient extends NetStreamClient implements IClient {
    private var urlParsed:Object;
    private var nc:NetConnection;

    private static const FORCED_FPS:Number = 20;
    private static const UPDATE_VIRT_BUFFER_INTERVAL:Number = 1000 / FORCED_FPS;
    private var updateLoop:uint = 0;
    private var virtPause:Boolean = false;
    private var userPause:Boolean = false;
    private var frameByFrame:Boolean = false;
    private var virtBuffer:Number = 0;
    private var streamBuffer:Number = 0;

    public function HTTPClient(urlParsed:Object) {
      this.urlParsed = urlParsed;
    }

    public function start(options:Object):Boolean {
      Logger.log('HTTPClient: playing:', urlParsed.full);

      this.frameByFrame = Player.config.frameByFrame;
      nc = new NetConnection();
      nc.connect(null);
      nc.addEventListener(NetStatusEvent.NET_STATUS, onConnectionStatus);

      this.ns = new NetStream(nc);
      this.setupNetStream();

      this.ns.play(urlParsed.full);
      this.ns.addEventListener(NetStatusEvent.NET_STATUS, this.onNetStreamStatus);
      this.updateLoop = setInterval(this.updateVirtualBuffer, UPDATE_VIRT_BUFFER_INTERVAL);
      return true;
    }

    public function stop():Boolean {
      clearInterval(this.updateLoop);
      this.ns.dispose();
      this.nc.close();
      this.currentState = 'stopped';
      dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
      return true;
    }

    public function seek(position:Number):Boolean {
      this.ns.seek(position);
      return true;
    }

    public function pause():Boolean {
      if (this.currentState !== 'playing') {
        ErrorManager.dispatchError(800);
        return false;
      }

      this.userPause = true;
      this.ns.pause();

      if (this.virtPause) {
        dispatchEvent(new ClientEvent(ClientEvent.PAUSED, { 'reason': 'user' }));
      }

      return true;
    }

    public function resume():Boolean {
      if (this.currentState !== 'paused') {
        ErrorManager.dispatchError(801);
        return false;
      }
      this.userPause = false;
      if (!this.virtPause) {
        this.ns.resume();
      } else {
        this.currentState = 'playing';
        dispatchEvent(new ClientEvent(ClientEvent.START_PLAY));
      }
      return true;
    }

    public function setFrameByFrame(frameByFrame:Boolean):Boolean {
      this.frameByFrame = frameByFrame;
      return true;
    }

    override public function bufferedTime():Number {
      var c:Number = this.getCurrentTime();
      return c >= 0 ? Math.max(this.virtBuffer - c, 0) : c;
    }

    public function playFrames(timestamp:Number):void {
      if (this.virtBuffer < timestamp) {
        this.virtBuffer = timestamp;
      }
    }

    public function setBuffer(seconds:Number):Boolean {
      this.ns.bufferTime = seconds;
      if (!this.userPause && !this.virtPause) {
        this.ns.pause();
        this.ns.resume();
      }
      return true;
    }

    private function updateVirtualBuffer():void {
      if (this.currentState == 'stopped') {
        return;
      }

      if (this.frameByFrame) {
        var buffer:Number = this.getCurrentTime() + super.bufferedTime();
        var step:Number = 1000 / FORCED_FPS;
        while (this.streamBuffer < buffer) {
          this.streamBuffer = Math.min(this.streamBuffer + step, buffer)
          dispatchEvent(new ClientEvent(ClientEvent.FRAME, this.streamBuffer));
        }
        if (!this.virtPause && this.virtBuffer <= this.getCurrentTime() && !streamEnded) {
          Logger.log('HTTPClient: pause caused by virtual buffer ran out', { currentTime: this.getCurrentTime(), virtualBuffer: this.virtBuffer, realBuffer: super.bufferedTime() });
          this.virtPause = true;
          // Prevent NetStreamClient from triggering paused event
          this.currentState = 'paused';
          this.ns.pause();
        } else if (this.virtPause && this.virtBuffer - this.getCurrentTime() >= this.ns.bufferTime * 1000) {
          this.virtPause = false;
          if (!this.userPause) {
            this.ns.resume();
          }
        }
      } else {
        this.virtBuffer = this.getCurrentTime() + super.bufferedTime();
      }
    }

    private function onNetStreamStatus(event:NetStatusEvent):void {
      if ('NetStream.Play.Stop' === event.info.code) {
        clearInterval(this.updateLoop);
      }
    }

    private function onConnectionStatus(event:NetStatusEvent):void {
      Logger.log('HTTPClient: connection status:', event.info.code);
      if ('NetConnection.Connect.Closed' === event.info.code && this.currentState !== 'stopped') {
        clearInterval(this.updateLoop);
        this.currentState = 'stopped';
        dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
        this.ns.dispose();
      }
    }
  }
}

package com.axis.httpclient {
  import com.axis.ClientEvent;
  import com.axis.NetStreamClient;
  import com.axis.ErrorManager;
  import com.axis.IClient;
  import com.axis.Logger;

  import flash.utils.*;
  import flash.events.Event;
  import flash.events.EventDispatcher;
  import flash.events.NetStatusEvent;
  import flash.media.Video;
  import flash.net.NetConnection;
  import flash.net.NetStream;

  public class HTTPClient extends NetStreamClient implements IClient {
    private var urlParsed:Object;
    private var nc:NetConnection;

    private static const FORCED_FPS:Number = 30;
    private static const UPDATE_VIRT_BUFFER_INTERVAL:Number = 1000 / FORCED_FPS;
    private var updateLoop:uint = 0;
    private var virtPause:Boolean = false;
    private var userPause:Boolean = false;
    private var frameByFrame:Boolean = false;
    private var virtBuffer:Number = 0;
    private var streamBuffer:Number = 0;

    public function HTTPClient(urlParsed:Object) {
      this.urlParsed = urlParsed;
    }

    public function start(options:Object):Boolean {
      Logger.log('HTTPClient: playing:', urlParsed.full);

      this.frameByFrame = Player.config.frameByFrame;
      nc = new NetConnection();
      nc.connect(null);
      nc.addEventListener(NetStatusEvent.NET_STATUS, onConnectionStatus);

      this.ns = new NetStream(nc);
      this.setupNetStream();

      this.ns.play(urlParsed.full);
      this.ns.addEventListener(NetStatusEvent.NET_STATUS, this.onNetStreamStatus);
      this.updateLoop = setInterval(this.updateVirtualBuffer, UPDATE_VIRT_BUFFER_INTERVAL);
      return true;
    }

    public function stop():Boolean {
      clearInterval(this.updateLoop);
      this.ns.dispose();
      this.nc.close();
      this.currentState = 'stopped';
      dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
      return true;
    }

    public function seek(position:Number):Boolean {
      this.ns.seek(position);
      return true;
    }

    public function pause():Boolean {
      if (this.currentState !== 'playing') {
        ErrorManager.dispatchError(800);
        return false;
      }

      this.userPause = true;
      this.ns.pause();

      if (this.virtPause) {
        dispatchEvent(new ClientEvent(ClientEvent.PAUSED, { 'reason': 'user' }));
      }

      return true;
    }

    public function resume():Boolean {
      if (this.currentState !== 'paused') {
        ErrorManager.dispatchError(801);
        return false;
      }
      this.userPause = false;
      if (!this.virtPause) {
        this.ns.resume();
      } else {
        this.currentState = 'playing';
        dispatchEvent(new ClientEvent(ClientEvent.START_PLAY));
      }
      return true;
    }

    public function setFrameByFrame(frameByFrame:Boolean):Boolean {
      this.frameByFrame = frameByFrame;
      return true;
    }

    override public function bufferedTime():Number {
      var c:Number = this.getCurrentTime();
      return c >= 0 ? Math.max(this.virtBuffer - c, 0) : c;
    }

    public function playFrames(timestamp:Number):void {
      if (this.virtBuffer < timestamp) {
        this.virtBuffer = timestamp;
      }
    }

    public function setBuffer(seconds:Number):Boolean {
      this.ns.bufferTime = seconds;
      if (!this.userPause && !this.virtPause) {
        this.ns.pause();
        this.ns.resume();
      }
      return true;
    }

    private function updateVirtualBuffer():void {
      if (this.currentState == 'stopped') {
        return;
      }

      if (this.frameByFrame) {
        var buffer:Number = this.getCurrentTime() + super.bufferedTime();
        var step:Number = 1000 / FORCED_FPS;
        while (this.streamBuffer < buffer) {
          this.streamBuffer = Math.min(this.streamBuffer + step, buffer)
          dispatchEvent(new ClientEvent(ClientEvent.FRAME, this.streamBuffer));
        }
        if (!this.virtPause && this.virtBuffer <= this.getCurrentTime() && !streamEnded) {
          Logger.log('HTTPClient: pause caused by virtual buffer ran out', { currentTime: this.getCurrentTime(), virtualBuffer: this.virtBuffer, realBuffer: super.bufferedTime() });
          this.virtPause = true;
          // Prevent NetStreamClient from triggering paused event
          this.currentState = 'paused';
          this.ns.pause();
        } else if (this.virtPause && this.virtBuffer - this.getCurrentTime() >= this.ns.bufferTime * 1000) {
          this.virtPause = false;
          if (!this.userPause) {
            this.ns.resume();
          }
        }
      } else {
        this.virtBuffer = this.getCurrentTime() + super.bufferedTime();
      }
    }

    private function onNetStreamStatus(event:NetStatusEvent):void {
      if ('NetStream.Play.Stop' === event.info.code) {
        clearInterval(this.updateLoop);
      }
    }

    private function onConnectionStatus(event:NetStatusEvent):void {
      Logger.log('HTTPClient: connection status:', event.info.code);
      if ('NetConnection.Connect.Closed' === event.info.code && this.currentState !== 'stopped') {
        clearInterval(this.updateLoop);
        this.currentState = 'stopped';
        dispatchEvent(new ClientEvent(ClientEvent.STOPPED));
        this.ns.dispose();
      }
    }
  }
}

Increase virtual HTTP stream FPS to 30

Increase virtual HTTP stream FPS to 30

Affects granularity of frame by frame feature.


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

import flash.events.StatusEvent;
import flash.net.LocalConnection;

import mx.logging.ILogger;
import mx.logging.Log;

public final class LocalConnectionDelegate
{
    private static const LOG:ILogger = Log.getLogger('com.esri.builder.controllers.supportClasses.LocalConnectionDelegate');
    private static const CNAME:String = '_flexViewer';

    private var m_localConnection:LocalConnection;

    private function get localConnection():LocalConnection
    {
        if (m_localConnection === null)
        {
            if (Log.isDebug())
            {
                LOG.debug("Acquiring local connection");
            }

            m_localConnection = new LocalConnection();
            m_localConnection.addEventListener(StatusEvent.STATUS, localConnection_statusHandler);
        }
        return m_localConnection;
    }

    private function localConnection_statusHandler(event:StatusEvent):void
    {
        if (event.level == "error")
        {
            if (Log.isDebug())
            {
                LOG.debug("Call failed: {0}", event.toString());
            }
        }
    }

    public function setTitles(title:String, subtitle:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending titles changes: title {0} - subtitle {1}", title, subtitle);
        }

        callViewerMethod('setTitles', title, subtitle);
    }

    private function callViewerMethod(methodName:String, ... values):void
    {
        try
        {
            //use Function#apply to avoid passing rest argument as Array
            localConnection.send.apply(null, [ CNAME, methodName ].concat(values));
        }
        catch (error:Error)
        {
            if (Log.isDebug())
            {
                LOG.debug("Call to Viewer method {0} failed: {1}", methodName, error);
            }
        }
    }

    public function setLogo(value:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending logo changes {0}", value);
        }

        callViewerMethod('setLogo', value);
    }

    public function setTextColor(value:uint):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending text color changes {0}", value);
        }

        callViewerMethod('setTextColor', value);
    }

    public function setBackgroundColor(value:uint):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending background color changes {0}", value);
        }

        callViewerMethod('setBackgroundColor', value);
    }

    public function setRolloverColor(value:uint):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending rollover color changes {0}", value);
        }

        callViewerMethod('setRolloverColor', value);
    }

    public function setSelectionColor(value:uint):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending selection color changes {0}", value);
        }

        callViewerMethod('setSelectionColor', value);
    }

    public function setTitleColor(value:uint):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending title color changes {0}", value);
        }

        callViewerMethod('setTitleColor', value);
    }

    public function setLocale(localeChain:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending locale changes {0}", localeChain);
        }

        callViewerMethod('setLocale', localeChain);
    }

    public function setFontName(value:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending font name changes {0}", value);
        }

        callViewerMethod('setFontName', value);
    }

    public function setAppTitleFontName(value:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending app title font name changes {0}", value);
        }

        callViewerMethod('setAppTitleFontName', value);
    }

    public function setSubTitleFontName(value:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending subtitle font name changes {0}", value);
        }

        callViewerMethod('setSubTitleFontName', value);
    }

    public function setAlpha(value:Number):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending alpha changes {0}", value);
        }

        callViewerMethod('setAlpha', value);
    }

    public function setPredefinedStyles(value:Object):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending predefined changes {0}", value);
        }

        callViewerMethod('setPredefinedStyles', value);
    }
}
}

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

import com.esri.builder.supportClasses.LogUtil;

import flash.events.StatusEvent;
import flash.net.LocalConnection;

import mx.logging.ILogger;
import mx.logging.Log;

public final class LocalConnectionDelegate
{
    private static const LOG:ILogger = LogUtil.createLogger(LocalConnectionDelegate);
    private static const CONNECTION_NAME:String = '_flexViewer';

    private var _localConnection:LocalConnection;

    private function get localConnection():LocalConnection
    {
        if (_localConnection === null)
        {
            if (Log.isDebug())
            {
                LOG.debug("Acquiring local connection");
            }

            _localConnection = new LocalConnection();
            _localConnection.addEventListener(StatusEvent.STATUS, localConnection_statusHandler);
        }
        return _localConnection;
    }

    private function localConnection_statusHandler(event:StatusEvent):void
    {
        if (event.level == "error")
        {
            if (Log.isDebug())
            {
                LOG.debug("Call failed: {0}", event.toString());
            }
        }
    }

    public function setTitles(title:String, subtitle:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending titles changes: title {0} - subtitle {1}", title, subtitle);
        }

        callViewerMethod('setTitles', title, subtitle);
    }

    private function callViewerMethod(methodName:String, ... values):void
    {
        try
        {
            //use Function#apply to avoid passing rest argument as Array
            localConnection.send.apply(null, [ CONNECTION_NAME, methodName ].concat(values));
        }
        catch (error:Error)
        {
            if (Log.isDebug())
            {
                LOG.debug("Call to Viewer method {0} failed: {1}", methodName, error);
            }
        }
    }

    public function setLogo(value:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending logo changes {0}", value);
        }

        callViewerMethod('setLogo', value);
    }

    public function setTextColor(value:uint):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending text color changes {0}", value);
        }

        callViewerMethod('setTextColor', value);
    }

    public function setBackgroundColor(value:uint):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending background color changes {0}", value);
        }

        callViewerMethod('setBackgroundColor', value);
    }

    public function setRolloverColor(value:uint):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending rollover color changes {0}", value);
        }

        callViewerMethod('setRolloverColor', value);
    }

    public function setSelectionColor(value:uint):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending selection color changes {0}", value);
        }

        callViewerMethod('setSelectionColor', value);
    }

    public function setTitleColor(value:uint):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending title color changes {0}", value);
        }

        callViewerMethod('setTitleColor', value);
    }

    public function setLocale(localeChain:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending locale changes {0}", localeChain);
        }

        callViewerMethod('setLocale', localeChain);
    }

    public function setFontName(value:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending font name changes {0}", value);
        }

        callViewerMethod('setFontName', value);
    }

    public function setAppTitleFontName(value:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending app title font name changes {0}", value);
        }

        callViewerMethod('setAppTitleFontName', value);
    }

    public function setSubTitleFontName(value:String):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending subtitle font name changes {0}", value);
        }

        callViewerMethod('setSubTitleFontName', value);
    }

    public function setAlpha(value:Number):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending alpha changes {0}", value);
        }

        callViewerMethod('setAlpha', value);
    }

    public function setPredefinedStyles(value:Object):void
    {
        if (Log.isDebug())
        {
            LOG.debug("Sending predefined changes {0}", value);
        }

        callViewerMethod('setPredefinedStyles', value);
    }
}
}

Clean up.

Clean up.


package aerys.minko.type.animation
{
	import aerys.minko.scene.node.IScene;
	import aerys.minko.scene.node.ITransformable;
	import aerys.minko.scene.node.group.Group;
	import aerys.minko.type.animation.timeline.ITimeline;

	import flash.utils.getTimer;

	import org.osmf.elements.DurationElement;

	public class Animation
	{
		private var _id				: String;

		private var _beginTime		: uint;
		private var _lastTime		: uint;

		private var _playingOn		: IScene;
		private var _timelines		: Vector.<ITimeline>;
		private var _duration		: uint;

		public function get id() : String { return _id; }

		public function Animation(id		: String,
								  timelines : Vector.<ITimeline>)
		{
			_id			= id;
			_timelines	= timelines;
			_lastTime	= 0;

			_duration	= 0;
			for each (var timeline : ITimeline in timelines)
				if (_duration < timeline.duration)
					_duration = timeline.duration;
		}

		public function tick() : void
		{
			var time : uint = (getTimer() - _beginTime) % _duration;
			transformNodes(time);
		}

		public function step() : void
		{
			var time : uint = (_lastTime + 2500 / 30) % _duration;
			transformNodes(time);
		}

		public function stepReverse() : void
		{
			var time : int = (_lastTime - 2500 / 30) % _duration;
			if (time < 0)
				time = time + _duration;

			transformNodes(time);

		}

		private function transformNodes(time : uint) : void
		{
			_lastTime = time;

			var timelinesCount : uint = _timelines.length;
			for (var i : uint = 0; i < timelinesCount; ++i)
			{
				var timeline		: ITimeline	= _timelines[i];
				var timelineTarget	: String	= timeline.target;

				var target			: ITransformable;
				if (_playingOn.name == timelineTarget)
					target = ITransformable(_playingOn);
				else if (_playingOn is Group)
					target = ITransformable(Group(_playingOn).getDescendantByName(timelineTarget));

				if (!target)
					continue;

				timeline.updateAt(time, target);
			}
		}

		public function playOn(node : IScene) : void
		{
			_beginTime	= getTimer();
			_playingOn	= node;
			_lastTime	= 0;
		}

		public function stop() : void
		{
			_playingOn = null;
		}

	}
}

package aerys.minko.type.animation
{
	import aerys.minko.scene.node.IScene;
	import aerys.minko.scene.node.ITransformable;
	import aerys.minko.scene.node.group.Group;
	import aerys.minko.type.animation.timeline.ITimeline;

	import flash.utils.getTimer;

	import org.osmf.elements.DurationElement;

	public class Animation
	{
		private var _id				: String;

		private var _beginTime		: uint;
		private var _lastTime		: uint;

		private var _playingOn		: IScene;
		private var _timelines		: Vector.<ITimeline>;
		private var _duration		: uint;

		public function get id() : String { return _id; }

		public function Animation(id		: String,
								  timelines : Vector.<ITimeline>)
		{
			_id			= id;
			_timelines	= timelines;
			_lastTime	= 0;

			_duration	= 0;
			for each (var timeline : ITimeline in timelines)
				if (_duration < timeline.duration)
					_duration = timeline.duration;
		}

		public function tick() : void
		{
			var time : uint = (getTimer() - _beginTime) % _duration;
			transformNodes(time);
		}

		public function step(deltaTime : uint = 80) : void
		{
			var time : uint = (_lastTime + deltaTime) % _duration;
			transformNodes(time);
		}

		public function stepReverse(deltaTime : uint = 80) : void
		{
			var time : int = (_lastTime - deltaTime) % _duration;
			if (time < 0)
				time = time + _duration;

			transformNodes(time);
		}

		private function transformNodes(time : uint) : void
		{
			_lastTime = time;

			var timelinesCount : uint = _timelines.length;
			for (var i : uint = 0; i < timelinesCount; ++i)
			{
				var timeline		: ITimeline	= _timelines[i];
				var timelineTarget	: String	= timeline.target;

				var target			: ITransformable;
				if (_playingOn.name == timelineTarget)
					target = ITransformable(_playingOn);
				else if (_playingOn is Group)
					target = ITransformable(Group(_playingOn).getDescendantByName(timelineTarget));

				if (!target)
					continue;

				timeline.updateAt(time, target);
			}
		}

		public function playOn(node : IScene) : void
		{
			_beginTime	= getTimer();
			_playingOn	= node;
			_lastTime	= 0;
		}

		public function stop() : void
		{
			_playingOn = null;
		}

	}
}

Enable user to customise deltaTime when calling Animation::step and Animation::stepReverse


package aerys.minko.scene
{
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.binding.DataBindings;

	import avmplus.getQualifiedClassName;

	import flash.utils.Dictionary;
	import flash.utils.Proxy;
	import flash.utils.describeType;
	import flash.utils.flash_proxy;
	import flash.utils.getQualifiedClassName;

	public dynamic class SceneIterator extends Proxy
	{
		private static const TYPE_CACHE	: Dictionary		= new Dictionary(true);
		private static const OPERATORS	: Vector.<String>	= new <String>[
			"//", "/", "[", "]", "..", ".", "~=", "?=", "=", "@", "*"
		];
		private static const REGEX_TRIM	: RegExp			= /^\s+|\s+$/;

		private var _path		: String				= null;
		private var _selection	: Vector.<ISceneNode>	= null;
		private var _modifier	: String				= null;

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

		public function SceneIterator(path 		: String,
									  selection : Vector.<ISceneNode>,
									  modifier	: String	= "")
		{
			super();

			_modifier = modifier;

			initialize(path, selection);
		}

		public function toString() : String
		{
			return _selection.toString();
		}

		override flash_proxy function setProperty(name : *, value : *):void
		{
			var propertyName : String = name;

			for each (var node : ISceneNode in _selection)
				getValueObject(node, _modifier)[propertyName] = value;
		}

		override flash_proxy function getProperty(name : *) : *
		{
			var index : int = parseInt(name);

			if (index == name)
				return index < _selection.length ? _selection[index] : null;
			else
			{
				throw new Error(
					'Unable to get a property on a set of objects. '
					+ 'You must use the [] operator to fetch one of the objects.'
				);
			}
		}

		override flash_proxy function nextNameIndex(index : int) : int
		{
			return index < _selection.length ? index + 1 : 0;
		}

		override flash_proxy function nextName(index : int) : String
		{
			return String(index - 1);
		}

		override flash_proxy function nextValue(index : int) : *
		{
			return _selection[int(index - 1)];
		}

		override flash_proxy function callProperty(name:*, ...parameters):*
		{
			var methodName : String = name;

			for each (var node : ISceneNode in _selection)
			{
				var method : Function = getValueObject(node, _modifier)[methodName];

				method.apply(null, parameters);
			}

			return this;
		}

		private function initialize(path : String, selection : Vector.<ISceneNode>) : void
		{
			_path = path;

			// update root
			var token : String = getToken();

			_selection = selection.slice();
			if (token == "/")
			{
				selectRoots();
				nextToken(token);
			}

			// parse
			while (token = getToken())
			{
				switch (token)
				{
					case '//' :
						nextToken(token);
						selectDescendants();
						break ;
					case '/' :
						nextToken(token);
						selectChildren();
						break ;
					default :
						nextToken(token);
						parseNodeType(token);
						break ;
				}
			}
		}

		private function getToken(doNext : Boolean = false) : String
		{
			var token	: String	= null;

			if (!_path)
				return null;

			_path = _path.replace(/^\s+/, '');

			var nextOpIndex : int = int.MAX_VALUE;

			for each (var op : String in OPERATORS)
			{
				var opIndex : int = _path.indexOf(op);

				if (opIndex > 0 && opIndex < nextOpIndex)
					nextOpIndex = opIndex;

				if (opIndex == 0)
				{
					token = op;
					break ;
				}
			}

			if (!token)
				token = _path.substring(0, nextOpIndex);

			if (doNext)
				nextToken(token);

			return token;
		}

		private function getValueToken() : Object
		{
			var value : Object	= null;

			_path = _path.replace(/^\s+/, '');

			if (_path.charAt(0) == "'")
			{
				var endOfStringIndex : int = _path.indexOf("'", 1);

				if (endOfStringIndex < 0)
					throw new Error("Unterminated string expression.");

				var stringValue	: String	=  _path.substring(1, endOfStringIndex);

				_path = _path.substring(endOfStringIndex + 1);

				value = stringValue;
			}
			else
			{
				var token : String	= getToken(true);

				if (token == "true")
					value = true;
				else if (token == "false")
					value = false;
				else if (token.indexOf("0x") == 0)
					value = parseInt(token, 16);
			}

			return value;
		}

		private function nextToken(token : String) : void
		{
			_path = _path.substring(_path.indexOf(token) + token.length);
		}

		private function selectChildren(typeName : String = null) : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			if (typeName != null)
				typeName = typeName.toLowerCase();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node is Group)
				{
					var group 		: Group = node as Group;
					var numChildren : uint 	= group.numChildren;

					for (var i : uint = 0; i < numChildren; ++i)
					{
						var child 		: ISceneNode 	= group.getChildAt(i);
						var className	: String		= getQualifiedClassName(child)
						var childType 	: String 		= className.substr(className.lastIndexOf(':') + 1);

						if (typeName == null || childType.toLowerCase() == typeName)
							_selection.push(child);
					}
				}
			}
		}

		private function selectRoots() : void
		{
			var selection	: Vector.<ISceneNode>	= _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
				if (_selection.indexOf(node.root) < 0)
					_selection.push(node);
		}

		private function selectDescendants() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				_selection.push(node);
				if (node is Group)
					(node as Group).getDescendantsByType(ISceneNode, _selection);
			}
		}

		private function selectParents() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node.parent)
					_selection.push(node.parent);
				else
					_selection.push(node);
			}
		}

		private function parseNodeType(nodeType : String) : void
		{
			if (nodeType == '.')
			{
				// nothing
			}
			if (nodeType == '..')
				selectParents();
			else if (nodeType == '*')
				selectChildren();
			else
				selectChildren(nodeType);

			// apply predicates
			var token : String = getToken();

			while (token == "[")
			{
				nextToken(token);
				parsePredicate();

				token = getToken();
			}
		}

		private function parsePredicate() : void
		{
			var propertyName	: String	= getToken(true);
			var isBinding		: Boolean	= propertyName == '@';

			if (isBinding)
				propertyName = getToken(true);

			var index	: int	= parseInt(propertyName);

			if (index.toString() == propertyName)
			{
				if (index < _selection.length)
				{
					_selection[0] = _selection[index];
					_selection.length = 1;
				}
				else
					_selection.length = 0;
			}
			else
			{
				var operator	: String	= getToken(true);
				var value		: Object	= getValueToken();

				if (operator == "~=")
					filterOnRegExp(propertyName, String(value), isBinding);
				else if (operator == "?=")
					filterOnController(String(value));
				else
					filterOnValue(propertyName, value, isBinding);
			}

			checkNextToken("]");
		}

		private function filterOnRegExp(propertyName : String, regExpString : String, isBinding : Boolean) : void
		{
			var numNodes	: int		= _selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node		: ISceneNode	= _selection[i];
				var nodeValue 	: String		= null;

				if (isBinding && (node['bindings'] is DataBindings))
					nodeValue = String((node['bindings'] as DataBindings).getProperty(propertyName));
				else
					nodeValue = String(getValueObject(node, propertyName));

				var matches		: Array			= nodeValue.match(regExpString);

				if (!matches || matches.length == 0)
					removeFromSelection(i);
			}
		}

		private function filterOnValue(propertyName : String, value : Object, isBinding : Boolean) : void
		{
			var numNodes	: int	= _selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node		: ISceneNode	= _selection[i];
				var nodeValue 	: Object 		= null;

				if (isBinding && (node['bindings'] is DataBindings))
					nodeValue = (node['bindings'] as DataBindings).getProperty(propertyName);
				else
					nodeValue = getValueObject(node, propertyName);

				if (nodeValue != value)
					removeFromSelection(i);
			}
		}

		private function filterOnController(controllerName : Object) : Object
		{
			var numNodes	: int		= _selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node			: ISceneNode	= _selection[i];
				var numControllers	: uint			= node.numControllers;
				var keepSceneNode	: Boolean		= false;

				for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
				{
					var controllerType : String = getQualifiedClassName(node.getController(controllerId));
					controllerType = controllerType.substr(controllerType.lastIndexOf(':') + 1);

					if (controllerType == controllerName)
					{
						keepSceneNode = true;
						break;
					}
				}

				if (!keepSceneNode)
					removeFromSelection(i);
			}

			return null;
		}

		private function getValueObject(source : Object, modifier : String) : Object
		{
			if (modifier)
			{
				var tokens : Array = modifier.split(".");

				for each (var token : String in tokens)
					source = source[token];
			}

			return source;
		}

		private function removeFromSelection(index : int) : void
		{
			var numNodes : int = _selection.length;

			--numNodes;
			_selection[index] = _selection[numNodes];
			_selection.length = numNodes;
		}

		private function checkNextToken(expected : String) : void
		{
			var token : String = getToken(true);

			if (token != expected)
				throwParseError(expected, token);
		}

		private function throwParseError(expected	: String,
										 got		: String) : void
		{
			throw new Error(
				"Parse error: expected '" + expected
				+ "', got '" + got + "'."
			);
		}
	}
}

package aerys.minko.scene
{
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.binding.DataBindings;

	import avmplus.getQualifiedClassName;

	import flash.utils.Dictionary;
	import flash.utils.Proxy;
	import flash.utils.describeType;
	import flash.utils.flash_proxy;
	import flash.utils.getQualifiedClassName;

	public dynamic class SceneIterator extends Proxy
	{
		private static const TYPE_CACHE	: Dictionary		= new Dictionary(true);
		private static const OPERATORS	: Vector.<String>	= new <String>[
			"//", "/", "[", "]", "..", ".", "~=", "?=", "=", "@", "*", "(", ")"
		];
		private static const REGEX_TRIM	: RegExp			= /^\s+|\s+$/;

		private var _path		: String				= null;
		private var _selection	: Vector.<ISceneNode>	= null;
		private var _modifier	: String				= null;

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

		public function SceneIterator(path 		: String,
									  selection : Vector.<ISceneNode>,
									  modifier	: String	= "")
		{
			super();

			_modifier = modifier;

			initialize(path, selection);
		}

		public function toString() : String
		{
			return _selection.toString();
		}

		override flash_proxy function setProperty(name : *, value : *):void
		{
			var propertyName : String = name;

			for each (var node : ISceneNode in _selection)
				getValueObject(node, _modifier)[propertyName] = value;
		}

		override flash_proxy function getProperty(name : *) : *
		{
			var index : int = parseInt(name);

			if (index == name)
				return index < _selection.length ? _selection[index] : null;
			else
			{
				throw new Error(
					'Unable to get a property on a set of objects. '
					+ 'You must use the [] operator to fetch one of the objects.'
				);
			}
		}

		override flash_proxy function nextNameIndex(index : int) : int
		{
			return index < _selection.length ? index + 1 : 0;
		}

		override flash_proxy function nextName(index : int) : String
		{
			return String(index - 1);
		}

		override flash_proxy function nextValue(index : int) : *
		{
			return _selection[int(index - 1)];
		}

		override flash_proxy function callProperty(name:*, ...parameters):*
		{
			var methodName : String = name;

			for each (var node : ISceneNode in _selection)
			{
				var method : Function = getValueObject(node, _modifier)[methodName];

				method.apply(null, parameters);
			}

			return this;
		}

		private function initialize(path : String, selection : Vector.<ISceneNode>) : void
		{
			_path = path;

			// update root
			var token : String = getToken();

			_selection = selection.slice();
			if (token == "/")
			{
				selectRoots();
				nextToken(token);
			}

			// parse
			while (token = getToken())
			{
				switch (token)
				{
					case '//' :
						nextToken(token);
						selectDescendants();
						break ;
					case '/' :
						nextToken(token);
						selectChildren();
						break ;
					default :
						nextToken(token);
						parseNodeType(token);
						break ;
				}
			}
		}

		private function getToken(doNext : Boolean = false) : String
		{
			var token	: String	= null;

			if (!_path)
				return null;

			_path = _path.replace(/^\s+/, '');

			var nextOpIndex : int = int.MAX_VALUE;

			for each (var op : String in OPERATORS)
			{
				var opIndex : int = _path.indexOf(op);

				if (opIndex > 0 && opIndex < nextOpIndex)
					nextOpIndex = opIndex;

				if (opIndex == 0)
				{
					token = op;
					break ;
				}
			}

			if (!token)
				token = _path.substring(0, nextOpIndex);

			if (doNext)
				nextToken(token);

			return token;
		}

		private function getValueToken() : Object
		{
			var value : Object	= null;

			_path = _path.replace(/^\s+/, '');

			if (_path.charAt(0) == "'")
			{
				var endOfStringIndex : int = _path.indexOf("'", 1);

				if (endOfStringIndex < 0)
					throw new Error("Unterminated string expression.");

				var stringValue	: String	=  _path.substring(1, endOfStringIndex);

				_path = _path.substring(endOfStringIndex + 1);

				value = stringValue;
			}
			else
			{
				var token : String	= getToken(true);

				if (token == "true")
					value = true;
				else if (token == "false")
					value = false;
				else if (token.indexOf("0x") == 0)
					value = parseInt(token, 16);
			}

			return value;
		}

		private function nextToken(token : String) : void
		{
			_path = _path.substring(_path.indexOf(token) + token.length);
		}

		private function selectChildren(typeName : String = null) : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			if (typeName != null)
				typeName = typeName.toLowerCase();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node is Group)
				{
					var group 		: Group = node as Group;
					var numChildren : uint 	= group.numChildren;

					for (var i : uint = 0; i < numChildren; ++i)
					{
						var child 		: ISceneNode 	= group.getChildAt(i);
						var className	: String		= getQualifiedClassName(child)
						var childType 	: String 		= className.substr(className.lastIndexOf(':') + 1);

						if (typeName == null || childType.toLowerCase() == typeName)
							_selection.push(child);
					}
				}
			}
		}

		private function selectRoots() : void
		{
			var selection	: Vector.<ISceneNode>	= _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
				if (_selection.indexOf(node.root) < 0)
					_selection.push(node);
		}

		private function selectDescendants() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				_selection.push(node);
				if (node is Group)
					(node as Group).getDescendantsByType(ISceneNode, _selection);
			}
		}

		private function selectParents() : void
		{
			var selection : Vector.<ISceneNode> = _selection.slice();

			_selection.length = 0;
			for each (var node : ISceneNode in selection)
			{
				if (node.parent)
					_selection.push(node.parent);
				else
					_selection.push(node);
			}
		}

		private function parseNodeType(nodeType : String) : void
		{
			if (nodeType == '.')
			{
				// nothing
			}
			if (nodeType == '..')
				selectParents();
			else if (nodeType == '*')
				selectChildren();
			else
				selectChildren(nodeType);

			// apply predicates
			var token : String = getToken();

			while (token == "[")
			{
				nextToken(token);
				parsePredicate();

				token = getToken();
			}
		}

		private function parsePredicate() : void
		{
			var propertyName	: String	= getToken(true);
			var isBinding		: Boolean	= propertyName == '@';

			if (isBinding)
				propertyName = getToken(true);

			var index	: int	= parseInt(propertyName);

			if (index.toString() == propertyName)
			{
				if (index < _selection.length)
				{
					_selection[0] = _selection[index];
					_selection.length = 1;
				}
				else
					_selection.length = 0;
			}
			else if (propertyName == 'hasController')
			{
				checkNextToken('(');
				filterOnController(getToken(true));
				checkNextToken(')');
			}
			else
			{
				var operator	: String	= getToken(true);
				var value		: Object	= getValueToken();

				if (operator == "~=")
					filterOnRegExp(propertyName, String(value), isBinding);
				else
					filterOnValue(propertyName, value, isBinding);
			}

			checkNextToken("]");
		}

		private function filterOnRegExp(propertyName : String, regExpString : String, isBinding : Boolean) : void
		{
			var numNodes	: int		= _selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node		: ISceneNode	= _selection[i];
				var nodeValue 	: String		= null;

				if (isBinding && (node['bindings'] is DataBindings))
					nodeValue = String((node['bindings'] as DataBindings).getProperty(propertyName));
				else
					nodeValue = String(getValueObject(node, propertyName));

				var matches		: Array			= nodeValue.match(regExpString);

				if (!matches || matches.length == 0)
					removeFromSelection(i);
			}
		}

		private function filterOnValue(propertyName : String, value : Object, isBinding : Boolean) : void
		{
			var numNodes	: int	= _selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node		: ISceneNode	= _selection[i];
				var nodeValue 	: Object 		= null;

				if (isBinding && (node['bindings'] is DataBindings))
					nodeValue = (node['bindings'] as DataBindings).getProperty(propertyName);
				else
					nodeValue = getValueObject(node, propertyName);

				if (nodeValue != value)
					removeFromSelection(i);
			}
		}

		private function filterOnController(controllerName : Object) : Object
		{
			var numNodes	: int		= _selection.length;

			for (var i : int = numNodes - 1; i >= 0; --i)
			{
				var node			: ISceneNode	= _selection[i];
				var numControllers	: uint			= node.numControllers;
				var keepSceneNode	: Boolean		= false;

				for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
				{
					var controllerType : String = getQualifiedClassName(node.getController(controllerId));
					controllerType = controllerType.substr(controllerType.lastIndexOf(':') + 1);

					if (controllerType == controllerName)
					{
						keepSceneNode = true;
						break;
					}
				}

				if (!keepSceneNode)
					removeFromSelection(i);
			}

			return null;
		}

		private function getValueObject(source : Object, modifier : String) : Object
		{
			if (modifier)
			{
				var tokens : Array = modifier.split(".");

				for each (var token : String in tokens)
					source = source[token];
			}

			return source;
		}

		private function removeFromSelection(index : int) : void
		{
			var numNodes : int = _selection.length;

			--numNodes;
			_selection[index] = _selection[numNodes];
			_selection.length = numNodes;
		}

		private function checkNextToken(expected : String) : void
		{
			var token : String = getToken(true);

			if (token != expected)
				throwParseError(expected, token);
		}

		private function throwParseError(expected	: String,
										 got		: String) : void
		{
			throw new Error(
				"Parse error: expected '" + expected
				+ "', got '" + got + "'."
			);
		}
	}
}

add XPath special function 'hasController()' instead of weird ?= operator


m4_pattern_allow([^URBI_(PATH|SERVER)$])         -*- shell-script -*-

AS_INIT()dnl
URBI_PREPARE()

set -e

case $VERBOSE in
  x) set -x;;
esac

# Avoid zombies and preserve debugging information.
cleanup ()
{
  exit_status=$?

  # In case we were caught by set -e, kill the children.
  children_kill
  children_harvest
  children_report

  children_sta=$(children_status remote)
  # Don't clean before calling children_status...
  test x$VERBOSE != x || children_clean

  case $exit_status:$children_sta in
    0:0) ;;
    0:*) # Maybe a children exited for SKIP etc.
	 exit $children_sta;;
    *:*) # If liburbi-check failed, there is a big problem.
	 error SOFTWARE "liburbi-check itself failed with $exit_status";;
  esac
  # rst_expect sets exit=false if it saw a failure.
  $exit
}
for signal in 1 2 13 15; do
  trap 'error $((128 + $signal)) \
	      "received signal $signal ($(kill -l $signal))"' $signal
done
trap cleanup 0

# Overriden to include the test name.
stderr ()
{
  echo >&2 "$(basename $0): $me: $@"
  echo >&2
}

# Sleep some time, but taking into account the fact that
# instrumentation slows down a lot.
my_sleep ()
{
  if $INSTRUMENT; then
    sleep $(($1 * 5))
  else
    sleep $1
  fi
}

## -------------- ##
## Main program.  ##
## -------------- ##

exec 3>&2

: ${abs_builddir='@abs_builddir@'}
check_dir abs_builddir liburbi-check
: ${abs_top_builddir='@abs_top_builddir@'}
check_dir abs_top_builddir config.status
: ${abs_top_srcdir='@abs_top_srcdir@'}
check_dir abs_top_srcdir configure.ac

# Make it absolute.
chk=$(absolute "$1")
if test ! -f "$chk.cc"; then
  fatal "no such file: $chk.cc"
fi

period=32
# ./../../../tests/2.x/andexp-pipeexp.chk -> 2.x
medir=$(basename $(dirname "$chk"))
# ./../../../tests/2.x/andexp-pipeexp.chk -> 2.x/andexp-pipeexp
me=$medir/$(basename "$chk" ".cc")
# ./../../../tests/2.x/andexp-pipeexp.chk -> andexp-pipeexp
meraw=$(basename $me)    # MERAW!

srcdir=$(absolute $srcdir)
export srcdir
# Move to a private dir.
rm -rf $me.dir
mkdir -p $me.dir
cd $me.dir

# Help debugging
set | rst_pre "$me variables"

# $URBI_SERVER.
#
# If this SDK-Remote is part of the Kernel package, then we should not
# use an installed urbi-console, but rather the one which is part of
# this package.
if test -d $abs_top_srcdir/../../src/kernel; then
  stderr "This SDK-Remote is part of a kernel package."
  URBI_SERVER=$abs_top_builddir/../src/urbi-console
  export URBI_PATH=$abs_top_srcdir/../share
else
  stderr "This SDK-Remote is standalone."
fi
# Leaves trailing files, so run it in subdir.
find_urbi_server

# Compute expected output.
sed -n -e 's@//= @@p' $chk.cc >output.exp
touch error.exp
echo 0 >status.exp

# Start it.
valgrind=$(instrument "server.val")
cmd="$valgrind $URBI_SERVER --port 0 -w server.port --period $period"
echo "$cmd" >server.cmd
$cmd >server.out 2>server.err &
children_register server

my_sleep 2

# Start the test.
valgrind=$(instrument "remote.val")
cmd="$valgrind ../../tests localhost $(cat server.port) $meraw"
echo "$cmd" >remote.cmd
$cmd >remote.out.raw 2>remote.err &
children_register remote

# Let some time to run the tests.
children_wait 10

# Ignore the "client errors".
sed -e '/^E client error/d' remote.out.raw >remote.out.eff
# Compare expected output with actual output.
rst_expect output remote.out
rst_pre "Error output" remote.err

# Display Valgrind report.
rst_pre "Valgrind" remote.val

# Exit with success: liburbi-check made its job.  But now clean (on
# trap 0) will check $exit to see if there is a failure in the tests
# and adjust the exit status accordingly.
exit 0

m4_pattern_allow([^URBI_(PATH|SERVER)$])         -*- shell-script -*-

AS_INIT()dnl
URBI_PREPARE()

set -e

case $VERBOSE in
  x) set -x;;
esac

# Avoid zombies and preserve debugging information.
cleanup ()
{
  exit_status=$?

  # In case we were caught by set -e, kill the children.
  children_kill
  children_harvest
  children_report

  children_sta=$(children_status remote)
  # Don't clean before calling children_status...
  test x$VERBOSE != x || children_clean

  case $exit_status:$children_sta in
    0:0) ;;
    0:*) # Maybe a children exited for SKIP etc.
	 exit $children_sta;;
    *:*) # If liburbi-check failed, there is a big problem.
	 error SOFTWARE "liburbi-check itself failed with $exit_status";;
  esac
  # rst_expect sets exit=false if it saw a failure.
  $exit
}
for signal in 1 2 13 15; do
  trap 'error $((128 + $signal)) \
	      "received signal $signal ($(kill -l $signal))"' $signal
done
trap cleanup 0

# Overriden to include the test name.
stderr ()
{
  echo >&2 "$(basename $0): $me: $@"
  echo >&2
}

# Sleep some time, but taking into account the fact that
# instrumentation slows down a lot.
my_sleep ()
{
  if $INSTRUMENT; then
    sleep $(($1 * 5))
  else
    sleep $1
  fi
}

## -------------- ##
## Main program.  ##
## -------------- ##

exec 3>&2

: ${abs_builddir='@abs_builddir@'}
check_dir abs_builddir liburbi-check
: ${abs_top_builddir='@abs_top_builddir@'}
check_dir abs_top_builddir config.status
: ${abs_top_srcdir='@abs_top_srcdir@'}
check_dir abs_top_srcdir configure.ac

# Make it absolute.
chk=$(absolute "$1")
if test ! -f "$chk.cc"; then
  fatal "no such file: $chk.cc"
fi

period=32
# ./../../../tests/2.x/andexp-pipeexp.chk -> 2.x
medir=$(basename $(dirname "$chk"))
# ./../../../tests/2.x/andexp-pipeexp.chk -> 2.x/andexp-pipeexp
me=$medir/$(basename "$chk" ".cc")
# ./../../../tests/2.x/andexp-pipeexp.chk -> andexp-pipeexp
meraw=$(basename $me)    # MERAW!

srcdir=$(absolute $srcdir)
export srcdir
# Move to a private dir.
rm -rf $me.dir
mkdir -p $me.dir
cd $me.dir

# Help debugging
set | rst_pre "$me variables"

# $URBI_SERVER.
#
# If this SDK-Remote is part of the Kernel package, then we should not
# use an installed urbi-console, but rather the one which is part of
# this package.
if test -d $abs_top_srcdir/../../src/kernel; then
  stderr "This SDK-Remote is part of a kernel package" \
         "  ($abs_top_srcdir/../../src/kernel exists)."
  URBI_SERVER=$abs_top_builddir/../src/urbi-console
  export URBI_PATH=$abs_top_srcdir/../share
else
  stderr "This SDK-Remote is standalone."
fi
# Leaves trailing files, so run it in subdir.
find_urbi_server

# Compute expected output.
sed -n -e 's@//= @@p' $chk.cc >output.exp
touch error.exp
echo 0 >status.exp

# Start it.
valgrind=$(instrument "server.val")
cmd="$valgrind $URBI_SERVER --port 0 -w server.port --period $period"
echo "$cmd" >server.cmd
$cmd >server.out 2>server.err &
children_register server

my_sleep 2

# Start the test.
valgrind=$(instrument "remote.val")
cmd="$valgrind ../../tests localhost $(cat server.port) $meraw"
echo "$cmd" >remote.cmd
$cmd >remote.out.raw 2>remote.err &
children_register remote

# Let some time to run the tests.
children_wait 10

# Ignore the "client errors".
sed -e '/^E client error/d' remote.out.raw >remote.out.eff
# Compare expected output with actual output.
rst_expect output remote.out
rst_pre "Error output" remote.err

# Display Valgrind report.
rst_pre "Valgrind" remote.val

# Exit with success: liburbi-check made its job.  But now clean (on
# trap 0) will check $exit to see if there is a failure in the tests
# and adjust the exit status accordingly.
exit 0

Improve a log message.

Improve a log message.


package stutter
{
	import org.flexunit.assertThat;
	import org.hamcrest.collection.array;
	import org.hamcrest.object.equalTo;
	import org.hamcrest.object.isFalse;
	import org.hamcrest.object.isTrue;

	public class StutterRunTimeTest
	{
		public var l:StutterRunTime;

		[Before]
		public function setup():void
		{
			l = new StutterRunTime();
		}

		[Test]
		public function label():void
		{
			// (label a 42)
			l.eval([ S('label'), S('a'), 42 ]);

			// a
			// => 42
			assertThat(l.eval(S('a')), equalTo(42))
		}

		[Test]
		public function eq():void
		{
			l.eval([ S('label'), S('a'), 42 ]);

			// (eq 42 a)
			// => true
			assertThat(l.eval([ S('eq'), 42, S('a')]), isTrue());
		}

		[Test]
		public function quote():void
		{
			// `(1 2)
			// => [1, 2]
			assertThat(l.eval([ S('quote'), [ 1, 2 ] ]), array(1, 2));
		}

		[Test]
		public function car():void
		{
			// (car `(1 2))
			// => 1
			assertThat(l.eval([ S('car'), [ S('quote'), [ 1, 2 ] ]]), equalTo(1));
		}

		[Test]
		public function cdr():void
		{
			// (cdr `(1 2))
			// => [2]
			assertThat(l.eval([ S('cdr'), [ S('quote'), [ 1, 2 ] ]]), array(2));
		}

		[Test]
		public function cons():void
		{
			// (cons 1 `(2 3))
			// => [1, 2, 3]
			assertThat(l.eval([ S('cons'), 1, [ S('quote'), [ 2, 3 ] ]]), array(1, 2, 3));
		}

		[Test]
		public function if_():void
		{
			// (if (eq 1 2) 42 43)
			// => 43
			assertThat(l.eval([ S('if'), [ S('eq'), 1, 2 ], 42, 43 ]), equalTo(43));
		}

		[Test]
		public function atom():void
		{
			l.eval([ S('label'), S('a'), 42 ]);

			// (atom a)
			// => true
			assertThat(l.eval([ S('atom'), [ S('a') ]]), isTrue());

			// (atom 123)
			// => true
			assertThat(l.eval([ S('atom'), [ 123 ]]), isTrue());

			// (atom `(1))
			// => true
			assertThat(l.eval([ S('atom'), [ S('quote'), [ 1 ] ]]), isFalse());

			// (atom `(1 2))
			// => false
			assertThat(l.eval([ S('atom'), [ S('quote'), [ 1, 2 ]]]), isFalse());
		}

		[Test]
		public function lambda():void
		{
			// (label second `(lambda (x) (car (cdr x))))
			l.eval([ S('label'), S('second'), [ S('quote'), [ S('lambda'), [ S('x') ], [ S('car'), [ S('cdr'), S('x')]]]]]);

			// (second `(1 2 3))
			assertThat(l.eval([ S('second'), [ S('quote'), [ 1, 2, 3 ]]]), equalTo(2));
		}
	}
}

package stutter
{
	import org.flexunit.assertThat;
	import org.hamcrest.collection.array;
	import org.hamcrest.object.equalTo;
	import org.hamcrest.object.isFalse;
	import org.hamcrest.object.isTrue;

	public class StutterRunTimeTest
	{
		public var l:StutterRunTime;

		[Before]
		public function setup():void
		{
			l = new StutterRunTime();
		}

		[Test]
		public function label():void
		{
			// (label a 42)
			l.eval([ S('label'), S('a'), 42 ]);

			// a
			// => 42
			assertThat(l.eval(S('a')), equalTo(42))
		}

		[Test]
		public function eq():void
		{
			l.eval([ S('label'), S('a'), 42 ]);

			// (eq 42 a)
			// => true
			assertThat(l.eval([ S('eq'), 42, S('a')]), isTrue());
		}

		[Test]
		public function quote():void
		{
			// `(1 2)
			// => [1, 2]
			assertThat(l.eval([ S('quote'), [ 1, 2 ] ]), array(1, 2));
		}

		[Test]
		public function car():void
		{
			// (car `(1 2))
			// => 1
			assertThat(l.eval([ S('car'), [ S('quote'), [ 1, 2 ] ]]), equalTo(1));
		}

		[Test]
		public function cdr():void
		{
			// (cdr `(1 2))
			// => [2]
			assertThat(l.eval([ S('cdr'), [ S('quote'), [ 1, 2 ] ]]), array(2));
		}

		[Test]
		public function cons():void
		{
			// (cons 1 `(2 3))
			// => [1, 2, 3]
			assertThat(l.eval([ S('cons'), 1, [ S('quote'), [ 2, 3 ] ]]), array(1, 2, 3));
		}

		[Test]
		public function if_():void
		{
			// (if (eq 1 2) 42 43)
			// => 43
			assertThat(l.eval([ S('if'), [ S('eq'), 1, 2 ], 42, 43 ]), equalTo(43));
		}

		[Test]
		public function atom():void
		{
			l.eval([ S('label'), S('a'), 42 ]);

			// (atom a)
			// => true
			assertThat(l.eval([ S('atom'), [ S('a') ]]), isTrue());

			// (atom 123)
			// => true
			assertThat(l.eval([ S('atom'), [ 123 ]]), isTrue());

			// (atom `(1))
			// => true
			assertThat(l.eval([ S('atom'), [ S('quote'), [ 1 ] ]]), isFalse());

			// (atom `(1 2))
			// => false
			assertThat(l.eval([ S('atom'), [ S('quote'), [ 1, 2 ]]]), isFalse());
		}

		[Test]
		public function lambda():void
		{
			// (label second '(lambda (x) (car (cdr x))))
			l.eval([ S('label'), S('second'), [ S('quote'), [ S('lambda'), [ S('x') ], [ S('car'), [ S('cdr'), S('x')]]]]]);

			// (second '(1 2 3))
			assertThat(l.eval([ S('second'), [ S('quote'), [ 1, 2, 3 ]]]), equalTo(2));
		}
	}
}

correct syntax in examples

correct syntax in examples


package com.dotfold.dotvimstat.view.summary
{
	import asx.string.empty;

	import com.dotfold.dotvimstat.net.service.VimeoBasicService;
	import com.dotfold.dotvimstat.view.IView;
	import com.greensock.TweenLite;

	import modena.core.Element;
	import modena.core.ElementContent;
	import modena.ui.Label;

	/**
	 * VideosSummary view shows the count of videos retrieved from the Vimeo API.
	 *
	 * @author jamesmcnamee
	 *
	 */
	public class VideosSummary extends Element implements IView
	{
		public static const ELEMENT_NAME:String = "videosSummary";

		private var _label:Label;

		/**
		 * Constructor.
		 */
		public function VideosSummary(styleClass:String = null)
		{
			super(styleClass);
		}

		/**
		 * @inheritDoc
		 */
		override protected function createElementContent():ElementContent
		{
			var content:ElementContent = super.createElementContent();

			_label = new Label("summaryCount");
			content.addChild(_label);
			_label.alpha = 0;

			return content;
		}

		/**
		 * Update the label with count of videos.
		 */
		public function set videosCount(value:int):void
		{
			_label.text = value.toString();

			TweenLite.to(_label, 0.4, { alpha: 1, ease: 'easeInQuad' });
			invalidateRender();
		}

		override public function validateRender():void
		{
			super.validateRender();

			_label.x = (width - _label.width) / 2;
			_label.y = (height - _label.height) / 2;
		}

	}
}

package com.dotfold.dotvimstat.view.summary
{
	import com.dotfold.dotvimstat.view.IView;
	import com.dotfold.dotvimstat.view.animation.ElementAnimationSequencer;

	import modena.core.ElementContent;
	import modena.ui.HBox;
	import modena.ui.Label;

	/**
	 * VideosSummary view shows the count of videos retrieved from the Vimeo API.
	 *
	 * @author jamesmcnamee
	 *
	 */
	public class VideosSummary extends HBox implements IView
	{
		public static const ELEMENT_NAME:String = "videosSummary";

		private var _numVideos:Label;
		private var _staticText:Label;

		[Inject]
		public var animationQueue:ElementAnimationSequencer;

		/**
		 * Constructor.
		 */
		public function VideosSummary(styleClass:String = null)
		{
			super(styleClass);
		}

		/**
		 * @inheritDoc
		 */
		override protected function createElementContent():ElementContent
		{
			var content:ElementContent = super.createElementContent();

			_numVideos = new Label("summaryDetail");
			_numVideos.alpha = 0;

			_staticText = new Label("summarySmall");
			_staticText.text = "total videos";
			_staticText.alpha = 0;

			content.addChild(_numVideos);
			content.addChild(_staticText);

			return content;
		}

		/**
		 * Called from Mediator once data load is complete.
		 */
		public function show():void
		{
			runAnimationQueue();
		}

		/**
		 * Update the num videos label.
		 */
		public function set videosCount(value:int):void
		{
			_numVideos.text = value.toString();

			invalidateRender();
		}

		/**
		 * Add elements to the animation queue and start.
		 */
		private function runAnimationQueue():void
		{
			animationQueue
				.addElement(_numVideos)
				.addElement(_staticText)
				.play();
		}
	}
}

update video summary view

[feat] update video summary view

- use animation queue
- cleanup text handling


package aerys.minko.render.shader.part.phong.contribution
{
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.material.phong.PhongProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.part.phong.LightAwareShaderPart;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerFormat;

	/**
	 * Methods in this class allow to compute light contributions.
	 * It is extended by InfiniteShaderPart for directional lights, and LocalizedShaderPart for point and spot lights.
	 *
	 * All methods are available in tangent, local and world space.
	 *
	 * - Computing in tangent space is computationally more expensive, both in CPU and GPU, because we need to feed
	 * more data to the shaders than the other methods but it allows to do both bump and parallax mapping.
	 *
	 * - Doing it in local space is computationally more expensive on the CPU, because the light
	 * position will need to be converted in the CPU for each mesh and at each frame will produce the lighter shaders.
	 *
	 * Doing as much work on the CPU as possible, and have as short a possible shader would be the way to go
	 * if adobe's compiler was better than it is. Therefor, minko defaults to computing everything in world space
	 * which is faster in practical tests.
	 *
	 * The cost of each light will be in O(number of drawcalls) on the CPU.
	 * However, this is the way that will lead to the smaller shaders, and is therefor recomended when used
	 * in small scenes with many lights.
	 *
	 * - Computing lights in world space is cheaper on the CPU, because no extra computation is
	 * needed for each mesh, but will cost one extra matrix multiplication on the GPU (we need the world position
	 * or every fragment).
	 *
	 * @author Romain Gilliotte
	 *
	 */
	public class AbstractContributionShaderPart extends LightAwareShaderPart
	{

		public function AbstractContributionShaderPart(main : Shader)
		{
			super(main);
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computations will be done in tangent space.
		 *
		 * @param lightId The id of the localized light (PointLight or SpotLight)
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInTangentSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computation will be done in local space.
		 *
		 * @param lightId The id of the localized light (PointLight or SpotLight)
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInLocalSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computation will be done in world space.
		 *
		 * @param lightId The id of the light
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInWorldSpace(lightId : uint, normal : SFloat) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in tangent space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInTangentSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in local space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInLocalSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in world space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInWorldSpace(lightId : uint, normal : SFloat) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Compute final diffuse value from light direction and normal.
		 * Both requested vector can be in any space (tangent, local, light, view or whatever) but must be in the same space.
		 * Also they must be recheable in the fragment shader (they must be constant, or already interpolated)
		 *
		 * @param lightId
		 * @param fsLightDirection
		 * @param fsNormal
		 * @return
		 */
		protected function diffuseFromVectors(lightId			: uint,
											  fsLightDirection	: SFloat,
											  fsNormal			: SFloat) : SFloat
		{
			var fsLambertProduct	: SFloat = saturate(negate(dotProduct3(fsLightDirection, fsNormal)));
			var cDiffuse			: SFloat = getLightParameter(lightId, 'diffuse', 1);

			if (meshBindings.propertyExists(PhongProperties.DIFFUSE_MULTIPLIER))
				cDiffuse.scaleBy(meshBindings.getParameter(PhongProperties.DIFFUSE_MULTIPLIER, 1));

			return multiply(cDiffuse, fsLambertProduct);
		}

		/**
		 * Compute final specular value from light direction, normal, and camera direction.
		 * All three requested vector can be in any space (tangent, local, light, view or whatever) but must all be in the same sapce.
		 * Also they must be recheable in the fragment shader (they must be constant, or already interpolated)
		 *
		 * @param lightId
		 * @param fsLightDirection
		 * @param fsNormal
		 * @param fsCameraDirection
		 * @return
		 */
		protected function specularFromVectors(lightId				: uint,
											   fsLightDirection		: SFloat,
											   fsNormal				: SFloat,
											   fsCameraDirection	: SFloat) : SFloat
		{
			var fsLightReflectedDirection	: SFloat = reflect(fsLightDirection, fsNormal);
			var fsLambertProduct			: SFloat = saturate(negate(dotProduct3(fsLightReflectedDirection, fsCameraDirection)));

			var cLightSpecular	: SFloat 	= getLightParameter(lightId, 'specular', 1);
			var cLightShininess	: SFloat 	= getLightParameter(lightId, 'shininess', 1);

			if (meshBindings.propertyExists(PhongProperties.SPECULAR))
			{
				var specular	: SFloat	= meshBindings.getParameter(PhongProperties.SPECULAR, 4);

				cLightSpecular = multiply(cLightSpecular, specular.xyz);
			}

			if (meshBindings.propertyExists(PhongProperties.SPECULAR_MAP))
			{
				var fsSpecularSample 	: SFloat	= sampleTexture(
					meshBindings.getTextureParameter(
						PhongProperties.SPECULAR_MAP,
						1,
						0,
						1,
						0,
						meshBindings.getProperty(PhongProperties.SPECULAR_MAP_FORMAT, SamplerFormat.RGBA)
					),
					fsUV
				);

				cLightSpecular.scaleBy(fsSpecularSample.x);
			}

			if (meshBindings.propertyExists(PhongProperties.SHININESS))
				cLightShininess.scaleBy(meshBindings.getParameter(PhongProperties.SHININESS, 1));

			return multiply(cLightSpecular, power(fsLambertProduct, cLightShininess));
		}
	}
}

package aerys.minko.render.shader.part.phong.contribution
{
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.material.phong.PhongProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.part.phong.LightAwareShaderPart;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerFormat;

	/**
	 * Methods in this class allow to compute light contributions.
	 * It is extended by InfiniteShaderPart for directional lights, and LocalizedShaderPart for point and spot lights.
	 *
	 * All methods are available in tangent, local and world space.
	 *
	 * - Computing in tangent space is computationally more expensive, both in CPU and GPU, because we need to feed
	 * more data to the shaders than the other methods but it allows to do both bump and parallax mapping.
	 *
	 * - Doing it in local space is computationally more expensive on the CPU, because the light
	 * position will need to be converted in the CPU for each mesh and at each frame will produce the lighter shaders.
	 *
	 * Doing as much work on the CPU as possible, and have as short a possible shader would be the way to go
	 * if adobe's compiler was better than it is. Therefor, minko defaults to computing everything in world space
	 * which is faster in practical tests.
	 *
	 * The cost of each light will be in O(number of drawcalls) on the CPU.
	 * However, this is the way that will lead to the smaller shaders, and is therefor recomended when used
	 * in small scenes with many lights.
	 *
	 * - Computing lights in world space is cheaper on the CPU, because no extra computation is
	 * needed for each mesh, but will cost one extra matrix multiplication on the GPU (we need the world position
	 * or every fragment).
	 *
	 * @author Romain Gilliotte
	 *
	 */
	public class AbstractContributionShaderPart extends LightAwareShaderPart
	{

		public function AbstractContributionShaderPart(main : Shader)
		{
			super(main);
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computations will be done in tangent space.
		 *
		 * @param lightId The id of the localized light (PointLight or SpotLight)
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInTangentSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computation will be done in local space.
		 *
		 * @param lightId The id of the localized light (PointLight or SpotLight)
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInLocalSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the diffuse value of a given light.
		 * Computation will be done in world space.
		 *
		 * @param lightId The id of the light
		 * @return Shader subgraph representing the diffuse value of this light.
		 */
		public function computeDiffuseInWorldSpace(lightId : uint, normal : SFloat) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in tangent space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInTangentSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in local space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInLocalSpace(lightId : uint) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Creates the shader subgraph to compute the specular value of a given light.
		 * Computation will be done in world space.
		 *
		 * @param lightId
		 * @return
		 */
		public function computeSpecularInWorldSpace(lightId : uint, normal : SFloat) : SFloat
		{
			throw new Error('Must be overriden');
		}

		/**
		 * Compute final diffuse value from light direction and normal.
		 * Both requested vector can be in any space (tangent, local, light, view or whatever) but must be in the same space.
		 * Also they must be recheable in the fragment shader (they must be constant, or already interpolated)
		 *
		 * @param lightId
		 * @param fsLightDirection
		 * @param fsNormal
		 * @return
		 */
		protected function diffuseFromVectors(lightId			: uint,
											  fsLightDirection	: SFloat,
											  fsNormal			: SFloat) : SFloat
		{
			var fsLambertProduct	: SFloat = saturate(negate(dotProduct3(fsLightDirection, fsNormal)));
			var cDiffuse			: SFloat = getLightParameter(lightId, 'diffuse', 1);

			if (meshBindings.propertyExists(PhongProperties.DIFFUSE_MULTIPLIER))
				cDiffuse.scaleBy(meshBindings.getParameter(PhongProperties.DIFFUSE_MULTIPLIER, 1));

			return multiply(cDiffuse, fsLambertProduct);
		}

		/**
		 * Compute final specular value from light direction, normal, and camera direction.
		 * All three requested vector can be in any space (tangent, local, light, view or whatever) but must all be in the same sapce.
		 * Also they must be recheable in the fragment shader (they must be constant, or already interpolated)
		 *
		 * @param lightId
		 * @param fsLightDirection
		 * @param fsNormal
		 * @param fsCameraDirection
		 * @return
		 */
		protected function specularFromVectors(lightId				: uint,
											   fsLightDirection		: SFloat,
											   fsNormal				: SFloat,
											   fsCameraDirection	: SFloat) : SFloat
		{
			var fsLightReflectedDirection	: SFloat = reflect(fsLightDirection, fsNormal);
			var fsLambertProduct			: SFloat = saturate(negate(dotProduct3(fsLightReflectedDirection, fsCameraDirection)));

			var cLightSpecular	: SFloat 	= getLightParameter(lightId, 'specular', 1);
			var cLightShininess	: SFloat 	= getLightParameter(lightId, 'shininess', 1);

			if (meshBindings.propertyExists(PhongProperties.SPECULAR) && !meshBindings.propertyExists(PhongProperties.SPECULAR_MAP))
			{
				var specular	: SFloat	= meshBindings.getParameter(PhongProperties.SPECULAR, 4);

				cLightSpecular = multiply(cLightSpecular, specular.xyz);
			}

			if (meshBindings.propertyExists(PhongProperties.SPECULAR_MAP))
			{
				var fsSpecularSample 	: SFloat	= sampleTexture(
					meshBindings.getTextureParameter(
						PhongProperties.SPECULAR_MAP,
						1,
						0,
						1,
						0,
						meshBindings.getProperty(PhongProperties.SPECULAR_MAP_FORMAT, SamplerFormat.RGBA)
					),
					fsUV
				);

				cLightSpecular.scaleBy(fsSpecularSample.x);
			}

			if (meshBindings.propertyExists(PhongProperties.SHININESS))
				cLightShininess.scaleBy(meshBindings.getParameter(PhongProperties.SHININESS, 1));

			return multiply(cLightSpecular, power(fsLambertProduct, cLightShininess));
		}
	}
}

Remove specular color instruction when a specular map is available