package aerys.minko.render.shader.sprite
{
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.ShaderSettings;
	import aerys.minko.render.shader.part.DiffuseShaderPart;
	import aerys.minko.type.enum.Blending;
	import aerys.minko.type.enum.BlendingSource;
	import aerys.minko.type.enum.DepthTest;

	public class SpriteShader extends Shader
	{
        private var _diffuse    : DiffuseShaderPart;

		private var _uv	        : SFloat;

		public function SpriteShader()
		{
			super();

            _diffuse = new DiffuseShaderPart(this);
		}

		override protected function initializeSettings(settings : ShaderSettings) : void
		{
			var blending : uint = meshBindings.getProperty(
				BasicProperties.BLENDING, Blending.OPAQUE
			);
			settings.depthWriteEnabled = meshBindings.getProperty(
				BasicProperties.DEPTH_WRITE_ENABLED, true
			);
			settings.depthTest = meshBindings.getProperty(
				BasicProperties.DEPTH_TEST, DepthTest.LESS
			);

			if ((blending & 0xff) == BlendingSource.SOURCE_ALPHA)
			{
				settings.priority -= 0.5;
				settings.depthSortDrawCalls = true;
			}

			settings.blending			= blending;
			settings.enabled			= true;
			settings.scissorRectangle	= null;
		}

		override protected function getVertexPosition() : SFloat
		{
			var vertexXY	: SFloat	= vertexXY.xy;

			_uv = multiply(add(vertexXY, 0.5), float2(1, -1));

			var depth	: SFloat 	= meshBindings.getParameter('depth', 1);

			var xy		: SFloat	= float2(
				meshBindings.getParameter('x', 1),
				meshBindings.getParameter('y', 1)
			);
			var size	: SFloat	= float2(
				meshBindings.getParameter('width', 1),
				meshBindings.getParameter('height', 1)
			);
			var vpSize	: SFloat	= float2(
				sceneBindings.getParameter('viewportWidth', 1),
				sceneBindings.getParameter('viewportHeight', 1)
			);

			xy = divide(add(xy, divide(size, 2)), vpSize);
			xy = multiply(subtract(xy, 0.5), float2(2, -2));

			vertexXY.scaleBy(divide(size, divide(vpSize, 2)));
			vertexXY.incrementBy(xy);

			return float4(vertexXY, depth, 1);
		}

		override protected function getPixelColor() : SFloat
		{
			return _diffuse.getDiffuseColor(true, _uv);
		}
	}
}

package aerys.minko.render.shader.sprite
{
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.ShaderSettings;
	import aerys.minko.render.shader.part.DiffuseShaderPart;
	import aerys.minko.type.enum.Blending;
	import aerys.minko.type.enum.BlendingSource;
	import aerys.minko.type.enum.DepthTest;

	public class SpriteShader extends Shader
	{
        private var _diffuse    : DiffuseShaderPart;

		private var _uv	        : SFloat;

		public function SpriteShader()
		{
			super();

            _diffuse = new DiffuseShaderPart(this);
		}

		override protected function initializeSettings(settings : ShaderSettings) : void
		{
			var blending : uint = meshBindings.getProperty(
				BasicProperties.BLENDING, Blending.OPAQUE
			);
			settings.depthWriteEnabled = meshBindings.getProperty(
				BasicProperties.DEPTH_WRITE_ENABLED, true
			);
			settings.depthTest = meshBindings.getProperty(
				BasicProperties.DEPTH_TEST, DepthTest.LESS
			);

			if ((blending & 0xff) == BlendingSource.SOURCE_ALPHA)
			{
				settings.priority -= 0.5;
				settings.depthSortDrawCalls = true;
			}

			settings.blending			= blending;
			settings.enabled			= true;
			settings.scissorRectangle	= null;
		}

		override protected function getVertexPosition() : SFloat
		{
			var vertexXY	: SFloat	= vertexXY.xy;

			_uv = multiply(add(vertexXY, 0.5), float2(1, -1));

			var depth	: SFloat 	= meshBindings.getParameter('depth', 1);

			var xy		: SFloat	= float2(
				meshBindings.getParameter('x', 1),
				meshBindings.getParameter('y', 1)
			);
			var size	: SFloat	= float2(
				meshBindings.getParameter('width', 1),
				meshBindings.getParameter('height', 1)
			);
			var vpSize	: SFloat	= float2(
				sceneBindings.getParameter('viewportWidth', 1),
				sceneBindings.getParameter('viewportHeight', 1)
			);

			xy = divide(add(xy, divide(size, 2)), vpSize);
			xy = multiply(subtract(xy, 0.5), float2(2, -2));

			vertexXY.scaleBy(divide(size, divide(vpSize, 2)));
			vertexXY.incrementBy(xy);

			return float4(vertexXY, depth, 1);
		}

		override protected function getPixelColor() : SFloat
		{
			return _diffuse.getDiffuseColor(true, interpolate(_uv));
		}
	}
}

Fix sprite shader

Fix sprite shader


package com.kaltura.utils
{
	import flash.utils.describeType;

	/**
	 * ObjectUtil class holds different utilities for use with objects
	 */
	public class ObjectUtil
	{
		/**
		 * retreives a list of all keys on the given object
		 * @param obj	the object to operate on
		 * @return an array with all keys as strings
		 */
		public static function getObjectAllKeys( obj : Object ) : Array
		{
			var arr : Array = new Array();
			arr = getObjectStaticKeys( obj );
			arr = arr.concat( getObjectDynamicKeys( obj ) );
			return arr;
		}

		/**
		 * retreives a list of all the keys defined at authoring time.
		 * @param obj	the object to operate on
		 * @return an array with all keys as strings
		 */
		public static function getObjectStaticKeys( obj : Object ) : Array
		{
			var arr : Array = new Array();
			var classInfo:XML = describeType(obj);
			// don't take keys defined in ObjectProxy (i.e., uid)
			var accessors:XMLList = classInfo..accessor.(@declaredBy != "mx.utils::ObjectProxy");
			for each (var v:XML in accessors) {
				arr.push( v.@name.toString() );
			}
			return arr;
		}

		/**
		 * retreives a list of all the keys defined at runtime (for dynamic objects).
		 * @param obj	the object to operate on
		 * @return an array with all keys as strings
		 */
		public static function getObjectDynamicKeys( obj : Object ) : Array
		{
			var arr : Array = new Array();
			for( var str:String in obj )
				arr.push( str );

			return arr;
		}

		/**
		 * retreives a list of all the values on the given object.
		 * @param obj	the object to operate on
		 * @return an array with all values
		 */
		public static function getObjectAllValues( obj : Object ) : Array
		{
			var arr : Array = new Array();
			arr = getObjectStaticValues( obj );
			arr = arr.concat( getObjectDynamicValues( obj ) );
			return arr;
		}

		/**
		 * retreives a list of all the values of keys defined at authoring time.
		 * @param obj	the object to operate on
		 * @return an array with all values
		 */
		public static function getObjectStaticValues( obj : Object ) : Array
		{
			var arr : Array = new Array();
			var classInfo:XML = describeType(obj);
			for each (var v:XML in classInfo..variable)
			arr.push( obj[v.@name] );

			return arr;
		}

		/**
		 * retreives a list of all the values of keys defined at runtime (for dynamic objects).
		 * @param obj	the object to operate on
		 * @return an array with all values
		 */
		public static function getObjectDynamicValues( obj : Object ) : Array
		{
			var arr : Array = new Array();
			for( var str:String in obj )
				arr.push( obj[str] );

			return arr;
		}

		/**
		 * compare variables and properties of 2 given objects
		 * this function deliberately ignors the property uid.
		 * @param object1
		 * @param object2
		 * @return
		 *
		 */
		public static function compareObjects(object1:Object,object2:Object):Boolean
		{
			var ob1:Object = describeObject(object1);
			var ob2:Object = describeObject(object2);
			//run on obj1, check if the value exist in ob2 and check its value
			for (var o:Object in ob1)
			{
				if (ob2.hasOwnProperty(o))
				{
					if(ob1[o] != ob2[o] && o!="uid")
						return false;

				} else
				{
					return false;
				}
			}
			//run on obj2, check if the value exist in ob1 and check its value
			for (var p:Object in ob2)
			{
				if (ob1.hasOwnProperty(p))
				{
					if(ob2[p] != ob1[p] && p!="uid")
						return false;

				} else
				{
					return false;
				}
			}

			return true;
		}

		/**
		 * Return an object that holds all variables and properties and their values
		 * @param obj
		 * @return
		 *
		 */
		public static function describeObject(obj:Object):Object
		{
			var ob:Object = new Object();

			var classInfo:XML = describeType(obj);
			//map all variables
			for each (var v:XML in classInfo..variable)
			{
				ob[v.@name] = obj[v.@name];
			}
			//map all properties
			for each (var a:XML in classInfo..accessor)
			{
				ob[a.@name] = obj[a.@name];
			}
			return ob;
		}

	}
}

