Commit 8fa35960 authored by Davide Gagliardi's avatar Davide Gagliardi

video-feature

parent 33212e95
......@@ -4,3 +4,5 @@ ace-builds
node_modules
API/temp/*
!API/temp/ReadMe.txt
group-call
openvidu-js-node
var OpenVidu = require('openvidu-node-client').OpenVidu;
var OpenViduRole = require('openvidu-node-client').OpenViduRole;
const express = require('express');
var bodyParser = require('body-parser');
var fs = require('fs');
var session = require('express-session');
const https = require('https')
const io = require('socket.io')(https);
const app = express();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const port = process.env.PORT || 3000;
var arr = require('./API/compilers');
......@@ -17,13 +22,31 @@ var bruteforce = new ExpressBrute(store,{
lifetime: 3600
});
app.use(session({
saveUninitialized: true,
resave: false,
secret: 'MY_SECRET'
}));
// console.log(__dirname);
app.use(express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/API'));
app.use(express.static(__dirname + '/node_modules/ace-builds'));
app.use(express.static(__dirname + '/node_modules/file-saver/dist/'));
app.use(express.static(__dirname + '/node_modules/socket.io-client/dist/'));
app.use(bodyParser.urlencoded({
'extended': 'true'
})); // Parse application/x-www-form-urlencoded
app.use(bodyParser.json());
app.use(bodyParser.json({
type: 'application/vnd.api+json'
})); // Parse application/vnd.api+json as json
var options = {
key: fs.readFileSync('keys/openvidukey.pem'),
cert: fs.readFileSync('keys/openviducert.pem')
};
app.all('*', function(req, res, next)
{
......@@ -34,6 +57,8 @@ app.all('*', function(req, res, next)
next();
});
https.createServer(options, app).listen(port, () => console.log('listening on port ' + port));
function onConnection(socket){
console.log("user connected");
socket.on('drawing', (data) => socket.broadcast.emit('drawing', data));
......@@ -64,7 +89,6 @@ app.post('/compile',bruteforce.prevent,function(req, res)
//details of this are present in DockerSandbox.js
var sandboxType = new sandBox(timeout_value,path,folder,vm_name,arr.compilerArray[language][0],arr.compilerArray[language][1],code,arr.compilerArray[language][2],arr.compilerArray[language][3],arr.compilerArray[language][4],stdin);
//data will contain the output of the compiled/interpreted code
//the result maybe normal program output, list of error messages or a Timeout error
sandboxType.run(function(data,exec_time,err)
......@@ -76,4 +100,199 @@ app.post('/compile',bruteforce.prevent,function(req, res)
});
http.listen(port, () => console.log('listening on port ' + port));
// OPENVIDU App
// Mock database
var users = [{
user: "publisher1",
pass: "pass",
role: OpenViduRole.PUBLISHER
}, {
user: "publisher2",
pass: "pass",
role: OpenViduRole.PUBLISHER
}, {
user: "subscriber",
pass: "pass",
role: OpenViduRole.SUBSCRIBER
}];
// Environment variable: URL where our OpenVidu server is listening
var OPENVIDU_URL = "https://192.168.0.96:4443";
// Environment variable: secret shared with our OpenVidu server
var OPENVIDU_SECRET = '123456';
// Entrypoint to OpenVidu Node Client SDK
var OV = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
// Collection to pair session names with OpenVidu Session objects
var mapSessions = {};
// Collection to pair session names with tokens
var mapSessionNamesTokens = {};
// Login
app.post('/api-login/login', function (req, res) {
// Retrieve params from POST body
var user = req.body.user;
var pass = req.body.pass;
console.log("Logging in | {user, pass}={" + user + ", " + pass + "}");
if (login(user, pass)) { // Correct user-pass
// Validate session and return OK
// Value stored in req.session allows us to identify the user in future requests
console.log("'" + user + "' has logged in");
req.session.loggedUser = user;
res.status(200).send();
} else { // Wrong user-pass
// Invalidate session and return error
console.log("'" + user + "' invalid credentials");
req.session.destroy();
res.status(401).send('User/Pass incorrect');
}
});
// Logout
app.post('/api-login/logout', function (req, res) {
console.log("'" + req.session.loggedUser + "' has logged out");
req.session.destroy();
res.status(200).send();
});
// Get token (add new user to session)
app.post('/api-sessions/get-token', function (req, res) {
if (!isLogged(req.session)) {
req.session.destroy();
res.status(401).send('User not logged');
} else {
// The video-call to connect
var sessionName = req.body.sessionName;
// Role associated to this user
var role = users.find(u => (u.user === req.session.loggedUser)).role;
// Optional data to be passed to other users when this user connects to the video-call
// In this case, a JSON with the value we stored in the req.session object on login
var serverData = JSON.stringify({ serverData: req.session.loggedUser });
console.log("Getting a token | {sessionName}={" + sessionName + "}");
// Build tokenOptions object with the serverData and the role
var tokenOptions = {
data: serverData,
role: role
};
if (mapSessions[sessionName]) {
// Session already exists
console.log('Existing session ' + sessionName);
// Get the existing Session from the collection
var mySession = mapSessions[sessionName];
// Generate a new token asynchronously with the recently created tokenOptions
mySession.generateToken(tokenOptions)
.then(token => {
// Store the new token in the collection of tokens
mapSessionNamesTokens[sessionName].push(token);
// Return the token to the client
res.status(200).send({
0: token
});
})
.catch(error => {
console.error(error);
});
} else {
// New session
console.log('New session ' + sessionName);
// Create a new OpenVidu Session asynchronously
OV.createSession()
.then(session => {
// Store the new Session in the collection of Sessions
mapSessions[sessionName] = session;
// Store a new empty array in the collection of tokens
mapSessionNamesTokens[sessionName] = [];
// Generate a new token asynchronously with the recently created tokenOptions
session.generateToken(tokenOptions)
.then(token => {
// Store the new token in the collection of tokens
mapSessionNamesTokens[sessionName].push(token);
// Return the Token to the client
res.status(200).send({
0: token
});
})
.catch(error => {
console.error(error);
});
})
.catch(error => {
console.error(error);
});
}
}
});
// Remove user from session
app.post('/api-sessions/remove-user', function (req, res) {
if (!isLogged(req.session)) {
req.session.destroy();
res.status(401).send('User not logged');
} else {
// Retrieve params from POST body
var sessionName = req.body.sessionName;
var token = req.body.token;
console.log('Removing user | {sessionName, token}={' + sessionName + ', ' + token + '}');
// If the session exists
if (mapSessions[sessionName] && mapSessionNamesTokens[sessionName]) {
var tokens = mapSessionNamesTokens[sessionName];
var index = tokens.indexOf(token);
// If the token exists
if (index !== -1) {
// Token removed
tokens.splice(index, 1);
console.log(sessionName + ': ' + tokens.toString());
} else {
var msg = 'Problems in the app server: the TOKEN wasn\'t valid';
console.log(msg);
res.status(500).send(msg);
}
if (tokens.length == 0) {
// Last user left: session must be removed
console.log(sessionName + ' empty!');
delete mapSessions[sessionName];
}
res.status(200).send();
} else {
var msg = 'Problems in the app server: the SESSION does not exist';
console.log(msg);
res.status(500).send(msg);
}
}
});
/* AUXILIARY METHODS */
function login(user, pass) {
return (users.find(u => (u.user === user) && (u.pass === pass)));
}
function isLogged(session) {
return (session.loggedUser != null);
}
function getBasicAuth() {
return 'Basic ' + (new Buffer('OPENVIDUAPP:' + OPENVIDU_SECRET).toString('base64'));
}
-----BEGIN CERTIFICATE-----
MIIDgzCCAmugAwIBAgIJALDBPXTrlAhlMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV
BAYTAkVTMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQKDAhPcGVuVmlkdTEh
MB8GCSqGSIb3DQEJARYSb3BlbnZpZHVAZ21haWwuY29tMB4XDTE3MDUzMDE1MjQx
N1oXDTI3MDUyODE1MjQxN1owWDELMAkGA1UEBhMCRVMxEzARBgNVBAgMClNvbWUt
U3RhdGUxETAPBgNVBAoMCE9wZW5WaWR1MSEwHwYJKoZIhvcNAQkBFhJvcGVudmlk
dUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDbK/i
LN80IBnGUbzA4AFl4KQEX6RCreythnfOSnIcQSTP+KjesZMFgsGV0LteDxeCJ+kq
4YoS+CW7ojvOEz3xjCgo4tFdevz8ZoeO0RBQIbARbPako4OXC6vWs6LHwCR0aDWo
9HfS1Uusb8g77csRPRlNA3DGR8dcRTiEBdfHS6Jh/7V7XiDlaxPXj+iIY8PyCqOf
gv4clDt17R+dendDsgYxbmZaodrppNocMQIyUaDIwI4DZOa8nQYk9uuUhkiAFAQB
O642bx6NI0qDu5KgtMmbaS6s+rMnrL8eeZOicqff6XoC6tk6GE8Fo4iYkxp2gAiT
sigaSwpNRWhupISVAgMBAAGjUDBOMB0GA1UdDgQWBBTi3sXqpf42vUNehFU6y4Pn
PTJFRDAfBgNVHSMEGDAWgBTi3sXqpf42vUNehFU6y4PnPTJFRDAMBgNVHRMEBTAD
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBfl0C7XAbPdJzCjCsboMTzfC2B4uwspbST
MbUGnmNkTwzeOOsrLQmlCznKGprYV9vGR0MwBzYw2swWCzg/2MLN8swW7NF9gMkK
K61AANxT0qZV/aZhdM7W//pHJoQYsBsQT7cuTrL/VB/niD81uMA/mSWyXIn8KDIy
CPY6jiZ4qiIJnQWhWm1Cazv6O7wjDisvB1cCJhDvBv42KkFwtigt5MQnBEGOI2LD
iKCkXfj33E5B6n0sEel68WgYi6rx2tsR9lzAjCRF+jgNd7FfeUi999m7ykgEACR5
OYRqkVcIXz30r9RxQyFqZLNzyO9oaVZpex8ZbWwEzNXG2ccddnMJ
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAw2yv4izfNCAZxlG8wOABZeCkBF+kQq3srYZ3zkpyHEEkz/io
3rGTBYLBldC7Xg8XgifpKuGKEvglu6I7zhM98YwoKOLRXXr8/GaHjtEQUCGwEWz2
pKODlwur1rOix8AkdGg1qPR30tVLrG/IO+3LET0ZTQNwxkfHXEU4hAXXx0uiYf+1
e14g5WsT14/oiGPD8gqjn4L+HJQ7de0fnXp3Q7IGMW5mWqHa6aTaHDECMlGgyMCO
A2TmvJ0GJPbrlIZIgBQEATuuNm8ejSNKg7uSoLTJm2kurPqzJ6y/HnmTonKn3+l6
AurZOhhPBaOImJMadoAIk7IoGksKTUVobqSElQIDAQABAoIBAGr/2HFjFjbpGJOw
b0O/oqRQUh2e7EYiCoOcK37E3iPAO1KvmG6OFayfwjSwG9bNNpbqGU2EPeBTA/3v
PwV/HZxinB5+yhl/3IKp9LDqoR7uwwNXgNf2O3d5SXX91zO9bXhbEn5WlEDYzl00
uxKtCVF//ZlgN+AoruxDbkVDGbkhKHGzvqOW+BWwbYHPOttQ9TQx2ss5+DekpDFV
/FuvXGOcSSV/N+WbDwsqUiM8ovkcflEgQjZYlAY4Ro2U2buf3fKEmy1jxSznNp30
QA4WyYypyS8Hz6S/F4nsjtS3ufCurrWBv4vt2qB+8tH//07NUacjQWZdvEAtaJ+G
IyJgsOUCgYEA38b2SQiw74fwoK6so2XdpCzOObLQBOT1aeZQjt3v8XZ0MPzwWJZm
VkMBmolcEw24xA3jHhOQYafAGjRdaKxlRbJyGJqwENfAs7hO5JSLLWWXsorJQcb7
1OVTFPt7NZopx2Kw7kGGuj9884w57cSl9lsuMptqk8W7pbTo5opOfl8CgYEA35CR
x23kaFDh8+zKUuSdIrkUJ66cazgOfF0FYKiDewd1sGEdZT+gq7Wo8Hi8cp9q+Kuq
MkGrTzu21yMi2fo7RKoSOtV4RQViu6PuoYR/KvRq4F4abHEAivAG4xhPQife84IB
NkDQXaE9EXTP7DXPwj4HV8CnC9LC2qPFU7GVeYsCgYBWPK6c5qSJKrIouif9sDwC
EOJIighwWmvZK9DPvefB/gw49MEK4qr9g0US8Oxyy07w/wkPhiqV97eoYZW9yPIe
Me6WXMaNNxgkKlr86+HW1NfpDmMQ3kYefWHPLDsHJSoElJvqtYXeMKlOkjOg1a+/
iNP83Lftyr3N1jIK5jHpsQKBgE1+clm7qOnT547C7JrxLdrEZs0ehI/R3YuUPvHz
V6gEvPHHqAXZmVsL3CSG5WOiCNVrw9Ip2zTa0RUf08vVJkg1353PMyJRrJi4SVZp
dB8ym/1sASLHxNVkQC7l1UtsQKcN0Fe6/b8GzgFICW6qdHqzP55WZFD/3JUnIZZS
PyrjAoGBAMfuE0Nrw9Fq7f8+U/SsqiW9djJp4R34EF5n8qHkotfRoAbGktCGxLoF
QMbB9X0ibRMpxEKPHj+V0FXu9zUKFuqoriRc3alQYLNzJEdKeQQ/AN5xjG3ilM6D
/FunImbvSdJLjR9Ue6vlFdOquHevBCFDiHOJPQJ/y4qyZR5Avluw
-----END RSA PRIVATE KEY-----
......@@ -38,6 +38,15 @@
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
"axios": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
"integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
"requires": {
"follow-redirects": "1.5.10",
"is-buffer": "^2.0.2"
}
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
......@@ -48,6 +57,11 @@
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
},
"base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
},
"base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
......@@ -83,6 +97,15 @@
"type-is": "~1.6.17"
}
},
"buffer": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz",
"integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==",
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
......@@ -309,6 +332,33 @@
"underscore": "~1.8.3"
}
},
"express-session": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.16.1.tgz",
"integrity": "sha512-pWvUL8Tl5jUy1MLH7DhgUlpoKeVPUTe+y6WQD9YhcN0C5qAhsh4a8feVjiUXo3TFhIy191YGZ4tewW9edbl2xQ==",
"requires": {
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.2",
"safe-buffer": "5.1.2",
"uid-safe": "~2.1.5"
},
"dependencies": {
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
}
}
},
"file-saver": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz",
......@@ -328,6 +378,24 @@
"unpipe": "~1.0.0"
}
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"requires": {
"debug": "=3.1.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
}
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
......@@ -371,6 +439,11 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ieee754": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
},
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
......@@ -386,6 +459,11 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"is-buffer": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
"integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
},
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
......@@ -452,6 +530,20 @@
"ee-first": "1.1.1"
}
},
"on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
},
"openvidu-node-client": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/openvidu-node-client/-/openvidu-node-client-2.12.0.tgz",
"integrity": "sha512-/rXlcXz8L3aakE781/PMACqA6BGaGybyqkhEGNe4qRk9m8/cYgpwOosyPX9Um6w+Wr5YbivKKpkSk4ucaEs2OQ==",
"requires": {
"axios": "0.19.0",
"buffer": "5.4.3"
}
},
"parseqs": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
......@@ -492,6 +584,11 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
......@@ -709,6 +806,14 @@
"mime-types": "~2.1.24"
}
},
"uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"requires": {
"random-bytes": "~1.0.0"
}
},
"underscore": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
......
......@@ -24,8 +24,10 @@
"exports": "*",
"express": "^4.17.1",
"express-brute": "*",
"express-session": "1.16.1",
"file-saver": "^2.0.2",
"socket.io": "latest"
"openvidu-node-client": "2.12.0",
"socket.io": "^2.3.0"
},
"devDependencies": {}
}
......@@ -9,75 +9,146 @@
<link rel="stylesheet" href="css/e_style.css">
</head>
<body>
<div class="uk-container uk-container-expand">
<nav class="uk-navbar" uk-navbar>
<div class="uk-navbar-left">
<h1 class="uk-navbar-item uk-navbar-logo uk-text-bold uk-text-large">Musikinformatik</h1>
</div>
<div class="uk-navbar-item uk-navbar-right">
<a href="" uk-icon="icon: sign-out; ratio: 1.3"></a>
</div>
</nav>
<div id="not-logged">
<form class="form-group jumbotron" onsubmit="return false">
<p>
<label>User</label><input class="form-control" type="text" id="user" required>
</p>
<p>
<label>Pass</label><input class="form-control" type="password" id="pass" required>
</p>
<p class="text-center">
<button class="btn btn-lg btn-info" onclick="logIn()">Log in</button>
</p>
</form>
<table class="table">
<tr>
<th>User</th>
<th>Pass</th>
<th>Role<i data-toggle="tooltip" data-placement="bottom" title="" data-original-title="<div id='tooltip-div'>PUBLISHER<div>Send and receive media<hr></div>SUBSCRIBER<div>Receive media</div></div>"</i></th>
</tr>
<tr>
<td>publisher1</td>
<td>pass</td>
<td>PUBLISHER</td>
</tr>
<tr>
<td>publisher2</td>
<td>pass</td>
<td>PUBLISHER</td>
</tr>
<tr>
<td>subscriber</td>
<td>pass</td>
<td>SUBSCRIBER</td>
</tr>
</table>
</div>
<div class="sidebar uk-box uk-box-shadow-large uk-margin-auto uk-flex uk-flex-column uk-flex-between">
<div class="uk-margin-auto uk-padding-small" style="padding-top: 40px">
<ul class="uk-align-center uk-list uk-tab-left uk-list-large" uk-tab uk-switcher="connect: #main-content; animation: uk-animation-fade">
<li class="uk-margin-medium-bottom"><a uk-icon="icon: video-camera; ratio: 2" uk-tooltip="title: video; pos: right"></a></li>
<li class="uk-margin-medium-bottom"><a uk-icon="icon: pencil; ratio: 2" uk-tooltip="title: whiteboard; pos: right"></a></li>
<li class="uk-margin-medium-bottom"><a uk-icon="icon: laptop; ratio: 2" uk-tooltip="title: editor | interpreter | compiler; pos: right"></a></li>
<li class="uk-margin-medium-bottom"><a uk-icon="icon: upload; ratio: 2" uk-tooltip="title: upload; pos: right"></a></li>
<li><a href="#">chat</a></li>
</ul>
<div id="logged" style="display:none">
<div class="uk-container uk-container-expand">
<nav class="uk-navbar" uk-navbar>
<div class="uk-navbar-left">
<h1 class="uk-navbar-item uk-navbar-logo uk-text-bold uk-text-large">Musikinformatik</h1>
</div>
<div class="uk-navbar-item uk-navbar-right">
<button onclick="logOut()" uk-icon="icon: sign-out; ratio: 1.3"></button>
</div>
</nav>
</div>
<div class="uk-margin-auto">
<ul class="uk-align-center uk-list ">
<li><a href="#" target="_blank" uk-icon="icon: git-branch; ratio: 2"></a></li>
</ul>
<div class="sidebar uk-box uk-box-shadow-large uk-margin-auto uk-flex uk-flex-column uk-flex-between">
<div class="uk-margin-auto uk-padding-small" style="padding-top: 40px">
<ul class="uk-align-center uk-list uk-tab-left uk-list-large" uk-tab uk-switcher="connect: #main-content; animation: uk-animation-fade">
<li class="uk-margin-medium-bottom"><a uk-icon="icon: video-camera; ratio: 2" uk-tooltip="title: video; pos: right"></a></li>
<li class="uk-margin-medium-bottom"><a uk-icon="icon: pencil; ratio: 2" uk-tooltip="title: whiteboard; pos: right"></a></li>
<li class="uk-margin-medium-bottom"><a uk-icon="icon: laptop; ratio: 2" uk-tooltip="title: editor | interpreter | compiler; pos: right"></a></li>
<li class="uk-margin-medium-bottom"><a uk-icon="icon: upload; ratio: 2" uk-tooltip="title: upload; pos: right"></a></li>
<li><a href="#">chat</a></li>
</ul>
</div>
<div class="uk-margin-auto">
<ul class="uk-align-center uk-list ">
<li><a href="#" target="_blank" uk-icon="icon: git-branch; ratio: 2"></a></li>
</ul>
</div>
</div>
</div>
<div class="main-view uk-box-shadow-xlarge">
<ul class="uk-switcher window" id="main-content">
<!-- VIDEO -->
<li class="window-content">
<div>
<h1>VIDEO</h1>
</div>
</li>
<!-- BOARD -->
<li class="window-content">
<div style="position:relative">
<h1 id="whiteboard-head">board</h1>
<div class="layer1">
<canvas class="whiteboard"></canvas>
<div class="colors ">
<button id="color-menu" uk-icon="icon: settings; ratio: 2" type="button"></button>
<div class="palette" uk-dropdown="pos: top-center">
<div class="color white"></div>
<div class="color red"></div>
<div class="color green"></div>
<div class="color blue"></div>
<div class="color yellow"></div>