Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Licensed to the Apache Software Foundation (ASF) under one or more
0003  * contributor license agreements.  See the NOTICE file distributed with
0004  * this work for additional information regarding copyright ownership.
0005  * The ASF licenses this file to You under the Apache License, Version 2.0
0006  * (the "License"); you may not use this file except in compliance with
0007  * the License.  You may obtain a copy of the License at
0008  *
0009  *    http://www.apache.org/licenses/LICENSE-2.0
0010  *
0011  * Unless required by applicable law or agreed to in writing, software
0012  * distributed under the License is distributed on an "AS IS" BASIS,
0013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014  * See the License for the specific language governing permissions and
0015  * limitations under the License.
0016  */
0017 
0018 
0019 // timeFormat: StreamingPage.scala will generate a global "timeFormat" dictionary to store the time
0020 // and its formatted string. Because we cannot specify a timezone in JavaScript, to make sure the
0021 // server and client use the same timezone, we use the "timeFormat" dictionary to format all time
0022 // values used in the graphs.
0023 
0024 // A global margin left for all timeline graphs. It will be set in "registerTimeline". This will be
0025 // used to align all timeline graphs.
0026 var maxMarginLeftForTimeline = 0;
0027 
0028 // The max X values for all histograms. It will be set in "registerHistogram".
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 // Show a tooltip "text" for "node"
0039 function showBootstrapTooltip(node, text) {
0040     $(node).tooltip({title: text, trigger: "manual", container: "body"});
0041     $(node).tooltip("show");
0042 }
0043 
0044 // Hide the tooltip for "node"
0045 function hideBootstrapTooltip(node) {
0046     $(node).tooltip("destroy");
0047 }
0048 
0049 // Return the function to scroll to the corresponding
0050 // row on clicking a point of batch in the timeline.
0051 function getOnClickTimelineFunction() {
0052     // If the user click one point in the graphs, jump to the batch row and highlight it. And
0053     // recovery the batch row after 3 seconds if necessary.
0054     // We need to remember the last clicked batch so that we can recovery it.
0055     var lastClickedBatch = null;
0056     var lastTimeout = null;
0057 
0058     return function(d) {
0059         var batchSelector = $("#batch-" + d.x);
0060         // If there is a corresponding batch row, scroll down to it and highlight it.
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); // Clean up after 3 seconds
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 // Register a timeline graph. All timeline graphs should be register before calling any
0089 // "drawTimeline" so that we can determine the max margin left for all timeline graphs.
0090 function registerTimeline(minY, maxY) {
0091     var numOfChars = yValueFormat(maxY).length;
0092     // A least width for "maxY" in the graph
0093     var pxForMaxY = numOfChars * 8 + 10;
0094     // Make sure we have enough space to show the ticks in the y axis of timeline
0095     maxMarginLeftForTimeline = pxForMaxY > maxMarginLeftForTimeline? pxForMaxY : maxMarginLeftForTimeline;
0096 }
0097 
0098 // Register a histogram graph. All histogram graphs should be register before calling any
0099 // "drawHistogram" so that we can determine the max X value for histograms.
0100 function registerHistogram(values, minY, maxY) {
0101     var data = d3.layout.histogram().range([minY, maxY]).bins(histogramBinCount)(values);
0102     // d.x is the y values while d.y is the x values
0103     var maxX = d3.max(data, function(d) { return d.y; });
0104     maxXForHistogram = maxX > maxXForHistogram ? maxX : maxXForHistogram;
0105 }
0106 
0107 // Draw a line between (x1, y1) and (x2, y2)
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  * @param id the `id` used in the html `div` tag
0123  * @param data the data for the timeline graph
0124  * @param minX the min value of X axis
0125  * @param maxX the max value of X axis
0126  * @param minY the min value of Y axis
0127  * @param maxY the max value of Y axis
0128  * @param unitY the unit of Y axis
0129  * @param batchInterval if "batchInterval" is specified, we will draw a line for "batchInterval" in the graph
0130  */
0131 function drawTimeline(id, data, minX, maxX, minY, maxY, unitY, batchInterval) {
0132     // Hide the right border of "<td>". We cannot use "css" directly, or "sorttable.js" will override them.
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             // Remove milliseconds
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     // Only show the first and last time in the graph
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     // If the user click one point in the graphs, jump to the batch row and highlight it. And
0193     // recovery the batch row after 3 seconds if necessary.
0194     // We need to remember the last clicked batch so that we can recovery it.
0195     var lastClickedBatch = null;
0196     var lastTimeout = null;
0197 
0198     function isFailedBatch(batchTime) {
0199         return $("#batch-" + batchTime).attr("isFailed") == "true";
0200     }
0201 
0202     // Add points to the line. However, we make it invisible at first. But when the user moves mouse
0203     // over a point, it will be displayed with its detail.
0204     svg.selectAll(".point")
0205         .data(data)
0206         .enter().append("circle")
0207             .attr("stroke", function(d) { return isFailedBatch(d.x) ? "red" : "white";}) // white and opacity = 0 make it invisible
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                 // show the point
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                 // hide the point
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  * @param id the `id` used in the html `div` tag
0238  * @param values the data for the histogram graph
0239  * @param minY the min value of Y axis
0240  * @param maxY the max value of Y axis
0241  * @param unitY the unit of Y axis
0242  * @param batchInterval if "batchInterval" is specified, we will draw a line for "batchInterval" in the graph
0243  */
0244 function drawHistogram(id, values, minY, maxY, unitY, batchInterval) {
0245     // Hide the left border of "<td>". We cannot use "css" directly, or "sorttable.js" will override them.
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         // Add the "stable" text to the graph below the batch interval line.
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         // Toggle the class of the arrow between open and closed
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         // Toggle the class of the arrow between open and closed
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 }