diff --git a/bigbluebutton-client/build.xml b/bigbluebutton-client/build.xml
old mode 100644
new mode 100755
index 3927e5cc82743e06d90b646514c5729de11029cf..3db904aa6d9cb43ed4d38e4ed7fb61665229e6eb
--- a/bigbluebutton-client/build.xml
+++ b/bigbluebutton-client/build.xml
@@ -29,11 +29,24 @@
 	<property name="BREAKOUT" value="BreakoutModule" />
 	<property name="CLASSROOM_AUDIO" value="ClassroomAudioModule" />
 	<property name="SETTINGS" value="SettingsModule" />
+	<property name="VIDEO_DOCK" value="VideodockModule" />
+	
+	<property name="AVAILABLE_LOCALES" value="az_AZ,bg_BG,cs_CZ,da_DK,de_DE,el_GR,en_US,es_ES,es_LA,fa_IR,fr_FR,fr_CA,hr_HR,hu_HU,id_ID,it_IT,ja_JP,ko_KR,lv_LV,lt_LT,nb_NO,nl_NL,pl_PL,pt_BR,pt_PT,ro_RO,ru_RU,sv_SE,sr_RS,sr_SR,th_TH,tr_TR,vi_VN,uk_UA,zh_CN,zh_TW"/>
+	
+	<xmlproperty file="${SRC_DIR}/conf/locales.xml" collapseAttributes="true"/>
+	
 	<target name="init-ant-contrib">
 		<property name="ant-contrib.jar" location="${BASE_DIR}/build/lib/ant-contrib-0.6.jar"/>
 		<taskdef resource="net/sf/antcontrib/antcontrib.properties" classpath="${ant-contrib.jar}"/>
 	</target>
 	
+	<target name="localization" depends="init-ant-contrib">
+		<echo message="Parsing ${SRC_DIR}/conf/locales.xml for supported locales"/>
+		<echo message="Available locales ${AVAILABLE_LOCALES}"/>
+		<!--foreach list="${locales.locale.code}" target="build-locale" param="supportedlocale" delimiter=","/-->
+		<foreach list="${AVAILABLE_LOCALES}" target="build-locale" param="supportedlocale" delimiter=","/>
+	</target>
+	
 	<target name="branding" depends="init-ant-contrib">
 		<sequential>
 			<mxmlc file="${SRC_DIR}/branding/css/${themeFile}" output="${OUTPUT_DIR}/branding/css/${themeFile}.swf" debug="false" mxml.compatibility-version="3.0.0" swf-version="13" optimize="true">
@@ -45,6 +58,64 @@
                 <build-main src="${SRC_DIR}" target="${BBB_MAIN_TEST}" />
 	</target>
 
+	<target name="build-locale">
+		<echo message="Building ${supportedlocale}"/>
+		<available file="${LOCALE_DIR}/${supportedlocale}" type="dir" property="locale.dir.present"/>
+		<if> 
+           		<equals arg1="${locale.dir.present}" arg2="true"/> 
+           	<then> 
+              		<echo message="No need to copy ${LOCALE_DIR}/${supportedlocale}"/> 
+           	</then> 
+           	<else> 
+              		<echo message="Need to copy ${LOCALE_DIR}/${supportedlocale}"/> 
+              		<exec dir="${BASE_DIR}" vmlauncher="true" executable="copylocale">
+				<arg value="en_US"/>
+    				<arg value="${supportedlocale}"/>
+			</exec>
+           	</else> 
+        	</if>
+        	<compileLocale locale="${supportedlocale}" />        
+	</target>
+		
+	<macrodef name="compileLocale" description="Compiles the Resource package for the given locale">
+		<attribute name="locale" default="en_US"/>
+		<sequential>
+			<echo message="Building @{locale}"/>
+		<available file="${LOCALE_DIR}/@{locale}" type="dir" property="locale.dir.present"/>
+		<if> 
+           <equals arg1="${locale.dir.present}" arg2="true"/> 
+           <then> 
+              <echo message="No need to copy ${LOCALE_DIR}/@{locale}"/> 
+           </then> 
+           <else> 
+              <echo message="Need to copy ${LOCALE_DIR}/@{locale}"/> 
+              <exec dir="${BASE_DIR}" vmlauncher="true" executable="copylocale">
+				<arg value="en_US"/>
+    			<arg value="@{locale}"/>
+			</exec>
+           </else> 
+        </if>
+        
+			<!--
+			Create the Flex Home directory for the language in question.
+			This is necessary to compensate for a bug in pre-3.2 releases of 
+			mxmlc.
+			
+			<mkdir dir="${FLEX_HOME}/frameworks/locale/@{locale}"/>-->
+			
+			<!-- Invoke MXMLC -->
+			<mxmlc output="${OUTPUT_DIR}/locale/@{locale}_resources.swf">
+				<locale>@{locale}</locale>
+				<target-player>10.3.0</target-player>
+				<source-path path-element="locale/{locale}"/>
+				<include-resource-bundles>bbbResources</include-resource-bundles>
+				<include-resource-bundles>core</include-resource-bundles>
+				<include-resource-bundles>controls</include-resource-bundles>
+				<source-path path-element="${FLEX_HOME}/frameworks"/>
+			</mxmlc>
+		</sequential>
+	</macrodef>
+	
 	<target name="build-bbb-main" description="Compile BigBlueButton Main">
 		<build-main src="${SRC_DIR}" target="${BBB_MAIN}" />
 		
@@ -110,7 +181,11 @@
 	<target name="build-video" description="Compile Video Module">
 		<build-module src="${SRC_DIR}" target="${VIDEO}" />
 	</target>
-	
+
+	<target name="build-videodock" description="Compile Video Dock Module">
+		<build-module src="${SRC_DIR}" target="${VIDEO_DOCK}" />
+	</target>
+		
 	<target name="build-whiteboard" description="Compile Whiteboard Module">
 		<build-module src="${SRC_DIR}" target="${WHITEBOARD}" />
 	</target>
@@ -127,7 +202,7 @@
 	
 	<!-- just a grouping of modules to compile -->
 	<target name="build-deskshare-phone-video-whiteboard-dyn" 
-			depends="build-deskshare, build-phone, build-video, build-whiteboard, build-dyn, build-classroom-audio, build-settings"
+			depends="build-deskshare, build-phone, build-video, build-videodock, build-whiteboard, build-dyn, build-classroom-audio, build-settings"
 			description="Compile deskshare, phone, video, whiteboard, dynamic info modules">
 	</target>
 	
@@ -309,7 +384,7 @@
 				to be compiled withouth being optimized by using the linker -->
 	<target name="clean-build-bbb" depends="clean, init-ant-contrib, generate-html-wrapper, compile-deskshare-standalone, compile-bbb" 
 			description="Build BBB client skipping compiling of locales"/>	
-	<target name="clean-build-all" depends="clean, init-ant-contrib, generate-html-wrapper, compile-deskshare-standalone, compile-bbb" 
+	<target name="clean-build-all" depends="clean, init-ant-contrib, generate-html-wrapper, localization, compile-deskshare-standalone, compile-bbb" 
 			description="Build BBB client including locales"/>
 	<target name="modules" depends="init-ant-contrib, generate-html-wrapper, compile-deskshare-standalone, compile-bbb"
                         description="Build BBB client without locales"/>		
diff --git a/bigbluebutton-client/locale/en_US/bbbResources.properties b/bigbluebutton-client/locale/en_US/bbbResources.properties
index 8eb0d3f841af9335b05129f122bc30c141f37861..4b527d5c5d7c46dc9e2576522fd4cb35379e07a4 100755
--- a/bigbluebutton-client/locale/en_US/bbbResources.properties
+++ b/bigbluebutton-client/locale/en_US/bbbResources.properties
@@ -122,6 +122,9 @@ bbb.publishVideo.title = Share your webcam
 bbb.publishVideo.startPublishBtn.toolTip = Start Sharing
 bbb.video.publish.close.tooltip = Stop sharing your video
 bbb.video.publish.close.label = Close
+bbb.video.keepAspectBtn.tooltip = Keep window aspect
+bbb.video.fitVideoBtn.tooltip = Fit video
+bbb.video.originalSizeBtn.tooltip = Original size
 
 # Desktop Sharing
 bbb.desktopPublish.title = Desktop Sharing: Presenter's Preview
@@ -183,3 +186,5 @@ bbb.settings.warning.label = Warning
 bbb.settings.warning.close = Close this Warning
 bbb.settings.noissues = No outstanding issues have been detected.
 bbb.settings.instructions = Accept the Flash prompt that asks you for camera permissions. If you can see yourself and  hear yourself, your browser has been set up correctly. Other potentials issues are shown bellow. Click on each to find a possible solution.
+
+bbb.videodock.title = Video dock
\ No newline at end of file
diff --git a/bigbluebutton-client/resources/config.xml.template b/bigbluebutton-client/resources/config.xml.template
index 60ab2daf79694c00701f8b48fc251771486757b8..a37bf7aab5a7ad532232dce3cb913db8d3deb9de 100755
--- a/bigbluebutton-client/resources/config.xml.template
+++ b/bigbluebutton-client/resources/config.xml.template
@@ -94,6 +94,12 @@
 			dependsOn="ViewersModule"
 		/>
 
+		<!--<module name="VideodockModule" url="VideodockModule.swf?v=VERSION"
+			uri="rtmp://HOST/bigbluebutton" 
+			dependsOn="VideoconfModule"
+			autoDock="true"
+		/>-->
+		
 		<!-- new module in development: 
 		<module name="DynamicInfoModule" url="DynamicInfoModule.swf?v=VERSION" 
 			uri="rtmp://HOST/bigbluebutton" 
