Shared memory rewrite
This commit is contained in:
parent
afcddbfca6
commit
cd2fe225c5
81 changed files with 7775 additions and 5411 deletions
File diff suppressed because one or more lines are too long
|
@ -1,4 +1,4 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv='content-type' content='text/html;charset=utf-8' />
|
||||
<title>MistServer Manager</title>
|
||||
<title>MistServer MI</title>
|
5
lsp/jquery.js
vendored
5
lsp/jquery.js
vendored
File diff suppressed because one or more lines are too long
285
lsp/main.css
285
lsp/main.css
File diff suppressed because one or more lines are too long
423
lsp/main.js
423
lsp/main.js
|
@ -14,6 +14,8 @@ function consolelog() {
|
|||
}
|
||||
}
|
||||
|
||||
var ih = false;
|
||||
|
||||
function confirmDelete(question){
|
||||
return confirm(question);
|
||||
}
|
||||
|
@ -56,6 +58,151 @@ function formatTime(date){
|
|||
('00' + d.getSeconds()).slice(-2)
|
||||
].join(':');
|
||||
}
|
||||
/**
|
||||
* Format a time duration to something like "2 days, 00:00:00.000"
|
||||
* @param ms the duration to format in miliseconds
|
||||
*/
|
||||
function formatDuration(ms) {
|
||||
var secs = Math.floor(ms / 1000), mins = 0;
|
||||
ms = ms % 1000;
|
||||
if (secs >= 60) {
|
||||
mins = Math.floor(secs / 60);
|
||||
secs = secs % 60;
|
||||
}
|
||||
if (mins >= 60) {
|
||||
var hours = Math.floor(mins / 60);
|
||||
mins = mins % 60;
|
||||
}
|
||||
var string = ('00'+mins).slice(-2)+':'+('00'+secs).slice(-2)+'.'+('000'+ms).slice(-3);
|
||||
if (hours >= 24) {
|
||||
var days = Math.floor(hours / 24);
|
||||
hours = hours % 24;
|
||||
}
|
||||
if (hours > 0) {
|
||||
string = ('00'+hours).slice(-2)+':'+string;
|
||||
}
|
||||
if (days > 0) {
|
||||
string = days+' day'+(days > 1 ? 's' : '')+', '+string
|
||||
}
|
||||
return string;
|
||||
}
|
||||
/**
|
||||
* Capitalize the first letter
|
||||
* @param string the string
|
||||
*/
|
||||
function capFirstChar(string) {
|
||||
if (string.length <= 0) { return ''; }
|
||||
return string[0].toUpperCase() + string.slice(1);
|
||||
}
|
||||
/**
|
||||
* Flot tick generator for bandwidth
|
||||
* @param axis the axis
|
||||
*/
|
||||
function flotTicksBandwidthAxis(axis) {
|
||||
var range = axis.max - axis.min;
|
||||
var delta = range / 4;
|
||||
var start = axis.min;
|
||||
if (axis.max < 1024) { // unit: bytes/s
|
||||
if (delta > 100) { delta = Math.floor(delta/100)*100; start = Math.floor(start/100)*100; } // to lowest 100 bytes/s
|
||||
else if (delta > 10) { delta = Math.floor(delta/10)*10; start = Math.floor(start/10)*10; } // to lowest 10 bytes/s
|
||||
}
|
||||
else if (axis.max < 1048576) { //unit: kiB/s
|
||||
if (delta > 102400) { delta = Math.floor(delta/102400)*102400; start = Math.floor(start/102400)*102400; } //to lowest 100 kiB/s
|
||||
else if (delta > 10240) { delta = Math.floor(delta/10240)*10240; start = Math.floor(start/10240)*10240; } //to lowest 10 kiB/s
|
||||
else if (delta > 1024) { delta = Math.floor(delta/1024)*1024; start = Math.floor(start/1024)*1024; } //to lowest 1 kiB/s
|
||||
else { delta = Math.floor(delta/102.4)*102.4; start = Math.floor(start/102.4)*102.4; } //to lowest 0.1 kiB/s
|
||||
}
|
||||
else { //unit: miB/s
|
||||
if (delta > 104857600) { delta = Math.floor(delta/104857600)*104857600; start = Math.floor(start/104857600)*104857600; } //to lowest 100 miB/s
|
||||
else if (delta > 10485760) { delta = Math.floor(delta/10485760)*10485760; start = Math.floor(start/10485760)*10485760; } //to lowest 10 miB/s
|
||||
else if (delta > 1048576) { delta = Math.floor(delta/1048576)*1048576; start = Math.floor(start/1048576)*1048576; } //to lowest 1 miB/s
|
||||
else { delta = Math.floor(delta/104857.6)*104857.6; start = Math.floor(start/104857.6)*104857.6; } //to lowest 0.1 miB/s
|
||||
}
|
||||
var out = [];
|
||||
for (var i = start; i <= axis.max; i += delta) {
|
||||
out.push(i);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* Flot axis formatter for bandwidth
|
||||
* @param val the valuea
|
||||
* @param axis the axis
|
||||
*/
|
||||
function flotFormatBandwidthAxis(val,axis) {
|
||||
if (val < 0) { var sign = '-'; }
|
||||
else { var sign = ''; }
|
||||
val = Math.abs(val);
|
||||
|
||||
if (val < 1024) { return sign+Math.round(val)+' bytes/s'; } // 0 bytes/s through 1023 bytes/s
|
||||
if (val < 10235) { return sign+(val/1024).toFixed(2)+' kiB/s'; } // 1.00 kiB/s through 9.99 kiB/s
|
||||
if (val < 102449) { return sign+(val/1024).toFixed(1)+' kiB/s'; } // 10.0 kiB/s through 99.9 kiB/s
|
||||
if (val < 1048064) { return sign+Math.round(val/1024)+' kiB/s'; } // 100 kiB/s through 1023 kiB/s
|
||||
if (val < 10480518) { return sign+(val/1048576).toFixed(2)+' miB/s'; } // 1.00 miB/s through 9.99 miB/s
|
||||
if (val < 104805172) { return sign+(val/1048576).toFixed(1)+' miB/s'; } // 10.0 miB/s through 99.9 miB/s
|
||||
return sign+Math.round(val/1048576)+' miB/s'; // 100 miB/s and up
|
||||
}
|
||||
/**
|
||||
* Converts the statistics data into something flot understands
|
||||
* @param stats the statistics.totals object
|
||||
* @param cumulative cumulative mode if true
|
||||
*/
|
||||
function convertStatisticsToFlotFormat(stats,islive) {
|
||||
var plotdata = [
|
||||
{ label: 'Viewers', data: []},
|
||||
{ label: 'Bandwidth (Up)', data: [], yaxis: 2},
|
||||
{ label: 'Bandwidth (Down)', data: [], yaxis: 2}
|
||||
];
|
||||
|
||||
var oldtimestamp = 0;
|
||||
var i = 0, up = 0, down = 0;
|
||||
for (var timestamp in stats) {
|
||||
if (islive) {
|
||||
i++;
|
||||
up += stats[timestamp].up;
|
||||
down += stats[timestamp].down;
|
||||
//average over 5 seconds to prevent super spiky unreadable graph
|
||||
if ((i % 5) == 0) {
|
||||
plotdata[0].data.push([Number(timestamp)*1000,stats[timestamp].count]);
|
||||
plotdata[1].data.push([Number(timestamp)*1000,up/5]);
|
||||
plotdata[2].data.push([Number(timestamp)*1000,down/5]);
|
||||
up = 0;
|
||||
down = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
var dt = timestamp - oldtimestamp;
|
||||
if (stats[oldtimestamp]) {
|
||||
var up = (stats[timestamp].up - stats[oldtimestamp].up)/dt;
|
||||
var down = (stats[timestamp].down - stats[oldtimestamp].down)/dt;
|
||||
}
|
||||
else {
|
||||
var up = stats[timestamp].up;
|
||||
var down = stats[timestamp].down;
|
||||
}
|
||||
plotdata[0].data.push([Number(timestamp)*1000,stats[timestamp].count]);
|
||||
plotdata[1].data.push([Number(timestamp)*1000,up]);
|
||||
plotdata[2].data.push([Number(timestamp)*1000,down]);
|
||||
oldtimestamp = timestamp;
|
||||
}
|
||||
}
|
||||
for (var timestamp in stats) {
|
||||
var dt = timestamp - oldtimestamp;
|
||||
plotdata[0].data.push([Number(timestamp)*1000,stats[timestamp].count]);
|
||||
if (stats[oldtimestamp]) {
|
||||
var up = (stats[timestamp].up - stats[oldtimestamp].up)/dt;
|
||||
var down = (stats[timestamp].down - stats[oldtimestamp].down)/dt;
|
||||
}
|
||||
else {
|
||||
var up = stats[timestamp].up;
|
||||
var down = stats[timestamp].down;
|
||||
}
|
||||
plotdata[1].data.push([Number(timestamp)*1000,up]);
|
||||
plotdata[2].data.push([Number(timestamp)*1000,down]);
|
||||
oldtimestamp = timestamp;
|
||||
}
|
||||
return plotdata;
|
||||
}
|
||||
/**
|
||||
* Check if an URL points to a live datastream or a recorded file
|
||||
* @param url the url in question
|
||||
|
@ -136,9 +283,10 @@ function applyInput(){
|
|||
|
||||
//apply the inputs
|
||||
$('input.isSetting,select.isSetting').each(function(){
|
||||
|
||||
var objpath = findObjPath($(this));
|
||||
|
||||
if ($(this).val() == '') {
|
||||
if (($(this).val() == '') || ($(this).val() == 0)) {
|
||||
eval('delete '+objpath+';');
|
||||
}
|
||||
else {
|
||||
|
@ -163,6 +311,37 @@ function findObjPath($element) {
|
|||
}
|
||||
}
|
||||
|
||||
function ihAddBalloons() {
|
||||
var page = settings.ih.pages[settings.currentpage];
|
||||
if (!page) { return; }
|
||||
|
||||
//something with pageinfo
|
||||
if (page.pageinfo) {
|
||||
$('#page').prepend(
|
||||
$('<div>').addClass('ih-balloon').addClass('pageinfo').html(page.pageinfo)
|
||||
);
|
||||
}
|
||||
|
||||
for (inputid in page.inputs) {
|
||||
$('#'+inputid).parent().prepend(
|
||||
$('<div>').addClass('ih-balloon').addClass('inputinfo').attr('data-for',inputid).html(page.inputs[inputid]).hide()
|
||||
);
|
||||
$('#'+inputid).focus(function(){
|
||||
$('.ih-balloon[data-for='+$(this).attr('id')+']').show();
|
||||
$('.ih-balloon.pageinfo').hide();
|
||||
}).blur(function(){
|
||||
$('.ih-balloon[data-for='+$(this).attr('id')+']').hide();
|
||||
$('.ih-balloon.pageinfo').show();
|
||||
});
|
||||
}
|
||||
$('#page label').each(function(){
|
||||
$(this)
|
||||
});
|
||||
}
|
||||
function ihMakeBalloon(contents,forid) {
|
||||
return $('<div>').addClass('ih-balloon').attr('data-for',forid).html(contents).hide();
|
||||
}
|
||||
|
||||
function getData(callBack,sendData,timeOut,doShield){
|
||||
timeOut = timeOut | 30000;
|
||||
var data = {};
|
||||
|
@ -283,6 +462,81 @@ function getData(callBack,sendData,timeOut,doShield){
|
|||
|
||||
var jqxhr = $.ajax(obj);
|
||||
}
|
||||
|
||||
function getWikiData(url,callBack) {
|
||||
var wikiHost = 'http://rework.mistserver.org'; //must be changed when rework goes live
|
||||
|
||||
$('#message').removeClass('red').text('Connecting to the MistServer wiki..').append(
|
||||
$('<br>')
|
||||
).append(
|
||||
$('<a>').text('Cancel request').click(function(){
|
||||
jqxhr.abort();
|
||||
})
|
||||
);
|
||||
|
||||
var obj = {
|
||||
'url': wikiHost+url,
|
||||
'type': 'GET',
|
||||
'crossDomain': true,
|
||||
'data': {
|
||||
'skin': 'plain'
|
||||
},
|
||||
'error':function(jqXHR,textStatus,errorThrown){
|
||||
switch (textStatus) {
|
||||
case 'timeout':
|
||||
textStatus = $('<i>').text('The connection timed out. ');
|
||||
break;
|
||||
case 'abort':
|
||||
textStatus = $('<i>').text('The connection was aborted. ');
|
||||
break;
|
||||
default:
|
||||
textStatus = $('<i>').text(textStatus+'. ').css('text-transform','capitalize');
|
||||
}
|
||||
$('#message').addClass('red').text('An error occurred while attempting to communicate with the MistServer wiki:').append(
|
||||
$('<br>')
|
||||
).append(
|
||||
textStatus
|
||||
).append(
|
||||
$('<a>').text('Send server request again').click(function(){
|
||||
getWikiData(url,callback);
|
||||
})
|
||||
);
|
||||
},
|
||||
'success': function(returnedData){
|
||||
$('#message').text('Wiki data received');
|
||||
|
||||
//convert to DOM elements
|
||||
//returnedData = $.parseHTML(returnedData);
|
||||
returnedData = $(returnedData);
|
||||
|
||||
//fix broken slash-links in the imported data
|
||||
returnedData.find('a[href]').each(function(){
|
||||
if ((this.hostname == '') || (this.hostname == undefined)) {
|
||||
$(this).attr('href',wikiHost+$(this).attr('href'));
|
||||
}
|
||||
if (!$(this).attr('target')) {
|
||||
$(this).attr('target','_blank');
|
||||
}
|
||||
}).find('img[src]').each(function(){
|
||||
var a = $('<a>').attr('href',$(this).attr('src'));
|
||||
if ((a.hostname == '') || (a.hostname == undefined)) {
|
||||
$(this).attr('src',wikiHost+$(this).attr('src'));
|
||||
}
|
||||
});
|
||||
|
||||
consolelog('['+(new Date).toTimeString().split(' ')[0]+']','Received wiki data:',returnedData);
|
||||
|
||||
if (callBack) {
|
||||
callBack(returnedData);
|
||||
}
|
||||
$('#message').text('Last communication with the MistServer wiki at '+formatTime((new Date).getTime()/1000));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
var jqxhr = $.ajax(obj);
|
||||
}
|
||||
|
||||
function saveAndReload(tabName){
|
||||
var sendData = $.extend(true,{},settings.settings);
|
||||
delete sendData.logs;
|
||||
|
@ -345,12 +599,8 @@ function updateOverview() {
|
|||
var streams = 0;
|
||||
var streamsOnline = 0;
|
||||
|
||||
for (var index in data.statistics) {
|
||||
if (data.statistics[index].curr) {
|
||||
for (viewer in data.statistics[index].curr) {
|
||||
viewers++;
|
||||
}
|
||||
}
|
||||
if (data.clients && data.clients.data) {
|
||||
viewers = data.clients.data.length;
|
||||
}
|
||||
|
||||
for (var index in data.streams) {
|
||||
|
@ -363,7 +613,9 @@ function updateOverview() {
|
|||
$('#cur_streams_online').text(streamsOnline+'/'+streams+' online');
|
||||
$('#cur_num_viewers').text(seperateThousands(viewers,' '));
|
||||
$('#settings-config-time').text(formatDateLong(data.config.time));
|
||||
});
|
||||
|
||||
settings.settings.statistics = data.statistics;
|
||||
},{clients: {}});
|
||||
}
|
||||
function updateProtocols() {
|
||||
getData(function(data){
|
||||
|
@ -380,6 +632,9 @@ function updateProtocols() {
|
|||
|
||||
function displayProtocolSettings(theProtocol) {
|
||||
var capabilities = settings.settings.capabilities.connectors[theProtocol.connector];
|
||||
if (!capabilities) {
|
||||
return '';
|
||||
}
|
||||
var settingsList = [];
|
||||
for (var index in capabilities.required) {
|
||||
if ((theProtocol[index]) && (theProtocol[index] != '')) {
|
||||
|
@ -478,20 +733,31 @@ function buildProtocolParameterFields(data,required,objpath) {
|
|||
return $container.html();
|
||||
}
|
||||
function updateStreams() {
|
||||
var streamlist = [];
|
||||
for (var stream in settings.settings.streams) {
|
||||
streamlist.push(stream);
|
||||
}
|
||||
getData(function(data){
|
||||
var datafields = {};
|
||||
for (var index in data.clients.fields) {
|
||||
datafields[data.clients.fields[index]] = index;
|
||||
}
|
||||
var viewers = {};
|
||||
for (var index in data.clients.data) {
|
||||
if (viewers[data.clients.data[index][datafields['stream']]]) {
|
||||
viewers[data.clients.data[index][datafields['stream']]]++;
|
||||
}
|
||||
else {
|
||||
viewers[data.clients.data[index][datafields['stream']]] = 1;
|
||||
}
|
||||
}
|
||||
for (var index in data.streams) {
|
||||
$('#status-of-'+index).html(formatStatus(data.streams[index]))
|
||||
$('#viewers-of-'+index).text(seperateThousands(viewers[index],' '));
|
||||
}
|
||||
for (var index in data.statistics) {
|
||||
var viewers = 0;
|
||||
if (data.statistics[index].curr) {
|
||||
for (var jndex in data.statistics[index].curr) {
|
||||
viewers++;
|
||||
}
|
||||
}
|
||||
$('#viewers-of-'+index).text(seperateThousands(viewers,' '));
|
||||
}
|
||||
});
|
||||
|
||||
settings.settings.statistics = data.statistics;
|
||||
},{clients:{}});
|
||||
}
|
||||
function filterTable() {
|
||||
var displayRecorded = $('#stream-filter-recorded').is(':checked');
|
||||
|
@ -886,12 +1152,19 @@ function conversionSelectInput(theFiles) {
|
|||
|
||||
applyInput();
|
||||
|
||||
var extension = settings.settings.conversion.convert._new_.output.split('.');
|
||||
if (extension[extension.length-1] != 'dtsc') {
|
||||
extension.push('dtsc');
|
||||
settings.settings.conversion.convert._new_.output = extension.join('.');
|
||||
}
|
||||
settings.settings.conversion.convert._new_.output = settings.settings.conversion.convert._new_.outputdir.replace(/\/$/,'')+'/'+settings.settings.conversion.convert._new_.output;
|
||||
delete settings.settings.conversion.convert._new_.outputdir;
|
||||
if ((settings.settings.conversion.convert._new_.video) && (settings.settings.conversion.convert._new_.video.fps)) {
|
||||
settings.settings.conversion.convert._new_.fpks = Math.floor(settings.settings.conversion.convert._new_.fps * 1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
settings.settings.conversion.convert['c_'+(new Date).getTime()] = settings.settings.conversion.convert._new_;
|
||||
delete settings.settings.conversion.convert._new_;
|
||||
saveAndReload('conversion');
|
||||
|
@ -1019,10 +1292,85 @@ function updateServerstats() {
|
|||
},{capabilities:true});
|
||||
}
|
||||
|
||||
function buildstreamembed(streamName,embedbase) {
|
||||
$('#liststreams .button.current').removeClass('current')
|
||||
$('#liststreams .button').filter(function(){
|
||||
return $(this).text() == streamName;
|
||||
}).addClass('current');
|
||||
|
||||
$('#subpage').append(
|
||||
$('<div>').addClass('input_container').html(
|
||||
$('<label>').text('The info embed URL is:').append(
|
||||
$('<input>').attr('type','text').attr('readonly','readonly').val(embedbase+'info_'+streamName+'.js')
|
||||
)
|
||||
).append(
|
||||
$('<label>').text('The embed URL is:').append(
|
||||
$('<input>').attr('type','text').attr('readonly','readonly').val(embedbase+'embed_'+streamName+'.js')
|
||||
)
|
||||
).append(
|
||||
$('<label>').text('The embed code is:').css('overflow','hidden').append(
|
||||
$('<textarea>').val('<div>\n <script src="'+embedbase+'embed_'+streamName+'.js"></' + 'script>\n</div>')
|
||||
)
|
||||
)
|
||||
).append(
|
||||
$('<span>').attr('id','listprotocols').text('Loading..')
|
||||
).append(
|
||||
$('<p>').text('Preview:')
|
||||
).append(
|
||||
$('<div>').attr('id','preview-container')
|
||||
);
|
||||
|
||||
// jQuery doesn't work -> use DOM magic
|
||||
var script = document.createElement('script');
|
||||
script.src = embedbase+'embed_'+streamName+'.js';
|
||||
script.onload = function(){
|
||||
var priority = mistvideo[streamName].source;
|
||||
if (priority.length > 0) {
|
||||
priority.sort(function(a,b){
|
||||
return b.priority - a.priority;
|
||||
});
|
||||
var $table = $('<table>').html(
|
||||
$('<tr>').html(
|
||||
$('<th>').text('URL')
|
||||
).append(
|
||||
$('<th>').text('Type')
|
||||
).append(
|
||||
$('<th>').text('Priority')
|
||||
)
|
||||
);
|
||||
for (var i in priority) {
|
||||
$table.append(
|
||||
$('<tr>').html(
|
||||
$('<td>').text(priority[i].url)
|
||||
).append(
|
||||
$('<td>').text(priority[i].type)
|
||||
).append(
|
||||
$('<td>').addClass('align-center').text(priority[i].priority)
|
||||
)
|
||||
);
|
||||
}
|
||||
$('#listprotocols').html($table);
|
||||
}
|
||||
else {
|
||||
$('#listprotocols').html('No data in info embed file.');
|
||||
}
|
||||
}
|
||||
document.getElementById('preview-container').appendChild( script );
|
||||
}
|
||||
|
||||
$(function(){
|
||||
$('#menu div.button').click(function(){
|
||||
if ((settings.settings.LTS != 1) && ($(this).hasClass('LTS-only'))) { return; }
|
||||
$('#logo > a').click(function(){
|
||||
if ($.isEmptyObject(settings.settings)) {
|
||||
showTab('login')
|
||||
}
|
||||
else {
|
||||
showTab('overview');
|
||||
}
|
||||
});
|
||||
$('#menu div.button').click(function(e){
|
||||
//if ((settings.settings.LTS != 1) && ($(this).hasClass('LTS-only'))) { return; }
|
||||
showTab($(this).text().toLowerCase());
|
||||
e.stopPropagation();
|
||||
})
|
||||
$('body').on('keydown',function(e){
|
||||
switch (e.which) {
|
||||
|
@ -1101,6 +1449,41 @@ $(function(){
|
|||
$(this).val(v);
|
||||
this.setSelectionRange(curpos,curpos);
|
||||
});
|
||||
|
||||
$('.expandbutton').click(function(){
|
||||
$(this).toggleClass('active');
|
||||
});
|
||||
|
||||
|
||||
$('#ih-button').click(function(){
|
||||
if (ih) {
|
||||
$('.ih-balloon').remove();
|
||||
}
|
||||
else {
|
||||
getWikiData('/wiki/Integrated_Help',function(data){
|
||||
settings.ih = {
|
||||
raw: data.find('#mw-content-text').contents(),
|
||||
pages: {}
|
||||
}
|
||||
settings.ih.raw.filter('.page[data-pagename]').each(function(){
|
||||
var pagename = $(this).attr('data-pagename').replace(' ','_');
|
||||
settings.ih.pages[pagename] = {
|
||||
raw: $(this).contents(),
|
||||
pageinfo: $(this).find('.page-description').html(),
|
||||
inputs: {}
|
||||
}
|
||||
$(this).children('.input-description[data-inputid]').each(function(){
|
||||
settings.ih.pages[pagename].inputs[$(this).attr('data-inputid')] = $(this).html();
|
||||
});
|
||||
});
|
||||
consolelog('New integrated help data:',settings.ih);
|
||||
ihAddBalloons();
|
||||
});
|
||||
}
|
||||
ih = !ih;
|
||||
$(this).toggleClass('active');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$(window).on('hashchange', function(e) {
|
||||
|
|
843
lsp/pages.js
843
lsp/pages.js
|
@ -6,6 +6,7 @@ var defaults = {
|
|||
};
|
||||
|
||||
function showTab(tabName,streamName) {
|
||||
settings.currentpage = tabName.replace(' ','_');
|
||||
|
||||
ignoreHashChange = true;
|
||||
location.hash = location.hash.split('&')[0]+'&'+tabName+(streamName ? '@'+streamName : '');
|
||||
|
@ -14,9 +15,10 @@ function showTab(tabName,streamName) {
|
|||
|
||||
$('#menu .button').removeClass('current').filter(function(i){
|
||||
return $(this).text().toLowerCase() == tabName;
|
||||
}).addClass('current');
|
||||
}).addClass('current').parents('.expandbutton').addClass('active');
|
||||
|
||||
$('#page').html('');
|
||||
$('#tooltip').remove();
|
||||
clearInterval(theInterval);
|
||||
$('#menu').css('visibility', 'visible');
|
||||
|
||||
|
@ -121,6 +123,10 @@ function showTab(tabName,streamName) {
|
|||
|
||||
saveAndReload('overview');
|
||||
})
|
||||
).append(
|
||||
$('<button>').text('Cancel').addClass('escape-to-cancel').click(function(){
|
||||
showTab('login');
|
||||
})
|
||||
)
|
||||
);
|
||||
break;
|
||||
|
@ -434,8 +440,12 @@ function showTab(tabName,streamName) {
|
|||
$('<td>').attr('id','status-of-'+index).html(formatStatus(theStream))
|
||||
).append(
|
||||
$('<td>').html(
|
||||
$('<button>').text('Embed').click(function(){
|
||||
showTab('embed',$(this).parent().parent().data('stream'))
|
||||
$('<button>').text('Preview').click(function(){
|
||||
showTab('preview',$(this).parent().parent().data('stream'))
|
||||
})
|
||||
).append(
|
||||
$('<button>').text('Info').click(function(){
|
||||
showTab('streaminfo',$(this).parent().parent().data('stream'))
|
||||
})
|
||||
)
|
||||
).append(
|
||||
|
@ -497,7 +507,7 @@ function showTab(tabName,streamName) {
|
|||
$('<label>').text('Buffer time:').addClass('live-only').attr('for','settings-streams-'+streamName+'-DVR').append(
|
||||
$('<span>').addClass('unit').text('[ms]')
|
||||
).append(
|
||||
$('<input>').attr('type','text').attr('id','settings-streams-'+streamName+'-DVR').attr('placeholder','2 keyframes').addClass('isSetting').addClass('').addClass('validate-positive-integer')
|
||||
$('<input>').attr('type','text').attr('id','settings-streams-'+streamName+'-DVR').attr('placeholder','30000').addClass('isSetting').addClass('').addClass('validate-positive-integer')
|
||||
)
|
||||
).append(
|
||||
$('<label>').text('Record to:').addClass('live-only').addClass('LTS-only').attr('for','settings-streams-'+streamName+'-record').attr('title','The path to the file to record to. Leave this field blank if you do not wish to record to file.').append(
|
||||
|
@ -516,7 +526,7 @@ function showTab(tabName,streamName) {
|
|||
$('<p>').text('Encrypt this stream')
|
||||
).append(
|
||||
$('<div>').addClass('description').text(
|
||||
'To enable encryption, the Licene Acquisition URL must be entered, as well as either the content key or the key ID and seed.'
|
||||
'To enable encryption, the Licence Acquisition URL must be entered, as well as either the content key or the key ID and seed.'
|
||||
)
|
||||
).append(
|
||||
$('<label>').text('Licence Acquisition URL:').attr('for','settings-streams-'+streamName+'-la_url').append(
|
||||
|
@ -578,7 +588,131 @@ function showTab(tabName,streamName) {
|
|||
})
|
||||
|
||||
break;
|
||||
case 'embed':
|
||||
case 'streaminfo':
|
||||
var meta = settings.settings.streams[streamName].meta;
|
||||
if (!meta) {
|
||||
$('#page').html('No info available for stream "'+streamName+'".');
|
||||
}
|
||||
else {
|
||||
$meta = $('<table>').css('width','auto');
|
||||
if (meta.live) {
|
||||
$meta.html(
|
||||
$('<tr>').html(
|
||||
$('<td>').text('Type:')
|
||||
).append(
|
||||
$('<td>').text('Live')
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$meta.html(
|
||||
$('<tr>').html(
|
||||
$('<td>').text('Type:')
|
||||
).append(
|
||||
$('<td>').text('Pre-recorded (VoD)')
|
||||
)
|
||||
);
|
||||
}
|
||||
for (var index in meta.tracks) {
|
||||
var track = meta.tracks[index];
|
||||
if (track.type == '') { continue; }
|
||||
var $table = $('<table>').html(
|
||||
$('<tr>').html(
|
||||
$('<td>').text('Type:')
|
||||
).append(
|
||||
$('<td>').text(capFirstChar(track.type))
|
||||
)
|
||||
).append(
|
||||
$('<tr>').html(
|
||||
$('<td>').text('Codec:')
|
||||
).append(
|
||||
$('<td>').text(track.codec)
|
||||
)
|
||||
).append(
|
||||
$('<tr>').html(
|
||||
$('<td>').text('Duration:')
|
||||
).append(
|
||||
$('<td>').html(
|
||||
formatDuration(track.lastms-track.firstms)+'<br>(from '+formatDuration(track.firstms)+' to '+formatDuration(track.lastms)+')'
|
||||
)
|
||||
)
|
||||
).append(
|
||||
$('<tr>').html(
|
||||
$('<td>').text('Average bitrate:')
|
||||
).append(
|
||||
$('<td>').text(Math.round(track.bps/1024)+' KiB/s')
|
||||
)
|
||||
);
|
||||
|
||||
if (track.height) {
|
||||
$table.append(
|
||||
$('<tr>').html(
|
||||
$('<td>').text('Size:')
|
||||
).append(
|
||||
$('<td>').text(track.width+'x'+track.height+' px')
|
||||
)
|
||||
);
|
||||
}
|
||||
if (track.fpks) {
|
||||
$table.append(
|
||||
$('<tr>').html(
|
||||
$('<td>').text('Framerate:')
|
||||
).append(
|
||||
$('<td>').text(track.fpks/1000+' fps')
|
||||
)
|
||||
);
|
||||
}
|
||||
if (track.channels) {
|
||||
$table.append(
|
||||
$('<tr>').html(
|
||||
$('<td>').text('Channels:')
|
||||
).append(
|
||||
$('<td>').text(track.channels)
|
||||
)
|
||||
);
|
||||
}
|
||||
if (track.rate) {
|
||||
$table.append(
|
||||
$('<tr>').html(
|
||||
$('<td>').text('Samplerate:')
|
||||
).append(
|
||||
$('<td>').text(seperateThousands(track.rate,' ')+' Hz')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$meta.append(
|
||||
$('<tr>').html(
|
||||
$('<td>').text(capFirstChar(index)+':')
|
||||
).append(
|
||||
$('<td>').html(
|
||||
$table
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$('#page').html(
|
||||
$('<p>').text('Detailed information about stream "'+streamName+'"')
|
||||
).append(
|
||||
$('<div>').css({'width':'100%','display':'table','table-layout':'fixed','min-height':'300px'}).html(
|
||||
$('<div>').css('display','table-row').html(
|
||||
$('<div>').attr('id','info-stream-meta').css({'display':'table-cell','max-width':'50%','overflow':'auto'}).html(
|
||||
$meta
|
||||
)
|
||||
).append(
|
||||
$('<div>').attr('id','info-stream-statistics').css({'display':'table-cell','text-align':'center','min-height':'200px'})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
$('#page').append(
|
||||
$('<button>').text('Back').addClass('escape-to-cancel').click(function(){
|
||||
showTab('streams');
|
||||
})
|
||||
);
|
||||
break;
|
||||
case 'preview':
|
||||
var httpConnector = false;
|
||||
for (var index in settings.settings.config.protocols) {
|
||||
if ((settings.settings.config.protocols[index].connector == 'HTTP') || (settings.settings.config.protocols[index].connector == 'HTTP.exe')) {
|
||||
|
@ -586,37 +720,29 @@ function showTab(tabName,streamName) {
|
|||
}
|
||||
}
|
||||
if (httpConnector) {
|
||||
var embedbase = 'http://'+parseURL(settings.server).host+':'+(httpConnector.port ? httpConnector.port : 8080)+'/';
|
||||
$('#page').html(
|
||||
$('<div>').addClass('input_container').html(
|
||||
$('<p>').text('Embed info for stream "'+streamName+'"')
|
||||
).append(
|
||||
$('<label>').text('The info embed URL is:').append(
|
||||
$('<input>').attr('type','text').attr('readonly','readonly').val(embedbase+'info_'+streamName+'.js')
|
||||
$('<div>').addClass('table').html(
|
||||
$('<div>').addClass('row').html(
|
||||
$('<div>').addClass('cell').attr('id','liststreams').addClass('menu')
|
||||
).append(
|
||||
$('<div>').addClass('cell').attr('id','subpage').css('padding-left','1em')
|
||||
)
|
||||
).append(
|
||||
$('<label>').text('The embed URL is:').append(
|
||||
$('<input>').attr('type','text').attr('readonly','readonly').val(embedbase+'embed_'+streamName+'.js')
|
||||
)
|
||||
).append(
|
||||
$('<label>').text('The embed code is:').css('overflow','hidden').append(
|
||||
$('<textarea>').val('<div>\n <script src="'+embedbase+'embed_'+streamName+'.js"></' + 'script>\n</div>')
|
||||
)
|
||||
).append(
|
||||
$('<button>').text('Back').addClass('escape-to-cancel').click(function(){
|
||||
showTab('streams');
|
||||
})
|
||||
)
|
||||
).append(
|
||||
$('<p>').text('Preview:')
|
||||
).append(
|
||||
$('<div>').attr('id','preview-container')
|
||||
);
|
||||
var embedbase = 'http://'+parseURL(settings.server).host+':'+(httpConnector.port ? httpConnector.port : 8080)+'/';
|
||||
|
||||
// jQuery doesn't work -> use DOM magic
|
||||
var script = document.createElement('script');
|
||||
script.src = embedbase+'embed_'+streamName+'.js';
|
||||
document.getElementById('preview-container').appendChild( script );
|
||||
for (var s in settings.settings.streams) {
|
||||
if (!streamName) {
|
||||
streamName = s;
|
||||
}
|
||||
$('#liststreams').append(
|
||||
$('<div>').addClass('button').text(settings.settings.streams[s].name).click(function(){
|
||||
buildstreamembed($(this).text());
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
buildstreamembed(streamName,embedbase);
|
||||
}
|
||||
else {
|
||||
$('#page').html(
|
||||
|
@ -627,29 +753,31 @@ function showTab(tabName,streamName) {
|
|||
case 'limits':
|
||||
var $tbody = $('<tbody>');
|
||||
$('#page').html(
|
||||
$('<div>').addClass('description').text('This is an overview of the limits that have been configured on MistServer.')
|
||||
).append(
|
||||
$('<table>').html(
|
||||
$('<thead>').html(
|
||||
$('<tr>').html(
|
||||
$('<th>').text('Applies to')
|
||||
).append(
|
||||
$('<th>').text('Type')
|
||||
).append(
|
||||
$('<th>').text('Name')
|
||||
).append(
|
||||
$('<th>').text('Value')
|
||||
).append(
|
||||
$('<th>')
|
||||
$('<div>').addClass('LTS-only').html(
|
||||
$('<div>').addClass('description').text('This is an overview of the limits that have been configured on MistServer.')
|
||||
).append(
|
||||
$('<table>').html(
|
||||
$('<thead>').html(
|
||||
$('<tr>').html(
|
||||
$('<th>').text('Applies to')
|
||||
).append(
|
||||
$('<th>').text('Type')
|
||||
).append(
|
||||
$('<th>').text('Name')
|
||||
).append(
|
||||
$('<th>').text('Value')
|
||||
).append(
|
||||
$('<th>')
|
||||
)
|
||||
)
|
||||
).append(
|
||||
$tbody
|
||||
)
|
||||
).append(
|
||||
$tbody
|
||||
$('<button>').text('New').click(function(){
|
||||
showTab('edit limit','_new_');
|
||||
})
|
||||
)
|
||||
).append(
|
||||
$('<button>').text('New').click(function(){
|
||||
showTab('edit limit','_new_');
|
||||
})
|
||||
);
|
||||
|
||||
for (var index in settings.settings.config.limits) {
|
||||
|
@ -1041,6 +1169,551 @@ function showTab(tabName,streamName) {
|
|||
}
|
||||
$('#logs-refresh-every').val(defaults.logRefreshing[1]);
|
||||
break;
|
||||
case 'statistics':
|
||||
var graphs = {};
|
||||
var plot;
|
||||
$('#page').html(
|
||||
$('<div>').addClass('description').text('Here, you can select all kinds of data, and view them in a graph.')
|
||||
).append(
|
||||
$('<div>').addClass('input_container').html(
|
||||
$('<p>').text('Select the data to display')
|
||||
).append(
|
||||
$('<label>').text('Add to graph:').append(
|
||||
$('<select>').attr('id','graphid').html(
|
||||
$('<option>').text('New graph').val('new')
|
||||
).change(function(){
|
||||
if ($(this).val() == 'new') {
|
||||
$('#graphtype').removeAttr('disabled');
|
||||
}
|
||||
else {
|
||||
$('#graphtype').attr('disabled','disabled');
|
||||
//set to correct type
|
||||
}
|
||||
})
|
||||
)
|
||||
).append(
|
||||
$('<label>').text('Graph x-axis type:').append(
|
||||
$('<select>').attr('id','graphtype').html(
|
||||
$('<option>').text('Time line').val('time')
|
||||
).append(
|
||||
$('<option>').text('Map').val('coords')
|
||||
).change(function(){
|
||||
$('#dataset option').hide();
|
||||
$('#dataset option.axis_'+$(this).val()).show();
|
||||
$('#dataset').val( $('#dataset option.axis_'+$(this).val()).first().val());
|
||||
})
|
||||
)
|
||||
).append(
|
||||
$('<label>').text('Select data set:').append(
|
||||
$('<select>').attr('id','dataset').html(
|
||||
$('<option>').text('Viewers').val('clients').addClass('axis_time')
|
||||
).append(
|
||||
$('<option>').text('Bandwidth (up)').val('upbps').addClass('axis_time')
|
||||
).append(
|
||||
$('<option>').text('Bandwidth (down)').val('downbps').addClass('axis_time')
|
||||
).append(
|
||||
$('<option>').text('% CPU').val('cpuload').addClass('axis_time')
|
||||
).append(
|
||||
$('<option>').text('Memory load').val('memload').addClass('axis_time')
|
||||
).append(
|
||||
$('<option>').text('Viewer location').val('coords').addClass('axis_coords')
|
||||
).change(function(){
|
||||
switch ($(this).val()) {
|
||||
case 'clients':
|
||||
case 'upbps':
|
||||
case 'downbps':
|
||||
$('#dataset-details .replace-dataset').text('amount of viewers')
|
||||
$('#dataset-details').show();
|
||||
break;
|
||||
default:
|
||||
$('#dataset-details').hide();
|
||||
}
|
||||
})
|
||||
)
|
||||
).append(
|
||||
$('<div>').attr('id','dataset-details').addClass('checklist').css({
|
||||
'padding':'0.5em 0 0 40%',
|
||||
'font-size':'0.9em'
|
||||
}).html('Show <span class=replace-dataset></span> for:').append(
|
||||
$('<label>').text('The total').prepend(
|
||||
$('<input>').attr('type','radio').attr('name','cumutype').attr('checked','checked').val('all')
|
||||
)
|
||||
).append(
|
||||
$('<label>').text('The stream ').append(
|
||||
$('<select>').addClass('stream cumuval')
|
||||
).prepend(
|
||||
$('<input>').attr('type','radio').attr('name','cumutype').val('stream')
|
||||
)
|
||||
).append(
|
||||
$('<label>').text('The protocol ').append(
|
||||
$('<select>').addClass('protocol cumuval')
|
||||
).prepend(
|
||||
$('<input>').attr('type','radio').attr('name','cumutype').val('protocol')
|
||||
)
|
||||
)
|
||||
).append(
|
||||
$('<button>').text('Add data set').click(function(){
|
||||
//the graph
|
||||
if ($('#graphid').val() == 'new') {
|
||||
var graph = {};
|
||||
graph.id = $('#graphid').val();
|
||||
graph.type = $('#graphtype').val();
|
||||
graph.id = 'graph_'+($('#graphcontainer .graph').length+1);
|
||||
graph.datasets = [];
|
||||
graphs[graph.id] = graph;
|
||||
$('#graphcontainer').append(
|
||||
$('<div>').attr('id',graph.id).addClass('graph-item').html(
|
||||
$('<div>').addClass('legend')
|
||||
).append(
|
||||
$('<div>').addClass('graph')
|
||||
)
|
||||
);
|
||||
$('#graphid').append(
|
||||
$('<option>').text(graph.id)
|
||||
).val(graph.id).trigger('change');
|
||||
}
|
||||
else {
|
||||
var graph = graphs[$('#graphid').val()];
|
||||
}
|
||||
//the dataset itself
|
||||
var d = {
|
||||
display: true,
|
||||
type: $('#dataset').val(),
|
||||
label: '',
|
||||
yaxistype: 'amount',
|
||||
data: [],
|
||||
lines: { show: true },
|
||||
points: { show: false }
|
||||
};
|
||||
switch (d.type) {
|
||||
case 'cpuload':
|
||||
d.label = 'CPU load';
|
||||
d.yaxistype = 'percentage';
|
||||
break;
|
||||
case 'memload':
|
||||
d.label = 'Memory load';
|
||||
d.yaxistype = 'percentage';
|
||||
break;
|
||||
case 'upbps':
|
||||
case 'downbps':
|
||||
case 'clients':
|
||||
d.cumutype = $('#dataset-details input[name=cumutype]:checked').val();
|
||||
d.yaxistype = 'bytespersec';
|
||||
if (d.cumutype == 'all') {
|
||||
switch (d.type) {
|
||||
case 'clients':
|
||||
d.label = 'Total viewers';
|
||||
d.yaxistype = 'amount';
|
||||
break;
|
||||
case 'upbps':
|
||||
d.label = 'Total bandwidth (up)';
|
||||
break;
|
||||
case 'downbps':
|
||||
d.label = 'Total bandwidth (down)';
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
var which = $('#dataset-details.cumuval.'+d.cumutype).val();
|
||||
if (d.cumutype == 'stream') {
|
||||
d.stream = which;
|
||||
}
|
||||
else if (d.cumutype == 'protocol') {
|
||||
d.protocol = which;
|
||||
}
|
||||
switch (d.type) {
|
||||
case 'clients':
|
||||
d.label = 'Viewers ('+d.stream+')';
|
||||
d.yaxistype = 'amount';
|
||||
break;
|
||||
case 'upbps':
|
||||
d.label = 'Bandwidth (up) ('+d.stream+')';
|
||||
break;
|
||||
case 'downbps':
|
||||
d.label = 'Bandwidth (down) ('+d.stream+')';
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
graph.datasets.push(d);
|
||||
getPlotData();
|
||||
})
|
||||
)/*.append(
|
||||
$('<p>').text('Switch data display type').css('clear','both')
|
||||
).append(
|
||||
$('<label>').text('Show data in a:').append(
|
||||
$('<select>').html(
|
||||
$('<option>').text('graph')
|
||||
).append(
|
||||
$('<option>').text('table')
|
||||
)
|
||||
)
|
||||
)*/
|
||||
).append(
|
||||
$('<div>').attr('id','graphcontainer')
|
||||
);
|
||||
for (var i in settings.settings.streams) {
|
||||
$('#dataset-details .cumuval.stream').append(
|
||||
$('<option>').text(settings.settings.streams[i].name).val(i)
|
||||
);
|
||||
}
|
||||
for (var i in settings.settings.config.protocols) {
|
||||
$('#dataset-details .cumuval.protocol').append(
|
||||
$('<option>').text(settings.settings.config.protocols[i].connector)
|
||||
);
|
||||
}
|
||||
$('#graphtype').trigger('change');
|
||||
|
||||
var lastitem = null;
|
||||
var $tooltip = $('<div>').attr('id','tooltip');
|
||||
$('body').append($tooltip);
|
||||
$('.graph').live('plothover',function(e,pos,item){
|
||||
if (item) {
|
||||
var pos;
|
||||
if (item.pageX > ($(window).width() / 2)) {
|
||||
pos.left = 'auto';
|
||||
pos.right = $(window).width() - item.pageX + 8+'px';
|
||||
}
|
||||
else {
|
||||
pos.left = item.pageX + 8+'px';
|
||||
pos.right = 'auto';
|
||||
}
|
||||
if (item.pageY > ($(window).height() / 2)) {
|
||||
pos.top = 'auto';
|
||||
pos.bottom = $(window).height() - item.pageY + 8+'px';
|
||||
}
|
||||
else {
|
||||
pos.top = item.pageY + 8+'px';
|
||||
pos.bottom = 'auto';
|
||||
}
|
||||
$tooltip.css({
|
||||
'left': pos.left,
|
||||
'top': pos.top,
|
||||
'right': pos.right,
|
||||
'bottom': pos.bottom
|
||||
}).html(
|
||||
$('<p>').text(item.series.label).prepend(
|
||||
$('<div>').css({
|
||||
'background-color': item.series.color,
|
||||
'width': '20px',
|
||||
'height': '20px',
|
||||
'display': 'inline-block',
|
||||
'margin': '0 0.5em'
|
||||
})
|
||||
)
|
||||
).append(
|
||||
$('<table>').html(
|
||||
$('<tr>').html(
|
||||
$('<td>').text('Time:')
|
||||
).append(
|
||||
$('<td>').text(item.series.xaxis.tickFormatter(item.datapoint[0],item.series.xaxis))
|
||||
)
|
||||
).append(
|
||||
$('<tr>').html(
|
||||
$('<td>').text(item.series.label+':')
|
||||
).append(
|
||||
$('<td>').text(item.series.yaxis.tickFormatter(item.datapoint[1],item.series.yaxis))
|
||||
)
|
||||
)
|
||||
).fadeIn();
|
||||
}
|
||||
else {
|
||||
$('#tooltip').hide();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
theInterval = setInterval(function(){
|
||||
getPlotData();
|
||||
},10000);
|
||||
|
||||
function getPlotData() {
|
||||
getData(function(data){
|
||||
for (var j in graphs) {
|
||||
for (var i in graphs[j].datasets) {
|
||||
graphs[j].datasets[i] = findDataset(graphs[j].datasets[i],data);
|
||||
}
|
||||
drawGraph(graphs[j]);
|
||||
}
|
||||
},{capabilities:true,totals:{}});
|
||||
}
|
||||
|
||||
function findDataset(dataobj,sourcedata) {
|
||||
var now = sourcedata.config.time;
|
||||
switch (dataobj.type) {
|
||||
case 'cpuload':
|
||||
//remove any data older than 10 minutes
|
||||
var removebefore = false;
|
||||
for (var i in dataobj.data) {
|
||||
if (dataobj.data[i][0] < (now-600)*1000) {
|
||||
removebefore = Number(i)+1;
|
||||
}
|
||||
}
|
||||
if (removebefore !== false) {
|
||||
dataobj.data.splice(0,removebefore);
|
||||
}
|
||||
dataobj.data.push([now*1000,sourcedata.capabilities.load.one]);
|
||||
break;
|
||||
case 'memload':
|
||||
//remove any data older than 10 minutes
|
||||
var removebefore = false;
|
||||
for (var i in dataobj.data) {
|
||||
if (dataobj.data[i][0] < (now-600)*1000) {
|
||||
removebefore = Number(i)+1;
|
||||
}
|
||||
}
|
||||
if (removebefore !== false) {
|
||||
dataobj.data.splice(0,removebefore);
|
||||
}
|
||||
dataobj.data.push([now*1000,sourcedata.capabilities.load.memory]);
|
||||
break;
|
||||
case 'upbps':
|
||||
case 'downbps':
|
||||
case 'clients':
|
||||
//todo: depending on the stream..
|
||||
if (!sourcedata.totals || !sourcedata.totals.data) {
|
||||
dataobj.data.push([(now-600)*1000,0]);
|
||||
dataobj.data.push([now*1000,0]);
|
||||
}
|
||||
else {
|
||||
var fields = {};
|
||||
for (var index in sourcedata.totals.fields) {
|
||||
fields[sourcedata.totals.fields[index]] = index;
|
||||
}
|
||||
var time = sourcedata.totals.start;
|
||||
dataobj.data = [];
|
||||
if (time > now-590) {
|
||||
//prepend data with 0
|
||||
dataobj.data.push([(now-600)*1000,0]);
|
||||
dataobj.data.push([time*1000-1,0]);
|
||||
}
|
||||
var index = 0;
|
||||
dataobj.data.push([[time*1000,sourcedata.totals.data[index][fields[dataobj.type]]]]);
|
||||
for (var i in sourcedata.totals.interval) {
|
||||
if ((i % 2) == 1) {
|
||||
//fill gaps with 0
|
||||
time += sourcedata.totals.interval[i][1];
|
||||
dataobj.data.push([time*1000,0]);
|
||||
}
|
||||
else {
|
||||
for (var j = 0; j < sourcedata.totals.interval[i][0]; j++) {
|
||||
time += sourcedata.totals.interval[i][1];
|
||||
index++;
|
||||
dataobj.data.push([time*1000,sourcedata.totals.data[index][fields[dataobj.type]]]);
|
||||
}
|
||||
if (i < sourcedata.totals.interval.length-1) {
|
||||
dataobj.data.push([time*1000+1,0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (now > time + 10) {
|
||||
//append data with 0
|
||||
dataobj.data.push([time*1000+1,0]);
|
||||
dataobj.data.push([now*1000,0]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return dataobj;
|
||||
}
|
||||
|
||||
function drawGraph(graph){
|
||||
var datasets = graph.datasets;
|
||||
if (datasets.length < 1) {
|
||||
$('#'+graph.id).children('.graph,.legend').html('');
|
||||
return;
|
||||
}
|
||||
var yaxes = [];
|
||||
var yaxesTemplates = {
|
||||
percentage: {
|
||||
name: 'percentage',
|
||||
color: 'black',
|
||||
display: false,
|
||||
tickColor: 0,
|
||||
tickDecimals: 0,
|
||||
tickFormatter: function(val,axis){
|
||||
return val.toFixed(axis.tickDecimals) + '%';
|
||||
},
|
||||
tickLength: 0,
|
||||
min: 0
|
||||
},
|
||||
amount: {
|
||||
name: 'amount',
|
||||
color: 'black',
|
||||
display: false,
|
||||
tickColor: 0,
|
||||
tickDecimals: 0,
|
||||
tickFormatter: function(val,axis){
|
||||
return seperateThousands(val.toFixed(axis.tickDecimals),' ');
|
||||
},
|
||||
tickLength: 0,
|
||||
min: 0
|
||||
},
|
||||
bytespersec: {
|
||||
name: 'bytespersec',
|
||||
color: 'black',
|
||||
display: false,
|
||||
tickColor: 0,
|
||||
tickDecimals: 1,
|
||||
tickFormatter: function(val,axis){
|
||||
var suffix = ['bytes','KiB','MiB','GiB','TiB','PiB'];
|
||||
if (val == 0) {
|
||||
val = val+' '+suffix[0];
|
||||
}
|
||||
else {
|
||||
var exponent = Math.floor(Math.log(Math.abs(val)) / Math.log(1024));
|
||||
if (exponent < 0) {
|
||||
val = val.toFixed(axis.tickDecimals)+' '+suffix[0];
|
||||
}
|
||||
else {
|
||||
val = Math.round(val / Math.pow(1024,exponent) * Math.pow(10,axis.tickDecimals)) / Math.pow(10,axis.tickDecimals) +' '+suffix[exponent];
|
||||
}
|
||||
}
|
||||
return val + '/s';
|
||||
},
|
||||
tickLength: 0,
|
||||
ticks: function(axis,a,b,c,d){
|
||||
//taken from flot source code (function setupTickGeneration),
|
||||
//modified to think in multiples of 1024 by Carina van der Meer for DDVTECH
|
||||
|
||||
// heuristic based on the model a*sqrt(x) fitted to
|
||||
// some data points that seemed reasonable
|
||||
var noTicks = 0.3 * Math.sqrt($('.graph').first().height());
|
||||
|
||||
var delta = (axis.max - axis.min) / noTicks,
|
||||
exponent = Math.floor(Math.log(Math.abs(delta)) / Math.log(1024)),
|
||||
correcteddelta = delta / Math.pow(1024,exponent),
|
||||
dec = -Math.floor(Math.log(correcteddelta) / Math.LN10),
|
||||
maxDec = axis.tickDecimals;
|
||||
|
||||
if (maxDec != null && dec > maxDec) {
|
||||
dec = maxDec;
|
||||
}
|
||||
|
||||
var magn = Math.pow(10, -dec),
|
||||
norm = correcteddelta / magn, // norm is between 1.0 and 10.0
|
||||
size;
|
||||
|
||||
if (norm < 1.5) {
|
||||
size = 1;
|
||||
} else if (norm < 3) {
|
||||
size = 2;
|
||||
// special case for 2.5, requires an extra decimal
|
||||
if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
|
||||
size = 2.5;
|
||||
++dec;
|
||||
}
|
||||
} else if (norm < 7.5) {
|
||||
size = 5;
|
||||
} else {
|
||||
size = 10;
|
||||
}
|
||||
|
||||
size *= magn;
|
||||
size = size * Math.pow(1024,exponent);
|
||||
|
||||
if (axis.minTickSize != null && size < axis.minTickSize) {
|
||||
size = axis.minTickSize;
|
||||
}
|
||||
|
||||
axis.delta = delta;
|
||||
axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
|
||||
axis.tickSize = size;
|
||||
|
||||
var ticks = [],
|
||||
start = axis.tickSize * Math.floor(axis.min / axis.tickSize),
|
||||
i = 0,
|
||||
v = Number.NaN,
|
||||
prev;
|
||||
|
||||
do {
|
||||
prev = v;
|
||||
v = start + i * axis.tickSize;
|
||||
ticks.push(v);
|
||||
++i;
|
||||
} while (v < axis.max && v != prev);
|
||||
return ticks;
|
||||
},
|
||||
min: 0
|
||||
}
|
||||
};
|
||||
var xaxistemplates = {
|
||||
time: {
|
||||
name: 'time',
|
||||
mode: 'time',
|
||||
timezone: 'browser',
|
||||
ticks: 5
|
||||
}
|
||||
}
|
||||
var plotsets = [];
|
||||
for (var i in datasets) {
|
||||
if (datasets[i].display) {
|
||||
if (yaxesTemplates[datasets[i].yaxistype].display === false) {
|
||||
yaxes.push(yaxesTemplates[datasets[i].yaxistype]);
|
||||
yaxesTemplates[datasets[i].yaxistype].display = yaxes.length;
|
||||
}
|
||||
datasets[i].yaxis = yaxesTemplates[datasets[i].yaxistype].display;
|
||||
datasets[i].color = Number(i);
|
||||
plotsets.push(datasets[i]);
|
||||
}
|
||||
}
|
||||
if (yaxes[0]) { yaxes[0].color = 0; }
|
||||
plot = $.plot(
|
||||
$('#'+graph.id+' .graph'),
|
||||
plotsets,
|
||||
{
|
||||
legend: {show: false},
|
||||
xaxis: xaxistemplates[graph.type],
|
||||
yaxes: yaxes,
|
||||
grid: {
|
||||
hoverable: true,
|
||||
borderWidth: {top: 0, right: 0, bottom: 1, left: 1},
|
||||
color: 'black',
|
||||
backgroundColor: {colors: ['#fff','#ededed']}
|
||||
}
|
||||
}
|
||||
);
|
||||
$('#'+graph.id+' .legend').html(
|
||||
$('<div>').addClass('legend-list').addClass('checklist')
|
||||
);
|
||||
var plotdata = plot.getOptions();
|
||||
for (var i in datasets) {
|
||||
var $checkbox = $('<input>').attr('type','checkbox').data('dataset-index',i).click(function(){
|
||||
if ($(this).is(':checked')) {
|
||||
datasets[$(this).data('dataset-index')].display = true;
|
||||
}
|
||||
else {
|
||||
datasets[$(this).data('dataset-index')].display = false;
|
||||
}
|
||||
drawGraph($(this).parents('.graph-item'));
|
||||
});
|
||||
if (datasets[i].display) {
|
||||
$checkbox.attr('checked','checked');
|
||||
}
|
||||
$('#'+graph.id+' .legend-list').append(
|
||||
$('<label>').html(
|
||||
$checkbox
|
||||
).append(
|
||||
$('<div>').addClass('series-color').css('background-color',plotdata.colors[datasets[i].color % plotdata.colors.length])
|
||||
).append(
|
||||
datasets[i].label
|
||||
)
|
||||
);
|
||||
}
|
||||
if (datasets.length > 0) {
|
||||
$('#'+graph.id+' .legend').append(
|
||||
$('<button>').text('Clear all').click(function(){
|
||||
var graph = graphs[$(this).parents('.graph-item').attr('id')];
|
||||
graph.datasets = [];
|
||||
drawGraph(graph);
|
||||
}).css({'float':'none'})
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'server stats':
|
||||
var $cont = $('<div>').addClass('input_container');
|
||||
|
||||
|
@ -1094,6 +1767,55 @@ function showTab(tabName,streamName) {
|
|||
|
||||
$('#page').html($cont);
|
||||
break;
|
||||
case 'email for help':
|
||||
var config = $.extend({},settings.settings);
|
||||
delete config.statistics;
|
||||
config = JSON.stringify(config);
|
||||
$('#page').html(
|
||||
$('<div>').addClass('description').html(
|
||||
'You can use this form to email MistServer support if you\'re having difficulties.<br>'
|
||||
).append(
|
||||
'A copy of your server config file will automatically be included.'
|
||||
)
|
||||
).append(
|
||||
$('<div>').addClass('input_container').html(
|
||||
$('<form>').html(
|
||||
$('<label>').text('Your name:').append(
|
||||
$('<input>').attr('type','text').attr('name','name')
|
||||
)
|
||||
).append(
|
||||
$('<input>').attr('type','hidden').attr('name','company').val('-')
|
||||
).append(
|
||||
$('<label>').text('Your email address:').append(
|
||||
$('<input>').attr('type','email').attr('name','email')
|
||||
)
|
||||
).append(
|
||||
$('<input>').attr('type','hidden').attr('name','subject').val('Integrated Help')
|
||||
).append(
|
||||
$('<label>').text('Your message:').append(
|
||||
$('<textarea>').attr('name','message').height('20em')
|
||||
)
|
||||
).append(
|
||||
$('<label>').text('Your config file:').append(
|
||||
$('<textarea>').attr('name','configfile').attr('readonly','readonly').css({'height':'20em','font-size':'0.7em'}).val(config)
|
||||
)
|
||||
).append(
|
||||
$('<button>').text('Send').click(function(e){
|
||||
var data = $(this).parents('form').serialize();
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: 'http://mistserver.org/contact_us?skin=plain',
|
||||
data: data,
|
||||
success: function(d) {
|
||||
$('#page').html(d);
|
||||
}
|
||||
});
|
||||
e.preventDefault();
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'disconnect':
|
||||
showTab('login');
|
||||
$('#connection').addClass('red').removeClass('green').text('Disconnected');
|
||||
|
@ -1110,17 +1832,32 @@ function showTab(tabName,streamName) {
|
|||
|
||||
if ((settings.credentials.authstring) && (!settings.settings.LTS)) {
|
||||
$('.LTS-only input').add('.LTS-only select').add('.LTS-only button').attr('disabled','disabled');
|
||||
$('.LTS-only, .LTS-only p, .LTS-only label, .LTS-only button ').css('color','#b4b4b4');
|
||||
//$('.LTS-only, .LTS-only p, .LTS-only label, .LTS-only button').css('color','#b4b4b4');
|
||||
$('.LTS-only, .LTS-only > *').filter(':not(.LTSstuff_done)').each(function(){
|
||||
var t = [];
|
||||
if ($(this).attr('title')) {
|
||||
t.push($(this).attr('title'));
|
||||
}
|
||||
t.push('This is feature is only available in the LTS version.');
|
||||
t.push('This feature is only available in the LTS version.');
|
||||
$(this).attr('title',t.join(' ')).addClass('LTSstuff_done');
|
||||
});
|
||||
$('#page .LTS-only').prepend(
|
||||
$('<a>').text('Upgrade to LTS').attr('target','_blank').attr('href','http://mistserver.org/products/MistServer LTS').addClass('fakebutton')
|
||||
);
|
||||
|
||||
$('.linktoReleaseNotes.notedited').each(function(){
|
||||
$(this).attr('href',$(this).attr('href')+'/'+settings.settings.config.version.split('-')[0]).removeClass('.notedited');
|
||||
});
|
||||
}
|
||||
else if (settings.settings.LTS) {
|
||||
$('.LTS-only').removeClass('LTS-only');
|
||||
$('.linktoTnC.notLTSlink').attr('href','http://mistserver.org/wiki/MistServerLTS_license').removeClass('notLTSlink');
|
||||
$('.linktoReleaseNotes.notedited').each(function(){
|
||||
$(this).attr('href',$(this).attr('href')+'/'+settings.settings.config.version.split('-')[0]+'LTS').removeClass('.notedited');
|
||||
});
|
||||
}
|
||||
|
||||
if (ih) {
|
||||
ihAddBalloons();
|
||||
}
|
||||
}
|
||||
|
|
1
lsp/plugins/jquery.flot.crosshair.min.js
vendored
Normal file
1
lsp/plugins/jquery.flot.crosshair.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
(function($){var options={crosshair:{mode:null,color:"rgba(170, 0, 0, 0.80)",lineWidth:1}};function init(plot){var crosshair={x:-1,y:-1,locked:false};plot.setCrosshair=function setCrosshair(pos){if(!pos)crosshair.x=-1;else{var o=plot.p2c(pos);crosshair.x=Math.max(0,Math.min(o.left,plot.width()));crosshair.y=Math.max(0,Math.min(o.top,plot.height()))}plot.triggerRedrawOverlay()};plot.clearCrosshair=plot.setCrosshair;plot.lockCrosshair=function lockCrosshair(pos){if(pos)plot.setCrosshair(pos);crosshair.locked=true};plot.unlockCrosshair=function unlockCrosshair(){crosshair.locked=false};function onMouseOut(e){if(crosshair.locked)return;if(crosshair.x!=-1){crosshair.x=-1;plot.triggerRedrawOverlay()}}function onMouseMove(e){if(crosshair.locked)return;if(plot.getSelection&&plot.getSelection()){crosshair.x=-1;return}var offset=plot.offset();crosshair.x=Math.max(0,Math.min(e.pageX-offset.left,plot.width()));crosshair.y=Math.max(0,Math.min(e.pageY-offset.top,plot.height()));plot.triggerRedrawOverlay()}plot.hooks.bindEvents.push(function(plot,eventHolder){if(!plot.getOptions().crosshair.mode)return;eventHolder.mouseout(onMouseOut);eventHolder.mousemove(onMouseMove)});plot.hooks.drawOverlay.push(function(plot,ctx){var c=plot.getOptions().crosshair;if(!c.mode)return;var plotOffset=plot.getPlotOffset();ctx.save();ctx.translate(plotOffset.left,plotOffset.top);if(crosshair.x!=-1){var adj=plot.getOptions().crosshair.lineWidth%2===0?0:.5;ctx.strokeStyle=c.color;ctx.lineWidth=c.lineWidth;ctx.lineJoin="round";ctx.beginPath();if(c.mode.indexOf("x")!=-1){var drawX=Math.round(crosshair.x)+adj;ctx.moveTo(drawX,0);ctx.lineTo(drawX,plot.height())}if(c.mode.indexOf("y")!=-1){var drawY=Math.round(crosshair.y)+adj;ctx.moveTo(0,drawY);ctx.lineTo(plot.width(),drawY)}ctx.stroke()}ctx.restore()});plot.hooks.shutdown.push(function(plot,eventHolder){eventHolder.unbind("mouseout",onMouseOut);eventHolder.unbind("mousemove",onMouseMove)})}$.plot.plugins.push({init:init,options:options,name:"crosshair",version:"1.0"})})(jQuery);
|
2
lsp/plugins/jquery.flot.min.js
vendored
Normal file
2
lsp/plugins/jquery.flot.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
lsp/plugins/jquery.flot.time.min.js
vendored
Normal file
1
lsp/plugins/jquery.flot.time.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
lsp/plugins/jquery.js
vendored
Executable file
2
lsp/plugins/jquery.js
vendored
Executable file
File diff suppressed because one or more lines are too long
117
lsp/server.html
117
lsp/server.html
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue