diff options
Diffstat (limited to 'debian/missing-sources/d3evolution.js')
-rw-r--r-- | debian/missing-sources/d3evolution.js | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/debian/missing-sources/d3evolution.js b/debian/missing-sources/d3evolution.js new file mode 100644 index 0000000..521caa5 --- /dev/null +++ b/debian/missing-sources/d3evolution.js @@ -0,0 +1,562 @@ +/*! + * D3Evolution 1.1.0 (https://github.com/moisseev/D3Evolution) + * Copyright (c) 2016-2017, Alexander Moisseev, BSD 2-Clause + */ + +function D3Evolution(id, options) { + "use strict"; + + var opts = $.extend(true, { + title: "", + width: 800, + height: 400, + margin: {top: 80, right: 60, bottom: 40, left: 60}, + yAxisLabel: "", + + type: "line", // area|line + yScale: "lin", // lin|log + + duration: 1250, + interpolate: "curveLinear", + + // convert: "percentage", + + legend: { + buttonRadius: 7, + space: 130, + + entries: [ + // , + // , + // {label: "Greylisted", color: "#436EEE"}, + // {label: "Clean", color: "#66cc00"}, + ] + } + }, options); + + const curves = { + "curveLinear": d3.curveLinear, + "curveStep": d3.curveStep, + "curveStepBefore": d3.curveStepBefore, + "curveStepAfter": d3.curveStepAfter, + "curveMonotoneX": d3.curveMonotoneX, + "curveBasis": d3.curveBasis, + "curveBasisOpen": d3.curveBasisOpen, + "curveBundle": d3.curveBundle, + "curveCardinal": d3.curveCardinal, + "curveCardinalOpen": d3.curveCardinalOpen, + "curveCatmullRom": d3.curveCatmullRom, + "curveCatmullRomOpen": d3.curveCatmullRomOpen, + "curveNatural": d3.curveNatural, + }; + + var data; + var srcData; + var legendX; + + var width = opts.width - opts.margin.left - opts.margin.right; + var height = opts.height - opts.margin.top - opts.margin.bottom; + + var xScale = d3.scaleTime().range([0, width]); + + var yScale; + var yAxisScale; + + const setYScale = function () { + if (opts.yScale === "log") { + yScale = d3.scaleLog().clamp(true).range([height, 0]); + yAxisScale = d3.scaleLog().range([height - 30, 0]); + } else { + yScale = d3.scaleLinear().range([height, 0]); + yAxisScale = yScale.copy(); + } + }; + + setYScale(); + + var xAxis = d3.axisBottom().scale(xScale); + var yAxis = d3.axisLeft().scale(yAxisScale).ticks(5); + + var xAxisGrid = d3.axisBottom().tickFormat("").scale(xScale) + .tickSize(-height, 0); + var yAxisGrid = d3.axisLeft().tickFormat("").scale(yAxisScale) + .tickSize(-width, 0); + + var yScaleBoolean = d3.scaleQuantize().range([height, 0]); + var areaNull = d3.area() + .x(function (d) { return xScale(d.x); }) + .y0(function (d) { return height; }) + .y1(function (d) { return yScaleBoolean(d.y == null); }) + .curve(d3.curveStep); + + var line = d3.line() + .defined(function(d) { return d.y != null; }) + .x(function (d) { return xScale(d.x); }) + .y(function (d) { return yScale(d.y); }) + .curve(curves[opts.interpolate]); + + var area = d3.area() + .defined(function(d) { return d.y != null; }) + .x(function (d) { return xScale(d.x); }) + .y0(function (d) { return yScale(d.y0); }) + .y1(function (d) { return yScale(d.y0 + d.y); }) + .curve(curves[opts.interpolate]); + + var d3v3LayoutStack = function (data) { + data.reduce(function (res, curr) { + curr.map(function (d, i) { + d.y0 = (res.length ? res[i].y + res[i].y0 : 0); + }); + return curr; + }, []); + }; + + var stack = function () { + var yExtents; + + if (opts.type === "area") { + d3v3LayoutStack(data); + yExtents = (opts.yScale === "log") + ? d3.extent(d3.merge(data), function (d) { return ((d.y0 + d.y) === 0) ? null : d.y0 + d.y; }) + : d3.extent(d3.merge(data), function (d) { return d.y0 + d.y; }); + } else { + yExtents = (opts.yScale === "log") + ? d3.extent(d3.merge(data), function (d) { return (d.y === 0) ? null : d.y; }) + : d3.extent(d3.merge(data), function (d) { return d.y; }); + } + + if (opts.yScale === "log") { + if (yExtents[0] === undefined) { + yExtents = [.0095, .0105]; + } else if (yExtents[0] === yExtents[1]) { + yExtents[0] *= .9; + } + yAxisScale.domain([yExtents[0], yExtents[1]]); + var y0 = yAxisScale.invert(height); + yScale.domain([y0, yExtents[1]]); + } else { + yScale.domain([(yExtents[0] > 0) ? 0 : yExtents[0], yExtents[1]]); + yAxisScale.domain(yScale.domain()); + } + + /** + * Hide overlapping tick labels on logarithmic Y-axis. + * @param {number} d - Tick value. + * @param {object} p - Previous unhidden label. + * @param {number} p.y - Y-position of the tick. + * @param {string} f - Tick label format. + * @returns {string} Tick label format or empty string. + */ + function logFormat(d, p, f) { + // Minimal interval of labeled ticks + const minInterval = 15; + // The nearest power of 10. + const pow10 = Math.pow(10, Math.round(Math.log(d) / Math.LN10)); + + if ( + // Never hide labels of power of 10 tick marks + !(Math.abs(pow10 - d) < 1e-6) && ( + // Hide if the next power of 10 tick mark is too close + (Math.abs(yScale(pow10) - yScale(d)) < minInterval) || + // Hide if previous label is too close + ((p.y - yScale(d)) < minInterval) + ) + ) { + return ""; + } + + p.y = yScale(d); + return f(d); + } + + if (opts.convert === "percentage") { + var prevUnhidLabel = {y: height}; // Previous unhidden tick label + const percentFormat = d3.format(".0%"); + y0Axis.tickFormat(percentFormat); + yAxis.tickFormat((opts.yScale === "log") + ? function (d) { return logFormat(d, prevUnhidLabel, percentFormat) ;} + : percentFormat); + } else { + y0Axis.tickFormat(null); + yAxis.tickFormat(null); + } + + /** + * In some cases when extent values are to close (e.g. 0.00011 and 0.00019 + * on log scale), there are no ticks generated. Possible D3 bug. + * We should set at least labels for extents if there are no any ticks. + */ + yAxis.tickValues(!yAxisScale.ticks().length ? [yExtents[0], yExtents[1]] : null); + + const t = d3.transition() + .duration(opts.duration); + + g.select(".y.grid").transition(t).call(yAxisGrid.scale(yAxisScale)); + g.select(".y.axis").transition(t).call(yAxis.scale(yAxisScale)); + g.select(".y-zero.axis").call(y0Axis); + }; + + var colorScale = d3.scaleOrdinal(d3.schemeCategory10); + + var pathColor = function (i) { + return (opts.legend.entries[i] !== undefined && + opts.legend.entries[i].color !== undefined) ? + opts.legend.entries[i].color : + colorScale(i); + }; + + var pathLabel = function (i) { + return (opts.legend.entries[i] !== undefined && + opts.legend.entries[i].label !== undefined) ? + opts.legend.entries[i].label : + "path_" + i; + }; + + var convert2Percentage = function (a) { + var total = a.reduce(function (res, curr) { + return curr.map(function (d, i) { return d.y + (res[i] ? res[i] : 0); }); + }, []); + + var dataPercentage = $.extend(true, [], a); + + dataPercentage.forEach(function (s) { + s.forEach(function (d, i) { if (total[i]) {d.y /= total[i];} }); + }); + + return dataPercentage; + } + + var yPreprocess = function () { + if (opts.convert === "percentage") { + yAxisLabel.transition().duration(opts.duration).style("opacity", 0); + data = convert2Percentage(srcData); + } else { + yAxisLabel.transition().duration(opts.duration).style("opacity", 1); + data = srcData; +// data = $.extend(true, [], srcData) + } + stack(); + }; + + /** + * Substitute real zeroes with values mapped to zero position on the graph. + */ + const substY0 = function () { + if (opts.yScale === "log") { + const y0 = yScale.invert(height); + data.forEach(function (s) { + s.forEach(function (d, i) { return d.y == 0 ? d.y : y0; }); + }); + } + }; + + var svg = d3.select("#" + id).append("svg") + .classed('d3evolution', true) + .attr("width", opts.width) + .attr("height", opts.height); + + var legend = svg.append("g").attr("class", "legend"); + + var g = svg.append("g") + .attr("width", width) + .attr("height", height) + .attr("transform", "translate(" + opts.margin.left + ", " + opts.margin.top + ")"); + + g.append("g") + .attr("class", "x grid") + .attr("transform", "translate(0," + height + ")") + .call(xAxisGrid); + + g.append("g") + .attr("class", "y grid") + .attr("transform", "translate(0,0)") + .call(yAxisGrid); + + g.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + height + ")") + .call(xAxis); + + g.append("g") + .attr("class", "y axis") + .attr("transform", "translate(0,0)") + .call(yAxis); + + // Zero tick mark for log scale + var y0Scale = d3.scaleOrdinal().domain([0]).range([height]); + var y0Axis = d3.axisLeft().scale(y0Scale); + g.append("g") + .attr("class", "y-zero axis") + .call(y0Axis); + + var yAxisLabel = g.append("text") + .attr("class", "y label") + .attr("x", 20 - opts.margin.left) + .attr("y", -20) + .style("opacity", (opts.convert === "percentage") ? 0 : 1) + .text(opts.yAxisLabel); + + var title = svg.append("svg:text") + .attr("x", (opts.width / 2)) + .attr("y", (opts.margin.top / 3)) + .attr("text-anchor", "middle"); + + title.append("tspan") + .attr("class", "chart-title") + .text(opts.title + " "); + + title.timeRange = title.append("tspan"); + + this.data = function (a) { + srcData = $.extend(true, [], a); + + var opacity = []; + + legendX = opts.width - opts.margin.right - opts.legend.space * srcData.length; + + // Convert time in seconds to milliseconds + srcData.forEach(function (s) { + s.forEach(function (d) { d.x *= 1000; }); + }); + + var xExtents = d3.extent(d3.merge(srcData), function (d) { return d.x; }); + xScale.domain([xExtents[0], xExtents[1]]); + + const iso = function (a) { + return d3.timeFormat("%Y-%m-%d %H:%M:%S")(new Date(a)); + }; + title.timeRange + .text("[ " + iso(xExtents[0]) + " / " + iso(xExtents[1]) + " ]"); + + var pathNull = g.selectAll("path.path-null").data(srcData); + + pathNull.enter() + .append("path") + .attr("class", "path-null"); + + g.selectAll("path.path-null") + .transition().duration(opts.duration / 2) + .style("opacity", 0) + .on("end", function () { + g.selectAll("path.path-null") + .attr("d", areaNull) + .transition().duration(opts.duration / 2) + .style("opacity", 1); + }); + + pathNull.exit() + .remove(); + + yPreprocess(); + substY0(); + + var path = g.selectAll("path.path").data(data); + + path.enter() + .append("path") + .merge(path) + .attr("class", "path") + .attr("id", function (d, i) { return "path_" + i; }) + .on("click", function (d, i) { onClick(i); }) + .on("mouseover", function (d, i) { highlight(i); }) + .on("mouseout", function (d, i) { highlight(i, false); }); + + path.exit() + .remove(); + + path = g.selectAll("path.path"); + + if (opts.type === "area") { + path + .style("fill", function (d, i) { return pathColor(i); }) + .style("stroke", "none") + .style("fill-opacity", function (d, i) { return opacity[i]; }); + } else { + path + .style("fill", "none") + .style("stroke", function (d, i) { return pathColor(i); }) + .style("opacity", function (d, i) { return opacity[i]; }); + } + + path + .transition().duration(opts.duration) + .attr("d", (opts.type === "area") ? area : line); + + const t = d3.transition() + .duration(opts.duration); + + g.select(".x.grid").transition(t).call(xAxisGrid.scale(xScale)); + g.select(".x.axis").transition(t).call(xAxis.scale(xScale)); + + var onClick = function (i) { + opacity[i] = (opacity[i] != 0) ? 0 : 1; + + d3.select("#circle_" + i) + .transition().duration(opts.duration) + .style("fill-opacity", opacity[i] + 0.2); + + d3.select("#path_" + i) + .transition().duration(opts.duration) + .style("opacity", opacity[i]); + }; + + /** + * Highlight selected path and legend circle. + * @param {number} s - Selected path index. + * @param {boolean} [h] - If false, restore previous state. + */ + const highlight = function (s, h) { + d3.select("#circle_" + s) + .attr("r", opts.legend.buttonRadius * (h === false ? 1 : 1.3)); + + const op = function (i) { + if (h === false) + return opacity[i]; + return (i === s) ? 1 : (opacity[i] == 0) ? 0 : 0.4; + }; + + g.selectAll("path.path") + .style("opacity", function (d, i) { return op(i); }) + .style("fill-opacity", function (d, i) { return op(i); }); + }; + + var buttons = legend.selectAll("circle").data(data); + + buttons.enter().append("circle") + .attr("id", function (d, i) { return "circle_" + i; }) + .attr("cy", opts.margin.top * 2 / 3) + .attr("r", opts.legend.buttonRadius) + .style("fill", function (d, i) { return pathColor(i); }) + .style("stroke", function (d, i) { return pathColor(i); }) + .style("fill-opacity", function (d, i) { return opacity[i] + 0.2; }) + .on("click", function (d, i) { onClick(i); }) + .on("mouseover", function (d, i) { highlight(i); }) + .on("mouseout", function (d, i) { highlight(i, false); }); + + buttons.exit() + .remove(); + + legend.selectAll("circle") + .transition().duration(opts.duration) + .attr("cx", function (d, i) { return legendX + opts.legend.space * i; }); + + var labels = legend.selectAll("text").data(data); + + labels.enter() + .append("text") + .attr("y", opts.margin.top * 2 / 3) + .attr("dy", "0.3em") + .text(function (d, i) { return pathLabel(i); }) + .on("click", function (d, i) { onClick(i); }) + .on("mouseover", function (d, i) { highlight(i); }) + .on("mouseout", function (d, i) { highlight(i, false); }); + + labels.exit() + .remove(); + + legend.selectAll("text") + .transition().duration(opts.duration) + .attr("x", function (d, i) { + return legendX + opts.legend.space * i + 2 * opts.legend.buttonRadius; + }); + + return this; + }; + + this.legend = function (a) { + $.extend(true, opts.legend, a); + + legend.selectAll("circle") + .transition().duration(opts.duration) + .attr("cx", function (d, i) { return legendX + opts.legend.space * i; }) + .attr("r", opts.legend.buttonRadius) + .style("fill", function (d, i) { return pathColor(i); }) + .style("stroke", function (d, i) { return pathColor(i); }); + + legend.selectAll("text") + .text(function (d, i) { return pathLabel(i); }) + .transition().duration(opts.duration) + .attr("x", function (d, i) { + return legendX + opts.legend.space * i + 2 * opts.legend.buttonRadius; + }); + + g.selectAll("path.path") + .transition().duration(opts.duration) + .style("fill", (opts.type === "area") ? function (d, i) { return pathColor(i); } : "none") + .style("stroke", (opts.type !== "area") ? function (d, i) { return pathColor(i); } : "none"); + + return this; + }; + + this.convert = function (a) { + opts.convert = a; + + yPreprocess(); + + g.selectAll("path.path") + .data(data) + .transition().duration(opts.duration) + .attr("d", (opts.type === "area") ? area : line); + + return this; + }; + + this.interpolate = function (a) { + opts.interpolate = a; + + area.curve(curves[opts.interpolate]); + line.curve(curves[opts.interpolate]); + + g.selectAll("path.path") + .attr("d", (opts.type === "area") ? area : line); + + return this; + }; + + this.type = function (a) { + opts.type = a; + + stack(); + + g.selectAll("path.path") + .style("stroke", (opts.type !== "area") ? function (d, i) { return pathColor(i); } : "none") + .style("fill", (opts.type === "area") ? function (d, i) { return pathColor(i); } : "none") + .transition().duration(opts.duration) + .attr("d", (opts.type === "area") ? area : line); + + return this; + }; + + this.yAxisLabel = function (a) { + opts.yAxisLabel = a; + yAxisLabel + .transition().duration(opts.duration / 2) + .style("opacity", 0) + .on("end", function () { + yAxisLabel + .text(opts.yAxisLabel) + .transition().duration(opts.duration / 2) + .style("opacity", 1); + }); + + return this; + }; + + this.yScale = function (a) { + opts.yScale = a; + + setYScale(); + substY0(); + stack(); + + g.selectAll("path.path") + .transition().duration(opts.duration) + .attr("d", (opts.type === "area") ? area : line); + + return this; + }; + + this.destroy = function () { + d3.select("svg").remove(); + }; +} |