diff --git a/bigbluebutton-client/src/VideodockModule.mxml b/bigbluebutton-client/src/VideodockModule.mxml
new file mode 100755
index 0000000000000000000000000000000000000000..87315b5ba57730ce43a7d317422f429d28feadd5
--- /dev/null
+++ b/bigbluebutton-client/src/VideodockModule.mxml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="400" height="300" 
+		   xmlns:maps="org.bigbluebutton.modules.videodock.maps.*" implements="org.bigbluebutton.common.IBigBlueButtonModule">
+	<mx:Script>
+		<![CDATA[
+			import org.bigbluebutton.common.LogUtil;
+			
+			private var _moduleName:String = "Videodock Module";			
+			private var _attributes:Object; 
+			
+			private function onCreationComplete():void {
+				LogUtil.debug("VideodockModule initialized");	
+			}
+			
+			public function get moduleName():String {
+				return _moduleName;
+			}
+			
+			public function get autoDock():Boolean {
+				return (_attributes.autoDock == "true");
+			}
+			
+			public function start(attributes:Object):void {	
+				LogUtil.debug("Videodock attr: " + attributes.username);
+				_attributes = attributes;
+				
+				eventMap.module = this;
+				eventMap.startModule();
+			}
+			
+			public function stop():void {
+				eventMap.stopModule();
+			}
+			
+		]]>
+	</mx:Script>
+	
+	<maps:VideoDockEventMap id="eventMap"/>
+</mx:Module>
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/common/Images.as b/bigbluebutton-client/src/org/bigbluebutton/common/Images.as
index adaaca2328c8b032abe33532752e89a1c3bcbd18..6b4773ef7f44c44eda52805a77a51d974863394c 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/common/Images.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/common/Images.as
@@ -141,5 +141,17 @@ package org.bigbluebutton.common
 		
 		[Embed(source="assets/images/presenter.png")]
 		public var presenter:Class;
+		
+		[Embed(source="assets/images/lock_open.png")]
+		public var lock_open:Class;
+		
+		[Embed(source="assets/images/lock_close.png")]
+		public var lock_close:Class;
+		
+		[Embed(source="assets/images/arrow_in.png")]
+		public var arrow_in:Class;
+		
+		[Embed(source="assets/images/shape_handles.png")]
+		public var shape_handles:Class;		
 	}
 }
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/arrow_in.png b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/arrow_in.png
new file mode 100755
index 0000000000000000000000000000000000000000..745c65134db478a64016d63a7104e585452f2b9f
Binary files /dev/null and b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/arrow_in.png differ
diff --git a/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/lock_close.png b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/lock_close.png
new file mode 100755
index 0000000000000000000000000000000000000000..2ebc4f6f9663e32cad77d67ef93ab8843dfea3c0
Binary files /dev/null and b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/lock_close.png differ
diff --git a/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/lock_open.png b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/lock_open.png
new file mode 100755
index 0000000000000000000000000000000000000000..a471765ff1432092e7113e56b42f2798968b443f
Binary files /dev/null and b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/lock_open.png differ
diff --git a/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/shape_handles.png b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/shape_handles.png
new file mode 100755
index 0000000000000000000000000000000000000000..ce27fe3a0345e03e919b54ca3b6a8498743b2ee9
Binary files /dev/null and b/bigbluebutton-client/src/org/bigbluebutton/common/assets/images/shape_handles.png differ
diff --git a/bigbluebutton-client/src/org/bigbluebutton/common/events/CloseWindowEvent.as b/bigbluebutton-client/src/org/bigbluebutton/common/events/CloseWindowEvent.as
old mode 100644
new mode 100755
index 6885264784127db4520343779dec95db97cc05f6..fa503285ad5c02d9031cb3df9901bb79a0b6256f
--- a/bigbluebutton-client/src/org/bigbluebutton/common/events/CloseWindowEvent.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/common/events/CloseWindowEvent.as
@@ -36,7 +36,7 @@ package org.bigbluebutton.common.events
 		
 		public static const CLOSE_WINDOW_EVENT:String = 'CLOSE_WINDOW_EVENT';
 		
-		public function CloseWindowEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
+		public function CloseWindowEvent(type:String=CLOSE_WINDOW_EVENT, bubbles:Boolean=true, cancelable:Boolean=false)
 		{
 			super(type, bubbles, cancelable);
 		}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/common/events/DragWindowEvent.as b/bigbluebutton-client/src/org/bigbluebutton/common/events/DragWindowEvent.as
