You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2017/09/05 13:58:22 UTC

openmeetings git commit: [OPENMEETINGS-1644] basic support for hot keys

Repository: openmeetings
Updated Branches:
  refs/heads/master fca1a790b -> af05e4132


[OPENMEETINGS-1644] basic support for hot keys


Project: http://git-wip-us.apache.org/repos/asf/openmeetings/repo
Commit: http://git-wip-us.apache.org/repos/asf/openmeetings/commit/af05e413
Tree: http://git-wip-us.apache.org/repos/asf/openmeetings/tree/af05e413
Diff: http://git-wip-us.apache.org/repos/asf/openmeetings/diff/af05e413

Branch: refs/heads/master
Commit: af05e4132dcbce9087661297925be79bbd1fa531
Parents: fca1a79
Author: Maxim Solodovnik <so...@gmail.com>
Authored: Tue Sep 5 20:58:12 2017 +0700
Committer: Maxim Solodovnik <so...@gmail.com>
Committed: Tue Sep 5 20:58:12 2017 +0700

----------------------------------------------------------------------
 .../core/remote/ScopeApplicationAdapter.java    |  44 +---
 .../db/dao/basic/ConfigurationDao.java          |  74 ++++++-
 .../openmeetings/backup/BackupImport.java       |   8 +-
 .../installation/ImportInitvalues.java          |  21 +-
 .../src/site/xdoc/GeneralConfiguration.xml      |  26 ++-
 .../util/OpenmeetingsVariables.java             |  28 ++-
 .../openmeetings/web/pages/auth/SignInPage.java |   2 +-
 .../apache/openmeetings/web/room/RoomPanel.java |  23 +--
 .../apache/openmeetings/web/room/SwfPanel.java  |  13 +-
 .../openmeetings/web/room/VideoSettings.java    |  18 +-
 .../web/room/menu/StartSharingButton.java       |   2 +-
 .../org/apache/openmeetings/web/room/room.js    | 200 +++++++++++--------
 .../web/room/sidebar/RoomSidebar.html           |   2 +-
 .../web/room/wb/AbstractWbPanel.java            |   2 +-
 .../org/apache/openmeetings/web/room/wb/wb.js   |   2 +-
 15 files changed, 264 insertions(+), 201 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
----------------------------------------------------------------------
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
index 320cfb1..a592d70 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
@@ -20,14 +20,6 @@ package org.apache.openmeetings.core.remote;
 
 import static org.apache.openmeetings.util.OmFileHelper.HIBERNATE;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_EXT_PROCESS_TTL;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_CAM_QUALITY;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_ECHO_PATH;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_MIC_RATE;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_SECURE;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_SECURE_PROXY;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_VIDEO_BANDWIDTH;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_VIDEO_CODEC;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_VIDEO_FPS;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_HEADER_CSP;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_HEADER_XFRAME;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.EXT_PROCESS_TTL;
@@ -36,14 +28,10 @@ import static org.apache.openmeetings.util.OpenmeetingsVariables.HEADER_XFRAME_S
 import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.wicketApplicationName;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 import java.util.UUID;
 
 import org.apache.openmeetings.IApplication;
@@ -93,17 +81,6 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 	private static final String ROOM_PARAM = "roomClient";
 	private static final String WIDTH_PARAM = "width";
 	private static final String HEIGHT_PARAM = "height";
-	public static final String FLASH_SECURE = "secure";
-	public static final String FLASH_NATIVE_SSL = "native";
-	public static final String FLASH_PORT = "rtmpPort";
-	public static final String FLASH_SSL_PORT = "rtmpsPort";
-	public static final String FLASH_VIDEO_CODEC = "videoCodec";
-	public static final String FLASH_FPS = "fps";
-	public static final String FLASH_BANDWIDTH = "bandwidth";
-	public static final String FLASH_QUALITY = "quality";
-	public static final String FLASH_ECHO_PATH = "echoPath";
-	public static final String FLASH_MIC_RATE = "micRate";
-	private JSONObject flashSettings;
 
 	@Autowired
 	private ISessionManager sessionManager;
@@ -145,22 +122,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 			cfgDao.getCryptKey();
 
 			// init your handler here
-			Properties props = new Properties();
-			try (InputStream is = new FileInputStream(new File(new File(OmFileHelper.getRootDir(), "conf"), "red5.properties"))) {
-				props.load(is);
-			}
-			flashSettings = new JSONObject()
-					.put(FLASH_SECURE, "yes".equals(cfgDao.getConfValue(CONFIG_FLASH_SECURE, String.class, "no")))
-					.put(FLASH_NATIVE_SSL, "best".equals(cfgDao.getConfValue(CONFIG_FLASH_SECURE_PROXY, String.class, "none")))
-					.put(FLASH_PORT, props.getProperty("rtmp.port"))
-					.put(FLASH_SSL_PORT, props.getProperty("rtmps.port"))
-					.put(FLASH_VIDEO_CODEC, cfgDao.getConfValue(CONFIG_FLASH_VIDEO_CODEC, String.class, "h263"))
-					.put(FLASH_FPS, cfgDao.getConfValue(CONFIG_FLASH_VIDEO_FPS, Integer.class, "30"))
-					.put(FLASH_BANDWIDTH, cfgDao.getConfValue(CONFIG_FLASH_VIDEO_BANDWIDTH, Integer.class, "0"))
-					.put(FLASH_QUALITY, cfgDao.getConfValue(CONFIG_FLASH_CAM_QUALITY, Integer.class, "90"))
-					.put(FLASH_ECHO_PATH, cfgDao.getConfValue(CONFIG_FLASH_ECHO_PATH, Integer.class, "128"))
-					.put(FLASH_MIC_RATE, cfgDao.getConfValue(CONFIG_FLASH_MIC_RATE, Integer.class, "22"))
-					;
+			cfgDao.reloadRoomSettings();
 
 			for (String scopeName : scope.getScopeNames()) {
 				_log.debug("scopeName :: " + scopeName);
@@ -1024,10 +986,6 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 		return count != null && count > 0 ? count - 1 : 0;
 	}
 
