').data('protocol',index).html(
$('').text(theProtocol.connector)
).append(
$(' ').attr('id','status-of-'+index).html(formatStatus(theProtocol))
).append(
$(' ').text(displayProtocolSettings(theProtocol))
).append(
$(' ').html(
$('').text('Edit').click(function(){
showTab('edit protocol',$(this).parent().parent().data('protocol'))
})
).append(
$('').text('Delete').click(function(){
var protocolID = $(this).parent().parent().data('protocol');
if (confirmDelete('Are you sure you want to delete the protocol "'+settings.settings.config.protocols[protocolID].connector+'"?')) {
settings.settings.config.protocols.splice(protocolID,1);
saveAndReload('protocols');
}
})
)
)
);
}
theInterval = setInterval(function(){
updateProtocols();
},10000);
updateProtocols();
break;
case 'edit protocol':
var objpath;
if (streamName == '_new_') {
$('#page').html(
$('').text('Adding a new protocol')
);
objpath = 'newProtocol';
}
else {
$('#page').html(
$('
').text('Editing protocol "'+settings.settings.config.protocols[streamName].connector+'"')
);
objpath = 'settings.config.protocols['+streamName+']';
}
currentConnectors = [];
for (var index in settings.settings.config.protocols) {
currentConnectors.push(settings.settings.config.protocols[index].connector.replace('.exe',''));
}
var $selectProtocol = $('').attr('id','protocol-connector').attr('objpath',objpath+'.connector').addClass('isSetting').change(function(){
buildProtocolFields($(this).val(),objpath,streamName);
});
$('#page').append(
$('').addClass('input_container').html(
$('
').text('Protocol:').attr('for','protocol-connector').append(
$selectProtocol
)
).append(
$('').addClass('description').attr('id','protocol-description')
).append(
$('
').attr('id','protocol-fields')
).append(
$('
').addClass('enter-to-submit').text('Save').click(function(){
if (streamName == '_new_') {
settings.newProtocol = {};
if (applyInput() === false) { return; }
if (!settings.settings.config.protocols) { settings.settings.config.protocols = []; }
settings.settings.config.protocols.push(settings.newProtocol);
delete settings.newProtocol;
}
else {
if (applyInput() === false) { return; }
}
saveAndReload('protocols');
})
).append(
$('').text('Cancel').addClass('escape-to-cancel').click(function(){
showTab('protocols');
})
)
);
for (var index in settings.settings.capabilities.connectors) {
$selectProtocol.append(
$('').val(index).text(index)
);
}
if (streamName != '_new_') {
enterSettings();
}
buildProtocolFields($selectProtocol.val(),objpath,streamName);
break;
case 'streams':
var $tbody = $('').attr('id','streams-tbody');
$('#page').html(
$('').addClass('description').html(
'This is an overview of the streams that have been configured on MistServer.
You can sort them by clicking the colomn headers that have symbols and filter them using the checkboxes.
'
)
).append(
$('
').html(
$('
').attr('type','checkbox').attr('id','stream-filter-recorded').attr('checked','checked')
).append(
$('
').text('Show pre-recorded streams ').attr('for','stream-filter-recorded').css('display','inline')
).append(
$(' ').attr('type','checkbox').attr('id','stream-filter-live').attr('checked','checked')
).append(
$('').text('Show live streams').attr('for','stream-filter-live').css('display','inline')
)
).append(
$('').addClass('sortable').html(
$('').html(
$('').html(
$('').addClass('sort-type-string sortdesc').text('Name')
).append(
$(' ').addClass('sort-type-string').text('Type')
).append(
$(' ').addClass('sort-type-int').addClass('align-right').text('Viewers')
).append(
$(' ').addClass('sort-type-string').text('Status')
).append(
$(' ').addClass('dontsort')
).append(
$(' ').addClass('dontsort')
)
)
).append(
$tbody
)
).append(
$('').text('New').click(function(){
showTab('edit stream','_new_');
})
);
$('.sortable').stupidtable();
for (var index in settings.settings.streams) {
var theStream = settings.settings.streams[index];
//backwards compatibility
if ((theStream.source == undefined) && (theStream.channel)) {
theStream.source = theStream.channel.URL;
}
$tbody.append(
$('').data('stream',index).html(
$('').text(index)
).append(
$(' ').addClass('isLive').text(isLive(theStream.source) ? 'Live' : 'Recorded')
).append(
$(' ').attr('id','viewers-of-'+index).text(0).addClass('align-right')
).append(
$(' ').attr('id','status-of-'+index).html(formatStatus(theStream))
).append(
$(' ').html(
$('').text('Preview').click(function(){
showTab('preview',$(this).parent().parent().data('stream'))
})
).append(
$('').text('Info').click(function(){
showTab('streaminfo',$(this).parent().parent().data('stream'))
})
)
).append(
$('').html(
$('').text('Edit').click(function(){
showTab('edit stream',$(this).parent().parent().data('stream'))
})
).append(
$('').text('Delete').click(function(){
var streamName = $(this).parent().parent().data('stream');
if (confirmDelete('Are you sure you want to delete the stream "'+streamName+'"?')) {
delete settings.settings.streams[streamName];
saveAndReload('streams');
}
})
)
)
);
}
theInterval = setInterval(function(){
updateStreams();
},10000);
updateStreams();
$('#stream-filter-recorded,#stream-filter-live').click(function(){
filterTable();
});
break;
case 'edit stream':
if (streamName == '_new_') {
$('#page').html(
$('').text('Adding a new stream')
);
}
else {
$('#page').html(
$('
').text('Editing stream "'+streamName+'"')
);
}
$('#page').append(
$('
').addClass('input_container').html(
$('
').text('Stream name:').attr('for','settings-streams-'+streamName+'-name').append(
$(' ').attr('type','text').attr('id','settings-streams-'+streamName+'-name').addClass('isSetting').addClass('validate-lowercase-alphanumeric_-firstcharNaN').addClass('validate-required')
)
).append(
$('').text('Source:').attr('for','settings-streams-'+streamName+'-source').attr('title','The path to the stream, usually "/path/to/filename.dtsc" for files or "push://hostname/streamname" for live streams.').append(
$(' ').attr('type','text').attr('id','settings-streams-'+streamName+'-source').addClass('isSetting').addClass('validate-required').keyup(function(){
if(isLive($(this).val())){
$('.live-only').show();
}
else{
$('.live-only').hide();
$('.live-only').children('label').children('input').val('');
}
})
)
).append(
$('').text('Buffer time:').addClass('live-only').attr('for','settings-streams-'+streamName+'-DVR').append(
$('').addClass('unit').text('[ms]')
).append(
$(' ').attr('type','text').attr('id','settings-streams-'+streamName+'-DVR').attr('placeholder','30000').addClass('isSetting').addClass('').addClass('validate-positive-integer')
)
).append(
$('').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(
$(' ').attr('type','text').attr('id','settings-streams-'+streamName+'-record').addClass('isSetting')
)
).append(
$('').text('Cut first section:').addClass('live-only').addClass('LTS-only').attr('for','settings-streams-'+streamName+'-cut').attr('title','Remove the first part of a stream.').append(
$('').addClass('unit').text('[ms]')
).append(
$(' ').attr('type','text').attr('id','settings-streams-'+streamName+'-cut').addClass('isSetting').addClass('validate-positive-integer')
)
).append(
$(' ')
).append(
$('').addClass('LTS-only').html(
$('').text('Encrypt this stream')
).append(
$('
').addClass('description').text(
'To enable encryption, the Licence Acquisition URL must be entered, as well as either the content key or the key ID and seed.'
)
).append(
$('
').text('Licence Acquisition URL:').attr('for','settings-streams-'+streamName+'-la_url').append(
$(' ').attr('type','text').attr('id','settings-streams-'+streamName+'-la_url').addClass('isSetting')
)
).append(
$(' ')
).append(
$('').text('Content key:').attr('for','settings-streams-'+streamName+'-contentkey').append(
$(' ').attr('type','text').attr('id','settings-streams-'+streamName+'-contentkey').addClass('isSetting')
)
).append(
$('').text('- or -').addClass('description')
).append(
$('').text('Key ID:').attr('for','settings-treams-'+streamName+'-keyid').append(
$(' ').attr('type','text').attr('id','settings-streams-'+streamName+'-keyid').addClass('isSetting')
)
).append(
$('').text('Key seed:').attr('for','settings-streams-'+streamName+'-keyseed').append(
$(' ').attr('type','text').attr('id','settings-streams-'+streamName+'-keyseed').addClass('isSetting')
)
)
).append(
$('').addClass('enter-to-submit').text('Save').click(function(){
var newName = $('#settings-streams-'+streamName+'-name').val();
if (streamName != newName) {
if (!settings.settings.streams) { settings.settings.streams = {}; }
settings.settings.streams[streamName] = {};
if (applyInput() === false) { return; }
settings.settings.streams[newName] = settings.settings.streams[streamName];
delete settings.settings.streams[streamName];
}
else {
if (applyInput() === false) { return; }
}
saveAndReload('streams');
})
).append(
$('').text('Cancel').addClass('escape-to-cancel').click(function(){
showTab('streams');
})
)
);
if (streamName != '_new_') {
enterSettings();
}
if(isLive($('#settings-streams-'+streamName+'-source').val())){
$('.live-only').show();
}
else{
$('.live-only').hide();
$('.live-only').children('label').children('input').val('');
}
$('.live-only').each(function(){
var newtitle = [$(this).attr('title'),'Only applies to live streams.']
$(this).attr('title',newtitle.join(' '));
})
break;
case 'streaminfo':
var meta = settings.settings.streams[streamName].meta;
if (!meta) {
$('#page').html('No info available for stream "'+streamName+'".');
}
else {
$meta = $('').css('width','auto');
if (meta.live) {
$meta.html(
$('').html(
$('').text('Type:')
).append(
$(' ').text('Live')
)
);
}
else {
$meta.html(
$(' ').html(
$('').text('Type:')
).append(
$(' ').text('Pre-recorded (VoD)')
)
);
}
for (var index in meta.tracks) {
var track = meta.tracks[index];
if (track.type == '') { continue; }
var $table = $('').html(
$('').html(
$('').text('Type:')
).append(
$(' ').text(capFirstChar(track.type))
)
).append(
$(' ').html(
$('').text('Codec:')
).append(
$(' ').text(track.codec)
)
).append(
$(' ').html(
$('').text('Duration:')
).append(
$(' ').html(
formatDuration(track.lastms-track.firstms)+' (from '+formatDuration(track.firstms)+' to '+formatDuration(track.lastms)+')'
)
)
).append(
$(' ').html(
$('').text('Average bitrate:')
).append(
$(' ').text(Math.round(track.bps/1024)+' KiB/s')
)
);
if (track.height) {
$table.append(
$(' ').html(
$('').text('Size:')
).append(
$(' ').text(track.width+'x'+track.height+' px')
)
);
}
if (track.fpks) {
$table.append(
$(' ').html(
$('').text('Framerate:')
).append(
$(' ').text(track.fpks/1000+' fps')
)
);
}
if (track.channels) {
$table.append(
$(' ').html(
$('').text('Channels:')
).append(
$(' ').text(track.channels)
)
);
}
if (track.rate) {
$table.append(
$(' ').html(
$('').text('Samplerate:')
).append(
$(' ').text(seperateThousands(track.rate,' ')+' Hz')
)
);
}
$meta.append(
$(' ').html(
$('').text(capFirstChar(index)+':')
).append(
$(' ').html(
$table
)
)
);
}
$('#page').html(
$('').text('Detailed information about stream "'+streamName+'"')
).append(
$('
').css({'width':'100%','display':'table','table-layout':'fixed','min-height':'300px'}).html(
$('
').css('display','table-row').html(
$('
').attr('id','info-stream-meta').css({'display':'table-cell','max-width':'50%','overflow':'auto'}).html(
$meta
)
).append(
$('
').attr('id','info-stream-statistics').css({'display':'table-cell','text-align':'center','min-height':'200px'})
)
)
);
}
$('#page').append(
$('
').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')) {
httpConnector = settings.settings.config.protocols[index];
}
}
if (httpConnector) {
$('#page').html(
$('').addClass('table').html(
$('
').addClass('row').html(
$('
').addClass('cell').attr('id','liststreams').addClass('menu')
).append(
$('
').addClass('cell').attr('id','subpage').css('padding-left','1em')
)
)
);
var embedbase = 'http://'+parseURL(settings.server).host+':'+(httpConnector.port ? httpConnector.port : 8080)+'/';
for (var s in settings.settings.streams) {
if (!streamName) {
streamName = s;
}
$('#liststreams').append(
$('
').addClass('button').text(settings.settings.streams[s].name).click(function(){
buildstreamembed($(this).text());
})
);
}
buildstreamembed(streamName,embedbase);
}
else {
$('#page').html(
$('
').addClass('description').addClass('red').text('Could not find a HTTP connector. Please add one on the "Protocols" page.')
);
}
break;
case 'limits':
var $tbody = $('
');
$('#page').html(
$('').addClass('LTS-only').html(
$('
').addClass('description').text('This is an overview of the limits that have been configured on MistServer.')
).append(
$('
').html(
$('').html(
$('').html(
$('').text('Applies to')
).append(
$(' ').text('Type')
).append(
$(' ').text('Name')
).append(
$(' ').text('Value')
).append(
$(' ')
)
)
).append(
$tbody
)
).append(
$('').text('New').click(function(){
showTab('edit limit','_new_');
})
)
);
for (var index in settings.settings.config.limits) {
$tbody.append(
$('').data('limit',['server',index]).html(
$('').text('The whole server')
).append(
$(' ').text(settings.settings.config.limits[index].type)
).append(
$(' ').text(limitShortToLong(settings.settings.config.limits[index].name))
).append(
$(' ').html(limitValueFormat(settings.settings.config.limits[index]))
).append(
$(' ').html(
$('').text('Edit').click(function(){
showTab('edit limit',$(this).parent().parent().data('limit'));
})
).append(
$('').text('Delete').click(function(){
if (confirmDelete('Are you sure you want to delete the limit "Server: '+limitShortToLong(settings.settings.config.limits[index].name)+'"')) {
var which = $(this).parent().parent().data('limit')
delete settings.settings.config.limits[which[1]];
saveAndReload('limits');
}
})
)
)
);
}
for (var stream in settings.settings.streams) {
for (var index in settings.settings.streams[stream].limits) {
$tbody.append(
$('').data('limit',['stream-'+stream,index]).html(
$('').text('The stream "'+stream+'"')
).append(
$(' ').text(settings.settings.streams[stream].limits[index].type)
).append(
$(' ').text(limitShortToLong(settings.settings.streams[stream].limits[index].name))
).append(
$(' ').html(limitValueFormat(settings.settings.streams[stream].limits[index]))
).append(
$(' ').html(
$('').text('Edit').click(function(){
showTab('edit limit',$(this).parent().parent().data('limit'));
})
).append(
$('').text('Delete').click(function(){
var which = $(this).parent().parent().data('limit');
var stream = which[0].replace('stream-','');
if (confirmDelete('Are you sure you want to delete the limit "Stream "'+stream+'": '+limitShortToLong(settings.settings.streams[stream].limits[which[1]].name)+'"')) {
delete settings.settings.streams[stream].limits[which[1]];
saveAndReload('limits');
}
})
)
)
);
}
}
break;
case 'edit limit':
var objpath;
if (streamName == '_new_') {
$('#page').html(
$('').text('Adding a new limit')
)
objpath = 'settings.newlimit[0]';
}
else {
if (streamName[0] == 'server') {
objpath = 'settings.config.limits';
$('#page').html(
$('
').html('Editing server limit "').append(
$('').attr('id','limit-name-tag')
).append('"')
)
}
else {
objpath = 'settings.streams.'+streamName[0].replace('stream-','')+'.limits';
$('#page').html(
$('').html('Editing stream "'+streamName[0].replace('stream-','')+'" limit "').append(
$('').attr('id','limit-name-tag')
).append('"')
);
}
objpath += '['+streamName[1]+']';
}
var $appliesto = $('').attr('id','limit-applies-to').html(
$('').val('server').text('The whole server')
);
$('#page').append(
$('').addClass('input_container').html(
$('
').text('Applies to:').attr('for','limit-applies-to').append(
$appliesto
)
).append(
$('').text('Type:').attr('for','limit-type').append(
$('').attr('id','limit-type').attr('objpath',objpath+'.type').addClass('isSetting').html(
$('').val('soft').text('Soft')
).append(
$(' ').val('hard').text('Hard')
)
)
).append(
$('').addClass('description').text(
'The server will not allow a hard limit to be passed. A soft limit can be used to set alerts.'
)
).append(
$('
').text('Name:').attr('for','limit-name').append(
$('').attr('id','limit-name').attr('objpath',objpath+'.name').addClass('isSetting').html(
$('').val('kbps_max').text(limitShortToLong('kbps_max'))
).append(
$(' ').val('users').text(limitShortToLong('users'))
).append(
$(' ').val('geo').text(limitShortToLong('geo'))
).append(
$(' ').val('host').text(limitShortToLong('host'))
).change(function(){
changeLimitName();
})
)
).append(
$('').text('Value:').attr('id','limit-value-label').attr('for','limit-value').append(
$('').addClass('unit').text('[bytes/s]')
).append(
$(' ').attr('type','text').attr('id','limit-value').attr('objpath',objpath+'.value').addClass('validate-required').addClass('isSetting')
)
).append(
$('').attr('id','detailed-settings').css('overflow','hidden')
).append(
$('
').text('Save').addClass('enter-to-submit').click(function(){
if (streamName == '_new_') {
settings.settings.newlimit = [{}];
}
if (applyInput() === false) { return; }
moveLimit($('#limit-applies-to').val(),streamName,objpath);
saveAndReload('limits');
})
).append(
$('').text('Cancel').addClass('escape-to-cancel').click(function(){
showTab('limits');
})
)
);
enterSettings();
$("#limit-name-tag").text(limitShortToLong($('#limit-name').val()));
changeLimitName($('#limit-value').val());
for (var index in settings.settings.streams) {
$appliesto.append(
$('').val('stream-'+index).text('The stream "'+index+'"')
);
}
if (streamName != '_new_') {
if (streamName[0] == 'config') {
$appliesto.val('_server_');
}
else {
$appliesto.val('stream-'+streamName[1]);
}
}
break;
case 'conversion':
$('#page').html(
$('').text('Current conversions:')
);
if (settings.settings.conversion.status) {
var $tbody = $('
');
$('#page').append(
$('').html(
$('').html(
$('').html(
$('').text('Start time')
).append(
$(' ').text('Message')
).append(
$(' ').text('Details')
)
)
).append($tbody)
);
for (var index in settings.settings.conversion.status) {
var $details = $('');
if (settings.settings.conversion.status[index].details) {
$details.append(
$('').html(
$('').text('Input file:')
).append(
$(' ').text(settings.settings.conversion.status[index].details.input)
)
).append(
$(' ').html(
$('').text('Output file:')
).append(
$(' ').text(settings.settings.conversion.status[index].details.output)
)
).append(
$(' ').html(
$('').text('Encoder:')
).append(
$(' ').text(settings.settings.conversion.status[index].details.encoder)
)
);
if (settings.settings.conversion.status[index].details.video) {
$details.append(
$(' ').html(
$('').text('Video included').attr('colspan',2)
)
);
for (var s in settings.settings.conversion.status[index].details.video) {
$details.append(
$(' ').html(
$('').text('Video '+s+':')
).append(
$(' ').text(settings.settings.conversion.status[index].details.video[s])
)
)
}
}
if (settings.settings.conversion.status[index].details.audio) {
$details.append(
$(' ').html(
$('').text('Audio included').attr('colspan',2)
)
);
for (var s in settings.settings.conversion.status[index].details.audio) {
$details.append(
$(' ').html(
$('').text('Audio '+s+':')
).append(
$(' ').text(settings.settings.conversion.status[index].details.audio[s])
)
)
}
}
}
$tbody.append(
$(' ').html(
$('').text(formatDateLong(Number(index.replace('c_',''))/1000))
).append(
$(' ').html(formatConversionStatus(settings.settings.conversion.status[index])).attr('id','conversion-status-of-'+index)
).append(
$(' ').html($details)
)
)
}
}
else {
$('#page').append(
$('').text('None.')
);
}
$('#page').append(
$('').text('New').click(function(){
showTab('new conversion');
})
).append(
$('').text('Clear list').click(function(){
if (confirmDelete('Are you sure you want to clear the conversion list?')) {
settings.settings.conversion.clear = true;
saveAndReload('conversion');
}
})
);
if (settings.settings.conversion.status) {
theInterval = setInterval(function(){
updateConversions();
},10000);
updateConversions();
}
break;
case 'new conversion':
$('#page').html(
$('').text('New conversion')
).append(
$('
').addClass('input_container').html(
$('
').addClass('description').text('This page can be used to add a new conversion instruction.')
).append(
$('
').addClass('description').text('First, specify the directory that contains the file you wish to convert. Then click the "Search for input files"-button to scan it for files.')
).append(
$('
').text('Directory:').attr('for','conversion-query').append(
$(' ').attr('type','text').attr('id','conversion-query').val(defaults.conversion.inputdir).attr('placeholder','/path/to/directory')
)
).append(
$('').text('Search for input files').addClass('enter-to-submit').click(function(){
conversionDirQuery($('#conversion-query').val(),objpath);
})
).append(
$('').attr('id','query-status').addClass('description').css('display','inline-block').css('margin-top','12px')
)
).append(
$('').attr('id','conversion-input-file').addClass('input_container')
).append(
$('
').attr('id','conversion-details').addClass('input_container')
);
break;
case 'logs':
$('#page').append(
$('
').html(
$(' ').attr('type','checkbox').attr('id','logs-refresh').click(function(){
if ($(this).is(':checked')) {
defaults.logRefreshing[0] = true;
theInterval = setInterval(function(){
getData(function(data){
settings.settings.log = data.log;
$('#logs-table').remove();
$('#page').append(buildLogsTable());
});
},$('#logs-refresh-every').val());
getData(function(data){
settings.settings.log = data.log;
$('#logs-table').remove();
$('#page').append(buildLogsTable());
});
}
else {
defaults.logRefreshing[0] = false;
clearInterval(theInterval);
}
})
).append(
$('').text(' Refresh logs every ')
).append(
$('').attr('id','logs-refresh-every').append(
$('').val(10000).text('10 seconds')
).append(
$(' ').val(30000).text('30 seconds')
).append(
$(' ').val(60000).text('minute')
).append(
$(' ').val(300000).text('5 minutes')
).append(
$(' ').val(600000).text('10 minutes')
).append(
$(' ').val(1800000).text('30 minutes')
).change(function(){
defaults.logRefreshing[1] = $(this).val();
if ($('#logs-refresh').is(':checked')) {
clearInterval(theInterval);
theInterval = setInterval(function(){
getData(function(data){
settings.settings.log = data.log;
$('#logs-table').remove();
$('#page').append(buildLogsTable());
});
},$(this).val());
}
else {
clearInterval(theInterval);
}
})
)
).append(
$('').text('Purge logs').click(function(){
settings.settings.clearstatlogs = true;
saveAndReload('logs');
})
).append(
buildLogsTable()
);
getData(function(data){
settings.settings.log = data.log;
$('#logs-table').remove();
$('#page').append(buildLogsTable());
});
//load values for the check- and selectbox and start the interval if applicable
if (defaults.logRefreshing[0]) {
$('#logs-refresh').attr('checked','checked');
theInterval = setInterval(function(){
getData(function(data){
settings.settings.log = data.log;
$('#logs-table').remove();
$('#page').append(buildLogsTable());
});
},defaults.logRefreshing[1]);
}
$('#logs-refresh-every').val(defaults.logRefreshing[1]);
break;
case 'statistics':
var graphs = {};
var plot;
$('#page').html(
$('').addClass('description').text('Here, you can select all kinds of data, and view them in a graph.')
).append(
$('
').addClass('input_container').html(
$('
').text('Select the data to display')
).append(
$('').text('Add to graph:').append(
$('').attr('id','graphid').html(
$('').text('New graph').val('new')
).change(function(){
if ($(this).val() == 'new') {
$('#graphtype').removeAttr('disabled');
}
else {
$('#graphtype').attr('disabled','disabled');
//set to correct type
}
})
)
).append(
$('').text('Graph x-axis type:').append(
$('').attr('id','graphtype').html(
$('').text('Time line').val('time')
).append(
$(' ').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(
$('').text('Select data set:').append(
$('').attr('id','dataset').html(
$('').text('Viewers').val('clients').addClass('axis_time')
).append(
$(' ').text('Bandwidth (up)').val('upbps').addClass('axis_time')
).append(
$(' ').text('Bandwidth (down)').val('downbps').addClass('axis_time')
).append(
$(' ').text('% CPU').val('cpuload').addClass('axis_time')
).append(
$(' ').text('Memory load').val('memload').addClass('axis_time')
).append(
$(' ').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(
$('').attr('id','dataset-details').addClass('checklist').css({
'padding':'0.5em 0 0 40%',
'font-size':'0.9em'
}).html('Show
for:').append(
$('
').text('The total').prepend(
$(' ').attr('type','radio').attr('name','cumutype').attr('checked','checked').val('all')
)
).append(
$('').text('The stream ').append(
$('').addClass('stream cumuval')
).prepend(
$(' ').attr('type','radio').attr('name','cumutype').val('stream')
)
).append(
$('').text('The protocol ').append(
$('').addClass('protocol cumuval')
).prepend(
$(' ').attr('type','radio').attr('name','cumutype').val('protocol')
)
)
).append(
$('').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(
$('').attr('id',graph.id).addClass('graph-item').html(
$('
').addClass('legend')
).append(
$('
').addClass('graph')
)
);
$('#graphid').append(
$('
').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(
$('').text('Switch data display type').css('clear','both')
).append(
$('').text('Show data in a:').append(
$('').html(
$('').text('graph')
).append(
$(' ').text('table')
)
)
)*/
).append(
$('').attr('id','graphcontainer')
);
for (var i in settings.settings.streams) {
$('#dataset-details .cumuval.stream').append(
$('
').text(settings.settings.streams[i].name).val(i)
);
}
for (var i in settings.settings.config.protocols) {
$('#dataset-details .cumuval.protocol').append(
$(' ').text(settings.settings.config.protocols[i].connector)
);
}
$('#graphtype').trigger('change');
var lastitem = null;
var $tooltip = $('').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(
$('
').text(item.series.label).prepend(
$('
').css({
'background-color': item.series.color,
'width': '20px',
'height': '20px',
'display': 'inline-block',
'margin': '0 0.5em'
})
)
).append(
$('
').html(
$('').html(
$('').text('Time:')
).append(
$(' ').text(item.series.xaxis.tickFormatter(item.datapoint[0],item.series.xaxis))
)
).append(
$(' ').html(
$('').text(item.series.label+':')
).append(
$(' ').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(
$('').addClass('legend-list').addClass('checklist')
);
var plotdata = plot.getOptions();
for (var i in datasets) {
var $checkbox = $('
').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(
$('
').html(
$checkbox
).append(
$('').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(
$('
').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 = $('').addClass('input_container');
$cont.append(
$('
').text('CPU')
);
for (var index in settings.settings.capabilities.cpu) {
if (settings.settings.capabilities.cpu.length > 1) {
$cont.append(
$('').text('CPU '+index+1)
);
}
for (var property in settings.settings.capabilities.cpu[index]) {
$cont.append(
$('').text(property.charAt(0).toUpperCase()+property.slice(1)+':').append(
$('').text(seperateThousands(settings.settings.capabilities.cpu[index][property],' '))
)
);
}
}
if (settings.settings.capabilities.mem) {
$cont.append(
$('').text('Memory')
).append(
$('').text('Physical memory:').append(
$('').attr('id','stats-physical-memory')
)
).append(
$('').text('Swap memory:').append(
$('').attr('id','stats-swap-memory')
)
);
}
if (settings.settings.capabilities.load) {
$cont.append(
$('').text('CPU Load')
).append(
$('').text('Loading averages:').append(
$('').attr('id','stats-loading')
)
);
}
fillServerstatsTables(settings.settings);
theInterval = setInterval(function(){
updateServerstats();
},10000);
updateServerstats();
$('#page').html($cont);
break;
case 'email for help':
var config = $.extend({},settings.settings);
delete config.statistics;
config = JSON.stringify(config);
$('#page').html(
$('').addClass('description').html(
'You can use this form to email MistServer support if you\'re having difficulties.
'
).append(
'A copy of your server config file will automatically be included.'
)
).append(
$('
').addClass('input_container').html(
$('