package com.kaltura.utils
{
	import flash.utils.describeType;

	/**
	 * ObjectUtil class holds different utilities for use with objects
	 */
	public class ObjectUtil
	{
		/**
		 * retreives a list of all keys on the given object
		 * @param obj	the object to operate on
		 * @return an array with all keys as strings
		 */
		public static function getObjectAllKeys( obj : Object ) : Array
		{
			var arr : Array = new Array();
			arr = getObjectStaticKeys( obj );
			arr = arr.concat( getObjectDynamicKeys( obj ) );
			return arr;
		}

		/**
		 * retreives a list of all the keys defined at authoring time.
		 * @param obj	the object to operate on
		 * @return an array with all keys as strings
		 */
		public static function getObjectStaticKeys( obj : Object ) : Array
		{
			var arr : Array = new Array();
			var classInfo:XML = describeType(obj);
			// don't take keys defined in ObjectProxy (i.e., uid)
			var accessors:XMLList = classInfo..accessor.(@declaredBy != "mx.utils::ObjectProxy");
			for each (var v:XML in accessors) {
				arr.push( v.@name.toString() );
			}
			return arr;
		}

		/**
		 * retreives a list of all the keys defined at runtime (for dynamic objects).
		 * @param obj	the object to operate on
		 * @return an array with all keys as strings
		 */
		public static function getObjectDynamicKeys( obj : Object ) : Array
		{
			var arr : Array = new Array();
			for( var str:String in obj )
				arr.push( str );

			return arr;
		}

		/**
		 * retreives a list of all the values on the given object.
		 * @param obj	the object to operate on
		 * @return an array with all values
		 */
		public static function getObjectAllValues( obj : Object ) : Array
		{
			var arr : Array = new Array();
			arr = getObjectStaticValues( obj );
			arr = arr.concat( getObjectDynamicValues( obj ) );
			return arr;
		}

		/**
		 * retreives a list of all the values of keys defined at authoring time.
		 * @param obj	the object to operate on
		 * @return an array with all values
		 */
		public static function getObjectStaticValues( obj : Object ) : Array
		{
			var arr : Array = new Array();
			var classInfo:XML = describeType(obj);
			for each (var v:XML in classInfo..variable)
			arr.push( obj[v.@name] );

			return arr;
		}

		/**
		 * retreives a list of all the values of keys defined at runtime (for dynamic objects).
		 * @param obj	the object to operate on
		 * @return an array with all values
		 */
		public static function getObjectDynamicValues( obj : Object ) : Array
		{
			var arr : Array = new Array();
			for( var str:String in obj )
				arr.push( obj[str] );

			return arr;
		}

		/**
		 * compare variables and properties of 2 given objects
		 * this function deliberately ignors the property uid.
		 * @param object1
		 * @param object2
		 * @return
		 *
		 */
		public static function compareObjects(object1:Object,object2:Object):Boolean
		{
			var ob1:Object = describeObject(object1);
			var ob2:Object = describeObject(object2);
			//run on obj1, check if the value exist in ob2 and check its value
			for (var o:Object in ob1)
			{
				if (ob2.hasOwnProperty(o))
				{
					if(ob1[o] != ob2[o] && o!="uid")
						return false;

				} else
				{
					return false;
				}
			}
			//run on obj2, check if the value exist in ob1 and check its value
			for (var p:Object in ob2)
			{
				if (ob1.hasOwnProperty(p))
				{
					if(ob2[p] != ob1[p] && p!="uid")
						return false;

				} else
				{
					return false;
				}
			}

			return true;
		}

		/**
		 * Return an object that holds all variables and properties and their values
		 * @param obj
		 * @return
		 *
		 */
		public static function describeObject(obj:Object):Object
		{
			var ob:Object = new Object();

			var classInfo:XML = describeType(obj);
			//map all variables
			for each (var v:XML in classInfo..variable)
			{
				ob[v.@name] = obj[v.@name];
			}
			//map all properties
			for each (var a:XML in classInfo..accessor)
			{
				ob[a.@name] = obj[a.@name];
			}
			return ob;
		}

		/**
		 * Copy attributes from source object to target object
		 * @param source
		 * @param target
		 *
		 */
		public static function copyObject(source:Object, target:Object):void {
			var atts:Array = getObjectAllKeys(source);
			for (var i:int = 0; i< atts.length; i++) {
				target[atts[i]] = source[atts[i]];
			}
		}

	}
}

add copyObject function

add copyObject function

git-svn-id: 8a2ccb88241e16c78017770bc38d91d6d5396a5a@65593 6b8eccd3-e8c5-4e7d-8186-e12b5326b719


