DAN ZEN EXPO - CODE EXHIBIT - GOOSE


package samples {
	
	// GOOSE INTRODUCTION  
	// Goose lets you work with multiple cursor inputs - like multitouch
	// http://gooseflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Goose 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	
		
	// GOOSE OVERVIEW  
	// Goose splits into two parts:
	
	// 1. A multitouch emulator (GooseData, GooseRobin, Online Mouse Nodes)
	// 		with the Goose Emulator you run the online Mouse Nodes on two or more computers
	//		each mouse then feeds data through GooseRobin to GooseData
	//		you then feed the data from GooseData into Goose (part 2)	
	
	// 2. A multitouch processor (Goose, GooseEvent)
	//		Goose takes in multitouch data (like from the emulator in part 1)
	//		and shows multiple cursors in your application
	//		if you use the Goose Emulator then each mouse shows up as a cursor
	//		Goose and GooseEvent provide a set of methods and events for using the cursors
	//      You continue to use Goose when going from an emulator to real multitouch data

	// USING GOOSE  
	// below is a sample that uses Goose
	// to make it work you have to run two mouse nodes at 
	// http://www.danzen.com/goose/node.html
	// enter the name of the application that you pass to GooseData() below
	// you should change this to a relatively unique name so you do not conflict with others ;-)
	// see the videos on the Goose site for a guide to how to make all this work
	
	// the steps you can see below are as follows
	// import com.danzen.interfaces.goose.* in your document class
	// create a new GooseData object and pass it the name of your application
	// create a Goose object in your document class
	// add an Event.CHANGE event to your GooseData object and have it call a method
	// in the method pass the GooseData's xmlData property to the Goose update() method
	
	// this separation allows you to easily change from the emulator to real multitouch data
	// when you are ready to do so, just pass the real data to the Goose update() method
	// the data must be in the form of ManyML:
	
	// 
	// 
	// 
	// 
	
	// if z >= 0 then the item is treated as a touch in Goose
	// if z < 0 then the item is ignored by all events in Goose
	
	// CONSTRUCTOR  
	// public function Goose(theShowCursors:Boolean = true):void 
	//		constructor to start your multitouch processor
	//		PARAMETERS:
	//			theShowCursors:Boolean
	//				do you want to see the cursors - defaults to true
	//				but remember, in multitouch you do not see cursors usually
	//				as they are not there for rollovers and under the fingers for presses
						
	// EVENTS  
	// GooseEvent.TOUCH
	//		a touch up and down (see the touchDelay property) - cursor z property will be >= 0
	// GooseEvent.TOUCH_DOWN
	//		a touch down (like a MOUSE_DOWN) - cursor z property will be >= 0
	// GooseEvent.TOUCH_UP
	//		a touch up (like a MOUSE_UP) - cursor z property will be < 0
	// GooseEvent.TOUCH_MOVE
	//		moving while touching (like a MOUSE_MOVE)	
	//		will be automatically removed on TOUCH_UP
	// GooseEvent.PRESS
	//		a press up and down on a specified item (see the pressDelay property) - cursor z >= 0
	// GooseEvent.PRESS_DOWN
	//		a press down on a specified item - cursor z >= 0
	// GooseEvent.PRESS_UP
	//		a press up on a specified item - cursor z < 0
	// GooseEvent.PRESS_MOVE
	//		moving after pressing down on a specified item
	//		continues to register even if cursor is no longer on item
	//		will be automatically removed on PRESS_UP		
	// GooseEvent.PICK_UP
	//		the first time an item has been picked up for a follow or size
	// GooseEvent.PUT_DOWN
	//		the last time an item has been put down for last follow or size
	
	// METHODS  
	//	update(d:XML):void
	//		receives the XML data in the form of ManyML (http://manyml.wordpress.com)
	//		makes the cursors move and dispatches the touch and press events
	//	addPressListener(e:String, obj:InteractiveObject, fun:Function):void
	//		e:String - a GooseEvent press-type event
	//		obj:InteractiveObject - a MovieClip for instance on which you want to track the event
	//		fun:Function - the function you want to call when the event on the obj happens
	//		this will pass to the function a GooseObject with the following properties:
	//			cursor:Sprite - a reference to the cursor object	
	//			cursorZ:Number - the z value of the cursor
	//			cursorID:String - the id value of the cursor			
	//			obj:InteractiveObject - a reference to the object that triggered the event
	// removePressListener(e:String, obj:InteractiveObject, fun:Function):void
	//		removes the specified event from the specified object having related function				
	// startFollow(cursor:Sprite, obj:InteractiveObject, damp:Number=.1, type:String=Goose.DRAG_AVERAGE):void
	// 		make the specified cursor partake in the moving of specified object
	//		when the cursor is no longer pressing, its effect on the follow is removed
	// 		damp is the speed at which the object follows - 1 instant, 0 not at all (.1 is nice)
	// 		type is the rules for your follow:
	// 		Goose.DRAG_LOCKED exclusive drag for life of cursor except if already locked
	// 		Goose.DRAG_OVERRIDE latest drag is the only drag (unless DRAG_LOCKED)
	// 		Goose.DRAG_AVERAGE (default) all drags are averaged (unless DRAG_OVERRIDE or DRAG_LOCKED)
	// stopFollow(cursor:Sprite, obj:InteractiveObject):void
	//		stop following the specified cursor with the specified object
	// stopAllFollows():void
	//		stop all current following - future follows will still run
	// startScale(cursor:Sprite, obj:InteractiveObject, damp:Number=.1, proportional:Boolean=true, registration:String=Goose.REGISTRATION_AVERAGE):void
	//		make the specified cursor partake in the scaling of the specified object
	//		when the cursor is no longer pressing, its effect on the scaling is removed	
	// 		damp is the speed at which the object scales - 1 instant, 0 not at all (.1 is nice)
	// 		the Boolean is proportional scaling (true - default) or not (false)
	// 		the last parameter is where it scales from
	// 		Goose.REGISTRATION_CENTER - from the object center
	// 		Goose.REGISTRATION_POINT - from the registration point
	// 		Goose.REGISTRATION_AVERAGE (default) - from the average starting point of the cursors				
	// stopScale(cursor:Sprite, obj:InteractiveObject):void
	//		stop scaling the specified object with the specified cursor
	// stopAllScales():void
	//		stop all current scaling - future scales will still run
	// dispose():void
	//		removes listeners, closes socket, deletes data objects
	
	// PROPERTIES  
	// showCursors:Boolean - do you want to see the cursors (true) or not (false)
	// touchDelay:Number = .5 - max seconds until a touch is not a touch 
	// pressDelay:Number = .5 - max seconds until a press is not a press
	// touchRadius:Number = 10 - max pixel movement until a touch is not a touch 
	// pressRadius:Number = 10 - max pixel movement until a press is not a press
	
	// STATIC CONSTANTS  
	// Goose.DRAG_LOCKED - see startFollow() method
	// Goose.DRAG_OVERRIDE - see startFollow() method
	// Goose.DRAG_AVERAGE - see startFollow() method		
	// Goose.REGISTRATION_CENTER - see startScale() method
	// Goose.REGISTRATION_POINT - see startScale() method
	// Goose.REGISTRATION_AVERAGE - see startScale() method				
	
	import flash.display.MovieClip;
	import flash.text.*;
	import flash.events.*
	import flash.ui.Mouse;
	
	import com.danzen.interfaces.goose.*;
	
	public class GooseExample extends MovieClip {
		
		// this sample lets you move and resize a picture using two or more cursors
		// if you scale the picture down enough then it turns into a crumple
		// you can then put the crumple into the recycle bin
		// press the restore button (shows up once you throw away) to restore the picture
		
		// it was quite the feeling to be able to use multiuser data with Flash
		// Goose helps designers and developers work with multitouch without external systems
		// for those experimenting with blob detection or multitouch devices
		// you can take your data, convert it to ManyML and pass it to the Goose update() method
		// this you will do rather than run the emulator
		// then you can make use of the methods and events that Goose provides
		// to touch, press, scale and follow with multiple cursors
		// these were not paricularly easy to write and one day a rotate method may come to being
		// if you happen to write a rotate method, please let us know
		
		private var myGoose:Goose;
		private var myGooseData:GooseData;

		public function GooseExample() {			
					
			trace ("hi from GooseExample");
			
			// set up receiving data from the emulator
			myGooseData = new GooseData("default");  // please change this to your application name
			myGooseData.addEventListener(Event.CHANGE, feedGoose);
			
			// set up Goose multiuser data processor that will show you cursors
			myGoose = new Goose();							
			addChild(myGoose);			
						
			// here are the events we use - there is a PRESS event too later on (like a CLICK)			
			// the press events go on specified Interactive Objects (MovieClips and Sprites, etc.)
			// so you will note that there is one more parameter in the middle than the addEventListener()
			myGoose.addPressListener(GooseEvent.PRESS_DOWN, myPic, callPressDown); // there is a PRESS_UP too
			myGoose.addPressListener(GooseEvent.PRESS_MOVE, myPic, callPressMove);
			myGoose.addPressListener(GooseEvent.PUT_DOWN, myPic, callPutDown); // there is a PICK_UP too
			
			// here are samples of generic listeners - for any touch
			// these will give you a reference to the cursor that caused the event
			// they are not used in the example but you can play around with them
			myGoose.addEventListener(GooseEvent.TOUCH, callTouch);
			myGoose.addEventListener(GooseEvent.TOUCH_DOWN, callTouchDown);	
			myGoose.addEventListener(GooseEvent.TOUCH_MOVE, callTouchMove);	
			myGoose.addEventListener(GooseEvent.TOUCH_UP, callTouchUp);				
			
			// set up start picture properties for later reset
			myPic.inner.mouseEnabled = false;
			myPic.startScale = myPic.scaleX;
			myPic.startX = myPic.x;
			myPic.startY = myPic.y;
			myGarbage.startIndex = getChildIndex(myGarbage);
			myGarbage.activeIndex = getChildIndex(myPic);
			
		}
			
		private function feedGoose(e:Event) {
			// pass the data from GooseData into the update() method of Goose
			// this is what constantly feeds the multiple cursor data into Goose
			// you can pass in your own multitouch data in the form of ManyML
			// http://manyml.wordpress.com - a very simple XML language
			myGoose.update(e.target.xmlData); 
		}				

		private function callPressDown(e:GooseEvent) {			
									
			// startFollow() parameters:
			// the first two are what cursor to follow and with what object
			// the number is a damping - 1 instant, 0 not at all (.1 is nice)
			// the next is the type of follow:
			// Goose.DRAG_LOCKED exclusive drag for life of cursor except if already locked
			// Goose.DRAG_OVERRIDE latest drag is the only drag (unless DRAG_LOCKED)
			// Goose.DRAG_AVERAGE (default) all drags are averaged (unless DRAG_OVERRIDE or DRAG_LOCKED)
			
			myGoose.startFollow(e.cursor, e.obj, .1, Goose.DRAG_AVERAGE);
			
			// no need for a PRESS_UP as the follow method automatically stops
			// following when the cursor is gone and so does the PRESS_MOVE
						
			// startScale() parameters:
			// the first two are what cursor to scale with and with what object
			// the number is the damping - 1 instant, 0 not at all (.1 is nice)
			// the Boolean is proportional scaling (true - default) or not (false)
			// the last parameter is where it scales from
			// Goose.REGISTRATION_CENTER - from the object center
			// Goose.REGISTRATION_POINT - from the registration point
			// Goose.REGISTRATION_AVERAGE (default) - from the average starting point of the cursors
			
			myGoose.startScale(e.cursor, e.obj, .1, true, Goose.REGISTRATION_AVERAGE);
				
		}
		
		private function callPressMove(e:GooseEvent) {
			
			// if the scale is small enough then turn picture to crumple
			if (myPic.scaleX < .25) {
				myPic.gotoAndStop(2);	
				setChildIndex(myGarbage,myGarbage.activeIndex);
			} else {
				myPic.gotoAndStop(1);
			}
			
		}		
		
		private function callPutDown(e:GooseEvent) {						
			if (e.obj.hitTestObject(myGarbage) && MovieClip(e.obj).currentFrame == 2) {
				
				// hide picture and show restoreBut
				e.obj.alpha = 0;
				e.obj.mouseEnabled = false;
				restoreBut.alpha = .7;
				
				myGoose.addPressListener(GooseEvent.PRESS, restoreBut, restorePic);
			}
		}		
		
		private function restorePic(e:GooseEvent) {
			
			// reset picture and hide restoreBut
			myPic.alpha = 1;
			myPic.mouseEnabled = true;
			myPic.scaleX = myPic.scaleY = myPic.startScale;
			myPic.gotoAndStop(1);
			myPic.x = myPic.startX;
			myPic.y = myPic.startY;
			setChildIndex(myGarbage,myGarbage.startIndex);
			restoreBut.alpha = 0;			
			
			myGoose.removePressListener(GooseEvent.PRESS, restoreBut, restorePic);
		}
		
		private function callTouch(e:GooseEvent) {
			//trace ("touch ID = " + e.cursorID);
		}
		
		private function callTouchDown(e:GooseEvent) {
			//trace ("touch down ID = " + e.cursorID);
		}	
		
		private function callTouchMove(e:GooseEvent) {
			//trace ("touch move ID = " + e.cursorID);
		}	
		
		private function callTouchUp(e:GooseEvent) {
			//trace ("touch up ID = " + e.cursorID);
		}			
		
		private function callPicUp(e:GooseEvent) {
			//trace ("Pick up ID = " + e.cursorID);
		}				
		
	}
	
}


