From 284cb623b41261144584c49d5d1e664ee21506e0 Mon Sep 17 00:00:00 2001 From: Kristian Lyngstol Date: Fri, 10 Apr 2015 11:39:07 +0200 Subject: NMS: Implement time travel. --- web/nms.gathering.org/nms2/js/nms.js | 1067 ++++++++++++++++++++++++++++++++++ 1 file changed, 1067 insertions(+) create mode 100644 web/nms.gathering.org/nms2/js/nms.js (limited to 'web/nms.gathering.org/nms2/js') diff --git a/web/nms.gathering.org/nms2/js/nms.js b/web/nms.gathering.org/nms2/js/nms.js new file mode 100644 index 0000000..101f2b1 --- /dev/null +++ b/web/nms.gathering.org/nms2/js/nms.js @@ -0,0 +1,1067 @@ +var nms = { + updater:undefined, // Active updater + switches_now:undefined, // Most recent data + switches_then:undefined, // 2 minutes old + speed:0, // Current aggregated speed + full_speed:false, // Set to 'true' to include ALL interfaces + ping_data:undefined, + drawn:false, // Set to 'true' when switches are drawn + switch_showing:"", + nightMode:false, + nightBlur:{}, + switch_color:{}, + damage:false, + drawText:true, + now:false, + did_update:false // Set to 'true' after we've done some basic updating +}; + +var counters = { + undamaged:0, + damaged:0 +}; + +var c = document.getElementById("myCanvas"); +var ctx = c.getContext("2d"); +var fontSize = 16; +var fontFace = "Arial Black"; +var orig = { + width:1920, + height:1032 + }; + +var canvas = { + width:0, + height:0, + scale:1 +}; +var margin = { + x:10, + y:20, + text:3 +}; + + +/* + * Handlers. "updater" is run periodically when the handler is active, and + * "init" is run once when it's activated. + */ + +var handler_uplinks = { + updater:uplinkUpdater, + init:uplinkInit, + name:"Uplink map" +}; + +var handler_temp = { + updater:tempUpdater, + init:tempInit, + name:"Temperature map" +}; + +var handler_ping = { + updater:pingUpdater, + init:pingInit, + name:"IPv4 Ping map" +}; + +var handler_traffic = { + updater:trafficUpdater, + init:trafficInit, + name:"Uplink traffic map" +}; + +var handler_disco = { + updater:randomizeColors, + init:discoInit, + name:"Disco fever" +}; + +function byteCount(bytes) { + var units = ['', 'K', 'M', 'G', 'T', 'P']; + i = 0; + while (bytes > 1024) { + bytes = bytes / 1024; + i++; + } + return bytes.toFixed(1) + units[i]; +} + +function toggleNightMode() { + setNightMode(!nms.nightMode); +} + +function checkNow(now) { + if (Date.parse(now)) { + var d = new Date(Date.parse(now)); + var str = d.getFullYear() + "-" + (parseInt(d.getMonth())+1) + "-" + d.getDate() + " "; + str += d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds(); + return str; + + } + if (now == "") + return ""; + return false; +} + +function changeNow() { + var newnow = checkNow(document.getElementById("nowPicker").value); + if (!newnow) { + alert('Bad date-field in time travel field'); + return; + } + if (newnow == "") + newnow = false; + + nms.now = newnow; + updatePorts(); + var boxHide = document.getElementById("nowPickerBox"); + if (boxHide) { + boxHide.style.display = "none"; + } +} + +/* + * Hide switch info-box + */ +function hideSwitch() +{ + var swtop = document.getElementById("info-switch-parent"); + var switchele = document.getElementById("info-switch-table"); + if (switchele != undefined) + swtop.removeChild(switchele); + nms.switch_showing = ""; + +} +/* + * Display info on switch "x" in the info-box + */ +function switchInfo(x) +{ + var switchele = document.getElementById("info-switch-table"); + var sw = nms.switches_now["switches"][x]; + var swtop = document.getElementById("info-switch-parent"); + var tr; + var td1; + var td2; + + if (nms.switch_showing == x) { + hideSwitch(); + return; + } else { + hideSwitch(); + nms.switch_showing = x; + } + switchele = document.createElement("table"); + switchele.id = "info-switch-table"; + switchele.style.zIndex = 100; + switchele.className = "table table-bordered"; + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Sysname"; + td2.innerHTML = x + ''; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + var speed = 0; + var speed2 = 0; + for (port in nms.switches_now["switches"][x]["ports"]) { + if (nms.switches_now["switches"][x]["ports"] == undefined || + nms.switches_then["switches"][x]["ports"] == undefined) { + continue; + } + if (/ge-0\/0\/44$/.exec(port) || + /ge-0\/0\/45$/.exec(port) || + /ge-0\/0\/46$/.exec(port) || + /ge-0\/0\/47$/.exec(port)) + { + var t = nms.switches_then["switches"][x]["ports"][port]; + var n = nms.switches_now["switches"][x]["ports"][port]; + speed += (parseInt(t["ifhcoutoctets"]) - parseInt(n["ifhcoutoctets"])) / (parseInt(t["time"] - n["time"])); + speed2 += (parseInt(t["ifhcinoctets"]) - parseInt(n["ifhcinoctets"])) / (parseInt(t["time"] - n["time"])); + } + } + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Uplink speed (out , port 44,45,46,47)"; + td2.innerHTML = byteCount(8 * speed) + "b/s"; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Uplink speed (in , port 44,45,46,47)"; + td2.innerHTML = byteCount(8 * speed2) + "b/s"; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + speed = 0; + for (port in nms.switches_now["switches"][x]["ports"]) { + if (nms.switches_now["switches"][x]["ports"] == undefined || + nms.switches_then["switches"][x]["ports"] == undefined) { + continue; + } + var t = nms.switches_then["switches"][x]["ports"][port]; + var n = nms.switches_now["switches"][x]["ports"][port]; + speed += (parseInt(t["ifhcinoctets"]) -parseInt(n["ifhcinoctets"])) / (parseInt(t["time"] - n["time"])); + } + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Total speed (in)"; + td2.innerHTML = byteCount(8 * speed) + "b/s"; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + speed = 0; + for (port in nms.switches_now["switches"][x]["ports"]) { + if (nms.switches_now["switches"][x]["ports"] == undefined || + nms.switches_then["switches"][x]["ports"] == undefined) { + continue; + } + var t = nms.switches_then["switches"][x]["ports"][port]; + var n = nms.switches_now["switches"][x]["ports"][port]; + speed += (parseInt(t["ifhcoutoctets"]) -parseInt(n["ifhcoutoctets"])) / (parseInt(t["time"] - n["time"])); + } + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Total speed (out)"; + td2.innerHTML = byteCount(8 * speed) + "b/s"; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Management IP"; + td2.innerHTML = sw["management"]["ip"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Latency"; + if (nms.ping_data && nms.ping_data["switches"] && nms.ping_data["switches"][x]) { + td2.innerHTML = nms.ping_data["switches"][x]["latency"]; + } else { + td2.innerHTML = "N/A"; + } + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Temperature"; + td2.innerHTML = sw["temp"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Temperature age"; + td2.innerHTML = sw["temp_time"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Type"; + td2.innerHTML = sw["switchtype"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Last Updated"; + td2.innerHTML = sw["management"]["last_updated"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Poll frequency"; + td2.innerHTML = sw["management"]["poll_frequency"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + + swtop.appendChild(switchele); +} + +/* + * Update various info elements periodically. + */ +function updateInfo() +{ + if (!nms.drawn && nms.switches_now != undefined) { + nms.damage = true; + } + var speedele = document.getElementById("speed"); + speedele.innerHTML = (8 * parseInt(nms.speed) / 1024 / 1024 / 1024 ).toPrecision(5) + " Gbit/s"; +} + +/* + * Update function for uplink map + * Run periodically when uplink map is active. + */ +function uplinkUpdater() +{ + if (!nms.switches_now["switches"]) + return; + for (sw in nms.switches_now["switches"]) { + var uplinks=0; + for (port in nms.switches_now["switches"][sw]["ports"]) { + if (!nms.switches_then["switches"][sw]["ports"] || + !nms.switches_now["switches"][sw]["ports"]) + continue; + if (/ge-0\/0\/44$/.exec(port) || + /ge-0\/0\/45$/.exec(port) || + /ge-0\/0\/46$/.exec(port) || + /ge-0\/0\/47$/.exec(port)) + { + if (parseInt(nms.switches_then["switches"][sw]["ports"][port]["ifhcoutoctets"]) != parseInt(nms.switches_now["switches"][sw]["ports"][port]["ifhcoutoctets"])) { + uplinks += 1; + } + } + } + if (uplinks == 0) { + setSwitchColor(sw,"blue"); + } else if (uplinks == 1) { + setSwitchColor(sw,"red"); + } else if (uplinks == 2) { + setSwitchColor(sw, "yellow"); + } else if (uplinks == 3) { + setSwitchColor(sw, "green"); + } else if (uplinks > 3) { + setSwitchColor(sw, "white"); + } + } +} + +/* + * Init-function for uplink map + */ +function trafficInit() +{ + setLegend(1,"blue","0 uplink utilization"); + setLegend(5,"red", "1000Mb/s or more uplink utilization"); + setLegend(4,"yellow","100Mb/s to 800Mb/s uplink utilization"); + setLegend(3,"green", "5Mb/s to 100Mb/s uplink utilization"); + setLegend(2,"white","0 to 5Mb/s uplink utilization"); +} + +function trafficUpdater() +{ + if (!nms.switches_now["switches"]) + return; + for (sw in nms.switches_now["switches"]) { + var speed = 0; + for (port in nms.switches_now["switches"][sw]["ports"]) { + if (/ge-0\/0\/44$/.exec(port) || + /ge-0\/0\/45$/.exec(port) || + /ge-0\/0\/46$/.exec(port) || + /ge-0\/0\/47$/.exec(port)) + { + var t = nms.switches_then["switches"][sw]["ports"][port]; + var n = nms.switches_now["switches"][sw]["ports"][port]; + speed += (parseInt(t["ifhcoutoctets"]) -parseInt(n["ifhcoutoctets"])) / (parseInt(t["time"] - n["time"])); + speed += (parseInt(t["ifhcinoctets"]) -parseInt(n["ifhcinoctets"])) / (parseInt(t["time"] - n["time"])); + } + } + var m = 1024 * 1024 / 8; + if (speed == 0) { + setSwitchColor(sw,"blue"); + } else if (speed > (1000 * m)) { + setSwitchColor(sw,"red"); + } else if (speed > (800 * m)) { + setSwitchColor(sw, "yellow"); + } else if (speed > (5 * m)) { + setSwitchColor(sw, "green"); + } else { + setSwitchColor(sw, "white"); + } + } +} + +/* + * Init-function for uplink map + */ +function uplinkInit() +{ + setLegend(1,"blue","0 uplinks"); + setLegend(2,"red","1 uplink"); + setLegend(3,"yellow","2 uplinks"); + setLegend(4,"green","3 uplinks"); + setLegend(5,"white","4 uplinks"); +} + +function rgb_from_max(x) +{ + x = x/100; + var colorred = 255 * x; + var colorblue = 255 - colorred; + + return 'rgb(' + Math.round(colorred) + ", 0, " + Math.round(colorblue) + ')'; +} + +function temp_color(t) +{ + t = Math.round((t / 60) * 100); + return rgb_from_max(t); +} + +/* + * There are 4 legend-bars. This is a helper-function to set the color and + * description/name for each one. Used from handler init-functions. + */ +function setLegend(x,color,name) +{ + var el = document.getElementById("legend-" + x); + el.style.background = color; + el.innerHTML = name; +} + +function tempUpdater() +{ + for (sw in nms.switches_now["switches"]) { + var t = nms.switches_now["switches"][sw]["temp"]; + + setSwitchColor(sw, temp_color(t)); + } +} + +function tempInit() +{ + setLegend(1,temp_color(10),"10 °C"); + setLegend(2,temp_color(20),"20 °C"); + setLegend(3,temp_color(30),"30 °C"); + setLegend(4,temp_color(40),"40 °C"); + setLegend(5,temp_color(50),"50 °C"); +} + +function gradient_from_latency(latency_ms, latency_secondary_ms) +{ + if (latency_secondary_ms === undefined) { + return rgb_from_latency(latency_ms); + } + return 'linear-gradient(' + + rgb_from_latency(latency_ms) + ', ' + + rgb_from_latency(latency_secondary_ms) + ')'; +} + +function rgb_from_latency(latency_ms) +{ + if (latency_ms === null || latency_ms === undefined) { + return '#0000ff'; + } + + var l = latency_ms / 40.0; + if (l >= 2.0) { + return 'rgb(255, 0, 0)'; + } else if (l >= 1.0) { + l = 2.0 - l; + l = Math.pow(l, 1.0/2.2); + l = Math.round(l * 255.0); + return 'rgb(255, ' + l + ', 0)'; + } else { + l = Math.pow(l, 1.0/2.2); + l = Math.round(l * 255.0); + return 'rgb(' + l + ', 255, 0)'; + } +} + +function pingUpdater() +{ + for (var sw in nms.ping_data["switches"]) { + setSwitchColor(sw, gradient_from_latency(nms.ping_data["switches"][sw]["latency"])); + } +} + +function pingInit() +{ + setLegend(1,gradient_from_latency(1),"1ms"); + setLegend(2,gradient_from_latency(30),"30ms"); + setLegend(3,gradient_from_latency(60),"60ms"); + setLegend(4,gradient_from_latency(80),"80ms"); + setLegend(5,"#0000ff" ,"No response"); +} + +/* + * Run periodically to trigger map updates when a handler is active + */ +function updateMap() +{ + if (nms.updater != undefined) { + nms.updater(); + } +} + +/* + * Change map handler (e.g., change from uplink map to ping map) + */ +function setUpdater(fo) +{ + nms.updater = undefined; + resetColors(); + fo.init(); + nms.updater = fo.updater; + var foo = document.getElementById("updater_name"); + foo.innerHTML = fo.name + " "; + initialUpdate(); + if (nms.ping_data && nms.switches_then && nms.switches_now) { + nms.updater(); + } +} + + +/* + * Convenience function to avoid waiting for pollers when data is available + * for the first time. + */ +function initialUpdate() +{ + if (nms.ping_data && nms.switches_then && nms.switches_now && nms.updater != undefined && nms.did_update == false ) { + resizeEvent(); + if (!nms.drawn) { + nms.damage = true; + } + nms.updater(); + nms.did_update = true; + } +} + +/* + * Update nms.ping_data + */ +function updatePing() +{ + $.ajax({ + type: "GET", + url: "/ping-json2.pl?now=" + nms.now, + dataType: "text", + success: function (data, textStatus, jqXHR) { + nms.ping_data = JSON.parse(data); + initialUpdate(); + } + }); +} + +/* + * Update nms.switches_now and nms.switches_then + */ +function updatePorts() +{ + var now = ""; + if (nms.now) + now = "?now=" + nms.now; + $.ajax({ + type: "GET", + url: "/port-state.pl"+ now , + dataType: "text", + success: function (data, textStatus, jqXHR) { + var switchdata = JSON.parse(data); + nms.switches_now = switchdata; + parseIntPlacements(); + initialUpdate(); + } + }); + now=""; + if (nms.now) + now = "&now=" + nms.now; + $.ajax({ + type: "GET", + url: "/port-state.pl?time=5m" + now, + dataType: "text", + success: function (data, textStatus, jqXHR) { + var switchdata = JSON.parse(data); + nms.switches_then = switchdata; + initialUpdate(); + } + }) +} + +/* + * Use nms.switches_now and nms.switches_then to update 'nms.speed'. + * + * nms.speed is a total of ifHCInOctets across all interfaces. + * + * if nms.full_speed is true: Include ALL interfaces + * if nms.full_speed is false: Include only e* switches and exclude + * uplinks. + */ +function updateSpeed() +{ + var speed_in = parseInt(0); + var speed_kant = parseInt(0); + var counter=0; + var sw; + for (sw in nms.switches_now["switches"]) { + for (port in nms.switches_now["switches"][sw]["ports"]) { + if (!nms.switches_now["switches"][sw]["ports"][port]) { + console.log("ops"); + continue; + } + if (!nms.switches_then || !nms.switches_then["switches"] || !nms.switches_then["switches"][sw] || !nms.switches_then["switches"][sw]["ports"]) { + continue; + } + if (!nms.switches_now || !nms.switches_now["switches"] || !nms.switches_now["switches"][sw] || !nms.switches_now["switches"][sw]["ports"]) { + continue; + } + + if (!nms.switches_then["switches"][sw]["ports"][port]) { + console.log("ops"); + continue; + } + var diff = parseInt(parseInt(nms.switches_now["switches"][sw]["ports"][port]["time"]) - parseInt(nms.switches_then["switches"][sw]["ports"][port]["time"])); + var then = parseInt(nms.switches_then["switches"][sw]["ports"][port]["ifhcinoctets"]) ; + var now = parseInt(nms.switches_now["switches"][sw]["ports"][port]["ifhcinoctets"]) ; + var diffval = (now - then); + if (then == 0 || now == 0 || diffval == 0 || diffval == NaN) { + continue; + } + if (nms.full_speed || (( /e\d-\d/.exec(sw) || /e\d\d-\d/.exec(sw)) && ( /ge-\d\/\d\/\d$/.exec(port) || /ge-\d\/\d\/\d\d$/.exec(port)))) { + if (nms.full_speed || !( + /ge-0\/0\/44$/.exec(port) || + /ge-0\/0\/45$/.exec(port) || + /ge-0\/0\/46$/.exec(port) || + /ge-0\/0\/47$/.exec(port))) { + speed_in += parseInt(diffval/diff) ; + counter++; + } + } + //speed_in += parseInt(diffval/diff) / 1024 ; + } + } + nms.speed = speed_in; +} + +/* + * Draw a linknet with index i. + * + * XXX: Might have to change the index here to match backend + */ +function drawLinknet(i) +{ + var c1 = nms.switches_now.linknets[i].c1 ? nms.switches_now.linknets[i].c1 : "blue"; + var c2 = nms.switches_now.linknets[i].c2 ? nms.switches_now.linknets[i].c2 : "blue"; + if (nms.switches_now.switches[nms.switches_now.linknets[i].sysname1] && nms.switches_now.switches[nms.switches_now.linknets[i].sysname2]) { + connectSwitches(nms.switches_now.linknets[i].sysname1,nms.switches_now.linknets[i].sysname2, c1, c2); + } +} + +/* + * Draw all linknets + */ +function drawLinknets() +{ + if (nms.switches_now && nms.switches_now.linknets) { + for (var i in nms.switches_now.linknets) { + drawLinknet(i); + } + } +} + +/* + * Change both colors of a linknet. + * + * XXX: Probably have to change this to better match the backend data + */ +function setLinknetColors(i,c1,c2) +{ + if (nms.switches_now.linknets[i].c1 != c1 || + nms.switches_now.linknets[i].c2 != c2) { + nms.switches_now.linknets[i].c1 = c1; + nms.switches_now.linknets[i].c2 = c2; + nms.damage = true; + } +} + +/* + * (Re)draw a switch 'sw'. + * + * Color defaults to 'blue' if it's not set in the data structure. + */ +function drawSwitch(sw) +{ + var box = nms.switches_now['switches'][sw]['placement']; + var color = nms.switch_color[sw]; + if (color == undefined) { + color = "blue"; + } + ctx.fillStyle = color; + if (nms.nightMode && nms.nightBlur[sw] != true) { + ctx.shadowBlur = 10; + ctx.shadowColor = "#00EE00"; + nms.nightBlur[sw] = true; + } else { + ctx.shadowBlur = 0; + ctx.shadowColor = "#000000"; + } + drawBox(box['x'],box['y'],box['width'],box['height']); + ctx.shadowBlur = 0; + if (nms.drawText) { + if ((box['width'] + 10 )< box['height'] ) + drawSideways(sw,box['x'],box['y'],box['width'],box['height']); + else + drawRegular(sw,box['x'],box['y'],box['width'],box['height']); + } +} + +/* + * Make sure all placements of switches are parsed as integers so we don't + * have to pollute the code with pasreInt() every time we use it. + */ +function parseIntPlacements() { + for (var sw in nms.switches_now.switches) { + nms.switches_now.switches[sw]['placement']['x'] = + parseInt(nms.switches_now.switches[sw]['placement']['x']); + nms.switches_now.switches[sw]['placement']['y'] = + parseInt(nms.switches_now.switches[sw]['placement']['y']); + nms.switches_now.switches[sw]['placement']['width'] = + parseInt(nms.switches_now.switches[sw]['placement']['width']); + nms.switches_now.switches[sw]['placement']['height'] = + parseInt(nms.switches_now.switches[sw]['placement']['height']); + } +} + +/* + * Draw all switches + */ +function drawSwitches() +{ + if (!nms.switches_now || !nms.switches_now.switches) + return; + for (var sw in nms.switches_now.switches) { + drawSwitch(sw); + } + nms.drawn = true; +} + +/* + * Draw foreground/scene. + * + * This is used so linknets are drawn before switches. If a switch is all + * that has changed, we just need to re-draw that, but linknets require + * scene-redrawing. + */ +function drawScene() +{ + if (nms.damage) { + ctx.font = Math.round(fontSize * canvas.scale) + "px " + fontFace; + drawLinknets(); + drawSwitches(); + nms.damage = false; + counters.damaged++; + } else { + counters.undamaged++; + } +} + +/* + * Set the scale factor and (re)draw the scene and background. + * Uses canvas.scale and updates canvas.height and canvas.width. + */ +function setScale() +{ + c.height = canvas.height = orig.height * canvas.scale ; + c.width = canvas.width = orig.width * canvas.scale ; + drawBG(); + nightBlur = {}; + drawScene(); + document.getElementById("scaler").value = canvas.scale; + document.getElementById("scaler-text").innerHTML = (parseFloat(canvas.scale)).toPrecision(3); +} + +/* + * Returns true if the coordinates (x,y) is inside the box defined by + * box.{x,y,w.h} (e.g.: placement of a switch). + */ +function isin(box, x, y) +{ + if ((x >= box.x) && (x <= (box.x + box.width)) && (y >= box.y) && (y <= (box.y + box.height))) { + return true; + } + return false; + +} + +/* + * Return the name of the switch found at coordinates (x,y), or 'undefined' + * if none is found. + */ +function findSwitch(x,y) { + x = parseInt(parseInt(x) / canvas.scale); + y = parseInt(parseInt(y) / canvas.scale); + + for (var v in nms.switches_now.switches) { + if(isin(nms.switches_now.switches[v]['placement'],x,y)) { + return v; + } + } + return undefined; +} + +/* + * Set switch color of 'sw' to 'c', then re-draw the switch. + */ +function setSwitchColor(sw, c) +{ + if(!nms.switch_color || !nms.switch_color[sw] || nms.switch_color[sw] != c) { + nms.switch_color[sw] = c; + nms.damage = true; + } +} + +/* + * Return a random-ish color (for testing) + */ +function getRandomColor() +{ + var i = Math.round(Math.random() * 5); + var colors = [ "white", "red", "pink", "yellow", "orange", "green" ]; + return colors[i]; +} + +/* + * Helper functions for the front-end testing. + */ +function hideBorder() +{ + c.style.border = ""; +} + +function showBorder() +{ + c.style.border = "1px solid #000000"; +} + +/* + * Event handler for the front-end drag bar to change scale + */ +function scaleChange() +{ + var scaler = document.getElementById("scaler").value; + canvas.scale = scaler; + setScale(); +} + +/* + * Draw a "cross hair" at/around (x,y). + * + * Used for testing. + */ +function crossHair(x,y) +{ + ctx.fillStyle = "yellow"; + ctx.fillRect(x,y,-100,10); + ctx.fillStyle = "red"; + ctx.fillRect(x,y,100,10); + ctx.fillStyle = "blue"; + ctx.fillRect(x,y,10,-100); + ctx.fillStyle = "green"; + ctx.fillRect(x,y,10,100); +} + +/* + * Called when a switch is clicked + */ +function switchClick(sw) +{ + switchInfo(sw); +} + +/* + * Testing-function to randomize colors of linknets and switches + */ +function randomizeColors() +{ + for (var i in nms.switches_now.linknets) { + setLinknetColors(i, getRandomColor(), getRandomColor()); + } + for (var sw in nms.switches_now.switches) { + setSwitchColor(sw, getRandomColor()); + } + nms.damage = true; +} + +function discoInit() +{ + setNightMode(true); + setLegend(1,"blue","0"); + setLegend(5,"red", "1"); + setLegend(4,"yellow","2"); + setLegend(3,"green", "3"); + setLegend(2,"white","4"); +} +/* + * Resets the colors of linknets and switches. + * + * Useful when mode changes so we don't re-use colors from previous modes + * due to lack of data or bugs. + */ +function resetColors() +{ + if (!nms.switches_now) + return; + if (nms.switches_now.linknets) { + for (var i in nms.switches_now.linknets) { + setLinknetColors(i, "blue","blue"); + } + } + for (var sw in nms.switches_now.switches) { + setSwitchColor(sw, "blue"); + } +} + +/* + * onclick handler for the canvas + */ +function canvasClick(e) +{ + var sw = findSwitch(e.pageX - e.target.offsetLeft, e.pageY - e.target.offsetTop); + if (sw != undefined) { + switchClick(sw); + } +} + +/* + * Resize event-handler. + * + * Recomputes the scale and applies it. + * + * Has to use c.offset* since we are just scaling the canvas, not + * everything else. + * + */ +function resizeEvent() +{ + var width = window.innerWidth - c.offsetLeft; + var height = window.innerHeight - c.offsetTop; + if (width / (orig.width + margin.x) > height / (orig.height + margin.y)) { + canvas.scale = height / (orig.height + margin.y); + } else { + canvas.scale = width / (orig.width + margin.x); + } + setScale(); +} + +/* + * Draws the background image (scaled). + */ +function drawBG() +{ + if (nms.nightMode) { + invertCanvas(); + } else { + var image = document.getElementById('source'); + image.style.webkitFilter = "invert(100%)"; + ctx.drawImage(image, 0, 0, canvas.width, canvas.height); + } + nms.damage = true; + drawScene(); +} + +function setNightMode(toggle) { + nms.nightMode = toggle; + var body = document.getElementById("body"); + body.style.background = toggle ? "black" : "white"; + body.style.color = toggle ? "#00FF00" : "black"; + setScale(); +} +/* + * Draw a box (e.g.: switch). + */ +function drawBox(x,y,boxw,boxh) +{ + var myX = Math.round(x * canvas.scale); + var myY = Math.round(y * canvas.scale); + var myX2 = Math.round((boxw) * canvas.scale); + var myY2 = Math.round((boxh) * canvas.scale); + ctx.fillRect(myX,myY, myX2, myY2); + ctx.lineWidth = Math.round(0.5 * canvas.scale); + if (canvas.scale < 1.0) { + ctx.lineWidth = 0.5; + } + ctx.strokeStyle = "#000000"; + ctx.strokeRect(myX,myY, myX2, myY2); +} + +/* + * Draw text on a box - sideways! + * + * XXX: This is pretty nasty and should also probably take a box as input. + */ +function drawSideways(text,x,y,w,h) +{ + ctx.rotate(Math.PI * 3 / 2); + ctx.fillStyle = "white"; + ctx.strokeStyle = "black"; + ctx.lineWidth = Math.round(1 * canvas.scale); + if (canvas.scale < 0.7) { + ctx.lineWidth = 0.5; + } + ctx.fillText(text, - canvas.scale * (y + h - margin.text),canvas.scale * (x + w - margin.text) ); + ctx.strokeText(text, - canvas.scale * (y + h - margin.text),canvas.scale * (x + w - margin.text) ); + + ctx.rotate(Math.PI / 2); +} + +/* + * Draw background inverted (wooo) + * + * XXX: This is broken for chromium on local file system (e.g.: file:///) + * Seems like a chromium bug? + */ +function invertCanvas() { + var canvas = document.getElementById('myCanvas'); + var context = canvas.getContext('2d'); + var canvas2 = document.getElementById('mySecretCanvas'); + var context2 = canvas.getContext('2d'); + + var imageObj = document.getElementById('source'); + context2.drawImage(imageObj, 0, 0, canvas.width, canvas.height); + + var imageData = context2.getImageData(0, 0, canvas.width, canvas.height); + var data = imageData.data; + + for(var i = 0; i < data.length; i += 4) { + data[i] = 255 - data[i]; + data[i + 1] = 255 - data[i + 1]; + data[i + 2] = 255 - data[i + 2]; + } + context.putImageData(imageData, 0, 0); +} + +/* + * Draw regular text on a box. + * + * Should take the same format as drawSideways() + * + * XXX: Both should be renamed to have 'text' or something in them + */ +function drawRegular(text,x,y,w,h) { + + ctx.fillStyle = "white"; + ctx.strokeStyle = "black"; + ctx.lineWidth = Math.round(1 * canvas.scale); + if (canvas.scale < 0.7) { + ctx.lineWidth = 0.5; + } + ctx.fillText(text, (x + margin.text) * canvas.scale, (y + h - margin.text) * canvas.scale); + ctx.strokeText(text, (x + margin.text) * canvas.scale, (y + h - margin.text) * canvas.scale); +} + +/* + * Draw a line between switch "insw1" and "insw2", using a gradiant going + * from color1 to color2. + * + * XXX: beginPath() and closePath() is needed to avoid re-using the + * gradient/color + */ +function connectSwitches(insw1, insw2,color1, color2) { + var sw1 = nms.switches_now.switches[insw1].placement; + var sw2 = nms.switches_now.switches[insw2].placement; + if (color1 == undefined) + color1 = "blue"; + if (color2 == undefined) + color2 = "blue"; + var x0 = Math.floor((sw1.x + sw1.width/2) * canvas.scale); + var y0 = Math.floor((sw1.y + sw1.height/2) * canvas.scale); + var x1 = Math.floor((sw2.x + sw2.width/2) * canvas.scale); + var y1 = Math.floor((sw2.y + sw2.height/2) * canvas.scale); + var gradient = ctx.createLinearGradient(x1,y1,x0,y0); + gradient.addColorStop(0, color1); + gradient.addColorStop(1, color2); + ctx.beginPath(); + ctx.strokeStyle = gradient; + ctx.moveTo(x0,y0); + ctx.lineTo(x1,y1); + ctx.lineWidth = Math.floor(2 * canvas.scale); + ctx.closePath(); + ctx.stroke(); + ctx.moveTo(0,0); +} + +function debugIt(e) +{ + console.log("Debug triggered"); + console.log(e); +} -- cgit v1.2.3 From 3d0c76ac9ca315156e6cddcb361d747bd354bf98 Mon Sep 17 00:00:00 2001 From: Kristian Lyngstol Date: Fri, 10 Apr 2015 16:17:27 +0200 Subject: NMS2: Draw linknets, fix fonts and add replay --- web/nms.gathering.org/nms2/js/nms.js | 75 +++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 10 deletions(-) (limited to 'web/nms.gathering.org/nms2/js') diff --git a/web/nms.gathering.org/nms2/js/nms.js b/web/nms.gathering.org/nms2/js/nms.js index 101f2b1..7d9b288 100644 --- a/web/nms.gathering.org/nms2/js/nms.js +++ b/web/nms.gathering.org/nms2/js/nms.js @@ -10,6 +10,7 @@ var nms = { nightMode:false, nightBlur:{}, switch_color:{}, + linknet_color:{}, damage:false, drawText:true, now:false, @@ -23,7 +24,7 @@ var counters = { var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); -var fontSize = 16; +var fontSize = 14; var fontFace = "Arial Black"; var orig = { width:1920, @@ -104,6 +105,43 @@ function checkNow(now) { return false; } +var tgStart = { + year:2015, + month:04, + date:01, + hour:09, + minute:00, + second:0 +}; + +var replayTime = {}; + +var replayIncrement = 30; + +function timeReplay() { + nms.now = replayTime.year + '-' + replayTime.month + '-' + replayTime.date + ' ' + replayTime.hour + ':' + replayTime.minute + ':' + replayTime.second; + replayTime.minute += replayIncrement; + if (replayTime.minute >= 60) { + replayTime.minute = 0; + if (replayTime.hour == 23) { + replayTime.hour = 0; + replayTime.date += 1; + } else { + replayTime.hour += 1; + } + } + nms.damage = true; +} + +function startReplay() { + resetColors(); + for (var v in tgStart) { + replayTime[v] = tgStart[v]; + } + timeReplay(); + setInterval(timeReplay,1000); +} + function changeNow() { var newnow = checkNow(document.getElementById("nowPicker").value); if (!newnow) { @@ -453,6 +491,9 @@ function pingUpdater() for (var sw in nms.ping_data["switches"]) { setSwitchColor(sw, gradient_from_latency(nms.ping_data["switches"][sw]["latency"])); } + for (var ln in nms.ping_data["linknets"]) { + setLinknetColors(ln, gradient_from_latency(nms.ping_data["linknets"][ln][0]),gradient_from_latency(nms.ping_data["linknets"][ln][1])); + } } function pingInit() @@ -620,8 +661,8 @@ function updateSpeed() */ function drawLinknet(i) { - var c1 = nms.switches_now.linknets[i].c1 ? nms.switches_now.linknets[i].c1 : "blue"; - var c2 = nms.switches_now.linknets[i].c2 ? nms.switches_now.linknets[i].c2 : "blue"; + var c1 = nms.linknet_color[i] && nms.linknet_color[i].c1 ? nms.linknet_color[i].c1 : "blue"; + var c2 = nms.linknet_color[i] && nms.linknet_color[i].c2 ? nms.linknet_color[i].c2 : "blue"; if (nms.switches_now.switches[nms.switches_now.linknets[i].sysname1] && nms.switches_now.switches[nms.switches_now.linknets[i].sysname2]) { connectSwitches(nms.switches_now.linknets[i].sysname1,nms.switches_now.linknets[i].sysname2, c1, c2); } @@ -646,10 +687,13 @@ function drawLinknets() */ function setLinknetColors(i,c1,c2) { - if (nms.switches_now.linknets[i].c1 != c1 || - nms.switches_now.linknets[i].c2 != c2) { - nms.switches_now.linknets[i].c1 = c1; - nms.switches_now.linknets[i].c2 = c2; + if (!nms.linknet_color[i] || + nms.linknet_color[i].c1 != c1 || + nms.linknet_color[i].c2 != c2) { + if (!nms.linknet_color[i]) + nms.linknet_color[i] = {}; + nms.linknet_color[i]['c1'] = c1; + nms.linknet_color[i]['c2'] = c2; nms.damage = true; } } @@ -725,6 +769,17 @@ function drawSwitches() function drawScene() { if (nms.damage) { + if (nms.now != false) { + ctx.clearRect(0,0,200,20); + ctx.fillStyle = "white"; + ctx.strokeStyle = "black"; + ctx.lineWidth = Math.round(1 * canvas.scale); + if (canvas.scale < 0.7) { + ctx.lineWidth = 0.5; + } + ctx.strokeText("Now: " + nms.now, 0 + margin.text, 20 * canvas.scale); + ctx.fillText("Now: " + nms.now, 0 + margin.text, 20 * canvas.scale); + } ctx.font = Math.round(fontSize * canvas.scale) + "px " + fontFace; drawLinknets(); drawSwitches(); @@ -978,8 +1033,8 @@ function drawSideways(text,x,y,w,h) if (canvas.scale < 0.7) { ctx.lineWidth = 0.5; } - ctx.fillText(text, - canvas.scale * (y + h - margin.text),canvas.scale * (x + w - margin.text) ); ctx.strokeText(text, - canvas.scale * (y + h - margin.text),canvas.scale * (x + w - margin.text) ); + ctx.fillText(text, - canvas.scale * (y + h - margin.text),canvas.scale * (x + w - margin.text) ); ctx.rotate(Math.PI / 2); } @@ -1025,8 +1080,8 @@ function drawRegular(text,x,y,w,h) { if (canvas.scale < 0.7) { ctx.lineWidth = 0.5; } - ctx.fillText(text, (x + margin.text) * canvas.scale, (y + h - margin.text) * canvas.scale); ctx.strokeText(text, (x + margin.text) * canvas.scale, (y + h - margin.text) * canvas.scale); + ctx.fillText(text, (x + margin.text) * canvas.scale, (y + h - margin.text) * canvas.scale); } /* @@ -1054,7 +1109,7 @@ function connectSwitches(insw1, insw2,color1, color2) { ctx.strokeStyle = gradient; ctx.moveTo(x0,y0); ctx.lineTo(x1,y1); - ctx.lineWidth = Math.floor(2 * canvas.scale); + ctx.lineWidth = Math.floor(5 * canvas.scale); ctx.closePath(); ctx.stroke(); ctx.moveTo(0,0); -- cgit v1.2.3 From 7ee6554b7817aebf2d0a1983cf670c0828eeeb48 Mon Sep 17 00:00:00 2001 From: Kristian Lyngstol Date: Fri, 10 Apr 2015 18:04:53 +0200 Subject: NMS: Improve time travel and add About --- web/nms.gathering.org/nms2/js/nms.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'web/nms.gathering.org/nms2/js') diff --git a/web/nms.gathering.org/nms2/js/nms.js b/web/nms.gathering.org/nms2/js/nms.js index 7d9b288..7c4d960 100644 --- a/web/nms.gathering.org/nms2/js/nms.js +++ b/web/nms.gathering.org/nms2/js/nms.js @@ -132,14 +132,16 @@ function timeReplay() { } nms.damage = true; } - +var replayHandler = false; function startReplay() { + if (replayHandler) + clearInterval(replayHandler); resetColors(); for (var v in tgStart) { replayTime[v] = tgStart[v]; } timeReplay(); - setInterval(timeReplay,1000); + replayHandler = setInterval(timeReplay,1000); } function changeNow() { @@ -554,9 +556,10 @@ function initialUpdate() */ function updatePing() { + var now = nms.now ? ("?now=" + nms.now) : ""; $.ajax({ type: "GET", - url: "/ping-json2.pl?now=" + nms.now, + url: "/ping-json2.pl" + now, dataType: "text", success: function (data, textStatus, jqXHR) { nms.ping_data = JSON.parse(data); @@ -571,7 +574,7 @@ function updatePing() function updatePorts() { var now = ""; - if (nms.now) + if (nms.now != false) now = "?now=" + nms.now; $.ajax({ type: "GET", @@ -585,7 +588,7 @@ function updatePorts() } }); now=""; - if (nms.now) + if (nms.now != false) now = "&now=" + nms.now; $.ajax({ type: "GET", @@ -998,7 +1001,6 @@ function setNightMode(toggle) { nms.nightMode = toggle; var body = document.getElementById("body"); body.style.background = toggle ? "black" : "white"; - body.style.color = toggle ? "#00FF00" : "black"; setScale(); } /* -- cgit v1.2.3 From 870e95d022ac8dd3dc6d55c17629e2962ab3e87c Mon Sep 17 00:00:00 2001 From: Kristian Lyngstol Date: Fri, 10 Apr 2015 20:26:47 +0200 Subject: NMS: Tweak replay logic and fix linknet/ping drop We only get ping_data for live networks, but that creates a problem when we iterate that list instead of the complete list of switches/linknets: If a switch goes away, its last status would be shown. --- web/nms.gathering.org/nms2/js/nms.js | 62 +++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 29 deletions(-) (limited to 'web/nms.gathering.org/nms2/js') diff --git a/web/nms.gathering.org/nms2/js/nms.js b/web/nms.gathering.org/nms2/js/nms.js index 7c4d960..6f51b10 100644 --- a/web/nms.gathering.org/nms2/js/nms.js +++ b/web/nms.gathering.org/nms2/js/nms.js @@ -105,31 +105,28 @@ function checkNow(now) { return false; } -var tgStart = { - year:2015, - month:04, - date:01, - hour:09, - minute:00, - second:0 -}; +var tgStart = stringToEpoch('2015-03-31T15:00:00'); + +var replayTime = 0; -var replayTime = {}; +var replayIncrement = 30 * 60; -var replayIncrement = 30; +function stringToEpoch(t) { + var ret = new Date(Date.parse(t)); + return parseInt(parseInt(ret.valueOf()) / 1000); +} + +function epochToString(t) { + var d = new Date(parseInt(t) * parseInt(1000)); + var str = d.getFullYear() + "-" + (parseInt(d.getMonth())+1) + "-" + d.getDate() + "T"; + str += d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds(); + return str; +} + function timeReplay() { - nms.now = replayTime.year + '-' + replayTime.month + '-' + replayTime.date + ' ' + replayTime.hour + ':' + replayTime.minute + ':' + replayTime.second; - replayTime.minute += replayIncrement; - if (replayTime.minute >= 60) { - replayTime.minute = 0; - if (replayTime.hour == 23) { - replayTime.hour = 0; - replayTime.date += 1; - } else { - replayTime.hour += 1; - } - } + replayTime = parseInt(replayTime) + parseInt(replayIncrement); + nms.now = epochToString(replayTime); nms.damage = true; } var replayHandler = false; @@ -137,9 +134,7 @@ function startReplay() { if (replayHandler) clearInterval(replayHandler); resetColors(); - for (var v in tgStart) { - replayTime[v] = tgStart[v]; - } + replayTime = tgStart; timeReplay(); replayHandler = setInterval(timeReplay,1000); } @@ -490,11 +485,20 @@ function rgb_from_latency(latency_ms) function pingUpdater() { - for (var sw in nms.ping_data["switches"]) { - setSwitchColor(sw, gradient_from_latency(nms.ping_data["switches"][sw]["latency"])); + for (var sw in nms.switches_now["switches"]) { + var c = "blue"; + if (nms.ping_data['switches'] && nms.ping_data['switches'][sw]) + c = gradient_from_latency(nms.ping_data["switches"][sw]["latency"]); + setSwitchColor(sw, c); } - for (var ln in nms.ping_data["linknets"]) { - setLinknetColors(ln, gradient_from_latency(nms.ping_data["linknets"][ln][0]),gradient_from_latency(nms.ping_data["linknets"][ln][1])); + for (var ln in nms.switches_now["linknets"]) { + var c1 = "blue"; + var c2 = c1; + if (nms.ping_data['linknets'] && nms.ping_data['linknets'][ln]) { + c1 = gradient_from_latency(nms.ping_data["linknets"][ln][0]); + c2 = gradient_from_latency(nms.ping_data["linknets"][ln][1]); + } + setLinknetColors(ln, c1, c2); } } @@ -772,6 +776,7 @@ function drawSwitches() function drawScene() { if (nms.damage) { + ctx.font = Math.round(fontSize * canvas.scale) + "px " + fontFace; if (nms.now != false) { ctx.clearRect(0,0,200,20); ctx.fillStyle = "white"; @@ -783,7 +788,6 @@ function drawScene() ctx.strokeText("Now: " + nms.now, 0 + margin.text, 20 * canvas.scale); ctx.fillText("Now: " + nms.now, 0 + margin.text, 20 * canvas.scale); } - ctx.font = Math.round(fontSize * canvas.scale) + "px " + fontFace; drawLinknets(); drawSwitches(); nms.damage = false; -- cgit v1.2.3 From 87366459aeb9f8695430cfead08e8c59d74eb9d7 Mon Sep 17 00:00:00 2001 From: Kristian Lyngstol Date: Fri, 10 Apr 2015 21:14:34 +0200 Subject: NMS: Cleanups - Remove prototype (merged with index/nms) - Use fewer global variables --- web/nms.gathering.org/nms2/js/nms.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'web/nms.gathering.org/nms2/js') diff --git a/web/nms.gathering.org/nms2/js/nms.js b/web/nms.gathering.org/nms2/js/nms.js index 6f51b10..57e3ec6 100644 --- a/web/nms.gathering.org/nms2/js/nms.js +++ b/web/nms.gathering.org/nms2/js/nms.js @@ -14,6 +14,8 @@ var nms = { damage:false, drawText:true, now:false, + fontSize:14, + fontFace:"Arial Black", did_update:false // Set to 'true' after we've done some basic updating }; @@ -24,8 +26,6 @@ var counters = { var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); -var fontSize = 14; -var fontFace = "Arial Black"; var orig = { width:1920, height:1032 @@ -776,7 +776,7 @@ function drawSwitches() function drawScene() { if (nms.damage) { - ctx.font = Math.round(fontSize * canvas.scale) + "px " + fontFace; + ctx.font = Math.round(nms.fontSize * canvas.scale) + "px " + nms.fontFace; if (nms.now != false) { ctx.clearRect(0,0,200,20); ctx.fillStyle = "white"; -- cgit v1.2.3 From 3d9d4b13e3748dfc15811a28e2378c4061a1c772 Mon Sep 17 00:00:00 2001 From: Kristian Lyngstol Date: Sat, 11 Apr 2015 13:11:32 +0200 Subject: NMS: Multi-canvas drawing for potential performance Not a big deal for chromium at the moment, but firefox liked it. It also has more potential for future improvments. --- web/nms.gathering.org/nms2/js/nms.js | 253 ++++++++++++++++++++--------------- 1 file changed, 143 insertions(+), 110 deletions(-) (limited to 'web/nms.gathering.org/nms2/js') diff --git a/web/nms.gathering.org/nms2/js/nms.js b/web/nms.gathering.org/nms2/js/nms.js index 57e3ec6..a1dce66 100644 --- a/web/nms.gathering.org/nms2/js/nms.js +++ b/web/nms.gathering.org/nms2/js/nms.js @@ -11,7 +11,7 @@ var nms = { nightBlur:{}, switch_color:{}, linknet_color:{}, - damage:false, + textDrawn:{}, drawText:true, now:false, fontSize:14, @@ -19,13 +19,8 @@ var nms = { did_update:false // Set to 'true' after we've done some basic updating }; -var counters = { - undamaged:0, - damaged:0 -}; +var dr = {}; -var c = document.getElementById("myCanvas"); -var ctx = c.getContext("2d"); var orig = { width:1920, height:1032 @@ -42,7 +37,34 @@ var margin = { text:3 }; +var tgStart = stringToEpoch('2015-03-31T15:00:00'); +var tgEnd = stringToEpoch('2015-04-05T12:00:00'); +var replayTime = 0; +var replayIncrement = 30 * 60; +var replayHandler = false; +function initDrawing() { + dr['bg'] = {}; + dr['bg']['c'] = document.getElementById("bgCanvas"); + dr['bg']['ctx'] = dr['bg']['c'].getContext('2d'); + dr['link'] = {}; + dr['link']['c'] = document.getElementById("linkCanvas"); + dr['link']['ctx'] = dr['link']['c'].getContext('2d'); + dr['blur'] = {}; + dr['blur']['c'] = document.getElementById("blurCanvas"); + dr['blur']['ctx'] = dr['blur']['c'].getContext('2d'); + dr['switch'] = {}; + dr['switch']['c'] = document.getElementById("switchCanvas"); + dr['switch']['ctx'] = dr['switch']['c'].getContext('2d'); + dr['text'] = {}; + dr['text']['c'] = document.getElementById("textCanvas"); + dr['text']['ctx'] = dr['text']['c'].getContext('2d'); + dr['top'] = {}; + dr['top']['c'] = document.getElementById("topCanvas"); + dr['top']['ctx'] = dr['top']['c'].getContext('2d'); +} + +initDrawing(); /* * Handlers. "updater" is run periodically when the handler is active, and * "init" is run once when it's activated. @@ -88,11 +110,13 @@ function byteCount(bytes) { return bytes.toFixed(1) + units[i]; } -function toggleNightMode() { +function toggleNightMode() +{ setNightMode(!nms.nightMode); } -function checkNow(now) { +function checkNow(now) +{ if (Date.parse(now)) { var d = new Date(Date.parse(now)); var str = d.getFullYear() + "-" + (parseInt(d.getMonth())+1) + "-" + d.getDate() + " "; @@ -105,18 +129,15 @@ function checkNow(now) { return false; } -var tgStart = stringToEpoch('2015-03-31T15:00:00'); -var replayTime = 0; - -var replayIncrement = 30 * 60; - -function stringToEpoch(t) { +function stringToEpoch(t) +{ var ret = new Date(Date.parse(t)); return parseInt(parseInt(ret.valueOf()) / 1000); } -function epochToString(t) { +function epochToString(t) +{ var d = new Date(parseInt(t) * parseInt(1000)); var str = d.getFullYear() + "-" + (parseInt(d.getMonth())+1) + "-" + d.getDate() + "T"; str += d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds(); @@ -124,12 +145,17 @@ function epochToString(t) { } -function timeReplay() { +function timeReplay() +{ + if (replayTime >= tgEnd) { + clearInterval(replayHandler); + return; + } replayTime = parseInt(replayTime) + parseInt(replayIncrement); nms.now = epochToString(replayTime); - nms.damage = true; + drawNow(); } -var replayHandler = false; + function startReplay() { if (replayHandler) clearInterval(replayHandler); @@ -307,7 +333,8 @@ function switchInfo(x) function updateInfo() { if (!nms.drawn && nms.switches_now != undefined) { - nms.damage = true; + drawSwitches(); + nms.drawn = true; } var speedele = document.getElementById("speed"); speedele.innerHTML = (8 * parseInt(nms.speed) / 1024 / 1024 / 1024 ).toPrecision(5) + " Gbit/s"; @@ -414,12 +441,16 @@ function rgb_from_max(x) var colorred = 255 * x; var colorblue = 255 - colorred; - return 'rgb(' + Math.round(colorred) + ", 0, " + Math.round(colorblue) + ')'; + return 'rgb(' + Math.floor(colorred) + ", 0, " + Math.floor(colorblue) + ')'; } function temp_color(t) { - t = Math.round((t / 60) * 100); + if (t == undefined) { + console.log("Temp_color, but temp is undefined"); + return "blue"; + } + t = Math.floor((t / 60) * 100); return rgb_from_max(t); } @@ -437,9 +468,12 @@ function setLegend(x,color,name) function tempUpdater() { for (sw in nms.switches_now["switches"]) { - var t = nms.switches_now["switches"][sw]["temp"]; + var t = "white"; + if (nms.switches_now["switches"][sw]["temp"]) { + t = temp_color(nms.switches_now["switches"][sw]["temp"]); + } - setSwitchColor(sw, temp_color(t)); + setSwitchColor(sw, t); } } @@ -474,11 +508,11 @@ function rgb_from_latency(latency_ms) } else if (l >= 1.0) { l = 2.0 - l; l = Math.pow(l, 1.0/2.2); - l = Math.round(l * 255.0); + l = Math.floor(l * 255.0); return 'rgb(255, ' + l + ', 0)'; } else { l = Math.pow(l, 1.0/2.2); - l = Math.round(l * 255.0); + l = Math.floor(l * 255.0); return 'rgb(' + l + ', 255, 0)'; } } @@ -548,7 +582,8 @@ function initialUpdate() if (nms.ping_data && nms.switches_then && nms.switches_now && nms.updater != undefined && nms.did_update == false ) { resizeEvent(); if (!nms.drawn) { - nms.damage = true; + drawSwitches(); + drawLinknets(); } nms.updater(); nms.did_update = true; @@ -701,7 +736,7 @@ function setLinknetColors(i,c1,c2) nms.linknet_color[i] = {}; nms.linknet_color[i]['c1'] = c1; nms.linknet_color[i]['c2'] = c2; - nms.damage = true; + drawLinknet(i); } } @@ -717,22 +752,24 @@ function drawSwitch(sw) if (color == undefined) { color = "blue"; } - ctx.fillStyle = color; + dr.switch.ctx.fillStyle = color; if (nms.nightMode && nms.nightBlur[sw] != true) { - ctx.shadowBlur = 10; - ctx.shadowColor = "#00EE00"; + dr.switch.ctx.shadowBlur = 10; + dr.switch.ctx.shadowColor = "#00EE00"; nms.nightBlur[sw] = true; } else { - ctx.shadowBlur = 0; - ctx.shadowColor = "#000000"; + dr.switch.ctx.shadowBlur = 0; + dr.switch.ctx.shadowColor = "#000000"; } drawBox(box['x'],box['y'],box['width'],box['height']); - ctx.shadowBlur = 0; - if (nms.drawText) { - if ((box['width'] + 10 )< box['height'] ) - drawSideways(sw,box['x'],box['y'],box['width'],box['height']); - else - drawRegular(sw,box['x'],box['y'],box['width'],box['height']); + dr.switch.ctx.shadowBlur = 0; + if (!nms.textDrawn[sw]) { + if ((box['width'] + 10 )< box['height']) + drawSideways(sw,box['x'],box['y'],box['width'],box['height']); + else + drawRegular(sw,box['x'],box['y'],box['width'],box['height']); + + nms.textDrawn[sw] = true; } } @@ -766,6 +803,21 @@ function drawSwitches() nms.drawn = true; } +function drawNow() +{ + if (nms.now != false) { + dr.top.ctx.font = Math.round(nms.fontSize * canvas.scale) + "px " + nms.fontFace; + dr.top.ctx.clearRect(0,0,Math.floor(200 * canvas.scale),Math.floor(30 * canvas.scale)); + dr.top.ctx.fillStyle = "white"; + dr.top.ctx.strokeStyle = "black"; + dr.top.ctx.lineWidth = Math.round(1 * canvas.scale); + if (canvas.scale < 0.7) { + dr.top.ctx.lineWidth = 0.5; + } + dr.top.ctx.strokeText("Now: " + nms.now, 0 + margin.text, 20 * canvas.scale); + dr.top.ctx.fillText("Now: " + nms.now, 0 + margin.text, 20 * canvas.scale); + } +} /* * Draw foreground/scene. * @@ -775,26 +827,9 @@ function drawSwitches() */ function drawScene() { - if (nms.damage) { - ctx.font = Math.round(nms.fontSize * canvas.scale) + "px " + nms.fontFace; - if (nms.now != false) { - ctx.clearRect(0,0,200,20); - ctx.fillStyle = "white"; - ctx.strokeStyle = "black"; - ctx.lineWidth = Math.round(1 * canvas.scale); - if (canvas.scale < 0.7) { - ctx.lineWidth = 0.5; - } - ctx.strokeText("Now: " + nms.now, 0 + margin.text, 20 * canvas.scale); - ctx.fillText("Now: " + nms.now, 0 + margin.text, 20 * canvas.scale); - } - drawLinknets(); - drawSwitches(); - nms.damage = false; - counters.damaged++; - } else { - counters.undamaged++; - } + dr.text.ctx.font = Math.floor(nms.fontSize * canvas.scale) + "px " + nms.fontFace; + drawLinknets(); + drawSwitches(); } /* @@ -803,11 +838,18 @@ function drawScene() */ function setScale() { - c.height = canvas.height = orig.height * canvas.scale ; - c.width = canvas.width = orig.width * canvas.scale ; + + canvas.height = orig.height * canvas.scale ; + canvas.width = orig.width * canvas.scale ; + for (var a in dr) { + dr[a].c.height = canvas.height; + dr[a].c.width = canvas.width; + } + nms.nightBlur = {}; + nms.textDrawn = {}; drawBG(); - nightBlur = {}; drawScene(); + document.getElementById("scaler").value = canvas.scale; document.getElementById("scaler-text").innerHTML = (parseFloat(canvas.scale)).toPrecision(3); } @@ -848,7 +890,7 @@ function setSwitchColor(sw, c) { if(!nms.switch_color || !nms.switch_color[sw] || nms.switch_color[sw] != c) { nms.switch_color[sw] = c; - nms.damage = true; + drawSwitch(sw); } } @@ -921,7 +963,6 @@ function randomizeColors() for (var sw in nms.switches_now.switches) { setSwitchColor(sw, getRandomColor()); } - nms.damage = true; } function discoInit() @@ -975,8 +1016,8 @@ function canvasClick(e) */ function resizeEvent() { - var width = window.innerWidth - c.offsetLeft; - var height = window.innerHeight - c.offsetTop; + var width = window.innerWidth - dr.bg.c.offsetLeft; + var height = window.innerHeight - dr.bg.c.offsetTop; if (width / (orig.width + margin.x) > height / (orig.height + margin.y)) { canvas.scale = height / (orig.height + margin.y); } else { @@ -994,11 +1035,8 @@ function drawBG() invertCanvas(); } else { var image = document.getElementById('source'); - image.style.webkitFilter = "invert(100%)"; - ctx.drawImage(image, 0, 0, canvas.width, canvas.height); + dr.bg.ctx.drawImage(image, 0, 0, canvas.width, canvas.height); } - nms.damage = true; - drawScene(); } function setNightMode(toggle) { @@ -1012,17 +1050,17 @@ function setNightMode(toggle) { */ function drawBox(x,y,boxw,boxh) { - var myX = Math.round(x * canvas.scale); - var myY = Math.round(y * canvas.scale); - var myX2 = Math.round((boxw) * canvas.scale); - var myY2 = Math.round((boxh) * canvas.scale); - ctx.fillRect(myX,myY, myX2, myY2); - ctx.lineWidth = Math.round(0.5 * canvas.scale); + var myX = Math.floor(x * canvas.scale); + var myY = Math.floor(y * canvas.scale); + var myX2 = Math.floor((boxw) * canvas.scale); + var myY2 = Math.floor((boxh) * canvas.scale); + dr.switch.ctx.fillRect(myX,myY, myX2, myY2); + dr.switch.ctx.lineWidth = Math.floor(0.5 * canvas.scale); if (canvas.scale < 1.0) { - ctx.lineWidth = 0.5; + dr.switch.ctx.lineWidth = 0.5; } - ctx.strokeStyle = "#000000"; - ctx.strokeRect(myX,myY, myX2, myY2); + dr.switch.ctx.strokeStyle = "#000000"; + dr.switch.ctx.strokeRect(myX,myY, myX2, myY2); } /* @@ -1032,17 +1070,17 @@ function drawBox(x,y,boxw,boxh) */ function drawSideways(text,x,y,w,h) { - ctx.rotate(Math.PI * 3 / 2); - ctx.fillStyle = "white"; - ctx.strokeStyle = "black"; - ctx.lineWidth = Math.round(1 * canvas.scale); + dr.text.ctx.rotate(Math.PI * 3 / 2); + dr.text.ctx.fillStyle = "white"; + dr.text.ctx.strokeStyle = "black"; + dr.text.ctx.lineWidth = Math.floor(1 * canvas.scale); if (canvas.scale < 0.7) { - ctx.lineWidth = 0.5; + dr.text.ctx.lineWidth = 0.5; } - ctx.strokeText(text, - canvas.scale * (y + h - margin.text),canvas.scale * (x + w - margin.text) ); - ctx.fillText(text, - canvas.scale * (y + h - margin.text),canvas.scale * (x + w - margin.text) ); + dr.text.ctx.strokeText(text, - canvas.scale * (y + h - margin.text),canvas.scale * (x + w - margin.text) ); + dr.text.ctx.fillText(text, - canvas.scale * (y + h - margin.text),canvas.scale * (x + w - margin.text) ); - ctx.rotate(Math.PI / 2); + dr.text.ctx.rotate(Math.PI / 2); } /* @@ -1052,15 +1090,10 @@ function drawSideways(text,x,y,w,h) * Seems like a chromium bug? */ function invertCanvas() { - var canvas = document.getElementById('myCanvas'); - var context = canvas.getContext('2d'); - var canvas2 = document.getElementById('mySecretCanvas'); - var context2 = canvas.getContext('2d'); - var imageObj = document.getElementById('source'); - context2.drawImage(imageObj, 0, 0, canvas.width, canvas.height); + dr.bg.ctx.drawImage(imageObj, 0, 0, canvas.width, canvas.height); - var imageData = context2.getImageData(0, 0, canvas.width, canvas.height); + var imageData = dr.bg.ctx.getImageData(0, 0, canvas.width, canvas.height); var data = imageData.data; for(var i = 0; i < data.length; i += 4) { @@ -1068,7 +1101,7 @@ function invertCanvas() { data[i + 1] = 255 - data[i + 1]; data[i + 2] = 255 - data[i + 2]; } - context.putImageData(imageData, 0, 0); + dr.bg.ctx.putImageData(imageData, 0, 0); } /* @@ -1080,14 +1113,14 @@ function invertCanvas() { */ function drawRegular(text,x,y,w,h) { - ctx.fillStyle = "white"; - ctx.strokeStyle = "black"; - ctx.lineWidth = Math.round(1 * canvas.scale); + dr.text.ctx.fillStyle = "white"; + dr.text.ctx.strokeStyle = "black"; + dr.text.ctx.lineWidth = Math.floor(1 * canvas.scale); if (canvas.scale < 0.7) { - ctx.lineWidth = 0.5; + dr.text.ctx.lineWidth = 0.5; } - ctx.strokeText(text, (x + margin.text) * canvas.scale, (y + h - margin.text) * canvas.scale); - ctx.fillText(text, (x + margin.text) * canvas.scale, (y + h - margin.text) * canvas.scale); + dr.text.ctx.strokeText(text, (x + margin.text) * canvas.scale, (y + h - margin.text) * canvas.scale); + dr.text.ctx.fillText(text, (x + margin.text) * canvas.scale, (y + h - margin.text) * canvas.scale); } /* @@ -1108,17 +1141,17 @@ function connectSwitches(insw1, insw2,color1, color2) { var y0 = Math.floor((sw1.y + sw1.height/2) * canvas.scale); var x1 = Math.floor((sw2.x + sw2.width/2) * canvas.scale); var y1 = Math.floor((sw2.y + sw2.height/2) * canvas.scale); - var gradient = ctx.createLinearGradient(x1,y1,x0,y0); + var gradient = dr.link.ctx.createLinearGradient(x1,y1,x0,y0); gradient.addColorStop(0, color1); gradient.addColorStop(1, color2); - ctx.beginPath(); - ctx.strokeStyle = gradient; - ctx.moveTo(x0,y0); - ctx.lineTo(x1,y1); - ctx.lineWidth = Math.floor(5 * canvas.scale); - ctx.closePath(); - ctx.stroke(); - ctx.moveTo(0,0); + dr.link.ctx.beginPath(); + dr.link.ctx.strokeStyle = gradient; + dr.link.ctx.moveTo(x0,y0); + dr.link.ctx.lineTo(x1,y1); + dr.link.ctx.lineWidth = Math.floor(5 * canvas.scale); + dr.link.ctx.closePath(); + dr.link.ctx.stroke(); + dr.link.ctx.moveTo(0,0); } function debugIt(e) -- cgit v1.2.3 From 868a1c5a7c5b1785109a533151bb9e631adf9974 Mon Sep 17 00:00:00 2001 From: Kristian Lyngstol Date: Sat, 11 Apr 2015 14:51:00 +0200 Subject: NMS: Bump jquery and bootstrap version Also introduces several related style-fixes. --- web/nms.gathering.org/nms2/js/bootstrap-slider.js | 388 ---- web/nms.gathering.org/nms2/js/bootstrap.js | 2317 ++++++++++++++++++++ web/nms.gathering.org/nms2/js/bootstrap.min.js | 11 +- web/nms.gathering.org/nms2/js/jquery-1.10.2.min.js | 6 - web/nms.gathering.org/nms2/js/jquery.min.js | 4 + web/nms.gathering.org/nms2/js/nms.js | 6 +- web/nms.gathering.org/nms2/js/npm.js | 13 + 7 files changed, 2344 insertions(+), 401 deletions(-) delete mode 100644 web/nms.gathering.org/nms2/js/bootstrap-slider.js create mode 100644 web/nms.gathering.org/nms2/js/bootstrap.js delete mode 100644 web/nms.gathering.org/nms2/js/jquery-1.10.2.min.js create mode 100644 web/nms.gathering.org/nms2/js/jquery.min.js create mode 100644 web/nms.gathering.org/nms2/js/npm.js (limited to 'web/nms.gathering.org/nms2/js') diff --git a/web/nms.gathering.org/nms2/js/bootstrap-slider.js b/web/nms.gathering.org/nms2/js/bootstrap-slider.js deleted file mode 100644 index fe752f5..0000000 --- a/web/nms.gathering.org/nms2/js/bootstrap-slider.js +++ /dev/null @@ -1,388 +0,0 @@ -/* ========================================================= - * bootstrap-slider.js v2.0.0 - * http://www.eyecon.ro/bootstrap-slider - * ========================================================= - * Copyright 2012 Stefan Petre - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ========================================================= */ - -!function( $ ) { - - var Slider = function(element, options) { - this.element = $(element); - this.picker = $('
'+ - '
'+ - '
'+ - '
'+ - '
'+ - '
'+ - '
'+ - '
') - .insertBefore(this.element) - .append(this.element); - this.id = this.element.data('slider-id')||options.id; - if (this.id) { - this.picker[0].id = this.id; - } - - if (typeof Modernizr !== 'undefined' && Modernizr.touch) { - this.touchCapable = true; - } - - var tooltip = this.element.data('slider-tooltip')||options.tooltip; - - this.tooltip = this.picker.find('.tooltip'); - this.tooltipInner = this.tooltip.find('div.tooltip-inner'); - - this.orientation = this.element.data('slider-orientation')||options.orientation; - switch(this.orientation) { - case 'vertical': - this.picker.addClass('slider-vertical'); - this.stylePos = 'top'; - this.mousePos = 'pageY'; - this.sizePos = 'offsetHeight'; - this.tooltip.addClass('right')[0].style.left = '100%'; - break; - default: - this.picker - .addClass('slider-horizontal') - .css('width', this.element.outerWidth()); - this.orientation = 'horizontal'; - this.stylePos = 'left'; - this.mousePos = 'pageX'; - this.sizePos = 'offsetWidth'; - this.tooltip.addClass('top')[0].style.top = -this.tooltip.outerHeight() - 14 + 'px'; - break; - } - - this.min = this.element.data('slider-min')||options.min; - this.max = this.element.data('slider-max')||options.max; - this.step = this.element.data('slider-step')||options.step; - this.value = this.element.data('slider-value')||options.value; - if (this.value[1]) { - this.range = true; - } - - this.selection = this.element.data('slider-selection')||options.selection; - this.selectionEl = this.picker.find('.slider-selection'); - if (this.selection === 'none') { - this.selectionEl.addClass('hide'); - } - this.selectionElStyle = this.selectionEl[0].style; - - - this.handle1 = this.picker.find('.slider-handle:first'); - this.handle1Stype = this.handle1[0].style; - this.handle2 = this.picker.find('.slider-handle:last'); - this.handle2Stype = this.handle2[0].style; - - var handle = this.element.data('slider-handle')||options.handle; - switch(handle) { - case 'round': - this.handle1.addClass('round'); - this.handle2.addClass('round'); - break - case 'triangle': - this.handle1.addClass('triangle'); - this.handle2.addClass('triangle'); - break - } - - if (this.range) { - this.value[0] = Math.max(this.min, Math.min(this.max, this.value[0])); - this.value[1] = Math.max(this.min, Math.min(this.max, this.value[1])); - } else { - this.value = [ Math.max(this.min, Math.min(this.max, this.value))]; - this.handle2.addClass('hide'); - if (this.selection == 'after') { - this.value[1] = this.max; - } else { - this.value[1] = this.min; - } - } - this.diff = this.max - this.min; - this.percentage = [ - (this.value[0]-this.min)*100/this.diff, - (this.value[1]-this.min)*100/this.diff, - this.step*100/this.diff - ]; - - this.offset = this.picker.offset(); - this.size = this.picker[0][this.sizePos]; - - this.formater = options.formater; - - this.layout(); - - if (this.touchCapable) { - // Touch: Bind touch events: - this.picker.on({ - touchstart: $.proxy(this.mousedown, this) - }); - } else { - this.picker.on({ - mousedown: $.proxy(this.mousedown, this) - }); - } - - if (tooltip === 'show') { - this.picker.on({ - mouseenter: $.proxy(this.showTooltip, this), - mouseleave: $.proxy(this.hideTooltip, this) - }); - } else { - this.tooltip.addClass('hide'); - } - }; - - Slider.prototype = { - constructor: Slider, - - over: false, - inDrag: false, - - showTooltip: function(){ - this.tooltip.addClass('in'); - //var left = Math.round(this.percent*this.width); - //this.tooltip.css('left', left - this.tooltip.outerWidth()/2); - this.over = true; - }, - - hideTooltip: function(){ - if (this.inDrag === false) { - this.tooltip.removeClass('in'); - } - this.over = false; - }, - - layout: function(){ - this.handle1Stype[this.stylePos] = this.percentage[0]+'%'; - this.handle2Stype[this.stylePos] = this.percentage[1]+'%'; - if (this.orientation == 'vertical') { - this.selectionElStyle.top = Math.min(this.percentage[0], this.percentage[1]) +'%'; - this.selectionElStyle.height = Math.abs(this.percentage[0] - this.percentage[1]) +'%'; - } else { - this.selectionElStyle.left = Math.min(this.percentage[0], this.percentage[1]) +'%'; - this.selectionElStyle.width = Math.abs(this.percentage[0] - this.percentage[1]) +'%'; - } - if (this.range) { - this.tooltipInner.text( - this.formater(this.value[0]) + - ' : ' + - this.formater(this.value[1]) - ); - this.tooltip[0].style[this.stylePos] = this.size * (this.percentage[0] + (this.percentage[1] - this.percentage[0])/2)/100 - (this.orientation === 'vertical' ? this.tooltip.outerHeight()/2 : this.tooltip.outerWidth()/2) +'px'; - } else { - this.tooltipInner.text( - this.formater(this.value[0]) - ); - this.tooltip[0].style[this.stylePos] = this.size * this.percentage[0]/100 - (this.orientation === 'vertical' ? this.tooltip.outerHeight()/2 : this.tooltip.outerWidth()/2) +'px'; - } - }, - - mousedown: function(ev) { - - // Touch: Get the original event: - if (this.touchCapable && ev.type === 'touchstart') { - ev = ev.originalEvent; - } - - this.offset = this.picker.offset(); - this.size = this.picker[0][this.sizePos]; - - var percentage = this.getPercentage(ev); - - if (this.range) { - var diff1 = Math.abs(this.percentage[0] - percentage); - var diff2 = Math.abs(this.percentage[1] - percentage); - this.dragged = (diff1 < diff2) ? 0 : 1; - } else { - this.dragged = 0; - } - - this.percentage[this.dragged] = percentage; - this.layout(); - - if (this.touchCapable) { - // Touch: Bind touch events: - $(document).on({ - touchmove: $.proxy(this.mousemove, this), - touchend: $.proxy(this.mouseup, this) - }); - } else { - $(document).on({ - mousemove: $.proxy(this.mousemove, this), - mouseup: $.proxy(this.mouseup, this) - }); - } - - this.inDrag = true; - var val = this.calculateValue(); - this.element.trigger({ - type: 'slideStart', - value: val - }).trigger({ - type: 'slide', - value: val - }); - return false; - }, - - mousemove: function(ev) { - - // Touch: Get the original event: - if (this.touchCapable && ev.type === 'touchmove') { - ev = ev.originalEvent; - } - - var percentage = this.getPercentage(ev); - if (this.range) { - if (this.dragged === 0 && this.percentage[1] < percentage) { - this.percentage[0] = this.percentage[1]; - this.dragged = 1; - } else if (this.dragged === 1 && this.percentage[0] > percentage) { - this.percentage[1] = this.percentage[0]; - this.dragged = 0; - } - } - this.percentage[this.dragged] = percentage; - this.layout(); - var val = this.calculateValue(); - this.element - .trigger({ - type: 'slide', - value: val - }) - .data('value', val) - .prop('value', val); - return false; - }, - - mouseup: function(ev) { - if (this.touchCapable) { - // Touch: Bind touch events: - $(document).off({ - touchmove: this.mousemove, - touchend: this.mouseup - }); - } else { - $(document).off({ - mousemove: this.mousemove, - mouseup: this.mouseup - }); - } - - this.inDrag = false; - if (this.over == false) { - this.hideTooltip(); - } - this.element; - var val = this.calculateValue(); - this.element - .trigger({ - type: 'slideStop', - value: val - }) - .data('value', val) - .prop('value', val); - return false; - }, - - calculateValue: function() { - var val; - if (this.range) { - val = [ - (this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step), - (this.min + Math.round((this.diff * this.percentage[1]/100)/this.step)*this.step) - ]; - this.value = val; - } else { - val = (this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step); - this.value = [val, this.value[1]]; - } - return val; - }, - - getPercentage: function(ev) { - if (this.touchCapable) { - ev = ev.touches[0]; - } - var percentage = (ev[this.mousePos] - this.offset[this.stylePos])*100/this.size; - percentage = Math.round(percentage/this.percentage[2])*this.percentage[2]; - return Math.max(0, Math.min(100, percentage)); - }, - - getValue: function() { - if (this.range) { - return this.value; - } - return this.value[0]; - }, - - setValue: function(val) { - this.value = val; - - if (this.range) { - this.value[0] = Math.max(this.min, Math.min(this.max, this.value[0])); - this.value[1] = Math.max(this.min, Math.min(this.max, this.value[1])); - } else { - this.value = [ Math.max(this.min, Math.min(this.max, this.value))]; - this.handle2.addClass('hide'); - if (this.selection == 'after') { - this.value[1] = this.max; - } else { - this.value[1] = this.min; - } - } - this.diff = this.max - this.min; - this.percentage = [ - (this.value[0]-this.min)*100/this.diff, - (this.value[1]-this.min)*100/this.diff, - this.step*100/this.diff - ]; - this.layout(); - } - }; - - $.fn.slider = function ( option, val ) { - return this.each(function () { - var $this = $(this), - data = $this.data('slider'), - options = typeof option === 'object' && option; - if (!data) { - $this.data('slider', (data = new Slider(this, $.extend({}, $.fn.slider.defaults,options)))); - } - if (typeof option == 'string') { - data[option](val); - } - }) - }; - - $.fn.slider.defaults = { - min: 0, - max: 10, - step: 1, - orientation: 'horizontal', - value: 5, - selection: 'before', - tooltip: 'show', - handle: 'round', - formater: function(value) { - return value; - } - }; - - $.fn.slider.Constructor = Slider; - -}( window.jQuery ); \ No newline at end of file diff --git a/web/nms.gathering.org/nms2/js/bootstrap.js b/web/nms.gathering.org/nms2/js/bootstrap.js new file mode 100644 index 0000000..1c88b71 --- /dev/null +++ b/web/nms.gathering.org/nms2/js/bootstrap.js @@ -0,0 +1,2317 @@ +/*! + * Bootstrap v3.3.4 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +if (typeof jQuery === 'undefined') { + throw new Error('Bootstrap\'s JavaScript requires jQuery') +} + ++function ($) { + 'use strict'; + var version = $.fn.jquery.split(' ')[0].split('.') + if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) { + throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher') + } +}(jQuery); + +/* ======================================================================== + * Bootstrap: transition.js v3.3.4 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + WebkitTransition : 'webkitTransitionEnd', + MozTransition : 'transitionend', + OTransition : 'oTransitionEnd otransitionend', + transition : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } + } + } + + return false // explicit for ie8 ( ._.) + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false + var $el = this + $(this).one('bsTransitionEnd', function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } + + $(function () { + $.support.transition = transitionEnd() + + if (!$.support.transition) return + + $.event.special.bsTransitionEnd = { + bindType: $.support.transition.end, + delegateType: $.support.transition.end, + handle: function (e) { + if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) + } + } + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: alert.js v3.3.4 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.VERSION = '3.3.4' + + Alert.TRANSITION_DURATION = 150 + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.closest('.alert') + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + // detach from parent, fire event then clean up data + $parent.detach().trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one('bsTransitionEnd', removeElement) + .emulateTransitionEnd(Alert.TRANSITION_DURATION) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.alert + + $.fn.alert = Plugin + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: button.js v3.3.4 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } + + Button.VERSION = '3.3.4' + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state = state + 'Text' + + if (data.resetText == null) $el.data('resetText', $el[val]()) + + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + $el[val](data[state] == null ? this.options[state] : data[state]) + + if (state == 'loadingText') { + this.isLoading = true + $el.addClass(d).attr(d, d) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d) + } + }, this), 0) + } + + Button.prototype.toggle = function () { + var changed = true + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') == 'radio') { + if ($input.prop('checked') && this.$element.hasClass('active')) changed = false + else $parent.find('.active').removeClass('active') + } + if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } else { + this.$element.attr('aria-pressed', !this.$element.hasClass('active')) + } + + if (changed) this.$element.toggleClass('active') + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + var old = $.fn.button + + $.fn.button = Plugin + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document) + .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + Plugin.call($btn, 'toggle') + e.preventDefault() + }) + .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { + $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: carousel.js v3.3.4 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = null + this.sliding = null + this.interval = null + this.$active = null + this.$items = null + + this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) + + this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element + .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) + } + + Carousel.VERSION = '3.3.4' + + Carousel.TRANSITION_DURATION = 600 + + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true, + keyboard: true + } + + Carousel.prototype.keydown = function (e) { + if (/input|textarea/i.test(e.target.tagName)) return + switch (e.which) { + case 37: this.prev(); break + case 39: this.next(); break + default: return + } + + e.preventDefault() + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getItemIndex = function (item) { + this.$items = item.parent().children('.item') + return this.$items.index(item || this.$active) + } + + Carousel.prototype.getItemForDirection = function (direction, active) { + var activeIndex = this.getItemIndex(active) + var willWrap = (direction == 'prev' && activeIndex === 0) + || (direction == 'next' && activeIndex == (this.$items.length - 1)) + if (willWrap && !this.options.wrap) return active + var delta = direction == 'prev' ? -1 : 1 + var itemIndex = (activeIndex + delta) % this.$items.length + return this.$items.eq(itemIndex) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || this.getItemForDirection(type, $active) + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var that = this + + if ($next.hasClass('active')) return (this.sliding = false) + + var relatedTarget = $next[0] + var slideEvent = $.Event('slide.bs.carousel', { + relatedTarget: relatedTarget, + direction: direction + }) + this.$element.trigger(slideEvent) + if (slideEvent.isDefaultPrevented()) return + + this.sliding = true + + isCycling && this.pause() + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) + $nextIndicator && $nextIndicator.addClass('active') + } + + var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" + if ($.support.transition && this.$element.hasClass('slide')) { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one('bsTransitionEnd', function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { + that.$element.trigger(slidEvent) + }, 0) + }) + .emulateTransitionEnd(Carousel.TRANSITION_DURATION) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger(slidEvent) + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + var old = $.fn.carousel + + $.fn.carousel = Plugin + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + var clickHandler = function (e) { + var href + var $this = $(this) + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 + if (!$target.hasClass('carousel')) return + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + Plugin.call($target, options) + + if (slideIndex) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + } + + $(document) + .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) + .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + Plugin.call($carousel, $carousel.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.3.4 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + + '[data-toggle="collapse"][data-target="#' + element.id + '"]') + this.transitioning = null + + if (this.options.parent) { + this.$parent = this.getParent() + } else { + this.addAriaAndCollapsedClass(this.$element, this.$trigger) + } + + if (this.options.toggle) this.toggle() + } + + Collapse.VERSION = '3.3.4' + + Collapse.TRANSITION_DURATION = 350 + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var activesData + var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') + + if (actives && actives.length) { + activesData = actives.data('bs.collapse') + if (activesData && activesData.transitioning) return + } + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + if (actives && actives.length) { + Plugin.call(actives, 'hide') + activesData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing')[dimension](0) + .attr('aria-expanded', true) + + this.$trigger + .removeClass('collapsed') + .attr('aria-expanded', true) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in')[dimension]('') + this.transitioning = 0 + this.$element + .trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element[dimension](this.$element[dimension]())[0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse in') + .attr('aria-expanded', false) + + this.$trigger + .addClass('collapsed') + .attr('aria-expanded', false) + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .removeClass('collapsing') + .addClass('collapse') + .trigger('hidden.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + Collapse.prototype.getParent = function () { + return $(this.options.parent) + .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') + .each($.proxy(function (i, element) { + var $element = $(element) + this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) + }, this)) + .end() + } + + Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { + var isOpen = $element.hasClass('in') + + $element.attr('aria-expanded', isOpen) + $trigger + .toggleClass('collapsed', !isOpen) + .attr('aria-expanded', isOpen) + } + + function getTargetFromTrigger($trigger) { + var href + var target = $trigger.attr('data-target') + || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + + return $(target) + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.collapse + + $.fn.collapse = Plugin + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { + var $this = $(this) + + if (!$this.attr('data-target')) e.preventDefault() + + var $target = getTargetFromTrigger($this) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + + Plugin.call($target, option) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: dropdown.js v3.3.4 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle="dropdown"]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.VERSION = '3.3.4' + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $('