DAN ZEN EXPO - CODE EXHIBIT - ROBIN

package samples{

	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.*;
	import flash.utils.Timer;
	import flash.filters.*;
	import flash.geom.Rectangle;

	// see the Robin.as file located at the path below for instructions on installing classes

	import com.danzen.utilities.Robin;

	// Robin has a simple concept but is very powerful
	// to get the most out of Robin, please read every worm - oops, word of this description 
	
	// ROBIN INTRODUCTION  
	// Robin is a custom Flash AS3 class with supporting NodeJS to handle realtime communication
	// realtime communication is used for chats and multiuser games with avatars, etc.
	// http://robinflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// The backend was initially PHP but was switched over to NodeJS for http://droner.mobi
	// if you are using Robin for commercial purposes, you are welcome to donate to Dan Zen
	// donations can be made to agency@danzen.com at http://www.paypal.com
	
	// ROBIN OVERVIEW  
	// this is the Flash overview - you have to have set up the NodeJS side too.
	// Robin works by distributing properties (strings or numbers)
	// you can have any number of properties and you make up their (single word) names
	// you create a Robin object and set events for connection and incoming data
	// then you set properties and read other people's matching properties
	// for instance, you can set your x, y and text properties
	// then you can read other people's x, y and text properties
	// you use Robin's setProperty() or setProperties() methods to set properties
	// you use Robin's getProperties() or getSenderProperty() methods to get properties
	
	// COMPARATIVE NOTE  
	// Robin is different than the SharedObject class in Flash
	// the SharedObject class lets you share the same properties
	// Robin lets each person have unique properties
	// However, you can use the latest values to emulate SharedObject functionality with Robin
	// Union (formerly Unity) at http://www.unionplatform.com provides an alternative
	// Union uses Java in the backend and Robin uses NodeJS
	// Union has more structure whereas Robin is just setting and getting properties
	
	// ROOMS WITH ROBIN  
	// there is no official keyword called rooms but it is a good analogy for chats, etc.
	// the properties you can set and get with Robin are for your "room" only
	// when you create the Robin object, the first three parameters affect the room
	// 1. the first parameter is the application name so this in a sense sets up the "house"
	// you make up this name and it will probably just be the name of your app
	// 2. the second parameter is how many people are allowed in a "room"
	// this defaults to 1000000 so if you leave it out then it is like one big room
	// setting 3 as the maximum per room means that people are added to "rooms" of no more than 3
	// more rooms are created as more people join
	// 3. the third parameter tells Robin how to fill the rooms
	// sending true means that Robin fills in places where people have left
	// sending false means that Robin always adds people to the latest room to be created
	// this defaults to true (filling vacated places) which would be the most common way
	// but if you had a game where four people are playing and one loses and leaves
	// you may not want to fill in a new person before the game has reached conclusion
	// rooms are always removed along with the room data when the last person leaves the room
	// getProperties() may still have a length even with no room data
	// NOTE: you should always call the numPeople property to get the number of people in your room
	
	// SETTING PROPERTIES 
	// any property that you set on Robin gets sent to the server
	// the server updates the property then distributes all the properties to everyone else in the same room
	// you can set a single property or you can set multiple properties at once
	// both of these will send an update to everyone else in the room
	// so be careful - if you update two properties individually, two updates get sent
	// if you have assigned the Robin object to a variable called myRobin then use:
		// myRobin.setProperty("x", 27); 
	// this will send your updated x property value to the server
	// the server will then distribute an update with all the properties including your new x property 
	// this update goes to all the people in the room except you
	// to send x, y and text properties at the same time use:
		// myRobin.setProperties({x:27, y:32, text:"hello worms"});
	// the server will then distribute an update with all the properties including your changed ones
	// note that we use an Object literal above to specify properties - optionally you could use:
		// var myObject:Object = new Object();
		// myObject.x = 27;
		// myObject.y = 32;
		// myObject.text = "hello worms";
		// myRobin.setProperties(myObject);
		
	// RECEIVING PROPERTIES	 
	// let's assume that you are setting x, y and text properties on Robin
	// you can get the value of your x property, for instance, like so:
		// myRobin.getProperty("x");
	// Robin's DataEvent.DATA event is triggered when someone else in your room sets a property
	// you can then check for current values of other people in the room's properties	
	// here is the code to get everyone in the room's x property (except yours):
		// myRobin.getProperties("x");
	// this returns an Array of everyone in your room's x property (except yours)
	// you can find out an Array of names of the properties that are available like so:
		// myRobin.getPropertyNames();
	// someone must have changed a property for your data event to be triggered
	// here is how you find out the index number of that someone - the sender:
		// myRobin.senderIndex;
	// so you could find out the sender's x property like so:
		// myRobin.getProperties("x")[myRobin.senderIndex]; // but use below instead
	// or you can use the getSenderProperty() method instead like so:
		// myRobin.getSenderProperty("x");
	// all the properties per room are always received by the data event (except yours)
	// this takes up extra bandwidth - but it keeps things simple
	// also, you always know that the data is coming from the server and is current
	// Always use the numPeople property to find out how many people are in the room
	
	// LATEST VALUES  
	// with the getLatestValue() method you can find out the last value distributed for a property
	// this might include your value
	// this value can be used to emulate the Flash remote SharedObject functionality
	// for instance, to let everyone control the x and y position of a ball as well as their own x and y
	// you could set x and y properties on Robin for yourself and ballX and ballY for the ball
	// for the ball, you would always position it to the latest values of ballX and ballY like so:
		// ball.x = myRobin.getLatestValue("ballX");
		// ball.y = myRobin.getLatestValue("ballY");	
	// you can get the index of the sender who changed the latest value like so:
		// getLatestValueIndex("ballX"); // returns a number
	// this returns the index of the sender who updated ballX or -1 if you were the last to update ballX
	// you can get an Array of the latest properties that were updated
		// getLatestProperties(); // returns an Array of property names as strings
	
	// HISTORY  
	// you will always get the current properties from the server each data trigger
	// but sometimes you want a history of what has transpired
	// this could be a leading scorer or the last 20 messages in a chatroom, etc.
	// Robin puts the contents of the history file into a history property at connect time
		// myRobin.history; // this is only updated at connect time
	// You can append to and clear a history file that is shared by the room like so:
		// myRobin.appendToHistory("a line of text\n"); // note that you need to add the \n for new lines
		// myRobin.clearHistory(); 
	// the clearHistory() clears the history for new people coming to the room
	// it does not clear the history property for the people already in the room
	// and of course anyone can appendToHistory() after it is cleared		


	public class AvatarsChoice extends MovieClip {

		private var myRobin:Robin;
		private var myAvatar:MovieClip;
		private var myTimer:Timer;
		private var totalAvatars:Number;
		private var players:Array=[];
		private var myFrame:Number;
		private var myX:Number;
		private var myY:Number;

		public function AvatarsChoice():void {

			trace("hi from AvatarsChoice");

			myAvatar = new Avatar();
			totalAvatars=myAvatar.totalFrames;

			var myMenu:MovieClip = new MovieClip();
			var choice:Avatar;
			var spacing:Number=90;
			for (var i:uint=1; i<=totalAvatars; i++) {
				choice = new Avatar();				
				choice.gotoAndStop(i);
				choice.x=i*spacing;
				choice.y=100;
				choice.alpha=.5;
				myMenu.addChild(choice);
			}
			myMenu.buttonMode=true;
			myMenu.addEventListener(MouseEvent.CLICK, doChoice);
			myMenu.addEventListener(MouseEvent.MOUSE_OVER, function(e:MouseEvent):void {e.target.alpha=.7;});
			myMenu.addEventListener(MouseEvent.MOUSE_OUT, function(e:MouseEvent):void {e.target.alpha=.5;});
			myMenu.x=-60;
			addChild(myMenu);

		}

		private function doChoice(e:MouseEvent):void {
			myFrame=e.currentTarget.getChildIndex(MovieClip(e.target))+1;
			myX=e.target.x-60;
			myY=e.target.y;
			myRobin=new Robin("RobinAvatarsChoice");
			myRobin.addEventListener(Event.CONNECT, init);
			myRobin.addEventListener(IOErrorEvent.IO_ERROR, doError); // connect problems			
		}

		private function init(e:Event):void {
			trace("Connected!");
			
			myRobin.addEventListener(DataEvent.DATA, receiveData);
			myRobin.addEventListener(Robin.CLIENT_JOINED, playerJoined);
			myRobin.addEventListener(Robin.CLIENT_LEFT, playerLeft);

			removeChildAt(1);// Robin icon is child 0
			var padding:Number=100;
			myAvatar.gotoAndStop(myFrame);
			myAvatar.x=myX;
			myAvatar.y=myY;
			myAvatar.buttonMode=true;
			myAvatar.filters=[new DropShadowFilter(4,45,0,.7,10,10,1,3)];
			addChild(myAvatar);

			// record the avatar frame, x and y in Robin
			myRobin.setProperties({avatar:myFrame, x:myX, y:myY});

			// place any other avatars
			placeOthers();

			myAvatar.addEventListener(MouseEvent.MOUSE_DOWN, dragMe);
			
		}
		
		private function dragMe(e:MouseEvent):void {
			stage.addEventListener(MouseEvent.MOUSE_MOVE, moveMe);
			stage.addEventListener(MouseEvent.MOUSE_UP, dropMe);
			myAvatar.startDrag(false,new Rectangle(0,0,stage.stageWidth-myAvatar.width,stage.stageHeight-myAvatar.height));
		}

		private function moveMe(e:MouseEvent):void {
			myRobin.setProperties({x:myAvatar.x, y:myAvatar.y});
		}

		private function dropMe(e:MouseEvent):void {
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, moveMe);
			stage.removeEventListener(MouseEvent.MOUSE_UP, dropMe);
			myAvatar.stopDrag();
			myRobin.setProperties({x:myAvatar.x, y:myAvatar.y});
		}

		private function receiveData(e:Event):void {
			moveOthers();
		}
		
		private function playerJoined(e:Event):void {
			var i:Number = myRobin.lastIndexToJoin;
			players[i] = new Avatar();			
			players[i].gotoAndStop(Number(myRobin.getSenderProperty("avatar")));
			addChild(players[i]);
			addChild(myAvatar); // put ours on top
		}
		
		private function playerLeft(e:Event):void {
			var i:Number = myRobin.lastIndexToLeave;
			if (players[i]) {
				removeChild(players[i]);
				players[i]=null;
			}
		}

		private function placeOthers():void {
			
			if (myRobin.numPeople>0) {
				var xProp:String;// x of an avatar
				var yProp:String;// y of an avatar
				var aProp:String;// frame number of an avatar
				
				// there could be blanks in getProperties from people leaving
				// so always cycle through the full length of getProperties
				// instead of cycling through numPeople

				for (var i:uint=0; i<myRobin.getProperties("x").length; i++) {
					trace ("here " + i);
					xProp=myRobin.getProperties("x")[i];
					yProp=myRobin.getProperties("y")[i];
					aProp=myRobin.getProperties("avatar")[i];					
					trace ("here2 " + i);
					if (xProp!="") { // make sure non blank						
						players[i] = new Avatar();											
						players[i].x=Number(xProp);
						players[i].y=Number(yProp);
						players[i].gotoAndStop(Number(aProp));
						addChild(players[i]);	
					} 					
				}
				addChild(myAvatar);
			} 			
		}
				
		private function moveOthers():void {
			if (myRobin.numPeople>0) {
				var xProp:String = myRobin.getSenderProperty("x");
				var yProp:String = myRobin.getSenderProperty("y");		
				if (xProp!="") {						
					players[myRobin.senderIndex].x=Number(xProp);
					players[myRobin.senderIndex].y=Number(yProp);
				}				
			} 			
		}		
		
		private function doError(e:Event):void {
			trace ("error connecting to robinServer");
		}		
	}
}