package com.danzen.interfaces.goose {

	// GOOSE INTRODUCTION  
	// Goose lets you work with multiple cursor inputs - like multitouch
	// http://gooseflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Goose 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	
		
	// GOOSE OVERVIEW  
	// Goose splits into two parts:
	
	// 1. A multitouch emulator (GooseData, GooseRobin, Online Mouse Nodes)
	// 		with the Goose Emulator you run the online Mouse Nodes on two or more computers
	//		each mouse then feeds data through GooseRobin to GooseData
	//		you then feed the data from GooseData into Goose (part 2)	
	
	// 2. A multitouch processor (Goose, GooseEvent)
	//		Goose takes in multitouch data (like from the emulator in part 1)
	//		and shows multiple cursors in your application
	//		if you use the Goose Emulator then each mouse shows up as a cursor
	//		Goose and GooseEvent provide a set of methods and events for using the cursors
	//      You continue to use Goose when going from an emulator to real multitouch data

	// USING GOOSE  
	// see the sample as file for an example of how to use Goose
	// import com.danzen.interfaces.goose.* in your document class
	// create a new GooseData object and pass it the name of your application
	// create a Goose object in your document class
	// add an Event.CHANGE event to your GooseData object and have it call a method
	// in the method pass the GooseData's xmlData property to the Goose update() method
	
	// this separation allows you to easily change from the emulator to real multitouch data
	// when you are ready to do so, just pass the real data to the Goose update() method
	// the data must be in the form of ManyML:
	
	// <manyml>
	// <item id="1000" x="984" y="1" z="-100" />
	// <item id="2000" x="243" y="7" z="0" />
	// </manyml>
	
	// if z >= 0 then the item is treated as a touch in Goose
	// if z < 0 then the item is ignored by all events in Goose
	
	// Goose has a few methods and many properties described below
	
	import flash.display.Sprite;
	import flash.events.*;
	import flash.display.InteractiveObject;
	import flash.utils.getTimer;
	import flash.utils.Dictionary;
	import flash.geom.Matrix;
	import fl.motion.MatrixTransformer;

	public class Goose extends Sprite {

		// CONSTRUCTOR  
		// public function Goose(theShowCursors:Boolean = true):void 
		//		constructor to start your multitouch processor
		//		PARAMETERS:
		//			theShowCursors:Boolean
		//				do you want to see the cursors - defaults to true
		//				but remember, in multitouch you do not see cursors usually
		//				as they are not there for rollovers and under the fingers for presses
							
		// EVENTS
		// GooseEvent.TOUCH
		//		a touch up and down (see the touchDelay property) - cursor z property will be >= 0
		// GooseEvent.TOUCH_DOWN
		//		a touch down (like a MOUSE_DOWN) - cursor z property will be >= 0
		// GooseEvent.TOUCH_UP
		//		a touch up (like a MOUSE_UP) - cursor z property will be < 0
		// GooseEvent.TOUCH_MOVE
		//		moving while touching (like a MOUSE_MOVE)	
		//		will be automatically removed on TOUCH_UP
		// GooseEvent.PRESS
		//		a press up and down on a specified item (see the pressDelay property) - cursor z >= 0
		// GooseEvent.PRESS_DOWN
		//		a press down on a specified item - cursor z >= 0
		// GooseEvent.PRESS_UP
		//		a press up on a specified item - cursor z < 0
		// GooseEvent.PRESS_MOVE
		//		moving after pressing down on a specified item
		//		continues to register even if cursor is no longer on item
		//		will be automatically removed on PRESS_UP		
		// GooseEvent.PICK_UP
		//		the first time an item has been picked up for a follow or size
		// GooseEvent.PUT_DOWN
		//		the last time an item has been put down for last follow or size
		
		// METHODS
		//	update(d:XML):void
		//		receives the XML data in the form of ManyML (http://manyml.wordpress.com)
		//		makes the cursors move and dispatches the touch and press events
		//	addPressListener(e:String, obj:InteractiveObject, fun:Function):void
		//		e:String - a GooseEvent press-type event
		//		obj:InteractiveObject - a MovieClip for instance on which you want to track the event
		//		fun:Function - the function you want to call when the event on the obj happens
		//		this will pass to the function a GooseObject with the following properties:
		//			cursor:Sprite - a reference to the cursor object	
		//			cursorZ:Number - the z value of the cursor
		//			cursorID:String - the id value of the cursor			
		//			obj:InteractiveObject - a reference to the object that triggered the event
		// removePressListener(e:String, obj:InteractiveObject, fun:Function):void
		//		removes the specified event from the specified object having related function				
		// startFollow(cursor:Sprite, obj:InteractiveObject, damp:Number=.1, type:String=Goose.DRAG_AVERAGE):void
		// 		make the specified cursor partake in the moving of specified object
		//		when the cursor is no longer pressing, its effect on the follow is removed
		// 		damp is the speed at which the object follows - 1 instant, 0 not at all (.1 is nice)
		// 		type is the rules for your follow:
		// 		Goose.DRAG_LOCKED exclusive drag for life of cursor except if already locked
		// 		Goose.DRAG_OVERRIDE latest drag is the only drag (unless DRAG_LOCKED)
		// 		Goose.DRAG_AVERAGE (default) all drags are averaged (unless DRAG_OVERRIDE or DRAG_LOCKED)
		// stopFollow(cursor:Sprite, obj:InteractiveObject):void
		//		stop following the specified cursor with the specified object
		// stopAllFollows():void
		//		stop all current following - future follows will still run
		// startScale(cursor:Sprite, obj:InteractiveObject, damp:Number=.1, proportional:Boolean=true, registration:String=Goose.REGISTRATION_AVERAGE):void
		//		make the specified cursor partake in the scaling of the specified object
		//		when the cursor is no longer pressing, its effect on the scaling is removed	
		// 		damp is the speed at which the object scales - 1 instant, 0 not at all (.1 is nice)
		// 		the Boolean is proportional scaling (true - default) or not (false)
		// 		the last parameter is where it scales from
		// 		Goose.REGISTRATION_CENTER - from the object center
		// 		Goose.REGISTRATION_POINT - from the registration point
		// 		Goose.REGISTRATION_AVERAGE (default) - from the average starting point of the cursors				
		// stopScale(cursor:Sprite, obj:InteractiveObject):void
		//		stop scaling the specified object with the specified cursor
		// stopAllScales():void
		//		stop all current scaling - future scales will still run
		// dispose():void
		//		removes listeners, closes socket, deletes data objects
		
		// PROPERTIES 
		// showCursors:Boolean - do you want to see the cursors (true) or not (false)
		// touchDelay:Number = .5 - max seconds until a touch is not a touch 
		// pressDelay:Number = .5 - max seconds until a press is not a press
		// touchRadius:Number = 10 - max pixel movement until a touch is not a touch 
		// pressRadius:Number = 10 - max pixel movement until a press is not a press
		
		// STATIC CONSTANTS
		// Goose.DRAG_LOCKED - see startFollow() method
		// Goose.DRAG_OVERRIDE - see startFollow() method
		// Goose.DRAG_AVERAGE - see startFollow() method		
		// Goose.REGISTRATION_CENTER - see startScale() method
		// Goose.REGISTRATION_POINT - see startScale() method
		// Goose.REGISTRATION_AVERAGE - see startScale() method			

		public static const DRAG_LOCKED:String = "dragLocked";
		public static const DRAG_OVERRIDE:String = "dragOverride";
		public static const DRAG_AVERAGE:String = "dragAverage";
		
		public static const REGISTRATION_CENTER:String = "registrationCenter";
		public static const REGISTRATION_POINT:String = "registrationPoint";
		public static const REGISTRATION_AVERAGE:String = "registrationAverage";

		public var xmlData:XML;
		public var touchDelay:Number = .5;// max seconds until a touch is not a touch 
		public var pressDelay:Number = .5;// max seconds until a press is not a press
		public var touchRadius:Number = 10;// max pixel movement until a touch is not a touch 
		public var pressRadius:Number = 10;// max pixel movement until a press is not a press

		private var myShowCursors:Boolean;
		private var myCursors:Sprite;
		private var currentCursors:Object = {};
		
		// Dictionaries to record and track addPressEvent listeners		
		private var press:Dictionary = new Dictionary(true);
		private var pressDown:Dictionary = new Dictionary(true);
		private var pressUp:Dictionary = new Dictionary(true);
		private var pressMove:Dictionary = new Dictionary(true);
		private var pickUp:Dictionary = new Dictionary(true);
		private var putDown:Dictionary = new Dictionary(true);
		private var currentPresses:Dictionary = new Dictionary(true);
		private var cursorLookup:Dictionary = new Dictionary(true);
		private var follow:Dictionary = new Dictionary(true);
		private var scale:Dictionary = new Dictionary(true);
		
		public function Goose(theShowCursors:Boolean = true) {
	
			trace("hi from Goose");
			myCursors = new Sprite();
			addChild(myCursors);
			showCursors = theShowCursors;
			addEventListener(Event.ENTER_FRAME, runFollow);
			addEventListener(Event.ENTER_FRAME, runScale);
		}

		public function update(d:XML) {	
			/*var d:XML = <manyml>
				<item id="1000" x="50" y="20" z="-100" />
				<item id="2000" x="500" y="80" z="0" />
			</manyml>;*/
			
			// move cursors and determine cursor joins and leaves
			// dispatch cursor events
			var newCursor:Sprite;
			var newCursors:Object = {};
			var theseCursors:Object = {};
			var deltaX:Number;
			var deltaY:Number;
			var downCheck:Boolean = false;

			for each (var i:XML in d.item) {
				theseCursors[i.@id] = 1;
				if (! currentCursors[i.@id]) {
					newCursor = new Sprite();
					newCursor.graphics.lineStyle(2,0x888888,1,false,"none");
					newCursor.graphics.beginFill(0xffff00, .7);
					newCursor.graphics.drawCircle(0,0,10);
					newCursor.x = i.@x;
					newCursor.y = i.@y;
					myCursors.addChild(newCursor);
					newCursors[i.@id] = [newCursor,i.@z,getTimer(),i.@x,i.@y];
					cursorLookup[newCursor] = i.@id;
					if (i.@z >= 0) {
						newCursor.scaleX = newCursor.scaleY = 2;
						// -------------------TOUCH_DOWN PRESS_DOWN
						dispatchEvent(new GooseEvent(GooseEvent.TOUCH_DOWN, newCursor, Number(i.@z), i.@id));
						dispatchPressEvent(GooseEvent.PRESS_DOWN, newCursor, Number(i.@z), i.@id);
						downCheck = true;
					}
				} else {
					currentCursors[i.@id][0].x = i.@x;
					currentCursors[i.@id][0].y = i.@y;

					if (currentCursors[i.@id][1] < 0 && i.@z >= 0) {
						// -------------------TOUCH_DOWN PRESS_DOWN
						dispatchEvent(new GooseEvent(GooseEvent.TOUCH_DOWN, currentCursors[i.@id][0], Number(i.@z), i.@id));
						dispatchPressEvent(GooseEvent.PRESS_DOWN, currentCursors[i.@id][0], Number(i.@z), i.@id);
						currentCursors[i.@id][2] = getTimer();
						currentCursors[i.@id][3] = i.@x;
						currentCursors[i.@id][4] = i.@y;
						downCheck = true;
					} else if (currentCursors[i.@id][1] >= 0 && i.@z < 0) {
						// -------------------TOUCH TOUCH_UP PRESS PRESS_UP
						if ((getTimer() - currentCursors[i.@id][2]) < touchDelay * 1000) {
							deltaX = Math.pow((currentCursors[i.@id][3]-currentCursors[i.@id][0].x),2);
							deltaY = Math.pow((currentCursors[i.@id][4]-currentCursors[i.@id][0].y),2);
							if (Math.sqrt(deltaX + deltaY) < touchRadius) {
								dispatchEvent(new GooseEvent(GooseEvent.TOUCH, currentCursors[i.@id][0], Number(i.@z), i.@id));
								dispatchPressEvent(GooseEvent.PRESS, currentCursors[i.@id][0], Number(i.@z), i.@id);
							}
						}
						dispatchEvent(new GooseEvent(GooseEvent.TOUCH_UP, currentCursors[i.@id][0], Number(i.@z), i.@id));
						dispatchPressEvent(GooseEvent.PRESS_UP, currentCursors[i.@id][0], Number(i.@z), i.@id);
					}

					currentCursors[i.@id][1] = i.@z;

					// -------------------TOUCH_MOVE PRESS_MOVE
					if (currentCursors[i.@id][1] >= 0) {
						currentCursors[i.@id][0].scaleX = currentCursors[i.@id][0].scaleY = 2;
						dispatchEvent(new GooseEvent(GooseEvent.TOUCH_MOVE, currentCursors[i.@id][0], Number(i.@z), i.@id));
						dispatchPressEvent(GooseEvent.PRESS_MOVE, currentCursors[i.@id][0], Number(i.@z), i.@id);
						downCheck = true;
					} else {
						currentCursors[i.@id][0].scaleX = currentCursors[i.@id][0].scaleY = 1;
					}
				}

			}
			if (! downCheck) {
				// reset the current presses in case the cursor just left
				// this is important for real blob detection
				// when every press might get a unique id
				// the Dictionary should clear itself as cursors are deleted
				for (var p:* in currentPresses) {
					currentPresses[p] = {};
				}
			}			

			// remove any missing cursors and add any new ones
			for (var c:String in currentCursors) {
				if (! theseCursors[c]) {
					delete cursorLookup[currentCursors[c][0]];
					myCursors.removeChild(currentCursors[c][0]);
					delete currentCursors[c];
				}
			}
			for (var n:String in newCursors) {
				currentCursors[n] = newCursors[n].concat();
			}

		}

		private function dispatchPressEvent(e:String, c:Sprite, cZ:Number, cID:String) {
									
			var o:*;
			
			// watch out here as they may not have registered a PRESS_DOWN
			// yet we still need to know for the other events if we started on the object
			// this stores that we have a press on the object
			if (e == GooseEvent.PRESS_DOWN) {
				for (o in currentPresses) {
					if (o.hitTestPoint(c.x,c.y)) {
						currentPresses[o][cID] = 1;
					}
				}
			} 			
			
			// here we dispatch the event with one amazing line ;-)
			for (o in this[e]) {
				if (!o.mouseEnabled) {continue;}
				if (!currentPresses[o]) {continue;}
				if (currentPresses[o][cID]!=1) {continue;}
				
				if (o.hitTestPoint(c.x,c.y)) {										
					// call the function stored in the dictionary for the object
					this[e][o](new GooseEvent(e,c,cZ,cID,o)); // beautiful
				}
			}
			
			// remove the tracking of the press on the object
			if (e == GooseEvent.PRESS_UP) {
				for (o in currentPresses) {					
					delete currentPresses[o][cID];
					stopFollow(c, o);
					stopScale(c, o);
				}
			}		
		}

		public function addPressListener(e:String, obj:InteractiveObject, fun:Function) {
			this[e][obj] = fun;
			if (!currentPresses[obj]) {
				currentPresses[obj] = {};
			}
		}

		public function removePressListener(e:String, obj:InteractiveObject, fun:Function) {
			delete this[e][obj];
		}

		//----  FOLLOW  ---------------------------------------------------		
		
		public function startFollow(cursor:Sprite, obj:InteractiveObject, damp:Number=.1, type:String=Goose.DRAG_AVERAGE) {
			// might have nesting problems here unless do local to global...
			// write into follow array or object to loop through in the enter frame
			damp = Math.max(0, Math.min(1, damp));			
			if (follow[obj]) {
				if (follow[obj][0][2] == Goose.DRAG_LOCKED) {return;}
				if (type == Goose.DRAG_LOCKED || type == Goose.DRAG_OVERRIDE) {
					// overwrite the current followings whatever they are
					follow[obj] = [[cursor, damp, type, obj.x, obj.y, cursor.x, cursor.y]];
				} else {
					follow[obj].push([cursor, damp, type, obj.x, obj.y, cursor.x, cursor.y]);
				}
			} else {
				follow[obj] = [[cursor, damp, type, obj.x, obj.y, cursor.x, cursor.y]];
				if (scale[obj]) {return;}
				if (!this[GooseEvent.PICK_UP]) {return;}
				if (!this[GooseEvent.PICK_UP][obj]) {return;}
				var cID:String = cursorLookup[cursor];
				var cZ:Number = currentCursors[cID][1];
				this[GooseEvent.PICK_UP][obj](new GooseEvent(GooseEvent.PICK_UP,cursor,cZ,cID,obj));
			}			
		}
		
		private function runFollow(e:Event) { // called by ENTER_FRAME
			var curs:Sprite;
			var damp:Number;
			var objStartX:Number;
			var objStartY:Number;
			var cursStartX:Number;
			var cursStartY:Number;
			var cursEndX:Number;
			var cursEndY:Number;
			
			var totDamp:Number;
			var totObjStartX:Number;
			var totObjStartY:Number;
			var totCursStartX:Number;
			var totCursStartY:Number;
			var totCursX:Number;
			var totCursY:Number;
						
			var newX:Number;
			var newY:Number;
			var diffX:Number;
			var diffY:Number;
			
			var i:uint;
			var len:Number;
			
			for (var f:* in follow) {				
				totDamp=0;
				totObjStartX=0;
				totObjStartY=0;
				totCursStartX=0;
				totCursStartY=0;
				totCursX=0;
				totCursY=0;
				// take averages
				len = follow[f].length;
				for (i=0; i<len; i++) {
					curs = follow[f][i][0];
					totCursX+=curs.x;
					totCursY+=curs.y;
					totDamp+=follow[f][i][1];
					totObjStartX+=follow[f][i][3];
					totObjStartY+=follow[f][i][4];
					totCursStartX+=follow[f][i][5];
					totCursStartY+=follow[f][i][6];					
				}
								
				damp = totDamp/len;				
				objStartX = totObjStartX/len;
				objStartY = totObjStartY/len;
				cursStartX = totCursStartX/len;
				cursStartY = totCursStartY/len;
				cursEndX = totCursX/len;
				cursEndY = totCursY/len;
				
				newX = objStartX + (cursEndX - cursStartX);
				newY = objStartY + (cursEndY - cursStartY);
				diffX = newX - f.x;
				diffY = newY - f.y;
				if (Math.abs(diffX) >= 2) {			
					f.x = f.x + diffX * damp; 
				}
				if (Math.abs(diffY) >= 2) {
					f.y = f.y + diffY * damp;
				}				
			}
		}		

		public function stopFollow(cursor:Sprite, obj:InteractiveObject) {
			// update the follow array or object
			if (follow[obj]) {
				var len:Number = follow[obj].length;				
				for (var i:uint=0; i<len; i++) {
					if (follow[obj][i][0] == cursor) {
						follow[obj].splice(i,1);
						if (follow[obj].length == 0) {
							delete follow[obj];
							if (scale[obj]) {return;}
							if (!this[GooseEvent.PUT_DOWN]) {return;}
							if (!this[GooseEvent.PUT_DOWN][obj]) {return;}
							var cID:String = cursorLookup[cursor];
							var cZ:Number = currentCursors[cID][1];
							this[GooseEvent.PUT_DOWN][obj](new GooseEvent(GooseEvent.PUT_DOWN,cursor,cZ,cID,obj));							
						}
						break;
					}
				}
			}
		}

		public function stopAllFollows() {
			// clear the follow array or object					
			if (!this[GooseEvent.PUT_DOWN]) {
				follow = new Dictionary();
				return;
			}
			var o:*;
			var c:Sprite;
			var cID:String;
			var cZ:Number;
			for (o in follow) {
				if (scale[o]) {return;}
				if (!this[GooseEvent.PUT_DOWN][o]) {continue;}
				c = follow[o][0][0]; // just get the first cursor as the cursor that puts down
				cID = cursorLookup[c];
				cZ = currentCursors[cID][1];
				this[GooseEvent.PUT_DOWN][o](new GooseEvent(GooseEvent.PUT_DOWN,c,cZ,cID,o));							
			}
			follow = new Dictionary();
		}
		

		//----  SCALE  ---------------------------------------------------		
		
		
		public function startScale(cursor:Sprite, obj:InteractiveObject, damp:Number=.1, proportional:Boolean=true, registration:String=Goose.REGISTRATION_AVERAGE) {
			
			damp = Math.max(0, Math.min(1, damp));			
			if (scale[obj]) {				
				scale[obj].push([cursor, damp, proportional, registration, obj.x, obj.y, cursor.x, cursor.y, obj.width, obj.height]);				
			} else {
				scale[obj] = [[cursor, damp, proportional, registration, obj.x, obj.y, cursor.x, cursor.y, obj.width, obj.height]];
				if (follow[obj]) {return;}
				if (!this[GooseEvent.PICK_UP]) {return;}
				if (!this[GooseEvent.PICK_UP][obj]) {return;}
				var cID:String = cursorLookup[cursor];
				var cZ:Number = currentCursors[cID][1];
				this[GooseEvent.PICK_UP][obj](new GooseEvent(GooseEvent.PICK_UP,cursor,cZ,cID,obj));
			}			
		}

		private function runScale(e:Event) { // called by ENTER_FRAME
			var curs:Sprite;
			var damp:Number;
			var prop:Boolean;
			var reg:String;
			var objStartX:Number;
			var objStartY:Number;
			var objStartW:Number;
			var objStartH:Number;
			var cursStartX:Number;
			var cursStartY:Number;	
			
			var totDamp:Number;
			var totObjStartX:Number;
			var totObjStartY:Number;
			var totObjStartW:Number;
			var totObjStartH:Number;
			var totCursStartX:Number;
			var totCursStartY:Number;
			var totCursX:Number;
			var totCursY:Number;
						
			var newW:Number;
			var newH:Number;
			var diffW:Number;
			var diffH:Number;
			
			var i:uint;
			var len:Number;
			
			for (var obj:* in scale) {				
				totDamp=0;
				totObjStartX=0;
				totObjStartY=0;
				totObjStartW=0;
				totObjStartH=0;
				totCursStartX=0;
				totCursStartY=0;
				totCursX=0;
				totCursY=0;
				// take averages
				len = scale[obj].length;
				if (len <2) {continue;}
				for (i=0; i<len; i++) {
					totDamp+=scale[obj][i][1];
					totObjStartX+=scale[obj][i][4];
					totObjStartY+=scale[obj][i][5];
					totCursStartX+=scale[obj][i][6];
					totCursStartY+=scale[obj][i][7];
					totObjStartW+=scale[obj][i][8];
					totObjStartH+=scale[obj][i][9];
					// take the last prop and reg
					prop = scale[obj][i][2];	
					reg = scale[obj][i][3];	
				}
												
				damp = totDamp/len;							
				objStartX = totObjStartX/len;
				objStartY = totObjStartY/len;
				cursStartX = totCursStartX/len;
				cursStartY = totCursStartY/len;
				objStartW = totObjStartW/len;
				objStartH = totObjStartH/len;
				
				var maxDiffX:Number = -100000;
				var minDiffX:Number = 100000;
				var maxDiffY:Number = -100000;
				var minDiffY:Number = 100000;
				
				var currentMaxX:Number;
				var currentMaxY:Number;
				var currentMinX:Number;
				var currentMinY:Number;
				
				for (i=0; i<len; i++) {
					curs = scale[obj][i][0];
					if (curs.x-scale[obj][i][6] > maxDiffX) {
						maxDiffX = curs.x-scale[obj][i][6];
						currentMaxX = scale[obj][i][6];
					}
					if (curs.y-scale[obj][i][7] > maxDiffY) {
						maxDiffY = curs.y-scale[obj][i][7];
						currentMaxY = scale[obj][i][7];
					}
					if (curs.x-scale[obj][i][6] < minDiffX) {
						minDiffX = curs.x-scale[obj][i][6];
						currentMinX = scale[obj][i][6];
					}
					if (curs.y-scale[obj][i][7] < minDiffY) {
						minDiffY = curs.y-scale[obj][i][7];
						currentMinY = scale[obj][i][7];
					}										
				}
				
				var diffWidth:Number;
				var diffHeight:Number;
				
				if (currentMaxX >= currentMinX) {
					diffWidth = maxDiffX - minDiffX;
				} else {
					diffWidth = minDiffX - maxDiffX;
				}
				
				if (currentMaxY >= currentMinY) {
					diffHeight = maxDiffY - minDiffY;
				} else {
					diffHeight = minDiffY - maxDiffY;
				}
				
				if (prop) { // find largest proportional difference
					if (Math.abs(diffWidth / obj.width) > Math.abs(diffHeight / obj.height)) {
						diffHeight = diffWidth / obj.width * obj.height;
					} else {
						diffWidth = diffHeight / obj.height * obj.width;
					}					
				}
				
				newW = objStartW + diffWidth;
				newH = objStartH + diffHeight;
				diffW = newW - obj.width;
				diffH = newH - obj.height;					
				
				if (obj.width + diffW * damp < 10 || obj.height + diffH * damp < 10) {
					continue;
				}
				if (obj.width + diffW * damp > 2880 || obj.height + diffH * damp > 2880) {
					continue;
				}				
				
				if (Math.abs(diffW) >= 2) {			
					obj.width = obj.width + diffW * damp; 
				}
				if (Math.abs(diffH) >= 2) {
					obj.height = obj.height + diffH * damp;
				}
				if (reg == Goose.REGISTRATION_CENTER) {
					obj.x -= diffW/2 * damp;
					obj.y -= diffH/2 * damp;
				} else if (reg == Goose.REGISTRATION_AVERAGE) {
					obj.x -= diffW * (cursStartX-objStartX) / objStartW  * damp;
					obj.y -= diffH * (cursStartY-objStartY) / objStartH  * damp;
				}
			}
		}	


		public function stopScale(cursor:Sprite, obj:InteractiveObject) {
			// update the scale array or object
			if (scale[obj]) {
				var len:Number = scale[obj].length;				
				for (var i:uint=0; i<len; i++) {
					if (scale[obj][i][0] == cursor) {
						scale[obj].splice(i,1);
						if (scale[obj].length == 0) {
							delete scale[obj];
							if (follow[obj]) {return;}
							if (!this[GooseEvent.PUT_DOWN]) {return;}
							if (!this[GooseEvent.PUT_DOWN][obj]) {return;}
							var cID:String = cursorLookup[cursor];
							var cZ:Number = currentCursors[cID][1];
							this[GooseEvent.PUT_DOWN][obj](new GooseEvent(GooseEvent.PUT_DOWN,cursor,cZ,cID,obj));							
						}
						break;
					}
				}
			}
		}

		public function stopAllScales() {
			// clear the scale array or object					
			if (!this[GooseEvent.PUT_DOWN]) {
				scale = new Dictionary();
				return;
			}
			var o:*;
			var c:Sprite;
			var cID:String;
			var cZ:Number;
			for (o in scale) {				
				if (follow[o]) {return;}
				if (!this[GooseEvent.PUT_DOWN][o]) {continue;}
				c = scale[o][0][0]; // just get the first cursor as the cursor that puts down
				cID = cursorLookup[c];
				cZ = currentCursors[cID][1];
				this[GooseEvent.PUT_DOWN][o](new GooseEvent(GooseEvent.PUT_DOWN,c,cZ,cID,o));							
			}
			scale = new Dictionary();
		}	
		
		
		//-------------------------------------------------------		

			

		public function get showCursors():Boolean {
			return myShowCursors;
		}

		public function set showCursors(b:Boolean) {
			myShowCursors = b;
			if (myShowCursors) {
				myCursors.alpha = 1;
			} else {
				myCursors.alpha = 0;
			}
		}
		
		public function dispose() {
			removeEventListener(Event.ENTER_FRAME, runFollow);
			removeEventListener(Event.ENTER_FRAME, runScale);			
			press=null;
			pressDown=null;
			pressUp=null;
			pressMove=null;
			pickUp=null;
			putDown=null;
			currentPresses=null;
			cursorLookup=null;
			follow=null;
			scale=null;		
			removeChild(myCursors);
			myCursors=null;
			currentCursors=null;
		}


	}
}

