v2
123
assets/css/dish.css
Executable 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
@@ -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
@@ -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
BIN
assets/img/cover.png
Executable file
|
After Width: | Height: | Size: 19 KiB |
25
assets/img/handshake.svg
Executable 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
|
After Width: | Height: | Size: 33 KiB |
BIN
assets/img/icon-128x128.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
assets/img/icon-144x144.png
Executable file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/img/icon-152x152.png
Executable file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
assets/img/icon-192x192.png
Executable file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
assets/img/icon-384x384.png
Executable file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/img/icon-48x48.png
Executable file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/img/icon-512x512.png
Executable file
|
After Width: | Height: | Size: 21 KiB |
BIN
assets/img/icon-72x72.png
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
assets/img/icon-96x96.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
assets/img/icons/arrow.png
Executable file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
assets/img/icons/audio.gif
Executable file
|
After Width: | Height: | Size: 243 KiB |
BIN
assets/img/icons/check.png
Executable file
|
After Width: | Height: | Size: 666 B |
BIN
assets/img/icons/clipboard.png
Executable file
|
After Width: | Height: | Size: 661 B |
BIN
assets/img/icons/close.png
Executable file
|
After Width: | Height: | Size: 744 B |
BIN
assets/img/icons/compose.png
Executable file
|
After Width: | Height: | Size: 919 B |
BIN
assets/img/icons/edit.png
Executable file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/img/icons/emoji.png
Executable file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
assets/img/icons/fail.png
Executable file
|
After Width: | Height: | Size: 577 B |
BIN
assets/img/icons/fish.png
Executable file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/img/icons/gif.png
Executable file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/img/icons/leave.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/img/icons/lock.png
Executable file
|
After Width: | Height: | Size: 859 B |
BIN
assets/img/icons/mention.png
Executable file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/img/icons/menu.png
Executable file
|
After Width: | Height: | Size: 325 B |
BIN
assets/img/icons/message.png
Executable file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
assets/img/icons/pay.png
Executable file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
assets/img/icons/pin.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
assets/img/icons/plus.png
Executable file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/img/icons/replay.png
Executable file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/img/icons/save.png
Executable file
|
After Width: | Height: | Size: 667 B |
BIN
assets/img/icons/screen.png
Executable file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/img/icons/search.png
Executable file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/img/icons/signature.png
Executable file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
assets/img/icons/trash.png
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
assets/img/icons/update.png
Executable file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
assets/img/icons/users.png
Executable file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/img/icons/video.png
Executable file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/img/icons/view.png
Executable file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/img/icons/voice.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
assets/img/icons/warning.png
Executable file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/img/logo.png
Executable file
|
After Width: | Height: | Size: 21 KiB |
5
assets/img/unstoppable.svg
Executable 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
1
assets/js/anchorme.js
Executable file
130
assets/js/confetti.js
Executable 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
@@ -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
@@ -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
@@ -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
345
assets/js/he.js
Executable file
1198
assets/js/hl.js
Executable file
3646
assets/js/janus.js
Executable file
1141
assets/js/listeners.js
Executable file
8
assets/js/mask.js
Executable file
320
assets/js/punycode.js
Executable 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
10100
assets/js/qr2.js
Executable file
1402
assets/js/script.js
Executable file
492
assets/js/stream.js
Executable 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
68
assets/js/ws.js
Executable 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
@@ -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("");
|
||||
}
|
||||