mirror of
https://github.com/eliasstepanik/vdo.ninja.git
synced 2026-01-11 13:48:38 +00:00
Add files via upload
includes a feature request, session.introOnClean=true;
This commit is contained in:
parent
f54b96c4e4
commit
bc5d447e9d
133
examples/addtoscene.html
Normal file
133
examples/addtoscene.html
Normal file
@ -0,0 +1,133 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>IFRAME Example</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||
<style>
|
||||
body{
|
||||
padding:0;
|
||||
margin:0;
|
||||
background-color: #0000;
|
||||
}
|
||||
iframe {
|
||||
border:0;
|
||||
margin:0;
|
||||
padding:0;
|
||||
display:block;
|
||||
width:100%;
|
||||
height:90%
|
||||
}
|
||||
#viewlink {
|
||||
width:400px;
|
||||
}
|
||||
#container {
|
||||
display:block;
|
||||
padding:0px;
|
||||
padding:0px;
|
||||
}
|
||||
input{
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
}
|
||||
button{
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
|
||||
function loadIframe(){
|
||||
|
||||
document.getElementById("container").innerHTML = "";
|
||||
|
||||
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
var iframeContainer = document.createElement("div");
|
||||
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;";
|
||||
|
||||
iframe.src = "../?dir=teststeve123&password=1234";
|
||||
iframeContainer.appendChild(iframe);
|
||||
document.getElementById("container").appendChild(iframeContainer);
|
||||
|
||||
var listOfStreamIDs = [
|
||||
"1234_pov",
|
||||
"2345_pov",
|
||||
"3456_pov",
|
||||
"4567_pov",
|
||||
"5678_pov"
|
||||
];
|
||||
|
||||
|
||||
for (var i=0;i<listOfStreamIDs.length;i++){
|
||||
|
||||
var button = document.createElement("a");
|
||||
button.innerHTML = "Invite "+listOfStreamIDs[i];
|
||||
button.target = "_blank";
|
||||
button.href = "../?room=teststeve123&password=1234&broadcast&transparent&autostart&nmb&nvb&gain=0&webcam&l=stevetest&push="+listOfStreamIDs[i]+"_pov";
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "TOGGLE "+listOfStreamIDs[i];
|
||||
button.dataset.sid = listOfStreamIDs[i];
|
||||
button.onclick = function(){
|
||||
iframe.contentWindow.postMessage({
|
||||
action: "addScene",
|
||||
value: "1",
|
||||
target: listOfStreamIDs[i],
|
||||
}, '*');
|
||||
iframe.contentWindow.postMessage({
|
||||
action: "mic",
|
||||
value: true,
|
||||
target: listOfStreamIDs[i],
|
||||
}, '*');
|
||||
|
||||
}; // target can be a stream ID or * for all.
|
||||
iframeContainer.appendChild(button);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////// LISTEN FOR EVENTS
|
||||
|
||||
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
|
||||
var eventer = window[eventMethod];
|
||||
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
|
||||
|
||||
|
||||
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
|
||||
|
||||
eventer(messageEvent, function (e) {
|
||||
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
|
||||
|
||||
|
||||
if ("action" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.innerHTML = "event: "+e.data.action+"<br />";
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
|
||||
|
||||
if ("streamIDs" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.innerHTML = "streamID list:<br />";
|
||||
for (var key in e.data.streamIDs) {
|
||||
outputWindow.innerHTML += "streamID: " + key + ", label:"+e.data.streamIDs[key] + "\n";
|
||||
}
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div id="container">
|
||||
<button onclick="loadIframe();">Go to Directors Room</button>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
451
examples/mixer.html
Normal file
451
examples/mixer.html
Normal file
@ -0,0 +1,451 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>IFRAME Example</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||
<style>
|
||||
body{
|
||||
padding:0;
|
||||
margin:0;
|
||||
background-color: #0000;
|
||||
}
|
||||
iframe {
|
||||
border:0;
|
||||
padding:0;
|
||||
display:block;
|
||||
width:1280px;
|
||||
height:720px;
|
||||
background-color: #111;
|
||||
}
|
||||
#viewlink {
|
||||
width:400px;
|
||||
}
|
||||
#container {
|
||||
display:block;
|
||||
padding:0px;
|
||||
}
|
||||
input{
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
}
|
||||
button{
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
}
|
||||
canvas{
|
||||
padding:10px;
|
||||
cursor:pointer;
|
||||
}
|
||||
.thing {
|
||||
width: 100px;
|
||||
height: 2em;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em;
|
||||
background: rgba(0,0,0,0.8);
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
cursor: grab;
|
||||
}
|
||||
.empty {
|
||||
width: 100px;
|
||||
height: 2em;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em;
|
||||
background: rgba(0,0,0,0.8);
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
user-select: none;
|
||||
}
|
||||
.col {
|
||||
width: 130px;
|
||||
height: 450px;
|
||||
padding: 1em;
|
||||
border: 1px solid;
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
float: left;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
|
||||
function allowDrop(ev) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
function swapNodes(n1, n2) {
|
||||
var p1 = n1.parentNode;
|
||||
var p2 = n2.parentNode;
|
||||
var i1, i2;
|
||||
|
||||
if ( !p1 || !p2 || p1.isEqualNode(n2) || p2.isEqualNode(n1) ) return;
|
||||
|
||||
for (var i = 0; i < p1.children.length; i++) {
|
||||
if (p1.children[i].isEqualNode(n1)) {
|
||||
i1 = i;
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < p2.children.length; i++) {
|
||||
if (p2.children[i].isEqualNode(n2)) {
|
||||
i2 = i;
|
||||
}
|
||||
}
|
||||
|
||||
if ( p1.isEqualNode(p2) && i1 < i2 ) {
|
||||
i2++;
|
||||
}
|
||||
p1.insertBefore(n2, p1.children[i1]);
|
||||
p2.insertBefore(n1, p2.children[i2]);
|
||||
}
|
||||
|
||||
function drag(ev) {
|
||||
ev.dataTransfer.setData("text", ev.target.id);
|
||||
}
|
||||
|
||||
function drop(ev) {
|
||||
ev.preventDefault();
|
||||
var data = ev.dataTransfer.getData("text");
|
||||
var origThing = document.getElementById(data);
|
||||
console.log(origThing);
|
||||
console.log(data);
|
||||
console.log(ev);
|
||||
//var newThing = origThing.cloneNode(true);
|
||||
if (ev.target.classList.contains("thing")){
|
||||
//ev.target.parentNode.insertBefore(origThing, ev.target.nextSibling);
|
||||
//elem.parentNode.insertBefore(elem, elem.parentNode.firstChild);
|
||||
swapNodes( ev.target, origThing);
|
||||
var slot = origThing.dataset.slot;
|
||||
origThing.dataset.slot = ev.target.dataset.slot;
|
||||
ev.target.dataset.slot = slot;
|
||||
|
||||
} else if (ev.target.classList.contains("empty")){
|
||||
ev.target.parentNode.insertBefore(origThing, ev.target.nextSibling);
|
||||
origThing.dataset.slot = ev.target.dataset.slot;
|
||||
ev.target.style.display = "none";
|
||||
}
|
||||
origThing.style.backgroundColor = ev.target.style.backgroundColor;
|
||||
|
||||
|
||||
}
|
||||
|
||||
function dropRemove(ev) {
|
||||
ev.preventDefault();
|
||||
var data = ev.dataTransfer.getData("text");
|
||||
var origThing = document.getElementById(data);
|
||||
if (origThing.dataset.slot){
|
||||
document.querySelector(".empty[data-slot='"+origThing.dataset.slot+"']").style.display = "block";
|
||||
delete origThing.dataset.slot;
|
||||
}
|
||||
origThing.style.backgroundColor = "#000";
|
||||
if (ev.target.classList.contains("thing")){
|
||||
ev.target.parentNode.insertBefore(origThing, ev.target.nextSibling);
|
||||
} else {
|
||||
ev.target.appendChild(origThing);
|
||||
}
|
||||
document.getElementById("col2").appendChild(document.getElementById("delete"));
|
||||
}
|
||||
|
||||
var streamIDs = [];
|
||||
|
||||
function updateList(){
|
||||
//<div id="col2" ondrop="dropRemove(event)" ondragover="allowDrop(event)">
|
||||
// <div class="thing" draggable="true" ondragstart="drag(event)" id="thing4">THING 4</div>
|
||||
// <div class="thing" draggable="true" ondragstart="drag(event)" id="thing1">THING 1</div>
|
||||
//</div>
|
||||
for (var i=0;i<streamIDs.length;i++){
|
||||
if (!document.getElementById("sid_"+streamIDs[i])){
|
||||
var thing = document.createElement("div");
|
||||
thing.draggable = true;
|
||||
thing.classList.add("thing");
|
||||
thing.addEventListener("dragstart", drag);
|
||||
thing.dataset.sid = streamIDs[i];
|
||||
thing.id = "sid_"+streamIDs[i];
|
||||
thing.innerText = streamIDs[i];
|
||||
document.getElementById("col2").appendChild(thing);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("col2").appendChild(document.getElementById("delete"));
|
||||
}
|
||||
|
||||
function loadIframe(){
|
||||
|
||||
document.getElementById("container").innerHTML = "";
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
var iframeContainer = document.createElement("div");
|
||||
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;";
|
||||
|
||||
var promptRoom = prompt("Enter a room name to use");
|
||||
if (promptRoom){
|
||||
var iframesrc = "https://vdo.ninja/?transparent&cleanoutput&manual&scene=manualtestscene&room="+promptRoom;
|
||||
} else {
|
||||
promptRoom = "testroom123312";
|
||||
var iframesrc = "https://vdo.ninja/?transparent&cleanoutput&manual&scene=manualtestscene&room="+promptRoom;
|
||||
}
|
||||
|
||||
function activate(){
|
||||
console.log(this.dataset.layout);
|
||||
var layout = JSON.parse(this.dataset.layout);
|
||||
|
||||
iframe.contentWindow.postMessage({"target":"*", "remove":true}, '*');
|
||||
|
||||
|
||||
|
||||
for (var i=0;i<layout.length;i++){
|
||||
|
||||
var stream = document.querySelector(".thing[data-slot='"+(i+1)+"'");
|
||||
if (!stream){continue;}
|
||||
|
||||
var x = layout[i].x|| 0;
|
||||
var y = layout[i].y || 0;
|
||||
var w = layout[i].w || 0;
|
||||
var h = layout[i].h || 0;
|
||||
var cover = layout[i].cover || false;
|
||||
|
||||
if (!(w && h)){continue;}
|
||||
|
||||
x = x + "%";
|
||||
y = y + "%";
|
||||
w = w + "%";
|
||||
h = h + "%";
|
||||
|
||||
if (cover){
|
||||
cover = "object-fit:cover;";
|
||||
} else {
|
||||
cover = "";
|
||||
}
|
||||
|
||||
iframe.contentWindow.postMessage({"target":stream.dataset.sid, "add":true, "settings":{"style": "width:"+w+";height:"+h+";position:absolute;left:"+x+";top:"+y+";display:block;"+cover}}, '*');
|
||||
}
|
||||
}
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Refresh list";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"getStreamIDs":true}, '*');};
|
||||
button.style.display = "block";
|
||||
document.getElementById("sources").appendChild(button);
|
||||
|
||||
var a = document.createElement("a");
|
||||
a.innerHTML = "Invite Guest Link";
|
||||
a.href = "https://vdo.ninja/?room="+promptRoom+"&broadcast";
|
||||
a.target = "_blank";
|
||||
document.getElementById("sources").appendChild(a);
|
||||
|
||||
var colors = [
|
||||
"#00AAAA",
|
||||
"#FF0000",
|
||||
"#0000FF",
|
||||
"#AA00AA",
|
||||
"#00FF00",
|
||||
"#AAAA00"
|
||||
];
|
||||
|
||||
|
||||
var slots = document.getElementById("col1").children;
|
||||
for (var i=0;i<slots.length;i++){
|
||||
slots[i].style.backgroundColor = colors[i];
|
||||
}
|
||||
|
||||
|
||||
function drawLayout(layout){
|
||||
for (var i=0;i<layout.length;i++){
|
||||
layout[i].i = i;
|
||||
}
|
||||
|
||||
function compare( a, b ) { // sorts layout based on z-index.
|
||||
var aa = a.z || 0;
|
||||
var bb = b.z || 0;
|
||||
if ( aa > bb ){
|
||||
return 1;
|
||||
}
|
||||
if ( aa < bb ){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
layout.sort(compare);
|
||||
|
||||
|
||||
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width="80";
|
||||
canvas.height="45";
|
||||
var ctx = canvas.getContext('2d');
|
||||
document.getElementById("container").appendChild(canvas);
|
||||
ctx.beginPath();
|
||||
ctx.rect(0, 0, 80, 45);
|
||||
ctx.fillStyle = "#000";
|
||||
ctx.fill();
|
||||
|
||||
for (var i=0;i<layout.length;i++){
|
||||
|
||||
ctx.fillStyle = colors[layout[i].i];
|
||||
ctx.lineWidth = 3;
|
||||
var x = layout[i].x*0.8 || 0;
|
||||
var y = layout[i].y*0.45 || 0;
|
||||
var w = layout[i].w*0.8 || 0;
|
||||
var h = layout[i].h*0.45 || 0;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.rect(x, y, w, h);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
canvas.dataset.layout = JSON.stringify(layout);
|
||||
canvas.onclick = activate;
|
||||
}
|
||||
|
||||
var data = [
|
||||
{x:0, y:0, w:100, h:100}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
var data = [
|
||||
{x:0, y:0, w:0, h:0},
|
||||
{x:0, y:0, w:100, h:100, cover:true}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
var data = [
|
||||
{x:0, y:25, w:50, h:50, cover:true},
|
||||
{x:50, y:25, w:50, h:50, cover:true}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
|
||||
var data = [
|
||||
{x:70, y:70, w:30, h:30, z:1, cover:false},
|
||||
{x:0, y:0, w:100, h:100,z:0, cover:true}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
var data = [
|
||||
{x:0, y:0, w:100, h:100,z:0, cover:true},
|
||||
{x:70, y:70, w:30, h:30, z:1, cover:false}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
var data = [
|
||||
{x:0, y:0, w:20, h:20, z:1, cover:false},
|
||||
{x:0, y:0, w:100, h:100,z:0, cover:true}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
var data = [
|
||||
{x:0, y:0, w:50, h:50},
|
||||
{x:50, y:0, w:50, h:50},
|
||||
{x:0, y:50, w:50, h:50},
|
||||
{x:50, y:50, w:50, h:50}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
var data = [
|
||||
{x:0, y:16.667, w:66.667, h:66.667},
|
||||
{x:66.667, y:0, w:33.333, h:33.333},
|
||||
{x:66.667, y:33.333, w:33.333, h:33.333},
|
||||
{x:66.667, y:66.667, w:33.333, h:33.333}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
var data = [
|
||||
{x:66.667, y:0, w:33.333, h:33.333},
|
||||
{x:0, y:16.667, w:66.667, h:66.667},
|
||||
{x:66.667, y:33.333, w:33.333, h:33.333},
|
||||
{x:66.667, y:66.667, w:33.333, h:33.333}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
var data = [
|
||||
{x:0, y:0, w:0, h:0},
|
||||
{},
|
||||
{x:0, y:0, w:100, h:100}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
var data = [
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{x:0, y:0, w:100, h:100}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
var data = [
|
||||
{},
|
||||
{},
|
||||
{x:70, y:70, w:30, h:30, z:1, cover:false},
|
||||
{x:0, y:0, w:100, h:100,z:0, cover:true}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
var data = [
|
||||
{},
|
||||
{},
|
||||
{x:0, y:25, w:50, h:50, cover:true},
|
||||
{x:50, y:25, w:50, h:50, cover:true}
|
||||
];
|
||||
drawLayout(data);
|
||||
|
||||
|
||||
iframe.src = iframesrc;
|
||||
iframeContainer.appendChild(iframe);
|
||||
document.getElementById("container").appendChild(iframeContainer);
|
||||
|
||||
|
||||
//////////// LISTEN FOR EVENTS
|
||||
|
||||
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
|
||||
var eventer = window[eventMethod];
|
||||
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
|
||||
|
||||
|
||||
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
|
||||
|
||||
eventer(messageEvent, function (e) {
|
||||
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
|
||||
|
||||
|
||||
if ("action" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.innerHTML = "event: "+e.data.action+"<br />";
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
|
||||
if (e.data.action === "new-view-connection"){
|
||||
iframe.contentWindow.postMessage({"getStreamIDs":true}, '*');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ("streamIDs" in e.data){
|
||||
streamIDs = [];
|
||||
for (var key in e.data.streamIDs){
|
||||
streamIDs.push(key);
|
||||
}
|
||||
updateList();
|
||||
console.log(streamIDs);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="loadIframe();">
|
||||
<div class="col" id="sources">
|
||||
<div id="col2" ondrop="dropRemove(event)" ondragover="allowDrop(event)">
|
||||
<div class="thing" draggable="false" id="delete" style="background-color:rgb(96 9 9);">REMOVE</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col" id="col1" ondrop="drop(event)" ondragover="allowDrop(event)">
|
||||
<div class="empty" data-slot="1">SLOT 1</div>
|
||||
<div class="empty" data-slot="2">SLOT 2</div>
|
||||
<div class="empty" data-slot="3">SLOT 3</div>
|
||||
<div class="empty" data-slot="4">SLOT 4</div>
|
||||
</div>
|
||||
<div id="container">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
147
examples/mobiledirector.css
Normal file
147
examples/mobiledirector.css
Normal file
@ -0,0 +1,147 @@
|
||||
body{
|
||||
zoom: 75%;
|
||||
}
|
||||
button[data-action-type='solo-chat'] {
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
button[data-action-type] {
|
||||
padding: 20px 10px;
|
||||
}
|
||||
|
||||
#controlButtons{
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
button[data-cluster='2'] {
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
button[data-action-type='solo-video'] {
|
||||
display:unset!important;
|
||||
visibility: visible;
|
||||
width:unset;
|
||||
height:unset;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div > a.soloLink{
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
div.shift{
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
div.streamID{
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
button[data-action-type='forward'] {
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
button[data-action-type='direct-chat'] {
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
button[data-action-type='hangup'] {
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
button[data-action-type='solo-chat'] {
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
button[data-action-type='solo-chat'] {
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
button[data-cluster='1'] {
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
|
||||
button[data-action-type='recorder-local'] {
|
||||
display:none! important;
|
||||
}
|
||||
span[data-action-type='ordering'] {
|
||||
display:none! important;
|
||||
}
|
||||
button[data-action-type='open-file-share'] {
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
button[data-action-type='add-channel']{
|
||||
display:none! important;
|
||||
}
|
||||
button[data-action-type='toggle-remote-speaker']{
|
||||
display:none! important;
|
||||
}
|
||||
button[data-action-type='toggle-remote-display']{
|
||||
display:none! important;
|
||||
}
|
||||
button[data-action-type='hide-guest']{
|
||||
display:none! important;
|
||||
}
|
||||
button[data-action-type='create-timer']{
|
||||
display:none! important;
|
||||
}
|
||||
button[data-action-type='change-url']{
|
||||
display:none! important;
|
||||
}
|
||||
button[data-action-type='change-params']{
|
||||
display:none! important;
|
||||
}
|
||||
span[data-action-type='change-quality']{
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
span[data-action-type='sceneCluster2']{
|
||||
display:none! important;
|
||||
}
|
||||
span[data-action-type='sceneCluster1']{
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
.orderspan{
|
||||
display:none! important;
|
||||
}
|
||||
#roomHeader{
|
||||
display:none! important;
|
||||
}
|
||||
.directorContainer {
|
||||
display:none!important;
|
||||
}
|
||||
|
||||
.hideDropMenu{
|
||||
display:none!important;
|
||||
}
|
||||
|
||||
#header{
|
||||
display:none!important;
|
||||
}
|
||||
body {
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
button[class="pull-right"]{
|
||||
display:none! important;
|
||||
}
|
||||
|
||||
div#guestFeeds {
|
||||
padding: 1px!important;
|
||||
margin: 1px!important;
|
||||
}
|
||||
div[class="vidcon directorMargins"] {
|
||||
padding: 1px!important;
|
||||
margin: 1px!important;
|
||||
width: 260px;
|
||||
}
|
||||
button[data-action-type]{
|
||||
margin: 1px!important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -747,8 +747,8 @@
|
||||
</li>
|
||||
|
||||
<br />
|
||||
<h4 style="color:#daad09;">
|
||||
👋 👀 Welcome to VDO Ninja! We've rebranded! 📼 Nothing else is changing and we're staying 100% free.
|
||||
<h4>
|
||||
<font style="color:#daad09;">Welcome to VDO Ninja! We've rebranded! Nothing else is changing and we're staying 100% free.</font>
|
||||
</h4>
|
||||
<br />
|
||||
🎁 Site updated October 19th (v19.4). The <a href="https://docs.vdo.ninja/release-notes/v19">v19 release notes are here</a>. If new issues occur, the older v18 can be <a href="https://vdo.ninja/v183/">found here</a>.
|
||||
@ -1844,6 +1844,7 @@
|
||||
// session.roomid // "yyyy"
|
||||
// session.scene
|
||||
// session.title // "zzzz"
|
||||
// session.introOnClean = true; // this will load the page with the webcam selection screen if &push or &room is in the URL; no need to use &webcam.
|
||||
</script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/aes.js"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=204"></script>
|
||||
|
||||
179
lib.js
179
lib.js
@ -12,7 +12,6 @@
|
||||
var formSubmitting = true;
|
||||
var activatedPreview = false;
|
||||
|
||||
var screensharebutton = true;
|
||||
var screensharesupport = true;
|
||||
|
||||
|
||||
@ -1482,8 +1481,18 @@ function setupIncomingVideoTracking(v, UUID){ // video element.
|
||||
});
|
||||
v.classList.add("fadein"); // allows the video to fade in.
|
||||
}
|
||||
|
||||
applyMuteState(UUID);;
|
||||
v.dataset.usermuted = false;
|
||||
|
||||
applyMuteState(UUID);
|
||||
v.addEventListener('volumechange',function(e){
|
||||
var muteState = checkMuteState(UUID);
|
||||
if (this.muted && (this.muted !== muteState)){
|
||||
this.dataset.usermuted = true;
|
||||
} else if (!this.muted){
|
||||
this.dataset.usermuted = false;
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(session.processStats, 1000, UUID);
|
||||
}
|
||||
@ -1507,14 +1516,15 @@ function updateVolume(update=false){
|
||||
setStorage("micVolume_"+hash, session.audioGain, hours=6);
|
||||
});
|
||||
}
|
||||
if (session.audioGain == 0){
|
||||
getById("header").classList.add('orange');
|
||||
getById("head7").classList.remove('advanced');
|
||||
} else {
|
||||
getById("header").classList.remove('orange');
|
||||
getById("head7").classList.add('advanced');
|
||||
}
|
||||
}
|
||||
if (session.audioGain == 0){
|
||||
getById("header").classList.add('orange');
|
||||
getById("head7").classList.remove('advanced');
|
||||
} else {
|
||||
getById("header").classList.remove('orange');
|
||||
getById("head7").classList.add('advanced');
|
||||
}
|
||||
|
||||
} else {
|
||||
var pswd = session.password || "";
|
||||
generateHash(session.streamID + session.roomid + pswd + session.salt, 6).then(function(hash) {
|
||||
@ -2325,15 +2335,51 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
|
||||
var container = vid.parentNode;
|
||||
if (container.move){
|
||||
clearInterval(container.move);
|
||||
container.move = null;
|
||||
}
|
||||
|
||||
if (session.animatedMoves){
|
||||
|
||||
|
||||
var left = Math.max(offsetx+Math.floor(((i%rw)+0)*w/rw),0);
|
||||
var top = Math.max(offsety+Math.floor((Math.floor(i/rw)+0)*h/rh + hi),0);
|
||||
var width = Math.ceil(w/rw);
|
||||
var height = Math.ceil(h/rh);
|
||||
if ((typeof session.layout === "object") && (session.layout!==null)){
|
||||
if (vid.dataset.sid in session.layout){
|
||||
var left = (window.innerWidth/100*session.layout[vid.dataset.sid].x) || 0;
|
||||
var top = (window.innerHeight/100*session.layout[vid.dataset.sid].y) || 0;
|
||||
var width = (window.innerWidth/100*session.layout[vid.dataset.sid].w) || 0;
|
||||
var height = (window.innerHeight/100*session.layout[vid.dataset.sid].h) || 0;
|
||||
container.style.zIndex = session.layout[vid.dataset.sid].z || 0;
|
||||
if (session.layout[vid.dataset.sid].c){
|
||||
vid.style.objectFit = "cover";
|
||||
} else {
|
||||
vid.style.objectFit = "contain";
|
||||
}
|
||||
} else {
|
||||
container.style.zIndex = 0;
|
||||
if (session.cover){
|
||||
vid.style.objectFit = "cover";
|
||||
} else {
|
||||
vid.style.objectFit = "contain";
|
||||
}
|
||||
container.style.width="0";
|
||||
container.style.height="0";
|
||||
session.requestRateLimit(session.hiddenSceneViewBitrate, i, false); // it's added already, so we know it needs sound. But lets d
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
container.style.zIndex = 0;
|
||||
var left = Math.max(offsetx+Math.floor(((i%rw)+0)*w/rw),0);
|
||||
var top = Math.max(offsety+Math.floor((Math.floor(i/rw)+0)*h/rh + hi),0);
|
||||
var width = Math.ceil(w/rw);
|
||||
var height = Math.ceil(h/rh);
|
||||
|
||||
if (session.layout===null){ // if using layouts, layouts should never be false, but NULL to indicate auto mixing.
|
||||
container.style.zIndex = 0;
|
||||
if (session.cover){
|
||||
vid.style.objectFit = "cover";
|
||||
} else {
|
||||
vid.style.objectFit = "contain";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
container.tleft = left;
|
||||
@ -2346,8 +2392,7 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
|
||||
container.move = setInterval(function(CCC){
|
||||
try{
|
||||
|
||||
if (!CCC){
|
||||
return;}
|
||||
if (!CCC){return;}
|
||||
var ww = (parseInt(CCC.style.width) - CCC.twidth);
|
||||
var hh = (parseInt(CCC.style.height) - CCC.theight);
|
||||
var tt = (parseInt(CCC.style.top) - CCC.ttop);
|
||||
@ -2431,6 +2476,42 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
|
||||
container.style.height = Math.ceil(h/rh)+"px";
|
||||
}
|
||||
|
||||
} else if ((typeof session.layout === "object") && (session.layout!==null)){ //////////////////
|
||||
|
||||
if (vid.dataset.sid in session.layout){
|
||||
|
||||
var container = document.createElement("div");
|
||||
container.style.position = "absolute";
|
||||
container.style.display = "flex";
|
||||
container.style.alignItems = "center";
|
||||
|
||||
|
||||
|
||||
var left = (window.innerWidth/100*session.layout[vid.dataset.sid].x) || 0;
|
||||
var top = (window.innerHeight/100*session.layout[vid.dataset.sid].y) || 0;
|
||||
var width = (window.innerWidth/100*session.layout[vid.dataset.sid].w) || 0;
|
||||
var height = (window.innerHeight/100*session.layout[vid.dataset.sid].h) || 0;
|
||||
|
||||
container.style.left = left+"px";
|
||||
container.style.top = top+"px";
|
||||
container.style.width = width+"px";
|
||||
container.style.height = height+"px";
|
||||
|
||||
container.style.zIndex = session.layout[vid.dataset.sid].z || 0;
|
||||
if (session.layout[vid.dataset.sid].c){
|
||||
vid.style.objectFit = "cover";
|
||||
} else {
|
||||
vid.style.objectFit = "contain";
|
||||
}
|
||||
} else {
|
||||
if (session.cover){
|
||||
vid.style.objectFit = "cover";
|
||||
} else {
|
||||
vid.style.objectFit = "contain";
|
||||
}
|
||||
session.requestRateLimit(session.hiddenSceneViewBitrate, i, false); // it's added already, so we know it needs sound. But lets d
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
var container = document.createElement("div");
|
||||
container.style.position = "absolute";
|
||||
@ -2441,6 +2522,15 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
|
||||
container.style.top = offsety+Math.floor((Math.floor(i/rw)+0)*h/rh + hi)+"px";
|
||||
container.style.width = Math.ceil(w/rw)+"px";
|
||||
container.style.height = Math.ceil(h/rh)+"px";
|
||||
|
||||
if (session.layout===null){
|
||||
container.style.zIndex = 0;
|
||||
if (session.cover){
|
||||
vid.style.objectFit = "cover";
|
||||
} else {
|
||||
vid.style.objectFit = "contain";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@ -3039,7 +3129,7 @@ eventer(messageEvent, function(e) { // this listens for child IFRAMES.
|
||||
session.screenShareElement.parentNode.removeChild(session.screenShareElement);
|
||||
session.screenShareElement = false;
|
||||
|
||||
updateMixer();
|
||||
updateMixer();
|
||||
getById("screenshare2button").classList.add("float");
|
||||
getById("screenshare2button").classList.remove("float2");
|
||||
}
|
||||
@ -5412,6 +5502,40 @@ function directEnable(ele, event, director=false) { // A directing room only is
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function issueLayout(layout=false, scene=false) { // A directing room only is controlled by the Director, with the exception of MUTE.
|
||||
log("issueLayout()");
|
||||
var msg = {};
|
||||
msg.action = "layout";
|
||||
msg.value = layout;
|
||||
|
||||
/* session.layout = {
|
||||
"stevetestA": {
|
||||
x:0,
|
||||
y:0,
|
||||
w:40,
|
||||
h:40,
|
||||
z:0,
|
||||
c:false
|
||||
|
||||
},
|
||||
"stevetestB": {
|
||||
x:50,
|
||||
y:50,
|
||||
w:40,
|
||||
h:40,
|
||||
z:1,
|
||||
c:true
|
||||
}
|
||||
}; */
|
||||
scene = scene+"";
|
||||
for (var uuid in session.pcs){
|
||||
if (session.pcs[uuid].scene===scene){
|
||||
session.sendMessage(msg, uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var previousURL = "";
|
||||
var stillNeedURL = true;
|
||||
var reloadCancelled = false;
|
||||
@ -5800,12 +5924,19 @@ function applyMuteState(UUID){ // this is the mute state of PLAYBACK audio; not
|
||||
if (!(UUID in session.rpcs)){return;}
|
||||
var muteOutcome = session.rpcs[UUID].mutedState || session.rpcs[UUID].mutedStateMixer || session.rpcs[UUID].mutedStateScene || session.speakerMuted;
|
||||
if (session.rpcs[UUID].videoElement){
|
||||
if (session.rpcs[UUID].videoElement.dataset.usermuted){return;}
|
||||
session.rpcs[UUID].videoElement.muted = muteOutcome;
|
||||
}
|
||||
// session.scene
|
||||
return muteOutcome;
|
||||
}
|
||||
|
||||
function checkMuteState(UUID){ // this is the mute state of PLAYBACK audio; not the microphone or outbound.
|
||||
if (!(UUID in session.rpcs)){return false;}
|
||||
var muteOutcome = session.rpcs[UUID].mutedState || session.rpcs[UUID].mutedStateMixer || session.rpcs[UUID].mutedStateScene || session.speakerMuted;
|
||||
return muteOutcome;
|
||||
}
|
||||
|
||||
function remoteVolumeUI(ele){
|
||||
ele.nextSibling.innerHTML = ele.value;
|
||||
}
|
||||
@ -6079,7 +6210,7 @@ function publishScreen() {
|
||||
if (session.recordLocal !== false) {
|
||||
getById("recordLocalbutton").className = "float";
|
||||
}
|
||||
if (screensharebutton) {
|
||||
if (session.screensharebutton) {
|
||||
getById("screensharebutton").className = "float2";
|
||||
}
|
||||
getById("controlButtons").style.display = "flex";
|
||||
@ -6211,7 +6342,7 @@ function publishWebcam(btn = false) {
|
||||
if (session.recordLocal !== false) {
|
||||
getById("recordLocalbutton").className = "float";
|
||||
}
|
||||
if (screensharebutton) {
|
||||
if (session.screensharebutton) {
|
||||
if (session.roomid) {
|
||||
getById("screenshare2button").className = "float";
|
||||
getById("screensharebutton").className = "float advanced";
|
||||
@ -6780,14 +6911,14 @@ function audioMeter(mediaStreamSource, audioContext) {
|
||||
session.muted_activeSpeaker=true;
|
||||
session.speakerMuted=true;
|
||||
clearTimeout(timer);
|
||||
toggleSpeakerMute(true);
|
||||
toggleSpeakerMute(true); // okay, sicne this is quietOthers
|
||||
}
|
||||
} else if (session.muted_activeSpeaker==true){
|
||||
session.speakerMuted=false;
|
||||
session.muted_activeSpeaker=false;
|
||||
session.activelySpeaking=false;
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function(){toggleSpeakerMute(true);},250);
|
||||
timer = setTimeout(function(){toggleSpeakerMute(true);},250); // okay, sicne this is quietOthers
|
||||
}
|
||||
}// else if (session.activeSpeaker){
|
||||
// if (total>10){
|
||||
@ -7029,12 +7160,12 @@ function activeSpeaker(border=false) {
|
||||
if (session.muted_activeSpeaker==false){
|
||||
session.muted_activeSpeaker=true;
|
||||
session.speakerMuted=true;
|
||||
toggleSpeakerMute(true);
|
||||
toggleSpeakerMute(true); // okay, sicne this is quietOthers
|
||||
}
|
||||
} else if (session.muted_activeSpeaker==true){
|
||||
session.speakerMuted=false;
|
||||
session.muted_activeSpeaker=false;
|
||||
toggleSpeakerMute(true);
|
||||
toggleSpeakerMute(true); // okay, sicne this is quietOthers
|
||||
}
|
||||
}
|
||||
|
||||
@ -7345,7 +7476,7 @@ function createRoomCallback(passAdd, passAdd2) {
|
||||
|
||||
//getById("mutespeakerbutton").style.display = null;
|
||||
session.speakerMuted = true; // the director will start with audio playback muted.
|
||||
toggleSpeakerMute(true);
|
||||
toggleSpeakerMute(true); // okay since only run on start
|
||||
|
||||
|
||||
if (session.cleanDirector == false && session.cleanOutput==false) {
|
||||
|
||||
95
main.js
95
main.js
@ -159,7 +159,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
|
||||
if (urlParams.has('nosettings') || urlParams.has('ns')) {
|
||||
screensharebutton = false;
|
||||
session.screensharebutton = false;
|
||||
session.showSettings = false;
|
||||
}
|
||||
|
||||
@ -356,7 +356,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
|
||||
if (urlParams.has('webcam') || urlParams.has('wc') || urlParams.has('miconly')) {
|
||||
session.webcamonly = true;
|
||||
screensharebutton = false;
|
||||
session.screensharebutton = false;
|
||||
if (urlParams.has('miconly')){
|
||||
session.videoDevice=0;
|
||||
getById("add_camera").innerHTML = "Share your Microphone";
|
||||
@ -398,7 +398,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
} else if (urlParams.has('webcam2') || urlParams.has('wc2')) {
|
||||
session.webcamonly = true;
|
||||
screensharebutton = false;
|
||||
session.screensharebutton = false;
|
||||
session.introButton = true;
|
||||
} else if (urlParams.has('screenshare2') || urlParams.has('ss2')) {
|
||||
session.screenshare = true;
|
||||
@ -416,7 +416,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
|
||||
if (urlParams.has('ssb')) {
|
||||
screensharebutton = true;
|
||||
session.screensharebutton = true;
|
||||
}
|
||||
|
||||
if (urlParams.has('mute') || urlParams.has('muted') || urlParams.has('m')) {
|
||||
@ -430,6 +430,14 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
if (urlParams.has('videomute') || urlParams.has('videomuted') || urlParams.has('vm')) {
|
||||
session.videoMutedFlag = true;
|
||||
}
|
||||
|
||||
if (urlParams.has('layout')) {
|
||||
try {
|
||||
session.layout = JSON.parse(urlParams.has('layout'));
|
||||
} catch(e){
|
||||
session.layout = null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (urlParams.has('deaf') || urlParams.has('deafen')) {
|
||||
@ -585,19 +593,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
session.closedCaptions = true;
|
||||
}
|
||||
|
||||
if (session.webcamonly == true) {
|
||||
if (session.introButton){
|
||||
getById("container-2").className = 'column columnfade advanced'; // Hide screen share
|
||||
getById("head3").classList.add('advanced');
|
||||
getById("head3a").classList.add('advanced');
|
||||
} else {
|
||||
getById("container-2").className = 'column columnfade advanced'; // Hide screen share
|
||||
getById("container-3").classList.add("skip-animation");
|
||||
getById("container-3").classList.remove('pointer');
|
||||
delayedStartupFuncs.push([previewWebcam]);
|
||||
}
|
||||
}
|
||||
|
||||
if (urlParams.has('css')){
|
||||
var cssURL = urlParams.get('css');
|
||||
cssURL = decodeURI(cssURL);
|
||||
@ -1313,7 +1308,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
getById("shareScreenGear").style.display = "none";
|
||||
getById("dropButton").style.display = "none";
|
||||
getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile
|
||||
screensharebutton = false;
|
||||
session.screensharebutton = false;
|
||||
screensharesupport = false;
|
||||
|
||||
if (session.audioDevice!==0){
|
||||
@ -2265,6 +2260,41 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
}
|
||||
|
||||
if ((session.roomid) || (urlParams.has('roomid')) || (urlParams.has('r')) || (urlParams.has('room')) || (filename) || (session.permaid !== false)) {
|
||||
|
||||
var roomid = "";
|
||||
if (filename) {
|
||||
roomid = filename;
|
||||
} else if (urlParams.has('room')) {
|
||||
roomid = urlParams.get('room');
|
||||
} else if (urlParams.has('roomid')) {
|
||||
roomid = urlParams.get('roomid');
|
||||
} else if (urlParams.has('r')) {
|
||||
roomid = urlParams.get('r');
|
||||
} else if (session.roomid) {
|
||||
roomid = session.roomid;
|
||||
}
|
||||
session.roomid = sanitizeRoomName(roomid);
|
||||
}
|
||||
|
||||
if (session.webcamonly == true) {
|
||||
if (session.introButton){
|
||||
getById("container-2").className = 'column columnfade advanced'; // Hide screen share
|
||||
getById("head3").classList.add('advanced');
|
||||
getById("head3a").classList.add('advanced');
|
||||
} else {
|
||||
getById("container-2").className = 'column columnfade advanced'; // Hide screen share
|
||||
getById("container-3").classList.add("skip-animation");
|
||||
getById("container-3").classList.remove('pointer');
|
||||
delayedStartupFuncs.push([previewWebcam]);
|
||||
}
|
||||
}
|
||||
if (session.introOnClean && (session.permaid===false) && (session.roomid===false)){
|
||||
//getById("container-2").className = 'column columnfade advanced'; // Hide screen share
|
||||
getById("head3").classList.add('advanced');
|
||||
getById("head3a").classList.add('advanced');
|
||||
}
|
||||
|
||||
if (session.cleanViewer){
|
||||
if (session.view && !session.director && session.permaid===false){
|
||||
session.cleanOutput = true;
|
||||
@ -2303,23 +2333,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
}
|
||||
|
||||
if ((session.roomid) || (urlParams.has('roomid')) || (urlParams.has('r')) || (urlParams.has('room')) || (filename) || (session.permaid !== false)) {
|
||||
|
||||
var roomid = "";
|
||||
if (filename) {
|
||||
roomid = filename;
|
||||
} else if (urlParams.has('room')) {
|
||||
roomid = urlParams.get('room');
|
||||
} else if (urlParams.has('roomid')) {
|
||||
roomid = urlParams.get('roomid');
|
||||
} else if (urlParams.has('r')) {
|
||||
roomid = urlParams.get('r');
|
||||
} else if (session.roomid) {
|
||||
roomid = session.roomid;
|
||||
}
|
||||
|
||||
session.roomid = sanitizeRoomName(roomid);
|
||||
|
||||
|
||||
if (session.roomid!==false){
|
||||
if (!(session.cleanOutput)) {
|
||||
if (session.roomid === "test") {
|
||||
if (session.password === session.defaultPassword) {
|
||||
@ -2841,7 +2856,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
session.obsStateSync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ("sendMessage" in e.data) { // webrtc send to viewers
|
||||
session.sendMessage(e.data);
|
||||
}
|
||||
@ -3055,6 +3071,13 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
session.manual = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (("scene" in e.data) && ("layout" in e.data)){
|
||||
warnlog("changing layout request via IFRAME API");
|
||||
issueLayout(e.data.layout, e.data.scene);
|
||||
}
|
||||
|
||||
|
||||
if (("action" in e.data) && (e.data.action!="null")) { /////////////// reuse the Companion API
|
||||
processMessage(e.data); // reuse the companion API
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user