|
@@ -0,0 +1,250 @@
|
|
|
+import Ember from 'ember';
|
|
|
+import Converter from 'yarn-ui/utils/converter';
|
|
|
+
|
|
|
+export default Ember.Component.extend({
|
|
|
+ canvas: {
|
|
|
+ svg: undefined,
|
|
|
+ h: 0,
|
|
|
+ w: 0,
|
|
|
+ tooltip: undefined
|
|
|
+ },
|
|
|
+
|
|
|
+ clusterMetrics: undefined,
|
|
|
+ modelArr: [],
|
|
|
+ colors: d3.scale.category10().range(),
|
|
|
+ _selected: undefined,
|
|
|
+
|
|
|
+ selected: function() {
|
|
|
+ return this._selected;
|
|
|
+ }.property(),
|
|
|
+
|
|
|
+ tableComponentName: function() {
|
|
|
+ return "app-attempt-table";
|
|
|
+ }.property(),
|
|
|
+
|
|
|
+ setSelected: function(d) {
|
|
|
+ if (this._selected == d) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // restore color
|
|
|
+ if (this._selected) {
|
|
|
+ var dom = d3.select("#timeline-bar-" + this._selected.get("id"));
|
|
|
+ dom.attr("fill", this.colors[0]);
|
|
|
+ }
|
|
|
+
|
|
|
+ this._selected = d;
|
|
|
+ this.set("selected", d);
|
|
|
+ dom = d3.select("#timeline-bar-" + d.get("id"));
|
|
|
+ dom.attr("fill", this.colors[1]);
|
|
|
+ },
|
|
|
+
|
|
|
+ getPerItemHeight: function() {
|
|
|
+ var arrSize = this.modelArr.length;
|
|
|
+
|
|
|
+ if (arrSize < 20) {
|
|
|
+ return 30;
|
|
|
+ } else if (arrSize < 100) {
|
|
|
+ return 10;
|
|
|
+ } else {
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ getPerItemGap: function() {
|
|
|
+ var arrSize = this.modelArr.length;
|
|
|
+
|
|
|
+ if (arrSize < 20) {
|
|
|
+ return 5;
|
|
|
+ } else if (arrSize < 100) {
|
|
|
+ return 1;
|
|
|
+ } else {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ getCanvasHeight: function() {
|
|
|
+ return (this.getPerItemHeight() + this.getPerItemGap()) * this.modelArr.length + 200;
|
|
|
+ },
|
|
|
+
|
|
|
+ draw: function(start, end) {
|
|
|
+ // get w/h of the svg
|
|
|
+ var bbox = d3.select("#" + this.get("parent-id"))
|
|
|
+ .node()
|
|
|
+ .getBoundingClientRect();
|
|
|
+ this.canvas.w = bbox.width;
|
|
|
+ this.canvas.h = this.getCanvasHeight();
|
|
|
+
|
|
|
+ this.canvas.svg = d3.select("#" + this.get("parent-id"))
|
|
|
+ .append("svg")
|
|
|
+ .attr("width", this.canvas.w)
|
|
|
+ .attr("height", this.canvas.h)
|
|
|
+ .attr("id", this.get("my-id"));
|
|
|
+ this.renderTimeline(start, end);
|
|
|
+ },
|
|
|
+
|
|
|
+ renderTimeline: function(start, end) {
|
|
|
+ var border = 30;
|
|
|
+ var singleBarHeight = this.getPerItemHeight();
|
|
|
+ var gap = this.getPerItemGap();
|
|
|
+ var textWidth = 50;
|
|
|
+ /*
|
|
|
+ start-time end-time
|
|
|
+ |--------------------------------------|
|
|
|
+ ==============
|
|
|
+ ==============
|
|
|
+ ==============
|
|
|
+ ===============
|
|
|
+ */
|
|
|
+ var xScaler = d3.scale.linear()
|
|
|
+ .domain([start, end])
|
|
|
+ .range([0, this.canvas.w - 2 * border - textWidth]);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Render frame of timeline view
|
|
|
+ */
|
|
|
+ this.canvas.svg.append("line")
|
|
|
+ .attr("x1", border + textWidth)
|
|
|
+ .attr("y1", border - 5)
|
|
|
+ .attr("x2", this.canvas.w - border)
|
|
|
+ .attr("y2", border - 5)
|
|
|
+ .attr("class", "chart");
|
|
|
+
|
|
|
+ this.canvas.svg.append("line")
|
|
|
+ .attr("x1", border + textWidth)
|
|
|
+ .attr("y1", border - 10)
|
|
|
+ .attr("x2", border + textWidth)
|
|
|
+ .attr("y2", border - 5)
|
|
|
+ .attr("class", "chart");
|
|
|
+
|
|
|
+ this.canvas.svg.append("line")
|
|
|
+ .attr("x1", this.canvas.w - border)
|
|
|
+ .attr("y1", border - 10)
|
|
|
+ .attr("x2", this.canvas.w - border)
|
|
|
+ .attr("y2", border - 5)
|
|
|
+ .attr("class", "chart");
|
|
|
+
|
|
|
+ this.canvas.svg.append("text")
|
|
|
+ .text(Converter.timeStampToDate(start))
|
|
|
+ .attr("y", border - 15)
|
|
|
+ .attr("x", border + textWidth)
|
|
|
+ .attr("class", "bar-chart-text")
|
|
|
+ .attr("text-anchor", "left");
|
|
|
+
|
|
|
+ this.canvas.svg.append("text")
|
|
|
+ .text(Converter.timeStampToDate(end))
|
|
|
+ .attr("y", border - 15)
|
|
|
+ .attr("x", this.canvas.w - border)
|
|
|
+ .attr("class", "bar-chart-text")
|
|
|
+ .attr("text-anchor", "end");
|
|
|
+
|
|
|
+ // show bar
|
|
|
+ var bar = this.canvas.svg.selectAll("bars")
|
|
|
+ .data(this.modelArr)
|
|
|
+ .enter()
|
|
|
+ .append("rect")
|
|
|
+ .attr("y", function(d, i) {
|
|
|
+ return border + (gap + singleBarHeight) * i;
|
|
|
+ })
|
|
|
+ .attr("x", function(d, i) {
|
|
|
+ return border + textWidth + xScaler(d.get("startTs"));
|
|
|
+ })
|
|
|
+ .attr("height", singleBarHeight)
|
|
|
+ .attr("fill", function(d, i) {
|
|
|
+ return this.colors[0];
|
|
|
+ }.bind(this))
|
|
|
+ .attr("width", function(d, i) {
|
|
|
+ var finishedTs = xScaler(d.get("finishedTs"));
|
|
|
+ finishedTs = finishedTs > 0 ? finishedTs : xScaler(end);
|
|
|
+ return finishedTs - xScaler(d.get("startTs"));
|
|
|
+ })
|
|
|
+ .attr("id", function(d, i) {
|
|
|
+ return "timeline-bar-" + d.get("id");
|
|
|
+ });
|
|
|
+ bar.on("click", function(d) {
|
|
|
+ this.setSelected(d);
|
|
|
+ }.bind(this));
|
|
|
+
|
|
|
+ this.bindTooltip(bar);
|
|
|
+
|
|
|
+ if (this.modelArr.length <= 20) {
|
|
|
+ // show bar texts
|
|
|
+ for (var i = 0; i < this.modelArr.length; i++) {
|
|
|
+ this.canvas.svg.append("text")
|
|
|
+ .text(this.modelArr[i].get(this.get("label")))
|
|
|
+ .attr("y", border + (gap + singleBarHeight) * i + singleBarHeight / 2)
|
|
|
+ .attr("x", border)
|
|
|
+ .attr("class", "bar-chart-text");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ bindTooltip: function(d) {
|
|
|
+ d.on("mouseover", function(d) {
|
|
|
+ this.tooltip
|
|
|
+ .style("left", (d3.event.pageX) + "px")
|
|
|
+ .style("top", (d3.event.pageY - 28) + "px");
|
|
|
+ }.bind(this))
|
|
|
+ .on("mousemove", function(d) {
|
|
|
+ this.tooltip.style("opacity", .9);
|
|
|
+ this.tooltip.html(d.get("tooltipLabel"))
|
|
|
+ .style("left", (d3.event.pageX) + "px")
|
|
|
+ .style("top", (d3.event.pageY - 28) + "px");
|
|
|
+ }.bind(this))
|
|
|
+ .on("mouseout", function(d) {
|
|
|
+ this.tooltip.style("opacity", 0);
|
|
|
+ }.bind(this));
|
|
|
+ },
|
|
|
+
|
|
|
+ initTooltip: function() {
|
|
|
+ this.tooltip = d3.select("body")
|
|
|
+ .append("div")
|
|
|
+ .attr("class", "tooltip")
|
|
|
+ .attr("id", "chart-tooltip")
|
|
|
+ .style("opacity", 0);
|
|
|
+ },
|
|
|
+
|
|
|
+ didInsertElement: function() {
|
|
|
+ // init tooltip
|
|
|
+ this.initTooltip();
|
|
|
+
|
|
|
+ // init model
|
|
|
+ if (this.get("rmModel")) {
|
|
|
+ this.get("rmModel").forEach(function(o) {
|
|
|
+ this.modelArr.push(o);
|
|
|
+ }.bind(this));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.get("tsModel")) {
|
|
|
+ this.get("tsModel").forEach(function(o) {
|
|
|
+ this.modelArr.push(o);
|
|
|
+ }.bind(this));
|
|
|
+ }
|
|
|
+
|
|
|
+ this.modelArr.sort(function(a, b) {
|
|
|
+ var tsA = a.get("startTs");
|
|
|
+ var tsB = b.get("startTs");
|
|
|
+
|
|
|
+ return tsA - tsB;
|
|
|
+ });
|
|
|
+ if (this.modelArr.length > 0) {
|
|
|
+ var begin = this.modelArr[0].get("startTs");
|
|
|
+ }
|
|
|
+ var end = 0;
|
|
|
+ for (var i = 0; i < this.modelArr.length; i++) {
|
|
|
+ var ts = this.modelArr[i].get("finishedTs");
|
|
|
+ if (ts > end) {
|
|
|
+ end = ts;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (end < begin) {
|
|
|
+ end = Date.now();
|
|
|
+ }
|
|
|
+
|
|
|
+ this.draw(begin, end);
|
|
|
+
|
|
|
+ if (this.modelArr.length > 0) {
|
|
|
+ this.setSelected(this.modelArr[0]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+});
|