Commit a2e322ed authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Rework custom video controls.

Move custom video controls into its own function.  Remove some
DOM traversals, avoid querySelector.  Remove dead code.
parent 907a712d
...@@ -209,21 +209,13 @@ ...@@ -209,21 +209,13 @@
<div id="videocontrols-template" class="invisible"> <div id="videocontrols-template" class="invisible">
<div class="video-controls vc-overlay"> <div class="video-controls vc-overlay">
<span class="volume" title="Volume"> <span class="volume" title="Volume">
<i class="fas fa-volume-up" data-type="bt-volume" aria-hidden="true"></i> <i class="fas fa-volume-up" aria-hidden="true"></i>
</span> </span>
<span class="pip" title="Picture In Picture"> <span class="pip" title="Picture In Picture">
<i class="fas fa-clone" data-type="bt-pip" aria-hidden="true"></i> <i class="fas fa-clone" aria-hidden="true"></i>
</span> </span>
<span class="fullscreen" title="Fullscreen"> <span class="fullscreen" title="Fullscreen">
<i class="fas fa-expand-alt" data-type="bt-fullscreen" aria-hidden="true"></i> <i class="fas fa-expand-alt" aria-hidden="true"></i>
</span>
</div>
</div>
<div id="top-videocontrols-template" class="invisible">
<div class="top-video-controls">
<span class="expand invisible" title="Maximize">
<i class="fas fa-external-link" data-type="bt-expand" aria-hidden="true"></i>
</span> </span>
</div> </div>
</div> </div>
......
...@@ -1002,19 +1002,17 @@ function muteLocalTracks(mute) { ...@@ -1002,19 +1002,17 @@ function muteLocalTracks(mute) {
} }
/** /**
* setMedia adds a new media element corresponding to stream c.
*
* @param {Stream} c * @param {Stream} c
* @param {boolean} isUp * @param {boolean} isUp
* - indicates whether the stream goes in the up direction
* @param {HTMLVideoElement} [video] * @param {HTMLVideoElement} [video]
* - the video element to add. If null, a new element with custom
* controls will be created.
*/ */
function setMedia(c, isUp, video) { function setMedia(c, isUp, video) {
let peersdiv = document.getElementById('peers'); let peersdiv = document.getElementById('peers');
let local_media;
for(let id in serverConnection.up) {
if (id === c.id) {
local_media = serverConnection.up[id];
}
}
let div = document.getElementById('peer-' + c.id); let div = document.getElementById('peer-' + c.id);
if(!div) { if(!div) {
...@@ -1026,21 +1024,28 @@ function setMedia(c, isUp, video) { ...@@ -1026,21 +1024,28 @@ function setMedia(c, isUp, video) {
let media = /** @type {HTMLVideoElement} */ let media = /** @type {HTMLVideoElement} */
(document.getElementById('media-' + c.id)); (document.getElementById('media-' + c.id));
if(!media) { if(media) {
if(video) {
throw new Error("Duplicate video");
}
} else {
if(video) { if(video) {
media = video; media = video;
} else { } else {
media = document.createElement('video'); media = document.createElement('video');
media.controls = false;
if(isUp) if(isUp)
media.muted = true; media.muted = true;
media.srcObject = c.stream;
} }
media.classList.add('media'); media.classList.add('media');
media.autoplay = true; media.autoplay = true;
/** @ts-ignore */ /** @ts-ignore */
media.playsinline = true; media.playsinline = true;
media.id = 'media-' + c.id; media.id = 'media-' + c.id;
div.appendChild(media); div.appendChild(media);
if(!video)
addCustomControls(media, div, c);
} }
let label = document.getElementById('label-' + c.id); let label = document.getElementById('label-' + c.id);
...@@ -1051,129 +1056,129 @@ function setMedia(c, isUp, video) { ...@@ -1051,129 +1056,129 @@ function setMedia(c, isUp, video) {
div.appendChild(label); div.appendChild(label);
} }
if(!video) {
let template = document.getElementById('videocontrols-template')
.firstElementChild;
let top_template = document.getElementById('top-videocontrols-template')
.firstElementChild;
let top_controls = document.getElementById('topcontrols-' + c.id);
if(template && !top_controls) {
top_controls = /** @type{HTMLElement} */(top_template.cloneNode(true));
top_controls.id = 'topcontrols-' + c.id;
div.appendChild(top_controls);
}
let controls = document.getElementById('controls-' + c.id);
if(template && !controls) {
controls = /** @type{HTMLElement} */(template.cloneNode(true));
controls.id = 'controls-' + c.id;
div.appendChild(controls);
let volume = controls.querySelector(".fa-volume-up");
if(media.muted) {
if (volume) {
volume.classList.remove("fa-volume-up");
volume.classList.add("fa-volume-off");
}
}
if (local_media && local_media.kind === "local")
volume.parentElement.remove();
}
media.srcObject = c.stream;
}
setLabel(c); setLabel(c);
setMediaStatus(c); setMediaStatus(c);
showVideo(); showVideo();
resizePeers(); resizePeers();
registerControlEvent(div.id);
} }
/** /**
* @param {HTMLVideoElement} video * @param {Element} elt
*/ */
async function videoPIP(video) { function cloneHTMLElement(elt) {
/** @ts-ignore */ if(!(elt instanceof HTMLElement))
if (video.requestPictureInPicture) { throw new Error('Unexpected element type');
/** @ts-ignore */ return /** @type{HTMLElement} */(elt.cloneNode(true));
await video.requestPictureInPicture(); }
/**
* @param {HTMLVideoElement} media
* @param {HTMLElement} container
* @param {Stream} c
*/
function addCustomControls(media, container, c) {
media.controls = false;
let controls = document.getElementById('controls-' + c.id);
if(controls) {
console.warn('Attempted to add duplicate controls');
return;
}
let template =
document.getElementById('videocontrols-template').firstElementChild;
controls = cloneHTMLElement(template);
controls.id = 'controls-' + c.id;
let volume = getVideoButton(controls, 'volume');
if(c.kind === 'local') {
volume.remove();
} else { } else {
displayWarning("Video PIP Mode not supported!"); setVolumeButton(
/** @type{HTMLElement} */(volume.firstElementChild),
media.muted,
);
} }
container.appendChild(controls);
registerControlHandlers(media, container);
} }
/** /**
* @param {HTMLElement} target * @param {HTMLElement} container
* @param {string} name
*/ */
function getParentVideo(target) { function getVideoButton(container, name) {
// target is the <i> element, parent the div <div><span><i/></span></div> return /** @type {HTMLElement} */(container.getElementsByClassName(name)[0]);
let control = target.parentElement.parentElement; }
let id = control.id.split('-')[1];
let media = /** @type {HTMLVideoElement} */ /**
(document.getElementById('media-' + id)); * @param {HTMLElement} button
if (!media) { * @param {boolean} muted
displayError("Cannot find media!"); */
function setVolumeButton(button, muted) {
if(!muted) {
button.classList.remove("fa-volume-off");
button.classList.add("fa-volume-up");
} else {
button.classList.remove("fa-volume-up");
button.classList.add("fa-volume-off");
} }
return media;
} }
/** /**
* @param {string} peerid * @param {HTMLVideoElement} media
* @param {HTMLElement} container
*/ */
function registerControlEvent(peerid) { function registerControlHandlers(media, container) {
let peer = document.getElementById(peerid); let volume = getVideoButton(container, 'volume');
//Add event listener when a video component is added to the DOM
let volume = /** @type {HTMLElement} */(peer.querySelector("span.volume"));
if (volume) { if (volume) {
volume.onclick = function(event) { volume.onclick = function(event) {
event.preventDefault(); event.preventDefault();
let volume = /** @type{HTMLElement} */(event.target); media.muted = !media.muted;
let video = getParentVideo(volume); setVolumeButton(
if(volume.className.indexOf("fa-volume-off") !== -1) { /** @type{HTMLElement} */(event.target),
volume.classList.remove("fa-volume-off"); media.muted,
volume.classList.add("fa-volume-up"); );
video.muted = false;
} else {
volume.classList.remove("fa-volume-up");
volume.classList.add("fa-volume-off");
// mute video sound
video.muted = true;
}
}; };
} }
let pip = /** @type {HTMLElement} */(peer.querySelector("span.pip")); let pip = getVideoButton(container, 'pip');
if(pip) { if(pip) {
/** @ts-ignore */ /** @ts-ignore */
if(HTMLVideoElement.prototype.requestPictureInPicture) { if(HTMLVideoElement.prototype.requestPictureInPicture) {
pip.onclick = function(event) { pip.onclick = function(e) {
event.preventDefault(); e.preventDefault();
let pip = /** @type{HTMLElement} */(event.target); /** @ts-ignore */
let video = getParentVideo(pip); if(media.requestPictureInPicture) {
videoPIP(video); /** @ts-ignore */
media.requestPictureInPicture();
} else {
displayWarning('Picture in Picture not supported.');
}
}; };
} else { } else {
pip.style.display = 'none'; pip.style.display = 'none';
} }
} }
let fs = /** @type {HTMLElement} */(peer.querySelector("span.fullscreen")); let fs = getVideoButton(container, 'fullscreen');
if(fs) { if(fs) {
fs.onclick = function(event) { if(HTMLVideoElement.prototype.requestFullscreen) {
event.preventDefault(); fs.onclick = function(e) {
let fs = /** @type {HTMLElement} */(event.target); e.preventDefault();
let video = getParentVideo(fs); if(media.requestFullscreen) {
if(video.requestFullscreen) { media.requestFullscreen();
video.requestFullscreen(); } else {
} else { displayWarning('Full screen not supported!');
displayWarning("Video Fullscreen not supported!"); }
} };
}; } else {
fs.style.display = 'none';
}
} }
} }
/** /**
* @param {string} id * @param {string} id
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment