snapshot of https://github.com/jitsi/jitsi-meet.git @ fd1749dd28f4138aa80234fc7f311b19aaffa0e7 (2026-09-15)
Some checks failed
Prosody Plugin Tests / test (push) Failing after 10m11s
Close stale issues and PRs / stale (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-15 15:56:32 +05:00
commit 1d2bfcac0c
3159 changed files with 464524 additions and 0 deletions

13
static/404.html Normal file
View File

@@ -0,0 +1,13 @@
<html>
<head>
<!--#include virtual="/base.html" -->
<link rel="stylesheet" href="css/all.css"/>
<!--#include virtual="/title.html" -->
</head>
<body>
<div class="error_page">
<h2>404 Not Found</h2>
<p class="error_page__message">You can create a new conversation <a class="link" onclick="window.location = window.location.protocol + '//' + window.location.hostname">here</a></p>
</div>
</body>
</html>

23
static/close.html Normal file
View File

@@ -0,0 +1,23 @@
<html>
<head>
<!--#include virtual="/base.html" -->
<link rel="stylesheet" href="css/all.css"/>
<!--#include virtual="/title.html" -->
<script><!--#include virtual="/interface_config.js" --></script>
<script src="static/close.js"></script>
</head>
<body>
<div class="redirectPageMessage">
<div class="thanks-msg">
<p>Thank you for your feedback!</p>
</div>
<div class="hint-msg">
<p>
<span id="hintQuestion">Did you know?</span>
<span class="hint-msg__holder" id="hintMessage"></span>
</p>
<div class="happy-software"></div>
</div>
</div>
</body>
</html>

67
static/close.js Normal file
View File

@@ -0,0 +1,67 @@
/* global interfaceConfig */
// list of tips
const hints = [
'You can pin participants by clicking on their thumbnails.',
'You can tell others you have something to say by using the "Raise Hand" '
+ 'feature',
'You can learn about key shortcuts by pressing Shift+?',
'You can learn more about the state of everyone\'s connection by hovering '
+ 'on the bars in their thumbnail',
'You can hide all thumbnails by using the button in the bottom right corner'
];
/**
* Get a random hint message from hint array.
*
* @return {string} the hint message.
*/
function getHint() {
const l = hints.length - 1;
const n = Math.round(Math.random() * l);
return hints[n];
}
/**
* Inserts text message
* into DOM element
*
* @param id {string} element identificator
* @param msg {string} text message
*/
function insertTextMsg(id, msg) {
const el = document.getElementById(id);
if (el) {
el.textContent = msg;
}
}
/**
* Sets the hint and thanks messages. Will be executed on load event.
*/
function onLoad() {
// Intentionally use string concatenation as this file does not go through
// babel but IE11 is still supported.
// eslint-disable-next-line prefer-template
const thankYouMessage = 'Thank you for using ' + interfaceConfig.APP_NAME;
// Works only for close2.html because close.html doesn't have this element.
insertTextMsg('thanksMessage', thankYouMessage);
// If there is a setting show a special message only for the guests
if (interfaceConfig.CLOSE_PAGE_GUEST_HINT) {
if (window.sessionStorage.getItem('guest') === 'true') {
const element = document.getElementById('hintQuestion');
element.classList.add('hide');
insertTextMsg('hintMessage', interfaceConfig.CLOSE_PAGE_GUEST_HINT);
return;
}
}
insertTextMsg('hintMessage', getHint());
}
window.onload = onLoad;

23
static/close2.html Normal file
View File

@@ -0,0 +1,23 @@
<html>
<head>
<!--#include virtual="/base.html" -->
<link rel="stylesheet" href="css/all.css"/>
<!--#include virtual="/title.html" -->
<script><!--#include virtual="/interface_config.js" --></script>
<script src="static/close.js"></script>
</head>
<body>
<div class="redirectPageMessage">
<div class="thanks-msg">
<p id="thanksMessage"></p>
</div>
<div class="hint-msg">
<p>
<span id="hintQuestion">Did you know?</span>
<span class="hint-msg__holder" id="hintMessage"></span>
</p>
<div class="happy-software"></div>
</div>
</div>
</body>
</html>

0
static/close3.html Normal file
View File

0
static/close3.js Normal file
View File

29
static/dialInInfo.html Normal file
View File

@@ -0,0 +1,29 @@
<html xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--#include virtual="/base.html" -->
<link rel="stylesheet" href="css/all.css">
<script>
window.EXCALIDRAW_ASSET_PATH = 'libs/excalidraw/';
document.addEventListener('DOMContentLoaded', () => {
if (!JitsiMeetJS.app) {
return;
}
JitsiMeetJS.app.renderEntryPoint({
Component: JitsiMeetJS.app.entryPoints.DIALIN
});
});
</script>
<!--#include virtual="/title.html" -->
<script><!--#include virtual="/config.js" --></script>
<script><!--#include virtual="/interface_config.js" --></script>
<script src="libs/lib-jitsi-meet.min.js?v=139"></script>
<script src="libs/app.bundle.min.js?v=139"></script>
</head>
<body>
<div id="react" role="main"></div>
</body>
</html>

43
static/logout.html Normal file
View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Logged Out</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f5f5f5;
}
.container {
text-align: center;
padding: 20px;
}
.success {
color: #27ae60;
font-size: 48px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="success">✓</div>
<p>You have been logged out successfully.</p>
</div>
<script>
// Notify parent window that logout is complete
if (window.parent) {
window.parent.postMessage({
type: 'logout-success'
}, window.location.origin);
}
</script>
</body>
</html>

12
static/msredirect.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body>
<script>
window.opener
&& window.opener.postMessage({
type: 'ms-login',
url: window.location.href
}, window.location.origin);
</script>
</body>
</html>

21
static/oauth.html Normal file
View File

@@ -0,0 +1,21 @@
<html itemscope itemtype="http://schema.org/Product" prefix="og: http://ogp.me/ns#" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--#include virtual="/base.html" -->
<!--#include virtual="/title.html" -->
<script>
(function() {
var windowName = window.name;
window.opener && window.opener.postMessage({
type: 'dropbox-login',
windowName,
url: window.location.href
}, window.location.origin);
})();
</script>
</head>
<body>
</body>
</html>

64
static/offline.html Normal file
View File

@@ -0,0 +1,64 @@
<html>
<head>
<head>
<!--#include virtual="/head.html" -->
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--#include virtual="/base.html" -->
<link rel="apple-touch-icon" href="images/apple-touch-icon.png">
<!--#include virtual="/title.html" -->
</head>
<style>
body,
.content {
display: flex;
}
.content {
align-items: center;
flex-direction: column;
font-family: Arial, Helvetica, sans-serif;
font-size: 0.875rem;
font-weight: 400;
line-height: 1.125rem;
margin: auto;
max-width: 376px;
text-align: center;
}
h4 {
font-size: 1.5rem;
font-weight: 600;
line-height: 2rem;
margin: 24px auto;
}
.icon {
background-color: #FDD13A;
border-radius: 50%;
box-sizing: border-box;
height: 56px;
padding: 16px;
width: 56px;
}
</style>
</head>
<body>
<div class="content">
<div class="icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M21.8799 19.5904L12.8501 3.49284C12.7614 3.33481 12.6288 3.20474 12.4676 3.11782C11.9982 2.86467 11.4083 3.03257 11.1502 3.49284L2.1202 19.5904C2.04143 19.7309 2.00012 19.8885 2.00012 20.0488C2.00012 20.5741 2.43443 20.9999 2.97017 20.9999H21.0299C21.1934 20.9999 21.3542 20.9594 21.4974 20.8822C21.9668 20.629 22.138 20.0507 21.8799 19.5904ZM4.61072 19.0976L12 5.92477L19.3892 19.0976H4.61072ZM11.0302 16.2445C11.0302 15.7192 11.456 15.2934 11.9813 15.2934H12.0191C12.5444 15.2934 12.9702 15.7192 12.9702 16.2445C12.9702 16.7698 12.5444 17.1956 12.0191 17.1956H11.9813C11.456 17.1956 11.0302 16.7698 11.0302 16.2445ZM12.0002 10.5378C11.4645 10.5378 11.0302 10.9721 11.0302 11.5078V13.3722C11.0302 13.9079 11.4645 14.3422 12.0002 14.3422C12.5359 14.3422 12.9702 13.9079 12.9702 13.3722V11.5078C12.9702 10.9721 12.5359 10.5378 12.0002 10.5378Z"
fill="#131519" fill-opacity="0.87" />
</svg>
</div>
<h4>Connection error</h4>
Your device may be offline or our servers may be experiencing problems.
</div>
</body>
</html>

0
static/planLimit.html Normal file
View File

36
static/prejoin.html Normal file
View File

@@ -0,0 +1,36 @@
<html xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--#include virtual="/base.html" -->
<link rel="stylesheet" href="css/all.css">
<script>
window.EXCALIDRAW_ASSET_PATH = 'libs/excalidraw/';
document.addEventListener('DOMContentLoaded', () => {
if (!JitsiMeetJS.app) {
return;
}
const url = new URL(window.location.href);
const params = new URLSearchParams(url.search);
const styleType = params.get('styleType');
JitsiMeetJS.app.renderEntryPoint({
Component: JitsiMeetJS.app.entryPoints.PREJOIN,
props: {
styleType
}
})
})
</script>
<!--#include virtual="/title.html" -->
<script><!--#include virtual="/config.js" --></script>
<script><!--#include virtual="/interface_config.js" --></script>
<script src="libs/lib-jitsi-meet.min.js?v=139"></script>
<script src="libs/app.bundle.min.js?v=139"></script>
</head>
<body>
<div id="react" role="main"></div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

@@ -0,0 +1,23 @@
<html>
<head>
<!--#include virtual="/base.html" -->
<link rel="stylesheet" href="css/all.css"/>
<!--#include virtual="/title.html" -->
</head>
<body>
<div class = 'unsupported-desktop-browser'>
<h2 class = 'unsupported-desktop-browser__title'>
It looks like you're using a browser we don't fully support.
</h2>
<p class ='unsupported-desktop-browser__description'>
We recommend to try with the latest version of&nbsp;
<a
className = 'unsupported-desktop-browser__link'
href = 'https://www.google.com/chrome/' >Chrome</a>&nbsp;or&nbsp;
<a
class = 'unsupported-desktop-browser__link'
href = 'https://www.chromium.org/'>Chromium</a>
</p>
</div>
</body>
</html>

30
static/secondScreen.html Normal file
View File

@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html style="height: 100%;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--
Marks this document as the second-screen shell. The meeting window checks
for it before building anything in the popup, because a load event only
means that something was served: a 404 body, a redirect landing or a
dev-server proxy response would otherwise be treated as this page and
silently painted over. Keep in sync with SECOND_SCREEN_MARKER in
react/features/multi-screen/functions.web.ts.
-->
<meta name="jitsi-second-screen" content="1">
<title>Jitsi Meet</title>
</head>
<body style="margin: 0; height: 100%; background: #000; overflow: hidden;">
<!--
Intentionally empty: this page is the shell for a second-screen window
(react/features/multi-screen). The meeting window portals the window's
content into it. It exists so the popup has a real same-origin URL
instead of about:blank: embeds rendered inside the window (e.g. the
YouTube shared-video player) require the page to send a valid referrer,
which an about:blank window cannot (YouTube fails with error 153).
-->
</body>
</html>

View File

@@ -0,0 +1 @@
<template id="settings-toolbar-additional-content-template"></template>

364
static/sso.html Normal file
View File

@@ -0,0 +1,364 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSO Authentication</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f5f5f5;
}
.container {
text-align: center;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.spinner {
display: none;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error {
display: none;
color: #e74c3c;
margin-top: 20px;
}
.success {
display: none;
color: #27ae60;
font-size: 48px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<div id="spinner" class="spinner"></div>
<p id="spinner-msg" style="display: none;">Completing authentication...</p>
<div id="success" class="success">✓</div>
<p id="message-for-opener" style="display: none;">Authentication successful! You can close this window.</p>
<div id="error" class="error"></div>
</div>
<script>
<!--#include virtual="/config.js" -->
(function() {
/**
* Decodes a JWT token and returns the payload
* @param {string} token - The JWT token to decode
* @returns {object} The decoded payload
*/
function decodeJWT(token) {
try {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
} catch (error) {
throw new Error('Failed to decode JWT: ' + error.message);
}
}
/**
* Validates that the nonce in the ID token matches the stored nonce
* @param {string} idToken - The ID token to validate
* @param {string} expectedNonce - The nonce stored in sessionStorage
*/
function validateNonce(idToken, expectedNonce) {
const payload = decodeJWT(idToken);
if (!payload.nonce) {
throw new Error('ID token does not contain a nonce claim');
}
if (payload.nonce !== expectedNonce) {
throw new Error('Nonce validation failed: ID token nonce does not match stored nonce');
}
}
async function handleAuthorizationCode() {
try {
// Parse query parameters (Authorization Code Flow uses query params, not hash)
const urlParams = new URLSearchParams(window.location.search);
// Get state parameter first to check for refresh token
const stateParam = urlParams.get('state');
if (!stateParam) {
throw new Error('No state parameter received');
}
const state = JSON.parse(decodeURIComponent(stateParam));
// Get SSO configuration from config
const { sso } = config;
if (!sso || !sso.tokenService || !sso.clientId) {
throw new Error('Missing SSO configuration (tokenService or clientId)');
}
// Check if we have a refresh token in state - use it to refresh tokens
if (state.refreshToken) {
console.log('Using refresh token to get new tokens');
// Show spinner while refreshing tokens
document.getElementById('spinner').style.display = 'block';
document.getElementById('spinner-msg').style.display = 'block';
// Use refresh token to get new tokens
const tokenResponse = await fetch(`https://${sso.tokenService}/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: state.refreshToken,
client_id: sso.clientId
})
});
if (!tokenResponse.ok) {
const errorData = await tokenResponse.json().catch(() => ({}));
throw new Error(errorData.error_description || errorData.error || 'Token refresh failed');
}
const tokens = await tokenResponse.json();
const accessToken = tokens.access_token;
const idToken = tokens.id_token;
const refreshToken = tokens.refresh_token || state.refreshToken; // Use new refresh token if provided, otherwise keep old one
if (!accessToken || !idToken) {
throw new Error('Missing tokens in refresh response');
}
// Validate nonce if available in sessionStorage (optional for refresh flows)
const refreshNonce = sessionStorage.getItem('oauth_nonce');
if (refreshNonce) {
try {
validateNonce(idToken, refreshNonce);
} catch (nonceError) {
console.warn('Nonce validation failed during refresh:', nonceError.message);
// Don't fail the refresh flow if nonce validation fails
}
}
const message = {
type: 'oauth-success',
accessToken: accessToken,
idToken: idToken,
refreshToken: refreshToken
};
// Send message to parent window
if (window.opener) {
document.getElementById('spinner').style.display = 'none';
document.getElementById('spinner-msg').style.display = 'none';
document.getElementById('success').style.display = 'block';
document.getElementById('message-for-opener').style.display = 'block';
window.opener.postMessage(message, window.location.origin);
} else if (window.parent && window.parent !== window) {
console.log('Sending refreshed tokens to parent (iframe)');
window.parent.postMessage(message, window.location.origin);
} else {
// Standalone page - redirect to room
const tenant = state.tenant || '';
const roomSafe = state.roomSafe || state.room || '';
if (!roomSafe) {
throw new Error('No room specified in state');
}
const protocol = window.location.protocol;
const host = window.location.host;
const path = tenant ? `/${tenant}/${roomSafe}` : `/${roomSafe}`;
const hashParams = new URLSearchParams();
for (const [key, value] of Object.entries(state)) {
if (key.startsWith('config.')) {
hashParams.append(key, String(value));
}
}
hashParams.append('jwt', `"${accessToken}"`);
const redirectUrl = `${protocol}//${host}${path}#${hashParams.toString()}`;
window.location.href = redirectUrl;
}
return; // Exit after handling refresh token
}
// No refresh token in state - proceed with authorization code flow
// Check for OAuth errors
const error = urlParams.get('error');
if (error) {
const errorDescription = urlParams.get('error_description');
console.error('OAuth error:', error, errorDescription);
if (window.opener) {
window.opener.postMessage({
type: 'oauth-error',
error: errorDescription || error
}, window.location.origin);
} else if (window.parent && window.parent !== window) {
window.parent.postMessage({
type: 'oauth-error',
error: errorDescription || error
}, window.location.origin);
}
return;
}
// Get authorization code
const code = urlParams.get('code');
if (!code) {
throw new Error('No authorization code received');
}
// Retrieve code_verifier and oauth_nonce from sessionStorage (set during login initiation)
const codeVerifier = sessionStorage.getItem('code_verifier');
if (!codeVerifier) {
throw new Error('No PKCE code verifier found in session');
}
const oauthNonce = sessionStorage.getItem('oauth_nonce');
if (!oauthNonce) {
throw new Error('No OAuth nonce found in session');
}
// Show spinner while exchanging code for tokens
document.getElementById('spinner').style.display = 'block';
document.getElementById('spinner-msg').style.display = 'block';
// Exchange authorization code for tokens
const tokenResponse = await fetch(`https://${sso.tokenService}/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: window.location.origin + window.location.pathname,
client_id: sso.clientId,
code_verifier: codeVerifier
})
});
if (!tokenResponse.ok) {
const errorData = await tokenResponse.json().catch(() => ({}));
throw new Error(errorData.error_description || errorData.error || 'Token exchange failed');
}
const tokens = await tokenResponse.json();
const accessToken = tokens.access_token;
const idToken = tokens.id_token;
const refreshToken = tokens.refresh_token;
if (!accessToken || !idToken) {
throw new Error('Missing tokens in response');
}
// Validate nonce in ID token
validateNonce(idToken, oauthNonce);
const message = {
type: 'oauth-success',
accessToken: accessToken,
idToken: idToken,
refreshToken: refreshToken
};
// Send message to parent window
if (window.opener) {
// Opened via window.open() - popup
document.getElementById('spinner').style.display = 'none';
document.getElementById('spinner-msg').style.display = 'none';
document.getElementById('success').style.display = 'block';
document.getElementById('message-for-opener').style.display = 'block';
window.opener.postMessage(message, window.location.origin);
} else if (window.parent && window.parent !== window) {
// Loaded in iframe
console.log('Sending message to parent (iframe)');
window.parent.postMessage(message, window.location.origin);
} else {
// Standalone page - redirect to room
// Extract required values
const tenant = state.tenant || '';
const roomSafe = state.roomSafe || state.room || '';
if (!roomSafe) {
throw new Error('No room specified in state');
}
// Build the new URL
const protocol = window.location.protocol;
const host = window.location.host;
// Construct path: /tenant/roomSafe
const path = tenant ? `/${tenant}/${roomSafe}` : `/${roomSafe}`;
// Build hash parameters - only config.* values and jwt
const hashParams = new URLSearchParams();
// Add only config.* parameters from state
for (const [key, value] of Object.entries(state)) {
if (key.startsWith('config.')) {
hashParams.append(key, String(value));
}
}
// Add access_token as jwt with quotes
hashParams.append('jwt', `"${accessToken}"`);
// Build final URL
const redirectUrl = `${protocol}//${host}${path}#${hashParams.toString()}`;
console.log('Redirecting to:', redirectUrl);
// Redirect
window.location.href = redirectUrl;
}
} catch (error) {
console.error('SSO Error:', error);
const errorMessage = {
type: 'oauth-error',
error: error.message
};
if (window.opener) {
window.opener.postMessage(errorMessage, window.location.origin);
} else if (window.parent && window.parent !== window) {
window.parent.postMessage(errorMessage, window.location.origin);
} else {
document.getElementById('error').textContent = `Error: ${error.message}`;
document.getElementById('error').style.display = 'block';
document.getElementById('spinner').style.display = 'none';
document.getElementById('spinner-msg').style.display = 'none';
}
}
}
// Start authentication flow
handleAuthorizationCode();
})();
</script>
</body>
</html>

View File

@@ -0,0 +1,17 @@
<html>
<head>
<!--#include virtual="/base.html" -->
<link rel="stylesheet" href="css/all.css"/>
<!--#include virtual="/title.html" -->
</head>
<body>
<div class = 'unsupported-desktop-browser'>
<h2 class = 'unsupported-desktop-browser__title'>
WebRTC is not available in your browser
</h2>
<p class ='unsupported-desktop-browser__description'>
It looks like your browser does not support WebRTC. Please enable it or try another browser.
</p>
</div>
</body>
</html>

View File

@@ -0,0 +1 @@
<template id = "welcome-page-additional-card-template"></template>

View File

@@ -0,0 +1 @@
<template id = "welcome-page-additional-content-template"></template>

33
static/whiteboard.html Normal file
View File

@@ -0,0 +1,33 @@
<html xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--#include virtual="/base.html" -->
<link rel="stylesheet" href="css/all.css">
<script>
window.EXCALIDRAW_ASSET_PATH = 'libs/excalidraw/';
document.addEventListener('DOMContentLoaded', async () => {
if (!JitsiMeetJS.app) {
return;
}
JitsiMeetJS.app.renderEntryPoint({
Component: JitsiMeetJS.app.entryPoints.WHITEBOARD
});
});
</script>
<!--#include virtual="/title.html" -->
<script><!--#include virtual="/config.js" --></script>
<script><!--#include virtual="/interface_config.js" --></script>
<script src="libs/lib-jitsi-meet.min.js?v=139"></script>
<script src="libs/app.bundle.min.js?v=139"></script>
</head>
<body>
<div id="react" role="main"></div>
</body>
</html>