package org.jivesoftware.xiff.core
{
	import flash.events.ErrorEvent;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import flash.xml.XMLDocument;
	import flash.xml.XMLNode;

	import mx.messaging.messages.HTTPRequestMessage;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.HTTPService;

	import org.jivesoftware.xiff.auth.Anonymous;
	import org.jivesoftware.xiff.auth.External;
	import org.jivesoftware.xiff.auth.Plain;
	import org.jivesoftware.xiff.auth.SASLAuth;
	import org.jivesoftware.xiff.data.IQ;
	import org.jivesoftware.xiff.data.bind.BindExtension;
	import org.jivesoftware.xiff.data.session.SessionExtension;
	import org.jivesoftware.xiff.events.ConnectionSuccessEvent;
	import org.jivesoftware.xiff.events.IncomingDataEvent;
	import org.jivesoftware.xiff.events.LoginEvent;
	import org.jivesoftware.xiff.util.Callback;

	public class XMPPBOSHConnection extends XMPPConnection
	{
		private static var saslMechanisms:Object = {
			"PLAIN":Plain,
			"ANONYMOUS":Anonymous,
            "EXTERNAL":External
		};

		private var maxConcurrentRequests:uint = 2;
		private var boshVersion:Number = 1.6;
		private var headers:Object = {
			"post": [],
			"get": ['Cache-Control', 'no-store', 'Cache-Control', 'no-cache', 'Pragma', 'no-cache']
		};
		private var requestCount:int = 0;
		private var failedRequests:Array = [];
		private var requestQueue:Array = [];
		private var responseQueue:Array = [];
		private const responseTimer:Timer = new Timer(0.0, 1);
		private var isDisconnecting:Boolean = false;
		private var boshPollingInterval:Number = 10000;
		private var sid:String;
		private var rid:Number;
		private var wait:int;
		private var inactivity:int;
		private var pollTimer:Timer = new Timer(1, 1);
		private var pollTimeoutTimer:Timer = new Timer(1, 1);

		private var auth:SASLAuth;
		private var authHandler:Function;
		private var https:Boolean = false;
		private var _port:Number = -1;

		private var callbacks:Object = {};

		public static var logger:Object = null;
		private var lastPollTime:Date = null;

		public override function connect(streamType:String=null):Boolean
		{
			if(logger)
				logger.log("CONNECT()", "INFO");
			BindExtension.enable();
			active = false;
			loggedIn = false;
			var attrs:Object = {
	            xmlns: "http://jabber.org/protocol/httpbind",
	            hold: maxConcurrentRequests,
	            rid: nextRID,
	            secure: https,
	            wait: 10,
	            ver: boshVersion
	        }

	        var result:XMLNode = new XMLNode(1, "body");
	        result.attributes = attrs;
	        sendRequests(result);

	        return true;
		}

		public static function registerSASLMechanism(name:String, authClass:Class):void
		{
			saslMechanisms[name] = authClass;
		}

		public static function disableSASLMechanism(name:String):void
		{
			saslMechanisms[name] = null;
		}

		public function set secure(flag:Boolean):void
		{
			https = flag;
		}

		public override function set port(portnum:Number):void
		{
			_port = portnum;
		}

		public override function get port():Number
		{
			if(_port == -1)
				return https ? 8483 : 8080;
			return _port;
		}

		public function get httpServer():String
		{
			return (https ? "https" : "http") + "://" + server + ":" + port + "/http-bind/";
		}

		public override function disconnect():void
		{
			super.disconnect();
			pollTimer.stop();
			pollTimer = null;
			pollTimeoutTimer.stop();
			pollTimeoutTimer = null;
		}

		public function processConnectionResponse(responseBody:XMLNode):void
		{
			var attributes:Object = responseBody.attributes;
			sid = attributes.sid;
	        boshPollingInterval = attributes.polling * 1000;

	        wait = attributes.wait;
	        inactivity = attributes.inactivity * 1000;
	        if(inactivity - 2000 >= boshPollingInterval || (boshPollingInterval <= 1000 && boshPollingInterval > 0))
	        {
	        	boshPollingInterval += 1000;
	        }

	        inactivity -= 1000;

	        var serverRequests:int = attributes.requests;
	        if (serverRequests)
	            maxConcurrentRequests = Math.min(serverRequests, maxConcurrentRequests);
	        active = true;
	        configureConnection(responseBody);
	        responseTimer.addEventListener(TimerEvent.TIMER_COMPLETE, processResponse);
	        trace(boshPollingInterval);
	        pollTimer.delay = boshPollingInterval;
	        pollTimer.addEventListener(TimerEvent.TIMER_COMPLETE, pollServer);
	        pollTimer.start();

	        pollTimeoutTimer.delay = inactivity;
	        pollTimeoutTimer.addEventListener(TimerEvent.TIMER_COMPLETE, function(evt:TimerEvent):void {
				trace("Poll timed out, resuming");
				pollServer(evt, true);
			});
		}

		private function processResponse(event:TimerEvent=null):void
		{
			// Read the data and send it to the appropriate parser
			var currentNode:XMLNode = responseQueue.shift();
			var nodeName:String = currentNode.nodeName.toLowerCase();

			switch( nodeName )
			{
				case "stream:features":
					//we already handled this in httpResponse()
					break;

				case "stream:error":
					handleStreamError( currentNode );
					break;

				case "iq":
					handleIQ( currentNode );
					break;

				case "message":
					handleMessage( currentNode );
					break;

				case "presence":
					handlePresence( currentNode );
					break;

				case "success":
					handleAuthentication( currentNode );
					break;

				case "failure":
					handleAuthentication( currentNode );
					break;
				default:
					dispatchError( "undefined-condition", "Unknown Error", "modify", 500 );
					break;
			}

			resetResponseProcessor();
		}

		private function resetResponseProcessor():void
		{
			if(responseQueue.length > 0)
			{
				responseTimer.reset();
				responseTimer.start();
			}
		}

		private function httpResponse(req:XMLNode, isPollResponse:Boolean, evt:ResultEvent):void
		{
			requestCount--;
			var rawXML:String = evt.result as String;

			if(logger)
	       		logger.log(rawXML, "INCOMING");

			var xmlData:XMLDocument = new XMLDocument();
			xmlData.ignoreWhite = this.ignoreWhite;
			xmlData.parseXML( rawXML );
			var bodyNode:XMLNode = xmlData.firstChild;

			var event:IncomingDataEvent = new IncomingDataEvent();
			event.data = xmlData;
			dispatchEvent( event );

			if(bodyNode.attributes["type"] == "terminal")
			{
				dispatchError( "BOSH Error", bodyNode.attributes["condition"], "", -1 );
				active = false;
			}

			for each(var childNode:XMLNode in bodyNode.childNodes)
			{
				if(childNode.nodeName == "stream:features")
				{
					_expireTagSearch = false;
					processConnectionResponse( bodyNode );
				}
				else
					responseQueue.push(childNode);
			}

			resetResponseProcessor();

			//if we have no outstanding requests, then we're free to send a poll at the next opportunity
			if(requestCount == 0 && !sendQueuedRequests())
				resetPollTimer();
		}

		private function httpError(req:XMLNode, isPollResponse:Boolean, evt:FaultEvent):void
		{
			disconnect();
			dispatchError("Unknown HTTP Error", evt.fault.rootCause.text, "", -1);
		}

		protected override function sendXML(body:*):void
		{
			sendQueuedRequests(body);
		}

		private function sendQueuedRequests(body:*=null):Boolean
		{
			if(body)
				requestQueue.push(body);
			else if(requestQueue.length == 0)
				return false;

			return sendRequests();
		}

		//returns true if any requests were sent
		private function sendRequests(data:XMLNode = null, isPoll:Boolean = false):Boolean
		{
			if(requestCount >= maxConcurrentRequests)
				return false;

			requestCount++;

			if(!data)
			{
				if(isPoll)
				{
					data = createRequest();
				}
				else
				{
					var temp:Array = [];
					for(var i:uint = 0; i < 10 && requestQueue.length > 0; i++)
					{
						temp.push(requestQueue.shift());
					}
					data = createRequest(temp);
				}
			}

			//build the http request
	        var request:HTTPService = new HTTPService();
	        request.method = "post";
	        request.headers = headers[request.method];
	        request.url = httpServer;
	        request.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
	        request.contentType = "text/xml";

	        var responseCallback:Callback = new Callback(this, httpResponse, data, isPoll);
	        var errorCallback:Callback = new Callback(this, httpError, data, isPoll);

	        request.addEventListener(ResultEvent.RESULT, responseCallback.call, false);
	        request.addEventListener(FaultEvent.FAULT, errorCallback.call, false);

	       	if(logger)
	       		logger.log(data, "OUTGOING");

			request.send(data);

			if(isPoll) {
				lastPollTime = new Date();
				pollTimeoutTimer.reset();
				pollTimeoutTimer.start();
				trace("Polling at: " + lastPollTime.getMinutes() + ":" + lastPollTime.getSeconds());
			}

			pollTimer.stop();

			return true;
		}

    	private function resetPollTimer():void
    	{
    		if(!pollTimer)
    			return;

    		pollTimeoutTimer.stop();

    		pollTimer.reset();
    		var timeSinceLastPoll:Number = 0;
    		if(lastPollTime)
    			 timeSinceLastPoll = new Date().time - lastPollTime.time;

    		if(timeSinceLastPoll >= boshPollingInterval)
    			timeSinceLastPoll = 0;

    		pollTimer.delay = boshPollingInterval - timeSinceLastPoll;
    		pollTimer.start();
    	}

    	private function pollServer(evt:TimerEvent, force:Boolean=false):void
    	{
    		if(force)
    			trace("Forcing poll!");
    		//We shouldn't poll if the connection is dead, if we had requests to send instead, or if there's already one in progress
    		if(!isActive() || ((sendQueuedRequests() || requestCount > 0) && !force))
    			return;

    		//this should be safe since sendRequests checks to be sure it's not over the concurrent requests limit, and we just ensured that the queue is empty by calling sendQueuedRequests()
    		sendRequests(null, true);
    	}

    	private function get nextRID():Number {
    		if(!rid)
    			rid = Math.floor(Math.random() * 1000000);
    		return ++rid;
    	}

    	private function createRequest(bodyContent:Array=null):XMLNode {
    		var attrs:Object = {
    			xmlns: "http://jabber.org/protocol/httpbind",
				rid: nextRID,
				sid: sid
    		}
    		var req:XMLNode = new XMLNode(1, "body");
    		if(bodyContent)
    			for each(var content:XMLNode in bodyContent)
    					req.appendChild(content);

    		req.attributes = attrs;

    		return req;
    	}

	    private function handleAuthentication(responseBody:XMLNode):void
	    {
	    //    if(!response || response.length == 0) {
	     //       return;
	     //   }
	        var status:Object = auth.handleResponse(0, responseBody);
	        if (status.authComplete) {
	            if (status.authSuccess) {
	                bindConnection();
	            }
	            else {
					dispatchError("Authentication Error", "", "", 401);
	                disconnect();
	            }
	        }
	    }

	    private function configureConnection(responseBody:XMLNode):void
	    {
	    	var features:XMLNode = responseBody.firstChild;

	        var authentication:Object = {};
	        for each(var feature:XMLNode in features.childNodes)
			{
	            if (feature.nodeName == "mechanisms")
	                authentication.auth = configureAuthMechanisms(feature);
	            else if (feature.nodeName == "bind")
	                authentication.bind = true;
	            else if (feature.nodeName == "session")
	                authentication.session = true;
	        }

	        auth = authentication.auth;
	        dispatchEvent(new ConnectionSuccessEvent());
	        authHandler = handleAuthentication;
	        sendXML(auth.request);
	    }

	    private function configureAuthMechanisms(mechanisms:XMLNode):SASLAuth
	    {
	        var authMechanism:SASLAuth;
	        var authClass:Class;
	        for each(var mechanism:XMLNode in mechanisms.childNodes)
	        {
	        	authClass = saslMechanisms[mechanism.firstChild.nodeValue];
	   			if(authClass)
	   				break;
	        }

	        if (!authClass) {
	        	dispatchError("SASL missing", "The server is not configured to support any available SASL mechanisms", "SASL", -1);
	        	return null;
	        }

	   		return new authClass(this);
	    }

	    public function handleBindResponse(packet:IQ):void
	    {
	    	var bind:BindExtension = packet.getExtension("bind") as BindExtension;

            var jid:UnescapedJID = bind.jid.unescaped;

            myResource = jid.resource;
            myUsername = jid.node;
            domain = jid.domain;

            establishSession();
	    }

	    private function bindConnection():void
	    {
	    	var bindIQ:IQ = new IQ(null, "set");

			var bindExt:BindExtension = new BindExtension();
			if(resource)
	        	bindExt.resource = resource;

	        bindIQ.addExtension(bindExt);

			//this is laaaaaame, it should be a function
			bindIQ.callbackName = "handleBindResponse";
			bindIQ.callbackScope = this;

	        send(bindIQ);
	    }

	    public function handleSessionResponse(packet:IQ):void
	    {
	    	dispatchEvent(new LoginEvent());
	    }

	    private function establishSession():void
	    {
	        var sessionIQ:IQ = new IQ(null, "set");

	        sessionIQ.addExtension(new SessionExtension());

	        sessionIQ.callbackName = "handleSessionResponse";
	        sessionIQ.callbackScope = this;

	        send(sessionIQ);
	    }

		//do nothing, we use polling instead
		public override function sendKeepAlive():void {}
	}
}