new file mode 100755
index 0000000000000000000000000000000000000000..1951e35ac35bbefe84379fb6ae63e2de824ca3af
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/common/events/DragWindowEvent.as
@@ -0,0 +1,44 @@
+/**
+ * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+ *
+ * Copyright (c) 2010 BigBlueButton Inc. and 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.
+ *
+ * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+package org.bigbluebutton.common.events
+{
+	import flash.events.Event;
+	import flash.geom.Point;
+	import mx.core.UIComponent;
+	import flexlib.mdi.containers.*;
+	
+	public class DragWindowEvent extends Event
+	{
+		public static const DRAG_WINDOW_EVENT:String = "DRAG_WINDOW_EVENT";
+		public static const DRAG_START:String = "DRAG_START";
+		public static const DRAG_END:String = "DRAG_END";
+		public static const DRAG:String = "DRAG";
+		
+		public var mouseGlobal:Point;
+		public var window:MDIWindow;
+		public var mode:String;
+		
+		public function DragWindowEvent(mode:String, type:String = DRAG_WINDOW_EVENT)
+		{
+			super(type, true, false);
+			this.mode = mode;
+		}
+		
+	}
+}
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml
index cdc0af45976a963fcfde96daa5ba96a337fd1746..224f08663b7c2d9af8bc15d3d15cefe82a806417 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainApplicationShell.mxml
@@ -313,6 +313,8 @@
            <mx:dataProvider>
               <mx:Array>
 			      <mx:String>Small videos</mx:String>
+				  <mx:String>Presenter + presentation</mx:String>
+				  <mx:String>Meeting</mx:String>
               </mx:Array>
            </mx:dataProvider>
       	</mx:ComboBox>
diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainCanvas.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainCanvas.mxml
index 1e5cef3ea656d08866e921884f46b56d74e0b054..c53f398b5f4bc87a69e3061d417afcd36734c18c 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/main/views/MainCanvas.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/MainCanvas.mxml
@@ -27,7 +27,7 @@
 			import flexlib.mdi.containers.MDIWindow;
 			
 			import mx.controls.Alert;
-			
+			import mx.utils.ArrayUtil;
 			import org.bigbluebutton.common.IBbbModuleWindow;
 			import org.bigbluebutton.common.LogUtil;
 
@@ -35,8 +35,10 @@
 			public static const BOTTOM_LEFT:String = "BOTTOM_LEFT_WINDOW";
 			public static const MIDDLE:String = "MIDDLE_WINDOW";
 			public static const BOTTOM:String = "BOTTOM_WINDOW";
-			public static const RIGHT:String = "RIGHT_WINDOW";
+			public static const TOP_RIGHT:String = "TOP_RIGHT_WINDOW";
+			public static const BOTTOM_RIGHT:String = "BOTTOM_RIGHT_WINDOW";
 			public static const POPUP:String = "POP_UP_WINDOW";
+			public static const UNTOUCHED:String = "UNTOUCHED_WINDOW";
 			
 			public static const DESKTOP_SHARING_VIEW:String = "POP_UP_DESKSHARE_VIEW";
 			public static const DESKTOP_SHARING_PUBLISH:String = "POP_UP_DESKSHARE_PUBLISH";
@@ -60,7 +62,13 @@
 			}
 			
 			public function removeWindow(window:IBbbModuleWindow):void{
-				windowManager.remove(window as MDIWindow);
+				// the flexlib windowManager remove method doesn't test if the given window is on the windows list
+				// this test avoid some exceptions when run the app on debugger flash player
+				if (ArrayUtil.getItemIndex(window, windowManager.windowList) != -1) {
+					windowManager.remove(window as MDIWindow);
+				} else {
+					LogUtil.debug("Trying to remove the window [ " + window + " ] but it's not a MainCanvas child");
+				}
 			}
 			
 			public function removeAllWindows():void{
@@ -114,12 +122,18 @@
 						win.width = this.width - 5;
 						win.height = 300;
 						break;
-					case RIGHT:
+					case TOP_RIGHT:
 						x = this.width - rightWindowWidth - 10;
 						y = 1;
 						win.width = rightWindowWidth;
 						win.height = rightWindowHeight;
 						break;
+					case BOTTOM_RIGHT:
+						x = this.width - rightWindowWidth - 10;
+						y = rightWindowHeight + 10;;
+						win.width = rightWindowWidth;
+						win.height = this.height - rightWindowHeight - 10;
+						break;					
 					case POPUP:
 						x = (Math.random() * this.width) - 640;
 						y = (Math.random() * this.height) - 520;
@@ -134,6 +148,10 @@
 						x = 1;
 						y = 1;
 						break;
+					case UNTOUCHED:
+						// don't reposition the window
+						x = win.x;
+						y = win.y;
 				}
 				windowManager.absPos(win, x, y);
 			}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatWindow.mxml
index 3e4ae7e9c141465c1d45456995f8b4b7b04bb48d..f247bca29aaa1d89bcb3dff753f9ae72737ae674 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatWindow.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/chat/views/ChatWindow.mxml
@@ -53,7 +53,7 @@
 			[Bindable] public var chatOptions:ChatOptions;
 			
 			public function getPrefferedPosition():String{
-				return MainCanvas.RIGHT;
+				return MainCanvas.TOP_RIGHT;
 			} 
 			
 			private function onCreationComplete():void {
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/ButtonsOverlay.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/ButtonsOverlay.as
new file mode 100755
index 0000000000000000000000000000000000000000..246e5e7ce97670ed4ba48f868eee9766a839ad6a
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/ButtonsOverlay.as
@@ -0,0 +1,55 @@
+/**
+ * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+ *
+ * Copyright (c) 2010 BigBlueButton Inc. and 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.
+ *
+ * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+package org.bigbluebutton.modules.videoconf.business
+{
+	import mx.containers.HBox;
+	import mx.controls.Button;
+	import flash.utils.Dictionary;
+	import flash.events.MouseEvent;
+	import org.bigbluebutton.common.LogUtil;
+	
+	public class ButtonsOverlay extends HBox
+	{
+		private var buttons:Dictionary = new Dictionary;
+		private var BUTTONS_SIZE:int = 20;
+		private var BUTTONS_PADDING:int = 10;
+		
+		public function add(name:String, icon:Class, tooltip:String, listener:Function):void {
+			var button:Button = new Button;
+			button.setStyle("icon", icon);
+			button.toolTip = tooltip;
+			button.addEventListener(MouseEvent.CLICK, listener);
+			button.width = button.height = BUTTONS_SIZE;
+			this.addChild(button);
+			buttons[name] = button;
+		}
+		
+		public function get(name:String):Button {
+			var tmp:Object = buttons[name];
+			//return (flash.utils.getQualifiedClassName(tmp) == "mx.controls::Button"? (tmp as Button): null);
+			return (tmp as Button);
+		}
+		
+		public function get padding():int {
+			return BUTTONS_PADDING;
+		}
+	}
+	
+}
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoWindowItf.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoWindowItf.as
new file mode 100755
index 0000000000000000000000000000000000000000..8549329f47a109601f30f0945de3f64b7518fdbc
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoWindowItf.as
@@ -0,0 +1,322 @@
+/**
+ * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+ *
+ * Copyright (c) 2010 BigBlueButton Inc. and 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.
+ *
+ * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+package org.bigbluebutton.modules.videoconf.business
+{
+	import flexlib.mdi.containers.MDIWindow;
+	import flexlib.mdi.events.MDIWindowEvent;
+	
+	import org.bigbluebutton.common.LogUtil;
+	import org.bigbluebutton.common.Images;
+	import org.bigbluebutton.common.IBbbModuleWindow;
+	import org.bigbluebutton.common.events.DragWindowEvent;
+	import org.bigbluebutton.common.events.CloseWindowEvent;
+	import org.bigbluebutton.main.views.MainCanvas;
+	import org.bigbluebutton.util.i18n.ResourceUtil;
+	
+	import mx.core.UIComponent;
+	import mx.controls.Button;	
+	
+	import flash.events.MouseEvent;
+	import flash.media.Video;
+	import flash.geom.Point;
+	
+	public class VideoWindowItf extends MDIWindow implements IBbbModuleWindow
+	{
+		protected var _video:Video;
+		protected var _videoHolder:UIComponent;
+		// images must be static because it needs to be created *before* the PublishWindow creation
+		static protected var images:Images = new Images();
+		
+		static public var PADDING_HORIZONTAL:Number = 6;
+		static public var PADDING_VERTICAL:Number = 29;
+		protected var _minWidth:int = 160 + PADDING_HORIZONTAL;
+		protected var _minHeight:int = 120 + PADDING_VERTICAL;
+		protected var aspectRatio:Number = 1;
+		protected var keepAspect:Boolean = false;
+		protected var originalWidth:Number;
+		protected var originalHeight:Number;
+		
+		protected var mousePositionOnDragStart:Point;
+		
+		public var streamName:String;
+		public var userId:int;
+		[Bindable] public var resolutions:Array;
+		
+		protected function getVideoResolution(stream:String):Array {
+			for each (var resStr:String in resolutions){
+				if (resStr == stream.substr(0, resStr.length))
+					return resStr.split( "x" );
+			}
+			return null;
+		}
+		
+		protected function get paddingVertical():Number {
+			return this.borderMetrics.top + this.borderMetrics.bottom;
+		}
+		
+		protected function get paddingHorizontal():Number {
+			return this.borderMetrics.left + this.borderMetrics.right;
+		}
+		
+		static private var RESIZING_DIRECTION_UNKNOWN:int = 0; 
+		static private var RESIZING_DIRECTION_VERTICAL:int = 1; 
+		static private var RESIZING_DIRECTION_HORIZONTAL:int = 2; 
+		static private var RESIZING_DIRECTION_BOTH:int = 3;
+		private var resizeDirection:int = RESIZING_DIRECTION_BOTH;
+		
+		/**
+		 * when the window is resized by the user, the application doesn't know
+		 * about the resize direction
+		 */
+		public function onResizeStart(event:MDIWindowEvent = null):void {
+			resizeDirection = RESIZING_DIRECTION_UNKNOWN;
+		}
+		
+		/**
+		 * after the resize ends, the direction is set to BOTH because of the
+		 * non-user resize actions - like when the window is docked, and so on
+		 */
+		public function onResizeEnd(event:MDIWindowEvent = null):void {
+			resizeDirection = RESIZING_DIRECTION_BOTH;
+		}
+		
+		protected function onResize():void {
+			if (_video == null || _videoHolder == null || this.minimized) return;
+			
+			// limits the window size to the parent size
+			this.width = (this.parent != null? Math.min(this.width, this.parent.width): this.width);
+			this.height = (this.parent != null? Math.min(this.height, this.parent.height): this.height); 
+			
+			var tmpWidth:Number = this.width - PADDING_HORIZONTAL;
+			var tmpHeight:Number = this.height - PADDING_VERTICAL;
+			
+			// try to discover in which direction the user is resizing the window
+			if (resizeDirection != RESIZING_DIRECTION_BOTH) {
+				if (tmpWidth == _video.width && tmpHeight != _video.height)
+					resizeDirection = (resizeDirection == RESIZING_DIRECTION_VERTICAL || resizeDirection == RESIZING_DIRECTION_UNKNOWN? RESIZING_DIRECTION_VERTICAL: RESIZING_DIRECTION_BOTH);
+				else if (tmpWidth != _video.width && tmpHeight == _video.height)
+					resizeDirection = (resizeDirection == RESIZING_DIRECTION_HORIZONTAL || resizeDirection == RESIZING_DIRECTION_UNKNOWN? RESIZING_DIRECTION_HORIZONTAL: RESIZING_DIRECTION_BOTH);
+				else
+					resizeDirection = RESIZING_DIRECTION_BOTH;
+			}
+			
+			// depending on the direction, the tmp size is different
+			switch (resizeDirection) {
+				case RESIZING_DIRECTION_VERTICAL:
+					tmpWidth = Math.floor(tmpHeight * aspectRatio);
+					break;
+				case RESIZING_DIRECTION_HORIZONTAL:
+					tmpHeight = Math.floor(tmpWidth / aspectRatio);
+					break;
+				case RESIZING_DIRECTION_BOTH:
+					// this direction is used also for non-user window resize actions
+					tmpWidth = Math.min (tmpWidth, Math.floor(tmpHeight * aspectRatio));
+					tmpHeight = Math.min (tmpHeight, Math.floor(tmpWidth / aspectRatio));
+					break;
+			}
+			
+			_video.width = _videoHolder.width = tmpWidth;
+			_video.height = _videoHolder.height = tmpHeight;
+			
+			if (!keepAspect || this.maximized) {
+				// center the video in the window
+				_video.x = Math.floor ((this.width - PADDING_HORIZONTAL - tmpWidth) / 2);
+				_video.y = Math.floor ((this.height - PADDING_VERTICAL - tmpHeight) / 2);
+			} else {
+				// fit window dimensions on video
+				_video.x = 0;
+				_video.y = 0;
+				this.width = tmpWidth + PADDING_HORIZONTAL;
+				this.height = tmpHeight + PADDING_VERTICAL;
+			}
+			
+			// reposition the window to fit inside the parent window
+			if (this.parent != null) {
+				if (this.x + this.width > this.parent.width)
+					this.x = this.parent.width - this.width;
+				if (this.x < 0)
+					this.x = 0;
+				if (this.y + this.height > this.parent.height)
+					this.y = this.parent.height - this.height;
+				if (this.y < 0)
+					this.y = 0;
+			}
+			
+			updateButtonsPosition();
+		}
+		
+		public function updateWidth():void {
+			this.width = Math.floor((this.height - paddingVertical) * aspectRatio) + paddingHorizontal;
+			onResize();
+		}
+		
+		public function updateHeight():void {
+			this.height = Math.floor((this.width - paddingHorizontal) / aspectRatio) + paddingVertical;
+			onResize();
+		}
+		
+		protected function setAspectRatio(width:int,height:int):void {
+			aspectRatio = (width/height);
+			this.minHeight = Math.floor((this.minWidth - PADDING_HORIZONTAL) / aspectRatio) + PADDING_VERTICAL;
+		}
+		
+		public function getPrefferedPosition():String{
+			if (_buttonsEnabled)
+				return MainCanvas.POPUP;
+			else
+				// the window is docked, so it should not be moved on reset layout
+				return MainCanvas.UNTOUCHED;
+		}
+		
+		public function onDrag(event:MDIWindowEvent = null):void {
+			var e:DragWindowEvent = new DragWindowEvent(DragWindowEvent.DRAG);
+			e.mouseGlobal = this.localToGlobal(new Point(mouseX, mouseY));
+			e.window = this;
+			dispatchEvent(e);
+		}
+		
+		public function onDragStart(event:MDIWindowEvent = null):void {
+			var e:DragWindowEvent = new DragWindowEvent(DragWindowEvent.DRAG_START);
+			e.window = this;
+			dispatchEvent(e);
+		}
+		
+		public function onDragEnd(event:MDIWindowEvent = null):void {
+			var e:DragWindowEvent = new DragWindowEvent(DragWindowEvent.DRAG_END);
+			e.mouseGlobal = this.localToGlobal(new Point(mouseX, mouseY));
+			e.window = this;
+			dispatchEvent(e);
+		}
+		
+		override public function close(event:MouseEvent = null):void{
+			var e:CloseWindowEvent = new CloseWindowEvent();
+			e.window = this;
+			dispatchEvent(e);
+			
+			super.close(event);
+		}
+		
+		private var _buttons:ButtonsOverlay = null;
+		private var _buttonsEnabled:Boolean = true;
+		
+		private var img_unlock_keep_aspect:Class = images.lock_open;
+		private var img_lock_keep_aspect:Class = images.lock_close;
+		private var img_fit_video:Class = images.arrow_in;
+		private var img_original_size:Class = images.shape_handles;
+		
+		protected function get buttons():ButtonsOverlay {
+			if (_buttons == null) {
+				_buttons = new ButtonsOverlay;
+				_buttons.add("originalSizeBtn", img_original_size, ResourceUtil.getInstance().getString('bbb.video.originalSizeBtn.tooltip'), onOriginalSizeClick);
+				
+				// hiding the other buttons
+				//_buttons.add("keepAspectBtn", img_lock_keep_aspect, ResourceUtil.getInstance().getString('bbb.video.keepAspectBtn.tooltip'), onKeepAspectClick);
+				//_buttons.add("fitVideoBtn", img_fit_video, ResourceUtil.getInstance().getString('bbb.video.fitVideoBtn.tooltip'), onFitVideoClick);
+				
+				_buttons.visible = false;
+				
+				this.addChild(_buttons);
+			} 
+			return _buttons;
+		}
+		
+		protected function createButtons():void {
+			// creates the window keeping the aspect ratio 
+			onKeepAspectClick();
+		}
+		
+		protected function updateButtonsPosition():void {
+			if (buttons.visible == false) {
+				buttons.y = buttons.x = 0;
+			} else {
+				buttons.y = _video.y + _video.height - buttons.height - buttons.padding;
+				buttons.x = _video.x + _video.width - buttons.width - buttons.padding;
+			}
+		}
+		
+		protected function showButtons(event:MouseEvent = null):void {
+			if (_buttonsEnabled && buttons.visible == false) {
+				buttons.visible = true;
+				updateButtonsPosition();
+			}
+		}
+		
+		protected function hideButtons(event:MouseEvent = null):void {
+			if (_buttonsEnabled && buttons.visible == true) {
+				buttons.visible = false;
+				updateButtonsPosition();
+			}
+		}
+		
+		protected function onDoubleClick(event:MouseEvent = null):void {
+			// it occurs when the window is docked, for example
+			if (!this.maximizeRestoreBtn.visible) return;
+			
+			this.maximizeRestore();
+		}
+		
+		override public function maximizeRestore(event:MouseEvent = null):void {
+			// if the user is maximizing the window, the control buttons should disappear
+			buttonsEnabled = this.maximized;
+			super.maximizeRestore(event);
+		}
+		
+		public function set buttonsEnabled(enabled:Boolean):void {
+			if (!enabled) 
+				hideButtons();
+			_buttonsEnabled = enabled;
+		}
+		
+		protected function onOriginalSizeClick(event:MouseEvent = null):void {
+			_video.width = _videoHolder.width = originalWidth;
+			_video.height = _videoHolder.height = originalHeight;
+			onFitVideoClick();
+		}		
+		
+		protected function onFitVideoClick(event:MouseEvent = null):void {
+			var newWidth:int = _video.width + paddingHorizontal;
+			var newHeight:int = _video.height + paddingVertical;
+			
+			this.x += (this.width - newWidth)/2;
+			this.y += (this.height - newHeight)/2;
+			this.width = newWidth;
+			this.height = newHeight;
+			onResize();
+		}
+		
+		protected function onKeepAspectClick(event:MouseEvent = null):void {
+			keepAspect = !keepAspect;
+			
+			var keepAspectBtn:Button = buttons.get("keepAspectBtn");
+			if (keepAspectBtn != null) { 
+				keepAspectBtn.selected = keepAspect;
+				keepAspectBtn.setStyle("icon", (keepAspect? img_lock_keep_aspect: img_unlock_keep_aspect));
+			}
+			
+			var fitVideoBtn:Button = buttons.get("fitVideoBtn");
+			if (fitVideoBtn != null) {
+				fitVideoBtn.enabled = !keepAspect;
+			}		
+			
+			onFitVideoClick();
+		}
+		
+	}
+}
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/events/OpenVideoWindowEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/events/OpenVideoWindowEvent.as
new file mode 100755
index 0000000000000000000000000000000000000000..1fe01c195174918cf3c564d62362ed9ca43bbfec
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/events/OpenVideoWindowEvent.as
@@ -0,0 +1,44 @@
+/**
+ * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+ *
+ * Copyright (c) 2010 BigBlueButton Inc. and 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.
+ *
+ * BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+package org.bigbluebutton.modules.videoconf.events
+{
+	import flash.events.Event;
+	
+	import org.bigbluebutton.common.IBbbModuleWindow;
+	
+	/**
+	 * Dispatch this event with your IBbbModuleWindow instance attached to add the MDIWindow to the main canvas area of bbb-client.
+	 * 
+	 */	
+	public class OpenVideoWindowEvent extends Event
+	{
+		/**
+		 * The MDIWindow instance to show on the main canvas 
+		 */		
+		public var window:IBbbModuleWindow;
+		
+		public static const OPEN_VIDEO_WINDOW_EVENT:String = 'OPEN_VIDEO_WINDOW_EVENT';
+		
+		public function OpenVideoWindowEvent(type:String=OPEN_VIDEO_WINDOW_EVENT, bubbles:Boolean=true, cancelable:Boolean=false)
+		{
+			super(type, bubbles, cancelable);
+		}
+		
+	}
+}
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml
index 3ccb4a2803c0ab95cef36a23b897cdd642da9f56..0d3f0591a3dd1dfd3eaa040c5a00048ea19d9bf2 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml
@@ -39,6 +39,7 @@
 			import org.bigbluebutton.modules.videoconf.business.VideoProxy;
 			import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
 			import org.bigbluebutton.modules.videoconf.events.OpenPublishWindowEvent;
