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 2023/08/17 08:36:46 UTC

[openmeetings] branch master updated: [OPENMEETINGS-2232] JS code is better aligned and browserable

This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/master by this push:
     new 1cb368917 [OPENMEETINGS-2232] JS code is better aligned and browserable
1cb368917 is described below

commit 1cb368917e2eb58e2f087e89d97d8fdadd608e5f
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Thu Aug 17 15:36:31 2023 +0700

    [OPENMEETINGS-2232] JS code is better aligned and browserable
---
 openmeetings-web/pom.xml                           | 82 ++--------------------
 openmeetings-web/src/main/front/chat/package.json  | 18 -----
 openmeetings-web/src/main/front/main/package.json  | 21 ------
 openmeetings-web/src/main/front/package.json       | 28 ++++++++
 openmeetings-web/src/main/front/room/package.json  | 21 ------
 .../src/main/front/settings/package.json           | 22 ------
 .../src/main/front/{chat/src => src/chat}/chat.js  |  4 +-
 .../front/{chat/src => src/chat}/cssemoticons.js   |  0
 .../src/main/front/{chat/src => src/chat}/index.js |  0
 .../src/main/front/{chat => }/src/index.js         |  5 +-
 .../src/main/front/{main/src => src/main}/index.js | 15 ++--
 .../main/front/{main/src => src/main}/omutils.js   | 51 ++++----------
 .../main/front/{main/src => src/main}/settings.js  |  2 +-
 .../front/{room/src => src/room}/activities.js     |  3 +
 .../src/main/front/{room/src => src/room}/index.js |  0
 .../front/{room/src => src/room}/quick-poll.js     |  1 +
 .../src/main/front/{room/src => src/room}/room.js  |  8 ++-
 .../main/front/{room/src => src/room}/sharer.js    |  5 +-
 .../front/{room/src => src/room}/sip-dialer.js     |  0
 .../front/{room/src => src/room}/user-list-util.js |  0
 .../main/front/{room/src => src/room}/user-list.js |  6 ++
 .../front/{room/src => src/room}/user-settings.js  |  3 +
 .../{room/src => src/room}/video-manager-util.js   |  4 ++
 .../front/{room/src => src/room}/video-manager.js  | 12 ++--
 .../src/main/front/{room/src => src/room}/video.js | 20 ++++--
 .../main/front/{room/src => src/room}/volume.js    |  2 +
 .../{settings/src => src/settings}/WebRtcPeer.js   |  5 +-
 .../front/{settings/src => src/settings}/index.js  |  9 +--
 .../{settings/src => src/settings}/mic-level.js    |  1 +
 .../{settings/src => src/settings}/ring-buffer.js  |  0
 .../{settings/src => src/settings}/settings.js     | 10 ++-
 .../{settings/src => src/settings}/video-util.js   | 53 ++++++++++----
 .../src/main/front/{wb/src => src/wb}/index.js     |  0
 .../front/{wb/src => src/wb}/interview-area.js     |  1 +
 .../main/front/{wb/src => src/wb}/wb-area-base.js  |  0
 .../src/main/front/{wb/src => src/wb}/wb-area.js   |  1 +
 .../src/main/front/{wb/src => src/wb}/wb-player.js |  3 +
 .../src/main/front/{wb/src => src/wb}/wb-role.js   |  0
 .../front/{wb/src => src/wb}/wb-tool-apointer.js   |  0
 .../main/front/{wb/src => src/wb}/wb-tool-arrow.js |  0
 .../main/front/{wb/src => src/wb}/wb-tool-base.js  |  0
 .../front/{wb/src => src/wb}/wb-tool-clipart.js    |  0
 .../front/{wb/src => src/wb}/wb-tool-ellipse.js    |  0
 .../main/front/{wb/src => src/wb}/wb-tool-line.js  |  0
 .../main/front/{wb/src => src/wb}/wb-tool-math.js  |  0
 .../main/front/{wb/src => src/wb}/wb-tool-paint.js |  2 +
 .../front/{wb/src => src/wb}/wb-tool-pointer.js    |  0
 .../main/front/{wb/src => src/wb}/wb-tool-rect.js  |  0
 .../front/{wb/src => src/wb}/wb-tool-shape-base.js |  0
 .../main/front/{wb/src => src/wb}/wb-tool-shape.js |  2 +
 .../front/{wb/src => src/wb}/wb-tool-stat-math.js  |  0
 .../main/front/{wb/src => src/wb}/wb-tool-text.js  |  2 +
 .../front/{wb/src => src/wb}/wb-tool-textbox.js    |  0
 .../main/front/{wb/src => src/wb}/wb-tool-uline.js |  0
 .../main/front/{wb/src => src/wb}/wb-tool-util.js  |  0
 .../front/{wb/src => src/wb}/wb-tool-whiteout.js   |  0
 .../src/main/front/{wb/src => src/wb}/wb-tools.js  |  3 +
 .../src/main/front/{wb/src => src/wb}/wb-utils.js  |  0
 .../src/main/front/{wb/src => src/wb}/wb-zoom.js   |  1 +
 .../src/main/front/{wb/src => src/wb}/wb.js        |  1 +
 openmeetings-web/src/main/front/wb/package.json    | 23 ------
 openmeetings-web/src/main/front/webpack.common.js  | 39 ++++++++++
 openmeetings-web/src/main/front/webpack.dev.js     | 12 ++++
 openmeetings-web/src/main/front/webpack.prod.js    | 11 +++
 .../web/common/OmAjaxClientInfoBehavior.java       |  6 +-
 .../apache/openmeetings/web/room/RoomPanel.java    | 11 +--
 .../openmeetings/web/room/VideoSettings.java       |  6 +-
 .../apache/openmeetings/web/room/wb/WbPanel.java   |  3 -
 .../apache/openmeetings/web/user/chat/Chat.java    |  6 +-
 69 files changed, 254 insertions(+), 290 deletions(-)

diff --git a/openmeetings-web/pom.xml b/openmeetings-web/pom.xml
index 003db33d9..cafe0bb91 100644
--- a/openmeetings-web/pom.xml
+++ b/openmeetings-web/pom.xml
@@ -81,92 +81,20 @@
 						<goals><goal>install-node-and-npm</goal></goals>
 					</execution>
 					<execution>
-						<id>main-install</id>
+						<id>npm-install</id>
 						<goals><goal>npm</goal></goals>
 						<configuration>
-							<workingDirectory>src/main/front/main</workingDirectory>
+							<workingDirectory>src/main/front</workingDirectory>
 						</configuration>
 					</execution>
 					<execution>
-						<id>main</id>
+						<id>js</id>
 						<goals><goal>npm</goal></goals>
 						<configuration>
 							<arguments>run build</arguments>
-							<workingDirectory>src/main/front/main</workingDirectory>
+							<workingDirectory>src/main/front</workingDirectory>
 							<environmentVariables>
-								<outDir>${project.build.directory}/generated-sources/js/org/apache/openmeetings/web/common/</outDir>
-							</environmentVariables>
-						</configuration>
-					</execution>
-					<execution>
-						<id>chat-install</id>
-						<goals><goal>npm</goal></goals>
-						<configuration>
-							<workingDirectory>src/main/front/chat</workingDirectory>
-						</configuration>
-					</execution>
-					<execution>
-						<id>chat</id>
-						<goals><goal>npm</goal></goals>
-						<configuration>
-							<arguments>run build</arguments>
-							<workingDirectory>src/main/front/chat</workingDirectory>
-							<environmentVariables>
-								<outDir>${project.build.directory}/generated-sources/js/org/apache/openmeetings/web/user/chat/</outDir>
-							</environmentVariables>
-						</configuration>
-					</execution>
-					<execution>
-						<id>settings-install</id>
-						<goals><goal>npm</goal></goals>
-						<configuration>
-							<workingDirectory>src/main/front/settings</workingDirectory>
-						</configuration>
-					</execution>
-					<execution>
-						<id>settings</id>
-						<goals><goal>npm</goal></goals>
-						<configuration>
-							<arguments>run build</arguments>
-							<workingDirectory>src/main/front/settings</workingDirectory>
-							<environmentVariables>
-								<outDir>${project.build.directory}/generated-sources/js/org/apache/openmeetings/web/room/</outDir>
-							</environmentVariables>
-						</configuration>
-					</execution>
-					<execution>
-						<id>room-install</id>
-						<goals><goal>npm</goal></goals>
-						<configuration>
-							<workingDirectory>src/main/front/room</workingDirectory>
-						</configuration>
-					</execution>
-					<execution>
-						<id>room</id>
-						<goals><goal>npm</goal></goals>
-						<configuration>
-							<arguments>run build</arguments>
-							<workingDirectory>src/main/front/room</workingDirectory>
-							<environmentVariables>
-								<outDir>${project.build.directory}/generated-sources/js/org/apache/openmeetings/web/room/</outDir>
-							</environmentVariables>
-						</configuration>
-					</execution>
-					<execution>
-						<id>wb-install</id>
-						<goals><goal>npm</goal></goals>
-						<configuration>
-							<workingDirectory>src/main/front/wb</workingDirectory>
-						</configuration>
-					</execution>
-					<execution>
-						<id>wb</id>
-						<goals><goal>npm</goal></goals>
-						<configuration>
-							<arguments>run build</arguments>
-							<workingDirectory>src/main/front/wb</workingDirectory>
-							<environmentVariables>
-								<outDir>${project.build.directory}/generated-sources/js/org/apache/openmeetings/web/room/wb/</outDir>
+								<outDir>${project.build.directory}/${project.build.finalName}/js/</outDir>
 							</environmentVariables>
 						</configuration>
 					</execution>