package org.jivesoftware.xiff.core
{
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import flash.xml.XMLDocument;
	import flash.xml.XMLNode;

	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.HTTPService;

	import org.jivesoftware.xiff.auth.Anonymous;
	import org.jivesoftware.xiff.auth.External;
	import org.jivesoftware.xiff.auth.Plain;
	import org.jivesoftware.xiff.auth.SASLAuth;
	import org.jivesoftware.xiff.data.IQ;
	import org.jivesoftware.xiff.data.bind.BindExtension;
	import org.jivesoftware.xiff.data.session.SessionExtension;
	import org.jivesoftware.xiff.events.ConnectionSuccessEvent;
	import org.jivesoftware.xiff.events.IncomingDataEvent;
	import org.jivesoftware.xiff.events.LoginEvent;
	import org.jivesoftware.xiff.util.Callback;

	public class XMPPBOSHConnection extends XMPPConnection
	{
		private static var saslMechanisms:Object = {
			"PLAIN":Plain,
			"ANONYMOUS":Anonymous,
            "EXTERNAL":External
		};

		private var maxConcurrentRequests:uint = 2;
		private var boshVersion:Number = 1.6;
		private var headers:Object = {
			"post": [],
			"get": ['Cache-Control', 'no-store', 'Cache-Control', 'no-cache', 'Pragma', 'no-cache']
		};
		private var requestCount:int = 0;
		private var failedRequests:Array = [];
		private var requestQueue:Array = [];
		private var responseQueue:Array = [];
		private const responseTimer:Timer = new Timer(0.0, 1);
		private var isDisconnecting:Boolean = false;
		private var boshPollingInterval:Number = 10000;
		private var sid:String;
		private var rid:Number;
		private var wait:int;
		private var inactivity:int;
		private var pollTimer:Timer = new Timer(1, 1);
		private var pollTimeoutTimer:Timer = new Timer(1, 1);

		private var auth:SASLAuth;
		private var authHandler:Function;
		private var https:Boolean = false;
		private var _port:Number = -1;

		private var callbacks:Object = {};

		public static var logger:Object = null;
		private var lastPollTime:Date = null;

		public override function connect(streamType:String=null):Boolean
		{
			if(logger)
				logger.log("CONNECT()", "INFO");
			BindExtension.enable();
			active = false;
			loggedIn = false;
			var attrs:Object = {
	            xmlns: "http://jabber.org/protocol/httpbind",
	            hold: maxConcurrentRequests,
	            rid: nextRID,
	            secure: https,
	            wait: 10,
	            ver: boshVersion
	        }

	        var result:XMLNode = new XMLNode(1, "body");
	        result.attributes = attrs;
	        sendRequests(result);

	        return true;
		}

		public static function registerSASLMechanism(name:String, authClass:Class):void
		{
			saslMechanisms[name] = authClass;
		}

		public static function disableSASLMechanism(name:String):void
		{
			saslMechanisms[name] = null;
		}

		public function set secure(flag:Boolean):void
		{
			https = flag;
		}

		public function get secure():Boolean
		{
			return https;
		}

		public override function set port(portnum:Number):void
		{
			_port = portnum;
		}

		public override function get port():Number
		{
			if(_port == -1)
				return https ? 8483 : 8080;
			return _port;
		}

		public function get httpServer():String
		{
			return (https ? "https" : "http") + "://" + server + ":" + port + "/http-bind/";
		}

		public override function disconnect():void
		{
			super.disconnect();
			pollTimer.stop();
			pollTimer = null;
			pollTimeoutTimer.stop();
			pollTimeoutTimer = null;
		}

		public function processConnectionResponse(responseBody:XMLNode):void
		{
			var attributes:Object = responseBody.attributes;
			sid = attributes.sid;
	        boshPollingInterval = attributes.polling * 1000;

	        wait = attributes.wait;
	        inactivity = attributes.inactivity * 1000;
	        if(inactivity - 2000 >= boshPollingInterval || (boshPollingInterval <= 1000 && boshPollingInterval > 0))
	        {
	        	boshPollingInterval += 1000;
	        }

	        inactivity -= 1000;

	        var serverRequests:int = attributes.requests;
	        if (serverRequests)
	            maxConcurrentRequests = Math.min(serverRequests, maxConcurrentRequests);
	        active = true;
	        configureConnection(responseBody);
	        responseTimer.addEventListener(TimerEvent.TIMER_COMPLETE, processResponse);
	        trace(boshPollingInterval);
	        pollTimer.delay = boshPollingInterval;
	        pollTimer.addEventListener(TimerEvent.TIMER_COMPLETE, pollServer);
	        pollTimer.start();

	        pollTimeoutTimer.delay = inactivity;
	        pollTimeoutTimer.addEventListener(TimerEvent.TIMER_COMPLETE, function(evt:TimerEvent):void {
				trace("Poll timed out, resuming");
				pollServer(evt, true);
			});
		}

		private function processResponse(event:TimerEvent=null):void
		{
			// Read the data and send it to the appropriate parser
			var currentNode:XMLNode = responseQueue.shift();
			var nodeName:String = currentNode.nodeName.toLowerCase();

			switch( nodeName )
			{
				case "stream:features":
					//we already handled this in httpResponse()
					break;

				case "stream:error":
					handleStreamError( currentNode );
					break;

				case "iq":
					handleIQ( currentNode );
					break;

				case "message":
					handleMessage( currentNode );
					break;

				case "presence":
					handlePresence( currentNode );
					break;

				case "success":
					handleAuthentication( currentNode );
					break;

				case "failure":
					handleAuthentication( currentNode );
					break;
				default:
					dispatchError( "undefined-condition", "Unknown Error", "modify", 500 );
					break;
			}

			resetResponseProcessor();
		}

		private function resetResponseProcessor():void
		{
			if(responseQueue.length > 0)
			{
				responseTimer.reset();
				responseTimer.start();
			}
		}

		private function httpResponse(req:XMLNode, isPollResponse:Boolean, evt:ResultEvent):void
		{
			requestCount--;
			var rawXML:String = evt.result as String;

			if(logger)
	       		logger.log(rawXML, "INCOMING");

			var xmlData:XMLDocument = new XMLDocument();
			xmlData.ignoreWhite = this.ignoreWhite;
			xmlData.parseXML( rawXML );
			var bodyNode:XMLNode = xmlData.firstChild;

			var event:IncomingDataEvent = new IncomingDataEvent();
			event.data = xmlData;
			dispatchEvent( event );

			if(bodyNode.attributes["type"] == "terminal")
			{
				dispatchError( "BOSH Error", bodyNode.attributes["condition"], "", -1 );
				active = false;
			}

			for each(var childNode:XMLNode in bodyNode.childNodes)
			{
				if(childNode.nodeName == "stream:features")
				{
					_expireTagSearch = false;
					processConnectionResponse( bodyNode );
				}
				else
					responseQueue.push(childNode);
			}

			resetResponseProcessor();

			//if we have no outstanding requests, then we're free to send a poll at the next opportunity
			if(requestCount == 0 && !sendQueuedRequests())
				resetPollTimer();
		}

		private function httpError(req:XMLNode, isPollResponse:Boolean, evt:FaultEvent):void
		{
			disconnect();
			dispatchError("Unknown HTTP Error", evt.fault.rootCause.text, "", -1);
		}

		protected override function sendXML(body:*):void
		{
			sendQueuedRequests(body);
		}

		private function sendQueuedRequests(body:*=null):Boolean
		{
			if(body)
				requestQueue.push(body);
			else if(requestQueue.length == 0)
				return false;

			return sendRequests();
		}

		//returns true if any requests were sent
		private function sendRequests(data:XMLNode = null, isPoll:Boolean = false):Boolean
		{
			if(requestCount >= maxConcurrentRequests)
				return false;

			requestCount++;

			if(!data)
			{
				if(isPoll)
				{
					data = createRequest();
				}
				else
				{
					var temp:Array = [];
					for(var i:uint = 0; i < 10 && requestQueue.length > 0; i++)
					{
						temp.push(requestQueue.shift());
					}
					data = createRequest(temp);
				}
			}

			//build the http request
	        var request:HTTPService = new HTTPService();
	        request.method = "post";
	        request.headers = headers[request.method];
	        request.url = httpServer;
	        request.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
	        request.contentType = "text/xml";

	        var responseCallback:Callback = new Callback(this, httpResponse, data, isPoll);
	        var errorCallback:Callback = new Callback(this, httpError, data, isPoll);

	        request.addEventListener(ResultEvent.RESULT, responseCallback.call, false);
	        request.addEventListener(FaultEvent.FAULT, errorCallback.call, false);

	       	if(logger)
	       		logger.log(data, "OUTGOING");

			request.send(data);

			if(isPoll) {
				lastPollTime = new Date();
				pollTimeoutTimer.reset();
				pollTimeoutTimer.start();
				trace("Polling at: " + lastPollTime.getMinutes() + ":" + lastPollTime.getSeconds());
			}

			pollTimer.stop();

			return true;
		}

    	private function resetPollTimer():void
    	{
    		if(!pollTimer)
    			return;

    		pollTimeoutTimer.stop();

    		pollTimer.reset();
    		var timeSinceLastPoll:Number = 0;
    		if(lastPollTime)
    			 timeSinceLastPoll = new Date().time - lastPollTime.time;

    		if(timeSinceLastPoll >= boshPollingInterval)
    			timeSinceLastPoll = 0;

    		pollTimer.delay = boshPollingInterval - timeSinceLastPoll;
    		pollTimer.start();
    	}

    	private function pollServer(evt:TimerEvent, force:Boolean=false):void
    	{
    		if(force)
    			trace("Forcing poll!");
    		//We shouldn't poll if the connection is dead, if we had requests to send instead, or if there's already one in progress
    		if(!isActive() || ((sendQueuedRequests() || requestCount > 0) && !force))
    			return;

    		//this should be safe since sendRequests checks to be sure it's not over the concurrent requests limit, and we just ensured that the queue is empty by calling sendQueuedRequests()
    		sendRequests(null, true);
    	}

    	private function get nextRID():Number {
    		if(!rid)
    			rid = Math.floor(Math.random() * 1000000);
    		return ++rid;
    	}

    	private function createRequest(bodyContent:Array=null):XMLNode {
    		var attrs:Object = {
    			xmlns: "http://jabber.org/protocol/httpbind",
				rid: nextRID,
				sid: sid
    		}
    		var req:XMLNode = new XMLNode(1, "body");
    		if(bodyContent)
    			for each(var content:XMLNode in bodyContent)
    					req.appendChild(content);

    		req.attributes = attrs;

    		return req;
    	}

	    private function handleAuthentication(responseBody:XMLNode):void
	    {
	    //    if(!response || response.length == 0) {
	     //       return;
	     //   }
	        var status:Object = auth.handleResponse(0, responseBody);
	        if (status.authComplete) {
	            if (status.authSuccess) {
	                bindConnection();
	            }
	            else {
					dispatchError("Authentication Error", "", "", 401);
	                disconnect();
	            }
	        }
	    }

	    private function configureConnection(responseBody:XMLNode):void
	    {
	    	var features:XMLNode = responseBody.firstChild;

	        var authentication:Object = {};
	        for each(var feature:XMLNode in features.childNodes)
			{
	            if (feature.nodeName == "mechanisms")
	                authentication.auth = configureAuthMechanisms(feature);
	            else if (feature.nodeName == "bind")
	                authentication.bind = true;
	            else if (feature.nodeName == "session")
	                authentication.session = true;
	        }

	        auth = authentication.auth;
	        dispatchEvent(new ConnectionSuccessEvent());
	        authHandler = handleAuthentication;
	        sendXML(auth.request);
	    }

	    private function configureAuthMechanisms(mechanisms:XMLNode):SASLAuth
	    {
	        var authMechanism:SASLAuth;
	        var authClass:Class;
	        for each(var mechanism:XMLNode in mechanisms.childNodes)
	        {
	        	authClass = saslMechanisms[mechanism.firstChild.nodeValue];
	   			if(authClass)
	   				break;
	        }

	        if (!authClass) {
	        	dispatchError("SASL missing", "The server is not configured to support any available SASL mechanisms", "SASL", -1);
	        	return null;
	        }

	   		return new authClass(this);
	    }

	    public function handleBindResponse(packet:IQ):void
	    {
	    	var bind:BindExtension = packet.getExtension("bind") as BindExtension;

            var jid:UnescapedJID = bind.jid.unescaped;

            myResource = jid.resource;
            myUsername = jid.node;
            domain = jid.domain;

            establishSession();
	    }

	    private function bindConnection():void
	    {
	    	var bindIQ:IQ = new IQ(null, "set");

			var bindExt:BindExtension = new BindExtension();
			if(resource)
	        	bindExt.resource = resource;

	        bindIQ.addExtension(bindExt);

			//this is laaaaaame, it should be a function
			bindIQ.callbackName = "handleBindResponse";
			bindIQ.callbackScope = this;

	        send(bindIQ);
	    }

	    public function handleSessionResponse(packet:IQ):void
	    {
	    	dispatchEvent(new LoginEvent());
	    }

	    private function establishSession():void
	    {
	        var sessionIQ:IQ = new IQ(null, "set");

	        sessionIQ.addExtension(new SessionExtension());

	        sessionIQ.callbackName = "handleSessionResponse";
	        sessionIQ.callbackScope = this;

	        send(sessionIQ);
	    }

		//do nothing, we use polling instead
		public override function sendKeepAlive():void {}
	}
}

