This repository has been archived on 2025-11-02. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
dashboard/assets/javascripts/timeknots.js
2022-09-20 00:27:25 +01:00

248 lines
9.1 KiB
JavaScript

var TimeKnots = {
draw: function(id, events, options) {
// remove any exisitng elements
d3.select(id).select("div").html("");
var TODAY = Date.now();
var cfg = {
width: 600,
height: 200,
radius: 10,
lineWidth: 4,
color: "#999",
background: "#FFF",
dateFormat: "%Y/%m/%d %H:%M:%S",
horizontalLayout: true,
showLabels: false,
labelFormat: "%Y/%m/%d %H:%M:%S",
addNow: false,
seriesColor: d3.scale.category20(),
dateDimension: true
};
//default configuration overrid
if (options != undefined) {
for (var i in options) {
cfg[i] = options[i];
}
}
if (cfg.addNow != false) {
events.push({
date: new Date(),
name: cfg.addNowLabel || "Today"
});
}
//sort events ascending
events.sort(function(a,b){
return new Date(a.date) - new Date(b.date);
});
var svg = d3.select(id).select("div").append('svg').attr("width", cfg.width).attr("height", cfg.height);
//Calculate times in terms of timestamps
if (!cfg.dateDimension) {
var timestamps = events.map(function(d) {
return d.value
}); //new Date(d.date).getTime()});
var maxValue = d3.max(timestamps);
var minValue = d3.min(timestamps);
} else {
var timestamps = events.map(function(d) {
return Date.parse(d.date);
}); //new Date(d.date).getTime()});
var maxValue = d3.max(timestamps);
var minValue = d3.min(timestamps);
}
var margin = (d3.max(events.map(function(d) {
return d.radius
})) || cfg.radius) * 1.5 + cfg.lineWidth;
var step = (cfg.horizontalLayout) ? ((cfg.width - 2 * margin) / (maxValue - minValue)) : ((cfg.height - 2 * margin) / (maxValue - minValue));
var series = [];
if (maxValue == minValue) {
step = 0;
if (cfg.horizontalLayout) {
margin = cfg.width / 2
} else {
margin = cfg.height / 2
}
}
linePrevious = {
x1: null,
x2: null,
y1: null,
y2: null
}
TIMELINE_LEFT_MARGIN = 130;
TEXT_LEFT_MARGIN = 110;
DATE_LEFT_MARGIN = 100;
svg.selectAll("line")
.data(events).enter().append("line")
.attr("class", "timeline-line")
.attr("x1", function(d) {
var ret;
if (cfg.horizontalLayout) {
var datum = (cfg.dateDimension) ? new Date(d.date).getTime() : d.value;
ret = Math.floor(step * (datum - minValue) + margin) - TIMELINE_LEFT_MARGIN
} else {
ret = Math.floor(cfg.width / 2) - TIMELINE_LEFT_MARGIN
}
linePrevious.x1 = ret
return ret
})
.attr("x2", function(d) {
if (linePrevious.x1 != null) {
return linePrevious.x1
}
if (cfg.horizontalLayout) {
var datum = (cfg.dateDimension) ? new Date(d.date).getTime() : d.value;
ret = Math.floor(step * (datum - minValue))
}
return Math.floor(cfg.width / 2) - TIMELINE_LEFT_MARGIN
})
.attr("y1", function(d) {
var ret;
if (cfg.horizontalLayout) {
ret = Math.floor(cfg.height / 2)
} else {
var datum = (cfg.dateDimension) ? new Date(d.date).getTime() : d.value;
ret = Math.floor(step * (datum - minValue)) + margin
}
linePrevious.y1 = ret
return ret
})
.attr("y2", function(d) {
if (linePrevious.y1 != null) {
return linePrevious.y1
}
if (cfg.horizontalLayout) {
return Math.floor(cfg.height / 2)
}
var datum = (cfg.dateDimension) ? new Date(d.date).getTime() : d.value;
return Math.floor(step * (datum - minValue))
})
.style("stroke", function(d) {
if (d.color != undefined) {
return d.color
}
if (d.series != undefined) {
if (series.indexOf(d.series) < 0) {
series.push(d.series);
}
return cfg.seriesColor(series.indexOf(d.series));
}
return cfg.color
})
.style("stroke-width", cfg.lineWidth);
svg.selectAll("circle")
.data(events).enter()
.append("circle")
.attr("class", "timeline-event")
.attr("r", function(d) {
if (d.radius != undefined) {
return d.radius
}
return cfg.radius
})
.style("stroke", function(d) {
if (d.color != undefined) {
return d.color
}
if (d.series != undefined) {
if (series.indexOf(d.series) < 0) {
series.push(d.series);
}
console.log(d.series, series, series.indexOf(d.series));
return cfg.seriesColor(series.indexOf(d.series));
}
return cfg.color
})
.style("stroke-width", function(d) {
if (d.lineWidth != undefined) {
return d.lineWidth
}
return cfg.lineWidth
})
.style("fill", function(d) {
if (d.background != undefined) {
return d.background
}
return cfg.background
})
.attr("cy", function(d) {
if (cfg.horizontalLayout) {
return Math.floor(cfg.height / 2)
}
var datum = (cfg.dateDimension) ? new Date(d.date).getTime() : d.value;
return Math.floor(step * (datum - minValue) + margin)
})
.attr("cx", function(d) {
if (cfg.horizontalLayout) {
var datum = (cfg.dateDimension) ? new Date(d.date).getTime() : d.value;
var x = Math.floor(step * (datum - minValue) + margin);
return x;
}
return Math.floor(cfg.width / 2) - TIMELINE_LEFT_MARGIN
});
svg.selectAll("text")
.data(events).enter()
.append('text').attr("class", "event-description")
.html(function(d){
return d.name;
})
.attr("x", function(d) {
return Math.floor(cfg.width / 2) - TEXT_LEFT_MARGIN
})
.attr("y", function(d) {
var datum = (cfg.dateDimension) ? new Date(d.date).getTime() : d.value;
var y = Math.floor(step * (datum - minValue) + margin);
return TimeKnots.checkAndFixCollision(svg, ".event-description", y, this);
})
.attr("text-anchor", "center")
.style("opacity", function(d) {
if (d.opacity != undefined) {
return d.opacity
}
return 1
});
svg.selectAll("p")
.data(events).enter()
.append('text').attr("class", "event-description-date")
.html(function(d){
if (moment(d.date).isSame(TODAY, 'day') && d.name !== "TODAY") {
return "TODAY";
}
return moment(new Date(d.date)).format("MMM D");
})
.attr("x", function(d) {
return Math.floor(cfg.width / 2) + DATE_LEFT_MARGIN
})
.attr("y", function(d) {
var datum = (cfg.dateDimension) ? new Date(d.date).getTime() : d.value;
var y = Math.floor(step * (datum - minValue) + margin);
return TimeKnots.checkAndFixCollision(svg, ".event-description-date", y, this);
})
.attr("text-anchor", "right")
.style("opacity", function(d) {
if (d.opacity != undefined) {
return d.opacity
}
return 1
});
},
checkAndFixCollision: function(svg, id, y, that) {
svg.selectAll(id).each(function() {
if (this != that) {
if (this.getAttribute('y') != null && (this.getAttribute('y') >= (y-10) && this.getAttribute('y') <= (y+10))) {
y += 15 - 0.05*(this.getAttribute('y') - y);
return y;
}
}
});
return y;
}
}