diff --git a/openmeetings-web/src/main/front/chat/package.json b/openmeetings-web/src/main/front/chat/package.json
deleted file mode 100644
index cbe5592e1..000000000
--- a/openmeetings-web/src/main/front/chat/package.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "name": "chat",
-  "version": "1.0.0",
-  "description": "Methods for OM chat",
-  "main": "src/index.js",
-  "scripts": {
-    "build-dev": "browserify src/index.js --transform-key=staging -o ${outDir}${npm_package_name}.js",
-    "build-prod": "browserify src/index.js --transform-key=production -p tinyify -o ${outDir}${npm_package_name}.min.js",
-    "build": "npm run build-dev && npm run build-prod"
-  },
-  "author": "",
-  "license": "Apache-2.0",
-  "rat-license": "Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0",
-  "devDependencies": {
-    "browserify": "^17.0.0",
-    "tinyify": "^4.0.0"
-  }
-}
diff --git a/openmeetings-web/src/main/front/main/package.json b/openmeetings-web/src/main/front/main/package.json
deleted file mode 100644
index f2911a24f..000000000
--- a/openmeetings-web/src/main/front/main/package.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "name": "main",
-  "version": "1.0.0",
-  "description": "Common Utility Modules",
-  "main": "src/index.js",
-  "scripts": {
-    "build-dev": "browserify src/index.js --transform-key=staging -o ${outDir}${npm_package_name}.js",
-    "build-prod": "browserify src/index.js --transform-key=production -p tinyify -o ${outDir}${npm_package_name}.min.js",
-    "build": "npm run build-dev && npm run build-prod"
-  },
-  "author": "",
-  "license": "Apache-2.0",
-  "rat-license": "Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0",
-  "devDependencies": {
-    "browserify": "^17.0.0",
-    "tinyify": "^4.0.0"
-  },
-  "dependencies": {
-    "ua-parser-js": "^1.0.35"
-  }
-}
diff --git a/openmeetings-web/src/main/front/package.json b/openmeetings-web/src/main/front/package.json
new file mode 100644
index 000000000..eb4bf82a0
--- /dev/null
+++ b/openmeetings-web/src/main/front/package.json
@@ -0,0 +1,28 @@
+{
+	"name": "om-frontend",
+	"version": "1.0.0",
+	"description": "",
+	"main": "./src/index.js",
+	"scripts": {
+		"build-dev": "webpack --config webpack.dev.js",
+		"build-prod": "webpack --config webpack.prod.js",
+		"build": "npm run build-dev && npm run build-prod"
+	},
+	"author": "",
+	"license": "Apache-2.0",
+	"rat-license": "Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0",
+	"devDependencies": {
+		"circular-dependency-plugin": "^5.2.2",
+		"webpack": "^5.88.2",
+		"webpack-cli": "^5.1.4",
+		"webpack-merge": "^5.9.0"
+	},
+	"dependencies": {
+		"fabric": "^5.3.0",
+		"freeice": "2.2.2",
+		"mathjax-full": "^3.2.2",
+		"nosleep.js": "^0.12.0",
+		"ua-parser-js": "^1.0.35",
+		"webrtc-adapter": "^8.2.2"
+	}
+}
diff --git a/openmeetings-web/src/main/front/room/package.json b/openmeetings-web/src/main/front/room/package.json
deleted file mode 100644
index dbaf2f2d4..000000000
--- a/openmeetings-web/src/main/front/room/package.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "name": "room",
-  "version": "1.0.0",
-  "description": "Methods for OM room",
-  "main": "src/index.js",
-  "scripts": {
-    "build-dev": "browserify src/index.js --transform-key=staging -o ${outDir}${npm_package_name}.js",
-    "build-prod": "browserify src/index.js --transform-key=production -p tinyify -o ${outDir}${npm_package_name}.min.js",
-    "build": "npm run build-dev && npm run build-prod"
-  },
-  "author": "",
-  "license": "Apache-2.0",
-  "rat-license": "Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0",
-  "devDependencies": {
-    "browserify": "^17.0.0",
-    "tinyify": "^4.0.0"
-  },
-  "dependencies": {
-    "nosleep.js": "^0.12.0"
-  }
-}
diff --git a/openmeetings-web/src/main/front/settings/package.json b/openmeetings-web/src/main/front/settings/package.json
deleted file mode 100644
index 2f363699b..000000000
--- a/openmeetings-web/src/main/front/settings/package.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "name": "settings",
-  "version": "1.0.0",
-  "description": "Video Utilities and video Settings dialog",
-  "main": "src/index.js",
-  "scripts": {
-    "build-dev": "browserify src/index.js --transform-key=staging -o ${outDir}${npm_package_name}.js",
-    "build-prod": "browserify src/index.js --transform-key=production -p tinyify -o ${outDir}${npm_package_name}.min.js",
-    "build": "npm run build-dev && npm run build-prod"
-  },
-  "author": "",
-  "license": "Apache-2.0",
-  "rat-license": "Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0",
-  "devDependencies": {
-    "browserify": "^17.0.0",
-    "tinyify": "^4.0.0"
-  },
-  "dependencies": {
-    "freeice": "2.2.2",
-    "webrtc-adapter": "^8.2.2"
-  }
-}
diff --git a/openmeetings-web/src/main/front/chat/src/chat.js b/openmeetings-web/src/main/front/src/chat/chat.js
similarity index 99%
rename from openmeetings-web/src/main/front/chat/src/chat.js
rename to openmeetings-web/src/main/front/src/chat/chat.js
index a9cba6067..ab119b7de 100644
--- a/openmeetings-web/src/main/front/chat/src/chat.js
+++ b/openmeetings-web/src/main/front/src/chat/chat.js
@@ -1,4 +1,6 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
+const Settings = require('../main/settings');
 const CSSEmoticon = require('./cssemoticons');
 
 const msgIdPrefix = 'chat-msg-id-'
@@ -180,7 +182,7 @@ function _reinit(opts) {
 			});
 		__setCssHeight(closedSize);
 	}