Add an accessor for whether or not we are using https. r=Armando

git-svn-id: c197267f952b24206666de142881703007ca05d5@10681 b35dd754-fafc-0310-a699-88a17e54d16e


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

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

	/**
	 * The GravityWell action applies a force on the particle to draw it towards
	 * a single point. The force applied is inversely proportional to the square
	 * of the distance from the particle to the point, in accordance with Newton's
	 * law of gravity.
	 *
	 *
This simulates the effect of gravity over large distances (as between
	 * planets, for example). To simulate the effect of gravity at the surface
	 * of the eacrth, use an Acceleration action with the direction of force
	 * downwards.

	 *
	 * @see Acceleration
	 */

	public class GravityWell extends ActionBase
	{
		private var _x:Number;
		private var _y:Number;
		private var _power:Number;
		private var _epsilonSq:Number;
		private var _gravityConst:Number = 10000; // just scales the power to a more reasonable number

		private var lp:Particle2D;
		private var lx:Number;
		private var ly:Number;
		private var ldSq:Number;
		private var ld:Number;
		private var lfactor:Number;

		/**
		 * The constructor creates a GravityWell action for use by an emitter.
		 * To add a GravityWell to all particles created by an emitter, use the
		 * emitter's addAction method.
		 *
		 * @see org.flintparticles.common.emitters.Emitter#addAction()
		 *
		 * @param power The strength of the gravity force - larger numbers produce a
		 * stronger force.
		 * @param x The x coordinate of the point towards which the force draws
		 * the particles.
		 * @param y The y coordinate of the point towards which the force draws
		 * the particles.
		 * @param epsilon The minimum distance for which gravity is calculated.
		 * Particles closer than this distance experience a gravity force as if
		 * they were this distance away. This stops the gravity effect blowing
		 * up as distances get small. For realistic gravity effects you will want
		 * a small epsilon ( ~1 ), but for stable visual effects a larger
		 * epsilon (~100) is often better.
		 */
		public function GravityWell( power:Number = 0, x:Number = 0, y:Number = 0, epsilon:Number = 100 )
		{
			this.power = power;
			this.x = x;
			this.y = y;
			this.epsilon = epsilon;
		}

		/**
		 * The strength of the gravity force - larger numbers produce a
		 * stronger force.
		 */
		public function get power():Number
		{
			return _power / _gravityConst;
		}
		public function set power( value:Number ):void
		{
			_power = value * _gravityConst;
		}

		/**
		 * The x coordinate of the point towards which the force draws
		 * the particles.
		 */
		public function get x():Number
		{
			return _x;
		}
		public function set x( value:Number ):void
		{
			_x = value;
		}

		/**
		 * The y coordinate of the point towards which the force draws
		 * the particles.
		 */
		public function get y():Number
		{
			return _y;
		}
		public function set y( value:Number ):void
		{
			_y = value;
		}

		/**
		 * The minimum distance for which the gravity force is calculated.
		 * Particles closer than this distance experience the gravity as if
		 * they were this distance away. This stops the gravity effect blowing
		 * up as distances get small.  For realistic gravity effects you will want
		 * a small epsilon ( ~1 ), but for stable visual effects a larger
		 * epsilon (~100) is often better.
		 */
		public function get epsilon():Number
		{
			return Math.sqrt( _epsilonSq );
		}
		public function set epsilon( value:Number ):void
		{
			_epsilonSq = value * value;
		}

		/**
		 * Calculates the gravity force on the particle and applies it for
		 * the period of time indicated.
		 *
		 *
This method is called by the emitter and need not be called by the
		 * user.

		 *
		 * @param emitter The Emitter that created the particle.
		 * @param particle The particle to be updated.
		 * @param time The duration of the frame - used for time based updates.
		 *
		 * @see org.flintparticles.common.actions.Action#update()
		 */
		override public function update( emitter:Emitter, particle:Particle, time:Number ):void
		{
			if( particle.mass == 0 )
			{
				return;
			}
			lp = Particle2D( particle );
			lx = _x - lp.x;
			ly = _y - lp.y;
			ldSq = lx * lx + ly * ly;
			if( ldSq == 0 )
			{
				return;
			}
			ld = Math.sqrt( ldSq );
			if( ldSq < _epsilonSq ) ldSq = _epsilonSq;
			lfactor = ( _power * time ) / ( ldSq * ld );
			lp.velX += lx * lfactor;
			lp.velY += ly * lfactor;
		}
	}
}

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

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

	/**
	 * The GravityWell action applies a force on the particle to draw it towards
	 * a single point. The force applied is inversely proportional to the square
	 * of the distance from the particle to the point, in accordance with Newton's
	 * law of gravity.
	 *
	 *
This simulates the effect of gravity over large distances (as between
	 * planets, for example). To simulate the effect of gravity at the surface
	 * of the eacrth, use an Acceleration action with the direction of force
	 * downwards.

	 *
	 * @see Acceleration
	 */

	public class GravityWell extends ActionBase
	{
		private var _x:Number;
		private var _y:Number;
		private var _power:Number;
		private var _epsilonSq:Number;
		private var _gravityConst:Number = 10000; // just scales the power to a more reasonable number

		/**
		 * The constructor creates a GravityWell action for use by an emitter.
		 * To add a GravityWell to all particles created by an emitter, use the
		 * emitter's addAction method.
		 *
		 * @see org.flintparticles.common.emitters.Emitter#addAction()
		 *
		 * @param power The strength of the gravity force - larger numbers produce a
		 * stronger force.
		 * @param x The x coordinate of the point towards which the force draws
		 * the particles.
		 * @param y The y coordinate of the point towards which the force draws
		 * the particles.
		 * @param epsilon The minimum distance for which gravity is calculated.
		 * Particles closer than this distance experience a gravity force as if
		 * they were this distance away. This stops the gravity effect blowing
		 * up as distances get small. For realistic gravity effects you will want
		 * a small epsilon ( ~1 ), but for stable visual effects a larger
		 * epsilon (~100) is often better.
		 */
		public function GravityWell( power:Number = 0, x:Number = 0, y:Number = 0, epsilon:Number = 100 )
		{
			this.power = power;
			this.x = x;
			this.y = y;
			this.epsilon = epsilon;
		}

		/**
		 * The strength of the gravity force - larger numbers produce a
		 * stronger force.
		 */
		public function get power():Number
		{
			return _power / _gravityConst;
		}
		public function set power( value:Number ):void
		{
			_power = value * _gravityConst;
		}

		/**
		 * The x coordinate of the point towards which the force draws
		 * the particles.
		 */
		public function get x():Number
		{
			return _x;
		}
		public function set x( value:Number ):void
		{
			_x = value;
		}

		/**
		 * The y coordinate of the point towards which the force draws
		 * the particles.
		 */
		public function get y():Number
		{
			return _y;
		}
		public function set y( value:Number ):void
		{
			_y = value;
		}

		/**
		 * The minimum distance for which the gravity force is calculated.
		 * Particles closer than this distance experience the gravity as if
		 * they were this distance away. This stops the gravity effect blowing
		 * up as distances get small.  For realistic gravity effects you will want
		 * a small epsilon ( ~1 ), but for stable visual effects a larger
		 * epsilon (~100) is often better.
		 */
		public function get epsilon():Number
		{
			return Math.sqrt( _epsilonSq );
		}
		public function set epsilon( value:Number ):void
		{
			_epsilonSq = value * value;
		}

		/**
		 * Calculates the gravity force on the particle and applies it for
		 * the period of time indicated.
		 *
		 *
This method is called by the emitter and need not be called by the
		 * user.

		 *
		 * @param emitter The Emitter that created the particle.
		 * @param particle The particle to be updated.
		 * @param time The duration of the frame - used for time based updates.
		 *
		 * @see org.flintparticles.common.actions.Action#update()
		 */
		override public function update( emitter:Emitter, particle:Particle, time:Number ):void
		{
			if( particle.mass == 0 )
			{
				return;
			}
			var p:Particle2D = Particle2D( particle );
			var x:Number = _x - p.x;
			var y:Number = _y - p.y;
			var dSq:Number = x * x + y * y;
			if( dSq == 0 )
			{
				return;
			}
			var d:Number = Math.sqrt( dSq );
			if( dSq < _epsilonSq ) dSq = _epsilonSq;
			var factor:Number = ( _power * time ) / ( dSq * d );
			p.velX += x * factor;
			p.velY += y * factor;
		}
	}
}