-	public JSONObject getFlashSettings() {
-		return flashSettings;
-	}
-
 	public CheckDto check() {
 		IConnection current = Red5.getConnectionLocal();
 		StreamClient c = sessionManager.get(IClientUtil.getId(current.getClient()));

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java
----------------------------------------------------------------------
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java
index edf14db..84086cd 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/basic/ConfigurationDao.java
@@ -23,25 +23,50 @@ import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_APPLICAT
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_APPLICATION_NAME;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_CRYPT;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_EXT_PROCESS_TTL;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_CAM_QUALITY;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_ECHO_PATH;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_MIC_RATE;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_SECURE;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_SECURE_PROXY;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_VIDEO_BANDWIDTH;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_VIDEO_CODEC;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_VIDEO_FPS;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_HEADER_CSP;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_HEADER_XFRAME;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_KEYCODE_ARRANGE;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_KEYCODE_EXCLUSIVE;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_KEYCODE_MUTE;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MAX_UPLOAD_SIZE;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SIP_ENABLED;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.DEFAULT_APP_NAME;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.DEFAULT_BASE_URL;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.DEFAULT_MAX_UPLOAD_SIZE;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.EXT_PROCESS_TTL;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_BANDWIDTH;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_ECHO_PATH;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_FPS;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_MIC_RATE;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_NATIVE_SSL;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_PORT;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_QUALITY;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_SECURE;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_SSL_PORT;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_VIDEO_CODEC;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.ROOM_SETTINGS;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.configKeyCryptClassName;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.whiteboardDrawStatus;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.wicketApplicationName;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
 import java.lang.reflect.Constructor;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
+import java.util.Properties;
 
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
@@ -57,6 +82,7 @@ import org.apache.openmeetings.db.dao.IDataProviderDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.entity.basic.Configuration;
 import org.apache.openmeetings.util.DaoHelper;
+import org.apache.openmeetings.util.OmFileHelper;
 import org.apache.openmeetings.util.crypt.CryptProvider;
 import org.apache.wicket.Application;
 import org.red5.logging.Red5LoggerFactory;
@@ -64,6 +90,8 @@ import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 
+import com.github.openjson.JSONObject;
+
 /**
  * Insert/update/Delete on {@link Configuration}<br/>
  * <br/>
@@ -253,13 +281,20 @@ public class ConfigurationDao implements IDataProviderDao<Configuration> {
 			entity = em.merge(entity);
 		}
 		switch (key) {
+			case CONFIG_FLASH_SECURE:
+			case CONFIG_FLASH_SECURE_PROXY:
+			case CONFIG_FLASH_VIDEO_CODEC:
+			case CONFIG_FLASH_VIDEO_FPS:
+			case CONFIG_FLASH_VIDEO_BANDWIDTH:
+			case CONFIG_FLASH_CAM_QUALITY:
+			case CONFIG_FLASH_ECHO_PATH:
+			case CONFIG_FLASH_MIC_RATE:
+				reloadRoomSettings();
+				break;
 			case CONFIG_CRYPT:
 				configKeyCryptClassName = value;
 				CryptProvider.reset();
 				break;
-			case "show.whiteboard.draw.status":
-				whiteboardDrawStatus = Boolean.valueOf("1".equals(value));
-				break;
 			case CONFIG_APPLICATION_NAME:
 				APPLICATION_NAME = value;
 				break;
@@ -317,11 +352,32 @@ public class ConfigurationDao implements IDataProviderDao<Configuration> {
 		return configKeyCryptClassName;
 	}
 
-	public boolean getWhiteboardDrawStatus() {
-		if (whiteboardDrawStatus == null) {
-			String drawStatus = getConfValue("show.whiteboard.draw.status", String.class, "0");
-			whiteboardDrawStatus = Boolean.valueOf("1".equals(drawStatus));
+	public JSONObject reloadRoomSettings() {
+		try {
+			Properties props = new Properties();
+			try (InputStream is = new FileInputStream(new File(new File(OmFileHelper.getRootDir(), "conf"), "red5.properties"))) {
+				props.load(is);
+			}
+			ROOM_SETTINGS = new JSONObject()
+				.put(FLASH_SECURE, "yes".equals(getConfValue(CONFIG_FLASH_SECURE, String.class, "no")))
+				.put(FLASH_NATIVE_SSL, "best".equals(getConfValue(CONFIG_FLASH_SECURE_PROXY, String.class, "none")))
+				.put(FLASH_PORT, props.getProperty("rtmp.port"))
+				.put(FLASH_SSL_PORT, props.getProperty("rtmps.port"))
+				.put(FLASH_VIDEO_CODEC, getConfValue(CONFIG_FLASH_VIDEO_CODEC, String.class, "h263"))
+				.put(FLASH_FPS, getConfValue(CONFIG_FLASH_VIDEO_FPS, Integer.class, "30"))
+				.put(FLASH_BANDWIDTH, getConfValue(CONFIG_FLASH_VIDEO_BANDWIDTH, Integer.class, "0"))
+				.put(FLASH_QUALITY, getConfValue(CONFIG_FLASH_CAM_QUALITY, Integer.class, "90"))
+				.put(FLASH_ECHO_PATH, getConfValue(CONFIG_FLASH_ECHO_PATH, Integer.class, "128"))
+				.put(FLASH_MIC_RATE, getConfValue(CONFIG_FLASH_MIC_RATE, Integer.class, "22"))
+				.put("keycode", new JSONObject()
+						.put("arrange", getConfValue(CONFIG_KEYCODE_ARRANGE, Integer.class, "119"))
+						.put("exclusive", getConfValue(CONFIG_KEYCODE_EXCLUSIVE, Integer.class, "123"))
+						.put("mute", getConfValue(CONFIG_KEYCODE_MUTE, Integer.class, "118"))
+						)
+				;
+		} catch (Exception e) {
+			log.error("Unexpected exception while reloading room settings: ", e);
 		}
-		return whiteboardDrawStatus.booleanValue();
+		return ROOM_SETTINGS;
 	}
 }

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
----------------------------------------------------------------------
diff --git a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
index 7cb704b..cbd5a09 100644
--- a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
+++ b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
@@ -315,6 +315,10 @@ public class BackupImport {
 				if (c.getKey() == null || c.isDeleted()) {
 					continue;
 				}
+				String newKey = outdatedConfigKeys.get(c.getKey());
+				if (newKey != null) {
+					c.setKey(newKey);
+				}
 				Configuration cfg = configurationDao.forceGet(c.getKey());
 				if (cfg != null && !cfg.isDeleted()) {
 					log.warn("Non deleted configuration with same key is found! old value: {}, new value: {}", cfg.getValue(), c.getValue());
@@ -323,10 +327,6 @@ public class BackupImport {
 				if (c.getUser() != null && c.getUser().getId() == null) {
 					c.setUser(null);
 				}
-				String newKey = outdatedConfigKeys.get(c.getKey());
-				if (newKey != null) {
-					c.setKey(newKey);
-				}
 				if (CONFIG_CRYPT.equals(c.getKey())) {
 					try {
 						Class<?> clazz = Class.forName(c.getValue());

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java
----------------------------------------------------------------------
diff --git a/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java b/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java
index 2e39c2f..95b336b 100644
--- a/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java
+++ b/openmeetings-install/src/main/java/org/apache/openmeetings/installation/ImportInitvalues.java
@@ -49,6 +49,9 @@ import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_GOOGLE_A
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_HEADER_CSP;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_HEADER_XFRAME;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_IGNORE_BAD_SSL;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_KEYCODE_ARRANGE;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_KEYCODE_EXCLUSIVE;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_KEYCODE_MUTE;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_LOGIN_MIN_LENGTH;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MAX_UPLOAD_SIZE;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PASS_MIN_LENGTH;
@@ -67,6 +70,7 @@ import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SCREENSH
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SIP_ENABLED;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SIP_EXTEN_CONTEXT;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SIP_ROOM_PREFIX;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_PASS;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_PORT;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_SERVER;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SMTP_SYSTEM_EMAIL;
@@ -196,7 +200,7 @@ public class ImportInitvalues {
 
 		cfgDao.add(CONFIG_SMTP_USER, cfg.mailAuthName, null, "System auth email username");
 
-		cfgDao.add(CONFIG_SMTP_PORT, cfg.mailAuthPass, null, "System auth email password");
+		cfgDao.add(CONFIG_SMTP_PASS, cfg.mailAuthPass, null, "System auth email password");
 
 		cfgDao.add("mail.smtp.starttls.enable", cfg.mailUseTls, null, "Enable TLS 1=true, 0=false");
 
@@ -274,9 +278,6 @@ public class ImportInitvalues {
 
 		cfgDao.add(CONFIG_DASHBOARD_SHOW_RSS, "0", null, "Show RSS Tab");
 
-		cfgDao.add("show.whiteboard.draw.status", "0", null,
-				"Display name of the user who draw the current object (User Name auto-disapper after 3 seconds.");
-
 		cfgDao.add(CONFIG_MAX_UPLOAD_SIZE, "" + DEFAULT_MAX_UPLOAD_SIZE, null,
 				"Maximum size of upload file (bytes)"); // defaults to 100MB
 
@@ -292,12 +293,12 @@ public class ImportInitvalues {
 		cfgDao.add("calendar.conference.rooms.default.size", "50", null,
 				"Default number of participants conference room created via calendar");
 
-		// give exclusive audio key code
-		cfgDao.add("exclusive.audio.keycode", "123", null,
-				"A hot key code for the 'give exclusive audio' functionality. Keycode 123 is F12");
-		// mute/unmute audio key code
-		cfgDao.add("mute.keycode", "118", null,
-				"A hot key code for the 'mute/unmute audio' functionality. Keycode 118 is F7");
+		cfgDao.add(CONFIG_KEYCODE_ARRANGE, "119", null
+				, "A hot key code for arrange video windows functionality. Should be used with Shift key. (Keycode 119 is F8)");
+		cfgDao.add(CONFIG_KEYCODE_EXCLUSIVE, "123", null
+				, "A hot key code for the 'give exclusive audio' functionality. Should be used with Shift key. (Keycode 123 is F12)");
+		cfgDao.add(CONFIG_KEYCODE_MUTE, "118", null
+				, "A hot key code for the 'mute/unmute audio' functionality. Should be used with Shift key. (Keycode 118 is F7)");
 
 		// system-wide ldap params
 		cfgDao.add(CONFIG_DEFAULT_LDAP_ID, "0", null, "Ldap domain selected by default in the login screen");

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-server/src/site/xdoc/GeneralConfiguration.xml
----------------------------------------------------------------------
diff --git a/openmeetings-server/src/site/xdoc/GeneralConfiguration.xml b/openmeetings-server/src/site/xdoc/GeneralConfiguration.xml
index 2933596..4d9caec 100644
--- a/openmeetings-server/src/site/xdoc/GeneralConfiguration.xml
+++ b/openmeetings-server/src/site/xdoc/GeneralConfiguration.xml
@@ -104,14 +104,6 @@
 					<td> 1.8.x <b>default value changed in 3.3.0</b> </td>
 				</tr>
 				<tr>
-					<td> show.whiteboard.draw.status </td>
-					<td> 0 </td>
-					<td> Display name of the user who draw the current object
-						(User Name auto-disapper after 3 seconds.
-					</td>
-					<td> 1.8.x </td>
-				</tr>
-				<tr>
 					<td> mail.smtp.server </td>
 					<td> localhost </td>
 					<td> this is the smtp server to send messages </td>
@@ -255,6 +247,24 @@
 					<td> 3.0.x </td>
 				</tr>
 				<tr>
+					<td> video.arrange.keycode </td>
+					<td> 119 </td>
+					<td> A hot key code for arrange video windows functionality. Should be used with Shift key. (Keycode 119 is F8) </td>
+					<td> 2.2.x </td>
+				</tr>
+				<tr>
+					<td> exclusive.audio.keycode </td>
+					<td> 123 </td>
+					<td> A hot key code for the 'give exclusive audio' functionality. Should be used with Shift key. (Keycode 123 is F12) </td>
+					<td> 2.2.x </td>
+				</tr>
+				<tr>
+					<td> mute.keycode </td>
+					<td> 118 </td>
+					<td> A hot key code for the 'mute/unmute audio' functionality. Should be used with Shift key. (Keycode 118 is F7) </td>
+					<td> 2.2.x </td>
+				</tr>
+				<tr>
 					<td> application.base.url </td>
 					<td> http://localhost:5080/openmeetings </td>
 					<td> Base URL your OPenmeetings installation will be accessible at. </td>

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java
----------------------------------------------------------------------
diff --git a/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java b/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java
index a5fd5ef..731da2f 100644
--- a/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java
+++ b/openmeetings-util/src/main/java/org/apache/openmeetings/util/OpenmeetingsVariables.java
@@ -18,6 +18,8 @@
  */
 package org.apache.openmeetings.util;
 
+import com.github.openjson.JSONObject;
+
 public class OpenmeetingsVariables {
 	public static final String CONFIG_CRYPT = "crypt.class.name";
 	public static final String CONFIG_DASHBOARD_SHOW_CHAT = "dashboard.show.chat";
@@ -77,23 +79,37 @@ public class OpenmeetingsVariables {
 	public static final String HEADER_XFRAME_SAMEORIGIN = "SAMEORIGIN";
 	public static final String HEADER_CSP_SELF = "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval';";
 	public static final String MENU_ROOMS_NAME = "Conference Rooms";
+	public static final String CONFIG_KEYCODE_ARRANGE = "video.arrange.keycode";
+	public static final String CONFIG_KEYCODE_EXCLUSIVE = "exclusive.audio.keycode";
+	public static final String CONFIG_KEYCODE_MUTE = "mute.keycode";
 	public static final int RECENT_ROOMS_COUNT = 5;
 	public static final int LEVEL_USER = 1;
 	public static final int LEVEL_GROUP_ADMIN = 2;
 	public static final int LEVEL_ADMIN = 3;
+	public static final String WEB_DATE_PATTERN = "dd.MM.yyyy HH:mm:ss"; //FIXME need to be made locale based
+	public static final int USER_LOGIN_MINIMUM_LENGTH = 4;
+	public static final int USER_PASSWORD_MINIMUM_LENGTH = 8;
+	public static final String DEFAULT_APP_NAME = "OpenMeetings";
+	public static final long DEFAULT_MAX_UPLOAD_SIZE = 100 * 1024 * 1024; // 100MB
+	public static final String FLASH_SECURE = "secure";
+	public static final String FLASH_NATIVE_SSL = "native";
+	public static final String FLASH_PORT = "rtmpPort";
+	public static final String FLASH_SSL_PORT = "rtmpsPort";
+	public static final String FLASH_VIDEO_CODEC = "videoCodec";
+	public static final String FLASH_FPS = "fps";
+	public static final String FLASH_BANDWIDTH = "bandwidth";
+	public static final String FLASH_QUALITY = "quality";
+	public static final String FLASH_ECHO_PATH = "echoPath";
+	public static final String FLASH_MIC_RATE = "micRate";
+
 	public static int DEFAULT_MINUTES_REMINDER_SEND = 15;
 	public static String DEFAULT_BASE_URL = "http://localhost:5080/openmeetings/";
 
-	public static final String WEB_DATE_PATTERN = "dd.MM.yyyy HH:mm:ss"; //FIXME need to be made locale based
 	public static String webAppRootKey = null;
 	public static String webAppRootPath = null;
 	public static String configKeyCryptClassName = null;
-	public static final int USER_LOGIN_MINIMUM_LENGTH = 4;
-	public static final int USER_PASSWORD_MINIMUM_LENGTH = 8;
-	public static Boolean whiteboardDrawStatus = null;
 	public static String wicketApplicationName = null;
 	public static String APPLICATION_NAME = null;
-	public static final String DEFAULT_APP_NAME = "OpenMeetings";
-	public static final long DEFAULT_MAX_UPLOAD_SIZE = 100 * 1024 * 1024; // 100MB
 	public static int EXT_PROCESS_TTL = 20;
+	public static JSONObject ROOM_SETTINGS = new JSONObject();
 }

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java
index 7755221..7d93829 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/auth/SignInPage.java
@@ -19,8 +19,8 @@
 package org.apache.openmeetings.web.pages.auth;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_REGISTER_FRONTEND;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_IGNORE_BAD_SSL;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_REGISTER_FRONTEND;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
 import static org.apache.openmeetings.web.app.Application.getBean;
 

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
index 5dee0af..4a0d8ff 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
@@ -40,7 +40,6 @@ import java.util.Calendar;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.UUID;
 
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
@@ -125,8 +124,9 @@ public class RoomPanel extends BasePanel {
 
 		@Override
 		protected void respond(AjaxRequestTarget target) {
-			target.appendJavaScript("setRoomSizes();");
-			ExtendedClientProperties cp = WebSession.get().getExtendedProperties();
+			target.appendJavaScript("Room.setSize();");
+			WebSession ws = WebSession.get();
+			ExtendedClientProperties cp = ws.getExtendedProperties();
 			getBean(ConferenceLogDao.class).add(
 					ConferenceLog.Type.roomEnter
 					, getUserId(), "0", r.getId()
@@ -137,7 +137,10 @@ public class RoomPanel extends BasePanel {
 					.put("uid", _c.getUid())
 					.put("interview", isInterview)
 					.put("showMicStatus", !r.getHiddenElements().contains(RoomElement.MicrophoneStatus));
-			target.appendJavaScript(String.format("VideoManager.init(%s);", options));
+			if (!Strings.isEmpty(r.getRedirectURL()) && (ws.getSoapLogin() != null || ws.getInvitation() != null)) {
+				options.put("reloadUrl", r.getRedirectURL());
+			}
+			target.appendJavaScript(String.format("Room.init(%s);", options));
 			WebSocketHelper.sendRoom(new RoomMessage(r.getId(), getUserId(), RoomMessage.Type.roomEnter));
 			// play video from other participants
 			initVideos(target);
@@ -684,7 +687,7 @@ public class RoomPanel extends BasePanel {
 		if (handler != null) {
 			handler.add(getBasePage().getHeader(), getMainPanel().getTopControls());
 			if (isVisible()) {
-				handler.appendJavaScript("roomLoad();");
+				handler.appendJavaScript("Room.load();");
 			}
 		}
 		return this;
@@ -695,7 +698,7 @@ public class RoomPanel extends BasePanel {
 			getMainPanel().getChat().toggle(handler, true);
 		}
 		handler.add(this.setVisible(true));
-		handler.appendJavaScript("roomLoad();");
+		handler.appendJavaScript("Room.load();");
 	}
 
 	@Override
@@ -704,7 +707,7 @@ public class RoomPanel extends BasePanel {
 		if (r.isHidden(RoomElement.Chat)) {
 			getMainPanel().getChat().toggle(handler, true);
 		}
-		handler.appendJavaScript("if (typeof roomUnload == 'function') { roomUnload(); }");
+		handler.appendJavaScript("if (typeof Room !== 'undefined') { Room.unload(); }");
 		Application.exitRoom(getClient());
 		getMainPanel().getChat().roomExit(r, handler);
 	}
@@ -719,12 +722,6 @@ public class RoomPanel extends BasePanel {
 		} else {
 			response.render(JavaScriptHeaderItem.forReference(WB_JS_REFERENCE));
 		}
-		WebSession ws = WebSession.get();
-		if (!Strings.isEmpty(r.getRedirectURL()) && (ws.getSoapLogin() != null || ws.getInvitation() != null)) {
-			response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forScript(
-					String.format("function roomReload(event, ui) {window.location.href='%s';}", r.getRedirectURL())
-					, String.format("room-reload-%s", UUID.randomUUID()))));
-		}
 		if (room.isVisible()) {
 			response.render(OnDomReadyHeaderItem.forScript(roomEnter.getCallbackScript()));
 		}

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/SwfPanel.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/SwfPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/SwfPanel.java
index 37e33c2..076bd2f 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/SwfPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/SwfPanel.java
@@ -18,17 +18,16 @@
  */
 package org.apache.openmeetings.web.room;
 
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.FLASH_NATIVE_SSL;
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.FLASH_PORT;
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.FLASH_SECURE;
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.FLASH_SSL_PORT;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_NATIVE_SSL;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_PORT;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_SECURE;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.ROOM_SETTINGS;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_SSL_PORT;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
-import static org.apache.openmeetings.web.app.Application.getBean;
 import static org.apache.wicket.RuntimeConfigurationType.DEVELOPMENT;
 
 import java.net.URL;
 
-import org.apache.openmeetings.core.remote.ScopeApplicationAdapter;
 import org.apache.openmeetings.web.app.Application;
 import org.apache.openmeetings.web.common.BasePanel;
 import org.apache.openmeetings.web.common.OmAjaxClientInfoBehavior;
@@ -124,7 +123,7 @@ public class SwfPanel extends BasePanel {
 				URL url = new URL(cp.getCodebase());
 				String path = url.getPath();
 				path = path.substring(1, path.indexOf('/', 2) + 1);
-				JSONObject gs = getBean(ScopeApplicationAdapter.class).getFlashSettings();
+				JSONObject gs = ROOM_SETTINGS;
 				s.put("flashProtocol", gs.getBoolean(FLASH_SECURE) ? "rtmps" : "rtmp")
 						.put("flashPort", gs.getBoolean(FLASH_SECURE) ? gs.getString(FLASH_SSL_PORT) : gs.getString(FLASH_PORT))
 						.put("proxy", gs.getBoolean(FLASH_NATIVE_SSL) ? "best" : "none")

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java
index 1d080b2..f1a0c1f 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java
@@ -18,12 +18,10 @@
  */
 package org.apache.openmeetings.web.room;
 
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.FLASH_FPS;
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.FLASH_NATIVE_SSL;
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.FLASH_PORT;
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.FLASH_SECURE;
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.FLASH_SSL_PORT;
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.FLASH_VIDEO_CODEC;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_PORT;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_SECURE;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_SSL_PORT;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.ROOM_SETTINGS;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
 import static org.apache.openmeetings.web.app.Application.NAME_ATTR_KEY;
 import static org.apache.openmeetings.web.app.Application.getBean;
@@ -34,7 +32,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.openmeetings.core.remote.ScopeApplicationAdapter;
 import org.apache.openmeetings.db.dao.room.RoomDao;
 import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.util.OmFileHelper;
@@ -75,17 +72,14 @@ public class VideoSettings extends Panel {
 
 	public static JSONObject getInitJson(ExtendedClientProperties cp, Long roomId, String sid) {
 		String scope = roomId == null ? OmFileHelper.HIBERNATE : "" + roomId;
-		JSONObject gs = getBean(ScopeApplicationAdapter.class).getFlashSettings();
-		JSONObject s = new JSONObject()
-				.put(FLASH_VIDEO_CODEC, gs.get(FLASH_VIDEO_CODEC))
-				.put(FLASH_FPS, gs.get(FLASH_FPS))
+		JSONObject gs = ROOM_SETTINGS;
+		JSONObject s = new JSONObject(ROOM_SETTINGS.toString())
 				.put("sid", sid)
 				.put("wmode", cp.isBrowserInternetExplorer() && cp.getBrowserVersionMajor() == 11 ? "opaque" : "direct");
 		try {
 			URL url = new URL(cp.getCodebase());
 			String path = url.getPath();
 			path = path.substring(1, path.indexOf('/', 2) + 1) + scope;
-			s.put(FLASH_NATIVE_SSL, gs.getBoolean(FLASH_NATIVE_SSL));
 			String host = getHost(roomId, url.getHost());
 			int port = url.getPort() > -1 ? url.getPort() : url.getDefaultPort();
 			if (gs.getBoolean(FLASH_SECURE)) {

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java
index 1ba6ff3..e116a70 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java
@@ -19,11 +19,11 @@
 package org.apache.openmeetings.web.room.menu;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.FLASH_NATIVE_SSL;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SCREENSHARING_ALLOW_REMOTE;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SCREENSHARING_FPS;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SCREENSHARING_FPS_SHOW;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SCREENSHARING_QUALITY;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_NATIVE_SSL;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
 import static org.apache.openmeetings.web.app.Application.getBean;
 import static org.apache.openmeetings.web.app.Application.getOnlineClient;

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js
index a37b8f9..f421fc5 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/room.js
@@ -58,7 +58,7 @@ var VideoUtil = (function() {
 			, bottom: winoff.top + win.height()};
 	}
 	function _getPos(list, w, h) {
-		if (VideoManager.getOptions().interview) {
+		if (Room.getOptions().interview) {
 			return {left: 0, top: 0};
 		}
 		var wba = $(WBA_SEL);
@@ -115,14 +115,12 @@ var VideoUtil = (function() {
 		} while (!posFound);
 		return {left: rectNew.left, top: rectNew.top};
 	}
-	function _arrange(e) {
-		if (e.which === 119 && e.shiftKey) { // Shift+F8
-			let list = [], elems = $(VID_SEL);
-			for (let i = 0; i < elems.length; ++i) {
-				let v = $(elems[i]);
-				v.css(_getPos(list, v.width(), v.height()));
-				list.push(_getRect(v));
-			}
+	function _arrange() {
+		let list = [], elems = $(VID_SEL);
+		for (let i = 0; i < elems.length; ++i) {
+			let v = $(elems[i]);
+			v.css(_getPos(list, v.width(), v.height()));
+			list.push(_getRect(v));
 		}
 	}
 
@@ -214,7 +212,7 @@ var Video = (function() {
 			, name = _getName()
 			, _w = c.self ? Math.max(300, c.width) : c.width
 			, _h = c.self ? Math.max(200, c.height) : c.height
-			, opts = VideoManager.getOptions();
+			, opts = Room.getOptions();
 		{ //scope
 			let cont = opts.interview ? $('.pod.pod-' + c.pod) : $('.room.box');
 			cont.append($('#user-video').clone().attr('id', _id).attr('title', name)
@@ -309,7 +307,7 @@ var Video = (function() {
 		vc = v.find('.video');
 		vc.width(_w).height(_h);
 		//broadcast
-		var o = VideoManager.getOptions();
+		var o = Room.getOptions();
 		if (c.self) {
 			o.cam = c.cam;
 			o.mic = c.mic;
@@ -329,7 +327,7 @@ var Video = (function() {
 		v.dialog("widget").css(_pos);
 	}
 	function _update(_c) {
-		var opts = VideoManager.getOptions();
+		var opts = Room.getOptions();
 		c.screenActivities = _c.screenActivities;
 		c.activities = _c.activities;
 		if (VideoUtil.hasAudio(c)) {
@@ -365,15 +363,15 @@ var Video = (function() {
 	return self;
 });
 var VideoManager = (function() {
-	var self = {}, options, share;
+	var self = {}, share, inited = false;
 
-	function _init(_options) {
-		options = _options;
-		VideoSettings.init(self.getOptions());
+	function _init() {
+		VideoSettings.init(Room.getOptions());
 		share = $('.room.box').find('.icon.shared.ui-button');
+		inited = true;
 	}
 	function _update(c) {
-		if (options === undefined) {
+		if (!inited) {
 			return;
 		}
 		for (let i = 0; i < c.streams.length; ++i) {
@@ -402,7 +400,7 @@ var VideoManager = (function() {
 		v.remove();
 	}
 	function _play(c) {
-		if (options === undefined) {
+		if (!inited) {
 			return;
 		}
 		if (VideoUtil.isSharing(c)) {
@@ -500,7 +498,6 @@ var VideoManager = (function() {
 		}
 	}
 
-	self.getOptions = function() { return JSON.parse(JSON.stringify(options)); };
 	self.init = _init;
 	self.update = _update;
 	self.play = _play;
@@ -513,75 +510,110 @@ var VideoManager = (function() {
 	self.exclusive = _exclusive;
 	return self;
 })();
-function setRoomSizes() {
-	var sb = $(".room.sidebar.left")
-		, w = $(window).width() - sb.width() - 8
-		, h = $(window).height() - $('#menu').height() - 3
-		, p = sb.find('.tabs');
-	sb.height(h);
-	var hh = h - 5;
-	p.height(hh);
-	$(".user.list", p).height(hh - $("ul", p).height() - $(".user.header", p).height() - 5);
-	var holder = $('.room.holder');
-	if (sb.width() > 230) {
-		holder.addClass('big').removeClass('small');
-	} else {
-		holder.removeClass('big').addClass('small');
+var Room = (function() {
+	var self = {}, options;
+
+	function _init(_options) {
+		options = _options;
+		VideoManager.init();
+	}
+	function _keyHandler(e) {
+		if (e.shiftKey) {
+			switch (e.which) {
+				case options.keycode.arrange: // Shift+F8 by default
+					VideoUtil.arrange();
+					break;
+				case options.keycode.exclusive: // Shift+F12 by default
+					//VideoUtil.arrange();
+					break;
+				case options.keycode.mute: // Shift+F7 by default
+					//VideoUtil.arrange();
+					break;
+			}
+		}
 	}
-	if (!!WbArea) {
-		WbArea.resize(sb.width() + 5, w, h);
+	function _setSize() {
+		var sb = $(".room.sidebar.left")
+			, w = $(window).width() - sb.width() - 8
+			, h = $(window).height() - $('#menu').height() - 3
+			, p = sb.find('.tabs');
+		sb.height(h);
+		var hh = h - 5;
+		p.height(hh);
+		$(".user.list", p).height(hh - $("ul", p).height() - $(".user.header", p).height() - 5);
+		var holder = $('.room.holder');
+		if (sb.width() > 230) {
+			holder.addClass('big').removeClass('small');
+		} else {
+			holder.removeClass('big').addClass('small');
+		}
+		if (!!WbArea) {
+			WbArea.resize(sb.width() + 5, w, h);
+		}
 	}
-}
-function roomReload(event, ui) {
-	window.location.reload();
-}
-function roomClosed(jqEvent, msg) {
-	roomUnload();
-	$(".room.holder").remove();
-	$("#chatPanel").remove();
-	var dlg = $('#disconnected-dlg');
-	dlg.dialog({
-		modal: true
-		, close: roomReload
-		, buttons: [
-			{
-				text: dlg.data('reload')
-				, icons: {primary: "ui-icon-refresh"}
-				, click: function() {
-					$(this).dialog("close");
+	function _reload(event, ui) {
+		if (!!options.reloadUrl) {
+			window.location.href = options.reloadUrl;
+		} else {
+			window.location.reload();
+		}
+	}
+	function _close(jqEvent, msg) {
+		_unload();
+		$(".room.holder").remove();
+		$("#chatPanel").remove();
+		var dlg = $('#disconnected-dlg');
+		dlg.dialog({
+			modal: true
+			, close: _reload
+			, buttons: [
+				{
+					text: dlg.data('reload')
+					, icons: {primary: "ui-icon-refresh"}
+					, click: function() {
+						$(this).dialog("close");
+					}
 				}
+			]
+		});
+	}
+	function _load() {
+		$(".room.sidebar.left").ready(function() {
+			_setSize();
+		});
+		$(window).on('resize.openmeetings', function() {
+			_setSize();
+		});
+		$(".room.sidebar.left").resizable({
+			handles: "e"
+			, stop: function(event, ui) {
+				_setSize();
 			}
-		]
-	});
-}
-function roomLoad() {
-	$(".room.sidebar.left").ready(function() {
-		setRoomSizes();
-	});
-	$(window).on('resize.openmeetings', function() {
-		setRoomSizes();
-	});
-	$(".room.sidebar.left").resizable({
-		handles: "e"
-		, stop: function(event, ui) {
-			setRoomSizes();
-		}
-	});
-	Wicket.Event.subscribe("/websocket/closed", roomClosed);
-	Wicket.Event.subscribe("/websocket/error", roomClosed);
-	$(window).keyup(VideoUtil.arrange);
-}
-function roomUnload() {
-	$(window).off('resize.openmeetings');
-	Wicket.Event.unsubscribe("/websocket/closed", roomClosed);
-	Wicket.Event.unsubscribe("/websocket/error", roomClosed);
-	if (!!WbArea) {
-		WbArea.destroy();
-	}
-	VideoSettings.close();
-	$('.ui-dialog.user-video').remove();
-	$(window).off('keyup', VideoUtil.arrange);
-}
+		});
+		Wicket.Event.subscribe("/websocket/closed", _close);
+		Wicket.Event.subscribe("/websocket/error", _close);
+		$(window).keyup(_keyHandler);
+	}
+	function _unload() {
+		$(window).off('resize.openmeetings');
+		Wicket.Event.unsubscribe("/websocket/closed", _close);
+		Wicket.Event.unsubscribe("/websocket/error", _close);
+		if (!!WbArea) {
+			WbArea.destroy();
+		}
+		VideoSettings.close();
+		$('.ui-dialog.user-video').remove();
+		$(window).off('keyup', _keyHandler);
+	}
+
+	self.init = _init;
+	self.getOptions = function() { return JSON.parse(JSON.stringify(options)); };
+	self.setSize = _setSize;
+	self.keyHandler = _keyHandler;
+	self.load = _load;
+	self.unload = _unload;
+	return self;
+})();
 function startPrivateChat(el) {
 	Chat.addTab('chatTab-u' + el.parent().parent().data("userid"), el.parent().parent().find('.user.name').text());
 	Chat.open();

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.html
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.html
index e44afe7..44e85e8 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.html
@@ -30,7 +30,7 @@
 				<div wicket:id="user"></div>
 			</div>
 		</div>
-		<script>setRoomSizes();</script>
+		<script>Room.setSize();</script>
 	</wicket:fragment>
 
 	<wicket:fragment wicket:id="file-panel">

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/AbstractWbPanel.java
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/AbstractWbPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/AbstractWbPanel.java
index bd0aba7..7ae8687 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/AbstractWbPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/AbstractWbPanel.java
@@ -96,7 +96,7 @@ public abstract class AbstractWbPanel extends Panel {
 
 	public AbstractWbPanel update(IPartialPageRequestHandler handler) {
 		if (handler != null) {
-			handler.appendJavaScript(String.format("setRoomSizes();WbArea.setRole('%s');", getRole()));
+			handler.appendJavaScript(String.format("Room.setSize();WbArea.setRole('%s');", getRole()));
 		}
 		return this;
 	}

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/af05e413/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb.js
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb.js
index 2b8827f..b0b7a98 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb.js
@@ -1426,7 +1426,7 @@ var WbArea = (function() {
 	self.clearAll = function(json) {
 		if (!_inited) return;
 		self.getWb(json.wbId).clearAll();
-		setRoomSizes();
+		Room.setSize();
 	};
 	self.clearSlide = function(json) {
 		if (!_inited) return;