racket/collects/meta/drdr/static/chart.js
2011-09-28 15:52:59 -04:00

257 lines
7.4 KiB
JavaScript

var path = ""
var data = null;
var sub_times = [];
var overall_times = [];
var chart_data = [];
var show_hide = {}
var options = { selection: { mode: "xy" },
legend: { backgroundOpacity: 0,
position: "sw",
show: true,
noColumns : 1,
labelFormatter :
function(label, series) {
if (show_hide[label] === undefined)
show_hide[label] = true;
var css = '';
if (!show_hide[label]) {
css = 'style="font-style: italic"';
}
var v = '<div '+css+' onclick="legend_click(\''+label+'\')">' + label + '</div>';
return v;}},
xaxes: [{min: null, max: null, label: 'push'}],
yaxes: [{min: null, max: null, label: "time"},
{position: "right"}],
grid: { clickable: true, hoverable : true }
};
function addCommas(nStr) {
var rgx = /(\d+)(\d{3})/;
while (rgx.test(nStr)) {
nStr = nStr.replace(rgx, '$1' + ',' + '$2');
}
return nStr;
}
// Number -> String
function format_ms(ms) {
return addCommas(String(ms)) + " ms"
}
// Number -> String
function format_time(ms) {
if (ms >= 300000)
return Number(ms/60000).toFixed(2) + " m " + "("+ format_ms(ms)+")";
if (ms >= 10000)
return Number(ms/1000).toFixed(2) + " s" + "("+ format_ms(ms)+")";
return format_ms(ms);
}
function legend_click(l) {
show_hide[l] = !show_hide[l];
show();
serialize_opts(options);
}
var placeholder = $("#_chart");
var previousPoint = null;
function showTooltip(x, y, contents) {
$('<div id="tooltip">' + contents + '</div>').css( {
position: 'absolute',
display: 'none',
top: y + 5,
left: x + 5,
border: '1px solid #fdd',
padding: '2px',
'background-color': '#fee',
opacity: 0.80
}).appendTo("body").fadeIn(200);
}
function makeTooltip(item,path) {
var x = item.datapoint[0];
var y = item.datapoint[1].toFixed(2);
showTooltip(item.pageX, item.pageY,
item.series.label + ' at <a href="http://drdr.racket-lang.org/'
+ x + path + '">push ' + x + "</a>: "
+ format_time(y));
}
placeholder.bind("plotselected", handle_selection);
// is the tooltip shown b/c of a click?
var tooltip_clicked = false;
function remove_tooltip() {
tooltip_clicked = false;
$("#tooltip").remove();
}
function hover(event,pos,item) {
if (tooltip_clicked) return;
if (item) {
// don't re-show the same tool-tip that's already shown
if (previousPoint != item.dataIndex) {
previousPoint = item.dataIndex;
remove_tooltip();
makeTooltip(item,path);
}
}
else {
remove_tooltip();
previousPoint = null;
}
}
function click(e,pos,item) {
if (tooltip_clicked) {
remove_tooltip();
return;
}
if (!item) return;
tooltip_clicked = true;
// if we've already got the tooltip, just keep it around
if (previousPoint != item.dataIndex) {
$("#tooltip").remove();
makeTooltip(item,path);
}
}
// sort chart data based on the order of a[0], b[0]
function sorter(a,b) {
if (a[0] < b[0]) return -1;
if (a[0] > b[0]) return 1;
return 0;
}
function load_data(d) {
chart_data = [];
overall_times = [];
sub_times = [];
pdata = []
data = d;
reset_chart();
pdata = data && JSON.parse(data);
var max_overall = 0;
var max_sub = 0;
// build the timing data arrays
for (var i = 0; i < pdata.length; i++) {
overall_times.push([pdata[i][0], pdata[i][1]]);
max_overall = Math.max(max_overall, pdata[i][1]);
if (pdata[i][2].length != 0) {
for (var j = 0; j < pdata[i][2].length; j++) {
sub_times[j] = sub_times[j] || [];
sub_times[j].push([pdata[i][0],pdata[i][2][j][0]]);
max_sub = Math.max(max_sub, pdata[i][2][j][0]);
}
}
};
// is there a significant difference between the overall times
// and the internal timings?
var ya = 1;
if ((max_overall > (5 * max_sub)) || ((max_overall * 5) < max_sub))
ya = 2;
// put the data into the chart format
chart_data.push({data: overall_times.sort(sorter), label: "Overall Time", color: "#804040"});
for(var i = 0; i < sub_times.length; i++) {
var n = (sub_times[i].length/overall_times.length);
chart_data.push({data: sub_times[i].sort(sorter), label: "Timer "+ (i+1),
lines: { show: (.9<n) },
points: { show: !(.9<n) },
yaxis: ya});
}
cur_options.legend.noColumns = Math.max(1,Math.round(chart_data.length / 10));
}
function get_data(_path) {
if (_path[0] != '/')
_path = '/' + _path;
path = _path;
$.ajax({url: 'http://drdr.racket-lang.org/json/timing'+path,
beforeSend: function(xhr) {
xhr.overrideMimeType( 'text/plain; charset=x-user-defined' );
},
success: function(d) { load_data(d); show(); }});
}
function show() {
for(var i = 0; i < chart_data.length; i++) {
if (show_hide[chart_data[i].label] === false) {
if (!chart_data[i].saved)
chart_data[i].saved = chart_data[i].data
chart_data[i].data = [];
}
else if (chart_data[i].data.length === 0 && chart_data[i].saved !== null) {
chart_data[i].data = chart_data[i].saved;
chart_data[i].saved = null;
}
}
$.plot(placeholder, chart_data, cur_options);
}
function serialize_opts(options) {
var o = {};
if (options.xaxes[0].min)
o.xmin = options.xaxes[0].min;
if (options.xaxes[0].max)
o.xmax = options.xaxes[0].max;
if (options.yaxes[0].min)
o.ymin = options.yaxes[0].min;
if (options.yaxes[0].max)
o.ymax = options.yaxes[0].max;
window.location.hash = "#" + (JSON.stringify([o,show_hide]));
}
function handle_selection(event, ranges) {
cur_options = $.extend(true, {}, cur_options, {
yaxes: [ { min: ranges.yaxis.from, max: ranges.yaxis.to },cur_options.yaxes[1]],
xaxes: [ { min: ranges.xaxis.from, max: ranges.xaxis.to } ]});
serialize_opts(cur_options);
show();
}
function set_legend(new_val) {
cur_options = $.extend(true,{},cur_options, {legend: {show: new_val}});
show();
if (new_val)
$("#setlegend").text("Hide Legend")
else
$("#setlegend").text("Show Legend")
}
function reset_chart() {
cur_options = options; show_hide = {}; show();
}
placeholder.bind("plothover", hover);
placeholder.bind("plotclick", click);
var opts = {xmin : null, ymin: null, xmax: null, ymax : null};
var cur_options = options;
try {
opts = JSON.parse(window.location.hash.substring(1));
} catch(e) {}
if (opts && opts.length == 2) {
cur_options.xaxes[0].min = opts[0].xmin;
cur_options.xaxes[0].max = opts[0].xmax;
cur_options.yaxes[0].min = opts[0].ymin;
cur_options.yaxes[0].max = opts[0].ymax;
for(i in opts[1]) {
console.log(i,opts[1][i]);
show_hide[i] = opts[1][i];
}
}