Tidy up GravityWell class

Tidy up GravityWell class


//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.presents.client {

import flash.errors.IOError;

import flash.events.Event;
import flash.events.IOErrorEvent;

import flash.net.Socket;

import flash.utils.ByteArray;
import flash.utils.Endian;

import com.threerings.util.Log;

import com.threerings.io.FrameAvailableEvent;
import com.threerings.io.FrameReader;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;

import com.threerings.presents.data.AuthCodes;
import com.threerings.presents.net.AuthRequest;
import com.threerings.presents.net.AuthResponse;
import com.threerings.presents.net.AuthResponseData;
import com.threerings.presents.net.DownstreamMessage;
import com.threerings.presents.net.LogoffRequest;
import com.threerings.presents.net.UpstreamMessage;

public class Communicator
{
    public function Communicator (client :Client)
    {
        _client = client;
    }

    public function logon () :void
    {
        // create our input/output business
        _outBuffer = new ByteArray();
        _outBuffer.endian = Endian.BIG_ENDIAN;
        _outStream = new ObjectOutputStream(_outBuffer);

        _inStream = new ObjectInputStream();

        _portIdx = 0;
        logonToPort();
    }

    /**
     * This method is strangely named, and it does two things which is
     * bad style. Either log on to the next port, or save that the port
     * we just logged on to was a good one.
     */
    protected function logonToPort (logonWasSuccessful :Boolean = false) :Boolean
    {
        var ports :Array = _client.getPorts();

        if (!logonWasSuccessful) {
            if (_portIdx >= ports.length) {
                return false;
            }
            if (_portIdx != 0) {
                _client.reportLogonTribulations(
                    new LogonError(AuthCodes.TRYING_NEXT_PORT, true));

                removeListeners();
            }

            // create the socket and set up listeners
            _socket = new Socket();
            _socket.endian = Endian.BIG_ENDIAN;
            _socket.addEventListener(Event.CONNECT, socketOpened);
            _socket.addEventListener(IOErrorEvent.IO_ERROR, socketError);
            _socket.addEventListener(Event.CLOSE, socketClosed);

            _frameReader = new FrameReader(_socket);
            _frameReader.addEventListener(FrameAvailableEvent.FRAME_AVAILABLE,
                inputFrameReceived);
        }

        var host :String = _client.getHostname();
        var pportKey :String = host + ".preferred_port";
        var pport :int = ports[0];
        var ppidx :int = Math.max(0, ports.indexOf(pport));
        var port :int = (ports[(_portIdx + ppidx) % ports.length] as int);

        if (logonWasSuccessful) {
            _portIdx = -1; // indicate that we're no longer trying new ports

        } else {
            Log.getLog(this).info(
                "Connecting [host=" + host + ", port=" + port + "].");
            _socket.connect(host, port);
        }

        return true;
    }

    protected function removeListeners () :void
    {
        _socket.removeEventListener(Event.CONNECT, socketOpened);
        _socket.removeEventListener(IOErrorEvent.IO_ERROR, socketError);
        _socket.removeEventListener(Event.CLOSE, socketClosed);

        _frameReader.removeEventListener(FrameAvailableEvent.FRAME_AVAILABLE,
            inputFrameReceived);
    }

