/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see .
*
*/
const defaultCopyright = '
Recorded with BigBlueButton.
Use Mozilla Firefox or Google Chrome to play this recording.
';
const defaultLogo = 'logo.png';
// Playback events
var dataReady = false;
var mediaReady = false;
var contentReady = false;
// Content events
var DOMLoaded = false;
var metadataLoaded = false;
var mediasChecked = false;
// Media control
var hasVideo = false;
var hasDeskshare = false;
var mediaSyncing = false;
var mediaSeeked = false;
var primaryMedia;
var secondaryMedias;
var allMedias;
function goToSlide(time) {
var pop = Popcorn("#video");
pop.currentTime(time);
};
/*
* From: http://stackoverflow.com/questions/1634748/how-can-i-delete-a-query-string-parameter-in-javascript/4827730#4827730
*/
function removeURLParameter(url, param) {
var urlparts= url.split('?');
if (urlparts.length>=2) {
var prefix= encodeURIComponent(param)+'=';
var pars= urlparts[1].split(/[&;]/g);
for (var i=pars.length; i-- > 0;) {
if (pars[i].indexOf(prefix, 0)==0)
pars.splice(i, 1);
}
if (pars.length > 0) {
return urlparts[0]+'?'+pars.join('&');
} else {
return urlparts[0];
}
} else {
return url;
}
};
function addURLParameter(url, param, value) {
var s = encodeURIComponent(param) + '=' + encodeURIComponent(value);
logger.info("==Adding URL parameter", s);
if (url.indexOf('?') == -1) {
return url + '?' + s;
} else {
return url + '&' + s;
}
};
/*
* Converts seconds to HH:MM:SS
* From: http://stackoverflow.com/questions/6312993/javascript-seconds-to-time-with-format-hhmmss#6313008
*/
function secondsToHHMMSS(secs) {
var hours = Math.floor(secs / 3600);
var minutes = Math.floor((secs - (hours * 3600)) / 60);
var seconds = secs - (hours * 3600) - (minutes * 60);
if (hours < 10) {hours = "0"+hours;}
if (minutes < 10) {minutes = "0"+minutes;}
if (seconds < 10) {seconds = "0"+seconds;}
var time = hours+':'+minutes+':'+seconds;
return time;
};
secondsToYouTubeFormat = function(secs) {
var hours = Math.floor(secs / 3600);
var minutes = Math.floor((secs - (hours * 3600)) / 60);
var seconds = secs - (hours * 3600) - (minutes * 60);
var time = "";
if (hours > 0) {time += hours+"h";}
if (minutes > 0) {time += minutes+"m";}
if (seconds > 0) {time += seconds+"s";}
if (secs == 0) {time = "0s";}
return time;
};
/*
* Full word version of the above function for screen readers
*/
function secondsToHHMMSSText(secs) {
var hours = Math.floor(secs / 3600);
var minutes = Math.floor((secs - (hours * 3600)) / 60);
var seconds = secs - (hours * 3600) - (minutes * 60);
var time = "";
if (hours > 1) {time += hours + " hours ";}
else if (hours == 1) {time += hours + " hour ";}
if (minutes > 1) {time += minutes + " minutes ";}
else if (minutes == 1) {time += minutes + " minute ";}
if (seconds > 1) {time += seconds + " seconds ";}
else if (seconds == 1) {time += seconds + " second ";}
return time;
};
function replaceTimeOnURL(secs) {
var newUrl = addURLParameter(removeURLParameter(document.URL, 't'), 't', secondsToYouTubeFormat(secs));
window.history.replaceState({}, "", newUrl);
};
/*
* From: https://locutus.io/php/strings/nl2br/
*/
function nl2br (str, isXhtml) {
// discuss at: https://locutus.io/php/nl2br/
// original by: Kevin van Zonneveld (https://kvz.io)
// improved by: Philip Peterson
// improved by: Onno Marsman (https://twitter.com/onnomarsman)
// improved by: Atli Þór
// improved by: Brett Zamir (https://brett-zamir.me)
// improved by: Maximusya
// bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
// bugfixed by: Kevin van Zonneveld (https://kvz.io)
// bugfixed by: Reynier de la Rosa (https://scriptinside.blogspot.com.es/)
// input by: Brett Zamir (https://brett-zamir.me)
// example 1: nl2br('Kevin\nvan\nZonneveld')
// returns 1: 'Kevin
\nvan
\nZonneveld'
// example 2: nl2br("\nOne\nTwo\n\nThree\n", false)
// returns 2: '
\nOne
\nTwo
\n
\nThree
\n'
// example 3: nl2br("\nOne\nTwo\n\nThree\n", true)
// returns 3: '
\nOne
\nTwo
\n
\nThree
\n'
// example 4: nl2br(null)
// returns 4: ''
// Some latest browsers when str is null return and unexpected null value
if (typeof str === 'undefined' || str === null) {
return ''
}
// Adjust comment to avoid issue on locutus.io display
var breakTag = (isXhtml || typeof isXhtml === 'undefined') ? '
' : '
'
return (str + '')
.replace(/(\r\n|\n\r|\r|\n)/g, breakTag + '$1')
}
/*
* Sets the title attribute in a thumbnail.
*/
function setTitleOnThumbnail($thumb) {
var src = $thumb.attr("src");
if (src !== undefined) {
var num = "?";
var name = "undefined";
var match = src.match(/slide-(.*).png/);
if (match) { num = match[1]; }
match = src.match(/([^/]*)\/slide-.*\.png/);
if (match) { name = match[1]; }
$thumb.attr("title", name + " (" + num + ")");
}
};
/*
* Associates several events on a thumbnail, e.g. click to change slide,
* mouse over/out functions, etc.
*/
function setEventsOnThumbnail($thumb) {
// Note: use ceil() so it jumps to a part of the video that actually is showing
// this slide, while floor() would most likely jump to the previously slide
// Popcorn event to mark a thumbnail when its slide is being shown
var timeIn = $thumb.attr("data-in");
var timeOut = $thumb.attr("data-out");
var pop = Popcorn("#video");
pop.code({
start: timeIn,
end: timeOut,
onStart: function(options) {
$parent = $(".thumbnail-wrapper").removeClass("active");
$parent = $("#thumbnail-" + Math.ceil(options.start)).parent();
$parent.addClass("active");
animateToCurrentSlide();
},
onEnd: function(options) {
$parent = $("#thumbnail-" + Math.ceil(options.start)).parent();
$parent.removeClass("active");
}
});
// Click on thumbnail changes the slide in popcorn
$thumb.parent().on("click", function() {
var time = Math.ceil($thumb.attr("data-in"));
goToSlide(time);
replaceTimeOnURL(time);
});
// Mouse over/out to show/hide the label over the thumbnail
$wrapper = $thumb.parent();
$wrapper.on("mouseover", function() {
$(this).addClass("hovered");
});
$wrapper.on("mouseout", function() {
$(this).removeClass("hovered");
});
};
function animateToCurrentSlide() {
var $container = $("#thumbnails").parents(".left-off-canvas-menu");
var currentThumb = $(".thumbnail-wrapper.active");
// Animate the scroll of thumbnails to center the current slide
var thumbnailOffset = currentThumb.prop('offsetTop') - $container.prop('offsetTop') +
(currentThumb.prop('offsetHeight') - $container.prop('offsetHeight')) / 2;
$container.stop();
$container.animate({scrollTop: thumbnailOffset}, 200);
};
/*
* Generates the list of thumbnails using shapes.svg
*/
function generateThumbnails() {
logger.info("==Generating thumbnails");
var elementsMap = {};
var imagesList = new Array();
xmlList = shapesSVGContent.getElementsByTagName("image");
var slideCount = 0;
logger.info("==Setting title on thumbnails");
for (var i = 0; i < xmlList.length; i++) {
var element = xmlList[i];
if (!$(element).attr("xlink:href"))
continue;
var src = element.getAttribute("xlink:href");
// If it's a full url, leave it as it is
if (!src.match(/^http[s]?:\/\//)) {
src = url + '/' + src;
}
if (src.match(/\/presentation\/.*slide-.*\.png/)) {
var timeInList = xmlList[i].getAttribute("in").split(" ");
var timeOutList = xmlList[i].getAttribute("out").split(" ");
for (var j = 0; j < timeInList.length; j++) {
var timeIn = Math.ceil(timeInList[j]);
var timeOut = Math.ceil(timeOutList[j]);
var img = $(document.createElement('img'));
img.attr("src", src);
img.attr("id", "thumbnail-" + timeIn);
img.attr("data-in", timeIn);
img.attr("data-out", timeOut);
img.addClass("thumbnail");
img.attr("alt", " ");
// Doesn't need to be focusable for blind users
img.attr("aria-hidden", "true");
// A label with the time the slide starts
var label = $(document.createElement('span'));
label.addClass("thumbnail-label");
// Doesn't need to be focusable for blind users
label.attr("aria-hidden", "true");
label.html(secondsToHHMMSS(timeIn));
var hiddenDesc = $(document.createElement('span'));
hiddenDesc.attr("id", img.attr("id") + "description");
hiddenDesc.attr("class", "visually-hidden");
hiddenDesc.html("Slide " + ++slideCount + " " + secondsToHHMMSSText(timeIn));
// A wrapper around the img and label
var div = $(document.createElement('div'));
div.addClass("thumbnail-wrapper");
// Tells accessibility software it can be clicked
div.attr("role", "link");
div.attr("aria-describedby", img.attr("id") + "description");
div.append(img);
div.append(label);
div.append(hiddenDesc);
if (parseFloat(timeIn) == 0) {
div.addClass("active");
}
imagesList.push(timeIn);
elementsMap[timeIn] = div;
setEventsOnThumbnail(img);
setTitleOnThumbnail(img);
}
}
}
imagesList.sort(function(a,b){return a - b});
for (var i in imagesList) {
$("#thumbnails").append(elementsMap[imagesList[i]]);
}
};
function loadCaptions(video) {
asyncRequest('GET', captionsJSON).then(function (response) {
logger.info("==Processing captions.json");
var captions = JSON.parse(response.responseText);
const video = document.getElementById("video");
captions.forEach(function(caption) {
var track = document.createElement("track");
track.setAttribute('kind', 'captions');
track.setAttribute('label', caption['localeName']);
track.setAttribute('srclang', caption['locale']);
track.setAttribute('src', url + '/caption_' + caption['locale'] + '.vtt');
video.appendChild(track);
});
document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'captions'}));
}, function (response) {
logger.info("==Video has no captions");
document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'captions'}));
});
};
function loadVideo() {
logger.info("==Loading video");
var video = document.createElement("video");
video.setAttribute('id','video');
video.setAttribute('class','webcam');
video.setAttribute('preload','auto');
video.setAttribute('playsinline',true);
video.setAttribute('crossorigin','anonymous');
let webmsource = document.createElement("source");
webmsource.setAttribute('src', url + '/video/webcams.webm');
webmsource.setAttribute('type','video/webm; codecs="vp8.0, vorbis"');
video.appendChild(webmsource);
let mp4source = document.createElement("source");
mp4source.setAttribute('src', url + '/video/webcams.mp4');
mp4source.setAttribute('type','video/mp4; codecs="avc1.42E01E"');
video.appendChild(mp4source);
video.setAttribute('data-timeline-sources', chatXML);
document.getElementById("video-area").appendChild(video);
loadCaptions();
checkLoadedMedia();
};
function loadAudio() {
logger.info("==Loading audio")
var audio = document.createElement("audio") ;
audio.setAttribute('id', 'video');
audio.setAttribute('preload', 'auto');
// The webm file will work in IE with WebM components installed,
// and should load faster in Chrome too
var webmsource = document.createElement("source");
webmsource.setAttribute('src', url + '/audio/audio.webm');
webmsource.setAttribute('type', 'audio/webm; codecs="vorbis"');
// Need to keep the ogg source around for compat with old recordings
var oggsource = document.createElement("source");
oggsource.setAttribute('src', url + '/audio/audio.ogg');
oggsource.setAttribute('type', 'audio/ogg; codecs="vorbis"');
// Browser Bug Workaround: The ogg file should be preferred in Firefox,
// since it can't seek in audio-only webm files.
if (navigator.userAgent.indexOf("Firefox") != -1) {
audio.appendChild(oggsource);
audio.appendChild(webmsource);
} else {
audio.appendChild(webmsource);
audio.appendChild(oggsource);
}
audio.setAttribute('data-timeline-sources', chatXML);
// Audio.setAttribute('controls','');
// Leave auto play turned off for accessiblity support
// Audio.setAttribute('autoplay','autoplay');
document.getElementById("audio-area").appendChild(audio);
checkLoadedMedia();
};
function loadDeskshare() {
logger.info("==Loading deskshare");
var deskshareVideo = document.createElement("video");
deskshareVideo.setAttribute('id','deskshare-video');
deskshareVideo.setAttribute('preload','auto');
deskshareVideo.setAttribute('playsinline',true);
var webmsource = document.createElement("source");
webmsource.setAttribute('src', url + '/deskshare/deskshare.webm');
webmsource.setAttribute('type','video/webm; codecs="vp8.0, vorbis"');
deskshareVideo.appendChild(webmsource);
var mp4source = document.createElement("source");
mp4source.setAttribute('src', url + '/deskshare/deskshare.mp4');
mp4source.setAttribute('type','video/mp4; codecs="avc1.42E01E"');
deskshareVideo.appendChild(mp4source);
var presentationArea = document.getElementById("presentation-area");
presentationArea.insertBefore(deskshareVideo,presentationArea.childNodes[0]);
checkLoadedDeskshare();
};
function setMediaSync() {
if (!hasDeskshare) {
return;
}
// Master video
primaryMedia = Popcorn("#video");
// Slave videos
secondaryMedias = [Popcorn("#deskshare-video")];
allMedias = [primaryMedia].concat(secondaryMedias);
// When we play the master video, we play all other videos as well...
primaryMedia.on("play", function() {
for (i = 0; i < secondaryMedias.length ; i++) {
secondaryMedias[i].play();
}
});
// When we pause the master video, we sync
primaryMedia.on("pause", function() {
sync();
});
primaryMedia.on("seeking", function() {
if (primaryMedia.played().length != 0) {
mediaSeeked = true;
}
});
// When finished seeking, we sync all medias...
primaryMedia.on("seeked", function() {
if (primaryMedia.paused()) {
sync();
} else {
primaryMedia.pause();
}
});
for (i = 0; i < allMedias.length ; i++) {
allMedias[i].on("waiting", function() {
// If one of the medias is 'waiting', we must sync
if (!primaryMedia.seeking() && !mediaSyncing) {
mediaSyncing = true;
// Pause the master video, causing to pause and sync all videos...
logger.debug("==Syncing videos");
primaryMedia.pause();
}
});
allMedias[i].on("canplay", function() {
if (mediaSyncing || mediaSeeked) {
var allMediasAreReady = true;
for (i = 0; i < allMedias.length ; i++) {
allMediasAreReady &= (allMedias[i].media.readyState == 4);
}
if (allMediasAreReady) {
mediaSyncing = false;
mediaSeeked = false;
// Play the master video, causing to play all videos...
logger.debug("==Resuming");
primaryMedia.play();
}
}
});
}
};
function sync() {
for (var i = 0; i < secondaryMedias.length ; i++) {
if (secondaryMedias[i].media.readyState > 1) {
secondaryMedias[i].pause();
// Set the current time will fire a "canplay" event to tell us that the video can be played...
secondaryMedias[i].currentTime(primaryMedia.currentTime());
}
}
};
function setMediaListeners() {
// Solo media
primaryMedia = Popcorn("#video");
primaryMedia.on("seeking", function() {
if (primaryMedia.played().length != 0) {
mediaSeeked = true;
}
});
// When finished seeking
primaryMedia.on("seeked", function() {
if (!primaryMedia.paused()) {
primaryMedia.pause();
}
});
primaryMedia.on("canplay", function() {
if (mediaSeeked) {
mediaSeeked = false;
logger.debug("==Resuming");
primaryMedia.play();
}
});
};
// Hack for mobile devices that not load media unless they are visible
function forceMediaEvents() {
// When the medias were loaded
if (mediaReady) return;
if (hasVideo) {
logger.debug("==Forcing video/captions ready event");
document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'video'}));
document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'captions'}));
} else {
logger.debug("==Forcing audio ready event");
document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'audio'}));
}
if (hasDeskshare) {
logger.debug("==Forcing deskshare ready event");
document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'deskshare'}));
}
}
document.addEventListener("DOMContentLoaded", function() {
logger.info("==DOM content loaded");
loadMetadata();
checkMedias();
document.dispatchEvent(new CustomEvent('content-ready', {'detail': 'dom'}));
}, false);
function loadPlayback() {
logger.info("==Loading playback");
var appName = navigator.appName;
var appVersion = navigator.appVersion;
// Hack to force mobile devices to show the playback when media do not load while hidden
var isMobile = mobileAndTabletCheck();
if (isMobile) {
logger.info("==Device is mobile");
setTimeout(forceMediaEvents, mobileTimeout);
}
startLoadingBar();
loadData();
loadBranding();
if (hasVideo) {
$("#audio-area").attr("style", "display:none;");
loadVideo();
} else {
$("#video-area").attr("style", "display:none;");
loadAudio();
}
// load up the acorn controls
logger.info("==Loading acorn media player");
$('#video').acornMediaPlayer({
theme: 'bigbluebutton',
volumeSlider: 'vertical'
});
$('#video').on("swap", function() {
swapVideoPresentation();
});
if (hasDeskshare) {
loadDeskshare();
} else {
setMediaListeners();
logger.debug("==Recording has no deskshare");
document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'deskshare'}));
}
};
function isMediaReady(media) {
if (media !== undefined && media !== null && media.readyState === 4) {
return true;
}
return false;
};
function checkLoadedMedia() {
// We use the video tag both for audio or video
let media = $('#video')[0];
if (isMediaReady(media)) {
if (hasVideo) {
document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'video'}));
} else {
document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'audio'}));
}
} else {
setTimeout(checkLoadedMedia, mediaCheckInterval);
}
};
function checkLoadedDeskshare() {
let deskshare = $('#deskshare-video')[0];
if (isMediaReady(deskshare)) {
document.dispatchEvent(new CustomEvent('media-ready', {'detail': 'deskshare'}));
} else {
setTimeout(checkLoadedDeskshare, mediaCheckInterval);
}
};
var secondsToWait = 0;
function addTime(time){
if (secondsToWait === 0) {
Popcorn('#video').pause();
window.setTimeout("Tick()", 1000);
}
secondsToWait += time;
};
function loadBranding() {
let logo = undefined;
let copyright = undefined;
let metadata = metadataXMLContent.getElementsByTagName("meta");
if (metadata.length > 0) {
metadata = metadata[0];
let logoCandidates = metadata.getElementsByTagName("playback-logo-url");
if (logoCandidates.length > 0) {
logo = logoCandidates[0].textContent;
}
let copyrightCandidates = metadata.getElementsByTagName("playback-copyright");
if (copyrightCandidates.length > 0) {
copyright = copyrightCandidates[0].textContent;
}
}
loadLogo(logo);
loadCopyright(copyright);
};
function loadLogo(logo) {
let logoURL = typeof logo !== 'undefined' ? logo : defaultLogo;
logger.info("==Loaded logo from", logoURL);
$("#slide").css('background-image', 'url(' + logoURL + ')');
document.getElementById("load-img").src = logoURL;
}
function loadCopyright(copyright) {
let copyrightText = typeof copyright !== 'undefined' ? copyright : defaultCopyright;
$("#copyright").html(copyrightText);
};
function Tick() {
if (secondsToWait <= 0 || !($("#accEnabled").is(':checked'))) {
secondsToWait = 0;
Popcorn('#video').play();
$('#countdown').html(""); // Remove the timer
return;
}
secondsToWait -= 1;
$('#countdown').html(secondsToWait);
window.setTimeout("Tick()", 1000);
};
// Swap the position of the DOM elements `elm1` and `elm2`.
function swapElements(elm1, elm2) {
var parent1, next1,
parent2, next2;
parent1 = elm1.parentNode;
next1 = elm1.nextSibling;
parent2 = elm2.parentNode;
next2 = elm2.nextSibling;
parent1.insertBefore(elm2, next1);
parent2.insertBefore(elm1, next2);
resizeComponents();
};
// Swaps the positions of the presentation and the video
function swapVideoPresentation() {
var pop = Popcorn("#video");
var wasPaused = pop.paused();
var mainSectionChild = $("#main-section").children("[data-swap]");
var sideSectionChild = $("#side-section").children("[data-swap]");
swapElements(mainSectionChild[0], sideSectionChild[0]);
if (!wasPaused) {
pop.play();
}
// Hide the cursor so it doesn't appear in the wrong place (e.g. on top of the video)
// if the cursor is currently being useful, he we'll be redrawn automatically soon
showCursor(false);
// Wait for the svg with the slides to be fully loaded, then restore slides state and resize them
function checkSVGLoaded() {
var done = false;
var svg = document.getElementsByTagName("object")[0];
if (svg !== undefined && svg !== null && currentImage && svg.getSVGDocument('svgfile')) {
var img = svg.getSVGDocument('svgfile').getElementById(currentImage.getAttribute("id"));
if (img !== undefined && img !== null) {
restoreSlidesState(img);
done = true;
}
}
if (!done) {
setTimeout(checkSVGLoaded, 50);
}
};
checkSVGLoaded();
};
function restoreSlidesState(img) {
logger.debug("==Restoring slide state");
// Set the current image as visible
img.style.visibility = "visible";
resizeSlide();
restoreCanvas();
var isPaused = Popcorn("#video").paused();
if (isPaused) {
restoreViewBoxSize();
restoreCursor(img);
}
};
function restoreCanvas() {
logger.debug("==Restoring canvas");
var numCurrent = currentImageId.substr(5);
var currentCanvas;
currentCanvas = getSVGElementById("canvas" + numCurrent);
if (currentCanvas !== null) {
currentCanvas.setAttribute("display", "");
}
};
function restoreViewBoxSize() {
logger.debug("==Restoring view box");
var t = Popcorn("#video").currentTime().toFixed(1);
var vboxVal = getViewboxAtTime(t);
if (vboxVal !== undefined) {
setViewBox(vboxVal);
}
};
function restoreCursor(img) {
logger.debug("==Restoring cursor");
var imageWidth = parseInt(img.getAttribute("width"), 10);
var imageHeight = parseInt(img.getAttribute("height"), 10);
showCursor(true);
drawCursor(parseFloat(currentCursorVal[0]) / (imageWidth/2), parseFloat(currentCursorVal[1]) / (imageHeight/2));
};
// Manually resize some components we can't properly resize just using css.
// Mostly the components in the side-section, that has more than one component that
// need to fill 100% height.
function resizeComponents() {
logger.info("==Resizing components");
let availableHeight = $("body").height();
if (hasVideo) {
availableHeight -= $("#video-area .acorn-controls").outerHeight(true);
} else {
availableHeight -= $("#audio-area .acorn-controls").outerHeight(true);
}
availableHeight -= $("#navbar").outerHeight(true);
if (window.innerHeight > window.innerWidth) {
logger.debug("==Portrait mode");
let mainSectionHeight = availableHeight * 0.6; // 60% for top bar
$("#main-section").outerHeight(mainSectionHeight);
let sideSectionHeight = availableHeight - mainSectionHeight;
$("#side-section").outerHeight(sideSectionHeight);
$("#chat-area").innerHeight(sideSectionHeight);
} else {
logger.debug("==Landscape mode");
$("#main-section").outerHeight(availableHeight);
$("#side-section").outerHeight(availableHeight);
logger.debug("==Data-swap children", $("#side-section").children("[data-swap]"));
let chatHeight = availableHeight - $("#side-section").children("[data-swap]").outerHeight(true);
$("#chat-area").innerHeight(chatHeight);
}
};
document.addEventListener('content-ready', function(event) {
logger.debug("==Content ready", event.detail);
if (contentReady) return;
switch(event.detail) {
case 'dom':
DOMLoaded = true;
break;
case 'metadata':
metadataLoaded = true;
break;
case 'medias-checked':
mediasChecked = true;
break;
default:
logger.warn("==Unhandled content-ready event", event.detail);
}
if (DOMLoaded && metadataLoaded && mediasChecked) {
loadPlayback();
document.dispatchEvent(new CustomEvent('playback-ready', {'detail': 'content'}));
}
}, false);
document.addEventListener('playback-ready', function(event) {
logger.debug("==Playback ready", event.detail);
switch(event.detail) {
case 'data':
dataReady = true;
break;
case 'media':
mediaReady = true;
break;
case 'content':
contentReady = true;
break;
default:
logger.warn("==Unhandled playback-ready event", event.detail);
}
if (dataReady && mediaReady && contentReady) {
runPopcorn();
if (firstLoad) {
initPopcorn();
}
}
}, false);