package com.danzen.interfaces.goose {
	
	import flash.display.Sprite;
    import flash.events.*;
	import flash.utils.Timer;
	
	// GOOSE OVERVIEW  
	// In general, Goose lets you work with multiple cursor inputs - like multitouch
	// Goose splits into two parts:
	
	// 1. A multitouch emulator (GooseData, GooseRobin, Online Mouse Nodes)
	// 		with the Goose Emulator you run the online Mouse Nodes on two or more computers
	//		each mouse then feeds data through GooseRobin to GooseData
	//		you then feed the data from GooseData into Goose (part 2)	
	
	// 2. A custom class to process the data (Goose, GooseEvent)
	//		Goose takes in multitouch data (like from the emulator in part 1)
	//		and shows multiple cursors in your application
	//		so if you use the Goose Emulator then each mouse shows up as a cursor
	//		Goose and GooseEvent provide a set of methods and events for using the cursors
	//      You continue to use Goose when going from an emulator to real multitouch data
	
	// GOOSEDATA  	
	// GooseData uses GooseRobin to receive multiple mouse data from different computers
	// mouse nodes are available at http://www.danzen.com/goose/node.html
	// or if you set up Robin yourself then you can make and administer your own nodes
	// Robin is available at http://robinflash.wordpress.com
	// if you do use your own Robin then you need to change the server address
	// in GooseRobin and the MultiuserEmulator
	// this would make sure that you have control over maintenance of the system
	// it also lets you try out Robin - the PHP multiuser server
	
	// you can also write your own data class to send data into Goose
	// Goose is set up so that once you emulate, you can switch over to real data
	// so if you run your own Blob Detect table, you can feed that data in to Goose
	// see http://dodoflash.wordpress.com for a Blob Detection option
	// the format of the data is quite simple and is available at http://multiml.wordpress.com
	// if you have a different data format, it will be quite easy for you to convert it
	
	// USING GOOSEDATA
	// make sure that you have the provided com folder in a folder that is in your class path
	// import com.danzen.interfaces.goose.* in your document class
	// create a new GooseData object and pass it the name of your application
	// create a Goose object in your document class
	// add an Event.CHANGE event to your GooseData object and have it call a method
	// in the method pass the GooseData's xmlData property to the Goose update() method
	
	// this separation allows you to easily change from the emulator to real multitouch data
	// when you are ready to do so, just pass the real data to the Goose update() method
	// the data must be in the form of ManyML:
	
	// <manyml>
	// <item id="1000" x="984" y="1" z="-100" />
	// <item id="2000" x="243" y="7" z="0" />
	// </manyml>
	
	// if z >= 0 then the item is treated as a touch in Goose
	// if z < 0 then the item is ignored by all events in Goose
	// see Goose for more information on how to use Goose
	
		
	public class GooseData extends Sprite {
		
		public var xmlData:XML;
		
		private var myRobin:GooseRobin;		
		private var myAppName:String = "goose"; // leave this as goose
		
		private var lastX:Number=0;
		private var lastY:Number=0;
		private var lastType
		
		private var myTimer:Timer;
		private var dataPath:String;		
		private var period:Number = 200;
				
        public function GooseData(theAppName:String) {
			trace ("hi from GooseData");
			myAppName += "_" + theAppName;
			myAppName = myAppName.replace(/[\n\r]/g,"");
			myAppName = myAppName.replace(/\s/g,"");
			myRobin = new GooseRobin(myAppName);			
			myRobin.addEventListener(Event.CONNECT, myRobinConnected);			
			myRobin.addEventListener(DataEvent.DATA, myRobinChange);			
        }		
		private function myRobinConnected(e:Event) {			
			trace ("connected");
			dispatchEvent(new Event(Event.CONNECT));			
		}
				
		private function myRobinChange(e:Event) {
			var theXML:String = "<manyml>\n";
			if (myRobin.getProperties("x")) {
				for (var i:uint = 0; i < myRobin.getProperties("x").length; i++) {	
					if (myRobin.getProperties("id")[i] == "") {continue;}
					theXML += '<item id="' + myRobin.getProperties("id")[i] + '" x="' + myRobin.getProperties("x")[i] + '" y="' + myRobin.getProperties("y")[i] + '" z="' + myRobin.getProperties("z")[i] + '" />\n';
				}
			}
			theXML += "</manyml>";
			xmlData = XML(theXML);
			dispatchEvent(new Event(Event.CHANGE));
		}
		
		public function dispose() {
			myRobin.removeEventListener(Event.CONNECT, myRobinConnected);			
			myRobin.removeEventListener(DataEvent.DATA, myRobinChange);	
			myRobin.dispose();
		}		
    }
} 