+			import org.bigbluebutton.modules.videoconf.events.OpenVideoWindowEvent;
 			import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
 			import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
 			import org.bigbluebutton.modules.videoconf.views.PublishWindow;
@@ -70,6 +71,11 @@
 				var windowEvent:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
 				windowEvent.window = window;
 				globalDispatcher.dispatchEvent(windowEvent);
+				
+				// this event will dock the window, if it's enabled
+				var openVideoEvent:OpenVideoWindowEvent = new OpenVideoWindowEvent();
+				openVideoEvent.window = window;
+				globalDispatcher.dispatchEvent(openVideoEvent);
 			}
 
 			private function viewVideoFile(e:BBBEvent):void {
@@ -93,7 +99,7 @@
 			private function openPublishWindow():void{
 				publishWindow = new PublishWindow();
 				publishWindow.videoOptions = proxy.videoOptions;
-				publishWindow.streamName = "-" + module.userid.toString();
+				publishWindow.userId = module.userid;
 				publishWindow.userrole = module.role;
 				publishWindow.quality = module.quality;
 				publishWindow.resolutions = module.resolutions.split(",");
@@ -110,6 +116,7 @@
 				broadcastEvent.stream = e.stream;
 				broadcastEvent.userid = module.userid;
 				globalDispatcher.dispatchEvent(broadcastEvent);
+				publishWindow.title = module.username + " (you)";
 			}
 			
 			private function stopPublishing(e:StopBroadcastEvent):void{
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/PublishWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/PublishWindow.mxml
index cb1a815582f0fc5525f88aca612d1d2f22ccf93d..2d6b6e81d17153f1a7dfa0bb8e437817a32b5e8d 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/PublishWindow.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/PublishWindow.mxml
@@ -20,20 +20,21 @@
   $Id: $
 --> 
 
-<pubVid:MDIWindow xmlns="flexlib.mdi.containers.*" 
+<pubVid:VideoWindowItf xmlns="org.bigbluebutton.modules.videoconf.business.*"
 	xmlns:mx="http://www.adobe.com/2006/mxml" 
-	xmlns:pubVid="flexlib.mdi.containers.*"
+	xmlns:pubVid="org.bigbluebutton.modules.videoconf.business.*"
 	implements="org.bigbluebutton.common.IBbbModuleWindow"
 	creationComplete="init()"
 	width="{camWidth + 6}" height="{camHeight + 80}" 
-	title="{ResourceUtil.getInstance().getString('bbb.publishVideo.title')}" 
 	xmlns:mate="http://mate.asfusion.com/"
-	resize="onResize()">
+	resize="onResize()"
+	layout="absolute">
 	
 	<mx:Script>
 		<![CDATA[
 			import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
 			import org.bigbluebutton.common.LogUtil;
+			import org.bigbluebutton.common.events.CloseWindowEvent;
 			import flexlib.mdi.events.MDIWindowEvent;
 			
 			import mx.core.UIComponent;
@@ -43,34 +44,20 @@
 			import org.bigbluebutton.common.LogUtil;
 			import org.bigbluebutton.common.events.LocaleChangeEvent;
 			import org.bigbluebutton.main.views.MainCanvas;
+			import org.bigbluebutton.modules.videoconf.events.OpenVideoWindowEvent;
 			import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
 			import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
 			import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent;
 			import org.bigbluebutton.util.i18n.ResourceUtil;
 			
-			private var images:Images = new Images();
 			[Bindable] public var camIcon:Class = images.control_play;
 			[Bindable] public var bbbLogo:Class = images.bbb_logo;
-			[Bindable] public var resolutions:Array = new Array("320x240", "640x480");
-			
-			private var video:Video;
-			public var streamName:String;
+
 			[Bindable] private var camWidth:Number = 320;
 			[Bindable] private var camHeight:Number = 240;
 			private var _userrole:String;
 			public var quality:Number = 0;
-			
-			private var _bResizePossible:Boolean = true;
-			private var _nAspectRatio:Number = 1;
-			
-			static private var _minWidth:int = 86;
-			static private var _minHeight:int = 174;
-
-			private var _nSavedVideoWidth:Number = 0;
-			private var _nSavedVideoHeight:Number = 0;
-			private var _nOldWindowWidth:Number = 0;
-			private var _nOldWindowHeight:Number = 0;
-			
+						
 			// Timer to auto-publish webcam. We need this timer to delay
 			// the auto-publishing until after the Viewers's window has loaded
 			// to receive the publishing events. Otherwise, the user joining next
@@ -81,14 +68,17 @@
 			public var videoOptions:VideoConfOptions;
 			
 			private function init():void{
+				_videoHolder = new UIComponent();
+				_videoHolder.width = camWidth;
+				_videoHolder.height = camHeight;
+				this.addChild(_videoHolder);				
+				this.title = ResourceUtil.getInstance().getString('bbb.publishVideo.title');
+				currentState = "dispVideoOptionsControlBar";
+				
 				checkIfMacCamera();
 				if (isPresenter()) showResControls(true);
 				if (Camera.names.length > 1) showVideoControls(true);
 				if (!isPresenter() && Camera.names.length == 1) startPublishing();
-				addEventListener(MDIWindowEvent.RESIZE_END, onResizeEvent);
-				addEventListener(MDIWindowEvent.RESIZE, onResizeEvent);
-				addEventListener(MDIWindowEvent.MAXIMIZE, onMaximize);
-				addEventListener(MDIWindowEvent.RESTORE, onRestore);
 
 				this.minWidth = _minWidth;
 				this.minHeight = _minHeight;
@@ -121,11 +111,7 @@
 					if (Camera.names[i] == webcam) cmbCameraSelector.selectedIndex = i;
 				}
 			}
-			
-			public function getPrefferedPosition():String{
-				return MainCanvas.POPUP;
-			}
-			
+						
 			private function startPublishing():void{
 				var camera:Camera = Camera.getCamera(cmbCameraSelector.selectedIndex.toString());
 				if (camera == null) return;
@@ -136,13 +122,13 @@
 				camera.setMode(camWidth, camHeight, videoOptions.camModeFps);
 				camera.setQuality(videoOptions.camQualityBandwidth, videoOptions.camQualityPicture);
 				
-				video = new Video(camWidth, camHeight);
+				_video = new Video(camWidth, camHeight);
 				//Next two lines may seem redundant but they're not. Do not delete.
-				video.width = camWidth;
-				video.height = camHeight;
-				adjustWindowSize();
-				video.attachCamera(camera);
-				videoHolder.addChild(video);
+				_video.width = camWidth;
+				_video.height = camHeight;
+				onResize();
+				_video.attachCamera(camera);
+				_videoHolder.addChild(_video);
 //				addChild(videoHolder);
 				
 				var e:StartBroadcastEvent = new StartBroadcastEvent();
@@ -155,6 +141,22 @@
 
 				maximizeRestoreBtn.visible = true;
 				this.resizable = true;
+				
+				addEventListener(MDIWindowEvent.RESIZE_START, onResizeStart);
+				addEventListener(MDIWindowEvent.RESIZE_END, onResizeEnd);
+				addEventListener(MDIWindowEvent.DRAG_START, onDragStart);
+				addEventListener(MDIWindowEvent.DRAG, onDrag);
+				addEventListener(MDIWindowEvent.DRAG_END, onDragEnd);
+				addEventListener(MouseEvent.MOUSE_OVER, showButtons);
+				addEventListener(MouseEvent.MOUSE_OUT, hideButtons);
+				addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
+				
+				createButtons();
+				
+				// this event will dock the window, if it's enabled
+				var openVideoEvent:OpenVideoWindowEvent = new OpenVideoWindowEvent();
+				openVideoEvent.window = this;
+				dispatchEvent(openVideoEvent);					
 			}
 			
 			override public function close(event:MouseEvent=null):void{
@@ -163,28 +165,28 @@
 			}
 			
 			private function stopPublishing():void{
-				if (video != null) {
-					video.attachCamera(null);
-					video.clear();
-					video = null;
+				if (_video != null) {
+					_video.attachCamera(null);
+					_video.clear();
+					_video = null;
 				}
 				var e:StopBroadcastEvent = new StopBroadcastEvent()
-				e.stream = this.streamName;
+				e.stream = streamName;
 				dispatchEvent(e);
 			}
 			
 			private function setResolution():void{
 				var res:Array = cmbResolution.selectedLabel.split( "x" );
-				camWidth = Number(res[0]);
-				camHeight = Number(res[1]);
-				_nAspectRatio = (camWidth/camHeight);
+				camWidth = originalWidth = Number(res[0]);
+				camHeight = originalHeight = Number(res[1]);
+				setAspectRatio(camWidth, camHeight);
 				
  				/**
          		* Add timestamp to create a unique stream name. This way we can record	 	
          		* stream without overwriting previously recorded streams.	 	
          		*/	 	
 				var curTime:Number = new Date().getTime();	 	
-        		this.streamName = cmbResolution.selectedLabel.concat(this.streamName) + "-" + curTime;
+        		this.streamName = cmbResolution.selectedLabel.concat(this.userId) + "-" + curTime;
 			}
 			
 			public function set userrole(role:String):void{
@@ -198,6 +200,7 @@
 			
 			private function showVideoControls(show:Boolean):void{
 				if (show){
+					currentState = "dispVideoOptionsControlBar"
 					videoOptionsBar.visible = true;
 					btnStartPublish.visible = true;
 					cmbCameraSelector.visible = true;
@@ -206,6 +209,7 @@
 					btnStartPublish.visible = false;
 					cmbCameraSelector.visible = false;
 					videoOptionsBar.visible = false;
+					currentState = "dispVideo";
 				}
 			}
 			
@@ -233,146 +237,27 @@
 				}
 			}
 			
-			private function adjustWindowSize():void{
-				videoHolder.width = video.width;
-				videoHolder.height = video.height;
-				this.width = video.width + 6;
-				this.height = video.height + 74;
-				
-				// prevent to show a video window bigger than the parent window
-				if (this.width > this.parent.width) {
-					video.width = this.parent.width - 6;
-					video.height = Math.floor(video.width / _nAspectRatio);
-					adjustWindowSize();
-				}
-				if (this.height > this.parent.height) {
-					video.height = this.parent.height - 74;
-					video.width = Math.floor(video.height * _nAspectRatio);
-					adjustWindowSize();
-				}
-				if (this.width < _minWidth) {
-					video.width = _minWidth - 6;
-					video.height = Math.floor(video.width / _nAspectRatio);
-					adjustWindowSize();
-				}
-				if (this.height < _minHeight) {
-					video.height = _minHeight - 74;
-					video.width = Math.floor(video.height * _nAspectRatio);
-					adjustWindowSize();
-				}
-			}			
-			
-			public function onResizeEvent(event:MDIWindowEvent):void {
-				if (event.type == MDIWindowEvent.RESIZE) {
-					// test if we are already resizing
-					if (_bResizePossible) {
-						_bResizePossible = false;
-						resizeWindow();
-						_bResizePossible = true;
-					}
-				} else if (event.type == MDIWindowEvent.RESIZE_END) {
-					adjustWindowSize();
-				}
-			}
-			
-			private function resizeWindow():void {
-				// prevent the window for blinking
-				if (this.width == _nOldWindowWidth && this.height == _nOldWindowHeight) {
-					adjustWindowSize();
-					return;
-				}
-
-				_nOldWindowWidth = this.width;
-				_nOldWindowHeight = this.height;
-
-				if (this.width == video.width + 6) {
-					// if it's a vertical resizing
-					video.height = this.height - 74;
-					video.width = video.height * _nAspectRatio;
-				} else {
-					// if it's a horizontal resizing
-					video.width = this.width - 6;
-					video.height  = Math.floor (video.width / _nAspectRatio);
-				}
-				adjustWindowSize();
-			}
-			
-			public function onMaximize(event:MDIWindowEvent):void {
-				_nSavedVideoWidth = video.width;
-				_nSavedVideoHeight = video.height;
-			
-			
-				var tmpWidth:Number = this.parent.width - 6;
-				var tmpHeight:Number = this.parent.height - 74;
-
-				if (tmpWidth > tmpHeight * _nAspectRatio)
-					tmpWidth = tmpHeight * _nAspectRatio;
-				if (tmpHeight > Math.floor(tmpWidth / _nAspectRatio))
-					tmpHeight = Math.floor(tmpWidth / _nAspectRatio);
-				
-				video.width = tmpWidth;
-				video.height = tmpHeight;
-				
-				video.x = Math.floor ((this.parent.width - 6 - video.width) / 2);
-				video.y = Math.floor ((this.parent.height - 74 - video.height) / 2);
-			}
-			
-			public function onRestore(event:MDIWindowEvent):void {
-				video.x = 0;
-				video.y = 0;
-				video.width = _nSavedVideoWidth;
-				video.height = _nSavedVideoHeight;
-				adjustWindowSize();
-			}
-
-			public function setWindowWidth(width:int):void {
-				if (video == null) return;
-				
-				video.width = width - 6;
-				video.height = Math.floor(video.width / _nAspectRatio);
-				adjustWindowSize();
-			}
-			
-			static public function getMinWidth():int {
-				return _minWidth;
-			}
-		
-			static public function getMinHeight():int {
-				return _minHeight;
-			}
-			
-			static public function getWindowHeightByWidth(width:int, aspect:Number):int {
-				return Math.floor((width - 6) / aspect) + 74;
-			}
-			
-			static public function getWindowWidthByHeight(height:int, aspect:Number):int {
-				return Math.floor((height - 74) * aspect) + 6;
-			}
 
 			override protected function resourcesChanged():void{
 				super.resourcesChanged();
-				this.title = ResourceUtil.getInstance().getString('bbb.publishVideo.title');
-			}
-			
-			private function localeChanged(e:LocaleChangeEvent):void{
-				resourcesChanged();
-			}
-			
-			private function onResize():void{
-				setWindowWidth(this.width);
-			}
-			
+				// when the user starts to publish, the window title is your name + (you)                   
+				if (currentState == "dispVideoOptionsControlBar")
+					this.title = ResourceUtil.getInstance().getString('bbb.publishVideo.title');
+			}			
 		]]>
 	</mx:Script>