    public function logoff () :void
    {
        if (_socket == null) {
            return;
        }

        sendMessage(new LogoffRequest());

        shutdown(null);
    }

    public function postMessage (msg :UpstreamMessage) :void
    {
        sendMessage(msg); // send it now: we have no out queue
    }

    protected function shutdown (logonError :Error) :void
    {
        if (_socket != null)  {
            try {
                _socket.close();
            } catch (err :Error) {
                Log.getLog(this).warning("Error closing failed socket [error=" + err + "].");
            }
            removeListeners();
            _socket = null;
            _outStream = null;
            _inStream = null;
            _frameReader = null;
            _outBuffer = null;
        }

        _client.notifyObservers(ClientEvent.CLIENT_DID_LOGOFF, null);
        _client.cleanup(logonError);
    }

    protected function sendMessage (msg :UpstreamMessage) :void
    {
        if (_outStream == null) {
            Log.getLog(this).warning("No socket, dropping msg [msg=" + msg + "].");
            return;
        }

        // write the message (ends up in _outBuffer)
        _outStream.writeObject(msg);

//        Log.debug("outBuffer: " + StringUtil.unhexlate(_outBuffer));

        // Frame it by writing the length, then the bytes.
        // We add 4 to the length, because the length is of the entire frame
        // including the 4 bytes used to encode the length!
        _socket.writeInt(_outBuffer.length + 4);
        _socket.writeBytes(_outBuffer);
        _socket.flush();

        // clean up the output buffer
        _outBuffer.length = 0;
        _outBuffer.position = 0;

        // make a note of our most recent write time
        updateWriteStamp();
    }

    /**
     * Returns the time at which we last sent a packet to the server.
     */
    internal function getLastWrite () :uint
    {
        return _lastWrite;
    }

    /**
     * Makes a note of the time at which we last communicated with the server.
     */
    internal function updateWriteStamp () :void
    {
        _lastWrite = flash.utils.getTimer();
    }

    /**
     * Called when a frame of data from the server is ready to be
     * decoded into a DownstreamMessage.
     */
    protected function inputFrameReceived (event :FrameAvailableEvent) :void
    {
        // convert the frame data into a message from the server
        var frameData :ByteArray = event.getFrameData();
        //Log.debug("length of in frame: " + frameData.length);
        //Log.debug("inBuffer: " + StringUtil.unhexlate(frameData));
        _inStream.setSource(frameData);
        var msg :DownstreamMessage;
        try {
            msg = (_inStream.readObject() as DownstreamMessage);
        } catch (e :Error) {
            var log :Log = Log.getLog(this);
            log.warning("Error processing downstream message: " + e);
            log.logStackTrace(e);
            return;
        }

        if (frameData.bytesAvailable > 0) {
            Log.getLog(this).warning(
                "Beans! We didn't fully read a frame, surely there's " +
                "a bug in some streaming code. " +
                "[bytesLeftOver=" + frameData.bytesAvailable + "].");
        }

        if (_omgr != null) {
            // if we're logged on, then just do the normal thing
            _omgr.processMessage(msg);
            return;
        }

        // Otherwise, this would be the AuthResponse to our logon attempt.
        var rsp :AuthResponse = (msg as AuthResponse);
        var data :AuthResponseData = rsp.getData();
        if (data.code !== AuthResponseData.SUCCESS) {
            shutdown(new Error(data.code));
            return;
        }

        // logon success
        _omgr = new ClientDObjectMgr(this, _client);
        _client.setAuthResponseData(data);
    }

    /**
     * Called when the connection to the server was successfully opened.
     */
    protected function socketOpened (event :Event) :void
    {
        logonToPort(true);
        // well that's great! let's logon
        var req :AuthRequest = new AuthRequest(
            _client.getCredentials(), _client.getVersion(), _client.getBootGroups());
        sendMessage(req);
    }

    /**
     * Called when there is an io error with the socket.
     */
    protected function socketError (event :IOErrorEvent) :void
    {
        // if we're trying ports, try the next one.
        if (_portIdx != -1) {
            _portIdx++;
            if (logonToPort()) {
                return;
            }
        }

        // total failure
        Log.getLog(this).warning("socket error: " + event + ", target=" + event.target);
        Log.dumpStack();
        shutdown(new Error("socket closed unexpectedly."));
    }

    /**
     * Called when the connection to the server was closed.
     */
    protected function socketClosed (event :Event) :void
    {
        Log.getLog(this).warning("socket was closed: " + event);
        _client.notifyObservers(ClientEvent.CLIENT_CONNECTION_FAILED);
        logoff();
    }

    protected var _client :Client;
    protected var _omgr :ClientDObjectMgr;

    protected var _outBuffer :ByteArray;
    protected var _outStream :ObjectOutputStream;

    protected var _inStream :ObjectInputStream;
    protected var _frameReader :FrameReader;

    protected var _socket :Socket;

    protected var _lastWrite :uint;

    /** The current port we'll try to connect to. */
    protected var _portIdx :int = -1;
}
}

//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.presents.client {

import flash.errors.IOError;

import flash.events.Event;
import flash.events.IOErrorEvent;

import flash.net.Socket;

import flash.utils.ByteArray;
import flash.utils.Endian;

import com.threerings.util.Log;

import com.threerings.io.FrameAvailableEvent;
import com.threerings.io.FrameReader;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;

import com.threerings.presents.data.AuthCodes;
import com.threerings.presents.net.AuthRequest;
import com.threerings.presents.net.AuthResponse;
import com.threerings.presents.net.AuthResponseData;
import com.threerings.presents.net.DownstreamMessage;
import com.threerings.presents.net.LogoffRequest;
import com.threerings.presents.net.UpstreamMessage;

public class Communicator
{
    public function Communicator (client :Client)
    {
        _client = client;
    }

    public function logon () :void
    {
        // create our input/output business
        _outBuffer = new ByteArray();
        _outBuffer.endian = Endian.BIG_ENDIAN;
        _outStream = new ObjectOutputStream(_outBuffer);

        _inStream = new ObjectInputStream();

        _portIdx = 0;
        logonToPort();
    }

    /**
     * This method is strangely named, and it does two things which is
     * bad style. Either log on to the next port, or save that the port
     * we just logged on to was a good one.
     */
    protected function logonToPort (logonWasSuccessful :Boolean = false) :Boolean
    {
        var ports :Array = _client.getPorts();

        if (!logonWasSuccessful) {
            if (_portIdx >= ports.length) {
                return false;
            }
            if (_portIdx != 0) {
                _client.reportLogonTribulations(
                    new LogonError(AuthCodes.TRYING_NEXT_PORT, true));

                removeListeners();
            }

            // create the socket and set up listeners
            _socket = new Socket();
            _socket.endian = Endian.BIG_ENDIAN;
            _socket.addEventListener(Event.CONNECT, socketOpened);
            _socket.addEventListener(IOErrorEvent.IO_ERROR, socketError);
            _socket.addEventListener(Event.CLOSE, socketClosed);

            _frameReader = new FrameReader(_socket);
            _frameReader.addEventListener(FrameAvailableEvent.FRAME_AVAILABLE,
                inputFrameReceived);
        }

        var host :String = _client.getHostname();
        var pportKey :String = host + ".preferred_port";
        var pport :int = ports[0];
        var ppidx :int = Math.max(0, ports.indexOf(pport));
        var port :int = (ports[(_portIdx + ppidx) % ports.length] as int);

        if (logonWasSuccessful) {
            _portIdx = -1; // indicate that we're no longer trying new ports

        } else {
            Log.getLog(this).info(
                "Connecting [host=" + host + ", port=" + port + "].");
            _socket.connect(host, port);
        }

        return true;
    }

    protected function removeListeners () :void
    {
        _socket.removeEventListener(Event.CONNECT, socketOpened);
        _socket.removeEventListener(IOErrorEvent.IO_ERROR, socketError);
        _socket.removeEventListener(Event.CLOSE, socketClosed);

        _frameReader.removeEventListener(FrameAvailableEvent.FRAME_AVAILABLE,
            inputFrameReceived);
    }

    public function logoff () :void
    {
        if (_socket == null) {
            return;
        }

        sendMessage(new LogoffRequest());

        shutdown(null);
    }

    public function postMessage (msg :UpstreamMessage) :void
    {
        sendMessage(msg); // send it now: we have no out queue
    }

