/*! @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("

Exception Caught:

"+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, '"); 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; ircv bash-data("+data.name+")", data); if (data.type=='done') { $("#screen").append('\nDONE'); } else { var buff = ""; console.log("ASCII: ", ascii(data.text)); for (var i=0; i'+$(this).siblings("input") .map(function() { var val = this.value; var sep = this.getAttribute('data-separator') || ''; if (this.type=='checkbox' && !this.checked) { val=''; sep='' } return sep+val; }).get().join('')+''); $(this).siblings("input[type=text]").val(''); previewCreate(); }); $("#create form fieldset .remove").unbind().click(function() { $(this).siblings('select.collect').children('option:selected').remove(); previewCreate(); }); } function init() { socket.io .on("connect", connected) .on("reconnect", connected) .on("disconnect", disconnected) .on("error", disconnected); socket .on("fail", error) .on("containers", containers) .on("images", images) .on("stats", stats) .on("logs", logs) .on("bash-data", bash_data); initForms(); start(); } /// On Load, Call @ref start /* $(window.onbeforeunload = function() { return "Are you sure you want to navigate away?"; }); */ $(init);