+	<pubVid:states>
+		<mx:State name="dispVideo"/>
+		<mx:State name="dispVideoOptionsControlBar">   
+			<mx:AddChild>
+				<mx:ControlBar id="videoOptionsBar" visible="true">
+					<mx:Button id="btnStartPublish" toolTip="{ResourceUtil.getInstance().getString('bbb.publishVideo.startPublishBtn.toolTip')}" icon="{camIcon}" click="startPublishing()" />
+					<mx:ComboBox id="cmbCameraSelector" dataProvider="{Camera.names}" width="150" visible="false" />
+					<mx:ComboBox id="cmbResolution" dataProvider="{resolutions}" width="20%" visible="false" />
+				</mx:ControlBar>
+			</mx:AddChild>
+		</mx:State>
+	</pubVid:states>
 	
-	<mx:UIComponent id="videoHolder" width="{camWidth}" height="{camHeight}" />
-	
-	<mx:ControlBar id="videoOptionsBar" visible="true">
-		<mx:Button id="btnStartPublish" toolTip="{ResourceUtil.getInstance().getString('bbb.publishVideo.startPublishBtn.toolTip')}" icon="{camIcon}" click="startPublishing()" />
-		<mx:ComboBox id="cmbCameraSelector" dataProvider="{Camera.names}" width="150" visible="false" />
-		<mx:ComboBox id="cmbResolution" dataProvider="{resolutions}" width="20%" visible="false" />
-	</mx:ControlBar>
-
 	<mate:Listener type="{CloseAllWindowsEvent.CLOSE_ALL_WINDOWS}" method="closeWindow" />
