var ajax_site = 'ajax';
var refresh_rate = 3000;
var send_timeout = 20000;
var retry_rate = 3000;
function Blocker(sleeper) {
this.counter = 0;
this.waiting = false;
this.sleeper = sleeper;
}
Blocker.prototype.setHook = function() {
var blocker = this;
return function() {
blocker.counter++;
}
}
Blocker.prototype.setNotifier = function(cb) {
var blocker = this;
return function() {
cb.apply(this, arguments);
if (--blocker.counter == 0 && blocker.waiting)
blocker.sleeper();
}
};
Blocker.prototype.go = function() {
if (this.counter == 0)
this.sleeper();
this.waiting = true;
}
function send_request(on_success) {
$.ajax({
url: ajax_site,
type: 'GET',
cache: false,
dataType: 'json',
async: true,
success: on_success,
timeout: send_timeout,
error: function(a, b, c) {
console.log("failed while connecting, reason: " + b);
setTimeout(function() {
send_request(on_success);
}, retry_rate);
}
});
}
function hex(x) {
return ("0" + parseInt(x).toString(16)).slice(-2);
}
function random_range(min, max) {
return Math.random() * (max - min) + min;
}
function random_color() {
var r = (random_range(0, 200) + 150) / 2;
var g = (random_range(0, 200) + 150) / 2;
var b = (random_range(0, 200) + 150) / 2;
return "#" + hex(r) + hex(g) + hex(b);
}
function LineGraph() {}
LineGraph.prototype.recalc_y_domain = function(data) {
var min = d3.min(data, function(d) { return d.y; });
var max = d3.max(data, function(d) { return d.y; });
var delta = max - min;
this.y.domain([min - delta / 2.0, max + delta / 3.0]);
}
LineGraph.prototype.recalc_domain = function(data) {
this.x.domain(d3.extent(data, function(d) { return d.x; }));
this.recalc_y_domain(data);
}
LineGraph.prototype.parse_data = function(d) {
var data = [];
for (var i = 0; i < d.records.length; i++)
{
var rec = d.records[i];
data.push({x: i, y: +rec.rec[0], rid: rec.rid});
}
return data;
}
LineGraph.prototype.update = function(d, i, b) {
var graph = this;
var data = this.parse_data(d);
if (data.length == 0) return;
var pdata = this.pdata;
var svg = d3.select(this.elem).select("svg");
var path = svg.select(".line");
var shift = pdata.length ? data[0].rid - pdata[0].rid : 0;
var add = pdata.length ? data[data.length - 1].rid - pdata[pdata.length - 1].rid : 0;
if (pdata.length == 0 || pdata[pdata.length - 1].rid < data[0].rid ||
pdata[0] > data[data.length - 1].rid || shift < 0 || add < 0)
{
if (data.length == 1)
{
data = [];
console.log("clear");
}
this.recalc_domain(data);
path.transition()
.duration(200)
.style("opacity", 1e-6)
.transition()
.attr("d", graph.valueline(data))
.transition()
.duration(200)
.style("opacity", 1)
.each(b.setHook())
.each("end", b.setNotifier(function() {
graph.pdata = data;
console.log("refresh complete");
}));
console.log("refresh started");
}
else
{
this.recalc_domain(pdata);
//console.log("shift: " + shift + "add: " + add);
data = this.pdata.concat(data.slice(data.length - add, data.length));
for (var i = 0; i < data.length; i++)
data[i].x = i;
this.recalc_y_domain(data);
path.transition().duration(750)
.attr("d", graph.valueline(data))
.transition()
.duration(750)
.attr("transform", "translate(" + this.x(-shift) + ")")
.each(b.setHook())
.each("end", b.setNotifier(function() {
for (var i = 0; i < shift; i++)
data.shift();
for (var i = 0; i < data.length; i++)
data[i].x = i;
graph.pdata = data;
d3.select(this).attr("d", graph.valueline(data))
.attr("transform", "translate(" + graph.x(0) + ")");
}));
}
svg.transition()
.duration(750)
.select(".x.axis")
.call(this.xAxis);
svg.transition()
.duration(750)
.select(".y.axis")
.call(this.yAxis);
}
LineGraph.prototype.setup = function(elem, d, i, b) {
var graph = this;
var data = this.parse_data(d);
this.elem = elem;
var margin = {top: 10, right: 0, bottom: 50, left: 30};
// Adds the svg canvas
var svg = d3.select(this.elem)
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var width = 500 - margin.left - margin.right;
var height = 250 - margin.top - margin.bottom;
console.log(width);
console.log(height);
// Set the ranges
this.x = d3.scale.linear().range([0, width]);
this.y = d3.scale.linear().range([height, 0]);
// Scale the range of the data
this.recalc_domain(data);
// Define the axes
this.xAxis = d3.svg.axis().scale(this.x)
.orient("bottom").ticks(5).tickFormat(d3.format("d"));
this.yAxis = d3.svg.axis().scale(this.y)
.orient("left").ticks(5);
// Define the line
this.valueline = d3.svg.line()
.x(function(d) { return this.x(d.x); })
.y(function(d) { return this.y(d.y); });
/*
svg.append("text")
.attr("x", (width / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("text-decoration", "none")
.text(d.name);
*/
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(this.xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(this.yAxis);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
d3.select(this.elem).transition()
.duration(750)
.style("height", height + margin.top + margin.bottom + "px")
.each(b.setHook())
.each("end", b.setNotifier(function() {
// Get the data
var path = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("path");
path.attr("class", "line")
.style("stroke", random_color)
.attr("d", graph.valueline(data));
var totalLength = path.node().getTotalLength();
path.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.ease("linear")
.attr("stroke-dashoffset", 0)
.each(b.setHook())
.each("end", b.setNotifier(function() {
path.attr("stroke-dasharray", "");
}));
graph.pdata = data;
}));
}
function ListGraph() {}
ListGraph.prototype.parse_data = function(d) {
var data = [];
for (var i = 0; i < d.records.length; i++)
{
var rec = d.records[i];
data.push({rid: rec[0], rec: rec});
}
return data;
}
ListGraph.prototype.setup = function(elem, d, i, b) {
var data = this.parse_data(d);
var div = d3.select(elem);
this.elem = elem;
/*
div.append("div")
.style("text-align", "center")
.append("a")
.style("font-size", "16px")
.style("text-decoration", "none")
.text(d.name);
*/
var table = d3.select(elem)
.append("table")
.attr("class", "listgraph table")
.style("width", "100%")
.style("height", "100%");
this.table = table;
table.selectAll("tr").data(data)
.enter().append("tr")
.selectAll("td").data(function(d) { return d.rec; })
.enter().append("td")
.style("opacity", 0)
.transition().duration(750)
.style("opacity", 1)
.text(function(d) { return d; })
.each(b.setHook())
.each("end", b.setNotifier(function(){}));
d3.select(elem)
//.transition().duration(750)
.style("height", null);
}
ListGraph.prototype.update = function(d, i, b) {
var graph = this;
var data = this.parse_data(d);
var items = this.table.selectAll("tr").data(data, function(d) { return d.rid; });
b.setHook()();
var b0 = new Blocker(b.setNotifier(function() {
items.order();
}));
items.selectAll("td").text(function(d) { return d; });
items.enter()
.append("tr")
.selectAll("td")
.data(function(d) { return d.rec; })
.enter()
.append("td")
.text(function(d) { return d; });
items.exit().remove();
b0.go();
}
var graph_handler = {"linegraph": LineGraph, "listgraph": ListGraph};
function update_jobs(resp, after) {
resp = Object.keys(resp).map(function(k) { return resp[k] });
var jobs = d3.select("#jobs").selectAll(".job_graph").data(resp, function(d) { return d.jid; });
var b = new Blocker(after);
jobs.each(function(d, i) {
d3.select(this).select(".job_content").node().handler.update(d, i, b);
});
var outer = jobs.enter()
.append("div")
.attr("class", "job_graph panel panel-info")
.style("width", "540px");
outer.append("div")
.attr("class", "panel-heading")
.text(function(d) { return d.name; });
outer.append("div")
.attr("class", "job_content panel-body")
.style("height", "0px")
.each(function(d, i) {
this.handler = new graph_handler[d.metadata.type]();
this.handler.setup(this, d, i, b);
});
console.log(jobs.exit());
jobs.exit()
.style("height", function(d) { return d3.select(this).style("height"); })
.transition().duration(750)
.style("height", "0px")
.each(b.setHook())
.each("end",b.setNotifier(function(){}))
.remove();
b.go();
}
function graph_tick() {
send_request(function(resp) {
update_jobs(resp, function() {
setTimeout(graph_tick, refresh_rate);
});
});
}
graph_tick();