0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026 var maxMarginLeftForTimeline = 0;
0027
0028
0029 var maxXForHistogram = 0;
0030
0031 var histogramBinCount = 10;
0032 var yValueFormat = d3.format(",.2f");
0033
0034 var unitLabelYOffset = -10;
0035
0036 var onClickTimeline = function() {};
0037
0038
0039 function showBootstrapTooltip(node, text) {
0040 $(node).tooltip({title: text, trigger: "manual", container: "body"});
0041 $(node).tooltip("show");
0042 }
0043
0044
0045 function hideBootstrapTooltip(node) {
0046 $(node).tooltip("destroy");
0047 }
0048
0049
0050
0051 function getOnClickTimelineFunction() {
0052
0053
0054
0055 var lastClickedBatch = null;
0056 var lastTimeout = null;
0057
0058 return function(d) {
0059 var batchSelector = $("#batch-" + d.x);
0060
0061 if (batchSelector.length > 0) {
0062 if (lastTimeout != null) {
0063 window.clearTimeout(lastTimeout);
0064 }
0065 if (lastClickedBatch != null) {
0066 clearBatchRow(lastClickedBatch);
0067 lastClickedBatch = null;
0068 }
0069 lastClickedBatch = d.x;
0070 highlightBatchRow(lastClickedBatch);
0071 lastTimeout = window.setTimeout(function () {
0072 lastTimeout = null;
0073 if (lastClickedBatch != null) {
0074 clearBatchRow(lastClickedBatch);
0075 lastClickedBatch = null;
0076 }
0077 }, 3000);
0078
0079 var topOffset = batchSelector.offset().top - 15;
0080 if (topOffset < 0) {
0081 topOffset = 0;
0082 }
0083 $('html,body').animate({scrollTop: topOffset}, 200);
0084 }
0085 }
0086 }
0087
0088
0089
0090 function registerTimeline(minY, maxY) {
0091 var numOfChars = yValueFormat(maxY).length;
0092
0093 var pxForMaxY = numOfChars * 8 + 10;
0094
0095 maxMarginLeftForTimeline = pxForMaxY > maxMarginLeftForTimeline? pxForMaxY : maxMarginLeftForTimeline;
0096 }
0097
0098
0099
0100 function registerHistogram(values, minY, maxY) {
0101 var data = d3.layout.histogram().range([minY, maxY]).bins(histogramBinCount)(values);
0102
0103 var maxX = d3.max(data, function(d) { return d.y; });
0104 maxXForHistogram = maxX > maxXForHistogram ? maxX : maxXForHistogram;
0105 }
0106
0107
0108 function drawLine(svg, xFunc, yFunc, x1, y1, x2, y2) {
0109 var line = d3.svg.line()
0110 .x(function(d) { return xFunc(d.x); })
0111 .y(function(d) { return yFunc(d.y); });
0112 var data = [{x: x1, y: y1}, {x: x2, y: y2}];
0113 svg.append("path")
0114 .datum(data)
0115 .style("stroke-dasharray", ("6, 6"))
0116 .style("stroke", "lightblue")
0117 .attr("class", "line")
0118 .attr("d", line);
0119 }
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131 function drawTimeline(id, data, minX, maxX, minY, maxY, unitY, batchInterval) {
0132
0133 d3.select(d3.select(id).node().parentNode)
0134 .style("padding", "8px 0 8px 8px")
0135 .style("border-right", "0px solid white");
0136
0137 var margin = {top: 20, right: 27, bottom: 30, left: maxMarginLeftForTimeline};
0138 var width = 500 - margin.left - margin.right;
0139 var height = 150 - margin.top - margin.bottom;
0140
0141 var x = d3.scale.linear().domain([minX, maxX]).range([0, width]);
0142 var y = d3.scale.linear().domain([minY, maxY]).range([height, 0]);
0143
0144 var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(function(d) {
0145 var formattedDate = timeFormat[d];
0146 var dotIndex = formattedDate.indexOf('.');
0147 if (dotIndex >= 0) {
0148
0149 return formattedDate.substring(0, dotIndex);
0150 } else {
0151 return formattedDate;
0152 }
0153 });
0154 var formatYValue = d3.format(",.2f");
0155 var yAxis = d3.svg.axis().scale(y).orient("left").ticks(5).tickFormat(formatYValue);
0156
0157 var line = d3.svg.line()
0158 .x(function(d) { return x(d.x); })
0159 .y(function(d) { return y(d.y); });
0160
0161 var svg = d3.select(id).append("svg")
0162 .attr("width", width + margin.left + margin.right)
0163 .attr("height", height + margin.top + margin.bottom)
0164 .append("g")
0165 .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
0166
0167
0168 xAxis.tickValues(x.domain());
0169
0170 svg.append("g")
0171 .attr("class", "x axis")
0172 .attr("transform", "translate(0," + height + ")")
0173 .call(xAxis);
0174
0175 svg.append("g")
0176 .attr("class", "y axis")
0177 .call(yAxis)
0178 .append("text")
0179 .attr("transform", "translate(0," + unitLabelYOffset + ")")
0180 .text(unitY);
0181
0182
0183 if (batchInterval && batchInterval <= maxY) {
0184 drawLine(svg, x, y, minX, batchInterval, maxX, batchInterval);
0185 }
0186
0187 svg.append("path")
0188 .datum(data)
0189 .attr("class", "line")
0190 .attr("d", line);
0191
0192
0193
0194
0195 var lastClickedBatch = null;
0196 var lastTimeout = null;
0197
0198 function isFailedBatch(batchTime) {
0199 return $("#batch-" + batchTime).attr("isFailed") == "true";
0200 }
0201
0202
0203
0204 svg.selectAll(".point")
0205 .data(data)
0206 .enter().append("circle")
0207 .attr("stroke", function(d) { return isFailedBatch(d.x) ? "red" : "white";})
0208 .attr("fill", function(d) { return isFailedBatch(d.x) ? "red" : "white";})
0209 .attr("opacity", function(d) { return isFailedBatch(d.x) ? "1" : "0";})
0210 .style("cursor", "pointer")
0211 .attr("cx", function(d) { return x(d.x); })
0212 .attr("cy", function(d) { return y(d.y); })
0213 .attr("r", function(d) { return isFailedBatch(d.x) ? "2" : "3";})
0214 .on('mouseover', function(d) {
0215 var tip = formatYValue(d.y) + " " + unitY + " at " + timeTipStrings[d.x];
0216 showBootstrapTooltip(d3.select(this).node(), tip);
0217
0218 d3.select(this)
0219 .attr("stroke", function(d) { return isFailedBatch(d.x) ? "red" : "steelblue";})
0220 .attr("fill", function(d) { return isFailedBatch(d.x) ? "red" : "steelblue";})
0221 .attr("opacity", "1")
0222 .attr("r", "3");
0223 })
0224 .on('mouseout', function() {
0225 hideBootstrapTooltip(d3.select(this).node());
0226
0227 d3.select(this)
0228 .attr("stroke", function(d) { return isFailedBatch(d.x) ? "red" : "white";})
0229 .attr("fill", function(d) { return isFailedBatch(d.x) ? "red" : "white";})
0230 .attr("opacity", function(d) { return isFailedBatch(d.x) ? "1" : "0";})
0231 .attr("r", function(d) { return isFailedBatch(d.x) ? "2" : "3";});
0232 })
0233 .on("click", onClickTimeline);
0234 }
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244 function drawHistogram(id, values, minY, maxY, unitY, batchInterval) {
0245
0246 d3.select(d3.select(id).node().parentNode)
0247 .style("padding", "8px 8px 8px 0")
0248 .style("border-left", "0px solid white");
0249
0250 var margin = {top: 20, right: 30, bottom: 30, left: 10};
0251 var width = 350 - margin.left - margin.right;
0252 var height = 150 - margin.top - margin.bottom;
0253
0254 var x = d3.scale.linear().domain([0, maxXForHistogram]).range([0, width - 50]);
0255 var y = d3.scale.linear().domain([minY, maxY]).range([height, 0]);
0256
0257 var xAxis = d3.svg.axis().scale(x).orient("top").ticks(5);
0258 var yAxis = d3.svg.axis().scale(y).orient("left").ticks(0).tickFormat(function(d) { return ""; });
0259
0260 var data = d3.layout.histogram().range([minY, maxY]).bins(histogramBinCount)(values);
0261
0262 var svg = d3.select(id).append("svg")
0263 .attr("width", width + margin.left + margin.right)
0264 .attr("height", height + margin.top + margin.bottom)
0265 .append("g")
0266 .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
0267
0268 if (batchInterval && batchInterval <= maxY) {
0269 drawLine(svg, x, y, 0, batchInterval, maxXForHistogram, batchInterval);
0270 }
0271
0272 svg.append("g")
0273 .attr("class", "x axis")
0274 .call(xAxis)
0275 .append("text")
0276 .attr("transform", "translate(" + (margin.left + width - 45) + ", " + unitLabelYOffset + ")")
0277 .text("#batches");
0278
0279 svg.append("g")
0280 .attr("class", "y axis")
0281 .call(yAxis);
0282
0283 svg.selectAll(".bar")
0284 .data(data)
0285 .enter()
0286 .append("g")
0287 .attr("transform", function(d) { return "translate(0," + (y(d.x) - height + y(d.dx)) + ")";})
0288 .attr("class", "bar").append("rect")
0289 .attr("width", function(d) { return x(d.y); })
0290 .attr("height", function(d) { return height - y(d.dx); })
0291 .on('mouseover', function(d) {
0292 var percent = yValueFormat(d.y * 100.0 / values.length) + "%";
0293 var tip = d.y + " batches (" + percent + ") between " + yValueFormat(d.x) + " and " + yValueFormat(d.x + d.dx) + " " + unitY;
0294 showBootstrapTooltip(d3.select(this).node(), tip);
0295 })
0296 .on('mouseout', function() {
0297 hideBootstrapTooltip(d3.select(this).node());
0298 });
0299
0300 if (batchInterval && batchInterval <= maxY) {
0301
0302 var stableXOffset = x(maxXForHistogram) - 20;
0303 var stableYOffset = y(batchInterval) + 15;
0304 svg.append("text")
0305 .style("fill", "lightblue")
0306 .attr("class", "stable-text")
0307 .attr("text-anchor", "middle")
0308 .attr("transform", "translate(" + stableXOffset + "," + stableYOffset + ")")
0309 .text("stable")
0310 .on('mouseover', function(d) {
0311 var tip = "Processing Time <= Batch Interval (" + yValueFormat(batchInterval) +" " + unitY +")";
0312 showBootstrapTooltip(d3.select(this).node(), tip);
0313 })
0314 .on('mouseout', function() {
0315 hideBootstrapTooltip(d3.select(this).node());
0316 });
0317 }
0318 }
0319
0320 $(function() {
0321 var status = window.localStorage && window.localStorage.getItem("show-streams-detail") == "true";
0322
0323 $("span.expand-input-rate").click(function() {
0324 status = !status;
0325 $("#inputs-table").toggle('collapsed');
0326
0327 $(this).find('.expand-input-rate-arrow').toggleClass('arrow-open').toggleClass('arrow-closed');
0328 if (window.localStorage) {
0329 window.localStorage.setItem("show-streams-detail", "" + status);
0330 }
0331 });
0332
0333 if (status) {
0334 $("#inputs-table").toggle('collapsed');
0335
0336 $(this).find('.expand-input-rate-arrow').toggleClass('arrow-open').toggleClass('arrow-closed');
0337 }
0338 });
0339
0340 function highlightBatchRow(batch) {
0341 $("#batch-" + batch).parent().addClass("batch-table-cell-highlight");
0342 }
0343
0344 function clearBatchRow(batch) {
0345 $("#batch-" + batch).parent().removeClass("batch-table-cell-highlight");
0346 }