    protected function shutdown (logonError :Error) :void
    {
        if (_socket != null)  {
            try {
                _socket.close();
            } catch (err :Error) {
                Log.getLog(this).warning("Error closing failed socket [error=" + err + "].");
            }
            removeListeners();
            _socket = null;
            _outStream = null;
            _inStream = null;
            _frameReader = null;
            _outBuffer = null;
        }

        _client.notifyObservers(ClientEvent.CLIENT_DID_LOGOFF, null);
        _client.cleanup(logonError);
    }

    protected function sendMessage (msg :UpstreamMessage) :void
    {
        if (_outStream == null) {
            Log.getLog(this).warning("No socket, dropping msg [msg=" + msg + "].");
            return;
        }

        // write the message (ends up in _outBuffer)
        _outStream.writeObject(msg);

//        Log.debug("outBuffer: " + StringUtil.unhexlate(_outBuffer));

        // Frame it by writing the length, then the bytes.
        // We add 4 to the length, because the length is of the entire frame
        // including the 4 bytes used to encode the length!
        _socket.writeInt(_outBuffer.length + 4);
        _socket.writeBytes(_outBuffer);
        _socket.flush();

        // clean up the output buffer
        _outBuffer.length = 0;
        _outBuffer.position = 0;

        // make a note of our most recent write time
        updateWriteStamp();
    }

    /**
     * Returns the time at which we last sent a packet to the server.
     */
    internal function getLastWrite () :uint
    {
        return _lastWrite;
    }

    /**
     * Makes a note of the time at which we last communicated with the server.
     */
    internal function updateWriteStamp () :void
    {
        _lastWrite = flash.utils.getTimer();
    }

    /**
     * Called when a frame of data from the server is ready to be
     * decoded into a DownstreamMessage.
     */
    protected function inputFrameReceived (event :FrameAvailableEvent) :void
    {
        // convert the frame data into a message from the server
        var frameData :ByteArray = event.getFrameData();
        //Log.debug("length of in frame: " + frameData.length);
        //Log.debug("inBuffer: " + StringUtil.unhexlate(frameData));
        _inStream.setSource(frameData);
        var msg :DownstreamMessage;
        try {
            msg = (_inStream.readObject() as DownstreamMessage);
        } catch (e :Error) {
            var log :Log = Log.getLog(this);
            log.warning("Error processing downstream message: " + e);
            log.logStackTrace(e);
            return;
        }

        if (frameData.bytesAvailable > 0) {
            Log.getLog(this).warning(
                "Beans! We didn't fully read a frame, surely there's " +
                "a bug in some streaming code. " +
                "[bytesLeftOver=" + frameData.bytesAvailable + ", msg=" + msg + "].");
        }

        if (_omgr != null) {
            // if we're logged on, then just do the normal thing
            _omgr.processMessage(msg);
            return;
        }

        // Otherwise, this would be the AuthResponse to our logon attempt.
        var rsp :AuthResponse = (msg as AuthResponse);
        var data :AuthResponseData = rsp.getData();
        if (data.code !== AuthResponseData.SUCCESS) {
            shutdown(new Error(data.code));
            return;
        }

        // logon success
        _omgr = new ClientDObjectMgr(this, _client);
        _client.setAuthResponseData(data);
    }

    /**
     * Called when the connection to the server was successfully opened.
     */
    protected function socketOpened (event :Event) :void
    {
        logonToPort(true);
        // well that's great! let's logon
        var req :AuthRequest = new AuthRequest(
            _client.getCredentials(), _client.getVersion(), _client.getBootGroups());
        sendMessage(req);
    }

    /**
     * Called when there is an io error with the socket.
     */
    protected function socketError (event :IOErrorEvent) :void
    {
        // if we're trying ports, try the next one.
        if (_portIdx != -1) {
            _portIdx++;
            if (logonToPort()) {
                return;
            }
        }

        // total failure
        Log.getLog(this).warning("socket error: " + event + ", target=" + event.target);
        Log.dumpStack();
        shutdown(new Error("socket closed unexpectedly."));
    }

    /**
     * Called when the connection to the server was closed.
     */
    protected function socketClosed (event :Event) :void
    {
        Log.getLog(this).warning("socket was closed: " + event);
        _client.notifyObservers(ClientEvent.CLIENT_CONNECTION_FAILED);
        logoff();
    }

    protected var _client :Client;
    protected var _omgr :ClientDObjectMgr;

    protected var _outBuffer :ByteArray;
    protected var _outStream :ObjectOutputStream;

    protected var _inStream :ObjectInputStream;
    protected var _frameReader :FrameReader;

    protected var _socket :Socket;

    protected var _lastWrite :uint;

    /** The current port we'll try to connect to. */
    protected var _portIdx :int = -1;
}
}

Include the offending message when reporting a short frame

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


package aerys.minko.render.material.sprite
{
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.ShaderSettings;
	import aerys.minko.render.shader.part.DiffuseShaderPart;
	import aerys.minko.type.enum.Blending;
	import aerys.minko.type.enum.DepthTest;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerMipMapping;
	import aerys.minko.type.enum.SamplerWrapping;

	public class SpriteShader extends Shader
	{
		private var _uv	: SFloat	= null;

		public function SpriteShader()
		{
			super();
		}

		override protected function initializeSettings(settings : ShaderSettings) : void
		{
			settings.blending = Blending.ALPHA;
			settings.depthTest = DepthTest.LESS;
		}

		override protected function getVertexPosition() : SFloat
		{
			var vertexXY	: SFloat	= vertexXY.xy;

			_uv = multiply(add(vertexXY, 0.5), float2(1, -1));

			var depth	: SFloat 	= meshBindings.getParameter('depth', 1);

			var xy		: SFloat	= float2(
				meshBindings.getParameter('x', 1),
				meshBindings.getParameter('y', 1)
			);
			var size	: SFloat	= float2(
				meshBindings.getParameter('width', 1),
				meshBindings.getParameter('height', 1)
			);
			var vpSize	: SFloat	= float2(
				sceneBindings.getParameter('viewportWidth', 1),
				sceneBindings.getParameter('viewportHeight', 1)
			);

			xy = divide(add(xy, divide(size, 2)), vpSize);
			xy = multiply(subtract(xy, 0.5), float2(2, -2));

			vertexXY.scaleBy(divide(size, divide(vpSize, 2)));
			vertexXY.incrementBy(xy);

			return float4(vertexXY, depth, 1);
		}

		override protected function getPixelColor() : SFloat
		{
			var diffuseMap	: SFloat = meshBindings.getTextureParameter(
				'diffuseMap',
				meshBindings.getConstant("diffuseFiltering", SamplerFiltering.LINEAR),
				meshBindings.getConstant("diffuseMipMapping", SamplerMipMapping.LINEAR),
				meshBindings.getConstant("diffuseWrapping", SamplerWrapping.REPEAT)
			);

			return sampleTexture(diffuseMap, interpolate(_uv));
		}
	}
}

package aerys.minko.render.material.sprite
{
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.ShaderSettings;
	import aerys.minko.render.shader.part.DiffuseShaderPart;
	import aerys.minko.type.enum.Blending;
	import aerys.minko.type.enum.DepthTest;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerMipMapping;
	import aerys.minko.type.enum.SamplerWrapping;

	public class SpriteShader extends Shader
	{
        private var _diffuse    : DiffuseShaderPart;

		private var _uv	        : SFloat;

		public function SpriteShader()
		{
			super();

            _diffuse = new DiffuseShaderPart(this);
		}

		override protected function initializeSettings(settings : ShaderSettings) : void
		{
//			settings.blending = Blending.ALPHA;
			settings.depthTest = DepthTest.LESS;
		}

		override protected function getVertexPosition() : SFloat
		{
			var vertexXY	: SFloat	= vertexXY.xy;

			_uv = multiply(add(vertexXY, 0.5), float2(1, -1));

			var depth	: SFloat 	= meshBindings.getParameter('depth', 1);

			var xy		: SFloat	= float2(
				meshBindings.getParameter('x', 1),
				meshBindings.getParameter('y', 1)
			);
			var size	: SFloat	= float2(
				meshBindings.getParameter('width', 1),
				meshBindings.getParameter('height', 1)
			);
			var vpSize	: SFloat	= float2(
				sceneBindings.getParameter('viewportWidth', 1),
				sceneBindings.getParameter('viewportHeight', 1)
			);

			xy = divide(add(xy, divide(size, 2)), vpSize);
			xy = multiply(subtract(xy, 0.5), float2(2, -2));

			vertexXY.scaleBy(divide(size, divide(vpSize, 2)));
			vertexXY.incrementBy(xy);

			return float4(vertexXY, depth, 1);
		}

		override protected function getPixelColor() : SFloat
		{
			return _diffuse.getDiffuseColor(true, _uv);
		}
	}
}

update SpriteShader to use DiffuseShaderPart.getDiffuseColor() with the new 'uv' optional argument