-	<mate:Listener type="{LocaleChangeEvent.LOCALE_CHANGED}" method="localeChanged" />
-</pubVid:MDIWindow>
+</pubVid:VideoWindowItf>
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml
index e61ca42c3cfceda71d875661f4d25c4998617b21..05308984e06721d884f1e0b36eea3b1a8c762338 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml
@@ -20,66 +20,50 @@
   $Id: $
 -->
 
-<MDIWindow xmlns="flexlib.mdi.containers.*" 
+<VideoWindowItf xmlns="org.bigbluebutton.modules.videoconf.business.*" 
 	xmlns:mx="http://www.adobe.com/2006/mxml" 
 	creationComplete="init()" 
 	implements="org.bigbluebutton.common.IBbbModuleWindow"
 	xmlns:mate="http://mate.asfusion.com/"
-	resize="onResize()">
+	resize="onResize()"
+	layout="absolute">
 	
 	<mx:Script>
 		<![CDATA[
-			import com.asfusion.mate.events.Dispatcher;
-			
-			import flexlib.mdi.events.MDIWindowEvent;
-			
+			import com.asfusion.mate.events.Dispatcher;			
+			import flexlib.mdi.events.MDIWindowEvent;			
 			import mx.controls.Alert;
-			import mx.core.UIComponent;
-			
+			import mx.core.UIComponent;			
 			import org.bigbluebutton.common.LogUtil;
 			import org.bigbluebutton.main.events.BBBEvent;
 			import org.bigbluebutton.main.views.MainCanvas;
 			import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
+			import org.bigbluebutton.common.events.CloseWindowEvent;
 			import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
-		
-			public var resolutions:Array = new Array("320x240", "640x480");
-			
-			private var video:Video;
-			private var ns:NetStream;
-			private var videoHolder:UIComponent;
-			private var stream:String;
-			private var videoHeight:Number;
-			private var videoWidth:Number;
-
-			private var _bResizePossible:Boolean = true;
-			private var _nAspectRatio:Number = 1;
-
-			static private var _minWidth:int = 166;
-			static private var _minHeight:int = 149;
-
-			private var _nSavedVideoWidth:Number = 0;
-			private var _nSavedVideoHeight:Number = 0;
-			private var _nOldWindowWidth:Number = 0;
-			private var _nOldWindowHeight:Number = 0;
-							
-			private var _xPosition:int;
-			private var _yPosition:int;
-			
+					
+			private var ns:NetStream;			
 			private var globalDispatcher:Dispatcher;
 
 			[Bindable]
 			public var videoOptions:VideoConfOptions;
 			
 			private function init():void{
-				videoHolder = new UIComponent();
-				videoHolder.addChild(video);
-				this.addChild(videoHolder);
+				_videoHolder = new UIComponent();
+				_videoHolder.addChild(_video);
+				this.addChild(_videoHolder);
 				
-				addEventListener(MDIWindowEvent.RESIZE_END, onResizeEvent);
-				addEventListener(MDIWindowEvent.RESIZE, onResizeEvent);
-				addEventListener(MDIWindowEvent.MAXIMIZE, onMaximize);
-				addEventListener(MDIWindowEvent.RESTORE, onRestore);
+				addEventListener(MDIWindowEvent.RESIZE_START, onResizeStart);
+				addEventListener(MDIWindowEvent.RESIZE_END, onResizeEnd);
 				addEventListener(MDIWindowEvent.CLOSE, onCloseEvent);
+
+				addEventListener(MDIWindowEvent.DRAG_START, onDragStart);
+				addEventListener(MDIWindowEvent.DRAG, onDrag);
+				addEventListener(MDIWindowEvent.DRAG_END, onDragEnd);
+				addEventListener(MouseEvent.MOUSE_OVER, showButtons);
+				addEventListener(MouseEvent.MOUSE_OUT, hideButtons);
+				addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
+				
+				createButtons();
 				
 				globalDispatcher = new Dispatcher();
 
@@ -87,14 +71,24 @@
 				this.minHeight = _minHeight;
 				maximizeRestoreBtn.visible = true;
 				this.resizable = true;
+				
+				/**
+				 *  At this point, the function startVideo has been called, and 
+				 *   the video has the exactly dimensions of the original stream.
+				 *  It's needed to call onResize() to fit the video window in the
+				 *  main canvas in case that the video dimensions are larger than
+				 *  the parent window. 
+				 */
+				onResize();				
+				
 				if (videoOptions.viewerWindowMaxed)
 					this.maximize();
 			}
 			
 			private function onCloseEvent(event:MDIWindowEvent = null):void {
-				LogUtil.debug("ViewWindow closing " + stream);
+				LogUtil.debug("ViewWindow closing " + streamName);
 				var bbbEvt:BBBEvent = new BBBEvent("ViewVideoCloseEvent");
-				bbbEvt.message = stream;
+				bbbEvt.message = streamName;
 				dispatchEvent(bbbEvt);
 			}
 			
@@ -107,58 +101,43 @@
 				ns.receiveVideo(true);
 				ns.receiveAudio(false);
 				
-				setVideoResolution(stream);
-				video = new Video(this.width, this.height);
-				video.width = this.width;
-				video.height = this.height;
-				video.attachNetStream(ns);
-				ns.play(stream);	
-				this.stream = stream;
-
-				this.width = video.width + 6;
-				this.height = video.height + 29;
-			}
-			
-			private function setVideoResolution(stream:String):void{
-				for each (var resStr:String in resolutions){
-					LogUtil.debug("VideoWindow::setVideoResolution testing " + resStr);
-					if (resStr == stream.substr(0, resStr.length)) {
-						var res:Array = resStr.split( "x" );
-						this.width = Number(res[0]);
-						this.height = Number(res[1]);
-						LogUtil.debug("VideoWindow::setVideoResolution width=" + this.width + " height=" + this.height);
-						_nAspectRatio = (this.width/this.height);
-						break;
-					}
-				}
+				var res:Array = getVideoResolution(stream);
+				if (res == null) // error
+					return;
+				_video = new Video(Number(res[0]), Number(res[1]));
+				_video.width = originalWidth = Number(res[0]);
+				_video.height = originalHeight = Number(res[1]);
+				setAspectRatio(Number(res[0]), Number(res[1])); 
+				_video.attachNetStream(ns);
+				ns.play(stream);
+				this.streamName = stream;
+				
+				this.width = _video.width + paddingHorizontal;
+				this.height = _video.height + paddingVertical;	
 			}
-			
+						
 			private function onAsyncError(e:AsyncErrorEvent):void{
 				LogUtil.debug("VideoWindow::asyncerror " + e.toString());
 			}
 			
 			public function onMetaData(info:Object):void{
 				LogUtil.debug("metadata: width=" + info.width + " height=" + info.height);
-				video.width = info.width;
-				video.height = info.height;
-				adjustWindowSize();
-			}
-			
-			public function getPrefferedPosition():String {
-				if (videoOptions.viewerWindowLocation == "MIDDLE") {
-					return MainCanvas.MIDDLE;
-				}
-				return MainCanvas.POPUP;
+				_video.width = info.width;
+				_video.height = info.height;
+				setAspectRatio(info.width, info.height);
+				onResize();
 			}
-			
+						
 			private function onNetStatus(e:NetStatusEvent):void{
 				switch(e.info.code){
 					case "NetStream.Publish.Start":
-						LogUtil.debug("NetStream.Publish.Start for broadcast stream " + stream);
+						LogUtil.debug("NetStream.Publish.Start for broadcast stream " + streamName);
 						break;
 					case "NetStream.Play.UnpublishNotify":
 						ns.close();
 						this.close();
+						// shouldn't call onCloseEvent() here because of the viewer cam icon
+						super.close();
 						break;
 					case "NetStream.Play.Start":
 						LogUtil.debug("Netstatus: " + e.info.code);
@@ -175,131 +154,16 @@
 			
 			override public function close(event:MouseEvent=null):void{
 				ns.close();
-				//onCloseEvent();
+				onCloseEvent();
 				super.close(event);
 			}		
 			
 			private function closeWindow(e:CloseAllWindowsEvent):void{
 				this.close();
 			}
-			
-			private function adjustWindowSize():void{
-				if (videoHolder == null) return; 
-				
-				videoHolder.width = video.width;
-				videoHolder.height = video.height;
-				this.width = video.width + 6;
-				this.height = video.height + 29;
-				
-				// prevent to show a video window bigger than the parent window
-				if (this.width > this.parent.width) {
-					video.width = this.parent.width - 6;
-					video.height = Math.floor(video.width / _nAspectRatio);
-					adjustWindowSize();
-				}
-				if (this.height > this.parent.height) {
-					video.height = this.parent.height - 29;
-					video.width = Math.floor(video.height * _nAspectRatio);
-					adjustWindowSize();
-				}
-				if (this.width < _minWidth) {
-					video.width = _minWidth - 6;
-					video.height = Math.floor(video.width / _nAspectRatio);
-					adjustWindowSize();
-				}
-				if (this.height < _minHeight) {
-					video.height = _minHeight - 29;
-					video.width = Math.floor(video.height * _nAspectRatio);
-					adjustWindowSize();
-				}
-			}			
-			
-			public function onResizeEvent(event:MDIWindowEvent):void {
-				if (event.type == MDIWindowEvent.RESIZE) {
-					// test if we are already resizing
-					if (_bResizePossible) {
-						_bResizePossible = false;
-						resizeWindow();
-						_bResizePossible = true;
-					}
-				} else if (event.type == MDIWindowEvent.RESIZE_END) {
-					adjustWindowSize();
-				}
-			}
-			
-			private function resizeWindow():void {
-				// prevent the window for blinking
-				if (this.width == _nOldWindowWidth && this.height == _nOldWindowHeight) {
-					adjustWindowSize();
-					return;
-				}
-
-				_nOldWindowWidth = this.width;
-				_nOldWindowHeight = this.height;
-
-				if (this.width == video.width + 6) {
-					// if it's a vertical resizing
-					video.height = this.height - 29;
-					video.width = video.height * _nAspectRatio;
-				} else {
-					// if it's a horizontal resizing
-					video.width = this.width - 6;
-					video.height  = Math.floor (video.width / _nAspectRatio);
-				}
-
-				adjustWindowSize();
-			}
-			
-			public function onMaximize(event:MDIWindowEvent):void {
-				_nSavedVideoWidth = video.width;
-				_nSavedVideoHeight = video.height;
-			
-			
-				var tmpWidth:Number = this.parent.width - 6;
-				var tmpHeight:Number = this.parent.height - 29;
-
-				if (tmpWidth > tmpHeight * _nAspectRatio)
-					tmpWidth = tmpHeight * _nAspectRatio;
-				if (tmpHeight > Math.floor(tmpWidth / _nAspectRatio))
-					tmpHeight = Math.floor(tmpWidth / _nAspectRatio);
-				
-				video.width = tmpWidth;
-				video.height = tmpHeight;
-				
-				video.x = Math.floor ((this.parent.width - 6 - video.width) / 2);
-				video.y = Math.floor ((this.parent.height - 29 - video.height) / 2);
-			}
-			
-			public function onRestore(event:MDIWindowEvent):void {
-				video.x = 0;
-				video.y = 0;
-				video.width = _nSavedVideoWidth;
-				video.height = _nSavedVideoHeight;
-				adjustWindowSize();
-			}
-			
-			public function setWindowWidth(width:int):void {
-				if (video == null) return;
-				
-				video.width = width - 6;
-				video.height = Math.floor(video.width / _nAspectRatio);
-				adjustWindowSize();
-			}
-			
-			static public function getMinWidth():int {
-				return _minWidth;
-			}
-		
-			static public function getMinHeight():int {
-				return _minHeight;
-			}
-			
-			private function onResize():void{
-				setWindowWidth(this.width);
-			}
-
+												
 		]]>
 	</mx:Script>
 	
 	<mate:Listener type="{CloseAllWindowsEvent.CLOSE_ALL_WINDOWS}" method="closeWindow" />
-</MDIWindow>
+</VideoWindowItf>
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videodock/maps/VideoDockEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videodock/maps/VideoDockEventMap.mxml
new file mode 100755
index 0000000000000000000000000000000000000000..28b17f7750bbf172c9bf31683f62a4c9bfc9179e
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videodock/maps/VideoDockEventMap.mxml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+BigBlueButton open source conferencing system - http://www.bigbluebutton.org
+
+Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below).
+
+BigBlueButton 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. 
+
+BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+
+$Id: $
+-->
+
+<EventMap xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://mate.asfusion.com/">
+	<mx:Script>
+		<![CDATA[
+			import org.bigbluebutton.common.LogUtil;
+			import org.bigbluebutton.common.events.OpenWindowEvent;
+			import org.bigbluebutton.common.events.CloseWindowEvent;
+			
+			import org.bigbluebutton.modules.videodock.views.VideoDock;
+			
+			public var module:VideodockModule;
+			private var videoDock:VideoDock;
+			
+			public function startModule():void{
+				videoDock = new VideoDock();
+				videoDock.autoDock = module.autoDock;
+				
+				var windowEvent:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
+				windowEvent.window = videoDock;
+				globalDispatcher.dispatchEvent(windowEvent);
+			}
+			
+			public function stopModule():void {
+				videoDock.removeAllChildren();
+			}
+			
+		]]>
+	</mx:Script>
+	
+</EventMap>
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videodock/views/VideoDock.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videodock/views/VideoDock.mxml
new file mode 100755
index 0000000000000000000000000000000000000000..4b9e09059a1efed71c6f598dba038651a4ec47ff
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videodock/views/VideoDock.mxml
@@ -0,0 +1,265 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+BigBlueButton open source conferencing system - http://www.bigbluebutton.org
+
+Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below).
+
+BigBlueButton 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. 
+
+BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+
+$Id: $
+-->
+
+<MDIWindow xmlns="flexlib.mdi.containers.*" 
+		   xmlns:mx="http://www.adobe.com/2006/mxml" 
+		   creationComplete="init()" 
+		   implements="org.bigbluebutton.common.IBbbModuleWindow"
+		   title="{ResourceUtil.getInstance().getString('bbb.videodock.title')}"
+		   xmlns:mate="http://mate.asfusion.com/"
+		   layout="absolute"
+		   horizontalAlign="center"
+		   verticalAlign="middle"
+		   resize="onChildAdd()"
+		   backgroundColor="0xDDDDDD">
+	
+	<mx:Script>
+		<![CDATA[
+			
+			import org.bigbluebutton.common.LogUtil;
+			import org.bigbluebutton.common.events.DragWindowEvent;
+			import org.bigbluebutton.common.events.OpenWindowEvent;
+			import org.bigbluebutton.common.events.CloseWindowEvent;
+			import org.bigbluebutton.main.views.MainCanvas;
+			import org.bigbluebutton.modules.videoconf.business.VideoWindowItf;
+			import org.bigbluebutton.modules.videoconf.events.OpenVideoWindowEvent;
+			import org.bigbluebutton.util.i18n.ResourceUtil;
+			
+			import mx.events.ChildExistenceChangedEvent;
+			
+			public var autoDock:Boolean = false;
+			private var childrenDimension:Dictionary = new Dictionary();
+			private var borderColor:String;
+			
+			private function init():void{
+				this.showCloseButton = false;
+				
+				this.minWidth = 172;
+				this.minHeight = 179;
+				this.maxWidth = this.parent.width;
+				this.maxHeight = this.parent.height;
+				
+				this.width = this.minWidth;
+				this.height = this.minHeight;
+				
+				addEventListener(ChildExistenceChangedEvent.CHILD_ADD, onChildAdd);			
+				addEventListener(ChildExistenceChangedEvent.CHILD_REMOVE, onChildRemove);
+				addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
+			}
+			
+			private function onChildAdd(e:ChildExistenceChangedEvent = null):void {
+				updateChildrenDimensions(this.getChildren());
+			}
+			
+			private function onChildRemove(e:ChildExistenceChangedEvent = null):void {
+				// copy the children array to "remove" the removing child and update the dimensions correctly
+				var children:Array = this.getChildren();
+				var index:int = children.indexOf(e.relatedObject);
+				if (index != -1)
+					children.splice(index, 1);
+				updateChildrenDimensions(children);
+			}
+			
+			public function getPrefferedPosition():String{
+				return MainCanvas.BOTTOM_RIGHT;
+			}
+			
+			private function onDragVideoWindow(e:DragWindowEvent):void{
+				switch (e.mode) {
+					case DragWindowEvent.DRAG:
+						if (hitTestPoint(e.mouseGlobal.x, e.mouseGlobal.y, true)) {
+							setStyle("borderColor","0xFF0000");
+							e.window.width = e.window.minWidth;
+							e.window.height = e.window.minHeight;
+						} else {
+							setStyle("borderColor",borderColor);
+							restoreWindowDimensions(e.window);
+						}
+						break;
+					case DragWindowEvent.DRAG_START:
+						borderColor = getStyle("borderColor");
+						removeVideoChild(e.window as VideoWindowItf);
+						saveWindowDimensions(e.window);
+						break;
+					case DragWindowEvent.DRAG_END:
+						setStyle("borderColor",borderColor);
+						restoreWindowDimensions(e.window);
+						if (hitTestPoint(e.mouseGlobal.x, e.mouseGlobal.y, true))
+							addVideoChild(e.window as VideoWindowItf);
+						break;
+				}
+			}
+			
+			private function saveWindowDimensions(window:MDIWindow):void {
+				var dimensions:Object = {width:window.width, height:window.height};
+				childrenDimension[window] = dimensions;
+			}
+			
+			private function restoreWindowDimensions(window:MDIWindow):void {
+				window.width = childrenDimension[window].width;
+				window.height = childrenDimension[window].height;
+			}
+			
+			private function repositionWindow(window:MDIWindow):void {
+				// \TODO reposition the window correctly between the windows
+				// setChildIndex(window, ?);
+			}
+			
+			private function isVideoWindow(window:Object):Boolean {
+				return (getQualifiedSuperclassName(window) == "org.bigbluebutton.modules.videoconf.business::VideoWindowItf")
+			}
+			
+			private function onCloseWindow(e:CloseWindowEvent):void {
+				// it should not just undock the window, it should close the window forever
+				if (isVideoWindow(e.window) && this.contains(e.window as VideoWindowItf))
+					this.removeChild(e.window as VideoWindowItf);
+			}
+			
+			private function onOpenWindow(e:OpenVideoWindowEvent):void {
+				if (isVideoWindow(e.window) && autoDock)
+					addVideoChild(e.window as VideoWindowItf);
+			}
+			
+			private function addVideoChild(window:VideoWindowItf):void {
+				if (this.contains(window))
+					return;
+				
+				LogUtil.debug("Docking window");
+				
+				saveWindowDimensions(window);
+				
+				window.minimizeBtn.visible = false;
+				window.maximizeRestoreBtn.visible = false;
+				window.resizable = false;
+				window.buttonsEnabled = false;
+				
+				var e:CloseWindowEvent = new CloseWindowEvent();
+				e.window = window;
+				dispatchEvent(e);
+				this.addChild(window);
+			}
+			
+			private function removeVideoChild(window:VideoWindowItf):void {
+				if (!this.contains(window))
+					return;
+				
+				window.minimizeBtn.visible = true;
+				window.maximizeRestoreBtn.visible = true;
+				window.resizable = true;
+				window.buttonsEnabled = true;
+				
+				this.removeChild(window);
+				var e:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
+				e.window = window;
+				dispatchEvent(e);
+				
+				restoreWindowDimensions(window);
+			}
+			
+			override public function close(event:MouseEvent = null):void {
+				removeAllChildren();
+				super.close(event);
+			}
+			
+			private function updateChildrenDimensions(children:Array):void {
+				var numChildren:int = children.length;
+				if (numChildren == 0) return;
+				
+				var horizontalGap:Number = getStyle("horizontalGap");
+				var verticalGap:Number = getStyle("verticalGap");
+				
+				var availableWidth:Number = this.width
+					- this.borderMetrics.left - this.borderMetrics.right;
+				var availableHeight:Number = this.height
+					- this.borderMetrics.top - this.borderMetrics.bottom;
+				
+				var childWidth:Number = 0;
+				var childHeight:Number = 0;
+				var nRows:Number = 0;
+				var nColumns:Number = 0;
+				
+				// we would like to maximize the window size
+				for (var rows:Number = 1; rows <= numChildren; ++rows) {
+					var columns:Number = Math.ceil(numChildren / rows);
+					var maxWidth:Number = Math.floor((availableWidth - horizontalGap * (columns - 1)) / columns) - VideoWindowItf.PADDING_HORIZONTAL;
+					var maxHeight:Number = Math.floor((availableHeight - verticalGap * (rows - 1)) / rows) - VideoWindowItf.PADDING_VERTICAL;
+					
+					// the possible dimensions shouldn't be less or equal 0 (it could happen with many videos)
+					if (maxWidth <= 0 || maxHeight <=0)
+						continue;
+					
+					var desiredAR:Number = 4/3;
+					var width:Number = maxWidth;
+					var height:Number = maxHeight;
+					if (maxWidth / maxHeight > desiredAR)
+						width = Math.floor(maxHeight * desiredAR);
+					else
+						height = Math.floor(maxWidth / desiredAR);
+					
+					if (width > childWidth) {
+						childWidth = width;
+						childHeight = height;
+						nRows = rows;
+						nColumns = columns;
+					}
+				}                        
+				
+				childWidth += VideoWindowItf.PADDING_HORIZONTAL;
+				childHeight += VideoWindowItf.PADDING_VERTICAL;
+				
+				for (var childIndex:int = 0; childIndex < numChildren; ++childIndex) {
+					var window:VideoWindowItf = children[childIndex];
+					window.width = childWidth;
+					window.updateHeight();
+					
+					if (window.height > childHeight) {
+						window.height = childHeight;
+						window.updateWidth();
+					}
+					
+					var row:int = childIndex / nColumns;
+					var column:int = childIndex % nColumns;
+					
+					var borderTop:int = (availableHeight - nRows * childHeight - (nRows - 1) * verticalGap) / 2;
+					var borderLeft:int = (availableWidth - nColumns * childWidth - (nColumns - 1) * horizontalGap) / 2;
+					
+					window.y = row * (childHeight + verticalGap) + borderTop;
+					window.x = column * (childWidth + horizontalGap) + borderLeft;
+				} 
+			}
+			
+			protected function onDoubleClick(event:MouseEvent = null):void {
+				this.maximizeRestore();
+			}
+			
+			override protected function resourcesChanged():void{
+				super.resourcesChanged();
+				this.title = ResourceUtil.getInstance().getString('bbb.videodock.title');
+			}			
+			
+		]]>
+	</mx:Script>
+	
+	<mate:Listener type="{DragWindowEvent.DRAG_WINDOW_EVENT}" method="onDragVideoWindow" />
+	<mate:Listener type="{OpenVideoWindowEvent.OPEN_VIDEO_WINDOW_EVENT}" method="onOpenWindow" />
+	<mate:Listener type="{CloseWindowEvent.CLOSE_WINDOW_EVENT}" method="onCloseWindow" />
+</MDIWindow>
\ No newline at end of file
diff --git a/bigbluebutton-client/src/org/bigbluebutton/util/i18n/ResourceUtil.as b/bigbluebutton-client/src/org/bigbluebutton/util/i18n/ResourceUtil.as
index a927baced0bf5521f95811a4d89ddd2b8a2e5321..6395b428100597fd0adcd8fd3ec743996f641b05 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/util/i18n/ResourceUtil.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/util/i18n/ResourceUtil.as
@@ -19,8 +19,6 @@
 package org.bigbluebutton.util.i18n
 {
 	import com.adobe.utils.StringUtil;
-	import com.asfusion.mate.events.Dispatcher;
-	
 	import flash.events.Event;
 	import flash.events.EventDispatcher;
 	import flash.events.IEventDispatcher;
@@ -48,7 +46,7 @@ package org.bigbluebutton.util.i18n
 		private static var BBB_RESOURCE_BUNDLE:String = 'bbbResources';
 		public static var DEFAULT_LANGUAGE:String = "en_US";
 		private static var currentLanguage:String = DEFAULT_LANGUAGE;
-		private var eventDispatcher:Dispatcher = new Dispatcher();
+		private var eventDispatcher:IEventDispatcher;
 		
 		private var localeChain:Array = new Array();
 		private var resourceManager:IResourceManager;
@@ -94,11 +92,23 @@ package org.bigbluebutton.util.i18n
 				if (resourceManager.localeChain[0] == localeChain[i]) localeAvailable = true;
 			}
 			
