diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/api/APIEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/api/APIEventMap.mxml
new file mode 100755
index 0000000000000000000000000000000000000000..3a9ec537caa58bbd2025da25f5b74475fde98d95
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/api/APIEventMap.mxml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<EventMap xmlns="http://mate.asfusion.com/" xmlns:mx="http://www.adobe.com/2006/mxml" >
+	<mx:Script>
+		<![CDATA[
+			import mx.controls.Alert;
+			import mx.events.FlexEvent;
+			
+			import org.bigbluebutton.main.events.ParticipantJoinEvent;
+			import org.bigbluebutton.main.events.PresenterStatusEvent;
+			
+			private var userManager:UserManager = UserManager.getInstance();
+			
+			private function participantEvent(event:ParticipantJoinEvent):void{
+				if (event.join) userManager.participantJoined(event.participant);
+				else if (!event.join) userManager.participantLeft(event.participant);
+			}
+			
+			private function presenterChanged(event:PresenterStatusEvent):void{
+				userManager.presenterChanged(event.userid);
+			}
+		]]>
+	</mx:Script>
+	
+	<EventHandlers type="{ParticipantJoinEvent.PARTICIPANT_JOINED_EVENT}" >
+		<InlineInvoker method="participantEvent" arguments="{event}" />
+	</EventHandlers>
+	
+	<EventHandlers type="{PresenterStatusEvent.PRESENTER_NAME_CHANGE}" >
+		<InlineInvoker method="presenterChanged" arguments="{event}" />
+	</EventHandlers>
+	
+</EventMap>
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/api/IUserListener.as b/bigbluebutton-client/src/org/bigbluebutton/main/api/IUserListener.as
new file mode 100755
index 0000000000000000000000000000000000000000..6ce8a4918ecfaf48016f00a74f5ea6016e0ab12f
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/api/IUserListener.as
@@ -0,0 +1,22 @@
+package org.bigbluebutton.main.api
+{
+	import org.bigbluebutton.main.model.User;
+
+	public interface IUserListener
+	{
+		/**
+		 * Called when a new user has joined
+		 */
+		function userJoined(user:User):void;
+		
+		/**
+		 * Called when a user has left
+		 */
+		function userLeft(user:User):void;
+		
+		/**
+		 * Called when the presenter has changed
+		 */
+		function presenterChanged(newPresenter:User):void;
+	}
+}
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/api/UserManager.as b/bigbluebutton-client/src/org/bigbluebutton/main/api/UserManager.as
new file mode 100755
index 0000000000000000000000000000000000000000..516058184208488671627e451f723127a2815df5
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/api/UserManager.as
@@ -0,0 +1,98 @@
+package org.bigbluebutton.main.api
+{
+	import mx.collections.ArrayCollection;
+	
+	import org.bigbluebutton.common.Role;
+	import org.bigbluebutton.main.model.User;
+
+	public class UserManager
+	{
+		private static var instance:UserManager = null;
+		
+		private var listeners:ArrayCollection;
+		
+		private var users:ArrayCollection;
+		
+		public function UserManager(enforcer:SingletonEnforcer)
+		{
+			if (enforcer == null){
+				throw new Error("There can only be 1 UserManager instance");
+			}
+			initialize();
+		}
+		
+		private function initialize():void{
+			listeners = new ArrayCollection();
+			users = new ArrayCollection();
+		}
+		
+		/**
+		 * Return the single instance of the UserManager class, which is a singleton
+		 */
+		public static function getInstance():UserManager{
+			if (instance == null){
+				instance = new UserManager(new SingletonEnforcer());
+			}
+			return instance;
+		}
+		
+		/**
+		 * Register a class to listen to updates from the UserManager
+		 */ 
+		public function registerListener(listener:IUserListener):void{
+			listeners.addItem(listener);
+		}
+		
+		/**
+		 * Returns an ArrayCollection of User objects, containing all users currently in the room.
+		 */
+		public function getUserList():ArrayCollection{
+			return users;
+		}
+		
+		/**
+		 * Returns the current Presenter. Returns NULL if there is currently no presenter assigned in the room
+		 */
+		public function getPresenter():User{
+			var j:int = 0;
+			var u:User = null;
+			for (var i:int = 0; i<users.length; i++){
+				if ((users.getItemAt(i) as User).isPresenter) u = users.getItemAt(i) as User;
+			}
+			return u;
+		}
+		
+		internal function participantJoined(participant:User):void{
+			users.addItem(participant);
+			for (var i:int = 0; i<listeners.length; i++){
+				(listeners.getItemAt(i) as IUserListener).userJoined(participant);
+			}
+		}
+		
+		internal function participantLeft(participant:User):void{
+			var j:int = -1;
+			for (var i:int = 0; i<users.length; i++){
+				if ((users.getItemAt(i) as User).userid == participant.userid) j = i;
+			}
+			if (j >= 0) users.removeItemAt(j);
+			
+			for (var k:int = 0; k<listeners.length; k++){
+				(listeners.getItemAt(k) as IUserListener).userLeft(participant);
+			}
+		}
+		
+		internal function presenterChanged(userId:int):void{
+			var user:User = null;
+			
+			for (var i:int = 0; i<users.length; i++){
+				if ((users.getItemAt(i) as User).userid == userId.toString()) user = users.getItemAt(i) as User;
+			}
+			
+			for (var k:int = 0; k<listeners.length; k++){
+				(listeners.getItemAt(k) as IUserListener).presenterChanged(user);
+			}
+		}
+	}
+}
+
+class SingletonEnforcer{}
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/User.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/User.as
new file mode 100755
index 0000000000000000000000000000000000000000..7833a31948116d3d548eefed2b08f67861f81b2a
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/User.as
@@ -0,0 +1,13 @@
+package org.bigbluebutton.main.model
+{
+	public class User
+	{
+		public var room:String;
+		public var conference:String;
+		public var userid:String;
+		public var name:String;
+		public var role:String;
+		public var isPresenter:Boolean;
+		public var authToken:String;
+	}
+}
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/viewers/model/vo/BBBUser.as b/bigbluebutton-client/src/org/bigbluebutton/modules/viewers/model/vo/BBBUser.as
new file mode 100755
index 0000000000000000000000000000000000000000..dc51485c44bb9f2aa3679485931439b523cc542a
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/viewers/model/vo/BBBUser.as
@@ -0,0 +1,133 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2008 by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 2.1 of the License, or (at your option) any later
+* version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+* 
+*/
+package org.bigbluebutton.modules.viewers.model.vo
+{
+	import com.asfusion.mate.events.Dispatcher;
+	
+	import mx.collections.ArrayCollection;
+	import mx.controls.Alert;
+	
+	import org.bigbluebutton.common.Role;
+	import org.bigbluebutton.modules.viewers.view.events.StreamStartedEvent;
+	
+	public class BBBUser
+	{
+		[Bindable] public var me:Boolean = false;
+		[Bindable] public var userid:Number;
+		[Bindable] public var name:String;
+		[Bindable] public var hasStream:Boolean = false;
+		[Bindable] public var streamName:String = "";
+		[Bindable] public var presenter:Boolean = false;
+		[Bindable] public var raiseHand:Boolean = false;
+		[Bindable] public var role:String = Role.VIEWER;	
+		[Bindable] public var room:String = "";
+		[Bindable] public var authToken:String = "";
+		
+		private var _status:StatusCollection = new StatusCollection();
+				
+		public function get status():ArrayCollection {
+			return _status.getAll();
+		}
+		
+		public function set status(s:ArrayCollection):void {
+			_status.status = s;
+		}	
+			
+		public function addStatus(status:Status):void {
+			_status.addStatus(status);
+		}
+		
+		public function changeStatus(status:Status):void {
+			//_status.changeStatus(status);
+			if (status.name == "presenter") {
+				presenter = status.value
+			}
+			switch (status.name) {
+				case "presenter":
+					presenter = status.value;
+					break;
+				case "hasStream":
+					hasStream = status.value;
+					if (hasStream) sendStreamStartedEvent();
+					break;
+				case "streamName":
+					streamName = status.value as String;
+					break;
+				case "raiseHand":
+					raiseHand = status.value as Boolean;
+					break;
+			}
+		}
+		
+		public function removeStatus(name:String):void {
+			_status.removeStatus(name);
+		}
+		
+		public function getStatus(name:String):Status {
+			return _status.getStatus(name);
+		}
+		/*
+		public var me:Boolean = false;
+		public var userid:Number;
+		public var name:String;
+		
+		public var role:String = Role.VIEWER;	
+		public var room:String = "";
+		public var authToken:String = "";
+		*/
+		/**
+		 * This is a workaround until we figure out how to make 
+		 * status Bindable in StatusItemRenderer.mxml (ralam 2/20/2009)
+		 */
+/*		private var _status:Object;
+		public var streamName:String = "";
+		public var presenter:Boolean = false;
+		public var hasStream:Boolean = false;
+		
+		public function set status(s:Object):void {
+			_status = s;
+			hasStream = s["hasStream"];
+			presenter = s["presenter"];
+			streamName = s["streamName"];
+		}
+	*/
+	
+		public static function copy(user:BBBUser):BBBUser {
+			var n:BBBUser = new BBBUser();
+			n.authToken = user.authToken;
+			n.me = user.me;
+			n.userid = user.userid;
+			n.name = user.name;
+			n.hasStream = user.hasStream;
+			n.streamName = user.streamName;
+			n.presenter = user.presenter;
+			n.raiseHand = user.raiseHand;
+			n.role = user.role;	
+			n.room = user.room;
+			
+			return n;		
+		}
+		
+		private function sendStreamStartedEvent():void{
+			var dispatcher:Dispatcher = new Dispatcher();
+			dispatcher.dispatchEvent(new StreamStartedEvent(this.name, this.streamName));
+		}
+	}
+}
\ No newline at end of file