This commit is contained in:
hshub
2024-04-16 22:26:30 +00:00
commit fce566a8b9
94 changed files with 52437 additions and 0 deletions

123
assets/css/dish.css Executable file
View File

@@ -0,0 +1,123 @@
.Scenary {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
flex-direction: column;
padding: 20px;
gap: 20px;
}
/* Container of Screen and Dish */
.Conference {
display: flex;
flex: 1;
border-radius: 10px;
gap: 20px;
max-height: 100%;
max-width: 100%;
}
/* Container of Cameras */
.Dish {
overflow: scroll;
display: flex;
align-content: center;
flex-wrap: wrap;
align-items: center;
justify-content: center;
vertical-align: middle;
flex: 1;
border-radius: 10px;
background: rgba(0, 0, 0, 0.3);
}
/* Camera */
.Dish>div {
position: relative;
vertical-align: middle;
align-self: center;
border-radius: 10px;
overflow: hidden;
display: inline-block;
box-shadow: var(--shadow-dark);
background: #fff;
animation: show 0.4s ease;
}
/* Video (check the nice property object-fit) */
.Dish>div video {
position: absolute;
right: 0;
object-fit: cover;
bottom: 0;
width: 100%;
height: 100%;
background: #000;
border-radius: 10px;
overflow: hidden;
left: 0;
top: 0;
background-size: cover;
overflow: hidden;
-webkit-transition: margin-top 1s ease-in-out;
-moz-transition: margin-top 1s ease-in-out;
-o-transition: margin-top 1s ease-in-out;
transition: margin-top 1s ease-in-out;
}
/* Animation of Loading Video */
.Dish>div video.loading {
margin-top: 100%;
}
/* Aspect Ratio Number */
.Dish div:after {
color: #aaa;
font-size: 13px;
font-family: Arial, Helvetica, sans-serif;
position: absolute;
bottom: 20px;
left: 23px;
font-weight: 100;
content: attr(data-aspect);
display: block;
}
/* Gray Diagonal */
.Dish div:before {
position: absolute;
height: 100%;
background: url(./../general/diagonal.jpg);
background-size: 100% 100%;
width: 100%;
opacity: 0.3;
font-weight: 100;
content: '';
display: block;
}
/* Screen */
.Screen {
flex: 2;
background: #000;
opacity: 0.8;
border-radius: 10px;
}
/* Animation of Cameras */
@keyframes show {
0% {
opacity: 0;
transform: scale(0.4) translateY(20px);
}
100% {
opacity: 1;
transform: scale(1) translateY(0);
}
}

123
assets/css/hljs-varo.css Executable file
View File

@@ -0,0 +1,123 @@
/*!
Theme: Default
Description: Original highlight.js style
Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
Maintainer: @highlightjs/core-team
Website: https://highlightjs.org/
License: see project LICENSE
Touched: 2021
*/
/*
This is left on purpose making default.css the single file that can be lifted
as-is from the repository directly without the need for a build step
Typically this "required" baseline CSS is added by `makestuff.js` during build.
*/
pre code.hljs {
display: block;
}
/* end baseline CSS */
/* Base color: saturation 0; */
.hljs-subst {
/* default */
}
/* purposely ignored */
.hljs-formula,
.hljs-attr,
.hljs-property,
.hljs-params {}
.hljs-comment {
color: #7a7a7a;
}
.hljs-tag,
.hljs-punctuation {
color: #7a7a7a;
}
.hljs-tag .hljs-name,
.hljs-tag .hljs-attr {
color: #fff;
}
.hljs-keyword,
.hljs-attribute,
.hljs-selector-tag,
.hljs-meta .hljs-keyword,
.hljs-doctag,
.hljs-name {
font-weight: bold;
}
/* User color: hue: 0 */
.hljs-type,
.hljs-string,
.hljs-number,
.hljs-selector-id,
.hljs-selector-class,
.hljs-quote,
.hljs-template-tag,
.hljs-deletion {
color: #409aed;
}
.hljs-title,
.hljs-section {
color: #409aed;
font-weight: bold;
}
.hljs-regexp,
.hljs-symbol,
.hljs-variable,
.hljs-template-variable,
.hljs-link,
.hljs-selector-attr,
.hljs-operator,
.hljs-selector-pseudo {
color: #40ed50;
}
/* Language color: hue: 90; */
.hljs-literal {
color: #db4437;
}
.hljs-built_in,
.hljs-bullet,
.hljs-code,
.hljs-addition {
color: #397300;
}
/* Meta color: hue: 200 */
.hljs-meta {
color: #7a7a7a;
}
.hljs-meta .hljs-string {
color: #38a;
}
/* Misc effects */
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

9
assets/css/hljs.css Executable file
View File

