Merge branch 'development' into LTS_development

This commit is contained in:
Thulinma 2017-03-06 11:53:52 +01:00
commit 25ddaa3595
13 changed files with 639 additions and 538 deletions

1
.gitignore vendored
View file

@ -57,4 +57,5 @@ rules.ninja
.ninja_log
.ninja_deps
aes_ctr128
/embed/testing

View file

@ -214,6 +214,7 @@ set(libSources
${SOURCE_DIR}/lib/vorbis.cpp
${SOURCE_DIR}/lib/triggers.cpp
)
########################################
# MistLib - Build #
########################################
@ -399,6 +400,7 @@ add_executable(MistOutHTTP
generated/polytrope.js.h
generated/dashjs.js.h
generated/videojs.js.h
generated/img.js.h
generated/playerdash.js.h
generated/playervideo.js.h
generated/core.js.h
@ -498,6 +500,10 @@ add_custom_command(OUTPUT generated/videojs.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/videojs.js video_js generated/videojs.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/videojs.js
)
add_custom_command(OUTPUT generated/img.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/img.js img_js generated/img.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/img.js
)
add_custom_command(OUTPUT generated/playerdash.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/players/dash.js playerdash_js generated/playerdash.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/players/dash.js

View file

@ -27,10 +27,10 @@ MistPlayer.prototype.sendEvent = function(type,message,target) {
return true;
}
MistPlayer.prototype.addlog = function(msg) {
this.sendEvent('log',msg,this.target);
this.sendEvent('log',msg,this.element);
}
MistPlayer.prototype.adderror = function(msg) {
this.sendEvent('error',msg,this.target);
this.sendEvent('error',msg,this.element);
}
MistPlayer.prototype.build = function () {
this.addlog('Error in player implementation');
@ -40,6 +40,32 @@ MistPlayer.prototype.build = function () {
err.className = 'error';
return err;
}
MistPlayer.prototype.timer = {
timers: {},
add: function(callback,delay){
var me = this;
var i = setTimeout(function(){
delete me.timers[i];
callback();
},delay);
this.timers[i] = {
delay: delay,
callback: callback
};
return i;
},
remove: function(i){
clearTimeout(i);
delete this.timers[i];
},
clear: function(){
for (var i in this.timers) {
clearTimeout(i);
}
this.timers = {};
}
};
//creates the player element, including custom functions
MistPlayer.prototype.getElement = function(tag){
var ele = document.createElement(tag);
@ -164,14 +190,15 @@ MistPlayer.prototype.buildMistControls = function(){
str.push(('0'+secs).slice(-2));
return str.join(':');
}
var timestampValue, bar;
function whilePlaying() {
timestampValue.nodeValue = formatTime(ele.currentTime);
bar.style.width = ((ele.currentTime-ele.startTime)/ele.duration*100)+'%';
setTimeout(function(){
me.timer.add(function(){
if (!ele.paused) {
whilePlaying();
}
},0.1e3);
},0.5e3);
};
function whileLivePlaying(track) {
@ -180,11 +207,11 @@ MistPlayer.prototype.buildMistControls = function(){
timestampValue.nodeValue = formatTime((playtime + track.lastms)/1e3);
setTimeout(function(){
me.timer.add(function(){
if (!ele.paused) {
whileLivePlaying(track);
}
},0.1e3);
},0.5e3);
};
@ -213,7 +240,12 @@ MistPlayer.prototype.buildMistControls = function(){
play.setAttribute('data-state','paused');
play.onclick = function(){
if (ele.paused) {
me.play();
if (options.live) {
me.load();
}
else {
me.play();
}
}
else {
me.pause();
@ -255,7 +287,7 @@ MistPlayer.prototype.buildMistControls = function(){
bar.style.left = (ele.startTime/ele.duration*100)+'%';
};
progress.ondragstart = function() { return false; };
var bar = document.createElement('div');
bar = document.createElement('div');
progress.appendChild(bar);
bar.className = 'bar';
var buffers = [];
@ -268,6 +300,11 @@ MistPlayer.prototype.buildMistControls = function(){
ele.addEventListener('seeking',function(){
me.target.setAttribute('data-loading','');
});
ele.addEventListener('seeked',function(){
me.target.removeAttribute('data-loading');
bar.style.left = (ele.currentTime/ele.duration*100)+'%';
//TODO reset lasttime
});
ele.addEventListener('canplay',function(){
me.target.removeAttribute('data-loading');
});
@ -282,7 +319,7 @@ MistPlayer.prototype.buildMistControls = function(){
var timestamp = document.createElement('div');
controls.appendChild(timestamp);
timestamp.className = 'button timestamp';
var timestampValue = document.createTextNode('-:--');
timestampValue = document.createTextNode('-:--');
timestamp.title = 'Time';
timestamp.appendChild(timestampValue);
@ -298,12 +335,15 @@ MistPlayer.prototype.buildMistControls = function(){
var pos0 = sound.getBoundingClientRect().top - parseInt(style.borderTopWidth,10);
var perc = (ypos - pos0 * zoom) / sound.offsetHeight / zoom;
var secs = Math.max(0,perc) * ele.duration;
return 1 - Math.min(1,Math.max(0,perc));
perc = 1 - Math.min(1,Math.max(0,perc)); //linear range between 0 and 1
perc = 1 - Math.pow((1-perc),1/2); //transform to quadratic range between 0 and 1
return perc;
}
volume.className = 'volume';
sound.title = 'Volume';
if ('mistVolume' in localStorage) {
if (('localStorage' in window) && (localStorage != null) && ('mistVolume' in localStorage)) {
ele.volume = localStorage['mistVolume'];
volume.style.height = ele.volume*100+'%';
}
@ -313,6 +353,7 @@ MistPlayer.prototype.buildMistControls = function(){
};
var mouseup = function(e){
document.removeEventListener('mousemove',mousemove);
controls.removeEventListener('mousemove',mousemove);
document.removeEventListener('touchmove',mousemove);
document.removeEventListener('mouseup',mouseup);
document.removeEventListener('touchend',mouseup);
@ -322,6 +363,7 @@ MistPlayer.prototype.buildMistControls = function(){
catch (e) {}
};
document.addEventListener('mousemove',mousemove);
controls.addEventListener('mousemove',mousemove); //this one is added because the controls hiding mechanism stops propagation to the document
document.addEventListener('touchmove',mousemove);
document.addEventListener('mouseup',mouseup);
document.addEventListener('touchend',mouseup);
@ -409,6 +451,9 @@ MistPlayer.prototype.buildMistControls = function(){
name = tracks[i][j].lang;
o.setAttribute('data-lang',tracks[i][j].lang);
}
else if ('desc' in tracks[i][j]) {
name = tracks[i][j].desc;
}
else {
name = 'Track '+(Number(j)+1);
}
@ -452,7 +497,7 @@ MistPlayer.prototype.buildMistControls = function(){
}
if (i == 'subtitle') {
s.value = 0;
if ('mistSubtitle' in localStorage) {
if (('localStorage' in window) && (localStorage != null) && ('mistSubtitle' in localStorage)) {
var option = s.querySelector('[data-lang="'+localStorage['mistSubtitle']+'"]');
if (option) {
s.value = option.value;
@ -508,9 +553,13 @@ MistPlayer.prototype.buildMistControls = function(){
});
ele.addEventListener('ended',function(){
play.setAttribute('data-state','paused');
if (options.live) {
me.load();
}
});
ele.addEventListener('volumechange',function(){
volume.style.height = ele.volume*100+'%';
var vol = 1 - Math.pow(1-ele.volume,2); //transform back from quadratic
volume.style.height = vol*100+'%';
if (ele.volume == 0) {
speaker.setAttribute('data-muted','');
}
@ -601,33 +650,56 @@ MistPlayer.prototype.askNextCombo = function(msg){
var me = this;
if (me.errorstate) { return; }
me.errorstate = true;
me.addlog('Showing error window');
me.report({
type: 'playback',
warn: 'Showing error window',
msg: msg
});
//show the error
var err = document.createElement('div');
var msgnode = document.createTextNode(msg ? msg : 'Player or stream error detected');
err.appendChild(msgnode);
err.className = 'error';
var button = document.createElement('button');
var t = document.createTextNode('Try next source/player');
button.appendChild(t);
err.appendChild(button);
button.onclick = function(){
me.nextCombo();
err.style.position = 'absolute';
err.style.width = '100%';
err.style['margin-left'] = 0;
this.target.appendChild(err);
this.element.style.opacity = '0.2';
//if there is a next source/player, show a button to activate it
var opts = this.mistplaySettings.options;
if (mistCheck(mistvideo[this.mistplaySettings.streamname],opts)) {
var button = document.createElement('button');
var t = document.createTextNode('Try next source/player');
button.appendChild(t);
err.appendChild(button);
button.onclick = function(){
me.nextCombo();
}
}
//show a button to reload with the current settings
var button = document.createElement('button');
var i = document.createElement('div'); //a css countdown clock for 10sec
i.className = 'countdown';
button.appendChild(i);
var t = document.createTextNode('Reload this player');
button.appendChild(t);
err.appendChild(button);
button.onclick = function(){
me.reload();
}
err.style.position = 'absolute';
err.style.top = 0;
err.style.width = '100%';
err.style['margin-left'] = 0;
this.target.appendChild(err);
this.element.style.opacity = '0.2';
//after 20 seconds, reload the player
err.timeOut = me.timer.add(function(){
me.report({
type: 'playback',
warn: 'Automatically reloaded the current player after playback error'
});
button.click();
},20e3);
};
MistPlayer.prototype.cancelAskNextCombo = function(){
if (this.errorstate) {
@ -637,12 +709,17 @@ MistPlayer.prototype.cancelAskNextCombo = function(){
var err = this.target.querySelector('.error');
if (err) {
this.target.removeChild(err);
if (err.timeOut) { this.timer.remove(err.timeOut); }
}
}
};
MistPlayer.prototype.reload = function(){
this.unload();
mistPlay(this.mistplaySettings.streamname,this.mistplaySettings.options);
this.report({
type: 'init',
info: 'Reloading player'
});
};
MistPlayer.prototype.nextCombo = function(){
this.unload();
@ -653,8 +730,6 @@ MistPlayer.prototype.nextCombo = function(){
///send information back to mistserver
///\param msg object containing the information to report
MistPlayer.prototype.report = function(msg) {
return false; ///\todo Remove this when the backend reporting function has been coded
///send a http post request
///\param url (string) url to send to
@ -690,6 +765,10 @@ MistPlayer.prototype.report = function(msg) {
msg.userinfo.time = Math.round(((new Date) - this.options.initTime)/1e3); //seconds since the info js was loaded
}
this.sendEvent('report',JSON.stringify(msg),this.element);
return false; ///\todo Remove this when the backend reporting function has been coded
try {
httpPost(this.options.host+'/report',{
report: JSON.stringify(msg)
@ -698,12 +777,100 @@ MistPlayer.prototype.report = function(msg) {
catch (e) { }
}
MistPlayer.prototype.unload = function(){
this.addlog('Unloading..');
if (('pause' in this) && (this.pause)) { this.pause(); }
if ('updateSrc' in this) { this.updateSrc(''); }
//delete this.element;
if ('updateSrc' in this) {
this.updateSrc('');
this.element.load(); //dont use this.load() to avoid interrupting play/pause
}
this.timer.clear();
this.target.innerHTML = '';
};
function mistCheck(streaminfo,options,embedLog) {
if (typeof embedLog != 'function') { embedLog = function(){}; }
embedLog('Checking available players..');
var source = false;
var mistPlayer = false;
if (options.startCombo) {
options.startCombo.started = {
player: false,
source: false
};
}
function checkPlayer(p_shortname) {
if ((options.startCombo) && (!options.startCombo.started.player)) {
if (p_shortname != options.startCombo.player) { return false; }
else {
options.startCombo.started.player = true;
}
}
embedLog('Checking '+mistplayers[p_shortname].name+' (priority: '+mistplayers[p_shortname].priority+') ..');
//loop over the available sources and check if this player can play it
var loop;
if (options.forceSource) {
loop = [streaminfo.source[options.forceSource]];
}
else {
loop = streaminfo.source;
}
for (var s in loop) {
if ((options.startCombo) && (!options.startCombo.started.source)) {
if (s == options.startCombo.source) {
options.startCombo.started.source = true;
}
continue;
}
if ((options.forceType) && (loop[s].type != options.forceType)) {
continue;
}
if (mistplayers[p_shortname].isMimeSupported(loop[s].type)) {
//this player supports this mime
if (mistplayers[p_shortname].isBrowserSupported(loop[s].type,loop[s],options,streaminfo,embedLog)) {
//this browser is supported
embedLog('Found a working combo: '+mistplayers[p_shortname].name+' with '+loop[s].type+' @ '+loop[s].url);
mistPlayer = p_shortname;
source = loop[s];
source.index = s;
return p_shortname;
}
else {
embedLog('This browser does not support '+loop[s].type+' via '+loop[s].url);
}
}
}
return false;
}
if (options.forcePlayer) {
checkPlayer(options.forcePlayer);
}
else {
//sort the players
var sorted = Object.keys(mistplayers);
sorted.sort(function(a,b){
return mistplayers[a].priority - mistplayers[b].priority;
});
for (var n in sorted) {
var p_shortname = sorted[n];
if (checkPlayer(p_shortname)) { break; }
}
}
return ((source && mistPlayer) ? {
source: source,
mistPlayer: mistPlayer
} : false);
}
/////////////////////////////////////////////////
// SELECT AND ADD A VIDEO PLAYER TO THE TARGET //
/////////////////////////////////////////////////
@ -711,13 +878,13 @@ MistPlayer.prototype.unload = function(){
function mistPlay(streamName,options) {
var protoplay = new MistPlayer();
protoplay.streamname = streamName;
function embedLog(msg) {
protoplay.streamname = streamName;
var embedLog = function(msg) {
protoplay.sendEvent('log',msg,options.target);
}
};
function mistError(msg) {
var info = {};
if ((typeof mistvideo != 'undefined') && ('streamName' in mistvideo)) { info = mistvideo[streamName]; }
if ((typeof mistvideo != 'undefined') && (streamName in mistvideo)) { info = mistvideo[streamName]; }
var displaymsg = msg;
if ('on_error' in info) { displaymsg = info.on_error; }
@ -745,12 +912,17 @@ function mistPlay(streamName,options) {
var local = options;
var global = (typeof mistoptions == 'undefined' ? {} : mistoptions);
var options = {
host: null,
autoplay: true,
controls: true,
loop: false,
poster: null,
callback: false
host: null, //override mistserver host (default is the host that player.js is loaded from)
autoplay: true, //start playing when loaded
controls: true, //show controls (MistControls when available)
loop: false, //don't loop when the stream has finished
poster: null, //don't show an image before the stream has started
callback: false, //don't call a function when the player has finished building
streaminfo: false, //don't use this streaminfo but collect it from the mistserverhost
startCombo: false, //start looking for a player/source match at the start
forceType: false, //don't force a mimetype
forcePlayer: false, //don't force a player
forceSource: false //don't force a source
};
for (var i in global) {
options[i] = global[i];
@ -763,9 +935,19 @@ function mistPlay(streamName,options) {
mistError('MistServer host undefined.');
return;
}
if (!options.target) {
mistError('Target container undefined');
return;
}
options.target.setAttribute('data-loading','');
var classes = options.target.className.split(' ');
if (classes.indexOf('mistvideo') == -1) {
classes.push('mistvideo');
options.target.className = classes.join(' ');
}
//check if the css is loaded
if (!document.getElementById('mist_player_css')) {
var css = document.createElement('link');
@ -781,28 +963,11 @@ function mistPlay(streamName,options) {
}
}
//get info js
var info = document.createElement('script');
info.src = options.host+'/info_'+encodeURIComponent(streamName)+'.js';
embedLog('Retrieving stream info from '+info.src);
document.head.appendChild(info);
info.onerror = function(){
options.target.innerHTML = '';
options.target.removeAttribute('data-loading');
mistError('Error while loading stream info.');
protoplay.report({
type: 'init',
error: 'Failed to load '+info.src
});
}
info.onload = function(){
function onstreaminfo() {
options.target.innerHTML = '';
options.target.removeAttribute('data-loading');
embedLog('Stream info was loaded succesfully');
//clean up info script
document.head.removeChild(info);
//get streaminfo data
var streaminfo = mistvideo[streamName];
//embedLog('Stream info contents: '+JSON.stringify(streaminfo));
@ -817,139 +982,36 @@ function mistPlay(streamName,options) {
return;
}
//sort the sources by priority and mime, but prefer HTTPS
streaminfo.source.sort(function(a,b){
return (b.priority - a.priority) || a.type.localeCompare(b.type) || b.url.localeCompare(a.url);
});
var mistPlayer = false;
var source;
var forceType = false;
if (('forceType' in options) && (options.forceType)) {
embedLog('Forcing '+options.forceType);
forceType = options.forceType;
}
var forceSource = false;
if (('forceSource' in options) && (options.forceSource)) {
forceSource = options.forceSource;
forceType = streaminfo.source[forceSource].type;
embedLog('Forcing source '+options.forceSource+': '+forceType+' @ '+streaminfo.source[forceSource].url);
options.forceType = streaminfo.source[options.forceSource].type;
embedLog('Forcing source '+options.forceSource+': '+options.forceType+' @ '+streaminfo.source[options.forceSource].url);
}
var forceSupportCheck = false;
if (('forceSupportCheck' in options) && (options.forceSupportCheck)) {
embedLog('Forcing a full support check');
forceSupportCheck = true;
}
var forcePlayer = false;
if (('forcePlayer' in options) && (options.forcePlayer)) {
if (options.forcePlayer in mistplayers) {
embedLog('Forcing '+mistplayers[options.forcePlayer].name);
forcePlayer = options.forcePlayer;
}
else {
embedLog('The forced player ('+options.forcePlayer+') isn\'t known, ignoring. Possible values are: '+Object.keys(mistplayers).join(', '));
options.forcePlayer = false;
}
}
var startCombo = false;
if ('startCombo' in options) {
startCombo = options.startCombo;
startCombo.started = {
player: false,
source: false
};
embedLog('Selecting a new player/source combo, starting after '+mistplayers[startCombo.player].name+' with '+streaminfo.source[startCombo.source].type+' @ '+streaminfo.source[startCombo.source].url);
if (('startCombo' in options) && (options.startCombo)) {
embedLog('Selecting a new player/source combo, starting after '+mistplayers[options.startCombo.player].name+' with '+streaminfo.source[options.startCombo.source].type+' @ '+streaminfo.source[options.startCombo.source].url);
}
embedLog('Checking available players..');
//sort the sources by simultracks, priority and mime, but prefer HTTPS
streaminfo.source.sort(function(a,b){
return (b.simul_tracks - a.simul_tracks) || (b.priority - a.priority) || a.type.localeCompare(b.type) || b.url.localeCompare(a.url);
});
var source = false;
function checkPlayer(p_shortname) {
if ((startCombo) && (!startCombo.started.player)) {
if (p_shortname != startCombo.player) { return false; }
else {
startCombo.started.player = true;
}
}
embedLog('Checking '+mistplayers[p_shortname].name+' (priority: '+mistplayers[p_shortname].priority+') ..');
streaminfo.working[p_shortname] = [];
if (forceType) {
if ((mistplayers[p_shortname].mimes.indexOf(forceType) > -1) && (checkMime(p_shortname,forceType))) {
return p_shortname;
}
}
else {
for (var m in mistplayers[p_shortname].mimes) {
if ((checkMime(p_shortname,mistplayers[p_shortname].mimes[m])) && (!forceSupportCheck)) {
return p_shortname;
}
}
}
return false;
}
function checkMime(p_shortname,mime) {
var loop;
if (forceSource) {
loop = [streaminfo.source[forceSource]];
}
else {
loop = streaminfo.source;
}
var broadcast = false;
for (var s in loop) {
if (loop[s].type == mime) {
broadcast = true;
if ((startCombo) && (!startCombo.started.source)) {
if (s == startCombo.source) {
startCombo.started.source = true;
}
continue;
}
if (mistplayers[p_shortname].isBrowserSupported(mime,loop[s],options,streaminfo,embedLog)) {
embedLog('Found a working combo: '+mistplayers[p_shortname].name+' with '+mime+' @ '+loop[s].url);
streaminfo.working[p_shortname].push(mime);
if (!source) {
mistPlayer = p_shortname;
source = loop[s];
source.index = s;
}
if (!forceSupportCheck) {
return source;
}
}
else {
embedLog('This browser does not support '+mime);
}
}
}
if (!broadcast) {
embedLog('Mist doesn\'t broadcast '+mime);
}
return false;
}
streaminfo.working = {};
if (forcePlayer) {
checkPlayer(forcePlayer);
}
else {
//sort the players
var sorted = Object.keys(mistplayers);
sorted.sort(function(a,b){
return mistplayers[a].priority - mistplayers[b].priority;
});
for (var n in sorted) {
var p_shortname = sorted[n];
if (checkPlayer(p_shortname)) { break; }
}
}
var r = mistCheck(streaminfo,options,embedLog);
var mistPlayer = r.mistPlayer;
var source = r.source;
options.target.innerHTML = '';
if (mistPlayer) {
//create the options to send to the player
@ -964,7 +1026,7 @@ function mistPlay(streamName,options) {
//pass player options and handle defaults
playerOpts.autoplay = options.autoplay;
playerOpts.controls = options.controls;
playerOpts.loop = options.loop;
playerOpts.loop = (playerOpts.live ? false : options.loop);
playerOpts.poster = options.poster;
function calcSize() {
@ -1026,6 +1088,7 @@ function mistPlay(streamName,options) {
if (player.setTracks(false)) {
//gather track info
//tracks
var tracks = {
video: [],
audio: [],
@ -1036,20 +1099,26 @@ function mistPlay(streamName,options) {
var skip = false;
switch (t.type) {
case 'video':
t.desc = ['['+t.codec+']',t.width+'x'+t.height,Math.round(t.bps/1024)+'kbps',t.fpks/1e3+'fps',t.lang];
t.desc = [t.width+'x'+t.height,/*Math.round(t.bps/1024)+'kbps',*/t.fpks/1e3+'fps',t.codec];
if (t.lang) {
t.desc.unshift(t.lang);
}
break;
case 'audio':
t.desc = ['['+t.codec+']',t.channels+' channels',Math.round(t.bps/1024)+'kbps',t.rate+'Hz',t.lang];
t.desc = [(t.channels == 2 ? 'Stereo' : (t.channels == 1 ? 'Mono' : t.channels+' channels')),/*Math.round(t.bps/1024)+'kbps',*/Math.round(t.rate/1000)+'kHz',t.codec];
if (t.lang) {
t.desc.unshift(t.lang);
}
break;
case 'subtitle':
t.desc = ['['+t.codec+']',t.lang];
t.desc = [t.lang,t.codec];
break;
default:
skip = true;
break;
}
if (skip) { continue; }
t.desc = t.desc.join(', ');
t.desc = t.desc.join(' ');
tracks[t.type].push(t);
}
player.tracks = tracks;
@ -1081,7 +1150,7 @@ function mistPlay(streamName,options) {
//build the player
player.mistplaySettings = {
streamname: streamName,
options: local,
options: options,
startCombo: {
player: mistPlayer,
source: source.index
@ -1094,7 +1163,7 @@ function mistPlay(streamName,options) {
catch (e) {
//show the next player/reload buttons if there is an error in the player build code
options.target.appendChild(player.element);
player.askNextCombo('Error while building player');
player.askNextCombo('Error while building player: '+e.stack);
throw e;
player.report({
type: 'init',
@ -1112,13 +1181,12 @@ function mistPlay(streamName,options) {
if (player.setTracks(false)) {
player.onready(function(){
//player.setTracks(usetracks);
if ('setTracks' in options) { player.setTracks(options.setTracks); }
});
}
//monitor for errors
element.checkStalledTimeout = false;
element.checkProgressTimeout = false;
element.sendPingTimeout = setInterval(function(){
if (player.paused) { return; }
player.report({
@ -1130,8 +1198,7 @@ function mistPlay(streamName,options) {
player.askNextCombo('The player has thrown an error');
var r = {
type: 'playback',
error: 'The player has thrown an error',
origin: e.target.outerHTML.slice(0,e.target.outerHTML.indexOf('>')+1),
error: 'The player has thrown an error'
};
if ('readyState' in player.element) {
r.readyState = player.element.readyState;
@ -1139,85 +1206,90 @@ function mistPlay(streamName,options) {
if ('networkState' in player.element) {
r.networkState = player.element.networkState;
}
if (('error' in player.element) && ('code' in player.element.error)) {
if (('error' in player.element) && (player.element.error) && ('code' in player.element.error)) {
r.code = player.element.error.code;
}
player.report(r);
});
element.checkStalledTimeout = false;
var stalled = function(e){
if (element.checkStalledTimeout) { return; }
element.checkStalledTimeout = setTimeout(function(){
if (player.paused) { return; }
var curpos = player.element.currentTime;
if (curpos == 0) { return; }
element.checkStalledTimeout = player.timer.add(function(){
if ((player.paused) || (curpos != player.element.currentTime)) { return; }
player.askNextCombo('Playback has stalled');
player.report({
'type': 'playback',
'warn': 'Playback was stalled for > 10 sec'
'warn': 'Playback was stalled for > 30 sec'
});
},10e3);
},30e3);
};
element.addEventListener('stalled',stalled,true);
element.addEventListener('waiting',stalled,true);
var progress = function(e){
if (element.checkStalledTimeout) {
clearTimeout(element.checkStalledTimeout);
element.checkStalledTimeout = false;
player.cancelAskNextCombo();
}
};
//element.addEventListener('progress',progress,true);
//element.addEventListener('playing',progress,true);
element.addEventListener('play',function(){
player.paused = false;
if ((!element.checkProgressTimeout) && (player.element) && ('currentTime' in player.element)) {
//check if the progress made is equal to the time spent
var lasttime = player.element.currentTime;
element.checkProgressTimeout = setInterval(function(){
var newtime = player.element.currentTime;
progress();
if (newtime == 0) { return; }
var progressed = newtime - lasttime;
lasttime = newtime;
if (progressed == 0) {
var msg = 'There should be playback but nothing was played';
var r = {
type: 'playback',
warn: msg
};
player.addlog(msg);
if ('readyState' in player.element) {
r.readyState = player.element.readyState;
}
if ('networkState' in player.element) {
r.networkState = player.element.networkState;
}
if (('error' in player.element) && (player.element.error) && ('code' in player.element.error)) {
r.code = player.element.error.code;
}
player.report(r);
player.askNextCombo('No playback');
if ('load' in player.element) { player.element.load(); }
return;
}
if (playerOpts.live) {
element.checkProgressTimeout = false;
var progress = function(e){
if (element.checkStalledTimeout) {
player.timer.remove(element.checkStalledTimeout);
element.checkStalledTimeout = false;
player.cancelAskNextCombo();
if (progressed < 1) {
var msg = 'It seems playback is lagging (progressed '+Math.round(progressed*100)/100+'/2s)'
player.addlog(msg);
player.report({
type: 'playback',
warn: msg
});
return;
}
},2e3);
}
},true);
element.addEventListener('pause',function(){
player.paused = true;
if (element.checkProgressTimeout) {
clearInterval(element.checkProgressTimeout);
element.checkProgressTimeout = false;
}
},true);
}
};
//element.addEventListener('progress',progress,true); //sometimes, there is progress but no playback
element.addEventListener('playing',progress,true);
element.addEventListener('play',function(){
player.paused = false;
if ((!element.checkProgressTimeout) && (player.element) && ('currentTime' in player.element)) {
//check if the progress made is equal to the time spent
var lasttime = player.element.currentTime;
element.checkProgressTimeout = setInterval(function(){
var newtime = player.element.currentTime;
var progress = newtime - lasttime;
lasttime = newtime;
if (progress < 0) { return; } //its probably a looping VOD or we've just seeked
if (progress == 0) {
var msg = 'There should be playback but nothing was played';
var r = {
type: 'playback',
warn: msg
};
player.addlog(msg);
if ('readyState' in player.element) {
r.readyState = player.element.readyState;
}
if ('networkState' in player.element) {
r.networkState = player.element.networkState;
}
if (('error' in player.element) && (player.element.error) && ('code' in player.element.error)) {
r.code = player.element.error.code;
}
player.report(r);
player.askNextCombo('No playback');
return;
}
player.cancelAskNextCombo();
if (progress < 20) {
var msg = 'It seems playback is lagging (progressed '+Math.round(progress*100)/100+'/30s)'
player.addlog(msg);
player.report({
type: 'playback',
warn: msg
});
return;
}
},30e3);
}
},true);
element.addEventListener('pause',function(){
player.paused = true;
if (element.checkProgressTimeout) {
clearInterval(element.checkProgressTimeout);
element.checkProgressTimeout = false;
}
},true);
}
if (player.resize) {
//monitor for resizes and fire if needed
@ -1239,8 +1311,8 @@ function mistPlay(streamName,options) {
}
else if (('source' in streaminfo) && (streaminfo.source.length)) {
var str = 'Could not find a compatible player and protocol combination for this stream and browser. ';
if (forceType) { str += "\n"+'The mimetype '+forceType+' was enforced. '; }
if (forcePlayer) { str += "\n"+'The player '+mistplayers[forcePlayer].name+' was enforced. '; }
if (options.forceType) { str += "\n"+'The mimetype '+options.forceType+' was enforced. '; }
if (options.forcePlayer) { str += "\n"+'The player '+options.forcePlayer+' was enforced. '; }
}
else {
var str = 'Stream not found.';
@ -1252,4 +1324,33 @@ function mistPlay(streamName,options) {
mistError(str);
}
}
if ((options.streaminfo) && (typeof options.streaminfo == 'object') && ('type' in options.streaminfo)
&& ('source' in options.streaminfo) && (options.streaminfo.source.length)
&& ('meta' in options.streaminfo) && ('tracks' in options.streaminfo.meta)) { //catch some of the most problematic stuff
if (typeof mistvideo == 'undefined') { mistvideo = {}; }
mistvideo[streamName] = options.streaminfo;
onstreaminfo();
}
else {
//get info js
var info = document.createElement('script');
info.src = options.host+'/info_'+encodeURIComponent(streamName)+'.js';
embedLog('Retrieving stream info from '+info.src);
document.head.appendChild(info);
info.onerror = function(){
options.target.innerHTML = '';
options.target.removeAttribute('data-loading');
mistError('Error while loading stream info.');
protoplay.report({
type: 'init',
error: 'Failed to load '+info.src
});
}
info.onload = function(){
//clean up info script
document.head.removeChild(info);
onstreaminfo();
}
}
}

File diff suppressed because one or more lines are too long

View file

@ -1,198 +0,0 @@
<html>
<head>
<title>Embed test</title>
<!--
include script for paid players
- jwplayer
- theoplayer
-->
<!--<script type='text/javascript' src='//cdn.theoplayer.com/latest/41718edc-cc2d-40d0-83d4-67c50c60f68f/theoplayer.loader.js'></script>-->
<!--<script src=players/jwplayer.js></script>
<script>jwplayer.key="2z0zTRsxD2HkL6m/LgDqvtUy2EThVn+gk1gN1Q==";</script>-->
<script>
// global options can be set here
var mistoptions = {
//host: 'http://cat.mistserver.org:8080'
host: 'http://thulmk3:8080'
//host: 'https://cat.mistserver.org:4433'
//host: 'http://localhost:8080'
//host: 'http://live.us.picarto.tv:8080'
//host: 'balderlaptop:8080'
};
</script>
<script src=core.js></script>
<!--<script src=wrappers/theoplayer.js></script>-->
<!--<script src=wrappers/jwplayer.js></script>-->
<script src=wrappers/html5.js></script>
<script src=wrappers/videojs.js></script>
<script src=wrappers/dashjs.js></script>
<script src=wrappers/flash_strobe.js></script>
<script src=wrappers/silverlight.js></script>
<script src=wrappers/polytrope.js></script>
<script src=players/dash.js></script>
<script src=players/videojs.js></script>
<link rel=stylesheet href=mist.css id=mist_player_css>
<style>
/* the website can override the css at will */
body {
padding: 0;
margin: 0;
max-width: 100vw;
max-height: 100vh;
}
.mistvideo {
margin: 1px;
}
</style>
<script>
function mistinit(){
var logele = document.querySelector('.log');
var contele = document.querySelector('.cont');
document.addEventListener('error',function(e){
console.log('[Error] '+e.message,e.target);
var msg = document.createTextNode('['+(new Date()).toTimeString().split(' ')[0]+'] '+e.message+' from '+e.target.outerHTML.slice(0,e.target.outerHTML.indexOf('>')+1));
var div = document.createElement('div');
div.appendChild(msg);
div.style.color = 'red';
logele.appendChild(div);
});
document.addEventListener('log',function(e){
console.log('[log] '+e.message);
return;
var msg = document.createTextNode('['+(new Date()).toTimeString().split(' ')[0]+'] '+e.message);
var div = document.createElement('div');
div.appendChild(msg);
logele.appendChild(div);
});
//tryplayers = Object.keys(mistplayers);
tryplayers = [];
//tryplayers.push('automatic');
//tryplayers.push('html5');
tryplayers.push('dashjs');
//tryplayers.push('videojs');
//tryplayers.push('flash_strobe');
//tryplayers.push('silverlight');
streams = [];
//streams.push('live');
//streams.push('golive+emitan');
//streams.push('subtel');
//streams.push('ogg');
//streams.push('vids+mist.mp4');
//streams.push('vids+hahalol.mp3');
//streams.push('lama');
//streams.push('bunny');
streams.push('golive+SockyChannel');
for (var j in streams) {
for (var i in tryplayers) {
var d = document.createElement('div');
var c = document.createElement('div');
c.className = 'mistvideo';
c.title = tryplayers[i];
d.appendChild(c);
contele.appendChild(d);
var p = mistPlay(streams[j],{
target: c,
maxwidth: 800,
forcePlayer: tryplayers[i],
//forceType: 'html5/video/mp4',
//forceType: 'html5/audio/mp3',
//forceType: 'html5/application/vnd.apple.mpegurl',
//forceType: 'dash/video/mp4',
//forceSource: 3,
loop: true,
//controls: 'stock'
callback: function(player) {
var button = document.createElement('button');
button.innerHTML = 'askNextCombo();';
button.onclick = function(){
player.askNextCombo('Button was clicked');
d.removeChild(this);
};
d.append(button);
}
});
}
}
};
/*
thumbnailing :')
document.addEventListener('initialized',function(e){
var canvas = document.createElement('canvas');
canvas.width = 180;
document.body.appendChild(canvas);
var context = canvas.getContext('2d');
var embedded;
for (var i in mistvideo) {
embedded = mistvideo[i].embedded[0];
break;
}
var video = embedded.player.element;
var f = video.width / canvas.width;
canvas.height = video.height / f;
video.addEventListener('canplay',function(){
context.drawImage(video,0,0,canvas.width,canvas.height);
var img = canvas.toDataURL('image/jpeg');
document.write('<img src="'+img+'">');
});
});
*/
</script>
</head>
<body onload=mistinit()>
<h1>Sup</h1>
<!--
<div class='mistvideo' id='bunny_84yt98eh9g8ht'>
<noscript>
<video controls autoplay>
<source src='http://localhost:8080/bunny.mp4' type='video/mp4'>
<a href='http://localhost:8080/bunny.html' target='_blank'>
Click here to play video
</a>
</video>
</noscript>
<script>
(function(){
var play = function(){
mistPlay('vids+subtel.mp4',{
//mistPlay('bunny',{
target: document.getElementById('bunny_84yt98eh9g8ht'),
//forcePlayer: 'dashjs'
});
}
if (!window.mistplayers) { //import shit
var p = document.createElement('script');
p.src = 'http://localhost:8080/player.js';
document.head.appendChild(p);
p.onload = function(){
play();
}
}
else {
play();
}
})();
</script>
</div>-->
<div class=cont></div>
<div class=log></div>
</body>
</html>

View file

@ -82,13 +82,19 @@ p.prototype.build = function (options,callback) {
break;
}
}
me.adderror(msg);
},true);
//prevent onerror loops
if (e.target == me.element) {
e.message = msg;
}
else {
me.adderror(msg);
}
});
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting'];
for (var i in events) {
ele.addEventListener(events[i],function(e){
me.addlog('Player event fired: '+e.type);
},true);
});
}
var player = dashjs.MediaPlayer().create();

View file

@ -95,11 +95,15 @@ p.prototype.build = function (options) {
else { ele.pause(); }
};
if (options.live) {
ele.addEventListener('error',function(e){
this.addlog('Built html');
//forward events
ele.addEventListener('error',function(e){
if (options.live) {
if ((ele.error) && (ele.error.code == 3)) {
e.stopPropagation();
ele.load();
e.stopPropagation(); //dont let this error continue to prevent the core from trying to handle the error
me.load();
me.cancelAskNextCombo();
e.message = 'Handled decoding error';
me.addlog('Decoding error: reloading..');
@ -107,18 +111,19 @@ p.prototype.build = function (options) {
type: 'playback',
warn: 'A decoding error was encountered, but handled'
});
return;
}
},true);
}
this.addlog('Built html');
//forward events
ele.addEventListener('error',function(e){
}
var msg;
if ('message' in e) {
msg = e.message;
}
else if ((e.target.tagName == 'SOURCE') && (e.target.getAttribute('src') == '')) {
e.stopPropagation();
//this error is triggered because the unload function was fired
return;
}
else {
msg = 'readyState: ';
switch (me.element.readyState) {
@ -154,15 +159,20 @@ p.prototype.build = function (options) {
break;
}
}
me.adderror(msg);
},true);
//prevent onerror loops
if (e.target == me.element) {
e.message = msg;
}
else {
me.adderror(msg);
}
});
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress'];
for (var i in events) {
ele.addEventListener(events[i],function(e){
me.addlog('Player event fired: '+e.type);
},true);
});
}
return cont;
}
p.prototype.play = function(){ return this.element.play(); };
@ -177,7 +187,32 @@ p.prototype.loop = function(bool){
}
return this.element.loop = bool;
};
p.prototype.load = function(){ return this.element.load(); };
p.prototype.load = function(){
var load;
if (this.element.paused) {
load = this.element.load();
}
else {
//sometimes there is a play / pause interrupt: try again
//TODO figure out if this happens on paused or on playing
this.load();
return;
}
//this helps to prevent the player from just showing a black screen after a reload
if (this.element.paused) {
var me = this;
var unpause = function(){
if (me.element.paused) {
me.element.play();
}
me.element.removeEventListener('progress',unpause);
}
this.element.addEventListener('progress',unpause);
}
return load;
};
if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) {
p.prototype.fullscreen = function(){
if(this.element.requestFullscreen) {

22
embed/wrappers/img.js Normal file
View file

@ -0,0 +1,22 @@
mistplayers.img = {
name: 'HTML img tag',
mimes: ['html5/image/jpeg'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype,source,options,streaminfo) {
//only use this if we are sure we just want an image
if ((options.forceType) || (options.forceSource) || (options.forcePlayer)) { return true; }
return false;
},
player: function(){}
};
var p = mistplayers.img.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options) {
var ele = this.getElement('img');
ele.src = options.src;
ele.style.display = 'block';
return ele;
}

View file

@ -6,15 +6,30 @@ mistplayers.videojs = {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype,source,options,streaminfo,logfunc) {
//dont use https if the player is loaded over http
if ((options.host.substr(0,7) == 'http://') && (source.url.substr(0,8) == 'https://')) {
if (logfunc) { logfunc('HTTP/HTTPS mismatch for this source'); }
return false;
}
var support = true;
//dont use videojs if this location is loaded over file://
if ((location.protocol == 'file:') && (mimetype == 'html5/application/vnd.apple.mpegurl')) {
if (logfunc) { logfunc('This source ('+mimetype+') won\'t work if the page is run via file://'); }
return false;
}
//dont use HLS if there is an MP3 audio track, unless we're on apple or edge
if ((mimetype == 'html5/application/vnd.apple.mpegurl') && (['iPad','iPhone','iPod','MacIntel'].indexOf(navigator.platform) == -1) && (navigator.userAgent.indexOf('Edge') == -1)) {
for (var i in streaminfo.meta.tracks) {
var t = streaminfo.meta.tracks[i];
if (t.codec == 'MP3') {
return false;
}
}
}
return ('MediaSource' in window);
},
player: function(){},
@ -117,13 +132,19 @@ p.prototype.build = function (options) {
break;
}
}
me.adderror(msg);
},true);
//prevent onerror loops
if (e.target == me.element) {
e.message = msg;
}
else {
me.adderror(msg);
}
});
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress'];
for (var i in events) {
ele.addEventListener(events[i],function(e){
me.addlog('Player event fired: '+e.type);
},true);
});
}
return cont;

View file

@ -792,6 +792,13 @@ button.return:before {
background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjEiIGlkPSJzdmcyIiBoZWlnaHQ9IjQ1IiB3aWR0aD0iNDUiPjxkZWZzIGlkPSJkZWZzNCIgLz48bWV0YWRhdGEgaWQ9Im1ldGFkYXRhNyI+PHJkZjpSREY+PGNjOldvcmsgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+PGRjOnRpdGxlPjwvZGM6dGl0bGU+PC9jYzpXb3JrPjwvcmRmOlJERj48L21ldGFkYXRhPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsLTEwMDcuMzYyMikiIGlkPSJsYXllcjEiPjxwYXRoIHRyYW5zZm9ybT0ibWF0cml4KDEuMDE0MTgyNywtMC41ODU1Mzg2NywwLjU4NTUzODY3LDEuMDE0MTgyNywtMC40ODQxOTgzMSwxMDIyLjg4OTMpIiBkPSJNIDEwLjMxMjUsLTYuMzQzNzUgQSAyLjk0MTYxODYsMi45NDE2MTg2IDAgMCAwIDcuOTA2MjUsLTQuODc1IGwgLTE0LjEyNSwyNC41IGEgMi45NDE2MTg2LDIuOTQxNjE4NiAwIDAgMCAyLjU2MjUsNC40MDYyNSBsIDI4LjI4MTI1LDAgQSAyLjk0MTYxODYsMi45NDE2MTg2IDAgMCAwIDI3LjE1NjI1LDE5LjYyNSBMIDEzLC00Ljg3NSBhIDIuOTQxNjE4NiwyLjk0MTYxODYgMCAwIDAgLTIuNjg3NSwtMS40Njg3NSB6IiBpZD0icGF0aDM4MDkiIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLW9wYWNpdHk6MSIgLz48L2c+PC9zdmc+");
background-repeat: no-repeat;
background-position: center center;
line-height: 100px;
}
.preview_icons .image img {
max-width: 200px;
max-height: 100px;
min-width: 0px;
vertical-align: middle;
}
.preview_icons .image.folder {
background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICB2ZXJzaW9uPSIxLjEiICBpZD0ic3ZnMiIgIHZpZXdCb3g9IjAgMCA0NSA0NSIgIGhlaWdodD0iNDUiICB3aWR0aD0iNDUiPiA8ZGVmcyAgIGlkPSJkZWZzNCIgLz4gPG1ldGFkYXRhICAgaWQ9Im1ldGFkYXRhNyI+ICA8cmRmOlJERj4gICA8Y2M6V29yayAgICAgcmRmOmFib3V0PSIiPiAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4gICAgPGRjOnR5cGUgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPiAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4gICA8L2NjOldvcms+ICA8L3JkZjpSREY+IDwvbWV0YWRhdGE+IDxnICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwtMTAwNy4zNjIyKSIgICBpZD0ibGF5ZXIxIj4gIDxwYXRoICAgIGlkPSJyZWN0NDE0NyIgICAgZD0ibSAyNi41MDAwMDEsMTAxNC4yOTk3IC01LjMzMzMzNCw1LjkyNzEgLTE4LjY2NjY2NywwIDAsMjkuNjM1NCAyNCwwIDE2LDAgYyAwLC0xMS44NTQyIDAsLTIzLjcwODMgMCwtMzUuNTYyNSB6IiAgICBzdHlsZT0ib3BhY2l0eToxO2ZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MTtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO3N0cm9rZS1vcGFjaXR5OjEiIC8+IDwvZz48L3N2Zz4=");

View file

@ -30,59 +30,60 @@ $(this).data("validate")(this,!0))return!1});b||(a.find(".isSetting").each(funct
[3,"3 - Previous level, and warning messages"],[4,"4 - Previous level, and status messages for development"],[5,"5 - Previous level, and more status messages for development"],[6,"6 - Previous level, and verbose debugging messages"],[7,"7 - Previous level, and very verbose debugging messages"],[8,"8 - Report everything in extreme detail"],[9,"9 - Report everything in insane detail"],[10,"10 - All messages enabled"]];case "select":e=$("<select>");for(g in d.select){var p=$("<option>");"string"==typeof d.select[g]?
p.text(d.select[g]):p.val(d.select[g][0]).text(d.select[g][1]);e.append(p)}break;case "textarea":e=$("<textarea>").on("keydown",function(a){a.stopPropagation()});break;case "checkbox":e=$("<input>").attr("type","checkbox");break;case "hidden":e=$("<input>").attr("type","hidden");m.hide();break;case "email":e=$("<input>").attr("type","email").attr("autocomplete","on").attr("required","");break;case "browse":e=$("<input>").attr("type","text");"filetypes"in d&&e.data("filetypes",d.filetypes);break;case "geolimited":case "hostlimited":e=
$("<input>").attr("type","hidden");break;case "radioselect":e=$("<div>").addClass("radioselect");for(c in d.radioselect){var l=$("<input>").attr("type","radio").val(d.radioselect[c][0]).attr("name",d.label);("LTSonly"in d&&!mist.data.LTS||d.readonly)&&l.prop("disabled",!0);p=$("<label>").append(l).append($("<span>").html(d.radioselect[c][1]));e.append(p);if(2<d.radioselect[c].length)for(g in l=$("<select>").change(function(){$(this).parent().find("input[type=radio]:enabled").prop("checked","true")}),
p.append(l),("LTSonly"in d&&!mist.data.LTS||d.readonly)&&l.prop("disabled",!0),d.radioselect[c][2])p=$("<option>"),l.append(p),d.radioselect[c][2][g]instanceof Array?p.val(d.radioselect[c][2][g][0]).html(d.radioselect[c][2][g][1]):p.html(d.radioselect[c][2][g])}break;case "checklist":e=$("<div>").addClass("checkcontainer");$controls=$("<div>").addClass("controls");$checklist=$("<div>").addClass("checklist");e.append($controls).append($checklist);$controls.append($("<label>").text("All").prepend($("<input>").attr("type",
"checkbox").click(function(){$(this).is(":checked")?$(this).closest(".checkcontainer").find("input[type=checkbox]").prop("checked",!0):$(this).closest(".checkcontainer").find("input[type=checkbox]").prop("checked",!1)})));for(c in d.checklist)"string"==typeof d.checklist[c]&&(d.checklist[c]=[d.checklist[c],d.checklist[c]]),$checklist.append($("<label>").text(d.checklist[c][1]).prepend($("<input>").attr("type","checkbox").attr("name",d.checklist[c][0])));break;case "DOMfield":e=d.DOMfield;break;default:e=
$("<input>").attr("type","text")}e.addClass("field").data("opts",d);"pointer"in d&&e.attr("name",d.pointer.index);f.append(e);if("classes"in d)for(g in d.classes)e.addClass(d.classes[g]);"placeholder"in d&&e.attr("placeholder",d.placeholder);"default"in d&&e.attr("placeholder",d["default"]);"unit"in d&&f.append($("<span>").addClass("unit").html(d.unit));"readonly"in d&&(e.attr("readonly","readonly"),e.click(function(){$(this).select()}));"qrcode"in d&&f.append($("<span>").addClass("unit").html($("<button>").text("QR").on("keydown",
function(a){a.stopPropagation()}).click(function(){var a=String($(this).closest(".field_container").find(".field").getval()),b=$("<div>").addClass("qrcode");UI.popup.show($("<span>").addClass("qr_container").append($("<p>").text(a)).append(b));b.qrcode({text:a,size:Math.min(b.width(),b.height())})})));"clipboard"in d&&document.queryCommandSupported("copy")&&f.append($("<span>").addClass("unit").html($("<button>").text("Copy").on("keydown",function(a){a.stopPropagation()}).click(function(){var a=String($(this).closest(".field_container").find(".field").getval()),
b=document.createElement("textarea");b.value=a;document.body.appendChild(b);b.select();var c=false;try{c=document.execCommand("copy")}catch(d){}if(c){$(this).text("Copied to clipboard!");document.body.removeChild(b);var g=$(this);setTimeout(function(){g.text("Copy")},5E3)}else{document.body.removeChild(b);alert("Failed to copy:\n"+a)}})));"rows"in d&&e.attr("rows",d.rows);"LTSonly"in d&&!mist.data.LTS&&(f.addClass("LTSonly"),e.prop("disabled",!0));switch(d.type){case "browse":l=$("<div>").addClass("grouper").append(m);
b.append(l);l=$("<button>").text("Browse").on("keydown",function(a){a.stopPropagation()});f.append(l);l.click(function(){function a(b){m.text("Loading..");mist.send(function(a){f.text(a.browse.path[0]);mist.data.LTS&&d.setval(a.browse.path[0]+"/");m.html(l.clone(true).text("..").attr("title","Folder up"));if(a.browse.subdirectories){a.browse.subdirectories.sort();for(var b in a.browse.subdirectories){var e=a.browse.subdirectories[b];m.append(l.clone(true).attr("title",f.text()+q+e).text(e))}}if(a.browse.files){a.browse.files.sort();
for(b in a.browse.files){var e=a.browse.files[b],h=f.text()+q+e,e=$("<a>").text(e).addClass("file").attr("title",h);m.append(e);if(p){var n=true,u;for(u in p)if(typeof p[u]!="undefined"&&mist.inputMatch(p[u],h)){n=false;break}n&&e.hide()}e.click(function(){var a=$(this).attr("title");d.setval(a).removeAttr("readonly").css("opacity",1);g.show();c.remove()})}}},{browse:b})}var b=$(this).closest(".grouper"),c=$("<div>").addClass("browse_container"),d=b.find(".field").attr("readonly","readonly").css("opacity",
0.5),g=$(this),e=$("<button>").text("Stop browsing").click(function(){g.show();c.remove();d.removeAttr("readonly").css("opacity",1)}),f=$("<span>").addClass("field"),m=$("<div>").addClass("browse_contents"),l=$("<a>").addClass("folder"),p=d.data("filetypes");b.append(c);c.append($("<label>").addClass("UIelement").append($("<span>").addClass("label").text("Current folder:")).append($("<span>").addClass("field_container").append(f).append(e))).append(m);var q="/";mist.data.config.version.indexOf("indows")>
-1&&(q="\\");l.click(function(){var b=f.text()+q+$(this).text();a(b)});b=d.getval();e=b.split("://");e.length>1&&(b=e[0]=="file"?e[1]:"");b=b.split(q);b.pop();b=b.join(q);g.hide();a(b)});break;case "geolimited":case "hostlimited":l={field:e};l.blackwhite=$("<select>").append($("<option>").val("-").text("Blacklist")).append($("<option>").val("+").text("Whitelist"));l.values=$("<span>").addClass("limit_value_list");switch(d.type){case "geolimited":l.prototype=$("<select>").append($("<option>").val("").text("[Select a country]"));
for(c in UI.countrylist)l.prototype.append($("<option>").val(c).html(UI.countrylist[c]));break;case "hostlimited":l.prototype=$("<input>").attr("type","text").attr("placeholder","type a host")}l.prototype.on("change keyup",function(){$(this).closest(".field_container").data("subUI").blackwhite.trigger("change")});l.blackwhite.change(function(){var a=$(this).closest(".field_container").data("subUI"),b=[],c=false;a.values.children().each(function(){c=$(this).val();c!=""?b.push(c):$(this).remove()});
a.values.append(a.prototype.clone(true));b.length>0?a.field.val($(this).val()+b.join(" ")):a.field.val("");a.field.trigger("change")});"LTSonly"in d&&!mist.data.LTS&&(l.blackwhite.prop("disabled",!0),l.prototype.prop("disabled",!0));l.values.append(l.prototype.clone(!0));f.data("subUI",l).addClass("limit_list").append(l.blackwhite).append(l.values)}"pointer"in d&&(e.data("pointer",d.pointer).addClass("isSetting"),l=d.pointer.main[d.pointer.index],"undefined"!=l&&e.setval(l));"value"in d&&e.setval(d.value);
if("datalist"in d)for(c in l="datalist_"+c+MD5(e[0].outerHTML),e.attr("list",l),l=$("<datalist>").attr("id",l),f.append(l),d.datalist)l.append($("<option>").val(d.datalist[c]));f=$("<span>").addClass("help_container");m.append(f);"help"in d&&(f.append($("<span>").addClass("ih_balloon").html(d.help)),e.on("focus mouseover",function(){$(this).closest("label").addClass("active")}).on("blur mouseout",function(){$(this).closest("label").removeClass("active")}));if("validate"in d){m=[];for(g in d.validate){l=
d.validate[g];if("function"!=typeof l)switch(l){case "required":l=function(a){return a==""?{msg:"This is a required field.",classes:["red"]}:false};break;case "int":l=function(a,b){var c=$(b).data("opts");if(!$(b)[0].validity.valid){var d=[];"min"in c&&d.push(" greater than or equal to "+c.min);"max"in c&&d.push(" smaller than or equal to "+c.max);return{msg:"Please enter an integer"+d.join(" and")+".",classes:["red"]}}if(parseInt(Number(a))!=a)return{msg:"Please enter an integer.",classes:["red"]}};
break;case "streamname":l=function(a,b){if(!isNaN(a.charAt(0)))return{msg:"The first character may not be a number.",classes:["red"]};if(a.toLowerCase()!=a)return{msg:"Uppercase letters are not allowed.",classes:["red"]};if(a.replace(/[^\da-z_]/g,"")!=a)return{msg:"Special characters (except for underscores) are not allowed.",classes:["red"]};if("streams"in mist.data&&a in mist.data.streams&&$(b).data("pointer").main.name!=a)return{msg:"This streamname already exists.<br>If you want to edit an existing stream, please click edit on the the streams tab.",
classes:["red"]}};break;default:l=function(){}}m.push(l)}e.data("validate_functions",m).data("help_container",f).data("validate",function(a,b){var c=$(a).getval(),d=$(a).data("validate_functions"),e=$(a).data("help_container");e.find(".err_balloon").remove();for(var g in d){var f=d[g](c,a);if(f){$err=$("<span>").addClass("err_balloon").html(f.msg);for(var m in f.classes)$err.addClass(f.classes[m]);e.prepend($err);b&&$(a).focus();return true}}return false}).addClass("hasValidate").on("change keyup",
function(){$(this).data("validate")($(this))});""!=e.getval()&&e.trigger("change")}"function"in d&&(e.on("change keyup",d["function"]),e.trigger("change"))}}b.on("keydown",function(a){switch(a.which){case 13:$(this).find("button.save").first().trigger("click");break;case 27:$(this).find("button.cancel").first().trigger("click")}});return b},buildVheaderTable:function(a){var b=$("<table>").css("margin","0.2em"),c=$("<tr>").addClass("header").append($("<td>").addClass("vheader").attr("rowspan",a.labels.length+
1).append($("<span>").text(a.vheader))),d=[];c.append($("<td>"));for(var e in a.labels)d.push($("<tr>").append($("<td>").html(""==a.labels[e]?"&nbsp;":a.labels[e]+":")));for(var g in a.content)for(e in c.append($("<td>").html(a.content[g].header)),a.content[g].body)d[e].append($("<td>").html(a.content[g].body[e]));b.append($("<tbody>").append(c).append(d));return b},plot:{addGraph:function(a,b){var c={id:a.id,xaxis:a.xaxis,datasets:[],elements:{cont:$("<div>").addClass("graph"),plot:$("<div>").addClass("plot"),
legend:$("<div>").addClass("legend").attr("draggable","true")}};UI.draggable(c.elements.legend);c.elements.cont.append(c.elements.plot).append(c.elements.legend);b.append(c.elements.cont);return c},go:function(a){if(!(1>Object.keys(a).length)){var b={totals:[],clients:[]},c;for(c in a)for(var d in a[c].datasets){var e=a[c].datasets[d];switch(e.datatype){case "clients":case "upbps":case "downbps":switch(e.origin[0]){case "total":b.totals.push({fields:[e.datatype],end:-15});break;case "stream":b.totals.push({fields:[e.datatype],
streams:[e.origin[1]],end:-15});break;case "protocol":b.totals.push({fields:[e.datatype],protocols:[e.origin[1]],end:-15})}break;case "cpuload":case "memload":b.capabilities={}}}0==b.totals.length&&delete b.totals;0==b.clients.length&&delete b.clients;mist.send(function(){for(var b in a){var c=a[b];if(1>c.datasets.length){c.elements.plot.html("");c.elements.legend.html("");break}switch(c.xaxis){case "time":var d=[];c.yaxes={};var e=[],p;for(p in c.datasets){var l=c.datasets[p];l.display&&(l.getdata(),
l.yaxistype in c.yaxes||(d.push(UI.plot.yaxes[l.yaxistype]),c.yaxes[l.yaxistype]=d.length),l.yaxis=c.yaxes[l.yaxistype],e.push(l))}d[0]&&(d[0].color=0);c.plot=$.plot(c.elements.plot,e,{legend:{show:!1},xaxis:UI.plot.xaxes[c.xaxis],yaxes:d,grid:{hoverable:!0,borderWidth:{top:0,right:0,bottom:1,left:1},color:"black",backgroundColor:{colors:["rgba(0,0,0,0)","rgba(0,0,0,0.025)"]}},crosshair:{mode:"x"}});d=$("<table>").addClass("legend-list").addClass("nolay").html($("<tr>").html($("<td>").html($("<h3>").text(c.id))).append($("<td>").css("padding-right",
"2em").css("text-align","right").html($("<span>").addClass("value")).append($("<button>").data("opts",c).text("X").addClass("close").click(function(){var b=$(this).data("opts");if(confirm("Are you sure you want to remove "+b.id+"?")){b.elements.cont.remove();var c=$(".graph_ids option:contains("+b.id+")"),d=c.parent();c.remove();UI.plot.del(b.id);delete a[b.id];d.trigger("change");UI.plot.go(a)}}))));c.elements.legend.html(d);var u=function(a){var b=c.elements.legend.find(".value"),d=1;if(typeof a==
"undefined")b.eq(0).html("Latest:");else{var e=c.plot.getXAxes()[0],a=Math.min(e.max,a),a=Math.max(e.min,a);b.eq(0).html(UI.format.time(a/1E3))}for(var g in c.datasets){var f="&nbsp;";if(c.datasets[g].display){var e=UI.plot.yaxes[c.datasets[g].yaxistype].tickFormatter,h=c.datasets[g].data;if(a)for(var l in h){if(h[l][0]==a){f=e(h[l][1]);break}if(h[l][0]>a){if(l!=0){f=h[l];h=h[l-1];f=e(f[1]+(a-f[0])*(h[1]-f[1])/(h[0]-f[0]))}break}}else f=e(c.datasets[g].data[c.datasets[g].data.length-1][1])}b.eq(d).html(f);
d++}};c.plot.getOptions();for(p in c.datasets)e=$("<input>").attr("type","checkbox").data("index",p).data("graph",c).click(function(){var a=$(this).data("graph");$(this).is(":checked")?a.datasets[$(this).data("index")].display=true:a.datasets[$(this).data("index")].display=false;var b={};b[a.id]=a;UI.plot.go(b)}),c.datasets[p].display&&e.attr("checked","checked"),d.append($("<tr>").html($("<td>").html($("<label>").html(e).append($("<div>").addClass("series-color").css("background-color",c.datasets[p].color)).append(c.datasets[p].label))).append($("<td>").css("padding-right",
"2em").css("text-align","right").html($("<span>").addClass("value")).append($("<button>").text("X").addClass("close").data("index",p).data("graph",c).click(function(){var b=$(this).data("index"),c=$(this).data("graph");if(confirm("Are you sure you want to remove "+c.datasets[b].label+" from "+c.id+"?")){c.datasets.splice(b,1);if(c.datasets.length==0){c.elements.cont.remove();var b=$(".graph_ids option:contains("+c.id+")"),d=b.parent();b.remove();d.trigger("change");UI.plot.del(c.id);delete a[c.id];
UI.plot.go(a)}else{UI.plot.save(c);b={};b[c.id]=c;UI.plot.go(b)}}}))));u();var h=!1;c.elements.plot.on("plothover",function(a,b,c){if(b.x!=h){u(b.x);h=b.x}if(c){a=$("<span>").append($("<h3>").text(c.series.label).prepend($("<div>").addClass("series-color").css("background-color",c.series.color))).append($("<table>").addClass("nolay").html($("<tr>").html($("<td>").text("Time:")).append($("<td>").html(UI.format.dateTime(c.datapoint[0]/1E3,"long")))).append($("<tr>").html($("<td>").text("Value:")).append($("<td>").html(c.series.yaxis.tickFormatter(c.datapoint[1],
c.series.yaxis)))));UI.tooltip.show(b,a.children())}else UI.tooltip.hide()}).on("mouseout",function(){u()})}}},b)}},save:function(a){var b={id:a.id,xaxis:a.xaxis,datasets:[]},c;for(c in a.datasets)b.datasets.push({origin:a.datasets[c].origin,datatype:a.datasets[c].datatype});a=mist.stored.get().graphs||{};a[b.id]=b;mist.stored.set("graphs",a)},del:function(a){var b=mist.stored.get().graphs||{};delete b[a];mist.stored.set("graphs",b)},datatype:{getOptions:function(a){var b=$.extend(!0,{},UI.plot.datatype.templates.general),
c=$.extend(!0,{},UI.plot.datatype.templates[a.datatype]),a=$.extend(!0,c,a),a=$.extend(!0,b,a);switch(a.origin[0]){case "total":switch(a.datatype){case "cpuload":case "memload":break;default:a.label+=" (total)"}break;case "stream":case "protocol":a.label+=" ("+a.origin[1]+")"}var b=[],d;for(d in a.basecolor)c=a.basecolor[d],c+=50*(0.5-Math.random()),c=Math.round(c),c=Math.min(255,Math.max(0,c)),b.push(c);a.color="rgb("+b.join(",")+")";return a},templates:{general:{display:!0,datatype:"general",label:"",
yaxistype:"amount",data:[],lines:{show:!0},points:{show:!1},getdata:function(){var a=mist.data.totals["stream"==this.origin[0]?this.origin[1]:"all_streams"]["protocol"==this.origin[0]?this.origin[1]:"all_protocols"][this.datatype];return this.data=a}},cpuload:{label:"CPU use",yaxistype:"percentage",basecolor:[237,194,64],cores:1,getdata:function(){var a=!1,b;for(b in this.data)this.data[b][0]<1E3*(mist.data.config.time-600)&&(a=b);!1!==a&&this.data.splice(0,Number(a)+1);this.data.push([1E3*mist.data.config.time,
mist.data.capabilities.cpu_use/10]);return this.data}},memload:{label:"Memory load",yaxistype:"percentage",basecolor:[175,216,248],getdata:function(){var a=!1,b;for(b in this.data)this.data[b][0]<1E3*(mist.data.config.time-600)&&(a=b);!1!==a&&this.data.splice(0,Number(a)+1);this.data.push([1E3*mist.data.config.time,mist.data.capabilities.load.memory]);return this.data}},clients:{label:"Connections",basecolor:[203,75,75]},upbps:{label:"Bandwidth up",yaxistype:"bytespersec",basecolor:[77,167,77]},downbps:{label:"Bandwidth down",
yaxistype:"bytespersec",basecolor:[148,64,237]}}},yaxes:{percentage:{name:"percentage",color:"black",tickColor:0,tickDecimals:0,tickFormatter:function(a){return UI.format.addUnit(UI.format.number(a),"%")},tickLength:0,min:0,max:100},amount:{name:"amount",color:"black",tickColor:0,tickDecimals:0,tickFormatter:function(a){return UI.format.number(a)},tickLength:0,min:0},bytespersec:{name:"bytespersec",color:"black",tickColor:0,tickDecimals:1,tickFormatter:function(a){return UI.format.bytes(a,!0)},tickLength:0,
ticks:function(a){var b=0.3*Math.sqrt($(".graph").first().height()),b=(a.max-a.min)/b,c=Math.floor(Math.log(Math.abs(b))/Math.log(1024)),d=b/Math.pow(1024,c),e=-Math.floor(Math.log(d)/Math.LN10),g=a.tickDecimals;null!=g&&e>g&&(e=g);var m=Math.pow(10,-e),d=d/m,f;if(1.5>d)f=1;else if(3>d){if(f=2,2.25<d&&(null==g||e+1<=g))f=2.5,++e}else f=7.5>d?5:10;f=f*m*Math.pow(1024,c);null!=a.minTickSize&&f<a.minTickSize&&(f=a.minTickSize);a.delta=b;a.tickDecimals=Math.max(0,null!=g?g:e);a.tickSize=f;b=[];c=a.tickSize*
Math.floor(a.min/a.tickSize);e=0;g=Number.NaN;do m=g,g=c+e*a.tickSize,b.push(g),++e;while(g<a.max&&g!=m);return b},min:0}},xaxes:{time:{name:"time",mode:"time",timezone:"browser",ticks:5}}},draggable:function(a){a.attr("draggable",!0);a.on("dragstart",function(a){$(this).css("opacity",0.4).data("dragstart",{click:{x:a.originalEvent.pageX,y:a.originalEvent.pageY},ele:{x:this.offsetLeft,y:this.offsetTop}})}).on("dragend",function(a){var c=$(this).data("dragstart"),d=c.ele.x-c.click.x+a.originalEvent.pageX,
a=c.ele.y-c.click.y+a.originalEvent.pageY;$(this).css({opacity:1,top:a,left:d,right:"auto",bottom:"auto"})});a.parent().on("dragleave",function(){})},format:{time:function(a,b){var c=new Date(1E3*a),d=[];d.push(("0"+c.getHours()).slice(-2));d.push(("0"+c.getMinutes()).slice(-2));"short"!=b&&d.push(("0"+c.getSeconds()).slice(-2));return d.join(":")},date:function(a,b){var c=new Date(1E3*a),d="Sun Mon Tue Wed Thu Fri Sat".split(" "),e=[];"long"==b&&e.push(d[c.getDay()]);e.push(("0"+c.getDate()).slice(-2));
e.push("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ")[c.getMonth()]);"short"!=b&&e.push(c.getFullYear());return e.join(" ")},dateTime:function(a,b){return UI.format.date(a,b)+", "+UI.format.time(a,b)},duration:function(a){var b=[0.001,1E3,60,60,24,7,1E9],c="ms sec min hr day week".split(" "),d={},e;for(e in c){var a=a/b[e],g=Math.round(a%b[Number(e)+1]);d[c[e]]=g;a-=g}var m;for(e=c.length-1;0<=e;e--)if(0<d[c[e]]){m=c[e];break}b=$("<span>");switch(m){case "week":b.append(UI.format.addUnit(d.week,
"wks, ")).append(UI.format.addUnit(d.day,"days"));break;case "day":b.append(UI.format.addUnit(d.day,"days, ")).append(UI.format.addUnit(d.hr,"hrs"));break;default:b.append([("0"+d.hr).slice(-2),("0"+d.min).slice(-2),("0"+d.sec).slice(-2)+(d.ms?"."+d.ms:"")].join(":"))}return b[0].innerHTML},number:function(a){if(isNaN(Number(a))||0==a)return a;var b=Math.pow(10,3-Math.floor(Math.log(a)/Math.LN10)-1),a=Math.round(a*b)/b;if(1E4<a){number=a.toString().split(".");for(a=/(\d+)(\d{3})/;a.test(number[0]);)number[0]=
number[0].replace(a,"$1 $2");a=number.join(".")}return a},status:function(a){var b=$("<span>");if("undefined"==typeof a.online)return b.text("Unknown, checking.."),"undefined"!=typeof a.error&&b.text(a.error),b;switch(a.online){case -1:b.text("Enabling");break;case 0:b.text("Unavailable").addClass("red");break;case 1:b.text("Active").addClass("green");break;case 2:b.text("Standby").addClass("orange");break;default:b.text(a.online)}"error"in a&&b.text(a.error);return b},capital:function(a){return a.charAt(0).toUpperCase()+
a.substring(1)},addUnit:function(a,b){var c=$("<span>").html(a);c.append($("<span>").addClass("unit").html(b));return c[0].innerHTML},bytes:function(a,b){var c="bytes KiB MiB GiB TiB PiB".split(" ");if(0==a)unit=c[0];else{var d=Math.floor(Math.log(Math.abs(a))/Math.log(1024));0>d?unit=c[0]:(a/=Math.pow(1024,d),unit=c[d])}return UI.format.addUnit(UI.format.number(a),unit+(b?"/s":""))}},navto:function(a,b){var c=location.hash,d=c.split("@");d[0]=[mist.user.name,mist.user.host].join("&");d[1]=[a,b].join("&");
"undefined"!=typeof screenlog&&screenlog.navto(d[1]);location.hash=d.join("@");location.hash==c&&$(window).trigger("hashchange")},showTab:function(a,b){var c=UI.elements.main;if(mist.user.loggedin&&!("ui_settings"in mist.data))c.html("Loading.."),mist.send(function(){UI.showTab(a,b)},{ui_settings:!0});else{var d=UI.elements.menu.removeClass("hide").find('.plain:contains("'+a+'")').closest(".button");0<d.length&&(UI.elements.menu.find(".button.active").removeClass("active"),d.addClass("active"));if("undefined"!=
typeof mistvideo)for(var e in mistvideo)if("embedded"in mistvideo[e])for(var g in mistvideo[e].embedded)try{mistvideo[e].embedded[g].player.unload()}catch(m){}UI.interval.clear();c.html($("<h2>").text(a));switch(a){case "Login":if(mist.user.loggedin){UI.navto("Overview");return}UI.elements.menu.addClass("hide");UI.elements.connection.status.text("Disconnected").removeClass("green").addClass("red");c.append(UI.buildUI([{type:"help",help:"Please provide your account details.<br>You were asked to set these when MistController was started for the first time. If you did not yet set any account details, log in with your desired credentials to create a new account."},
{label:"Host",help:"Url location of the MistServer API. Generally located at http://MistServerIP:4242/api","default":"http://localhost:4242/api",pointer:{main:mist.user,index:"host"}},{label:"Username",help:"Please enter your username here.",validate:["required"],pointer:{main:mist.user,index:"name"}},{label:"Password",type:"password",help:"Please enter your password here.",validate:["required"],pointer:{main:mist.user,index:"rawpassword"}},{type:"buttons",buttons:[{label:"Login",type:"save","function":function(){mist.user.password=
p.append(l),("LTSonly"in d&&!mist.data.LTS||d.readonly)&&l.prop("disabled",!0),d.radioselect[c][2])p=$("<option>"),l.append(p),d.radioselect[c][2][g]instanceof Array?p.val(d.radioselect[c][2][g][0]).html(d.radioselect[c][2][g][1]):p.html(d.radioselect[c][2][g])}break;case "checklist":e=$("<div>").addClass("checkcontainer");$controls=$("<div>").addClass("controls");$checklist=$("<div>").addClass("checklist");e.append($checklist);for(c in d.checklist)"string"==typeof d.checklist[c]&&(d.checklist[c]=
[d.checklist[c],d.checklist[c]]),$checklist.append($("<label>").text(d.checklist[c][1]).prepend($("<input>").attr("type","checkbox").attr("name",d.checklist[c][0])));break;case "DOMfield":e=d.DOMfield;break;default:e=$("<input>").attr("type","text")}e.addClass("field").data("opts",d);"pointer"in d&&e.attr("name",d.pointer.index);f.append(e);if("classes"in d)for(g in d.classes)e.addClass(d.classes[g]);"placeholder"in d&&e.attr("placeholder",d.placeholder);"default"in d&&e.attr("placeholder",d["default"]);
"unit"in d&&f.append($("<span>").addClass("unit").html(d.unit));"readonly"in d&&(e.attr("readonly","readonly"),e.click(function(){$(this).select()}));"qrcode"in d&&f.append($("<span>").addClass("unit").html($("<button>").text("QR").on("keydown",function(a){a.stopPropagation()}).click(function(){var a=String($(this).closest(".field_container").find(".field").getval()),b=$("<div>").addClass("qrcode");UI.popup.show($("<span>").addClass("qr_container").append($("<p>").text(a)).append(b));b.qrcode({text:a,
size:Math.min(b.width(),b.height())})})));"clipboard"in d&&document.queryCommandSupported("copy")&&f.append($("<span>").addClass("unit").html($("<button>").text("Copy").on("keydown",function(a){a.stopPropagation()}).click(function(){var a=String($(this).closest(".field_container").find(".field").getval()),b=document.createElement("textarea");b.value=a;document.body.appendChild(b);b.select();var c=false;try{c=document.execCommand("copy")}catch(d){}if(c){$(this).text("Copied to clipboard!");document.body.removeChild(b);
var g=$(this);setTimeout(function(){g.text("Copy")},5E3)}else{document.body.removeChild(b);alert("Failed to copy:\n"+a)}})));"rows"in d&&e.attr("rows",d.rows);"LTSonly"in d&&!mist.data.LTS&&(f.addClass("LTSonly"),e.prop("disabled",!0));switch(d.type){case "browse":l=$("<div>").addClass("grouper").append(m);b.append(l);l=$("<button>").text("Browse").on("keydown",function(a){a.stopPropagation()});f.append(l);l.click(function(){function a(b){m.text("Loading..");mist.send(function(a){f.text(a.browse.path[0]);
mist.data.LTS&&d.setval(a.browse.path[0]+"/");m.html(l.clone(true).text("..").attr("title","Folder up"));if(a.browse.subdirectories){a.browse.subdirectories.sort();for(var b in a.browse.subdirectories){var e=a.browse.subdirectories[b];m.append(l.clone(true).attr("title",f.text()+q+e).text(e))}}if(a.browse.files){a.browse.files.sort();for(b in a.browse.files){var e=a.browse.files[b],h=f.text()+q+e,e=$("<a>").text(e).addClass("file").attr("title",h);m.append(e);if(p){var n=true,u;for(u in p)if(typeof p[u]!=
"undefined"&&mist.inputMatch(p[u],h)){n=false;break}n&&e.hide()}e.click(function(){var a=$(this).attr("title");d.setval(a).removeAttr("readonly").css("opacity",1);g.show();c.remove()})}}},{browse:b})}var b=$(this).closest(".grouper"),c=$("<div>").addClass("browse_container"),d=b.find(".field").attr("readonly","readonly").css("opacity",0.5),g=$(this),e=$("<button>").text("Stop browsing").click(function(){g.show();c.remove();d.removeAttr("readonly").css("opacity",1)}),f=$("<span>").addClass("field"),
m=$("<div>").addClass("browse_contents"),l=$("<a>").addClass("folder"),p=d.data("filetypes");b.append(c);c.append($("<label>").addClass("UIelement").append($("<span>").addClass("label").text("Current folder:")).append($("<span>").addClass("field_container").append(f).append(e))).append(m);var q="/";mist.data.config.version.indexOf("indows")>-1&&(q="\\");l.click(function(){var b=f.text()+q+$(this).text();a(b)});b=d.getval();e=b.split("://");e.length>1&&(b=e[0]=="file"?e[1]:"");b=b.split(q);b.pop();
b=b.join(q);g.hide();a(b)});break;case "geolimited":case "hostlimited":l={field:e};l.blackwhite=$("<select>").append($("<option>").val("-").text("Blacklist")).append($("<option>").val("+").text("Whitelist"));l.values=$("<span>").addClass("limit_value_list");switch(d.type){case "geolimited":l.prototype=$("<select>").append($("<option>").val("").text("[Select a country]"));for(c in UI.countrylist)l.prototype.append($("<option>").val(c).html(UI.countrylist[c]));break;case "hostlimited":l.prototype=$("<input>").attr("type",
"text").attr("placeholder","type a host")}l.prototype.on("change keyup",function(){$(this).closest(".field_container").data("subUI").blackwhite.trigger("change")});l.blackwhite.change(function(){var a=$(this).closest(".field_container").data("subUI"),b=[],c=false;a.values.children().each(function(){c=$(this).val();c!=""?b.push(c):$(this).remove()});a.values.append(a.prototype.clone(true));b.length>0?a.field.val($(this).val()+b.join(" ")):a.field.val("");a.field.trigger("change")});"LTSonly"in d&&
!mist.data.LTS&&(l.blackwhite.prop("disabled",!0),l.prototype.prop("disabled",!0));l.values.append(l.prototype.clone(!0));f.data("subUI",l).addClass("limit_list").append(l.blackwhite).append(l.values)}"pointer"in d&&(e.data("pointer",d.pointer).addClass("isSetting"),l=d.pointer.main[d.pointer.index],"undefined"!=l&&e.setval(l));"value"in d&&e.setval(d.value);if("datalist"in d)for(c in l="datalist_"+c+MD5(e[0].outerHTML),e.attr("list",l),l=$("<datalist>").attr("id",l),f.append(l),d.datalist)l.append($("<option>").val(d.datalist[c]));
f=$("<span>").addClass("help_container");m.append(f);"help"in d&&(f.append($("<span>").addClass("ih_balloon").html(d.help)),e.on("focus mouseover",function(){$(this).closest("label").addClass("active")}).on("blur mouseout",function(){$(this).closest("label").removeClass("active")}));if("validate"in d){m=[];for(g in d.validate){l=d.validate[g];if("function"!=typeof l)switch(l){case "required":l=function(a){return a==""?{msg:"This is a required field.",classes:["red"]}:false};break;case "int":l=function(a,
b){var c=$(b).data("opts");if(!$(b)[0].validity.valid){var d=[];"min"in c&&d.push(" greater than or equal to "+c.min);"max"in c&&d.push(" smaller than or equal to "+c.max);return{msg:"Please enter an integer"+d.join(" and")+".",classes:["red"]}}if(parseInt(Number(a))!=a)return{msg:"Please enter an integer.",classes:["red"]}};break;case "streamname":l=function(a,b){if(!isNaN(a.charAt(0)))return{msg:"The first character may not be a number.",classes:["red"]};if(a.toLowerCase()!=a)return{msg:"Uppercase letters are not allowed.",
classes:["red"]};if(a.replace(/[^\da-z_]/g,"")!=a)return{msg:"Special characters (except for underscores) are not allowed.",classes:["red"]};if("streams"in mist.data&&a in mist.data.streams&&$(b).data("pointer").main.name!=a)return{msg:"This streamname already exists.<br>If you want to edit an existing stream, please click edit on the the streams tab.",classes:["red"]}};break;default:l=function(){}}m.push(l)}e.data("validate_functions",m).data("help_container",f).data("validate",function(a,b){var c=
$(a).getval(),d=$(a).data("validate_functions"),e=$(a).data("help_container");e.find(".err_balloon").remove();for(var g in d){var f=d[g](c,a);if(f){$err=$("<span>").addClass("err_balloon").html(f.msg);for(var m in f.classes)$err.addClass(f.classes[m]);e.prepend($err);b&&$(a).focus();return true}}return false}).addClass("hasValidate").on("change keyup",function(){$(this).data("validate")($(this))});""!=e.getval()&&e.trigger("change")}"function"in d&&(e.on("change keyup",d["function"]),e.trigger("change"))}}b.on("keydown",
function(a){switch(a.which){case 13:$(this).find("button.save").first().trigger("click");break;case 27:$(this).find("button.cancel").first().trigger("click")}});return b},buildVheaderTable:function(a){var b=$("<table>").css("margin","0.2em"),c=$("<tr>").addClass("header").append($("<td>").addClass("vheader").attr("rowspan",a.labels.length+1).append($("<span>").text(a.vheader))),d=[];c.append($("<td>"));for(var e in a.labels)d.push($("<tr>").append($("<td>").html(""==a.labels[e]?"&nbsp;":a.labels[e]+
":")));for(var g in a.content)for(e in c.append($("<td>").html(a.content[g].header)),a.content[g].body)d[e].append($("<td>").html(a.content[g].body[e]));b.append($("<tbody>").append(c).append(d));return b},plot:{addGraph:function(a,b){var c={id:a.id,xaxis:a.xaxis,datasets:[],elements:{cont:$("<div>").addClass("graph"),plot:$("<div>").addClass("plot"),legend:$("<div>").addClass("legend").attr("draggable","true")}};UI.draggable(c.elements.legend);c.elements.cont.append(c.elements.plot).append(c.elements.legend);
b.append(c.elements.cont);return c},go:function(a){if(!(1>Object.keys(a).length)){var b={totals:[],clients:[]},c;for(c in a)for(var d in a[c].datasets){var e=a[c].datasets[d];switch(e.datatype){case "clients":case "upbps":case "downbps":switch(e.origin[0]){case "total":b.totals.push({fields:[e.datatype],end:-15});break;case "stream":b.totals.push({fields:[e.datatype],streams:[e.origin[1]],end:-15});break;case "protocol":b.totals.push({fields:[e.datatype],protocols:[e.origin[1]],end:-15})}break;case "cpuload":case "memload":b.capabilities=
{}}}0==b.totals.length&&delete b.totals;0==b.clients.length&&delete b.clients;mist.send(function(){for(var b in a){var c=a[b];if(1>c.datasets.length){c.elements.plot.html("");c.elements.legend.html("");break}switch(c.xaxis){case "time":var d=[];c.yaxes={};var e=[],p;for(p in c.datasets){var l=c.datasets[p];l.display&&(l.getdata(),l.yaxistype in c.yaxes||(d.push(UI.plot.yaxes[l.yaxistype]),c.yaxes[l.yaxistype]=d.length),l.yaxis=c.yaxes[l.yaxistype],e.push(l))}d[0]&&(d[0].color=0);c.plot=$.plot(c.elements.plot,
e,{legend:{show:!1},xaxis:UI.plot.xaxes[c.xaxis],yaxes:d,grid:{hoverable:!0,borderWidth:{top:0,right:0,bottom:1,left:1},color:"black",backgroundColor:{colors:["rgba(0,0,0,0)","rgba(0,0,0,0.025)"]}},crosshair:{mode:"x"}});d=$("<table>").addClass("legend-list").addClass("nolay").html($("<tr>").html($("<td>").html($("<h3>").text(c.id))).append($("<td>").css("padding-right","2em").css("text-align","right").html($("<span>").addClass("value")).append($("<button>").data("opts",c).text("X").addClass("close").click(function(){var b=
$(this).data("opts");if(confirm("Are you sure you want to remove "+b.id+"?")){b.elements.cont.remove();var c=$(".graph_ids option:contains("+b.id+")"),d=c.parent();c.remove();UI.plot.del(b.id);delete a[b.id];d.trigger("change");UI.plot.go(a)}}))));c.elements.legend.html(d);var u=function(a){var b=c.elements.legend.find(".value"),d=1;if(typeof a=="undefined")b.eq(0).html("Latest:");else{var e=c.plot.getXAxes()[0],a=Math.min(e.max,a),a=Math.max(e.min,a);b.eq(0).html(UI.format.time(a/1E3))}for(var g in c.datasets){var f=
"&nbsp;";if(c.datasets[g].display){var e=UI.plot.yaxes[c.datasets[g].yaxistype].tickFormatter,h=c.datasets[g].data;if(a)for(var l in h){if(h[l][0]==a){f=e(h[l][1]);break}if(h[l][0]>a){if(l!=0){f=h[l];h=h[l-1];f=e(f[1]+(a-f[0])*(h[1]-f[1])/(h[0]-f[0]))}break}}else f=e(c.datasets[g].data[c.datasets[g].data.length-1][1])}b.eq(d).html(f);d++}};c.plot.getOptions();for(p in c.datasets)e=$("<input>").attr("type","checkbox").data("index",p).data("graph",c).click(function(){var a=$(this).data("graph");$(this).is(":checked")?
a.datasets[$(this).data("index")].display=true:a.datasets[$(this).data("index")].display=false;var b={};b[a.id]=a;UI.plot.go(b)}),c.datasets[p].display&&e.attr("checked","checked"),d.append($("<tr>").html($("<td>").html($("<label>").html(e).append($("<div>").addClass("series-color").css("background-color",c.datasets[p].color)).append(c.datasets[p].label))).append($("<td>").css("padding-right","2em").css("text-align","right").html($("<span>").addClass("value")).append($("<button>").text("X").addClass("close").data("index",
p).data("graph",c).click(function(){var b=$(this).data("index"),c=$(this).data("graph");if(confirm("Are you sure you want to remove "+c.datasets[b].label+" from "+c.id+"?")){c.datasets.splice(b,1);if(c.datasets.length==0){c.elements.cont.remove();var b=$(".graph_ids option:contains("+c.id+")"),d=b.parent();b.remove();d.trigger("change");UI.plot.del(c.id);delete a[c.id];UI.plot.go(a)}else{UI.plot.save(c);b={};b[c.id]=c;UI.plot.go(b)}}}))));u();var h=!1;c.elements.plot.on("plothover",function(a,b,c){if(b.x!=
h){u(b.x);h=b.x}if(c){a=$("<span>").append($("<h3>").text(c.series.label).prepend($("<div>").addClass("series-color").css("background-color",c.series.color))).append($("<table>").addClass("nolay").html($("<tr>").html($("<td>").text("Time:")).append($("<td>").html(UI.format.dateTime(c.datapoint[0]/1E3,"long")))).append($("<tr>").html($("<td>").text("Value:")).append($("<td>").html(c.series.yaxis.tickFormatter(c.datapoint[1],c.series.yaxis)))));UI.tooltip.show(b,a.children())}else UI.tooltip.hide()}).on("mouseout",
function(){u()})}}},b)}},save:function(a){var b={id:a.id,xaxis:a.xaxis,datasets:[]},c;for(c in a.datasets)b.datasets.push({origin:a.datasets[c].origin,datatype:a.datasets[c].datatype});a=mist.stored.get().graphs||{};a[b.id]=b;mist.stored.set("graphs",a)},del:function(a){var b=mist.stored.get().graphs||{};delete b[a];mist.stored.set("graphs",b)},datatype:{getOptions:function(a){var b=$.extend(!0,{},UI.plot.datatype.templates.general),c=$.extend(!0,{},UI.plot.datatype.templates[a.datatype]),a=$.extend(!0,
c,a),a=$.extend(!0,b,a);switch(a.origin[0]){case "total":switch(a.datatype){case "cpuload":case "memload":break;default:a.label+=" (total)"}break;case "stream":case "protocol":a.label+=" ("+a.origin[1]+")"}var b=[],d;for(d in a.basecolor)c=a.basecolor[d],c+=50*(0.5-Math.random()),c=Math.round(c),c=Math.min(255,Math.max(0,c)),b.push(c);a.color="rgb("+b.join(",")+")";return a},templates:{general:{display:!0,datatype:"general",label:"",yaxistype:"amount",data:[],lines:{show:!0},points:{show:!1},getdata:function(){var a=
mist.data.totals["stream"==this.origin[0]?this.origin[1]:"all_streams"]["protocol"==this.origin[0]?this.origin[1]:"all_protocols"][this.datatype];return this.data=a}},cpuload:{label:"CPU use",yaxistype:"percentage",basecolor:[237,194,64],cores:1,getdata:function(){var a=!1,b;for(b in this.data)this.data[b][0]<1E3*(mist.data.config.time-600)&&(a=b);!1!==a&&this.data.splice(0,Number(a)+1);this.data.push([1E3*mist.data.config.time,mist.data.capabilities.cpu_use/10]);return this.data}},memload:{label:"Memory load",
yaxistype:"percentage",basecolor:[175,216,248],getdata:function(){var a=!1,b;for(b in this.data)this.data[b][0]<1E3*(mist.data.config.time-600)&&(a=b);!1!==a&&this.data.splice(0,Number(a)+1);this.data.push([1E3*mist.data.config.time,mist.data.capabilities.load.memory]);return this.data}},clients:{label:"Connections",basecolor:[203,75,75]},upbps:{label:"Bandwidth up",yaxistype:"bytespersec",basecolor:[77,167,77]},downbps:{label:"Bandwidth down",yaxistype:"bytespersec",basecolor:[148,64,237]}}},yaxes:{percentage:{name:"percentage",
color:"black",tickColor:0,tickDecimals:0,tickFormatter:function(a){return UI.format.addUnit(UI.format.number(a),"%")},tickLength:0,min:0,max:100},amount:{name:"amount",color:"black",tickColor:0,tickDecimals:0,tickFormatter:function(a){return UI.format.number(a)},tickLength:0,min:0},bytespersec:{name:"bytespersec",color:"black",tickColor:0,tickDecimals:1,tickFormatter:function(a){return UI.format.bytes(a,!0)},tickLength:0,ticks:function(a){var b=0.3*Math.sqrt($(".graph").first().height()),b=(a.max-
a.min)/b,c=Math.floor(Math.log(Math.abs(b))/Math.log(1024)),d=b/Math.pow(1024,c),e=-Math.floor(Math.log(d)/Math.LN10),g=a.tickDecimals;null!=g&&e>g&&(e=g);var m=Math.pow(10,-e),d=d/m,f;if(1.5>d)f=1;else if(3>d){if(f=2,2.25<d&&(null==g||e+1<=g))f=2.5,++e}else f=7.5>d?5:10;f=f*m*Math.pow(1024,c);null!=a.minTickSize&&f<a.minTickSize&&(f=a.minTickSize);a.delta=b;a.tickDecimals=Math.max(0,null!=g?g:e);a.tickSize=f;b=[];c=a.tickSize*Math.floor(a.min/a.tickSize);e=0;g=Number.NaN;do m=g,g=c+e*a.tickSize,
b.push(g),++e;while(g<a.max&&g!=m);return b},min:0}},xaxes:{time:{name:"time",mode:"time",timezone:"browser",ticks:5}}},draggable:function(a){a.attr("draggable",!0);a.on("dragstart",function(a){$(this).css("opacity",0.4).data("dragstart",{click:{x:a.originalEvent.pageX,y:a.originalEvent.pageY},ele:{x:this.offsetLeft,y:this.offsetTop}})}).on("dragend",function(a){var c=$(this).data("dragstart"),d=c.ele.x-c.click.x+a.originalEvent.pageX,a=c.ele.y-c.click.y+a.originalEvent.pageY;$(this).css({opacity:1,
top:a,left:d,right:"auto",bottom:"auto"})});a.parent().on("dragleave",function(){})},format:{time:function(a,b){var c=new Date(1E3*a),d=[];d.push(("0"+c.getHours()).slice(-2));d.push(("0"+c.getMinutes()).slice(-2));"short"!=b&&d.push(("0"+c.getSeconds()).slice(-2));return d.join(":")},date:function(a,b){var c=new Date(1E3*a),d="Sun Mon Tue Wed Thu Fri Sat".split(" "),e=[];"long"==b&&e.push(d[c.getDay()]);e.push(("0"+c.getDate()).slice(-2));e.push("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ")[c.getMonth()]);
"short"!=b&&e.push(c.getFullYear());return e.join(" ")},dateTime:function(a,b){return UI.format.date(a,b)+", "+UI.format.time(a,b)},duration:function(a){var b=[0.001,1E3,60,60,24,7,1E9],c="ms sec min hr day week".split(" "),d={},e;for(e in c){var a=a/b[e],g=Math.round(a%b[Number(e)+1]);d[c[e]]=g;a-=g}var m;for(e=c.length-1;0<=e;e--)if(0<d[c[e]]){m=c[e];break}b=$("<span>");switch(m){case "week":b.append(UI.format.addUnit(d.week,"wks, ")).append(UI.format.addUnit(d.day,"days"));break;case "day":b.append(UI.format.addUnit(d.day,
"days, ")).append(UI.format.addUnit(d.hr,"hrs"));break;default:b.append([("0"+d.hr).slice(-2),("0"+d.min).slice(-2),("0"+d.sec).slice(-2)+(d.ms?"."+d.ms:"")].join(":"))}return b[0].innerHTML},number:function(a){if(isNaN(Number(a))||0==a)return a;var b=Math.pow(10,3-Math.floor(Math.log(a)/Math.LN10)-1),a=Math.round(a*b)/b;if(1E4<a){number=a.toString().split(".");for(a=/(\d+)(\d{3})/;a.test(number[0]);)number[0]=number[0].replace(a,"$1 $2");a=number.join(".")}return a},status:function(a){var b=$("<span>");
if("undefined"==typeof a.online)return b.text("Unknown, checking.."),"undefined"!=typeof a.error&&b.text(a.error),b;switch(a.online){case -1:b.text("Enabling");break;case 0:b.text("Unavailable").addClass("red");break;case 1:b.text("Active").addClass("green");break;case 2:b.text("Standby").addClass("orange");break;default:b.text(a.online)}"error"in a&&b.text(a.error);return b},capital:function(a){return a.charAt(0).toUpperCase()+a.substring(1)},addUnit:function(a,b){var c=$("<span>").html(a);c.append($("<span>").addClass("unit").html(b));
return c[0].innerHTML},bytes:function(a,b){var c="bytes KiB MiB GiB TiB PiB".split(" ");if(0==a)unit=c[0];else{var d=Math.floor(Math.log(Math.abs(a))/Math.log(1024));0>d?unit=c[0]:(a/=Math.pow(1024,d),unit=c[d])}return UI.format.addUnit(UI.format.number(a),unit+(b?"/s":""))}},navto:function(a,b){var c=location.hash,d=c.split("@");d[0]=[mist.user.name,mist.user.host].join("&");d[1]=[a,b].join("&");"undefined"!=typeof screenlog&&screenlog.navto(d[1]);location.hash=d.join("@");location.hash==c&&$(window).trigger("hashchange")},
showTab:function(a,b){var c=UI.elements.main;if(mist.user.loggedin&&!("ui_settings"in mist.data))c.html("Loading.."),mist.send(function(){UI.showTab(a,b)},{ui_settings:!0});else{var d=UI.elements.menu.removeClass("hide").find('.plain:contains("'+a+'")').closest(".button");0<d.length&&(UI.elements.menu.find(".button.active").removeClass("active"),d.addClass("active"));if("undefined"!=typeof mistvideo)for(var e in mistvideo)if("embedded"in mistvideo[e])for(var g in mistvideo[e].embedded)try{mistvideo[e].embedded[g].player.unload()}catch(m){}UI.interval.clear();
c.html($("<h2>").text(a));switch(a){case "Login":if(mist.user.loggedin){UI.navto("Overview");return}UI.elements.menu.addClass("hide");UI.elements.connection.status.text("Disconnected").removeClass("green").addClass("red");c.append(UI.buildUI([{type:"help",help:"Please provide your account details.<br>You were asked to set these when MistController was started for the first time. If you did not yet set any account details, log in with your desired credentials to create a new account."},{label:"Host",
help:"Url location of the MistServer API. Generally located at http://MistServerIP:4242/api","default":"http://localhost:4242/api",pointer:{main:mist.user,index:"host"}},{label:"Username",help:"Please enter your username here.",validate:["required"],pointer:{main:mist.user,index:"name"}},{label:"Password",type:"password",help:"Please enter your password here.",validate:["required"],pointer:{main:mist.user,index:"rawpassword"}},{type:"buttons",buttons:[{label:"Login",type:"save","function":function(){mist.user.password=
MD5(mist.user.rawpassword);delete mist.user.rawpassword;mist.send(function(){UI.navto("Overview")})}}]}]));break;case "Create a new account":UI.elements.menu.addClass("hide");c.append($("<p>").text("No account has been created yet in the MistServer at ").append($("<i>").text(mist.user.host)).append("."));c.append(UI.buildUI([{type:"buttons",buttons:[{label:"Select other host",type:"cancel",css:{"float":"left"},"function":function(){UI.navto("Login")}}]},{type:"custom",custom:$("<br>")},{label:"Desired username",
type:"str",validate:["required"],help:"Enter your desired username. In the future, you will need this to access the Management Interface.",pointer:{main:mist.user,index:"name"}},{label:"Desired password",type:"password",validate:["required",function(a,b){$(".match_password").not($(b)).trigger("change");return false}],help:"Enter your desired password. In the future, you will need this to access the Management Interface.",pointer:{main:mist.user,index:"rawpassword"},classes:["match_password"]},{label:"Repeat password",
type:"password",validate:["required",function(a,b){return a!=$(".match_password").not($(b)).val()?{msg:'The fields "Desired password" and "Repeat password" do not match.',classes:["red"]}:false}],help:"Repeat your desired password.",classes:["match_password"]},{type:"buttons",buttons:[{type:"save",label:"Create new account","function":function(){mist.send(function(){UI.navto("Account created")},{authorize:{new_username:mist.user.name,new_password:mist.user.rawpassword}});mist.user.password=MD5(mist.user.rawpassword);
delete mist.user.rawpassword}}]}]));break;case "Account created":UI.elements.menu.addClass("hide");c.append($("<p>").text("Your account has been created succesfully.")).append(UI.buildUI([{type:"text",text:"Would you like to enable all (currently) available protocols with their default settings?"},{type:"buttons",buttons:[{label:"Enable protocols",type:"save","function":function(){if(mist.data.config.protocols)c.append("Unable to enable all protocols as protocol settings already exist.<br>");else{c.append("Retrieving available protocols..<br>");
mist.send(function(a){var b=[],d;for(d in a.capabilities.connectors)if(a.capabilities.connectors[d].required)c.append('Could not enable protocol "'+d+'" because it has required settings.<br>');else{b.push({connector:d});c.append('Enabled protocol "'+d+'".<br>')}c.append("Saving protocol settings..<br>");mist.send(function(){c.append("Protocols enabled. Redirecting..");setTimeout(function(){UI.navto("Overview")},5E3)},{config:{protocols:b}})},{capabilities:true})}}},{label:"Skip",type:"cancel","function":function(){UI.navto("Overview")}}]}]));
break;case "Overview":var f=$("<span>").text("Loading.."),q=$("<span>"),p=$("<span>").addClass("logs"),l=$("<span>"),u=$("<span>"),h=$("<span>"),i=$("<span>");c.append(UI.buildUI([{type:"help",help:"You can find most basic information about your MistServer here.<br>You can also set the debug level and force a save to the config.json file that MistServer uses to save your settings. "},{type:"span",label:"Version",pointer:{main:mist.data.config,index:"version"}},{type:"span",label:"Version check",value:f,
LTSonly:!0},{type:"span",label:"Server time",value:u},{type:"span",label:"Configured streams",value:mist.data.streams?Object.keys(mist.data.streams).length:0},{type:"span",label:"Active streams",value:q},{type:"span",label:"Current connections",value:l},{type:"span",label:"Enabled protocols",value:h},{type:"span",label:"Disabled protocols",value:i},{type:"span",label:"Recent problems",value:p},$("<br>"),{type:"str",label:"Human readable name",pointer:{main:mist.data.config,index:"name"},help:"You can name your MistServer here for personal use. You'll still need to set host name within your network yourself."},
{type:"debug",label:"Debug level",pointer:{main:mist.data.config,index:"debug"},help:"You can set the amount of debug information MistServer saves in the log. A full reboot of MistServer is required before some components of MistServer can post debug information."},{type:"checkbox",label:"Force configurations save",pointer:{main:mist.data,index:"save"},help:"Tick the box in order to force an immediate save to the config.json MistServer uses to save your settings. Saving will otherwise happen upon closing MistServer. Don't forget to press save after ticking the box."},
{type:"buttons",buttons:[{type:"save",label:"Save","function":function(){var a={config:mist.data.config};if(mist.data.save)a.save=mist.data.save;mist.send(function(){UI.navto("Overview")},a)}}]}]));if(mist.data.LTS){var k=function(){var a=mist.stored.get().update||{};"uptodate"in a?a.error?f.addClass("red").text(a.error):a.uptodate?f.text("Your version is up to date.").addClass("green"):f.addClass("red").text("Version outdated!").append($("<button>").text("Update").css({"font-size":"1em","margin-left":"1em"}).click(function(){if(confirm("Are you sure you want to execute a rolling update?")){f.addClass("orange").removeClass("red").text("Rolling update command sent..");
mist.stored.del("update");mist.send(function(){UI.navto("Overview")},{autoupdate:true})}})):f.text("Unknown")};if(!mist.stored.get().update||36E5<(new Date).getTime()-mist.stored.get().update.lastchecked){var j=mist.stored.get().update||{};j.lastchecked=(new Date).getTime();mist.send(function(a){mist.stored.set("update",$.extend(true,j,a.update));k()},{checkupdate:!0})}else k()}else f.text("");g=function(){var a={totals:{fields:["clients"],start:-10},active_streams:true};if(!("cabailities"in mist.data))a.capabilities=
true;mist.send(function(){ga()},a)};var ga=function(){q.text("active_streams"in mist.data?mist.data.active_streams?mist.data.active_streams.length:0:"?");if("totals"in mist.data&&"all_streams"in mist.data.totals)var a=mist.data.totals.all_streams.all_protocols.clients,a=a.length?UI.format.number(a[a.length-1][1]):0;else a="Loading..";l.text(a);u.text(UI.format.dateTime(mist.data.config.time,"long"));p.html("");var a=0,b;for(b in mist.data.log){var c=mist.data.log[b];if(["FAIL","ERROR"].indexOf(c[1])>
-1){a++;var d=$("<span>").addClass("content").addClass("red"),e=c[2].split("|");for(b in e)d.append($("<span>").text(e[b]));p.append($("<div>").append($("<span>").append(UI.format.time(c[0]))).append(d));if(a==5)break}}a==0&&p.html("None.");a=[];c=[];for(b in mist.data.config.protocols){d=mist.data.config.protocols[b];a.indexOf(d.connector)>-1||a.push(d.connector)}h.text(a.length?a.join(", "):"None.");if("capabilities"in mist.data){for(b in mist.data.capabilities.connectors)a.indexOf(b)==-1&&c.push(b);
i.text(c.length?c.join(", "):"None.")}else i.text("Loading..")};g();ga();UI.interval.set(g,3E4);break;case "Protocols":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a)},{capabilities:!0});c.append("Loading..");return}var z=$("<tbody>");c.append(UI.buildUI([{type:"help",help:"You can find an overview of all the protocols and their relevant information here. You can add, edit or delete protocols."}])).append($("<button>").text("Delete all protocols").click(function(){if(confirm("Are you sure you want to delete all currently configured protocols?")){mist.data.config.protocols=
LTSonly:!0},{type:"span",label:"Server time",value:u},{type:"span",label:"Licensed to","default":"unknown",pointer:{main:mist.data.config.license,index:"user"},LTSonly:!0},{type:"span",label:"Configured streams",value:mist.data.streams?Object.keys(mist.data.streams).length:0},{type:"span",label:"Active streams",value:q},{type:"span",label:"Current connections",value:l},{type:"span",label:"Enabled protocols",value:h},{type:"span",label:"Disabled protocols",value:i},{type:"span",label:"Recent problems",
value:p},$("<br>"),{type:"str",label:"Human readable name",pointer:{main:mist.data.config,index:"name"},help:"You can name your MistServer here for personal use. You'll still need to set host name within your network yourself."},{type:"debug",label:"Debug level",pointer:{main:mist.data.config,index:"debug"},help:"You can set the amount of debug information MistServer saves in the log. A full reboot of MistServer is required before some components of MistServer can post debug information."},{type:"checkbox",
label:"Force configurations save",pointer:{main:mist.data,index:"save"},help:"Tick the box in order to force an immediate save to the config.json MistServer uses to save your settings. Saving will otherwise happen upon closing MistServer. Don't forget to press save after ticking the box."},{type:"buttons",buttons:[{type:"save",label:"Save","function":function(){var a={config:mist.data.config};if(mist.data.save)a.save=mist.data.save;mist.send(function(){UI.navto("Overview")},a)}}]}]));if(mist.data.LTS){var k=
function(a){"uptodate"in a?a.error?f.addClass("red").text(a.error):a.uptodate?f.text("Your version is up to date.").addClass("green"):f.addClass("red").text("Version outdated!").append($("<button>").text("Update").css({"font-size":"1em","margin-left":"1em"}).click(function(){if(confirm("Are you sure you want to execute a rolling update?")){f.addClass("orange").removeClass("red").text("Rolling update command sent..");mist.stored.del("update");mist.send(function(){UI.navto("Overview")},{autoupdate:true})}})):
f.text("Unknown")};if(!mist.stored.get().update||36E5<(new Date).getTime()-mist.stored.get().update.lastchecked){var j={};j.lastchecked=(new Date).getTime();mist.send(function(a){mist.stored.set("update",j);k(a.update)},{checkupdate:!0})}else mist.send(function(a){k(a.update)},{update:!0})}else f.text("");g=function(){var a={totals:{fields:["clients"],start:-10},active_streams:true};if(!("cabailities"in mist.data))a.capabilities=true;mist.send(function(){ga()},a)};var ga=function(){q.text("active_streams"in
mist.data?mist.data.active_streams?mist.data.active_streams.length:0:"?");if("totals"in mist.data&&"all_streams"in mist.data.totals)var a=mist.data.totals.all_streams.all_protocols.clients,a=a.length?UI.format.number(a[a.length-1][1]):0;else a="Loading..";l.text(a);u.text(UI.format.dateTime(mist.data.config.time,"long"));p.html("");var a=0,b;for(b in mist.data.log){var c=mist.data.log[b];if(["FAIL","ERROR"].indexOf(c[1])>-1){a++;var d=$("<span>").addClass("content").addClass("red"),e=c[2].split("|");
for(b in e)d.append($("<span>").text(e[b]));p.append($("<div>").append($("<span>").append(UI.format.time(c[0]))).append(d));if(a==5)break}}a==0&&p.html("None.");a=[];c=[];for(b in mist.data.config.protocols){d=mist.data.config.protocols[b];a.indexOf(d.connector)>-1||a.push(d.connector)}h.text(a.length?a.join(", "):"None.");if("capabilities"in mist.data){for(b in mist.data.capabilities.connectors)a.indexOf(b)==-1&&c.push(b);i.text(c.length?c.join(", "):"None.")}else i.text("Loading..")};g();ga();UI.interval.set(g,
3E4);break;case "Protocols":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a)},{capabilities:!0});c.append("Loading..");return}var z=$("<tbody>");c.append(UI.buildUI([{type:"help",help:"You can find an overview of all the protocols and their relevant information here. You can add, edit or delete protocols."}])).append($("<button>").text("Delete all protocols").click(function(){if(confirm("Are you sure you want to delete all currently configured protocols?")){mist.data.config.protocols=
[];mist.send(function(){UI.navto("Protocols")},{config:mist.data.config})}})).append($("<button>").text("Enable default protocols").click(function(){var a=Object.keys(mist.data.capabilities.connectors),b;for(b in mist.data.config.protocols){var c=a.indexOf(mist.data.config.protocols[b].connector);c>-1&&a.splice(c,1)}var d=[];for(b in a)(!("required"in mist.data.capabilities.connectors[a[b]])||Object.keys(mist.data.capabilities.connectors[a[b]].required).length==0)&&d.push(a[b]);c="Click OK to enable disabled protocols with their default settings:\n ";
c=d.length?c+d.join(", "):c+"None.";if(d.length!=a.length){a=a.filter(function(a){return d.indexOf(a)<0});c=c+("\n\nThe following protocols can only be set manually:\n "+a.join(", "))}if(confirm(c)&&d.length){for(b in d)mist.data.config.protocols.push({connector:d[b]});mist.send(function(){UI.navto("Protocols")},{config:mist.data.config})}})).append("<br>").append($("<button>").text("New protocol").click(function(){UI.navto("Edit Protocol")}).css("clear","both")).append($("<table>").html($("<thead>").html($("<tr>").html($("<th>").text("Protocol")).append($("<th>").text("Status")).append($("<th>").text("Settings")).append($("<th>")))).append(z));
var K=function(){function a(b){var c=mist.data.capabilities.connectors[b.connector];if(!c)return"";var d=[],e=["required","optional"],g;for(g in e)for(var t in c[e[g]])b[t]&&b[t]!=""?d.push(t+": "+b[t]):c[e[g]][t]["default"]&&d.push(t+": "+c[e[g]][t]["default"]);return $("<span>").addClass("description").text(d.join(", "))}z.html("");for(var b in mist.data.config.protocols){var c=mist.data.config.protocols[b];z.append($("<tr>").data("index",b).append($("<td>").text(c.connector)).append($("<td>").html(UI.format.status(c))).append($("<td>").html(a(c))).append($("<td>").css("text-align",

View file

@ -600,8 +600,7 @@ var UI = {
case 'checklist':
$field = $('<div>').addClass('checkcontainer');
$controls = $('<div>').addClass('controls');
$checklist = $('<div>').addClass('checklist');
$field.append($controls).append($checklist);
/* All tends to be confusing: disable it for now
$controls.append(
$('<label>').text('All').prepend(
$('<input>').attr('type','checkbox').click(function(){
@ -614,6 +613,10 @@ var UI = {
})
)
);
$field.append($controls);
*/
$checklist = $('<div>').addClass('checklist');
$field.append($checklist);
for (var i in e.checklist) {
if (typeof e.checklist[i] == 'string') {
e.checklist[i] = [e.checklist[i], e.checklist[i]];
@ -1388,7 +1391,6 @@ var UI = {
break;
case 'coords':
//TODO
break;
}
}
@ -2067,6 +2069,15 @@ var UI = {
type: 'span',
label: 'Server time',
value: $servertime
},{
type: 'span',
label: 'Licensed to',
'default': 'unknown',
pointer: {
main: mist.data.config.license,
index: 'user'
},
LTSonly: true
},{
type: 'span',
label: 'Configured streams',
@ -2133,8 +2144,7 @@ var UI = {
}
]));
if (mist.data.LTS) {
function update_update() {
var info = mist.stored.get().update || {};
function update_update(info) {
if (!('uptodate' in info)) {
$versioncheck.text('Unknown');
return;
@ -2163,15 +2173,17 @@ var UI = {
}
if ((!mist.stored.get().update) || ((new Date()).getTime()-mist.stored.get().update.lastchecked > 3600e3)) {
var update = mist.stored.get().update || {};
var update = {};
update.lastchecked = (new Date()).getTime();
mist.send(function(d){
mist.stored.set('update',$.extend(true,update,d.update));
update_update();
mist.stored.set('update',update);
update_update(d.update);
},{checkupdate: true});
}
else {
update_update();
mist.send(function(d){
update_update(d.update);
},{update: true});
}
}
else {
@ -2620,6 +2632,33 @@ var UI = {
$main.append(
$('<span>').addClass('description').text('Choose a stream below.')
).append($shortcuts);
//if there is a JPG output, add actual thumnails \o/
var thumbnails = false;
///\todo activate this code when the backend is ready
/*
if (UI.findOutput('JPG')) {
var jpgport = false;
//find the http port and make sure JPG is enabled
for (var i in mist.data.config.protocols) {
var protocol = mist.data.config.protocols[i];
if ((protocol.connector == 'HTTP') || (protocol.connector == 'HTTP.exe')) {
jpgport = (protocol.port ? ':'+protocol.port : ':8080');
}
if ((protocol.connector == 'JPG') || (protocol.connector == 'JPG.exe')) {
thumbnails = true;
}
}
if ((thumbnails) && (jpgport)) {
//now we get to use it as a magical function wheee!
jpgport = parseURL(mist.user.host).host+jpgport;
thumbnails = function(streamname) {
return 'http://'+jpgport+'/'+encodeURIComponent(streamname)+'.jpg';
}
}
}
*/
for (var i in select) {
var streamname = select[i];
var source = '';
@ -2649,6 +2688,17 @@ var UI = {
UI.navto('Embed',$(this).closest('div').attr('data-stream'));
});
var $image = $('<span>').addClass('image');
if ((thumbnails) && (folders.indexOf(streamname) == -1)) {
//there is a JPG output and this isn't a folder
$image.append(
$('<img>').attr('src',thumbnails(streamname)).error(function(){
$(this).hide();
})
);
}
//its a wildcard stream
if (streamname.indexOf('+') > -1) {
var streambits = streamname.split('+');
source = mist.data.streams[streambits[0]].source+streambits[1];
@ -2658,6 +2708,7 @@ var UI = {
}
else {
source = mist.data.streams[streamname].source;
//its a folder stream
if (folders.indexOf(streamname) > -1) {
$preview = '';
$embed = '';

View file

@ -89,6 +89,7 @@ namespace Mist {
//capa["optional"]["wrappers"]["allowed"].append("polytrope"); //currently borked
capa["optional"]["wrappers"]["allowed"].append("flash_strobe");
capa["optional"]["wrappers"]["allowed"].append("silverlight");
capa["optional"]["wrappers"]["allowed"].append("img");
capa["optional"]["wrappers"]["option"] = "--wrappers";
capa["optional"]["wrappers"]["short"] = "w";
cfg->addConnectorOptions(8080, capa);
@ -589,6 +590,11 @@ namespace Mist {
response.append((char*)video_js, (size_t)video_js_len);
used = true;
}
if (it->asStringRef() == "img"){
#include "img.js.h"
response.append((char*)img_js, (size_t)img_js_len);
used = true;
}
if (!used) {
WARN_MSG("Unknown player type: %s",it->asStringRef().c_str());
}