package com.danzen.utilities {
	
	// ROBIN INTRODUCTION  
	// Robin is a custom Flash AS3 class with supporting NodeJS to handle realtime communication
	// realtime communication is used for chats and multiuser games with avatars, etc.
	// http://robinflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// The backend was initially PHP but was switched over to NodeJS for http://droner.mobi
	// if you are using Robin for commercial purposes, you are welcome to donate to Dan Zen
	// donations can be made to agency@danzen.com at http://www.paypal.com
	
	// INSTALLING CLASSES
	// suggested installation:
	// create a "classes" folder on your hard drive - for example c:\classes
	// add the classes folder to your Flash class path:
	// Flash menu choose Edit > Preferences > ActionScript - ActionScript 3 Settings 
	// then use the + sign for the source path box to add the path to your classes folder	
	// put the provided com/ directory with its folders and files in the classes folder
	// the readme has more information if you need it	
		
	// USING ROBIN
	// please make sure that com/danzen/utilities/Robin.as is in a folder in your class path
	// set the configuration below to match the robinServer.js file port
	// make sure you run your nodeJS as recommended
	// the Flash side will not connect unless you have nodeJS running properly
	// there is a list of methods, events and properties below for reference
	// see the example files that come with Robin to see how to work with Robin		

	// CONFIGURATION
	// the myServer variable must match the domain that is hosting your robinServer (no http://)
	// you can set myServer to "localhost" if you are running nodeJS locally
	// the myPort variable needs to match what you set in the robinServer.js for the port
			
	
    import flash.display.Sprite;
    import flash.net.XMLSocket;
    import flash.system.Security;
    import flash.utils.Timer;
	import flash.events.*;
	
	public class Robin extends Sprite {		

		// just put your server domain without http:// for example www.danzen.com
		// see RobinNest.zip for examples to run on your local host NodeJS server
		
		//private var myServer:String = "127.0.0.1";  	
		private var myServer:String = "54.209.193.48"; 
		
		//private var myServer:String; // now obtained with constructor parameter
		private var myPort:Number = 8081;
		
		// see the example files that call the Robin class
		
		// CONSTRUCTOR
		// Robin(theApplicationName:String, theMaxPeople:uint = 0, theFill:Boolean = true, theStartProperties:Object = null):void
		//		constructor to connect your application to the server.js Node socket for multiuser functionality
		//		server.js must be running for this to work.  It works in your Flash authoring tool too (AS3)
		//		PARAMETERS:
		//			theApplicationName:String
		//				you make this up - it should be one word (or camel case) and probably unique for your app
		//			theMaxPeople:uint
		//				how many people are allowed per "room" - there are as many rooms as needed - 0 is virtually unlimited
		//			theFill:Boolean
		//				if someone leaves a room - set to true to fill in their location with the next person to join
		//		EXAMPLES:
		//			at its simplest making a new Robin object might look like this: 
		//				var myRobin:Robin = new Robin("FreezeTag");
		//			or you could add some limitations: 
		//				var myRobin:Robin = new Robin("FreezeTag", 10, false);		
		
		// EVENTS
		// IOErrorEvent.IO_ERROR
		//		trouble connecting - make sure server.js is running and you have the right domain and port (see CONFIGURATION)
		// Event.CONNECT
		//		connected to socket - you can set up your application to submit and receive data, etc.
		// DataEvent.DATA
		//		dispatched when someone in the room makes a change (not including you)
		// Event.CLOSE
		//		the socket is closed - could be that server.js stops for some reason - all data on the server will be lost
		// Robin.CLIENT_JOINED
		//		another client (not you) joined - can find out with lastIndexToJoin property
		// Robin.CLIENT_LEFT
		//		another client (not you) left - can find out with lastIndexToLeave property
				
		// METHODS
		// setProperty(propertyName:String, propertyValue:Object):void
		// 		sets your property to the value and sends out change to all in room (distributes)
		// setProperties(objectOfPropertiesToSet:Object):void
		//		pass in an object with properties and values and it sets yours to match and distributes them
		// getProperty(propertyName:String):String
		//		returns your value for the property you pass to it
		// getProperties(propertyName:String):Array
		//		returns an array of everyone in the room's values for the property you pass to it (can include blanks! see numPeople property)
		// getPropertyNames():Array
		//		returns an array of all property names that have been distributed
		// getSenderProperty(propertyName:String):String
		//		returns the value of the property you pass it that belongs to the last person to distribute (not you)
		// getLatestValue(propertyName:String):String
		// 		returns the last distributed value for the property you pass to it - could be yours - could be null if person leaving
		// getLatestValueIndex(propertyName:String):Number
		//		returns the index number of the last person to distribute a value for the property you pass to it
		// getLatestProperties():Array
		//		returns an array of the last properties to be distributed (sometimes multiple properties are distributed at once)
		// appendToHistory(someText:String=""):void
		//		adds the text passed to it to the history file for the room (deleted if room is empty)
		// clearHistory():void
		//		deletes the history file for the room		
		// dispose():void
		//		removes listeners, closes connection to socket, deletes data objects
		
		// PROPERTIES (READ ONLY) Getter Methods at bottom		
		// applicationName:String - the name of your application
		// server:String - the server you set in the CONFIGURATION
		// port:Number - the port you set in the CONFIGURATION
		// maxPeople:Number - see CONSTRUCTOR
		// numPeople:Number - how many people are in the room - do not go by the length of a getProperties() array
		// fill:Boolean - see CONSTRUCTOR
		// senderIndex:Number - the index number of the last person to send out data
		// history:String - the history text for your room at the time of application start
		// lastIndexToJoin:Number - the index of the last client to join (other than you)
		// lastIndexToLeave:Number - the index of the last client to leave (other than you)
		
		// PUBLIC CONSTANTS
		public static const CLIENT_JOINED:String = "clientJoined";
		public static const CLIENT_LEFT:String = "clientLeft";
		
		// PRIVATE VARIABLES
		// getter methods allow you to get these properties (less the "my" prefix)
		private var myApplicationName:String;
		private var myMaxPeople:uint;
		private var myFill:Boolean;
		private var mySenderIndex:Number;
		private var myHistory:String = "";
		private var myLastIndexToJoin:Number;
		private var myLastIndexToLeave:Number;
		private var disposeCheck:Boolean = false;

		// public methods are available to get this data
		private var myData:Object = new Object();
		private var theirData:Object = new Object();
		private var latestValues:Object = new Object();
		private var latestIndexes:Object = new Object();
		private var latestProperties:Array = [];	
		
		// internal variables
		private var mySocket:XMLSocket;
		private var initializationCheck:Boolean = false;
		private var myTimer:Timer;				
		private var connectCheck:Boolean = false;		
		private var lastIndexes:Array = [];
		private var pingTimer:Timer;
		private var connectAttempt:Number = 0;
		private var errorCheck:Boolean = false;
		private var errorTimer:Timer;
					
		public function Robin(				
				theApplicationName:String,							  
				theMaxPeople:uint = 0, 
				theFill:Boolean = true,
				theServer:String = "127.0.0.1"
								) { 				
		
			trace ("hi from Robin");
			trace (theServer);
			myApplicationName = theApplicationName;	
			if (!myApplicationName.match(/^[a-zA-Z0-9_-]+$/)) {
				trace ("----------------------------------");
				trace ("Robin Application Name: \""+myApplicationName+"\"");
				trace ("Sorry - your application name must include only a-z, A-Z, numbers, _ or -");
				trace ("----------------------------------");
				return;
			}		

			myMaxPeople = (theMaxPeople == 0) ? 1000000 : theMaxPeople;			
			myFill = theFill;
			myServer = theServer;
						
			mySocket = new XMLSocket();
			mySocket.timeout = 10;
			var fixTimer:Timer = new Timer(100,1); // create a delay for any error events
			fixTimer.addEventListener(TimerEvent.TIMER_COMPLETE, fix);			
			fixTimer.start();
			
			errorTimer = new Timer(500, 1);
			errorTimer.addEventListener(TimerEvent.TIMER, doErrorTimer);
			
			pingTimer = new Timer(15*1000); // create ping to defeat server timeOut (set to 15 seconds)
			pingTimer.addEventListener(TimerEvent.TIMER, ping);	
		}
		
		// --------------------  PRIVATE METHODS  -------------------------------
		
		private function fix(e:TimerEvent):void {
			getPolicy();
			makeConnection();
		}
		
		private function ping(e:TimerEvent):void {
			if (mySocket) {
				mySocket.send("p"); // send ping to socket server
			}			
		}
				
		private function getPolicy():void {			
			Security.loadPolicyFile("xmlsocket://"+myServer+":"+myPort);
			mySocket.connect(myServer, myPort);
			mySocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, policyDelay);			
			mySocket.addEventListener(IOErrorEvent.IO_ERROR, function(e:Event):void{}); 
			mySocket.close();
		}		
		
		private function policyDelay(e:Event):void {
			// do not want to try and access socket before policy returns
			// so bounce errors to the makeConnection until the policy connects
			// Web issue only
			// errorCheck monitors how many times this has happened 
			// as there may be no socket connection to connect
			if (!errorCheck) {
				makeConnection();
			}
		}
		
		private function makeConnection():void {	
			mySocket.connect(myServer, myPort);
			mySocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, noConnect);
			mySocket.addEventListener(Event.CONNECT, handleMeta);			
			mySocket.addEventListener(Event.CLOSE, handleMeta);			
			mySocket.addEventListener(IOErrorEvent.IO_ERROR, handleMeta);			
			mySocket.addEventListener(DataEvent.DATA, incomingData);		
		}		
		
		private function noConnect(e:Event):void {
			connectAttempt++;
			if (connectAttempt > 40) {
				if (errorCheck) {return;}
				errorCheck = true;
				dispatchEvent(new Event(IOErrorEvent.IO_ERROR));
				// wait to dispose in case delayed security error
				errorTimer.start();
			}
		}
		
		private function doErrorTimer(e:TimerEvent):void {		
			dispose();
		}
		
		private function handleMeta(e:Event):void {	
			switch(e.type){
				case 'ioError': 
					//trace ("error");
					dispatchEvent(new Event(IOErrorEvent.IO_ERROR));
					if (pingTimer) {
						pingTimer.stop();
					}
				break;				
				case 'connect':
					if (!initializationCheck) {
						initializationCheck = true;										
						sendData();							
						pingTimer.start();
					}					
				break;				
				case 'close': 
					//trace ("close");					
					dispatchEvent(new Event(Event.CLOSE));
					dispose();
				break; 							
			} 
		}		
				
		
		private function incomingData(e:DataEvent):void {
			
			// GENERAL DATA FORMAT
			// HISTORY -~^~- LATEST -~^~- VARIABLES
			// HISTORY -~^~- LATEST -~^~- is only sent to new clients once at start
			// HISTORY -~^~- LATEST -~^~- is not sent to existing clients
			// VARIABLES are sent every time although ignored if client is leaving			
			
			// EXAMPLE FORMAT NEW CLIENT
			// history as string
			// -~^~-\n 			// delimeter then the latest users for the properties
			// _u_^1    		// the second user was last to update anything
			// text^0|value		// the first user was the last to update the text and its value
			// x^1|value		// the second user was the last to update x and y then its value
			// y^1|value
			// -~^~- 		// delimeter then the VARIABLES section - this shows sample for new client
			// -1 			// mySenderIndex is -1 as the client sent message to themself
			// 				// blank line for variables sent as nobody sent variables - client is new
			// _u_^12		// etc. for VARIABLES section (see later below for processing that section)
			
			
			// SPLIT DATA			
			
			var latestData:String
			var incomingData:String;
			
			//trace ("DATA:\n-----------\n"+e.data+"\n-----------");
			
			if (!String(e.data)) {return;}
			if (!connectCheck) {				
				var feed:Array = String(e.data).split("-~^~-\n");
				myHistory = decode(feed[0]).replace(/\n$/,"");
				latestData = feed[1].replace(/\n$/,"");
				incomingData = feed[2].replace(/\n$/,"");					
			} else {
				incomingData = String(e.data).replace(/\n$/,"");
			}			
			
			// VARIABLES FORMAT
			// 0 					// -1 if new user or -2 if a user is leaving
			// x|y 					// blank return if new user or a user is leaving
			// _u_^12|22			// could be blanks in here for users who have left
			// text^test|hello		// might be blanks in here for no updates from user or they left
			// x^27|30
			// y^25|200			
			
			// PROCESS VARIABLES 
			var lines:Array = incomingData.split("\n"); // line locked data
			mySenderIndex = Number(lines.shift());
			
			var updatedProperties:String = lines.shift();
			
			/* needs to be after we check
			if (getProperties("_u_")) {
				lastIndexes = getProperties("_u_").slice(0);
			}
			*/
			
			// for new clients and normal messages - not for leaving room
			//if (connectCheck == false || mySenderIndex > -1) {												
				theirData = {};
				var temp:Array;
				var prop:String;
				var temp2:Array;
				for (var i:uint=0; i<lines.length; i++) {
					if (lines[i] == "") {continue;}
					temp = lines[i].split("^");
					prop = temp[0];
					if (temp[1] != "") {	
						temp2 = temp[1].split("|");
						for (var j:uint=0; j<temp2.length; j++) {
							 temp2[j] = decode(temp2[j]);
						}
						theirData[prop] = temp2;
						//theirData[prop] = decode(temp[1]).split("|");
					}
				}
			//}					
						
			// PROCESS LATEST
			// if -2 values - someone has left
			
			if (connectCheck==false) { // new client get data from latestData
			
				// _u_^1    		// the second user was last to update anything
				// text^0|value		// the first user was the last to update the text and its value
				// x^1|value		// the second user was the last to update x and y then its value
				// y^1|value			
				
				latestProperties = [];
				var latestLines:Array = latestData.split("\n");				
				var latestVV:Array;
				
				for (var iii:uint=0; iii<latestLines.length; iii++) {					
					if (latestLines[iii] != "") {					
						latestVV = latestLines[iii].split("^"); // [_u_, 0|value] then [text, 0|value]						
						latestIndexes[latestVV[0]] = Number(latestVV[1].split("|")[0]);						
						latestValues[latestVV[0]] = String(latestVV[1].split("|")[1]);					
					}
				}				
			} else { // existing client				
				if (mySenderIndex > -1) {
					if (updatedProperties == "") {
						// should always be something for a normal send but just in case
					} else {				
						latestProperties = updatedProperties.split("|");
						for (var ii:uint=0; ii<latestProperties.length; ii++) {
							// already added all normal sender data to theirData[v]=[value|value|value]
							// already set mySenderIndex
							// getSenderProperty(property) gets the value at the mySenderIndex for the property							
							latestValues[latestProperties[ii]] = getSenderProperty(latestProperties[ii]);														
							latestIndexes[latestProperties[ii]] = mySenderIndex;
						}
					}	
				}
			}		
			
			if (connectCheck == false) { // client joined
				//trace("Robin - connect");	
				dispatchEvent(new Event(Event.CONNECT));	
				connectCheck = true;								
			} else if (mySenderIndex == -2) { // sender leaving - figure out who left					
				if (hasLeft()) {

					// set latestIndex belonging to leaving index to -2		
					for (var vari:String in latestIndexes) {
						if (latestIndexes[vari] == lastIndexToLeave) {
							latestIndexes[vari] = -2;
							// do not update values
							// this gives user choice to take a value of someone who left
							// for instance in a shared ball position
							// or ignore the last value if they check the index first
						}											
					}					
				}				
				dispatchEvent(new Event(Robin.CLIENT_LEFT));
			} else {
				if (hasJoined(mySenderIndex)) { // sender joined				
					// new client - dispatch CLIENT_JOINED
					// trace("Robin - Joined");
					dispatchEvent(new Event(Robin.CLIENT_JOINED));							
				}				
				dispatchEvent(new DataEvent(DataEvent.DATA, false, false, String(e.data)));				
			} 
			
			if (getProperties("_u_")) {
				lastIndexes = getProperties("_u_").slice(0);
			}
			if (mySenderIndex == -2) {				
				lastIndexes[lastIndexToLeave] = "";
			}
			
		}			
		
		private function hasLeft():Boolean {			
			var list:Array = [];
			if (!getProperties("_u_")) {
				for (var j:uint=0; j<lastIndexes.length; j++) {
					list.push("");
				}
			} else {
				list = getProperties("_u_");	
			}			
			for (var i:uint=0; i<lastIndexes.length; i++) {				
				if (lastIndexes[i] != "" && lastIndexes[i] != list[i]) {
					myLastIndexToLeave = i;
					return true;					
				}
			}			
			return false;					
		}
		
		private function hasJoined(clientIndex:Number):Boolean {		
			if (clientIndex >= lastIndexes.length || lastIndexes[clientIndex] == "") {
				myLastIndexToJoin = clientIndex;
				return true;
			} else {
				return false;
			}			
		}
		
		private function sendData(s:String=""):void {
			var f:Number;
			if (myFill) {f=1;} else {f=0;}
			var prefix:String = "%^%"+myApplicationName+"|"+myMaxPeople+"|"+f;		
			// %^%sample9|3|1^y=5^x=20
			// the %^% makes sure that if there are multiple entries in the socket buffer
			// that they can be separated in the server code
			if (mySocket.connected) {
				mySocket.send(prefix+s);			
			} else {
				//trace ("crash");
				dispatchEvent(new Event(Event.CLOSE));
				dispose();
			}
		}			
		
		private function encode(w:String):String {
			w = w.replace(/\r\n/g,"\n");
			w = w.replace(/\n\r/g,"\n");
			w = w.replace(/\n/g,"`~`");
			w = w.replace(/\^/g,"*`*");
			//w = w.replace(/\|/g,"^~^");
			w = w.replace(/\|/g,"%`%");
			return w;
		}
		
		private function decode(w:String):String {
			w = w.replace(/`~`/g,"\n");
			//w = w.replace(/^~^/g,"|");
			w = w.replace(/%`%/g,"|");
			w = w.replace(/\*`\*/g,"^");
			return w;
		}		
		
		// --------------------  PUBLIC METHODS  -------------------------------

		public function setProperty(n:String, v:Object):void {
			// set one of my properties at a time
			myData[n] = v;								
			sendData("^" + n + "=" + encode(String(v)));			
		}

		public function setProperties(o:Object):void {
			// set a bunch of my properties at once with an object
			var vars:String = "";
			for (var i:Object in o) {
				myData[i] = String(o[i]);
				vars += "^" + i + "=" + encode(String(o[i]));
			}						
			sendData(vars);			
		}

		public function getProperty(w:String):String {
			// get one of my properties
			return myData[w];			
		}
		
		public function getProperties(w:String):Array {
			// get an array of their properties
			return theirData[w];
		}
		
		public function getPropertyNames():Array {
			// get a list of their property names
			var props:Array =[];
			for (var i:String in theirData) {
				props.push(i);
			}
			return props;			
		}
		
		public function getSenderProperty(w:String):String {
			if (theirData[w] && theirData[w][mySenderIndex]) {
				return theirData[w][mySenderIndex];
			} else {
				return null;
			}
		}
				
		public function getLatestValue(w:String):String {				
			if (latestValues[w]) {
				return latestValues[w];
			} else {
				return null;
			}	
		}
		
		public function getLatestValueIndex(w:String):Number {			
			return Number(latestIndexes[w]);
		}
		
		public function getLatestProperties():Array {			
			return latestProperties;
		}				
				
		public function appendToHistory(t:String=""):void {
			sendData("^_history_=" + encode(String(t)));
		}		
		
		public function clearHistory():void {
			sendData("^_clearhistory_=");
		}			
		
		public function dispose():void {
			// avoid double disposing
			if (disposeCheck) {return;}
			disposeCheck = true;
			mySocket.removeEventListener(Event.CONNECT, handleMeta);					
			mySocket.removeEventListener(IOErrorEvent.IO_ERROR, handleMeta);			
			mySocket.removeEventListener(DataEvent.DATA, incomingData);
			mySocket.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, policyDelay);
			mySocket.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, noConnect);
			
			if (mySocket.connected) {
				mySocket.close();
			}			
			mySocket.removeEventListener(Event.CLOSE, handleMeta);	
			mySocket = null;
			
			pingTimer.stop();
			pingTimer.removeEventListener(TimerEvent.TIMER, ping);	
			pingTimer = null;
			
			myData = null;
			theirData = null;
			latestValues = null;
			latestIndexes = null;
			latestProperties = null;
			myHistory = null;
			
			trace ("bye from Robin");			
		}
		
		// ----------------------- PUBLIC READ ONLY PROPERTIES ---------------------
		
		public function get applicationName():String {
			return myApplicationName;
		}
		public function get server():String {
			return myServer;
		}
		public function get port():Number {
			return myPort;
		}
		public function get maxPeople():Number {
			return myMaxPeople;
		}
		public function get numPeople():Number {
			var list:Array = getProperties("_u_");
			var num:uint = 0;
			if (list != null && list.length > 0) {				
				for (var i:uint=0; i<list.length; i++) {
					if (list[i] != "") {
						num++;
					}
				}
			} 
			return num;					
		}
		public function get fill():Boolean {
			return myFill;
		}		
		public function get senderIndex():Number {
			return mySenderIndex;
		}
		public function get history():String {
			return myHistory;
		}	
		public function get lastIndexToJoin():Number {
			return myLastIndexToJoin;
		}	
		public function get lastIndexToLeave():Number {
			return myLastIndexToLeave;
		}	
			
	
	}
	
}