@@ -0,0 +1,9 @@
/*!
Theme: Default
Description: Original highlight.js style
Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
Maintainer: @highlightjs/core-team
Website: https://highlightjs.org/
License: see project LICENSE
Touched: 2021
*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#f3f3f3;color:#444}.hljs-comment{color:#697070}.hljs-punctuation,.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#ab5656}.hljs-literal{color:#695}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#38a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}

3036
assets/css/style.css Executable file

File diff suppressed because it is too large Load Diff

BIN
assets/img/cover.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

25
assets/img/handshake.svg Executable file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 2000 2088.9" style="enable-background:new 0 0 2000 2088.9;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;}
</style>
<path class="st0" d="M1726.6,681.4l-129.8-230.6l251.5,0.1c6.7,0,14.6,4.5,18.2,10.5c4,6.6,24.4,40.3,49,80.7
c29.4,48.4,64.5,106.3,84.6,139.3H1726.6z M1261.4,2078c-6.1,10.9-14,10.9-16.6,10.9h-102.8c-54.4,0-117.1-0.1-154.5-0.1
l399.6-717.4c10.4-18.5,3.7-42-14.8-52.3c-5.7-3.2-12.1-4.9-18.6-4.9l0,0l-681,0.9l-135.5-234.5h992c0.1,0,0.2,0,0.3,0s0.2,0,0.3,0
c0.6,0,1.1-0.2,1.6-0.2c2.2-0.1,4.4-0.4,6.6-0.9c1.6-0.4,3.3-0.9,4.8-1.5c0.8-0.3,1.7-0.7,2.5-1c7.7-3.3,14.1-9.1,18.2-16.5
L1727,758.2h270L1261.4,2078z M921.1,2050.7c-8.7-14.3-20-32.9-32.3-53.1c-41.3-68.2-94.2-155.5-100.2-165.1
c-2-3.2-2.9-11.1,1.3-18.7c9.6-17.2,190.4-343,234.5-422.4l264.1-0.3L921.1,2050.7z M470.8,1601.8l-131.3-233.2l132.3-248
l132.7,229.7C563.3,1428,498.9,1549.1,470.8,1601.8z M294.6,1638.1c-66.8,0-133.3,0-143,0l0,0c-6.5,0-14.4-4.6-18-10.5l-42.7-70.4
C60.4,1507,21.5,1442.9,0,1407.5h273.4l129.8,230.6C374.9,1638.1,334.8,1638.1,294.6,1638.1L294.6,1638.1z M738.6,11
c6.1-11,14-11,16.5-11h258.3L612.7,717.4c-0.3,0.6-0.5,1.2-0.8,1.9c-0.6,1.2-1.1,2.4-1.6,3.7c-0.4,1.2-0.8,2.4-1.1,3.7
s-0.6,2.3-0.8,3.5c-0.2,1.4-0.4,2.8-0.4,4.3c0,0.6-0.2,1.2-0.2,1.9c0,0.5,0.1,0.9,0.2,1.4c0,1.4,0.2,2.8,0.4,4.2
c0.1,1.2,0.3,2.3,0.6,3.5c0.3,1.2,0.7,2.4,1.1,3.6c0.4,1.2,0.8,2.3,1.3,3.5s1.1,2.2,1.7,3.2s1.2,2.1,2,3.1c0.7,1,1.5,2,2.4,2.9
c0.8,0.9,1.6,1.8,2.5,2.7s1.8,1.5,2.8,2.3c1.1,0.8,2.2,1.6,3.3,2.3c0.4,0.3,0.8,0.6,1.2,0.9s0.9,0.3,1.4,0.6c2,1,4.1,1.9,6.2,2.5
c0.8,0.2,1.5,0.5,2.3,0.7c2.8,0.7,5.7,1.1,8.7,1.1c0,0,0,0,0.1,0h0.1h17c0.1,0,0.2,0,0.2,0l0,0l663.9-0.9
c17.6,30.5,50.4,88,78.3,136.9c21.4,37.6,39.2,68.6,53.2,93h-988c-0.4,0-0.8,0.1-1.3,0.2c-13.9,0.1-26.6,7.9-33.2,20.1l-163.6,306.6
H3.1C120.8,1119.3,730.7,25,738.6,11z M1079.4,39.1l24,39.5c42.1,69.5,101.6,167.6,107.9,177.8c2,3.2,2.9,11.1-1.3,18.7L975.6,697.3
l-264,0.3L1079.4,39.1z M1529.1,486.9l131.3,233.3l-133.9,247.5c-16-27.8-35.8-62.6-54.4-95.2c-36.8-64.4-63.6-111.3-78.7-137.5
C1422.1,682.8,1497.5,544.5,1529.1,486.9z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
assets/img/icann.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
assets/img/icon-128x128.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
assets/img/icon-144x144.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
assets/img/icon-152x152.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
assets/img/icon-192x192.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
assets/img/icon-384x384.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
assets/img/icon-48x48.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
assets/img/icon-512x512.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
assets/img/icon-72x72.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
assets/img/icon-96x96.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
assets/img/icons/arrow.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
assets/img/icons/audio.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

BIN
assets/img/icons/check.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

BIN
assets/img/icons/clipboard.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

BIN
assets/img/icons/close.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

BIN
assets/img/icons/compose.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 919 B

BIN
assets/img/icons/edit.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
assets/img/icons/emoji.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
assets/img/icons/fail.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

BIN
assets/img/icons/fish.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/img/icons/gif.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/img/icons/leave.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
assets/img/icons/lock.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

BIN
assets/img/icons/mention.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
assets/img/icons/menu.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

BIN
assets/img/icons/message.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
assets/img/icons/pay.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
assets/img/icons/pin.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
assets/img/icons/plus.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
assets/img/icons/replay.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
assets/img/icons/save.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 B

BIN
assets/img/icons/screen.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
assets/img/icons/search.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
assets/img/icons/signature.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
assets/img/icons/trash.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
assets/img/icons/update.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
assets/img/icons/users.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
assets/img/icons/video.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
assets/img/icons/view.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
assets/img/icons/voice.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
assets/img/icons/warning.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
assets/img/logo.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

5
assets/img/unstoppable.svg Executable file
View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0.3 0.234 43.492 39" width="43.492" height="39" xmlns="http://www.w3.org/2000/svg">
<path d="M 43.792 0.876 L 43.792 15.682 L 0.3 33.177 L 43.792 0.876 Z" fill="#00C9FF"/>
<path d="M 35.639 0.234 L 35.639 25.775 C 35.639 29.345 34.207 32.768 31.658 35.292 C 29.109 37.816 25.652 39.234 22.047 39.234 C 18.442 39.234 14.985 37.816 12.436 35.292 C 9.887 32.768 8.455 29.345 8.455 25.775 L 8.455 15.013 L 16.609 10.569 L 16.609 25.775 C 16.532 26.439 16.598 27.111 16.803 27.748 C 17.007 28.384 17.345 28.971 17.794 29.469 C 18.243 29.967 18.793 30.366 19.409 30.639 C 20.025 30.912 20.692 31.053 21.366 31.053 C 22.041 31.053 22.708 30.912 23.323 30.639 C 23.939 30.366 24.489 29.967 24.938 29.469 C 25.388 28.971 25.725 28.384 25.93 27.748 C 26.134 27.111 26.2 26.439 26.124 25.775 L 26.124 5.388 L 35.639 0.234 Z" fill="#0D67FE"/>
</svg>

After

Width:  |  Height:  |  Size: 894 B

1
assets/js/adapter.js Executable file

File diff suppressed because one or more lines are too long

1
assets/js/anchorme.js Executable file

File diff suppressed because one or more lines are too long

130
assets/js/confetti.js Executable file
View File

@@ -0,0 +1,130 @@
var maxParticleCount = 150; //set max confetti count
var particleSpeed = 2; //set the particle animation speed
var startConfetti; //call to start confetti animation
var stopConfetti; //call to stop adding confetti
var toggleConfetti; //call to start or stop the confetti animation depending on whether it's already running
var removeConfetti; //call to stop the confetti animation and remove all confetti immediately
(function() {
startConfetti = startConfettiInner;
stopConfetti = stopConfettiInner;
toggleConfetti = toggleConfettiInner;
removeConfetti = removeConfettiInner;
var colors = ["DodgerBlue", "OliveDrab", "Gold", "Pink", "SlateBlue", "LightBlue", "Violet", "PaleGreen", "SteelBlue", "SandyBrown", "Chocolate", "Crimson"]
var streamingConfetti = false;
var animationTimer = null;
var particles = [];
var waveAngle = 0;
function resetParticle(particle, width, height) {
particle.color = colors[(Math.random() * colors.length) | 0];
particle.x = Math.random() * width;
particle.y = Math.random() * height - height;
particle.diameter = Math.random() * 10 + 5;
particle.tilt = Math.random() * 10 - 10;
particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
particle.tiltAngle = 0;
return particle;
}
function startConfettiInner() {
var width = window.innerWidth;
var height = window.innerHeight;
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, 16.6666667);
};
})();
var canvas = document.getElementById("confetti-canvas");
if (canvas === null) {
canvas = document.createElement("canvas");
canvas.setAttribute("id", "confetti-canvas");
canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none");
document.body.appendChild(canvas);
canvas.width = width;
canvas.height = height;
window.addEventListener("resize", function() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}, true);
}
var context = canvas.getContext("2d");
while (particles.length < maxParticleCount)
particles.push(resetParticle({}, width, height));
streamingConfetti = true;
if (animationTimer === null) {
(function runAnimation() {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
if (particles.length === 0)
animationTimer = null;
else {
updateParticles();
drawParticles(context);
animationTimer = requestAnimFrame(runAnimation);
}
})();
}
}
function stopConfettiInner() {
streamingConfetti = false;
}
function removeConfettiInner() {
stopConfetti();
particles = [];
}
function toggleConfettiInner() {
if (streamingConfetti)
stopConfettiInner();
else
startConfettiInner();
}
function drawParticles(context) {
var particle;
var x;
for (var i = 0; i < particles.length; i++) {
particle = particles[i];
context.beginPath();
context.lineWidth = particle.diameter;
context.strokeStyle = particle.color;
x = particle.x + particle.tilt;
context.moveTo(x + particle.diameter / 2, particle.y);
context.lineTo(x, particle.y + particle.tilt + particle.diameter / 2);
context.stroke();
}
}
function updateParticles() {
var width = window.innerWidth;
var height = window.innerHeight;
var particle;
waveAngle += 0.01;
for (var i = 0; i < particles.length; i++) {
particle = particles[i];
if (!streamingConfetti && particle.y < -15)
particle.y = height + 100;
else {
particle.tiltAngle += particle.tiltAngleIncrement;
particle.x += Math.sin(waveAngle);
particle.y += (Math.cos(waveAngle) + particle.diameter + particleSpeed) * 0.5;
particle.tilt = Math.sin(particle.tiltAngle) * 15;
}
if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {
if (streamingConfetti && particles.length <= maxParticleCount)
resetParticle(particle, width, height);
else {
particles.splice(i, 1);
i--;
}
}
}
}
})();

94
assets/js/date.js Executable file
View File

@@ -0,0 +1,94 @@
/* eslint no-extend-native: 0 */
(function () {
// Defining locale
Date.shortMonths = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
Date.longMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
Date.shortDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
Date.longDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
// Defining patterns
var replaceChars = {
// Day
d: function () { var d = this.getDate(); return (d < 10 ? '0' : '') + d },
D: function () { return Date.shortDays[this.getDay()] },
j: function () { return this.getDate() },
l: function () { return Date.longDays[this.getDay()] },
N: function () { var N = this.getDay(); return (N === 0 ? 7 : N) },
S: function () { var S = this.getDate(); return (S % 10 === 1 && S !== 11 ? 'st' : (S % 10 === 2 && S !== 12 ? 'nd' : (S % 10 === 3 && S !== 13 ? 'rd' : 'th'))) },
w: function () { return this.getDay() },
z: function () { var d = new Date(this.getFullYear(), 0, 1); return Math.ceil((this - d) / 86400000) },
// Week
W: function () {
var target = new Date(this.valueOf())
var dayNr = (this.getDay() + 6) % 7
target.setDate(target.getDate() - dayNr + 3)
var firstThursday = target.valueOf()
target.setMonth(0, 1)
if (target.getDay() !== 4) {
target.setMonth(0, 1 + ((4 - target.getDay()) + 7) % 7)
}
var retVal = 1 + Math.ceil((firstThursday - target) / 604800000)
return (retVal < 10 ? '0' + retVal : retVal)
},
// Month
F: function () { return Date.longMonths[this.getMonth()] },
m: function () { var m = this.getMonth(); return (m < 9 ? '0' : '') + (m + 1) },
M: function () { return Date.shortMonths[this.getMonth()] },
n: function () { return this.getMonth() + 1 },
t: function () {
var year = this.getFullYear()
var nextMonth = this.getMonth() + 1
if (nextMonth === 12) {
year = year++
nextMonth = 0
}
return new Date(year, nextMonth, 0).getDate()
},
// Year
L: function () { var L = this.getFullYear(); return (L % 400 === 0 || (L % 100 !== 0 && L % 4 === 0)) },
o: function () { var d = new Date(this.valueOf()); d.setDate(d.getDate() - ((this.getDay() + 6) % 7) + 3); return d.getFullYear() },
Y: function () { return this.getFullYear() },
y: function () { return ('' + this.getFullYear()).substr(2) },
// Time
a: function () { return this.getHours() < 12 ? 'am' : 'pm' },
A: function () { return this.getHours() < 12 ? 'AM' : 'PM' },
B: function () { return Math.floor((((this.getUTCHours() + 1) % 24) + this.getUTCMinutes() / 60 + this.getUTCSeconds() / 3600) * 1000 / 24) },
g: function () { return this.getHours() % 12 || 12 },
G: function () { return this.getHours() },
h: function () { var h = this.getHours(); return ((h % 12 || 12) < 10 ? '0' : '') + (h % 12 || 12) },
H: function () { var H = this.getHours(); return (H < 10 ? '0' : '') + H },
i: function () { var i = this.getMinutes(); return (i < 10 ? '0' : '') + i },
s: function () { var s = this.getSeconds(); return (s < 10 ? '0' : '') + s },
v: function () { var v = this.getMilliseconds(); return (v < 10 ? '00' : (v < 100 ? '0' : '')) + v },
// Timezone
e: function () { return Intl.DateTimeFormat().resolvedOptions().timeZone },
I: function () {
var DST = null
for (var i = 0; i < 12; ++i) {
var d = new Date(this.getFullYear(), i, 1)
var offset = d.getTimezoneOffset()
if (DST === null) DST = offset
else if (offset < DST) { DST = offset; break } else if (offset > DST) break
}
return (this.getTimezoneOffset() === DST) | 0
},
O: function () { var O = this.getTimezoneOffset(); return (-O < 0 ? '-' : '+') + (Math.abs(O / 60) < 10 ? '0' : '') + Math.floor(Math.abs(O / 60)) + (Math.abs(O % 60) === 0 ? '00' : ((Math.abs(O % 60) < 10 ? '0' : '')) + (Math.abs(O % 60))) },
P: function () { var P = this.getTimezoneOffset(); return (-P < 0 ? '-' : '+') + (Math.abs(P / 60) < 10 ? '0' : '') + Math.floor(Math.abs(P / 60)) + ':' + (Math.abs(P % 60) === 0 ? '00' : ((Math.abs(P % 60) < 10 ? '0' : '')) + (Math.abs(P % 60))) },
T: function () { var tz = this.toLocaleTimeString(navigator.language, {timeZoneName: 'short'}).split(' '); return tz[tz.length - 1] },
Z: function () { return -this.getTimezoneOffset() * 60 },
// Full Date/Time
c: function () { return this.format('Y-m-d\\TH:i:sP') },
r: function () { return this.toString() },
U: function () { return Math.floor(this.getTime() / 1000) }
}
// Simulates PHP's date function
Date.prototype.format = function (format) {
var date = this
return format.replace(/(\\?)(.)/g, function (_, esc, chr) {
return (esc === '' && replaceChars[chr]) ? replaceChars[chr].call(date) : chr
})
}
}).call(this);