-			//Locale not found, set default
-			changeLocale(DEFAULT_LANGUAGE);			
+			/**
+			 *  http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/resources/IResourceManager.html#localeChain
+			 *  Always load the default language, so if the chosen language 
+			 *  doesn't provide a resource, the default language resource is used
+			 */
+			load(DEFAULT_LANGUAGE);
 			
+			if (!localeAvailable)
+				resourceManager.localeChain = [DEFAULT_LANGUAGE];
+			changeLocale(resourceManager.localeChain[0]);					
 		}
 		
+		private function load(language:String):IEventDispatcher {
+			var localeURI:String = 'locale/' + language + '_resources.swf';
+			return resourceManager.loadResourceModule(localeURI, false);
+		}		
+		
 		public static function getInstance():ResourceUtil {
 			if (instance == null) {
 				LogUtil.debug("Setting up supported locales.");
@@ -107,62 +117,41 @@ package org.bigbluebutton.util.i18n
 			return instance;
         }
         
-        public function changeLocale(language:String):void{        	
-    		var localeURI:String = 'locale/' + language + '/bbbResources.properties';
-
-			var date:Date = new Date();
-			var _urlLoader:URLLoader = new URLLoader();
-			_urlLoader.addEventListener(Event.COMPLETE, handleLocaleLoaded);
-			_urlLoader.addEventListener(IOErrorEvent.IO_ERROR, handleResourceNotLoaded);
-			_urlLoader.load(new URLRequest(localeURI + "?a=" + date.time));
-			
-			currentLanguage = language;
-        }
-		
-		private function handleLocaleLoaded(e:Event):void{
-			var fulltext:String = (e.target.data as String);
-			fulltext = com.adobe.utils.StringUtil.remove(fulltext, "\r");
-			
-			var allStrings:Array = fulltext.split("\n");
-			for (var i:Number=0; i<allStrings.length; i++){
-				var str:String = allStrings[i] as String;
+		public function changeLocale(... chain):void{        	
+			if(chain != null && chain.length > 0)
+			{
+				eventDispatcher = load(chain[0]);
+				localeChain = [chain[0]];
+				eventDispatcher.addEventListener(ResourceEvent.COMPLETE, localeChangeComplete);
+				eventDispatcher.addEventListener(ResourceEvent.ERROR, handleResourceNotLoaded);
 				
-				if (str.charAt(0) != '#'){
-					var keyValue:Array = str.split("=");
-					var key:String = mx.utils.StringUtil.trim(keyValue[0] as String);
-					var value:String = mx.utils.StringUtil.trim(keyValue[1] as String);
-					currentLocalization[key] = value;
-					trace(key + "=" + value);
-				}
+				currentLanguage = chain[0];
 			}
-			
-			trace(currentLocalization['bbb.logout.usercommand']);
-			
+		}
+		
+		private function localeChangeComplete(event:ResourceEvent):void{
+			if (localeChain[0] != DEFAULT_LANGUAGE)
+				localeChain.push(DEFAULT_LANGUAGE);
+			resourceManager.localeChain = localeChain;
 			update();
 		}
-        
-        /**
-         * Defaults to DEFAULT_LANGUAGE when an error is thrown by the ResourceManager 
-         * @param event
-         */        
-        private function handleResourceNotLoaded(event:ResourceEvent):void{
-			currentLanguage = DEFAULT_LANGUAGE;
-        	update();
-        }
-        
-        public function update():void{
-			eventDispatcher.dispatchEvent(new LocaleChangeEvent(LocaleChangeEvent.LOCALE_CHANGED));
-        	dispatchEvent(new Event(Event.CHANGE));
-        }
-        
-        [Bindable("change")]
-        public function getString(resourceName:String, parameters:Array = null, locale:String = null):String{
-			if (!parameters) return currentLocalization[resourceName]; //resourceManager.getString(BBB_RESOURCE_BUNDLE, resourceName, parameters, locale);
-			else return insertParameters(currentLocalization[resourceName], parameters);
+		
+		/**
+		 * Defaults to DEFAULT_LANGUAGE when an error is thrown by the ResourceManager 
+		 * @param event
+		 */        
+		private function handleResourceNotLoaded(event:ResourceEvent):void{
+			resourceManager.localeChain = [DEFAULT_LANGUAGE];
+			update();
+		}
+		
+		public function update():void{
+			dispatchEvent(new Event(Event.CHANGE));
 		}
 		
-		private function insertParameters(text:String, parameters:Array):String{
-			return mx.utils.StringUtil.substitute(text, parameters);
+		[Bindable("change")]
+		public function getString(resourceName:String, parameters:Array = null, locale:String = null):String{
+			return resourceManager.getString(BBB_RESOURCE_BUNDLE, resourceName, parameters, locale);
 		}
 		
 		public function getCurrentLanguageCode():String{