-	ctrlBlk.off().click(Chat.toggle);
+	ctrlBlk.off().click(_toggle);
 	$('#chatMessage').off().on('input propertychange paste', function () {
 		const room = $('.room-block .room-container');
 		if (room.length) {
diff --git a/openmeetings-web/src/main/front/chat/src/cssemoticons.js b/openmeetings-web/src/main/front/src/chat/cssemoticons.js
similarity index 100%
rename from openmeetings-web/src/main/front/chat/src/cssemoticons.js
rename to openmeetings-web/src/main/front/src/chat/cssemoticons.js
diff --git a/openmeetings-web/src/main/front/chat/src/index.js b/openmeetings-web/src/main/front/src/chat/index.js
similarity index 100%
copy from openmeetings-web/src/main/front/chat/src/index.js
copy to openmeetings-web/src/main/front/src/chat/index.js
diff --git a/openmeetings-web/src/main/front/chat/src/index.js b/openmeetings-web/src/main/front/src/index.js
similarity index 67%
rename from openmeetings-web/src/main/front/chat/src/index.js
rename to openmeetings-web/src/main/front/src/index.js
index 6ccf0439e..41fb9a013 100644
--- a/openmeetings-web/src/main/front/chat/src/index.js
+++ b/openmeetings-web/src/main/front/src/index.js
@@ -1,4 +1,3 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
-Object.assign(window, {
-	Chat: require('./chat')
-});
+
+// designed to be empty
\ No newline at end of file
diff --git a/openmeetings-web/src/main/front/main/src/index.js b/openmeetings-web/src/main/front/src/main/index.js
similarity index 95%
rename from openmeetings-web/src/main/front/main/src/index.js
rename to openmeetings-web/src/main/front/src/main/index.js
index 251cfe6d8..82fd2c11b 100644
--- a/openmeetings-web/src/main/front/main/src/index.js
+++ b/openmeetings-web/src/main/front/src/main/index.js
@@ -5,6 +5,14 @@ Wicket.BrowserInfo.collectExtraInfo = function(info) {
 	info.settings = Settings.load();
 };
 
+function _updateResize() {
+	const doc = document.documentElement;
+	doc.style.setProperty('--app-height', `${window.innerHeight}px`)
+}
+$(window).on('resize', _updateResize);
+//initial resize
+_updateResize();
+
 Object.assign(window, {
 	Settings: require('./settings')
 	, OmUtil: require('./omutils')
@@ -20,10 +28,3 @@ Object.assign(window, {
 		$('#busy-indicator').hide();
 	}
 });
-function _updateResize() {
-	const doc = document.documentElement
-	doc.style.setProperty('--app-height', `${window.innerHeight}px`)
-}
-$(window).on('resize', _updateResize);
-//initial resize
-_updateResize();
diff --git a/openmeetings-web/src/main/front/main/src/omutils.js b/openmeetings-web/src/main/front/src/main/omutils.js
similarity index 64%
rename from openmeetings-web/src/main/front/main/src/omutils.js
rename to openmeetings-web/src/main/front/src/main/omutils.js
index e2e98935c..7cf9c2526 100644
--- a/openmeetings-web/src/main/front/main/src/omutils.js
+++ b/openmeetings-web/src/main/front/src/main/omutils.js
@@ -1,10 +1,5 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
 
-const UAParser = require('ua-parser-js')
-	, ua = (typeof window !== 'undefined' && window.navigator) ? window.navigator.userAgent : ''
-	, parser = new UAParser(ua)
-	, browser = parser.getBrowser();
-
 let options, alertId = 0;
 
 function _init(_options) {
@@ -14,13 +9,14 @@ function _tmpl(tmplId, newId) {
 	return $(tmplId).clone().attr('id', newId || '');
 }
 function __alert(level, msg, autohideAfter) {
-	const holder = $('#alert-holder');
+	const holder = document.getElementById('alert-holder');
 	const curId = 'om-alert' + alertId++;
-	holder.append($(`<div id="${curId}" class="alert alert-${level} alert-dismissible fade show m-0" role="alert">${msg}
-			<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="${holder.data('lbl-close')}"></button>
-		</div>`));
+	holder.insertAdjacentHTML('beforeend',
+		`<div id="${curId}" class="alert alert-${level} alert-dismissible fade show m-0" role="alert">${msg}
+			<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="${holder.getAttribute('data-lbl-close')}"></button>
+		</div>`);
 	if (autohideAfter > 0) {
-		setTimeout(() => { $(`#${curId}`).alert('close');}, autohideAfter);
+		setTimeout(() => { bootstrap.Alert.getOrCreateInstance(`#${curId}`).close();}, autohideAfter);
 	}
 }
 function _error(msg, noAlert) {
@@ -48,11 +44,11 @@ function _log() {
 function _sendMessage(_m, _base) {
 	const base = _base || {}
 		, m = _m || {}
-		, msg = JSON.stringify($.extend({}, base, m));
+		, msg = JSON.stringify(Object.assign({}, base, m));
 	Wicket.WebSocket.send(msg);
 }
 function _requestNotifyPermission(callback, elseCallback) {
-	if (typeof(Notification) !== "undefined"
+	if (typeof(Notification) !== 'undefined'
 		&& Notification.permission !== 'granted'
 		&& Notification.permission !== 'denied') {
 		function onRequest(permission) {
@@ -60,20 +56,16 @@ function _requestNotifyPermission(callback, elseCallback) {
 				callback();
 			}
 		}
-		if (_isSafari()) {
-			Notification.requestPermission(onRequest);
-		} else {
-			Notification.requestPermission().then(onRequest);
-		}
+		Promise.resolve(Notification.requestPermission()).then(onRequest);
 	} else {
-		_info("No notification API for this browser");
+		_info('No notification API for this browser');
 		if (typeof(elseCallback) === 'function') {
 			elseCallback();
 		}
 	}
 }
 function _notify(msg, tag, elseCallback) {
-	if (typeof(Notification) !== "undefined"
+	if (typeof(Notification) !== 'undefined'
 		&& window === window.parent) {
 		function _newMessage() {
 			const opts = {
@@ -91,24 +83,12 @@ function _notify(msg, tag, elseCallback) {
 			_requestNotifyPermission(() => _newMessage());
 		}
 	} else {
-		_info("No notification API for this browser");
+		_info('No notification API for this browser');
 		if (typeof(elseCallback) === 'function') {
 			elseCallback();
 		}
 	}
 }
-function _isSafari() {
-	return browser.name === 'Safari';
-}
-function _isChrome() {
-	return browser.name === 'Chrome' || browser.name === 'Chromium';
-}
-function _isEdge() {
-	return browser.name === 'Edge' && "MSGestureEvent" in window;
-}
-function _isEdgeChromium() {
-	return browser.name === 'Edge' && !("MSGestureEvent" in window);
-}
 
 module.exports = {
 	init: _init
@@ -131,7 +111,7 @@ module.exports = {
 		_sendMessage(_m, {area: 'room', type: 'room'});
 	}
 	, setCssVar: function(key, val) {
-		($('body')[0]).style.setProperty(key, val);
+		document.querySelector('body').style.setProperty(key, val);
 	}
 	, ping: function() {
 		setTimeout(() => {
@@ -141,9 +121,4 @@ module.exports = {
 	}
 	, notify: _notify
 	, requestNotifyPermission: _requestNotifyPermission
-	, browser: browser
-	, isEdge: _isEdge
-	, isEdgeChromium: _isEdgeChromium
-	, isChrome: _isChrome
-	, isSafari: _isSafari
 };
diff --git a/openmeetings-web/src/main/front/main/src/settings.js b/openmeetings-web/src/main/front/src/main/settings.js
similarity index 85%
rename from openmeetings-web/src/main/front/main/src/settings.js
rename to openmeetings-web/src/main/front/src/main/settings.js
index e075c985e..9277b7d8c 100644
--- a/openmeetings-web/src/main/front/main/src/settings.js
+++ b/openmeetings-web/src/main/front/src/main/settings.js
@@ -16,7 +16,7 @@ function _save(s) {
 }
 
 module.exports = {
-	isRtl: 'rtl' === $('html').attr('dir')
+	isRtl: 'rtl' === document.querySelector('html').getAttribute('dir')
 	, load: _load
 	, save: _save
 };
diff --git a/openmeetings-web/src/main/front/room/src/activities.js b/openmeetings-web/src/main/front/src/room/activities.js
similarity index 98%
rename from openmeetings-web/src/main/front/room/src/activities.js
rename to openmeetings-web/src/main/front/src/room/activities.js
index 4c26d4059..3cdb0ab63 100644
--- a/openmeetings-web/src/main/front/room/src/activities.js
+++ b/openmeetings-web/src/main/front/src/room/activities.js
@@ -1,4 +1,7 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
+const Settings = require('../main/settings');
+
 const closedHeight = 20, timeout = 10000;
 let activities, aclean, modArea, area, openedHeight = 345
 	, openedHeightPx = openedHeight + 'px', inited = false
diff --git a/openmeetings-web/src/main/front/room/src/index.js b/openmeetings-web/src/main/front/src/room/index.js
similarity index 100%
rename from openmeetings-web/src/main/front/room/src/index.js
rename to openmeetings-web/src/main/front/src/room/index.js
diff --git a/openmeetings-web/src/main/front/room/src/quick-poll.js b/openmeetings-web/src/main/front/src/room/quick-poll.js
similarity index 97%
rename from openmeetings-web/src/main/front/room/src/quick-poll.js
rename to openmeetings-web/src/main/front/src/room/quick-poll.js
index aad2053e6..97de5a91b 100644
--- a/openmeetings-web/src/main/front/room/src/quick-poll.js
+++ b/openmeetings-web/src/main/front/src/room/quick-poll.js
@@ -1,4 +1,5 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
 const UserListUtil = require('./user-list-util');
 
 function _setQuickPollRights() {
diff --git a/openmeetings-web/src/main/front/room/src/room.js b/openmeetings-web/src/main/front/src/room/room.js
similarity index 95%
rename from openmeetings-web/src/main/front/room/src/room.js
rename to openmeetings-web/src/main/front/src/room/room.js
index 88e9b279d..376bb9731 100644
--- a/openmeetings-web/src/main/front/room/src/room.js
+++ b/openmeetings-web/src/main/front/src/room/room.js
@@ -1,4 +1,10 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const Settings = require('../main/settings');
+const VideoUtil = require('../settings/video-util');
+const VideoSettings = require('../settings/settings');
+const InterviewWbArea = require('../wb/interview-area');
+const DrawWbArea = require('../wb/wb-area')
+
 const NoSleep = require('nosleep.js');
 const VideoManager = require('./video-manager');
 const Sharer = require('./sharer');
@@ -87,7 +93,7 @@ function _keyHandler(e) {
 	if (__keyPressed(options.keycode.arrange, e)) {
 		VideoUtil.arrange();
 	} else if (__keyPressed(options.keycode.arrangeresize, e)) {
-		VideoUtil.arrangeResize();
+		VideoUtil.arrangeResize(VideoSettings.load());
 	} else if (__keyPressed(options.keycode.muteothers, e)) {
 		const v = _getSelfAudioClient();
 		if (v !== null) {
diff --git a/openmeetings-web/src/main/front/room/src/sharer.js b/openmeetings-web/src/main/front/src/room/sharer.js
similarity index 95%
rename from openmeetings-web/src/main/front/room/src/sharer.js
rename to openmeetings-web/src/main/front/src/room/sharer.js
index 774960ff7..38846254c 100644
--- a/openmeetings-web/src/main/front/room/src/sharer.js
+++ b/openmeetings-web/src/main/front/src/room/sharer.js
@@ -1,4 +1,5 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const VideoUtil = require('../settings/video-util');
 const VideoMgrUtil = require('./video-manager-util');
 
 const SHARE_STARTING = 'starting'
@@ -39,7 +40,7 @@ function _init() {
 	} else {
 		type = sharer.find('select.type');
 		fps = sharer.find('select.fps');
-		_disable(fps, OmUtil.isEdge());
+		_disable(fps, VideoUtil.isEdge());
 		sbtn = sharer.find('.share-start-stop').off().click(function() {
 			if (shareState === SHARE_STOPPED) {
 				_setShareState(SHARE_STARTING);
@@ -86,7 +87,7 @@ function _disable(e, state) {
 	}
 }
 function _typeDisabled() {
-	return OmUtil.isEdge() || OmUtil.isChrome() || OmUtil.isEdgeChromium();
+	return VideoUtil.isEdge() || VideoUtil.isChrome() || VideoUtil.isEdgeChromium();
 }
 function _setBtnState(btn, state) {
 	const dis = SHARE_STOPPED !== state
diff --git a/openmeetings-web/src/main/front/room/src/sip-dialer.js b/openmeetings-web/src/main/front/src/room/sip-dialer.js
similarity index 100%
rename from openmeetings-web/src/main/front/room/src/sip-dialer.js
rename to openmeetings-web/src/main/front/src/room/sip-dialer.js
diff --git a/openmeetings-web/src/main/front/room/src/user-list-util.js b/openmeetings-web/src/main/front/src/room/user-list-util.js
similarity index 100%
rename from openmeetings-web/src/main/front/room/src/user-list-util.js
rename to openmeetings-web/src/main/front/src/room/user-list-util.js
diff --git a/openmeetings-web/src/main/front/room/src/user-list.js b/openmeetings-web/src/main/front/src/room/user-list.js
similarity index 96%
rename from openmeetings-web/src/main/front/room/src/user-list.js
rename to openmeetings-web/src/main/front/src/room/user-list.js
index 75306a260..113565073 100644
--- a/openmeetings-web/src/main/front/room/src/user-list.js
+++ b/openmeetings-web/src/main/front/src/room/user-list.js
@@ -1,4 +1,10 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const Settings = require('../main/settings');
+const Chat = require('../chat/chat');
+const VideoUtil = require('../settings/video-util');
+const VideoSettings = require('../settings/settings');
+
+const OmUtil = require('../main/omutils');
 const VideoManager = require('./video-manager');
 const UserListUtil = require('./user-list-util');
 const QuickPoll = require('./quick-poll');
diff --git a/openmeetings-web/src/main/front/room/src/user-settings.js b/openmeetings-web/src/main/front/src/room/user-settings.js
similarity index 93%
rename from openmeetings-web/src/main/front/room/src/user-settings.js
rename to openmeetings-web/src/main/front/src/room/user-settings.js
index 43aed96cb..5aa07d92e 100644
--- a/openmeetings-web/src/main/front/room/src/user-settings.js
+++ b/openmeetings-web/src/main/front/src/room/user-settings.js
@@ -1,4 +1,7 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const Settings = require('../main/settings');
+const Chat = require('../chat/chat');
+const VideoSettings = require('../settings/settings');
 
 function __initMuteOthers() {
 	let s = VideoSettings.load();
diff --git a/openmeetings-web/src/main/front/room/src/video-manager-util.js b/openmeetings-web/src/main/front/src/room/video-manager-util.js
similarity index 92%
rename from openmeetings-web/src/main/front/room/src/video-manager-util.js
rename to openmeetings-web/src/main/front/src/room/video-manager-util.js
index e3d7c4fd2..b8422f145 100644
--- a/openmeetings-web/src/main/front/room/src/video-manager-util.js
+++ b/openmeetings-web/src/main/front/src/room/video-manager-util.js
@@ -1,4 +1,8 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
+const VideoUtil = require('../settings/video-util');
+const VideoSettings = require('../settings/settings');
+
 let share;
 
 function __savePod(v) {
diff --git a/openmeetings-web/src/main/front/room/src/video-manager.js b/openmeetings-web/src/main/front/src/room/video-manager.js
similarity index 93%
rename from openmeetings-web/src/main/front/room/src/video-manager.js
rename to openmeetings-web/src/main/front/src/room/video-manager.js
index b8df5cce5..5d3f61d14 100644
--- a/openmeetings-web/src/main/front/room/src/video-manager.js
+++ b/openmeetings-web/src/main/front/src/room/video-manager.js
@@ -1,4 +1,8 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
+const VideoUtil = require('../settings/video-util');
+const VideoSettings = require('../settings/settings');
+
 let share, inited = false;
 const VideoMgrUtil = require('./video-manager-util');
 const Video = require('./video');
@@ -44,7 +48,7 @@ function _onReceive(msg) {
 function _onKMessage(m) {
 	switch (m.id) {
 		case 'clientLeave':
-			$(`${VID_SEL}[data-client-uid="${m.uid}"]`).each(function() {
+			$(`${VideoUtil.VID_SEL}[data-client-uid="${m.uid}"]`).each(function() {
 				VideoMgrUtil.closeV($(this));
 			});
 			if (share.data('cuid') === m.uid) {
@@ -139,7 +143,7 @@ function _update(c) {
 		}
 	});
 	if (c.uid === Room.getOptions().uid) {
-		$(VID_SEL).each(function() {
+		$(VideoUtil.VID_SEL).each(function() {
 			$(this).data().setRights(c.rights);
 		});
 	}
@@ -187,7 +191,7 @@ function _play(streams, iceServers) {
 	});
 }
 function _find(uid) {
-	return $(`${VID_SEL}[data-client-uid="${uid}"][data-client-type="WEBCAM"]`);
+	return $(`${VideoUtil.VID_SEL}[data-client-uid="${uid}"][data-client-type="WEBCAM"]`);
 }
 function _userSpeaks(uid, active) {
 	const u = $(`#user${uid} .audio-activity`)
@@ -213,7 +217,7 @@ function _mute(uid, mute) {
 	}
 }
 function _muteOthers(uid) {
-	$(VID_SEL).each(function() {
+	$(VideoUtil.VID_SEL).each(function() {
 		const w = $(this), v = w.data(), v2 = w.data('client-uid');
 		if (v && v2) {
 			v.mute(uid !== v2);
diff --git a/openmeetings-web/src/main/front/room/src/video.js b/openmeetings-web/src/main/front/src/room/video.js
similarity index 96%
rename from openmeetings-web/src/main/front/room/src/video.js
rename to openmeetings-web/src/main/front/src/room/video.js
index 72c664cd5..c81427920 100644
--- a/openmeetings-web/src/main/front/room/src/video.js
+++ b/openmeetings-web/src/main/front/src/room/video.js
@@ -1,4 +1,10 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
+const VideoUtil = require('../settings/video-util');
+const MicLevel = require('../settings/mic-level');
+const WebRtcPeer = require('../settings/WebRtcPeer');
+const VideoSettings = require('../settings/settings');
+
 const AudioCtx = window.AudioContext || window.webkitAudioContext;
 const VideoMgrUtil = require('./video-manager-util');
 const Sharer = require('./sharer');
@@ -42,9 +48,9 @@ module.exports = class Video {
 				Sharer.setRecState(Sharer.SHARE_STOPPED);
 				OmUtil.error(err);
 			}
-			const b = OmUtil.browser;
+			const b = VideoUtil.browser;
 			let promise, cnts;
-			if (OmUtil.isEdge() && b.major > 16) {
+			if (VideoUtil.isEdge() && b.major > 16) {
 				cnts = {
 					video: true
 				};
@@ -53,7 +59,7 @@ module.exports = class Video {
 				cnts = {
 					video: true
 				};
-				if (OmUtil.isSafari()) {
+				if (VideoUtil.isSafari()) {
 					promise = new Promise((resolve) => {
 						VideoUtil.askPermission(resolve);
 					}).then(() => navigator.mediaDevices.getDisplayMedia(cnts));
@@ -111,7 +117,7 @@ module.exports = class Video {
 							data.aSrc = data.aCtx.createMediaStreamSource(stream);
 							data.aSrc.connect(data.gainNode);
 							data.gainNode.connect(data.analyser);
-							if (OmUtil.isEdge()) {
+							if (VideoUtil.isEdge()) {
 								data.analyser.connect(data.aCtx.destination);
 							} else {
 								data.aDest = data.aCtx.createMediaStreamDestination();
@@ -173,7 +179,7 @@ module.exports = class Video {
 			VideoUtil.playSrc(vid, state.stream, true);
 
 			const data = state.data;
-			data.rtcPeer = new WebRtcPeerSendonly(VideoUtil.addIceServers(state.options, msg));
+			data.rtcPeer = new WebRtcPeer.Sendonly(VideoUtil.addIceServers(state.options, msg));
 			if (data.analyser) {
 				level = new MicLevel();
 				level.meter(data.analyser, lm, _micActivity, OmUtil.error);
@@ -220,7 +226,7 @@ module.exports = class Video {
 				, onConnectionStateChange: () => __connectionStateChangeListener(state)
 			}, msg);
 			const data = state.data;
-			data.rtcPeer = new WebRtcPeerRecvonly(options);
+			data.rtcPeer = new WebRtcPeer.Recvonly(options);
 			data.rtcPeer.createOffer()
 				.then(sdpOffer => {
 					data.rtcPeer.processLocalOffer(sdpOffer);
@@ -423,7 +429,7 @@ module.exports = class Video {
 					widget.css('width', stored.w);
 					widget.css('height', stored.h)
 				} else {
-					VideoUtil.setPos(v, VideoUtil.getPos(VideoUtil.getRects(VIDWIN_SEL, _id), sd.width, sd.height + 25));
+					VideoUtil.setPos(v, VideoUtil.getPos(VideoUtil.getRects(VideoUtil.VIDWIN_SEL, _id), sd.width, sd.height + 25));
 				}
 			}
 			state.video = $(hasVideo ? '<video>' : '<audio>').attr('id', 'vid' + _id)
diff --git a/openmeetings-web/src/main/front/room/src/volume.js b/openmeetings-web/src/main/front/src/room/volume.js
similarity index 98%
rename from openmeetings-web/src/main/front/room/src/volume.js
rename to openmeetings-web/src/main/front/src/room/volume.js
index f5c7bef2b..f766f026b 100644
--- a/openmeetings-web/src/main/front/room/src/volume.js
+++ b/openmeetings-web/src/main/front/src/room/volume.js
@@ -1,4 +1,6 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
+
 module.exports = class Volume {
 	constructor() {
 		const self = this;
diff --git a/openmeetings-web/src/main/front/settings/src/WebRtcPeer.js b/openmeetings-web/src/main/front/src/settings/WebRtcPeer.js
similarity index 99%
rename from openmeetings-web/src/main/front/settings/src/WebRtcPeer.js
rename to openmeetings-web/src/main/front/src/settings/WebRtcPeer.js
index a8a3faffc..ff88259b7 100644
--- a/openmeetings-web/src/main/front/settings/src/WebRtcPeer.js
+++ b/openmeetings-web/src/main/front/src/settings/WebRtcPeer.js
@@ -18,6 +18,7 @@
 // taken from here:
 // https://github.com/OpenVidu/openvidu/blob/master/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts
 // and monkey-patched
+const OmUtil = require('../main/omutils');
 
 const freeice = require('freeice');
 
@@ -592,6 +593,6 @@ class WebRtcPeerSendrecv extends WebRtcPeer {
 };
 
 module.exports = {
-	WebRtcPeerRecvonly: WebRtcPeerRecvonly,
-	WebRtcPeerSendonly: WebRtcPeerSendonly
+	Recvonly: WebRtcPeerRecvonly,
+	Sendonly: WebRtcPeerSendonly
 };
diff --git a/openmeetings-web/src/main/front/settings/src/index.js b/openmeetings-web/src/main/front/src/settings/index.js
similarity index 60%
rename from openmeetings-web/src/main/front/settings/src/index.js
rename to openmeetings-web/src/main/front/src/settings/index.js
index 1d575b88a..d2ddbcf59 100644
--- a/openmeetings-web/src/main/front/settings/src/index.js
+++ b/openmeetings-web/src/main/front/src/settings/index.js
@@ -1,18 +1,13 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
 require('webrtc-adapter');
-const VideoUtil = require('./video-util');
-const {WebRtcPeerRecvonly, WebRtcPeerSendonly} = require('./WebRtcPeer');
 
 if (window.hasOwnProperty('isSecureContext') === false) {
 	window.isSecureContext = window.location.protocol == 'https:' || ["localhost", "127.0.0.1"].indexOf(window.location.hostname) !== -1;
 }
 
 Object.assign(window, {
-	VideoUtil: VideoUtil
-	, VIDWIN_SEL: VideoUtil.VIDWIN_SEL
-	, VID_SEL: VideoUtil.VID_SEL
+	VideoUtil: require('./video-util')
 	, MicLevel: require('./mic-level')
-	, WebRtcPeerRecvonly: WebRtcPeerRecvonly
-	, WebRtcPeerSendonly: WebRtcPeerSendonly
+	, WebRtcPeer: require('./WebRtcPeer')
 	, VideoSettings: require('./settings')
 });
diff --git a/openmeetings-web/src/main/front/settings/src/mic-level.js b/openmeetings-web/src/main/front/src/settings/mic-level.js
similarity index 98%
rename from openmeetings-web/src/main/front/settings/src/mic-level.js
rename to openmeetings-web/src/main/front/src/settings/mic-level.js
index d3c6d775f..c5baeea81 100644
--- a/openmeetings-web/src/main/front/settings/src/mic-level.js
+++ b/openmeetings-web/src/main/front/src/settings/mic-level.js
@@ -1,4 +1,5 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const VideoUtil = require('./video-util');
 const RingBuffer = require('./ring-buffer');
 
 module.exports = class MicLevel {
diff --git a/openmeetings-web/src/main/front/settings/src/ring-buffer.js b/openmeetings-web/src/main/front/src/settings/ring-buffer.js
similarity index 100%
rename from openmeetings-web/src/main/front/settings/src/ring-buffer.js
rename to openmeetings-web/src/main/front/src/settings/ring-buffer.js
diff --git a/openmeetings-web/src/main/front/settings/src/settings.js b/openmeetings-web/src/main/front/src/settings/settings.js
similarity index 97%
rename from openmeetings-web/src/main/front/settings/src/settings.js
rename to openmeetings-web/src/main/front/src/settings/settings.js
index 6623ba8d0..03a64e0f9 100644
--- a/openmeetings-web/src/main/front/settings/src/settings.js
+++ b/openmeetings-web/src/main/front/src/settings/settings.js
@@ -1,6 +1,10 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
+const Settings = require('../main/settings');
+
 const MicLevel = require('./mic-level');
 const VideoUtil = require('./video-util');
+const WebRtcPeer = require('./WebRtcPeer');
 
 const DEV_AUDIO = 'audioinput'
 	, DEV_VIDEO = 'videoinput'
@@ -129,7 +133,7 @@ function _updateRec() {
 	recBtn.prop('disabled', !recAllowed || (s.video.cam < 0 && s.video.mic < 0));
 }
 function _setCntsDimensions(cnts) {
-	if (OmUtil.isSafari()) {
+	if (VideoUtil.isSafari()) {
 		let width = s.video.width;
 		//valid widths are 320, 640, 1280
 		[320, 640, 1280].some(function(w) {
@@ -212,7 +216,7 @@ function _readValues(msg, func) {
 					VideoUtil.playSrc(vid[0], stream, true);
 					options.mediaStream = stream;
 
-					rtcPeer = new WebRtcPeerSendonly(options);
+					rtcPeer = new WebRtcPeer.Sendonly(options);
 					if (cnts.audio) {
 						lm.show();
 						level = new MicLevel();
@@ -387,7 +391,7 @@ function _onKMessage(m) {
 				, onIceCandidate: _onIceCandidate
 			}, m);
 			_clear();
-			rtcPeer = new WebRtcPeerRecvonly(options);
+			rtcPeer = new WebRtcPeer.Recvonly(options);
 			rtcPeer.createOffer()
 				.then(sdpOffer => {
 					rtcPeer.processLocalOffer(sdpOffer);
diff --git a/openmeetings-web/src/main/front/settings/src/video-util.js b/openmeetings-web/src/main/front/src/settings/video-util.js
similarity index 87%
rename from openmeetings-web/src/main/front/settings/src/video-util.js
rename to openmeetings-web/src/main/front/src/settings/video-util.js
index a3f55137c..7c0fc1451 100644
--- a/openmeetings-web/src/main/front/settings/src/video-util.js
+++ b/openmeetings-web/src/main/front/src/settings/video-util.js
@@ -1,4 +1,12 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
+const Settings = require('../main/settings');
+
+const UAParser = require('ua-parser-js')
+	, ua = (typeof window !== 'undefined' && window.navigator) ? window.navigator.userAgent : ''
+	, parser = new UAParser(ua)
+	, browser = parser.getBrowser();
+
 const WB_AREA_SEL = '.room-block .wb-block';
 const WBA_WB_SEL = '.room-block .wb-block .wb-tab-content';
 const VIDWIN_SEL = '.video.user-video';
@@ -8,6 +16,19 @@ const MIC_ACTIVITY = 'AUDIO';
 const SCREEN_ACTIVITY = 'SCREEN';
 const REC_ACTIVITY = 'RECORD';
 
+function _isSafari() {
+	return browser.name === 'Safari';
+}
+function _isChrome() {
+	return browser.name === 'Chrome' || browser.name === 'Chromium';
+}
+function _isEdge() {
+	return browser.name === 'Edge' && "MSGestureEvent" in window;
+}
+function _isEdgeChromium() {
+	return browser.name === 'Edge' && !("MSGestureEvent" in window);
+}
+
 function _getVid(uid) {
 	return 'video' + uid;
 }
@@ -165,13 +186,12 @@ function _arrange() {
 		list.push(_getRect(v));
 	});
 }
-function _arrangeResize() {
+function _arrangeResize(vSettings) {
 	const list = []
-		, s = VideoSettings.load()
 		, size = {width: 120, height: 90};
-	if (s.fixed.enabled) {
-		size.width = s.fixed.width;
-		size.height = s.fixed.height;
+	if (vSettings.fixed.enabled) {
+		size.width = vSettings.fixed.width;
+		size.height = vSettings.fixed.height;
 	}
 
 	function __getDialog(_v) {
@@ -259,16 +279,15 @@ function _disconnect(node) {
 	}
 }
 function _sharingSupported() {
-	const b = OmUtil.browser;
-	return (b.name === 'Edge' && b.major > 16)
+	return (browser.name === 'Edge' && browser.major > 16)
 		|| (typeof(navigator.mediaDevices.getDisplayMedia) === 'function'
-			&& (b.name === 'Firefox'
-				|| b.name === 'Opera'
-				|| b.name === 'Yandex'
-				|| OmUtil.isSafari()
-				|| OmUtil.isChrome()
-				|| OmUtil.isEdgeChromium()
-				|| (b.name === 'Mozilla' && b.major > 4)
+			&& (browser.name === 'Firefox'
+				|| browser.name === 'Opera'
+				|| browser.name === 'Yandex'
+				|| _isSafari()
+				|| _isChrome()
+				|| _isEdgeChromium()
+				|| (browser.name === 'Mozilla' && browser.major > 4)
 			));
 }
 function _highlight(el, clazz, count) {
@@ -328,4 +347,10 @@ module.exports = {
 	, sharingSupported: _sharingSupported
 	, highlight: _highlight
 	, playSrc: _playSrc
+
+	, browser: browser
+	, isEdge: _isEdge
+	, isEdgeChromium: _isEdgeChromium
+	, isChrome: _isChrome
+	, isSafari: _isSafari
 };
diff --git a/openmeetings-web/src/main/front/wb/src/index.js b/openmeetings-web/src/main/front/src/wb/index.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/index.js
rename to openmeetings-web/src/main/front/src/wb/index.js
diff --git a/openmeetings-web/src/main/front/wb/src/interview-area.js b/openmeetings-web/src/main/front/src/wb/interview-area.js
similarity index 98%
rename from openmeetings-web/src/main/front/wb/src/interview-area.js
rename to openmeetings-web/src/main/front/src/wb/interview-area.js
index 2440e637d..907260c85 100644
--- a/openmeetings-web/src/main/front/wb/src/interview-area.js
+++ b/openmeetings-web/src/main/front/src/wb/interview-area.js
@@ -1,4 +1,5 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
 const WbAreaBase = require('./wb-area-base');
 
 module.exports = class InterviewWbArea extends WbAreaBase {
diff --git a/openmeetings-web/src/main/front/wb/src/wb-area-base.js b/openmeetings-web/src/main/front/src/wb/wb-area-base.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-area-base.js
rename to openmeetings-web/src/main/front/src/wb/wb-area-base.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-area.js b/openmeetings-web/src/main/front/src/wb/wb-area.js
similarity index 99%
rename from openmeetings-web/src/main/front/wb/src/wb-area.js
rename to openmeetings-web/src/main/front/src/wb/wb-area.js
index 3132def91..c341ca42b 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-area.js
+++ b/openmeetings-web/src/main/front/src/wb/wb-area.js
@@ -1,4 +1,5 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
 const WbAreaBase = require('./wb-area-base');
 const Role = require('./wb-role');
 const Wb = require('./wb');
diff --git a/openmeetings-web/src/main/front/wb/src/wb-player.js b/openmeetings-web/src/main/front/src/wb/wb-player.js
similarity index 98%
rename from openmeetings-web/src/main/front/wb/src/wb-player.js
rename to openmeetings-web/src/main/front/src/wb/wb-player.js
index 3e639ed68..32864bf79 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-player.js
+++ b/openmeetings-web/src/main/front/src/wb/wb-player.js
@@ -1,4 +1,7 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
+const VideoUtil = require('../settings/video-util');
+
 const Role = require('./wb-role');
 const ToolUtil = require('./wb-tool-util');
 require('fabric');
diff --git a/openmeetings-web/src/main/front/wb/src/wb-role.js b/openmeetings-web/src/main/front/src/wb/wb-role.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-role.js
rename to openmeetings-web/src/main/front/src/wb/wb-role.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-apointer.js b/openmeetings-web/src/main/front/src/wb/wb-tool-apointer.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-apointer.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-apointer.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-arrow.js b/openmeetings-web/src/main/front/src/wb/wb-tool-arrow.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-arrow.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-arrow.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-base.js b/openmeetings-web/src/main/front/src/wb/wb-tool-base.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-base.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-base.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-clipart.js b/openmeetings-web/src/main/front/src/wb/wb-tool-clipart.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-clipart.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-clipart.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-ellipse.js b/openmeetings-web/src/main/front/src/wb/wb-tool-ellipse.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-ellipse.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-ellipse.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-line.js b/openmeetings-web/src/main/front/src/wb/wb-tool-line.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-line.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-line.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-math.js b/openmeetings-web/src/main/front/src/wb/wb-tool-math.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-math.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-math.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-paint.js b/openmeetings-web/src/main/front/src/wb/wb-tool-paint.js
similarity index 93%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-paint.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-paint.js
index d87e1b8bd..2c3dd6021 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-tool-paint.js
+++ b/openmeetings-web/src/main/front/src/wb/wb-tool-paint.js
@@ -1,4 +1,6 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const VideoUtil = require('../settings/video-util');
+
 const WbShapeBase = require('./wb-tool-shape-base');
 const ToolUtil = require('./wb-tool-util');
 
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-pointer.js b/openmeetings-web/src/main/front/src/wb/wb-tool-pointer.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-pointer.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-pointer.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-rect.js b/openmeetings-web/src/main/front/src/wb/wb-tool-rect.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-rect.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-rect.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-shape-base.js b/openmeetings-web/src/main/front/src/wb/wb-tool-shape-base.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-shape-base.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-shape-base.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-shape.js b/openmeetings-web/src/main/front/src/wb/wb-tool-shape.js
similarity index 96%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-shape.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-shape.js
index 7178bb430..7dfd5c6a3 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-tool-shape.js
+++ b/openmeetings-web/src/main/front/src/wb/wb-tool-shape.js
@@ -1,4 +1,6 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const VideoUtil = require('../settings/video-util');
+
 const WbShapeBase = require('./wb-tool-shape-base');
 
 module.exports = class WbShape extends WbShapeBase {
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-stat-math.js b/openmeetings-web/src/main/front/src/wb/wb-tool-stat-math.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-stat-math.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-stat-math.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-text.js b/openmeetings-web/src/main/front/src/wb/wb-tool-text.js
similarity index 98%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-text.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-text.js
index eee99c1ff..339e2da5f 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-tool-text.js
+++ b/openmeetings-web/src/main/front/src/wb/wb-tool-text.js
@@ -1,4 +1,6 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const VideoUtil = require('../settings/video-util');
+
 const ShapeBase = require('./wb-tool-shape-base');
 const ToolUtil = require('./wb-tool-util');
 require('fabric');
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-textbox.js b/openmeetings-web/src/main/front/src/wb/wb-tool-textbox.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-textbox.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-textbox.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-uline.js b/openmeetings-web/src/main/front/src/wb/wb-tool-uline.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-uline.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-uline.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-util.js b/openmeetings-web/src/main/front/src/wb/wb-tool-util.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-util.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-util.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-whiteout.js b/openmeetings-web/src/main/front/src/wb/wb-tool-whiteout.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-tool-whiteout.js
rename to openmeetings-web/src/main/front/src/wb/wb-tool-whiteout.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tools.js b/openmeetings-web/src/main/front/src/wb/wb-tools.js
similarity index 99%
rename from openmeetings-web/src/main/front/wb/src/wb-tools.js
rename to openmeetings-web/src/main/front/src/wb/wb-tools.js
index 895b8b323..30e23595d 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-tools.js
+++ b/openmeetings-web/src/main/front/src/wb/wb-tools.js
@@ -1,4 +1,7 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
+const Settings = require('../main/settings');
+
 const Role = require('./wb-role');
 const WbUtils = require('./wb-utils');
 const APointer = require('./wb-tool-apointer');
diff --git a/openmeetings-web/src/main/front/wb/src/wb-utils.js b/openmeetings-web/src/main/front/src/wb/wb-utils.js
similarity index 100%
rename from openmeetings-web/src/main/front/wb/src/wb-utils.js
rename to openmeetings-web/src/main/front/src/wb/wb-utils.js
diff --git a/openmeetings-web/src/main/front/wb/src/wb-zoom.js b/openmeetings-web/src/main/front/src/wb/wb-zoom.js
similarity index 99%
rename from openmeetings-web/src/main/front/wb/src/wb-zoom.js
rename to openmeetings-web/src/main/front/src/wb/wb-zoom.js
index 840bb3187..a48cb8e9b 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-zoom.js
+++ b/openmeetings-web/src/main/front/src/wb/wb-zoom.js
@@ -1,4 +1,5 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
 const Role = require('./wb-role');
 
 module.exports = class WbZoom {
diff --git a/openmeetings-web/src/main/front/wb/src/wb.js b/openmeetings-web/src/main/front/src/wb/wb.js
similarity index 99%
rename from openmeetings-web/src/main/front/wb/src/wb.js
rename to openmeetings-web/src/main/front/src/wb/wb.js
index fa4a4f806..1418f7597 100644
--- a/openmeetings-web/src/main/front/wb/src/wb.js
+++ b/openmeetings-web/src/main/front/src/wb/wb.js
@@ -1,4 +1,5 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+const OmUtil = require('../main/omutils');
 const Role = require('./wb-role');
 const WbTools = require('./wb-tools');
 const WbZoom = require('./wb-zoom');
diff --git a/openmeetings-web/src/main/front/wb/package.json b/openmeetings-web/src/main/front/wb/package.json
deleted file mode 100644
index 209c5723c..000000000
--- a/openmeetings-web/src/main/front/wb/package.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "name": "wb",
-  "version": "1.0.0",
-  "description": "Methods for WB",
-  "main": "src/index.js",
-  "scripts": {
-    "build-dev": "browserify src/index.js --transform-key=staging -p esmify -o ${outDir}${npm_package_name}.js",
-    "build-prod": "browserify src/index.js --transform-key=production -p esmify | terser -o ${outDir}${npm_package_name}.min.js",
-    "build": "npm run build-dev && npm run build-prod"
-  },
-  "author": "",
-  "license": "Apache-2.0",
-  "rat-license": "Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0",
-  "devDependencies": {
-    "browserify": "^17.0.0",
-    "esmify": "^2.1.1",
-    "terser": "^5.17.1"
-  },
-  "dependencies": {
-    "fabric": "^5.3.0",
-    "mathjax-full": "^3.2.2"
-  }
-}
diff --git a/openmeetings-web/src/main/front/webpack.common.js b/openmeetings-web/src/main/front/webpack.common.js
new file mode 100644
index 000000000..447f67d7d
--- /dev/null
+++ b/openmeetings-web/src/main/front/webpack.common.js
@@ -0,0 +1,39 @@
+// Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0
+const CircularDependencyPlugin = require('circular-dependency-plugin')
+
+module.exports = {
+	entry: {
+		main: {
+			import: './src/main/index.js'
+		},
+		chat: {
+			import: './src/chat/index.js'
+		},
+		settings: {
+			import: './src/settings/index.js'
+		},
+		room: {
+			import: './src/room/index.js'
+		},
+		wb: {
+			import: './src/wb/index.js'
+		},
+	},
+	externals: {
+		'../main/omutils': 'OmUtil',
+		'../main/settings': 'Settings',
+		'../chat/chat': 'Chat',
+		'../settings/video-util': 'VideoUtil',
+		'../settings/mic-level': 'MicLevel',
+		'../settings/WebRtcPeer': 'WebRtcPeer',
+		'../settings/settings': 'VideoSettings',
+		'../wb/interview-area': 'InterviewWbArea',
+		'../wb/wb-area': 'DrawWbArea',
+	},
+	plugins: [
+		new CircularDependencyPlugin(),
+	],
+	output: {
+		path: `${process.env.outDir}`,
+	},
+};
diff --git a/openmeetings-web/src/main/front/webpack.dev.js b/openmeetings-web/src/main/front/webpack.dev.js
new file mode 100644
index 000000000..b590a0cb2
--- /dev/null
+++ b/openmeetings-web/src/main/front/webpack.dev.js
@@ -0,0 +1,12 @@
+// Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0
+
+const { merge } = require('webpack-merge');
+const common = require('./webpack.common.js');
+
+module.exports = merge(common, {
+	mode: 'development',
+	devtool: 'inline-source-map',
+	output: {
+		filename: '[name].js',
+	},
+});
diff --git a/openmeetings-web/src/main/front/webpack.prod.js b/openmeetings-web/src/main/front/webpack.prod.js
new file mode 100644
index 000000000..d1a684eec
--- /dev/null
+++ b/openmeetings-web/src/main/front/webpack.prod.js
@@ -0,0 +1,11 @@
+// Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0
+
+const { merge } = require('webpack-merge');
+const common = require('./webpack.common.js');
+
+module.exports = merge(common, {
+	mode: 'production',
+	output: {
+		filename: '[name].min.js',
+	},
+});
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/OmAjaxClientInfoBehavior.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/OmAjaxClientInfoBehavior.java
index 7af38e58e..3e58c0556 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/OmAjaxClientInfoBehavior.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/OmAjaxClientInfoBehavior.java
@@ -29,17 +29,17 @@ import org.apache.wicket.ajax.AjaxClientInfoBehavior;
 import org.apache.wicket.markup.head.HeaderItem;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.JavaScriptUrlReferenceHeaderItem;
 import org.apache.wicket.markup.head.PriorityHeaderItem;
 import org.apache.wicket.markup.html.pages.BrowserInfoForm;
 import org.apache.wicket.protocol.http.request.WebClientInfo;
 import org.apache.wicket.request.cycle.RequestCycle;
-import org.apache.wicket.request.resource.JavaScriptResourceReference;
 
 import com.github.openjson.JSONObject;
 
 public class OmAjaxClientInfoBehavior extends AjaxClientInfoBehavior {
 	private static final long serialVersionUID = 1L;
-	private static final JavaScriptResourceReference MAIN_JS = new JavaScriptResourceReference(MainPanel.class, "main.js") {
+	private static final JavaScriptUrlReferenceHeaderItem MAIN_JS = new JavaScriptUrlReferenceHeaderItem("js/main.js", "om-main") {
 		private static final long serialVersionUID = 1L;
 
 		@Override
@@ -51,7 +51,7 @@ public class OmAjaxClientInfoBehavior extends AjaxClientInfoBehavior {
 	@Override
 	public void renderHead(Component component, IHeaderResponse response) {
 		super.renderHead(component, response);
-		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(MAIN_JS)));
+		response.render(new PriorityHeaderItem(MAIN_JS));
 		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forScript(
 				String.format("OmUtil.init(%s)", new JSONObject()
 						.put("debug", DEVELOPMENT == Application.get().getConfigurationType()))
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 f4bebbb9d..558bb6556 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
@@ -25,7 +25,7 @@ import static org.apache.openmeetings.db.entity.calendar.Appointment.allowedStar
 import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PDF;
 import static org.apache.openmeetings.web.app.WebSession.getDateFormat;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
-import static org.apache.openmeetings.web.room.wb.WbPanel.WB_JS_REFERENCE;
+import static org.apache.openmeetings.web.room.VideoSettings.VIDEO_SETTINGS_JS;
 
 import java.io.IOException;
 import java.nio.file.Files;
@@ -91,7 +91,6 @@ import org.apache.wicket.protocol.ws.api.BaseWebSocketBehavior;
 import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.apache.wicket.request.resource.JavaScriptResourceReference;
 import org.apache.wicket.request.resource.ResourceStreamResource;
 import org.apache.wicket.spring.injection.annot.SpringBean;
 import org.apache.wicket.util.resource.FileResourceStream;
@@ -690,13 +689,15 @@ public class RoomPanel extends BasePanel {
 	@Override
 	public void renderHead(IHeaderResponse response) {
 		super.renderHead(response);
-		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(WB_JS_REFERENCE)));
-		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(new JavaScriptResourceReference(RoomPanel.class, "room.js"))) {
+		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forUrl("js/room.js")) {
 			private static final long serialVersionUID = 1L;
 
 			@Override
 			public List<HeaderItem> getDependencies() {
-				return List.of(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(JQueryUILibrarySettings.get().getJavaScriptReference())));
+				return List.of(
+					VIDEO_SETTINGS_JS,
+					new PriorityHeaderItem(JavaScriptHeaderItem.forUrl("js/wb.js"))
+					, new PriorityHeaderItem(JavaScriptHeaderItem.forReference(JQueryUILibrarySettings.get().getJavaScriptReference())));
 			}
 		});
 		response.render(JavaScriptHeaderItem.forReference(TouchPunchResourceReference.instance()));
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 7a648d2d9..229617892 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
@@ -25,14 +25,12 @@ import org.apache.wicket.markup.head.JavaScriptHeaderItem;
 import org.apache.wicket.markup.head.PriorityHeaderItem;
 import org.apache.wicket.markup.html.panel.EmptyPanel;
 import org.apache.wicket.markup.html.panel.Panel;
-import org.apache.wicket.request.resource.JavaScriptResourceReference;
-import org.apache.wicket.request.resource.ResourceReference;
 
 import com.github.openjson.JSONObject;
 
 public class VideoSettings extends Panel {
 	private static final long serialVersionUID = 1L;
-	private static final ResourceReference SETTINGS_JS_REFERENCE = new JavaScriptResourceReference(VideoSettings.class, "settings.js");
+	public static final PriorityHeaderItem VIDEO_SETTINGS_JS = new PriorityHeaderItem(JavaScriptHeaderItem.forUrl("js/settings.js"));
 	public static final String URL = "url";
 	public static final String FALLBACK = "fallback";
 
@@ -44,7 +42,7 @@ public class VideoSettings extends Panel {
 	@Override
 	public void renderHead(IHeaderResponse response) {
 		super.renderHead(response);
-		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(SETTINGS_JS_REFERENCE)));
+		response.render(VIDEO_SETTINGS_JS);
 	}
 
 	public static JSONObject getInitJson(String sid) {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
index e466b28d7..df7d746e6 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
@@ -69,8 +69,6 @@ import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.ResourceModel;
-import org.apache.wicket.request.resource.JavaScriptResourceReference;
-import org.apache.wicket.request.resource.ResourceReference;
 import org.apache.wicket.spring.injection.annot.SpringBean;
 import org.apache.wicket.util.string.Strings;
 import org.danekja.java.util.function.serializable.SerializableBiConsumer;
@@ -90,7 +88,6 @@ public class WbPanel extends AbstractWbPanel {
 	private static final int DEFAULT_WIDTH = 640;
 	private static final int DEFAULT_HEIGHT = 480;
 	private static final int UNDO_SIZE = 20;
-	public static final ResourceReference WB_JS_REFERENCE = new JavaScriptResourceReference(WbPanel.class, "wb.js");
 	private final Long roomId;
 	private long wb2save = -1;
 	private final Map<Long, Deque<UndoObject>> undoList = new HashMap<>();
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java
index 3d91602c1..e7207ae46 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java
@@ -50,11 +50,11 @@ import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.head.HeaderItem;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.JavaScriptUrlReferenceHeaderItem;
 import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
 import org.apache.wicket.markup.head.PriorityHeaderItem;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.request.cycle.RequestCycle;
-import org.apache.wicket.request.resource.JavaScriptResourceReference;
 import org.apache.wicket.spring.injection.annot.SpringBean;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -171,7 +171,7 @@ public class Chat extends Panel {
 	@Override
 	public void renderHead(IHeaderResponse response) {
 		super.renderHead(response);
-		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(new JavaScriptResourceReference(Chat.class, "chat.js"))) {
+		response.render(new PriorityHeaderItem(new JavaScriptUrlReferenceHeaderItem("js/chat.js", "om-chat") {
 			private static final long serialVersionUID = 1L;
 
 			@Override
@@ -182,7 +182,7 @@ public class Chat extends Panel {
 						, new PriorityHeaderItem(JavaScriptHeaderItem.forScript("jQuery.fn.tooltip = bstooltip;", "restore-bs-tooltip"))
 						);
 			}
-		});
+		}));
 		response.render(new PriorityHeaderItem(getNamedFunction("chatActivity", chatActivity, explicit(PARAM_TYPE), explicit(PARAM_ROOM_ID), explicit(PARAM_MSG_ID))));
 
 		if (showDashboardChat) {