275
assets/js/dish.js Executable file
View File

@@ -0,0 +1,275 @@
class Dish {
// ratios
_ratios = ['4:3', '16:9', '1:1', '1:2']
// default options
_dish = false
_conference = false
_cameras = 0
_margin = 5
_aspect = 2
_video = false;
_ratio = this.ratio() // to perfomance call here
// create dish
constructor(scenary) {
// parent space to render dish
this._scenary = scenary
// create the conference and dish
this.create()
// render cameras
this.render()
return this;
}
// create Dish
create() {
// create conference (dish and screen container)
this._conference = document.createElement('div');
this._conference.classList.add('videoHolder');
// create dish (cameras container)
this._dish = document.createElement('div');
this._dish.classList.add('cams');
let screen = document.createElement('div');
screen.classList.add('screen');
let info = document.createElement('div');
info.classList.add("info");
screen.append(info);
let table = document.createElement('table');
info.append(table);
let video = document.createElement('video');
video.setAttribute("autoplay", "");
video.setAttribute("playsinline", "");
video.muted = true;
screen.append(video);
// append first to scenary
this._conference.prepend(screen);
//this.expand();
// append dish to conference
this._conference.appendChild(this._dish);
}
// set dish in scenary
append() {
// append to scenary
this._scenary.appendChild(this._conference);
}
// calculate dimensions
dimensions() {
this._width = this._dish.offsetWidth - (this._margin * 2);
this._height = this._dish.offsetHeight - (this._margin * 2);
}
// render cameras of dish
render() {
// delete cameras (only those that are left over)
if (this._dish.children) {
for (let i = this._cameras; i < this._dish.children.length; i++) {
let Camera = this._dish.children[i]
this._dish.removeChild(Camera);
}
}
// add cameras (only the necessary ones)
for (let i = this._dish.children.length; i < this._cameras; i++) {
let Camera = document.createElement('div')
this._dish.appendChild(Camera);
}
}
// resizer of cameras
resizer(width) {
for (var s = 0; s < this._dish.children.length; s++) {
// camera fron dish (div without class)
let element = this._dish.children[s];
// custom margin
element.style.margin = this._margin + "px"
// calculate dimensions
element.style.width = width + "px"
element.style.height = (width * this._ratio) + "px"
// to show the aspect ratio in demo (optional)
element.setAttribute('data-aspect', this._ratios[this._aspect]);
}
}
resize() {
// get dimensions of dish
this.dimensions()
// loop (i recommend you optimize this)
let max = 0
let i = 1
while (i < 5000) {
let area = this.area(i);
if (area === false) {
max = i - 1;
break;
}
i++;
}
// remove margins
max = max - (this._margin * 2);
// set dimensions to all cameras
this.resizer(max);
}
// split aspect ratio (format n:n)
ratio() {
var ratio = this._ratios[this._aspect].split(":");
return ratio[1] / ratio[0];
}
// calculate area of dish:
area(increment) {
let i = 0;
let w = 0;
let h = increment * this._ratio + (this._margin * 2);
while (i < (this._dish.children.length)) {
if ((w + increment) > this._width) {
w = 0;
h = h + (increment * this._ratio) + (this._margin * 2);
}
w = w + increment + (this._margin * 2);
i++;
}
if (h > this._height || increment > this._width) return false;
else return increment;
}
// add new camera
add() {
this._cameras++;
this.render();
this.resize();
}
// remove last camera
delete() {
this._cameras--;
this.render();
this.resize();
}
// return ratios
ratios() {
return this._ratios;
}
// return cameras
cameras() {
return this._cameras;
}
// set ratio
aspect(i) {
this._aspect = i;
this._ratio = this.ratio()
this.resize();
}
// set screen scenary
/*
expand() {
// detect screen exist
let screens = this._conference.querySelector('.screen');
if (screens) {
// remove screen
this._conference.removeChild(screens);
} else {
// add div to scenary
let screen = document.createElement('div');
screen.classList.add('screen');
let table = document.createElement('table');
screen.append(table);
let video = document.createElement('video');
video.setAttribute("autoplay", "");
video.setAttribute("playsinline", "");
video.muted = true;
screen.append(video);
// append first to scenary
this._conference.prepend(screen);
}
this.resize();
}
*/
video(camera, callback, hide = false) {
// check have video
if (this._dish.children[camera].video) {
if (hide) {
// delete video:
this._dish.children[camera].video = false
let videos = this._dish.children[camera].querySelectorAll('video');
this._dish.children[camera].removeChild(videos[0]);
}
} else {
// set video
this._dish.children[camera].video = true
// create video
let video = document.createElement('video');
video.classList.add('loading');
// random number 1-5
let filename = 'demo.mp4';
video.src = `./videos/${filename}`;
video.autoplay = true;
video.loop = true;
video.muted = true;
video.playsinline = true;
video.controls = false;
// event to show video
video.addEventListener('loadedmetadata', function () {
callback(video);
}, false);
// append video to camera
this._dish.children[camera].appendChild(video);
}
}
}

