/*! @file @id $Id$ This is the main application as it is fully run in the user's browser. */ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 var socket = io.connect(); var focused = null; var docker = new Docker(); function htmlenc(html) { return $('
').text(html).html(); } function htmldec(data) { return $('').html(data).text(); } /// Show error messsage /** Fades in an error message and logs to console. @param data (optional) The error can be a string or any structure. Strings are shown to the user, structures are logged only. @param stay (optional) If not given as @c true, reloads page after 5s. */ function error(data) { $("#status").fadeOut("slow", function() { $("#status").addClass("error") $("#status").removeClass("notice") $("#status").removeClass("success") if (data) { if (typeof data == 'string') { $("#status").html(data); console.log("error: "+data); } else { $("#status").html('unknown error: '+JSON.stringify(data)); console.log("error: ", data); console.log((new Error('stacktrace'))); } } else { $("#status").html('error'); console.log("error"); } $("#status").fadeIn("slow"); }); } /// Show notice messsage /** Fades in an notice message and logs to console. @param text (optional) The data is a string. */ function notice(text) { $("#status").fadeOut("slow", function() { $("#status").addClass("notice") $("#status").removeClass("error") $("#status").removeClass("success") if (text) { $("#status").html(text); console.log("notice: "+text); } else { $("#status").html(''); console.log("notice"); } $("#status").fadeIn("slow"); }); } /// Show notice messsage /** Fades in an success message and logs to console. @param text (optional) The data is a string. */ function success(text) { $("#status").fadeOut("slow", function() { $("#status").addClass("success") $("#status").removeClass("error") $("#status").removeClass("notice") if (text) { $("#status").html(text); console.log("success: "+text); } else { $("#status").html(''); console.log("success"); } $("#status").fadeIn("slow"); }); } /// Show status message in the main screen area /** @param text Text is a message or some complex HTML from the server. @param msg The success message text */ function status(text, msg) { $("#main").hide(); $("#main").html(text); if (msg) success(msg); else setTimeout("$('#status').fadeOut('slow')", 5000); zoom(0); stats(); $("#main").show(); $("form input:first-child").focus(); docker.containers.contextmenu("#main"); } function emit(signal, data) { console.log("<-snd "+signal, data); socket.emit(signal, data); } function connected() { console.log("server connected"); $("#connectionstatus #bad").hide(); $("#connectionstatus #good").show(); success("server connected"); } function disconnected() { console.log("server disconnected"); $("#connectionstatus #good").hide(); $("#connectionstatus #bad").show(); error("server disconnected", true); } function connectionstatus() { if (socket.connected) connected(); else disconnected(); } /// Toggle Menu Display function togglemenu() { $("#menu").toggle(); } /// Upload a configuration and send it to server function upload(evt) { if (!window.FileReader) return error("your browser does not support file upload", true); for (var i=0, f; f=evt.target.files[i]; ++i) { var file = f; var reader = new FileReader(); reader.onload = function(evt) { if (evt.target.error) return error("error reading file", true); if (evt.target.readyState==0) return notice("waiting for data …"); if (evt.target.readyState==1) return notice("loading data …"); tobecreated = JSON.parse(evt.target.result); showCreate(); } reader.readAsText(file); } } var tobecreated = {}; function previewCreate() { var name = $('#name').val(); var nodes = Object.create(tobecreated); if (name != '') { nodes[name] = { status: docker.containers.Status.Preview, id: null, name: name, image: { name: $('#image').val(), id: null }, ports: $('#createports option').map(function() {return $(this).data();}).get(), env: $('#createvars option').map(function() {return $(this).val();}).get(), volumes: $('#createvolumes option').map(function() {return $(this).data();}).get(), volumesfrom: $('#createvolumefroms option').map(function() {return $(this).val();}).get(), links: $('#createlinks option').map(function() {return $(this).data();}).get(), entrypoint: $('#createentrypoints option').map(function() {return $(this).val();}).get(), cmd: $('#createcommands option').map(function() {return $(this).val();}).get(), }; $('#doappend').unbind().click(function() { tobecreated[name] = Object.create(nodes[name]); tobecreated[name].status = docker.containers.Status.Prepared; $('#createpreview').append(''); $('#create form input[type=text]').val(''); $('#create form select.collect option').remove(); previewCreate(); }); $('#doremove').unbind().click(function() { $(this).siblings('#createpreview').children('option:selected').each(function(a,b,c) { delete tobecreated[b.innerHTML]; }).remove(); previewCreate(); }); $('#dosend').unbind().click(function() { if (Object.keys(tobecreated).length>0) { emit("create", docker.containers.configuration(tobecreated)); tobecreated = {}; showImage(); } }); } if ($('#image').val()!='') { var img = docker.images.get($('#image').val()); if (img) { $('#portintdata').empty().append(img.ports.map(function(i) { var option = document.createElement('option'); option.value = i.replace(/\/.*/g, ''); return option; })); $('#varnamedata').empty().append(img.env.map(function(i) { var option = document.createElement('option'); option.value = i.replace(/=.*/g, ''); return option; })); $('#varvaluedata').empty().append(img.env.map(function(i) { var option = document.createElement('option'); option.value = i.replace(/^[^=]*=/g, ''); return option; })); $('#volumeextdata').empty(); $('#volumeintdata').empty(); for (i in img.volumes) { var option = document.createElement('option'); option.value = i; $('#volumeextdata').append(option); $('#volumeintdata').append(option); } $('#volumesfromdata').empty().append(docker.containers.names(tobecreated).map(function(i) { var option = document.createElement('option'); option.value = i; return option; })); $('#linkcontainerdata').empty().append(docker.containers.names(tobecreated).map(function(i) { var option = document.createElement('option'); option.value = i; return option; })); } } if (name=='' && nodes) for (name in nodes) break; if (name && name!='') $('#preview').html(Viz("digraph {\n"+" rankdir="+rankdir+";\n" +docker.containers.subgraph(name, nodes) +"\n}")); else $('#preview').html(''); } function create() { } var zoomlevel = 0; function zoom(incr = 0) { zoomlevel = (zoomlevel+incr)%2; switch (zoomlevel) { case 0: { $("#main svg, #preview svg").css("width", "auto"); $("#main svg, #preview svg").css("height", "auto"); $("#main svg, #preview svg").css("max-width", "100%"); $("#main svg, #preview svg").css("max-height", "100%"); } break; case 1: { $("#main svg, #preview svg").css("width", "100%"); $("#main svg, #preview svg").css("height", "auto"); $("#main svg, #preview svg").css("max-width", "100%"); $("#main svg, #preview svg").css("max-height", "none"); } break; case 2: { $("#main.svg, #preview svg").css("width", "auto"); $("#main.svg, #preview svg").css("height", "100%"); $("#main.svg, #preview svg").css("max-width", "none"); $("#main.svg, #preview svg").css("max-height", "100%"); } break; } } var viz = null; var vizmore = null; var rankdir = "LR"; function rotateviz() { if (!viz) return; if (rankdir == "LR") rankdir = "TB"; else rankdir = "LR"; showviz(); previewCreate(); } function showviz(vizpath, more) { if (!vizpath) { vizpath = viz; more = vizmore; } else { viz = vizpath; vizmore = more; } res = "digraph {\n"+" rankdir="+rankdir+";\n"+viz+"\n}"; try { status(more?Viz(res)+more:Viz(res)); $('#main a > ellipse + text').attr('font-size', '12'); $('#main a > ellipse + text + text') .attr('font-weight', 'bold') .attr('font-size', '16') .each(function() {$(this).attr('y', parseFloat($(this).attr('y'))+1.0)}); $('#main a > ellipse + text + text + text').attr('font-size', '12'); } catch(e) { (res = res.split("\n")).forEach(function(v, i, a) { a[i] = ("000"+(i+1)).slice(-3)+": "+v; }); status(""+e+"
"+res.join("\n")+"");
}
}
function details(name) {
if (name) focused = name;
else if (!focused) return overview();
showviz(docker.containers.subgraph(focused));
}
/// Convert number of bytes to readable text
function size(num) {
if (num>0.6*1024) {
if (num>0.6*1024*1024) {
if (num>0.6*1024*1024*1024) {
if (num>0.6*1024*1024*1024*1024) {
return Math.round(num/1024/1024/1024/1024)+"TB";
} else {
return Math.round(num/1024/1024/1024)+"GB";
}
} else {
return Math.round(num/1024/1024)+"MB";
}
} else {
return Math.round(num/1024)+"kB";
}
} else {
return num+"B";
}
}
var oldoldstats = null;
var oldstats = null;
function stats(data) {
console.log("->rcv stats");
if (!data && oldstats && oldoldstats) {
data = oldstats;
oldstats = oldoldstats;
}
if (oldstats) for (name in data) {
var s = data[name];
var o = oldstats[name];
if (!o|| !s) continue;
$('#main text + text:contains("'+name+'") + text')
.html('cpu: '
+(Math.round((s.cpuacct.usage.data-o.cpuacct.usage.data)
/(s.cpuacct.usage.date-o.cpuacct.usage.date)
/100)
/100)
+'% mem: '
+size(s.memory.usage_in_bytes.data));
}
oldoldstats = oldstats;
oldstats = data;
}
function images(i) {
console.log("->rcv images");
docker.images.set(i);
$('#imagedata').empty().append(docker.images.tags().map(function(i) {
var option = document.createElement('option');
option.value = i;
return option;
}));
}
function containers(c) {
console.log("->rcv containers");
docker.containers.set(c);
if (focused && docker.containers.exists(focused))
details(focused);
else
overview();
}
function showImage() {
$("#create").hide();
$("#logs").hide();
$("#console").hide();
$("#close").hide();
$("#imagetools").show();
$("#main").show();
}
function showCreate() {
$("#main").hide();
$("#logs").hide();
$("#console").hide();
$("#imagetools").show();
$("#close").show();
$("#create").show();
previewCreate();
}
function showConsole() {
$("#main").hide();
$("#create").hide();
$("#logs").hide();
$("#imagetools").hide();
$("#console").show();
$("#close").show();
// $("#command").focus();
// $("#command").val("");
// if ($("#screen").val()!="") $("#screen").append("\n");
}
function showLogs() {
$("#main").hide();
$("#create").hide();
$("#console").hide();
$("#imagetools").hide();
$("#close").show();
$("#logs").show();
}
function logs(data) {
console.log("->rcv logs("+data.name+")");
if (data.type=='done') {
$("#logs").append('\nDONE');
} else {
$("#logs").append(''+htmlenc(data.text)+'');
}
}
function strInsert(str, pos, txt) {
return str.slice(0, pos)+txt+str.slice(pos);
}
function ansifilter(data) {
console.log("ansifilter");
var res = data;
var pos = -1;
var spans = 0;
while ((pos=res.indexOf("\x1B[")) >= 0) {
var end = res.indexOf("m", pos+2);
if (end>0) {
var control= res.slice(pos+2, end);
res = res.slice(0, pos)+res.slice(end+1);
control.split(';').forEach(function(c) {
switch (parseInt(c)) {
// set
case 1: res = strInsert(res, pos, ''); ++spans; break;
case 2: res = strInsert(res, pos, ''); ++spans; break;
case 4: res = strInsert(res, pos, ''); ++spans; break;
case 5: res = strInsert(res, pos, ''); ++spans; break;
case 7: res = strInsert(res, pos, ''); ++spans; break;
case 8: res = strInsert(res, pos, ''); ++spans; break;
// reset
case 0: for (;spans;--spans) res = strInsert(res, pos, ""); break;
case 21: if (spans) --spans; res = strInsert(res, pos, ""); break;
case 22: if (spans) --spans; res = strInsert(res, pos, ""); break;
case 23: if (spans) --spans; res = strInsert(res, pos, ""); break;
case 25: if (spans) --spans; res = strInsert(res, pos, ""); break;
case 27: if (spans) --spans; res = strInsert(res, pos, ""); break;
case 28: if (spans) --spans; res = strInsert(res, pos, ""); break;
// fg colors
case 39: res = strInsert(res, pos, ''); ++spans; break;
case 30: res = strInsert(res, pos, ''); ++spans; break;
case 31: res = strInsert(res, pos, ''); ++spans; break;
case 32: res = strInsert(res, pos, ''); ++spans; break;
case 33: res = strInsert(res, pos, ''); ++spans; break;
case 34: res = strInsert(res, pos, ''); ++spans; break;
case 35: res = strInsert(res, pos, ''); ++spans; break;
case 36: res = strInsert(res, pos, ''); ++spans; break;
case 37: res = strInsert(res, pos, ''); ++spans; break;
case 90: res = strInsert(res, pos, ''); ++spans; break;
case 91: res = strInsert(res, pos, ''); ++spans; break;
case 92: res = strInsert(res, pos, ''); ++spans; break;
case 93: res = strInsert(res, pos, ''); ++spans; break;
case 94: res = strInsert(res, pos, ''); ++spans; break;
case 95: res = strInsert(res, pos, ''); ++spans; break;
case 96: res = strInsert(res, pos, ''); ++spans; break;
case 97: res = strInsert(res, pos, ''); ++spans; break;
// bg colors
case 49: res = strInsert(res, pos, ''); ++spans; break;
case 40: res = strInsert(res, pos, ''); ++spans; break;
case 41: res = strInsert(res, pos, ''); ++spans; break;
case 42: res = strInsert(res, pos, ''); ++spans; break;
case 43: res = strInsert(res, pos, ''); ++spans; break;
case 44: res = strInsert(res, pos, ''); ++spans; break;
case 45: res = strInsert(res, pos, ''); ++spans; break;
case 46: res = strInsert(res, pos, ''); ++spans; break;
case 47: res = strInsert(res, pos, ''); ++spans; break;
case 100: res = strInsert(res, pos, ''); ++spans; break;
case 101: res = strInsert(res, pos, ''); ++spans; break;
case 102: res = strInsert(res, pos, ''); ++spans; break;
case 103: res = strInsert(res, pos, ''); ++spans; break;
case 104: res = strInsert(res, pos, ''); ++spans; break;
case 105: res = strInsert(res, pos, ''); ++spans; break;
case 106: res = strInsert(res, pos, ''); ++spans; break;
case 107: res = strInsert(res, pos, ''); ++spans; break;
}
});
} else {
break;
}
}
for (;spans;--spans) res += "";
console.log(res);
return res.replace(/\r\r\n/g, '\n');
}
function ascii(txt) {
var res = "";
for (i=0; i