/* robin.js

----  DZ commented and refactored -------
then took all old code and comments out and called file robinServer.js

 * 
 * RobinJS
 * 
 * Created for Robin Flash / PHP Multiuser Solution
 * https://robinflash.wordpress.com/
 * 
 * @author Andrew Blackbourn blackbourna@gmail.com
 * @producer Dan Zen door@danzen.com
 *
 * */
 
ROBIN_LOGS = 'logs/debuglog.txt';

function RobinServer() {
      
	
	// DZ GENERAL SETUP VARIABLES AND FUNCTIONS
	var net = require('net');
    var fs = require('fs');
    var port = 8081;
	
    try {
		var env = JSON.parse(fs.readFileSync('/home/dotcloud/environment.json', 'utf-8'));
		port = env['PORT_NODE'];
	} catch (e) {}
    var debug = true;
    var splitter = "_______________________";
	var dzSplitter = "\n\n***********************";
     function debuglog(x) {
        if (debug) console.log(x);
    }
	// .length for property maps
    Object.size = function (obj) { 
        var size = 0,
            key;
        for (key in obj) {
            if (obj.hasOwnProperty(key)) size++;
        }
        return size;
    }
    function getKeys(map) {
        var keys = [];
        for (var k in map)
        keys.push(k);
        return keys;
    }
    function getArray(map) {
        var values = [];
        for (var k in map)
        values.push(map[k]);
        return values;
    }
    // can change port using cmd args
    // node filename.js -p 1234
    if (process.argv.length == 4) {
        if (process.argv[2].toUpperCase() == "-P" && !isNaN(process.argv[3])) {
            port = process.argv[3];
        }
    }
    // from http://stackoverflow.com/questions/646628/javascript-startswith
    if (typeof String.prototype.startsWith != 'function') {
        String.prototype.startsWith = function (str) {
            return this.indexOf(str) == 0;
        };
    }
	// append null when writing to socket
    net.Socket.prototype.sendMessage = function (x) {
		try {
			if (this.write) this.write(x + "\0");			
        } catch (e) {
			console.log(e);
		}
    }

	
	
	// DZ MAIN CODE	
	// ApplicationPool holds all the ApplicationInstance objects
	// ApplicationInstance objects hold a rooms arrays of Room objects
	// Room objects have sockets array of Socket objects
	// Room objects have data properties such as currentVariables and who set the data, etc. 
	// Room objects have a buildMessage() method that makes a relative message to send to each Socket
	// Room objects have a broadcast() method that calls buildMessage() and sends the messages
	// Socket objects have a sendMessage() method that sends data to the clients
	// Socket objects have an clientIndex and a clientID
	// Socket objects also store what ApplicationInstance and Room they are in
	// Socket objects hold data variables sent to them
	
	applicationPool = new ApplicationPool();	
	
	// DZ server has a data event that captures incoming socket data
	// DZ after some initialization we run processMessage()
	
    var server = net.createServer(function (socket) {
        socket.on('data', function (data) {
			
            //console.log(" INCOMING DATA: " + data.toString());
            // Flash initially requests an XML policy file
            if (data.toString().match("<policy-file-request/>")) {               
                var policy = "<?xml version=\"1.0\"?><cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>";
                this.sendMessage(policy);
                return;
            } else if (data.toString().startsWith("%^%")) {				
				// DZ used %^% to separate messages because in PHP
				// DZ because would sometimes get two "messages" in data send
                var messages = data.toString().split('%^%');
                for (var m in messages) {
                   if (messages[m].length > 0) processMessage(messages[m], socket);
                }
            } else {
                socket.write("INVALID HEADER");
            }
        });
    }); // end net.createServer
	
	

    function processMessage(data, socket) { // after policy handshake, will begin to recieve messages from client
		// %^%AppName|maxInRoom|fillType^property=value^property=value
        // e.g. 		
        // %^%RobinBall|10|1^y=147^x=211.95
        var message = data.split("|");
        // message[0]=RobinBall
        // message[1]=10
        // message[2]=1^y=147^x=211.95
        for (var m in message) { // remove null terminators
            message[m] = message[m].replace("\u0000", "");
        }
		
        var appName = message[0];
        var maxPerRoom = message[1];
        // split name/val pairs
		var variableMessages = message[2].split("^"); // DZ - refactored below
		var fillType = variableMessages.shift(); // DZ - refactored below
		
        //var variableIndex = message[2].indexOf("^");
        //var fillType = message[2].substring(0, 1);
        //var variableMessages = (variableIndex > -1) ? message[2].substring(variableIndex).split("^").slice(1) : [];
		
		console.log(dzSplitter);
		
        if (!socket.room) { // set up new user (new user will never be last sender, etc.)
			
			console.log("MESSAGE IN: New Socket");
			
            var message = '';			 						
            socket.application = applicationPool.getApplication(socket, appName, maxPerRoom, fillType);  
			
			socket.variables = {};
            if (socket.room.history.length > 0) {
                message += socket.room.history + "\n";
            }           
            message += '-~^~-\n';
			
			/* DZ - summary of LAST section (after History and before variables)			
			-~^~-	// delimiter
			_u_^1 	// the last user index (relative to the receiving client) in the room to update
			text^0|last value // the properties shared so far ^ the last user index to update the property and the value
			x^1|last value
			y^1last value
			-~^~-
			*/
			
			// DZ convert to relative indexes
			// DZ if the client is positioned before the other then they would be taken out of the index count
			// DZ and the relative index of the other would be one less as below
							
			function convertToRelative(client,other) {
				return (other > client) ? other-1 : other;
			}           
			
			var relativeIndex = convertToRelative(socket.clientIndex, socket.room.lastBroadcastIndex);            		
            message += '_u_^' + relativeIndex + '\n'; // might be -1 if last client to update left - that's okay
            
			var lastIndex, lastValue;
            for (var v in socket.room.lastClientToUpdateVariable) {
				lastIndex = socket.room.getLastClientToUpdateVariableIndex(v);
				lastValue = socket.room.getLastClientToUpdateVariableValue(v);
				relativeIndex = convertToRelative(socket.clientIndex, lastIndex);
                message += v + '^' + relativeIndex + "|" + lastValue + '\n';
            }
            
            message += '-~^~-\n' + socket.room.buildMessage(socket, socket, -1); //DZ - -1 for new client
            console.log('BEGIN INIT MESSAGE OUT');
			console.log(splitter);
            console.log(message);
			console.log(splitter);
            console.log('END INIT MESSAGE OUT');
            socket.sendMessage(message);
			
			// DZ note that new socket does not broadcast - only sends message back to itself, the new socket
			// DZ it is up to client to send a second message with a change of property to trigger broadcast
			
            socket.active = true;
            socket.on('end', function () {				
                socket.room.removeSocket(socket);
				if (socket.room.countNonEmptySockets() == 0) {
					console.log("Last socket, cleared all sockets - deleted room");						
					//socket.room.history = '';
					//socket.room.sockets = [];
					//socket.room.lastBroadcastIndex = -1;
					//socket.room.lastClientToUpdateVariable = {}; // DZ - just used to Object literal being a {}
					for (var i=0; i<socket.application.rooms.length; i++) {
						if (socket.application.rooms[i] == socket.room) {
							socket.application.rooms.splice(i, 1);
						}
					}
					console.log("Number of rooms now = " + 	socket.application.rooms.length);		
				}						
            });
        } else {
			console.log("MESSAGE IN: Data");
            console.log('Recieved: ' + data);
            var somethingChanged = false;
            for (var v in variableMessages) { // update this socket's variable hashmap
                var vSplit = variableMessages[v].split("=");
                if (vSplit[0].toLowerCase() == '_history_') {
                    socket.room.history += vSplit[1];
                } else if (vSplit[0].toLowerCase() == '_clearhistory_') {
                    socket.room.history = ''; // DZ changed it to = rather than +=
                } else {
                    somethingChanged = true;
                    socket.variables[vSplit[0]] = vSplit[1];
                }
            }
            // console.log(socket.variables);
            if (somethingChanged) socket.room.broadcast(socket); // broadcast this user's variables
        }
    }

    function ApplicationPool() { // not quite a singleton but will do for now...
        this.applicationInstances = {}; // DZ just more used to {} than [] for associative array
        this.getApplication = function (socket, name, maxPerRoom, fillType) {					
            if (!this.applicationInstances[name]) { // ApplicationInstance held in associate array
                this.applicationInstances[name] = new ApplicationInstance(maxPerRoom, fillType, name);
            }
            this.applicationInstances[name].addSocket(socket);
            return this.applicationInstances[name];
        }
        this.closeAllApplications = function () {
            for (var a in this.applicationInstances) {
                this.applicationInstances[a].closeApplicationInstance();
            }
            this.applicationInstances = null;
			
        }
    } // end ApplicationPool
	
    function ApplicationInstance(maxSize, fillType, name) {
        this.name = name;

        this.getRoom = function () { // DZ gets last room
            return this.rooms[this.rooms.length - 1];
        }
        this.addSocket = function (socket) {
            // 0 = don't refill slot when user leaves room
            var socketRoom = null;
            var roomAvailable = false;
            for (var r in this.rooms) {
                if (this.rooms[r].hasRoomForOneMore()) {
                    socketRoom = this.rooms[r];
                    roomAvailable = true;
                    break;
                }
            }
            //console.log("Room available: " + roomAvailable);
            if (!roomAvailable) {
                this.rooms.push(new Room(fillType, maxSize));
                socketRoom = this.rooms[this.rooms.length - 1];
            }
            // add to empty socket if available, otherwise append to end!
            var addedToRoom = false;
            for (var s in socketRoom.sockets) {
				if (socketRoom.sockets[s].clientID == '') {
					// DZ when socket is removed socket is not deleted
					// DZ it is set to active=false and clientID removed
					// DZ the socket.clientIndex is kept so below works
					socket.clientIndex = socketRoom.sockets[s].clientIndex 
					// DZ replace old socket with new socket
					// DZ would it help to set the old socket to null first?
					// DZ socketRoom.sockets[s] = null;
					socketRoom.sockets[s] = socket; 
					addedToRoom = true;
					//console.log('Added to open slot');
					break;
				}
			}
			if (!addedToRoom) {
				socketRoom.sockets.push(socket);
				socket.clientIndex = socketRoom.sockets.length - 1;
			}
			
            socket.clientID = socketRoom.getNewClientID();
            socket.room = socketRoom;
            debuglog('Added ' + socket.clientID + ' to ' + ((roomAvailable) ? ' existing' : ' newly created') + ' room\n');
        }
        if (!this.rooms) {
            this.rooms = [new Room(fillType, maxSize)];
        }
        this.closeApplicationInstance = function () {
            for (var r in this.rooms) {
                this.rooms[r].closeRoom();
            }
            this.rooms = null;
        }
    } // end ApplicationInstance

    function Room(fillType, maxSize) {
        var self = this;
        if (!this.sockets) {			
            this.fillType = fillType;
            this.maxSize = maxSize;
            this.sockets = [];
            this.clientIDs = [];
            this.lastBroadcastIndex = -1; // last client that sent data
            this.history = "";
            this.lastMessage = "";
            this.currentVariables = {};
            this.lastClientToUpdateVariable = {}; // DZ - now stores client ID and variable value
			// DZ - do not think we use this
            // this.lastSender = null;
        }
        this.closeRoom = function () {
            for (var s in this.sockets) {
                this.sockets[s] = null;
            }
            this.sockets = null;
        }
        
        this.buildMessage = function(sender, recievingSocket, idx) {
			
			// DZ - format of the variables section of a message:
			// DZ - does not include the sender receivingSocket's data
			/*
			relativeLastSenderIndex
			lastSentPropertyName|lastSentPropertyName
			_u_^clientID|clientID
			propertyName^propertyValueClient0|propertyValueClient1
			propertyName^propertyValueClient0|propertyValueClient1
			
			// or for example:
			
			0
			x|y
			_u_^6|22
			x^5|10
			y^20|30			
			*/			
			
            if (!recievingSocket
                || recievingSocket == sender && idx != -1
                || recievingSocket.clientID == '') return ''; // DZ - okay to be same socket if new socket building message to itself			
            var message = "";
			
            var relativeIndex; // DZ - refactored a bit here as did not like storing idx as always relativeIndex
            if (idx > -1) {
				// DZ if the receiver (idx) is positioned before the sender then they would be taken out of the index count
				// DZ and the relative index of the sender would be one less as below
                relativeIndex = (sender.clientIndex > idx) ? sender.clientIndex - 1 : sender.clientIndex;
				relativeIndex = relativeIndex % self.maxSize; // DZ not sure we need but okay (not if -2 % 2)
            } else {
				relativeIndex = idx; // DZ - either -1 (new) or -2 (leaving)
			}
           
            message += relativeIndex + "\n"; // socket relative to receiver's index
			
			// DZ don't think we need below - the changed variables can just be left blank for a new client
			// DZ we get all the variable names and the updaters from the history area in the process message
			/* DZ	
            if (idx == -1) {
                for (var s in this.sockets) {
                    console.log('socket')
                    for (var v in this.sockets[s].variables) {
                        console.log('variable');
                        var val = sender.variables[v];
						// DZ if (!val && (val !== '') && (val !== 0)) {
						// DZ did you mean if there is a val?
                        if (val && (val !== '') && (val !== 0)) {
                            console.log('added var');
                            sender.variables[v] = '';
                        }
                    }
                }				
            } 
			*/
				
			if (getKeys(sender.variables).length) {
				message += getKeys(sender.variables).join("|") + "\n";
			} else { // DZ - need at least a \n for Robin to parse properly - this section is line locked
				message += "\n";
			}
			       
	       	message += "_u_^" + self.getOtherClientIDs(recievingSocket).join('|') + '\n';
						
			// DZ need to send all variables back every time - may not be most efficient
			// DZ but the spec says it I think to simplify proceedure
			// DZ most of the time, all variables are updated anyway - but not all the time
			// DZ possible that this was creating some of the anomolies
            // DZ for (var v in sender.variables) { // loop through sender's variables
			for (var v in self.currentVariables) { // loop through all room variables
                if (v.toLowerCase == '_history_' || v.toLowerCase == '_clearhistory_') continue;
                // prepare message
                message += v + "^";
                var others = self.getOthersInRoom(recievingSocket);
                var otherVars = [];
                for (var o in others) {
                    if (others[o]) {
                        if (others[o].variables[v]) {
                            otherVars.push(others[o].variables[v]);
                        } else {
                            otherVars.push('');
                        }
                    }
                }
                message += otherVars.join('|') + "\n";
            }			
            return message;
        }
        
		// DZ - refactored what happens when leaving
        this.broadcast = function (sender, leavingCheck) {		
						
            console.log('BROADCAST CALLED');
			console.log(this.countNonEmptySockets() + " users in the room");
								
			if (leavingCheck) { 
				this.lastBroadcastIndex = -2; // DZ - just to distinguish because leaving
			} else {
				this.lastBroadcastIndex = sender.clientIndex;  //DZ note absolute index
			}
            debuglog('Sender is: ' + sender.clientID);
            debuglog("Sender's variables: ");
			if (!leavingCheck) {
				for (var v in sender.variables) { // loop through sender's variables
					// used for _history messages
					if (this.currentVariables[v] != sender.variables[v]) {						
						this.lastClientToUpdateVariable[v] = sender.clientIndex+"|"+sender.variables[v];  //DZ note absolute index					
					}
					this.currentVariables[v] = sender.variables[v];
					// DZ - do not think we use this?
					// this.lastSender = sender;
				}
			}
			           
			// DZ below has been replaced by buildMessage()
            // countNonEmptySockets counts based on whether clientID.length > 0, but clientID is empty when a socket is removed
            /*if (this.countNonEmptySockets() == 1) { // only 1 socket in the room!
                return;
                var message = sender.clientIndex + '\n';
                message += getKeys(sender.variables).join("|") + "\n";
                message += "_u_^" + sender.clientID + '\n';
                for (var v in sender.variables) {
                    if (v.toLowerCase == '_history_' || v.toLowerCase == '_clearhistory_') continue;
                    // prepare message
                    message += v + "^" + sender.variables[v] + '\n';
                }
                this.lastMessage = message;
                console.log("Only 1 user in the room!");
                //sender.sendMessage(message);
                for (var s = 0; s < this.sockets.length; s++) {
                    var lastSocket = this.sockets[s];
                    lastSocket.room.broadcast(lastSocket);
                }
                return;
            }*/
			
			var sentCount = 0;
            for (var s=0; s<this.sockets.length; s++) { // loop through non-sender sockets 
                var recievingSocket = this.sockets[s];
				if (leavingCheck) { 
	                var message = this.buildMessage(sender, recievingSocket, -2); // DZ - -2 for leaving
				} else {
					var message = this.buildMessage(sender, recievingSocket, s);
				}
                if (!message) continue; // DZ - buildMessage returns "" if sender and reciever is same
                this.lastMessage = message;
                
				//debuglog('SENDING TO: ' + recievingSocket.clientID);
                //debuglog(message + splitter);
                //console.log(message + splitter);
				
				console.log("START DATA MESSAGE OUT");
				console.log(splitter);
				console.log(message);
				console.log(splitter);
				console.log("END DATA MESSAGE OUT");
				
                recievingSocket.sendMessage(message);
                // DZ this was already set -  this.lastMessage = message;
                sentCount++;
            }
            debuglog('Sent ' + sentCount + ' messages');
        }
        this.users = function () {
            var users = 'List of users: ';
            for (var s in this.sockets) {
                if (this.sockets[s] == null) continue;
                users += this.sockets[s].clientID + ' ';
            }
            return users;
        }
        this.removeSocket = function (socket) {
			
			console.log(dzSplitter);
			console.log("MESSAGE IN: Client Disconnected");
			
            for (var i = 0; i < this.sockets.length; i++) {
                if (this.sockets[i] == socket) {
					/*
                    var s = this.sockets[i];
                    if (i == this.sockets.length - 1) {
                        for (var v in s.variables) {
                            s.variables[v] = '';
                        }
                        if (this.lastBroadcastIndex == s.clientIndex) this.lastBroadcastIndex = -1;
                        // Don't strip off the trailing socket!
                        //this.sockets.splice(i, 1);
                    } else {
                        if (this.lastBroadcastIndex == s.clientIndex) this.lastBroadcastIndex = -1;
                        for (var v in s.variables) {
                            s.variables[v] = '';
                        }
                    }					
                    s.clientID = '';
                    s.room.broadcast(s, true);
                    s.active = false;
                    break;
					*/
					
					// DZ refactored the above code:   
					// DZ might want to delete socket.variables[v] but no big deal  
										
					var s = this.sockets[i];
					for (var v in socket.variables) {
						socket.variables[v] = '';
					}				
				
					// DZ - moved handling lastBroadcastIndex to broadcast()
					// DZ - if (this.lastBroadcastIndex == socket.clientIndex) this.lastBroadcastIndex = -1;
										
					// DZ - set any lastClientToUpdateVariable with this index to -2, leave the value though
					for (var v in this.lastClientToUpdateVariable) {
						lastIndex = this.getLastClientToUpdateVariableIndex(v);
						lastValue = this.getLastClientToUpdateVariableValue(v);
						if (lastIndex == s.clientIndex) {
							this.lastClientToUpdateVariable[v] = -2+"|"+lastValue; // DZ - -2 for client left
						}
					}				
												
                    s.clientID = '';					
					this.broadcast(s, true); 					
                    s.active = false;
                    break;
					
                }
            }	
			
			console.log("Socket removed!");
			// DZ - refactored the removing of room to after calling the removeSocket method			            
            
        }
        this.hasRoomForOneMore = function () {
            if (this.fillType == 1) {
                return this.countNonEmptySockets() < this.maxSize;
            } else {
                return this.sockets.length < this.maxSize;
            }
        }
        this.countNonEmptySockets = function () {
            var count = 0;
            for (var s in this.sockets) {
				// console.log('ID: '+this.sockets[s].clientID);
                if (this.sockets[s].active) count++;
            }
            // console.log("There are " + count + " sockets currently in this room. MaxSize = " + this.maxSize);
            return count;
        }
        this.getOthersInRoom = function (socket) {
            var others = []; // DZ list of sockets (including inactive sockets)
            for (var s in this.sockets) {
                if (socket != this.sockets[s] && this.sockets[s] != null && this.sockets[s] !== undefined) others.push(this.sockets[s]);
            }
            return others;
        }
        this.getOtherClientIDs = function (socket) {
            var others = this.getOthersInRoom(socket);
            var otherIDs = [];
            for (var o in others)
                otherIDs.push(others[o].clientID);
            return otherIDs;
        }
        this.getNewClientID = function () {
            var id;
            while (true) {
                id = Math.floor(Math.random() * 1e6);
                if (this.clientIDs.indexOf(id) == -1) break;
            }
            return id;
        }
		this.getLastClientToUpdateVariableIndex = function (v) {
			return this.lastClientToUpdateVariable[v].split("|")[0];
		}
		this.getLastClientToUpdateVariableValue = function (v) {			
			return this.lastClientToUpdateVariable[v].split("|")[1];
		}
		
    } // end Room
	
	

    this.startServer = function () {
        server.listen(port);
        console.log("Robin server started on port " + port + "...");
    }
    this.stopServer = function () {
        applicationPool.closeAllApplications();
        server.close();
    }
    this.setPort = function (customPort) {
        this.port = customPort;
    }

}
exports.robinServer = new RobinServer();