124
assets/js/e2ee.js Executable file
View File

@@ -0,0 +1,124 @@
export class e2ee {
async generateKeys() {
const keyPair = await window.crypto.subtle.generateKey(
{
name: "ECDH",
namedCurve: "P-256",
},
true,
["deriveKey", "deriveBits"]
);
const publicKeyJwk = await window.crypto.subtle.exportKey(
"jwk",
keyPair.publicKey
);
const privateKeyJwk = await window.crypto.subtle.exportKey(
"jwk",
keyPair.privateKey
);
return { publicKeyJwk, privateKeyJwk };
}
async importKey(x, y, d) {
const privateKey = await window.crypto.subtle.importKey(
"jwk",
{
kty: "EC",
crv: "P-256",
x: x,
y: y,
d: d,
ext: true,
},
{
name: "ECDH",
namedCurve: "P-256",
},
true,
["deriveKey", "deriveBits"]
);
const privateKeyJwk = await window.crypto.subtle.exportKey(
"jwk",
privateKey
);
return privateKeyJwk;
}
async deriveKey(publicKeyJwk, privateKeyJwk) {
const publicKey = await window.crypto.subtle.importKey(
"jwk",
publicKeyJwk,
{
name: "ECDH",
namedCurve: "P-256",
},
true,
[]
);
const privateKey = await window.crypto.subtle.importKey(
"jwk",
privateKeyJwk,
{
name: "ECDH",
namedCurve: "P-256",
},
true,
["deriveKey", "deriveBits"]
);
return await window.crypto.subtle.deriveKey(
{ name: "ECDH", public: publicKey },
privateKey,
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
};
async encryptMessage(text, derivedKey, conversation) {
const encodedText = new TextEncoder().encode(text);
const encryptedData = await window.crypto.subtle.encrypt(
{ name: "AES-GCM", iv: new TextEncoder().encode(conversation) },
derivedKey,
encodedText
);
const uintArray = new Uint8Array(encryptedData);
const string = String.fromCharCode.apply(null, uintArray);
const base64Data = btoa(string);
return base64Data;
};
async decryptMessage(text, derivedKey, conversation) {
try {
const initializationVector = new Uint8Array(new TextEncoder().encode(conversation)).buffer;
const string = atob(text);
const uintArray = new Uint8Array(
[...string].map((char) => char.charCodeAt(0))
);
const algorithm = {
name: "AES-GCM",
iv: initializationVector,
};
const decryptedData = await window.crypto.subtle.decrypt(
algorithm,
derivedKey,
uintArray
);
return new TextDecoder().decode(decryptedData);
}
catch {
return text;
}
};
}

24611
assets/js/emojis.js Executable file

File diff suppressed because it is too large Load Diff

345
assets/js/he.js Executable file

File diff suppressed because one or more lines are too long

1198
assets/js/hl.js Executable file

File diff suppressed because one or more lines are too long

3646
assets/js/janus.js Executable file

File diff suppressed because it is too large Load Diff

1141
assets/js/listeners.js Executable file

File diff suppressed because it is too large Load Diff

8
assets/js/mask.js Executable file

File diff suppressed because one or more lines are too long

320
assets/js/punycode.js Executable file
View File

@@ -0,0 +1,320 @@
var punycode = new function Punycode() {
// This object converts to and from puny-code used in IDN
//
// punycode.ToASCII ( domain )
//
// Returns a puny coded representation of "domain".
// It only converts the part of the domain name that
// has non ASCII characters. I.e. it dosent matter if
// you call it with a domain that already is in ASCII.
//
// punycode.ToUnicode (domain)
//
// Converts a puny-coded domain name to unicode.
// It only converts the puny-coded parts of the domain name.
// I.e. it dosent matter if you call it on a string
// that already has been converted to unicode.
//
//
this.utf16 = {
// The utf16-class is necessary to convert from javascripts internal character representation to unicode and back.
decode:function(input){
var output = [], i=0, len=input.length,value,extra;
while (i < len) {
value = input.charCodeAt(i++);
if ((value & 0xF800) === 0xD800) {
extra = input.charCodeAt(i++);
if ( ((value & 0xFC00) !== 0xD800) || ((extra & 0xFC00) !== 0xDC00) ) {
throw new RangeError("UTF-16(decode): Illegal UTF-16 sequence");
}
value = ((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000;
}
output.push(value);
}
return output;
},
encode:function(input){
var output = [], i=0, len=input.length,value;
while (i < len) {
value = input[i++];
if ( (value & 0xF800) === 0xD800 ) {
throw new RangeError("UTF-16(encode): Illegal UTF-16 value");
}
if (value > 0xFFFF) {
value -= 0x10000;
output.push(String.fromCharCode(((value >>>10) & 0x3FF) | 0xD800));
value = 0xDC00 | (value & 0x3FF);
}
output.push(String.fromCharCode(value));
}
return output.join("");
}
}
//Default parameters
var initial_n = 0x80;
var initial_bias = 72;
var delimiter = "\x2D";
var base = 36;
var damp = 700;
var tmin=1;
var tmax=26;
var skew=38;
var maxint = 0x7FFFFFFF;
// decode_digit(cp) returns the numeric value of a basic code
// point (for use in representing integers) in the range 0 to
// base-1, or base if cp is does not represent a value.
function decode_digit(cp) {
return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 : cp - 97 < 26 ? cp - 97 : base;
}
// encode_digit(d,flag) returns the basic code point whose value
// (when used for representing integers) is d, which needs to be in
// the range 0 to base-1. The lowercase form is used unless flag is
// nonzero, in which case the uppercase form is used. The behavior
// is undefined if flag is nonzero and digit d has no uppercase form.
function encode_digit(d, flag) {
return d + 22 + 75 * (d < 26) - ((flag != 0) << 5);
// 0..25 map to ASCII a..z or A..Z
// 26..35 map to ASCII 0..9
}
//** Bias adaptation function **
function adapt(delta, numpoints, firsttime ) {
var k;
delta = firsttime ? Math.floor(delta / damp) : (delta >> 1);
delta += Math.floor(delta / numpoints);
for (k = 0; delta > (((base - tmin) * tmax) >> 1); k += base) {
delta = Math.floor(delta / ( base - tmin ));
}
return Math.floor(k + (base - tmin + 1) * delta / (delta + skew));
}
// encode_basic(bcp,flag) forces a basic code point to lowercase if flag is zero,
// uppercase if flag is nonzero, and returns the resulting code point.
// The code point is unchanged if it is caseless.
// The behavior is undefined if bcp is not a basic code point.
function encode_basic(bcp, flag) {
bcp -= (bcp - 97 < 26) << 5;
return bcp + ((!flag && (bcp - 65 < 26)) << 5);
}
// Main decode
this.decode=function(input,preserveCase) {
// Dont use utf16
var output=[];
var case_flags=[];
var input_length = input.length;
var n, out, i, bias, basic, j, ic, oldi, w, k, digit, t, len;
// Initialize the state:
n = initial_n;
i = 0;
bias = initial_bias;
// Handle the basic code points: Let basic be the number of input code
// points before the last delimiter, or 0 if there is none, then
// copy the first basic code points to the output.
basic = input.lastIndexOf(delimiter);
if (basic < 0) basic = 0;
for (j = 0; j < basic; ++j) {
if(preserveCase) case_flags[output.length] = ( input.charCodeAt(j) -65 < 26);
if ( input.charCodeAt(j) >= 0x80) {
throw new RangeError("Illegal input >= 0x80");
}
output.push( input.charCodeAt(j) );
}
// Main decoding loop: Start just after the last delimiter if any
// basic code points were copied; start at the beginning otherwise.
for (ic = basic > 0 ? basic + 1 : 0; ic < input_length; ) {
// ic is the index of the next character to be consumed,
// Decode a generalized variable-length integer into delta,
// which gets added to i. The overflow checking is easier
// if we increase i as we go, then subtract off its starting
// value at the end to obtain delta.
for (oldi = i, w = 1, k = base; ; k += base) {
if (ic >= input_length) {
throw RangeError ("punycode_bad_input(1)");
}
digit = decode_digit(input.charCodeAt(ic++));
if (digit >= base) {
throw RangeError("punycode_bad_input(2)");
}
if (digit > Math.floor((maxint - i) / w)) {
throw RangeError ("punycode_overflow(1)");
}
i += digit * w;
t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
if (digit < t) { break; }
if (w > Math.floor(maxint / (base - t))) {
throw RangeError("punycode_overflow(2)");
}
w *= (base - t);
}
out = output.length + 1;
bias = adapt(i - oldi, out, oldi === 0);
// i was supposed to wrap around from out to 0,
// incrementing n each time, so we'll fix that now:
if ( Math.floor(i / out) > maxint - n) {
throw RangeError("punycode_overflow(3)");
}
n += Math.floor( i / out ) ;
i %= out;
// Insert n at position i of the output:
// Case of last character determines uppercase flag:
if (preserveCase) { case_flags.splice(i, 0, input.charCodeAt(ic -1) -65 < 26);}
output.splice(i, 0, n);
i++;
}
if (preserveCase) {
for (i = 0, len = output.length; i < len; i++) {
if (case_flags[i]) {
output[i] = (String.fromCharCode(output[i]).toUpperCase()).charCodeAt(0);
}
}
}
return this.utf16.encode(output);
};
//** Main encode function **
this.encode = function (input,preserveCase) {
//** Bias adaptation function **
var n, delta, h, b, bias, j, m, q, k, t, ijv, case_flags;
if (preserveCase) {
// Preserve case, step1 of 2: Get a list of the unaltered string
case_flags = this.utf16.decode(input);
}
// Converts the input in UTF-16 to Unicode
input = this.utf16.decode(input.toLowerCase());
var input_length = input.length; // Cache the length
if (preserveCase) {
// Preserve case, step2 of 2: Modify the list to true/false
for (j=0; j < input_length; j++) {
case_flags[j] = input[j] != case_flags[j];
}
}
var output=[];
// Initialize the state:
n = initial_n;
delta = 0;
bias = initial_bias;
// Handle the basic code points:
for (j = 0; j < input_length; ++j) {
if ( input[j] < 0x80) {
output.push(
String.fromCharCode(
case_flags ? encode_basic(input[j], case_flags[j]) : input[j]
)
);
}
}
h = b = output.length;
// h is the number of code points that have been handled, b is the
// number of basic code points
if (b > 0) output.push(delimiter);
// Main encoding loop:
//
while (h < input_length) {
// All non-basic code points < n have been
// handled already. Find the next larger one:
for (m = maxint, j = 0; j < input_length; ++j) {
ijv = input[j];
if (ijv >= n && ijv < m) m = ijv;
}
// Increase delta enough to advance the decoder's
// <n,i> state to <m,0>, but guard against overflow:
if (m - n > Math.floor((maxint - delta) / (h + 1))) {
throw RangeError("punycode_overflow (1)");
}
delta += (m - n) * (h + 1);
n = m;
for (j = 0; j < input_length; ++j) {
ijv = input[j];
if (ijv < n ) {
if (++delta > maxint) return Error("punycode_overflow(2)");
}
if (ijv == n) {
// Represent delta as a generalized variable-length integer:
for (q = delta, k = base; ; k += base) {
t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
if (q < t) break;
output.push( String.fromCharCode(encode_digit(t + (q - t) % (base - t), 0)) );
q = Math.floor( (q - t) / (base - t) );
}
output.push( String.fromCharCode(encode_digit(q, preserveCase && case_flags[j] ? 1:0 )));
bias = adapt(delta, h + 1, h == b);
delta = 0;
++h;
}
}
++delta, ++n;
}
return output.join("");
}
this.ToASCII = function ( domain ) {
var domain_array = domain.split(".");
var out = [];
for (var i=0; i < domain_array.length; ++i) {
var s = domain_array[i];
out.push(
s.match(/[^A-Za-z0-9-]/) ?
"xn--" + punycode.encode(s) :
s
);
}
return out.join(".");
}
this.ToUnicode = function ( domain ) {
var domain_array = domain.split(".");
var out = [];
for (var i=0; i < domain_array.length; ++i) {
var s = domain_array[i];
out.push(
s.match(/^xn--/) ?
punycode.decode(s.slice(4)) :
s
);
}
return out.join(".");
}
}();
export { punycode };

1
assets/js/qr.js Executable file

File diff suppressed because one or more lines are too long

10100
assets/js/qr2.js Executable file

File diff suppressed because it is too large Load Diff

1402
assets/js/script.js Executable file

File diff suppressed because it is too large Load Diff

492
assets/js/stream.js Executable file
View File

@@ -0,0 +1,492 @@
export class stream {
constructor(parent) {
this.parent = parent;
this.janus;
this.sfu;
this.janus2;
this.sfu2;
this.dish = new Dish($("#videoContainer")[0]);
this.dish.append();
this.dish.resize();
this.myid;
this.pvtid;
this.pvtid2;
window.addEventListener("resize", () => {
this.dish.resize();
});
Janus.init({debug: "none"});
}
async init(type=false) {
let done = new Promise(resolve => {
let instance = "main";
let j = this;
let janus = j.janus;
if (type == "screen") {
janus = j.janus2;
instance = "screen";
}
if (janus) {
resolve();
}
else {
if (type == "screen") {
j.janus2 = new Janus({
server: j.parent.streamURL,
success: () => {
j.janus2.attach({
plugin: "janus.plugin.videoroom",
success: (pluginHandle) => {
this.handle(instance, "success", pluginHandle).then(() => {
resolve();
});
},
error: (error) => {
this.handle(instance, "error", error).then(() => {
resolve();
});
},
onmessage: (msg, jsep) => {
if (["joined", "event"].includes(msg["videoroom"])) {
if (msg["videoroom"] == "joined") {
this.pvtid2 = msg["private_id"];
}
this.handle(instance, "message", msg, jsep);
}
if (jsep) {
this.sfu2.handleRemoteJsep({ jsep: jsep });
}
},
onlocalstream: (stream) => {
this.handle(instance, "local", stream);
j.parent.ui.setMute("toggleScreen", 0);
},
oncleanup: () => {
this.handle(instance, "cleanup", { display: j.parent.domain });
j.parent.ui.setMute("toggleScreen", 1);
}
});
}
});
}
else {
j.janus = new Janus({
server: j.parent.streamURL,
success: () => {
j.janus.attach({
plugin: "janus.plugin.videoroom",
success: (pluginHandle) => {
this.handle(instance, "success", pluginHandle).then(() => {
resolve();
});
},
error: (error) => {
this.handle(instance, "error", error).then(() => {
resolve();
});
},
onmessage: (msg, jsep) => {
if (["joined", "event", "talking", "stopped-talking"].includes(msg["videoroom"])) {
if (msg["videoroom"] == "joined") {
this.myid = msg["id"];
this.pvtid = msg["private_id"];
}
this.handle(instance, "message", msg, jsep);
}
if (jsep) {
this.sfu.handleRemoteJsep({ jsep: jsep });
}
},
onlocalstream: (stream) => {
this.handle(instance, "local", stream);
},
oncleanup: () => {
this.handle(instance, "cleanup", { display: j.parent.domain });
}
});
}
});
}
}
});
return await done;
}
subscribe(publisher) {
let j = this;
let feed;
j.janus.attach({
plugin: "janus.plugin.videoroom",
opaqueId: j.parent.domain,
success: (pluginHandle) => {
feed = pluginHandle;
let message = {
request: "join",
room: j.parent.conversation,
ptype: "subscriber",
feed: publisher.id,
private_id: j.pvtid
};
feed.send({ message: message });
},
onmessage: function(msg, jsep) {
if (jsep) {
Janus.debug("Handling SDP as well...", jsep);
feed.createAnswer({
jsep: jsep,
media: { audioSend: false, videoSend: false },
success: function(jsep) {
Janus.debug("Got SDP!", jsep);
let message = { request: "start", room: j.parent.conversation };
feed.send({ message: message, jsep: jsep });
},
error: function(error) {
Janus.error("WebRTC error:", error);
}
});
}
},
onremotestream: (stream) => {
publisher.stream = stream;
j.handle("video", "remote", publisher);
},
oncleanup: () => {
j.handle("video", "cleanup", publisher);
}
});
}
async handle(instance, type, data=false, jsep=false) {
let done = new Promise(resolve => {
switch (type) {
case "success":
if (instance == "screen") {
this.sfu2 = data;
this.register(this.sfu2);
}
else {
this.sfu = data;
if (Object.keys(this.parent.currentVideoUsers()).includes(this.parent.domain)) {
this.register(this.sfu);
}
else {
this.publishers(this.sfu);
}
}
resolve();
break;
case "error":
resolve();
break;
case "message":
switch (data.videoroom) {
case "talking":
this.talking(data.id, true);
break;
case "stopped-talking":
this.talking(data.id, false);
break;
default:
$.each(data["publishers"], (k, p) => {
if (Object.keys(this.parent.currentVideoUsers()).includes(p.display)) {
this.subscribe(p);
}
});
break;
}
resolve();
break;
case "local":
if (instance == "screen") {
this.attachScreen(this.parent.domain, data);
}
else {
this.addCam(this.parent.domain, this.myid);
this.attachVideo(this.parent.domain, data);
}
resolve();
break;
case "remote":
if (data.audio_codec || "talking" in data) {
this.addCam(data.display, data.id);
this.attachVideo(data.display, data.stream);
}
else {
this.attachScreen(data.display, data.stream);
}
resolve();
break;
case "cleanup":
if ((data.audio_codec || !data.id) && instance !== "screen") {
this.removeCam(data.display);
}
else {
this.removeScreen();
}
resolve();
break;
}
});
return await done;
}
getRandomNumber(digit) {
return Math.random().toFixed(digit).split('.')[1];
}
register(sfu) {
let message = {
request: "join",
room: this.parent.conversation,
ptype: "publisher",
id: this.getRandomNumber(16),
display: this.parent.domain
};
sfu.send({ message: message });
}
publishers(sfu) {
let message = {
request: "listparticipants",
room: this.parent.conversation
};
sfu.send({
"message" : message,
success: (result) => {
$.each(result.participants, (k, p) => {
if (Object.keys(this.parent.currentVideoUsers()).includes(p.display)) {
this.subscribe(p);
}
});
}
});
}
leave(sfu) {
let message = {
request: "leave"
};
sfu.send({ message: message });
}
async publish(type=false) {
let sfu = this.sfu;
let published = "main";
let media = { video: "video", audioRecv: false, videoRecv: false, audioSend: true, videoSend: true };
let message = { request: "configure", audio: true, video: true };
let done = new Promise(resolve => {
if (type == "screen") {
this.init("screen").then(() => {
sfu = this.sfu2;
published = "screen";
media = { video: "screen", audioRecv: false, videoRecv: false, audioSend: false, videoSend: true };
message = { request: "configure", audio: false, video: true };
resolve();
});
}
else {
resolve();
}
});
await done;
sfu.createOffer({
media: media,
success: (jsep) => {
sfu.send({ message: message, jsep: jsep });
if (type !== "screen") {
sfu.muteVideo();
sfu.muteAudio();
}
}
});
}
unpublish(type=false) {
let message = {
request: "unpublish"
};
if (type) {
switch (type) {
case "video":
if (this.sfu) {
this.sfu.send({ message: message });
}
break;
case "screen":
if (this.sfu2) {
this.sfu2.send({ message: message });
}
break;
}
}
else {
if (this.sfu) {
this.sfu.send({ message: message });
}
if (this.sfu2) {
this.sfu2.send({ message: message });
}
$(".controls > .button").addClass("muted");
}
}
toggleScreen() {
let muted = $(".controls .button[data-action=toggleScreen]").hasClass("muted");
if (muted) {
if (!$(".screen").hasClass("shown")) {
this.publish("screen");
}
}
else {
this.unpublish("screen");
}
this.dish.resize();
}
addCam(id, jid) {
if (!$(`.cam[data-id=${id}]`).length) {
let html = $(`
<div class="cam" data-id="${id}" data-jid="${jid}">
<div class="background"></div>
<video autoplay playsinline></video>
<div class="info">
<table></table>
</div>
</div>
`);
let row = $(`#users .users tr[data-id="${id}"]`).clone();
row.append($(`
<td>
<div class="icon voice"></div>
</td>
`));
html.find("table").append(row);
let row2 = $(`#users .users tr[data-id="${id}"]`).clone();
html.find(".background").append(row2);
if (id == this.parent.domain) {
html.find("video")[0].muted = true;
html.addClass("me");
}
$(".videoHolder .cams").append(html);
}
if (id && jid) {
this.parent.ui.setData($(`.cam[data-id=${id}]`), "jid", jid);
}
this.dish.resize();
}
attachVideo(id, video) {
Janus.attachMediaStream($(`.cam[data-id=${id}] video`).get(0), video);
this.dish.resize();
this.dish.resize();
}
attachScreen(id, video) {
let html = $(`.videoHolder .screen`);
let table = html.find("table");
let row = $(`#users .users tr[data-id="${id}"]`).clone();
table.empty();
table.append(row);
let screen = html.find("video").get(0);
screen.muted = true;
Janus.attachMediaStream(screen, video);
$(".screen").addClass("shown");
this.dish.resize();
this.dish.resize();
}
removeCam(id) {
$(`.cam[data-id=${id}]`).remove();
this.dish.resize();
}
removeScreen() {
$(".screen").removeClass("shown");
this.dish.resize();
}
mute(type, bool) {
switch (type) {
case "audio":
if (bool) {
this.sfu.muteAudio();
}
else {
this.sfu.unmuteAudio();
}
break;
case "video":
if (bool) {
this.sfu.muteVideo();
}
else {
this.sfu.unmuteVideo();
}
break;
}
}
talking(id, bool) {
if (bool) {
$(`.cam[data-jid=${id}]`).addClass("talking");
}
else {
$(`.cam[data-jid=${id}]`).removeClass("talking");
}
}
close() {
$(".cam").remove();
if (this.janus2) {
this.leave(this.sfu2);
this.sfu2 = null;
this.janus2 = null;
}
if (this.janus) {
this.leave(this.sfu);
this.sfu = null;
this.janus = null;
}
}
}

3156
assets/js/ui.js Executable file

File diff suppressed because it is too large Load Diff

68
assets/js/ws.js Executable file
View File

@@ -0,0 +1,68 @@
export class ws {
constructor(parent) {
this.parent = parent;
this.pingTimer;
this.typing;
}
async connect() {
let connected = new Promise(resolve => {
this.socket = new WebSocket(`wss://${window.location.host}/wss`);
this.socket.onopen = (e) => {
this.logMessage("CONNECTED");
this.identify();
this.pingTimer = setInterval(() => {
this.sendPing();
}, 30000);
this.typing = setInterval(() => {
this.parent.sendTyping();
this.parent.updateTypingStatus();
this.parent.ui.updateTypingView();
}, 250);
resolve();
}
this.socket.onclose = (e) => {
this.logMessage("DISCONNECTED");
clearInterval(this.pingTimer);
this.parent.endAllVideo();
this.parent.ready(false);
setTimeout(() => {
this.connect();
}, 2000);
}
this.socket.onmessage = (e) => {
this.logMessage(`IN: ${e.data}`);
this.parent.message(e.data);
}
});
return await connected;
}
sendPing() {
this.send(`PING`);
}
send(message) {
this.logMessage(`OUT: ${message}`);
this.socket.send(message);
}
identify() {
this.send(`IDENTIFY ${this.parent.session}`);
}
logMessage(message) {
if (this.parent.settings.debug) {
console.log(message);
}
}
}

279
assets/js/zwj.js Executable file
View File

@@ -0,0 +1,279 @@
function nameToUnicode(unicode) {
const skinColors = ["🏻", "🏼", "🏽", "🏾", "🏿"];
const tonedEmojis = [
"❤",
"💋",
"😶",
"😮",
"😵",
"👶",
"🧒",
"👦",
"👧",
"🧑",
"👱",
"👨",
"🧔",
"👨‍🦰",
"👨‍🦱",
"👨‍🦳",
"👨‍🦲",
"👩",
"👩‍🦰",
"🧑‍🦰",
"👩‍🦱",
"🧑‍🦱",
"👩‍🦳",
"🧑‍🦳",
"👩‍🦲",
"🧑‍🦲",
"🧓",
"👴",
"👵",
"🙍",
"🙎",
"🙅",
"🙆",
"💁",
"🙋",
"🧏",
"🙇",
"🤦",
"🤷",
"🧑‍🎓",
"👨‍🎓",
"👩‍🎓",
"🧑‍🏫",
"👨‍🏫",
"👩‍🏫",
"🧑‍🌾",
"👨‍🌾",
"👩‍🌾",
"🧑‍🍳",
"👨‍🍳",
"👩‍🍳",
"🧑‍🔧",
"👨‍🔧",
"👩‍🔧",
"🧑‍🏭",
"👨‍🏭",
"👩‍🏭",
"🧑‍💼",
"👨‍💼",
"👩‍💼",
"🧑‍🔬",
"👨‍🔬",
"👩‍🔬",
"🧑‍💻",
"👨‍💻",
"👩‍💻",
"🧑‍🎤",
"👨‍🎤",
"👩‍🎤",
"🧑‍🎨",
"👨‍🎨",
"👩‍🎨",
"🧑‍✈",
"👨‍✈",
"👩‍✈",
"🧑‍🚀",
"👨‍🚀",
"👩‍🚀",
"🧑‍🚒",
"👨‍🚒",
"👩‍🚒",
"👮",
"🕵",
"💂",
"🥷",
"👷",
"🤴",
"👸",
"👳",
"👲",
"🧕",
"🤵",
"👰",
"🤰",
"🤱",
"👩‍🍼",
"👨‍🍼",
"🧑‍🍼",
"👼",
"🎅",
"🤶",
"🧑‍🎄",
"🦸",
"🦹",
"🧙",
"🧚",
"🧛",
"🧜",
"🧝",
"🧞",
"🧟",
"💆",
"💇",
"🫅",
"🫃",
"🫄",
"🚶",
"🧍",
"🧎",
"🧑‍🦯",
"👨‍🦯",
"👩‍🦯",
"🧑‍🦼",
"👨‍🦼",
"👩‍🦼",
"🧑‍🦽",
"👨‍🦽",
"👩‍🦽",
"🏃",
"💃",
"🕺",
"👯",
"🧖",
"🧘",
"🧑‍🤝‍🧑",
"👭",
"👫",
"👬",
"💏",
"👩‍❤️‍💋‍👨",
"👨‍❤️‍💋‍👨",
"👩‍❤️‍💋‍👩",
"💑",
"👩‍❤️‍👨",
"👨‍❤️‍👨",
"👩‍❤️‍👩",
"👪",
"👨‍👩‍👦",
"👨‍👩‍👧",
"👨‍👩‍👧‍👦",
"👨‍👩‍👦‍👦",
"👨‍👩‍👧‍👧",
"👨‍👨‍👦",
"👨‍👨‍👧",
"👨‍👨‍👧‍👦",
"👨‍👨‍👦‍👦",
"👨‍👨‍👧‍👧",
"👩‍👩‍👦",
"👩‍👩‍👧",
"👩‍👩‍👧‍👦",
"👩‍👩‍👦‍👦",
"👩‍👩‍👧‍👧",
"👨‍👦",
"👨‍👦‍👦",
"👨‍👧",
"👨‍👧‍👦",
"👨‍👧‍👧",
"👩‍👦",
"👩‍👦‍👦",
"👩‍👧",
"👩‍👧‍👦",
"👩‍👧‍👧",
"🕴",
"🧗",
"🧗",
"🧗",
"🤺",
"🏇",
"⛷",
"🏂",
"🏌",
"🏄",
"🚣",
"🏊",
"⛹",
"🏋",
"🚴",
"🚵",
"🤸",
"🤼",
"🤽",
"🤾",
"🤹",
"🧘",
"👋",
"🤚",
"🖐",
"✋",
"🫱",
"🫲",
"🫳",
"🫴",
"🫰",
"🫵",
"🫶",
"🖖",
"👌",
"🤌",
"🤏",
"✌",
"🤞",
"🤟",
"🤘",
"🤙",
"👈",
"👉",
"👆",
"🖕",
"👇",
"☝",
"👍",
"👎",
"✊",
"👊",
"🤛",
"🤜",
"👏",
"🙌",
"👐",
"🤲",
"🤝",
"🙏",
"✍",
"💅",
"🤳",
"💪",
"🦵",
"🦶",
"👂",
"🦻",
"👃",
"🛌",
"🛀",
"🏳",
"🏴",
"👁",
"🐈",
"🐦",
"🐕",
"🦺",
"🐻"
];
const allChars = tonedEmojis.concat(skinColors);
let chars = [];
let i = 0;
for (let c of unicode) {
// remove last zwj if the next one is a skin color
if (skinColors.includes(c)) chars.pop();
// add emoji
chars.push(c);
// add zwj
if (allChars.includes(c)) chars.push("\u200d");
i++;
}
// remove last element if zwj
if (chars[chars.length - 1] === "\u200d") chars.pop();
// combine to string
return chars.join("");
}

BIN
assets/sound/pop.wav Executable file

Binary file not shown.