package com.danzen.interfaces.goose {

	import flash.display.Sprite;
	import flash.display.InteractiveObject;
    import flash.events.*;	
	
    public class GooseEvent extends Event {
		
		public static const TOUCH:String = "touch";
		public static const DOUBLE_TOUCH:String = "doubleTouch"; // not active
		public static const TOUCH_DOWN:String = "touchDown";
		public static const TOUCH_UP:String = "touchUp";
		public static const TOUCH_MOVE:String = "touchMove";
		
		public static const PRESS:String = "press";
		public static const DOUBLE_PRESS:String = "doublePress"; // not active
		public static const PRESS_DOWN:String = "pressDown";
		public static const PRESS_UP:String = "pressUp";
		public static const PRESS_MOVE:String = "pressMove";	
		
		public static const PICK_UP:String = "pickUp"; // for first follow or size
		public static const PUT_DOWN:String = "putDown"; // for last follow or size
		
		public var cursor:Sprite;
		public var cursorZ:Number;
		public var cursorID:String;
		public var obj:InteractiveObject;
		private var t:String;
		private var b:Boolean;
		private var c:Boolean;		
						
		public function GooseEvent(type:String,
								   theCursor:Sprite,								   
								   theCursorZ:Number,
								   theCursorID:String,
								   theObject:InteractiveObject = null,
								   bubbles:Boolean = false,
								   cancelable:Boolean = false) {
			t = type;
			b = bubbles;
			c = cancelable;			
			super (t,b,c);
			cursor = theCursor;	
			cursorZ = theCursorZ;
			cursorID = theCursorID;			
			obj = theObject;
		}
		
		public override function clone():Event {			
			return new GooseEvent(t,cursor,cursorZ,cursorID,obj,b,c);
		}
		
		public override function toString():String {
			return formatToString("GooseEvent","type","theCursor","theCursorZ","theCursorID","theObject","bubbles","cancelable","eventPhase");
		}	

    }
} 

		
package com.danzen.interfaces.goose  {
	
	// ROBIN INTRODUCTION  

	// Robin is a custom Flash AS3 class with supporting PHP 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
	// 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
	
	// this is a modified Robin to call the same server as the Goose Emulator nodes
	// the Goose Emulator Nodes are at http://www.danzen.com/goose/node.html
	
	// if you want more control over your system - say to hard code the application name
	// or make sure that if Robin goes down that you can restart it yourself
	// then please consider downloading and installing Robin (the URL is above)
	// you would then make a simple change in the server address below and in the emulator
	// if you do not use your own version of Robin then you do not need to adjust anything here
	
	// Note that your Flash application will not work on the Web with Goose Emulator Data
	// to make your application work you need to substitute the Goose data with real data
	// the Goose Emulator is only for you to build and test your application
		
    import flash.display.Sprite;
    import flash.events.*;	
	import flash.net.XMLSocket;	
	import flash.utils.Timer;
	
	public class GooseRobin extends Sprite {		

		// just put your server domain without http:// for example www.danzen.com
		private var myServer:String = "www.danzen.com";  
		private var myPort:Number = 9998;
		
		// CONSTRUCTOR
		// Robin(theApplicationName:String, theMaxPeople:uint = 0, theFill:Boolean = true, theStartProperties:Object = null):void
		//		constructor to connect your application to the robin.php socket for multiuser functionality
		//		robin.php 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
		//			theStartProperties:Object
		//				you can start off with properties set - send them in an object {prop1:numValue, prop2:"String Value, etc."}		
		//		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 and initial conditions: 
		//				var myRobin:Robin = new Robin("FreezeTag", 10, false, {x:200, y:300, clan:"Cyborg Rabbits"});		
		
		// EVENTS
		// IOErrorEvent.IO_ERROR
		//		trouble connecting - make sure robin.php 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)
		// SyncEvent.SYNC
		//		dispatched when any data is changed including yours (use for latest properties - equivalent to Flash remote SharedObject)
		// Event.CLOSE
		//		the socket is closed - could be that robin.php stops for some reason - all data on the server will be lost
		
		// 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 rooms values for the property you pass to it
		// 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
		// getLatestValueIndex(propertyName:String):Number
		//		returns the index number of the last person to distribute a value for the property you pass to it
		// getLatestProperties(propertyName:String):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 socket, deletes data objects
		
		// PROPERTIES (READ ONLY)		
		// 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
		// fill:Boolean - see CONSTRUCTOR
		// startProperties:Object - 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

		// 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 myStartProperties:Object;
		private var mySenderIndex:Number;
		private var myHistory:String = "";

		// 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;		
				
		public function GooseRobin (
				theApplicationName:String,							  
				theMaxPeople:uint = 0, 
				theFill:Boolean = true, 
				theStartProperties:Object = null
								) { 				
		
			trace ("hi from GooseRobin");			
			
			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;
			myStartProperties = theStartProperties;
						
			mySocket = new XMLSocket();
			getPolicy();
			makeConnection();
		}
		
		// --------------------  PRIVATE METHODS  -------------------------------
		
		private function getPolicy() {
			mySocket.connect(myServer, myPort);
			mySocket.addEventListener(IOErrorEvent.IO_ERROR, function(e:Event){}); // nothing to do	
			mySocket.close();
		}
		
		private function makeConnection() {	
			mySocket.connect(myServer, myPort);
			mySocket.addEventListener(Event.CONNECT, handleMeta);			
			mySocket.addEventListener(Event.CLOSE, handleMeta);			
			mySocket.addEventListener(IOErrorEvent.IO_ERROR, handleMeta);			
			mySocket.addEventListener(DataEvent.DATA, incomingData);		
		}		
		
		private function handleMeta(e:Event) {	
			switch(e.type){
				case 'ioError': 
					trace ("error");
					dispatchEvent(new Event(IOErrorEvent.IO_ERROR));
				break;				
				case 'connect':
					if (!initializationCheck) {

						initializationCheck = true;
						sendData();
						myTimer = new Timer(50, 1); // need to double send for some reason
						myTimer.addEventListener(TimerEvent.TIMER, init);
						myTimer.start();					
					}
				break;				
				case 'close': 
					trace ("close");
					dispatchEvent(new Event(Event.CLOSE));
				break; 							
			} 
		}		
		
		private function init(e:TimerEvent) {
			if (myStartProperties) {
				setProperties(myStartProperties);
			} else {
				sendData();
			}			
		}
		
		private function incomingData(e:DataEvent) {
			
			// first time data is the history file
			// followed by -~^~-\n line then the latest users for the properties
			// _u_^1    // the second user was last to update
			// text^0	// the first user was the last to update the text
			// x^1 		// the second user was the last to update x and y
			// y^1
			// followed by -~^~- line then the user data with -1 as the mySenderIndex
			
			var latestData:String
			var incomingData:String;
			
			if (!connectCheck) {
				var feed:Array = String(e.data).split("-~^~-\n");
				myHistory = decode(feed[0]);
				latestData = feed[1];
				incomingData = feed[2];	
			}	else {
				incomingData = String(e.data);
			}			
		
			// 0
			// x|y
			// _u_^12|22
			// text^test|hello
			// x^27|30
			// y^25|200
			var lines:Array = incomingData.split("\n");
			mySenderIndex = lines.shift();
			var updatedProperties:String = lines.shift();
						
			if (connectCheck == false || Number(mySenderIndex) != -1) {											
				theirData = {};
				var temp:Array;
				var prop:String;
				for (var i:uint=0; i<lines.length; i++) {
					if (lines[i] == "") {continue;}
					temp = lines[i].split("^");
					prop = temp[0];
					if (temp[1] != "") {
						theirData[prop] = decode(temp[1]).split("|");
					}
				}
			}			
			
			// set the latest information
			// note... the initial variables sent when instantiating Robin are not used for latest information
			
			if (connectCheck == false) {
				
				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("^");
						latestIndexes[latestVV[0]] = Number(latestVV[1]);						
						latestValues[latestVV[0]] = getProperties(latestVV[0])[Number(latestVV[1])];						
					}
				}
			} else {			
				if (updatedProperties == "") {
					// somebody left and may want to set any latestIndexes to -2
				} else {				
					latestProperties = updatedProperties.split("|");
					for (var ii:uint=0; ii<latestProperties.length; ii++) {
						if (mySenderIndex == -1) { // this is your own property
							latestValues[latestProperties[ii]] = getProperty(latestProperties[ii]);
						} else {
							latestValues[latestProperties[ii]] = getSenderProperty(latestProperties[ii]);							
						}
						latestIndexes[latestProperties[ii]] = mySenderIndex;
					}
				}	
			}
			
			dispatchEvent(new SyncEvent(SyncEvent.SYNC, false, false, []));

			if (connectCheck == false) {
				dispatchEvent(new Event(Event.CONNECT));	
				connectCheck = true;
			} else if (mySenderIndex != -1) {				
				dispatchEvent(new DataEvent(DataEvent.DATA, false, false, String(e.data)));
			}

		}			
		
		private function sendData(s:String="") {
			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 robin.php code
			mySocket.send(prefix+s);			
		}			
		
		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,"^~^");
			return w;
		}
		
		private function decode(w:String):String {
			w = w.replace(/`~`/g,"\n");
			w = w.replace(/^~^/g,"|");
			return w;
		}		
		
		// --------------------  PUBLIC METHODS  -------------------------------

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

		public function setProperties(o:Object) {
			// set a bunch of my properties at once with an object
			var vars = "";
			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(w:String):Array {			
			return latestProperties;
		}				
		
		public function appendToHistory(t:String="") {
			sendData("^_history_=" + encode(String(t)));
		}		
		
		public function clearHistory() {
			sendData("^_clearhistory_=");
		}			
		
		public function dispose() {
			mySocket.removeEventListener(Event.CONNECT, handleMeta);					
			mySocket.removeEventListener(IOErrorEvent.IO_ERROR, handleMeta);			
			mySocket.removeEventListener(DataEvent.DATA, incomingData);
			mySocket.close();
			mySocket.removeEventListener(Event.CLOSE, handleMeta);	
			mySocket = 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 fill():Boolean {
			return myFill;
		}
		public function get startProperties():Object {
			return myStartProperties;
		}
		public function get senderIndex():Number {
			return mySenderIndex;
		}
		public function get history():String {
			return myHistory;
		}		
			
	
	}
	
}