Added hovertip support back, now with dragging.
This commit is contained in:
Родитель
791bd03f72
Коммит
a5bd6245d6
|
@ -21,5 +21,5 @@ source = mozilla-inbound
|
||||||
source = mozilla-inbound
|
source = mozilla-inbound
|
||||||
|
|
||||||
[im]
|
[im]
|
||||||
source = ionmonkey
|
source = mozilla-inbound
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,61 @@ Machines = [8, 9, 10, 11, 12]
|
||||||
# Increment is currently 1 day.
|
# Increment is currently 1 day.
|
||||||
TimeIncrement = 60 * 60 * 24
|
TimeIncrement = 60 * 60 * 24
|
||||||
|
|
||||||
|
def condense(lines, timelist, timemap, historical):
|
||||||
|
recent = len(timelist) - historical
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
line['newdata'] = []
|
||||||
|
|
||||||
|
newtimelist = []
|
||||||
|
newtimemap = {}
|
||||||
|
|
||||||
|
# We want to take the N historical points and condense them into
|
||||||
|
# R slices, where R is the number of recent points.
|
||||||
|
increment = historical // recent
|
||||||
|
pos = 0
|
||||||
|
while pos < historical:
|
||||||
|
limit = min(pos + increment, historical)
|
||||||
|
for line in lines:
|
||||||
|
data = line['data']
|
||||||
|
newdata = line['newdata']
|
||||||
|
|
||||||
|
average = 0.0
|
||||||
|
count = 0
|
||||||
|
firstCset = None
|
||||||
|
lastCset = None
|
||||||
|
for point in data[pos:limit]:
|
||||||
|
if not point or not point['first']:
|
||||||
|
continue
|
||||||
|
if not firstCset:
|
||||||
|
firstCset = point['first']
|
||||||
|
lastCset = point['last']
|
||||||
|
average = ((average * count) + point['score']) / (count + 1)
|
||||||
|
count += 1
|
||||||
|
point = { 'first': firstCset,
|
||||||
|
'last': lastCset,
|
||||||
|
'score': average
|
||||||
|
}
|
||||||
|
newdata.append(point)
|
||||||
|
newtimemap[timelist[pos]] = len(newtimelist)
|
||||||
|
newtimelist.append(timelist[pos])
|
||||||
|
|
||||||
|
pos += increment
|
||||||
|
|
||||||
|
# Fix up the old lines.
|
||||||
|
for line in lines:
|
||||||
|
data = line['data']
|
||||||
|
newdata = line['newdata']
|
||||||
|
newdata.extend(data[historical:])
|
||||||
|
line['data'] = newdata
|
||||||
|
del line['newdata']
|
||||||
|
|
||||||
|
for t in timelist[historical:]:
|
||||||
|
newtimemap[t] = len(newtimelist)
|
||||||
|
newtimelist.append(t)
|
||||||
|
|
||||||
|
return lines, newtimelist, newtimemap
|
||||||
|
|
||||||
# The aggregate view attempts to coalesce all runs from a 24-hour period into
|
# The aggregate view attempts to coalesce all runs from a 24-hour period into
|
||||||
# one data point, by taking an average of all non-zero scores in that run. In
|
# one data point, by taking an average of all non-zero scores in that run. In
|
||||||
# order to ensure that each line has the same x-axis points, we take the
|
# order to ensure that each line has the same x-axis points, we take the
|
||||||
|
@ -79,61 +134,6 @@ def aggregate(runs, rows):
|
||||||
|
|
||||||
return points
|
return points
|
||||||
|
|
||||||
def condense(lines, timelist, timemap, historical):
|
|
||||||
recent = len(timelist) - historical
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
line['newdata'] = []
|
|
||||||
|
|
||||||
newtimelist = []
|
|
||||||
newtimemap = {}
|
|
||||||
|
|
||||||
# We want to take the N historical points and condense them into
|
|
||||||
# R slices, where R is the number of recent points.
|
|
||||||
increment = historical // recent
|
|
||||||
pos = 0
|
|
||||||
while pos < historical:
|
|
||||||
limit = min(pos + increment, historical)
|
|
||||||
for line in lines:
|
|
||||||
data = line['data']
|
|
||||||
newdata = line['newdata']
|
|
||||||
|
|
||||||
average = 0.0
|
|
||||||
count = 0
|
|
||||||
firstCset = None
|
|
||||||
lastCset = None
|
|
||||||
for point in data[pos:limit]:
|
|
||||||
if not point or not point['first']:
|
|
||||||
continue
|
|
||||||
if not firstCset:
|
|
||||||
firstCset = point['first']
|
|
||||||
lastCset = point['last']
|
|
||||||
average = ((average * count) + point['score']) / (count + 1)
|
|
||||||
count += 1
|
|
||||||
point = { 'first': firstCset,
|
|
||||||
'last': lastCset,
|
|
||||||
'score': average
|
|
||||||
}
|
|
||||||
newdata.append(point)
|
|
||||||
newtimemap[timelist[pos]] = len(newtimelist)
|
|
||||||
newtimelist.append(timelist[pos])
|
|
||||||
|
|
||||||
pos += increment
|
|
||||||
|
|
||||||
# Fix up the old lines.
|
|
||||||
for line in lines:
|
|
||||||
data = line['data']
|
|
||||||
newdata = line['newdata']
|
|
||||||
newdata.extend(data[historical:])
|
|
||||||
line['data'] = newdata
|
|
||||||
del line['newdata']
|
|
||||||
|
|
||||||
for t in timelist[historical:]:
|
|
||||||
newtimemap[t] = len(newtimelist)
|
|
||||||
newtimelist.append(t)
|
|
||||||
|
|
||||||
return lines, newtimelist, newtimemap
|
|
||||||
|
|
||||||
def aggregate_suite(cx, runs, machine, suite):
|
def aggregate_suite(cx, runs, machine, suite):
|
||||||
lines = [ ]
|
lines = [ ]
|
||||||
timemap = { }
|
timemap = { }
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
body {
|
||||||
|
background-color:#FFF;
|
||||||
|
font:.6875em Verdana, sans-serif;
|
||||||
|
color:#000;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color:#000;
|
||||||
|
text-decoration:underline;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size:2.265em;
|
||||||
|
font-weight:700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graph {
|
||||||
|
width: 555px;
|
||||||
|
height: 340px;
|
||||||
|
}
|
||||||
|
.graph-row {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.graph-container {
|
||||||
|
width: 600px;
|
||||||
|
height: 340px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ var AWFY = { };
|
||||||
AWFY.refreshTime = 1000 * 60 * 5;
|
AWFY.refreshTime = 1000 * 60 * 5;
|
||||||
AWFY.machineId = 0;
|
AWFY.machineId = 0;
|
||||||
AWFY.refresh = true;
|
AWFY.refresh = true;
|
||||||
|
AWFY.panes = [];
|
||||||
AWFY.queryParams = { };
|
AWFY.queryParams = { };
|
||||||
AWFY.drawLegend = function () {
|
AWFY.drawLegend = function () {
|
||||||
}
|
}
|
||||||
|
@ -53,7 +54,8 @@ AWFY.compute = function (xhr) {
|
||||||
aggregate: blobgraph.aggregate,
|
aggregate: blobgraph.aggregate,
|
||||||
timelist: blobgraph.timelist,
|
timelist: blobgraph.timelist,
|
||||||
timemap: blobgraph.timemap,
|
timemap: blobgraph.timemap,
|
||||||
earliest: blobgraph.earliest
|
earliest: blobgraph.earliest,
|
||||||
|
info: blobgraph.lines
|
||||||
};
|
};
|
||||||
graphs[name] = graph;
|
graphs[name] = graph;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +63,13 @@ AWFY.compute = function (xhr) {
|
||||||
var data = { graphs: graphs };
|
var data = { graphs: graphs };
|
||||||
|
|
||||||
// Everything built successfully, so now we can send this to be drawn.
|
// Everything built successfully, so now we can send this to be drawn.
|
||||||
this.draw(this, data);
|
for (var i = 0; i < this.panes.length; i++) {
|
||||||
|
var id = this.panes[i];
|
||||||
|
var elt = $('#' + id + '-graph');
|
||||||
|
var graph = data.graphs[id];
|
||||||
|
var display = new Display(this, elt, data, graph);
|
||||||
|
display.draw();
|
||||||
|
}
|
||||||
|
|
||||||
if (this.refresh)
|
if (this.refresh)
|
||||||
window.setTimeout(this.query.bind(this), this.refreshTime);
|
window.setTimeout(this.query.bind(this), this.refreshTime);
|
||||||
|
@ -85,17 +93,25 @@ AWFY.query = function(name, callback) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
callback(xhr);
|
||||||
callback(xhr);
|
|
||||||
} catch (e) {
|
|
||||||
AWFY.onQueryFail();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
xhr.open('GET', url, true);
|
xhr.open('GET', url, true);
|
||||||
xhr.send();
|
xhr.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
AWFY.startup = function() {
|
AWFY.onGraphHover = function (event, pos, item) {
|
||||||
|
var elt = $(event.target);
|
||||||
|
var display = elt.data('awfy-display');
|
||||||
|
display.onHover(event, pos, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
AWFY.onGraphClick = function (event, pos, item) {
|
||||||
|
var elt = $(event.target);
|
||||||
|
var display = elt.data('awfy-display');
|
||||||
|
display.onClick(event, pos, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
AWFY.startup = function () {
|
||||||
var query = window.location.search.substring(1);
|
var query = window.location.search.substring(1);
|
||||||
var items = query.split('&');
|
var items = query.split('&');
|
||||||
for (var i = 0; i < items.length; i++) {
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
@ -108,6 +124,12 @@ AWFY.startup = function() {
|
||||||
else
|
else
|
||||||
this.machineId = 11;
|
this.machineId = 11;
|
||||||
|
|
||||||
|
for (var i = 0; i < this.panes.length; i++) {
|
||||||
|
var id = this.panes[i];
|
||||||
|
$('#' + id + '-graph').bind("plothover", this.onGraphHover.bind(this));
|
||||||
|
$('#' + id + '-graph').bind("plotclick", this.onGraphClick.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
this.query('aggregate-' + this.machineId + '.json', this.compute.bind(this));
|
this.query('aggregate-' + this.machineId + '.json', this.compute.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2483,7 +2483,7 @@
|
||||||
if (series.points.symbol == "circle")
|
if (series.points.symbol == "circle")
|
||||||
octx.arc(x, y, radius, 0, 2 * Math.PI, false);
|
octx.arc(x, y, radius, 0, 2 * Math.PI, false);
|
||||||
else
|
else
|
||||||
series.points.symbol(octx, x, y, radius, false);
|
series.points.symbol(octx, x, y, radius, false, plot);
|
||||||
octx.closePath();
|
octx.closePath();
|
||||||
octx.stroke();
|
octx.stroke();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,105 @@
|
||||||
// vim: set ts=4 sw=4 tw=99 et:
|
// vim: set ts=4 sw=4 tw=99 et:
|
||||||
|
|
||||||
function DrawGraph(awfy, elt, data, graph)
|
function Display(awfy, elt, data, graph)
|
||||||
{
|
{
|
||||||
|
this.awfy = awfy;
|
||||||
|
this.data = data;
|
||||||
|
this.graph = graph;
|
||||||
|
this.elt = elt;
|
||||||
|
this.elt.data('awfy-display', this);
|
||||||
|
|
||||||
|
// The last hovering tooltip we displayed, that has not been clicked.
|
||||||
|
this.hovering = null;
|
||||||
|
|
||||||
|
if (graph.aggregate) {
|
||||||
|
// Find the range of historical points.
|
||||||
|
for (var i = 0; i < graph.timelist.length; i++) {
|
||||||
|
if (graph.timelist[i] >= graph.earliest)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i && i != graph.timelist.length)
|
||||||
|
this.historical = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Display.Months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
||||||
|
|
||||||
|
// Copy flot's tick algorithm.
|
||||||
|
Display.prototype.tickSize = function (min, max) {
|
||||||
|
var noTicks = 0.3 * Math.sqrt(this.elt.width());
|
||||||
|
var delta = (max - min) / noTicks;
|
||||||
|
var dec = -Math.floor(Math.log(delta) / Math.LN10);
|
||||||
|
var magn = Math.pow(10, -dec);
|
||||||
|
var norm = delta / magn;
|
||||||
|
var size;
|
||||||
|
if (norm < 1.5) {
|
||||||
|
size = 1;
|
||||||
|
} else if (norm < 3) {
|
||||||
|
size = 2;
|
||||||
|
if (norm > 2.25) {
|
||||||
|
size = 2.5;
|
||||||
|
++dec;
|
||||||
|
}
|
||||||
|
} else if (norm < 7.5) {
|
||||||
|
size = 5;
|
||||||
|
} else {
|
||||||
|
size = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
size *= magn;
|
||||||
|
return size;
|
||||||
|
};
|
||||||
|
|
||||||
|
Display.prototype.aggregateTicks = function () {
|
||||||
|
// Draw historical ticks at a higher density.
|
||||||
|
var ticks = this.tickSize(0, this.graph.timelist.length);
|
||||||
|
var list = [];
|
||||||
|
|
||||||
|
// This is all a bunch of hardcoded hacks for now.
|
||||||
|
var preticks = (ticks == 5) ? 6 : ticks;
|
||||||
|
var last_year = undefined;
|
||||||
|
var current_year = (new Date()).getFullYear();
|
||||||
|
for (var i = 0; i < this.historical; i += preticks) {
|
||||||
|
var d = new Date(this.graph.timelist[i] * 1000);
|
||||||
|
var text = Display.Months[d.getMonth()];
|
||||||
|
|
||||||
|
// Some graphs span over a year, so add in a hint when the year
|
||||||
|
// changes.
|
||||||
|
if ((i == 0 && d.getFullYear() != current_year) ||
|
||||||
|
(last_year && d.getFullYear() != last_year))
|
||||||
|
{
|
||||||
|
text += " " + d.getFullYear();
|
||||||
|
last_year = d.getFullYear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the tick mark, then try to add some more empty ones to
|
||||||
|
// make the graph appear denser.
|
||||||
|
list.push([i, text]);
|
||||||
|
if (preticks == 6) {
|
||||||
|
if (i + 2 < this.historical) {
|
||||||
|
list.push([i + 2, ""]);
|
||||||
|
if (i + 4 < this.historical)
|
||||||
|
list.push([i + 4, ""]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out where we should start placing sparser lines, since
|
||||||
|
// we don't want them too close to the historical lines.
|
||||||
|
i = list[list.length - 1][0] + ticks;
|
||||||
|
|
||||||
|
// If the aggregate graph has both historical and recent points,
|
||||||
|
for (; i < this.graph.timelist.length; i += ticks) {
|
||||||
|
var d = new Date(this.graph.timelist[Math.floor(i)] * 1000);
|
||||||
|
var text = Display.Months[d.getMonth()] + " " + d.getDate();
|
||||||
|
list.push([i, text]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
Display.prototype.draw = function () {
|
||||||
var options = { };
|
var options = { };
|
||||||
|
|
||||||
options.lines = { show: true };
|
options.lines = { show: true };
|
||||||
|
@ -11,112 +109,244 @@ function DrawGraph(awfy, elt, data, graph)
|
||||||
options.legend = { show: false };
|
options.legend = { show: false };
|
||||||
options.xaxis = { };
|
options.xaxis = { };
|
||||||
options.yaxis = { min: 0 };
|
options.yaxis = { min: 0 };
|
||||||
options.grid = { };
|
options.grid = { hoverable: true, clickable: true };
|
||||||
|
|
||||||
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
if (this.historical) {
|
||||||
|
// If the graph has both historical and recent points, indicated by
|
||||||
var historical;
|
// the "historical" midpoint, then we change some graph formatting
|
||||||
if (graph.aggregate) {
|
// to reflect that part of the graph has a greater time density.
|
||||||
// Find the range of historical points.
|
//
|
||||||
for (var i = 0; i < graph.timelist.length; i++) {
|
// To make this work, we modified flot to pass in its plot variable
|
||||||
if (graph.timelist[i] >= graph.earliest)
|
// when invoking this callback, in order to use c2p().
|
||||||
break;
|
options.points.symbol = (function (ctx, x, y, radius, shadow, plot) {
|
||||||
}
|
|
||||||
|
|
||||||
if (i && i != graph.timelist.length)
|
|
||||||
historical = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the graph has both historical and recent points, indicated by
|
|
||||||
// the "historical" midpoint, then we change some graph formatting
|
|
||||||
// to reflect that part of the graph has a greater time density.
|
|
||||||
if (historical) {
|
|
||||||
options.points.symbol = function (ctx, x, y, radius, shadow, plot) {
|
|
||||||
var axis = plot.getAxes();
|
var axis = plot.getAxes();
|
||||||
var rx = Math.round(axis.xaxis.c2p(x));
|
var rx = Math.round(axis.xaxis.c2p(x));
|
||||||
if (graph.timelist[rx] < graph.earliest) {
|
if (this.graph.timelist[rx] < this.graph.earliest) {
|
||||||
ctx.strokeRect(x - radius / 2, y - radius / 2, radius, radius);
|
ctx.strokeRect(x - radius / 2, y - radius / 2, radius, radius);
|
||||||
ctx.clearRect(x - radius / 4, y - radius / 4, radius / 2, radius / 2);
|
ctx.clearRect(x - radius / 4, y - radius / 4, radius / 2, radius / 2);
|
||||||
} else {
|
} else {
|
||||||
ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
|
ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
|
||||||
}
|
}
|
||||||
}
|
}).bind(this);
|
||||||
|
|
||||||
// Copy flot's tick algorithm.
|
options.xaxis.ticks = this.aggregateTicks();
|
||||||
var sizefn = function (min, max) {
|
|
||||||
var noTicks = 0.3 * Math.sqrt(elt.width());
|
|
||||||
var delta = (max - min) / noTicks;
|
|
||||||
var dec = -Math.floor(Math.log(delta) / Math.LN10);
|
|
||||||
var magn = Math.pow(10, -dec);
|
|
||||||
var norm = delta / magn;
|
|
||||||
var size;
|
|
||||||
if (norm < 1.5) {
|
|
||||||
size = 1;
|
|
||||||
} else if (norm < 3) {
|
|
||||||
size = 2;
|
|
||||||
if (norm > 2.25) {
|
|
||||||
size = 2.5;
|
|
||||||
++dec;
|
|
||||||
}
|
|
||||||
} else if (norm < 7.5) {
|
|
||||||
size = 5;
|
|
||||||
} else {
|
|
||||||
size = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
size *= magn;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the aggregate graph has both historical and recent points,
|
|
||||||
var ticks = sizefn(0, graph.timelist.length);
|
|
||||||
options.xaxis.ticks = [];
|
|
||||||
for (var i = 0; i < historical; i += ticks) {
|
|
||||||
var d = new Date(graph.timelist[Math.floor(i)] * 1000);
|
|
||||||
var text = months[d.getMonth()] + " " + d.getDate();
|
|
||||||
options.xaxis.ticks.push([i, text]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the recent ticks, but try to increase the density. This is
|
|
||||||
// all a bunch of special cased hacks for now, since all our graphs
|
|
||||||
// appear to compute ticksize = 5.
|
|
||||||
if (ticks == 5) {
|
|
||||||
// Make sure we don't skip too many lines in between.
|
|
||||||
if (i - historical >= 3)
|
|
||||||
i = historical + 2;
|
|
||||||
ticks = 6;
|
|
||||||
}
|
|
||||||
for (; i < graph.timelist.length; i += ticks) {
|
|
||||||
var d = new Date(graph.timelist[Math.floor(i)] * 1000);
|
|
||||||
var text = months[d.getMonth()] + " " + d.getDate();
|
|
||||||
options.xaxis.ticks.push([i, text]);
|
|
||||||
if (ticks == 6) {
|
|
||||||
options.xaxis.ticks.push([i + 2, ""]);
|
|
||||||
options.xaxis.ticks.push([i + 4, ""]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.xaxis.ticks) {
|
if (!options.xaxis.ticks) {
|
||||||
options.xaxis.tickFormatter = function (v, axis) {
|
options.xaxis.tickFormatter = function (v, axis) {
|
||||||
v = Math.round(v);
|
v = Math.round(v);
|
||||||
if (v < 0 || v >= graph.timelist.length)
|
if (v < 0 || v >= this.graph.timelist.length)
|
||||||
return '';
|
return '';
|
||||||
var t = graph.timelist[v];
|
var t = this.graph.timelist[v];
|
||||||
var d = new Date(t * 1000);
|
var d = new Date(t * 1000);
|
||||||
return months[d.getMonth()] + " " + d.getDate();
|
return Display.Months[d.getMonth()] + " " + d.getDate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var plot = $.plot(elt, graph.lines, options);
|
this.plot = $.plot(this.elt, this.graph.lines, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DrawFrontPage(awfy, data)
|
// Display.prototype.checkHoverDelta(pos) {
|
||||||
{
|
// if (!this.lastPos)
|
||||||
var graphs = data.graphs;
|
// return false;
|
||||||
|
// if (pos.pageX - this.lastPos.pageX >= 0 &&
|
||||||
|
// pos.pageX - this.lastPos.pageX <= 5 &&
|
||||||
|
// pos.pageY - this.lastPos.pageY >= 0 &&
|
||||||
|
// pos.pageY - this.lastPos.pageY <= 5)
|
||||||
|
// {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
DrawGraph(awfy, $("#kraken-graph"), data, graphs['kraken']);
|
Display.prototype.createToolTip = function (item, extended) {
|
||||||
DrawGraph(awfy, $("#sunspider-graph"), data, graphs['ss']);
|
var so = extended ? '<strong>' : '';
|
||||||
DrawGraph(awfy, $("#v8-graph"), data, graphs['v8real']);
|
var sc = extended ? '</strong>' : '';
|
||||||
|
|
||||||
|
var x = item.datapoint[0];
|
||||||
|
var y = item.datapoint[1];
|
||||||
|
var text = so + 'score: ' + sc + y.toFixed() + '<br>';
|
||||||
|
|
||||||
|
// Figure out the line this corresponds to.
|
||||||
|
var line = this.graph.info[item.seriesIndex];
|
||||||
|
if (!line)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find the point previous to this one.
|
||||||
|
var prev = null;
|
||||||
|
for (var i = x - 1; i >= 0; i--) {
|
||||||
|
if (line.data[i] && line.data[i].score) {
|
||||||
|
prev = line.data[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev) {
|
||||||
|
// Compute a difference.
|
||||||
|
var diff = Math.round((y - prev.score) * 10) / 10;
|
||||||
|
var perc = -Math.round(((y - prev.score) / prev.score) * 1000) / 10;
|
||||||
|
var better;
|
||||||
|
if ((perc < 0 && this.graph.direction == -1) ||
|
||||||
|
(perc > 0 && this.graph.direction == 1))
|
||||||
|
{
|
||||||
|
better = 'worse';
|
||||||
|
} else {
|
||||||
|
better = 'better';
|
||||||
|
}
|
||||||
|
perc = Math.abs(perc);
|
||||||
|
|
||||||
|
if (diff === diff) {
|
||||||
|
if (extended)
|
||||||
|
text += so + 'delta' + sc + ': ' + diff;
|
||||||
|
else
|
||||||
|
text += String.fromCharCode(916) + ': ' + diff;
|
||||||
|
if (this.graph.direction == -1)
|
||||||
|
text += 'ms';
|
||||||
|
text += ' (' + perc + '% ' + better + ')<br>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the vendor.
|
||||||
|
var mode = AWFYMaster.modes[line.modeid];
|
||||||
|
var vendor;
|
||||||
|
if (mode)
|
||||||
|
vendor = AWFYMaster.vendors[mode.vendor_id];
|
||||||
|
if (vendor) {
|
||||||
|
text += so + 'source: ' + sc +
|
||||||
|
vendor.browser +
|
||||||
|
' (' + mode.name + ')'+
|
||||||
|
'<br>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the datapoint.
|
||||||
|
var point = line.data[x];
|
||||||
|
|
||||||
|
if (extended && vendor) {
|
||||||
|
if (point.last) {
|
||||||
|
text += so + 'revs: ' + sc +
|
||||||
|
'<a href="' + vendor.url + point.first + '">' + point.first + '</a>' +
|
||||||
|
' to ' +
|
||||||
|
'<a href="' + vendor.url + point.last + '">' + point.last + '</a>' +
|
||||||
|
'<br>';
|
||||||
|
} else {
|
||||||
|
text += so + 'rev: ' + sc +
|
||||||
|
'<a href="' + vendor.url + point.first + '">' + point.first + '</a>' +
|
||||||
|
'<br>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pad = function (d) {
|
||||||
|
if (d == 0)
|
||||||
|
return '00';
|
||||||
|
else if (d < 10)
|
||||||
|
return '0' + d;
|
||||||
|
else
|
||||||
|
return '' + d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a year, if we should.
|
||||||
|
if (extended) {
|
||||||
|
var current_year = (new Date()).getFullYear();
|
||||||
|
var datefmt = function (t, forceYear) {
|
||||||
|
var d = new Date(t * 1000);
|
||||||
|
var text = Display.Months[d.getMonth()] + ' ' + d.getDate();
|
||||||
|
if (d.getFullYear() != current_year || forceYear)
|
||||||
|
text += ', ' + d.getFullYear();
|
||||||
|
if (d.getHours() || d.getMinutes()) {
|
||||||
|
text += ' ';
|
||||||
|
text += pad(d.getHours()) + ':' +
|
||||||
|
pad(d.getMinutes());
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point.last && x < this.graph.timelist.length - 1) {
|
||||||
|
text += so + 'tested: ' + sc +
|
||||||
|
datefmt(this.graph.timelist[x]) + ' to ' +
|
||||||
|
datefmt(this.graph.timelist[x + 1], true) + '<br>';
|
||||||
|
} else {
|
||||||
|
text += so + 'tested: ' + sc +
|
||||||
|
datefmt(this.graph.timelist[x]) + '<br>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Include a short timestamp if we're looking at recent changesets.
|
||||||
|
var d = new Date(this.graph.timelist[x] * 1000);
|
||||||
|
var now = new Date();
|
||||||
|
var set = false;
|
||||||
|
if (now.getMonth() == d.getMonth() &&
|
||||||
|
now.getFullYear() == d.getFullYear())
|
||||||
|
{
|
||||||
|
if (now.getDate() == d.getDate()) {
|
||||||
|
text += so + 'tested: ' + sc + 'Today, ';
|
||||||
|
set = true;
|
||||||
|
} else if (now.getDate() - 1 == d.getDate()) {
|
||||||
|
text += so + 'tested: ' + sc + 'Yesterday, ';
|
||||||
|
set = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!set) {
|
||||||
|
text += so + 'tested: ' + sc;
|
||||||
|
if (x < this.historical)
|
||||||
|
text += 'around ';
|
||||||
|
text += Display.Months[d.getMonth()] + ' ' + d.getDate();
|
||||||
|
if (now.getFullYear() != d.getFullYear())
|
||||||
|
text += ', ' + d.getFullYear() + ' ';
|
||||||
|
else
|
||||||
|
text += ' ';
|
||||||
|
}
|
||||||
|
if (x >= this.historical)
|
||||||
|
text += pad(d.getHours()) + ':' + pad(d.getMinutes()) + '<br>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ToolTip(item.pageX, item.pageY, item, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Display.prototype.onClick = function (event, pos, item) {
|
||||||
|
// Remove the existing hovertip.
|
||||||
|
if (this.hovering) {
|
||||||
|
this.hovering.remove();
|
||||||
|
this.hovering = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var tooltip = this.createToolTip(item, true);
|
||||||
|
tooltip.drawFloating();
|
||||||
|
|
||||||
|
// The color of the line will be the series color.
|
||||||
|
var line = this.graph.info[item.seriesIndex];
|
||||||
|
if (!line)
|
||||||
|
return;
|
||||||
|
var mode = AWFYMaster.modes[line.modeid];
|
||||||
|
if (!mode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tooltip.attachLine(mode.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
Display.prototype.areItemsEqual = function (item1, item2) {
|
||||||
|
return item1.seriesIndex == item2.seriesIndex &&
|
||||||
|
item1.dataIndex == item2.dataIndex &&
|
||||||
|
item1.datapoint[0] == item2.datapoint[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Display.prototype.onHover = function (event, pos, item) {
|
||||||
|
// Are we already hovering over something?
|
||||||
|
if (this.hovering) {
|
||||||
|
// If we're hovering over the same point, don't do anything.
|
||||||
|
if (item && this.areItemsEqual(item, this.hovering.item))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Otherwise, remove the div since we will redraw.
|
||||||
|
this.hovering.remove();
|
||||||
|
this.hovering = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.hovering = this.createToolTip(item, false);
|
||||||
|
this.hovering.drawBasic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,18 +5,21 @@
|
||||||
<meta http-equiv="content-language" content="en">
|
<meta http-equiv="content-language" content="en">
|
||||||
<title>ARE WE FAST YET?</title>
|
<title>ARE WE FAST YET?</title>
|
||||||
<link rel="stylesheet" title="Default Stylesheet" type="text/css" href="style.css">
|
<link rel="stylesheet" title="Default Stylesheet" type="text/css" href="style.css">
|
||||||
|
<link rel="stylesheet" title="Default Stylesheet" type="text/css" href="jquery/css/jquery-ui-1.9.2.custom.css">
|
||||||
<link rel="shortcut icon" href="http://www.arewefastyet.com/awfy_favicon.png">
|
<link rel="shortcut icon" href="http://www.arewefastyet.com/awfy_favicon.png">
|
||||||
<script type="text/javascript" src="flot/jquery.js"></script>
|
<script type="text/javascript" src="jquery/jquery-1.8.3.min.js"></script>
|
||||||
|
<script type="text/javascript" src="jquery/jquery-ui-1.9.2.custom.min.js"></script>
|
||||||
<script type="text/javascript" src="flot/jquery.flot.js"></script>
|
<script type="text/javascript" src="flot/jquery.flot.js"></script>
|
||||||
<script type="text/javascript" src="data/master.js"></script>
|
<script type="text/javascript" src="data/master.js"></script>
|
||||||
<script type="text/javascript" src="awfy.js"></script>
|
<script type="text/javascript" src="awfy.js"></script>
|
||||||
<script type="text/javascript" src="frontpage.js"></script>
|
<script type="text/javascript" src="frontpage.js"></script>
|
||||||
|
<script type="text/javascript" src="tooltip.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
AWFY.refresh = true;
|
AWFY.refresh = true;
|
||||||
AWFY.draw = DrawFrontPage;
|
AWFY.panes = ['kraken', 'ss', 'v8real'];
|
||||||
AWFY.startup();
|
AWFY.startup();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -28,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="graph-container">
|
<div class="graph-container">
|
||||||
<div id="sunspider-label">sunspider time</div>
|
<div id="sunspider-label">sunspider time</div>
|
||||||
<div class="graph" id="sunspider-graph"><h2>Loading...</h2></div>
|
<div class="graph" id="ss-graph"><h2>Loading...</h2></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
|
@ -38,7 +41,7 @@
|
||||||
<div class="graph-row">
|
<div class="graph-row">
|
||||||
<div class="graph-container">
|
<div class="graph-container">
|
||||||
<div id="v8-label">v8bench score</div>
|
<div id="v8-label">v8bench score</div>
|
||||||
<div class="graph" id="v8-graph"><h2>Loading...</h2></div>
|
<div class="graph" id="v8real-graph"><h2>Loading...</h2></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*! jQuery UI - v1.9.2 - 2012-11-28
|
||||||
|
* http://jqueryui.com
|
||||||
|
* Includes: jquery.ui.core.css
|
||||||
|
* Copyright (c) 2012 jQuery Foundation and other contributors Licensed MIT */
|
||||||
|
|
||||||
|
/* Layout helpers
|
||||||
|
----------------------------------*/
|
||||||
|
.ui-helper-hidden { display: none; }
|
||||||
|
.ui-helper-hidden-accessible { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
|
||||||
|
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
|
||||||
|
.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
|
||||||
|
.ui-helper-clearfix:after { clear: both; }
|
||||||
|
.ui-helper-clearfix { zoom: 1; }
|
||||||
|
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
|
||||||
|
|
||||||
|
|
||||||
|
/* Interaction Cues
|
||||||
|
----------------------------------*/
|
||||||
|
.ui-state-disabled { cursor: default !important; }
|
||||||
|
|
||||||
|
|
||||||
|
/* Icons
|
||||||
|
----------------------------------*/
|
||||||
|
|
||||||
|
/* states and images */
|
||||||
|
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
|
||||||
|
|
||||||
|
|
||||||
|
/* Misc visuals
|
||||||
|
----------------------------------*/
|
||||||
|
|
||||||
|
/* Overlays */
|
||||||
|
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
|
@ -0,0 +1,6 @@
|
||||||
|
/*! jQuery UI - v1.9.2 - 2012-11-28
|
||||||
|
* http://jqueryui.com
|
||||||
|
* Includes: jquery.ui.core.css
|
||||||
|
* Copyright (c) 2012 jQuery Foundation and other contributors Licensed MIT */
|
||||||
|
|
||||||
|
.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{zoom:1}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:absolute;top:0;left:0;width:100%;height:100%}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,65 @@
|
||||||
|
body {
|
||||||
|
background-color:#FFF;
|
||||||
|
font:.6875em Verdana, sans-serif;
|
||||||
|
color:#000;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color:#000;
|
||||||
|
text-decoration:underline;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size:2.265em;
|
||||||
|
font-weight:700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graph {
|
||||||
|
width: 555px;
|
||||||
|
height: 340px;
|
||||||
|
}
|
||||||
|
.graph-row {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.graph-container {
|
||||||
|
width: 600px;
|
||||||
|
height: 340px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
-moz-border-radius:.35em;
|
||||||
|
-webkit-border-radius:.35em;
|
||||||
|
background-color:#000;
|
||||||
|
color:#FFF;
|
||||||
|
display:none;
|
||||||
|
opacity:0.8;
|
||||||
|
padding:.25em;
|
||||||
|
position:absolute;
|
||||||
|
}
|
||||||
|
.tooltip a:link, .tooltip a:active, .tooltip a:visited {
|
||||||
|
color:#FFF;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.tooltip a:hover {
|
||||||
|
color:#FFF;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeable {
|
||||||
|
padding: 15px;
|
||||||
|
opacity: 1.0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeButton {
|
||||||
|
color:#CCC;
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
font-family: monospace;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
// vim: set ts=4 sw=4 tw=99 et:
|
||||||
|
function ToolTip(x, y, item, contents)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.item = item;
|
||||||
|
this.contents = contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolTip.prototype.drawFloating = function () {
|
||||||
|
var text = '<div class="tooltip closeable"></div>';
|
||||||
|
this.elt = $(text);
|
||||||
|
|
||||||
|
var ruler = $('<div class="tooltipRuler"></div>');
|
||||||
|
ruler.appendTo(this.elt);
|
||||||
|
|
||||||
|
var button = $('<a class="closeButton" href="#"></a>');
|
||||||
|
button.text('[x]');
|
||||||
|
button.appendTo(this.elt);
|
||||||
|
button.css('text-decoration', 'none');
|
||||||
|
button.click(this.remove.bind(this));
|
||||||
|
|
||||||
|
var span = $('<span>' + this.contents + '</span>');
|
||||||
|
span.appendTo(this.elt);
|
||||||
|
|
||||||
|
this.draw();
|
||||||
|
this.elt.draggable({ drag: this.onDrag.bind(this) });
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolTip.prototype.drawBasic = function () {
|
||||||
|
var text = '<div class="tooltip">' + this.contents + '</div>';
|
||||||
|
this.elt = $(text);
|
||||||
|
this.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolTip.prototype.draw = function () {
|
||||||
|
var tipWidth = 165;
|
||||||
|
var tipHeight = 75;
|
||||||
|
var xOffset = 5;
|
||||||
|
var yOffset = 5;
|
||||||
|
|
||||||
|
var ie = document.all && !window.opera;
|
||||||
|
var iebody = (document.compatMode == 'CSS1Compat')
|
||||||
|
? document.documentElement
|
||||||
|
: document.body;
|
||||||
|
var scrollLeft = ie ? iebody.scrollLeft : window.pageXOffset;
|
||||||
|
var scrollTop = ie ? iebody.scrollTop : window.pageYOffset;
|
||||||
|
var docWidth = ie ? iebody.clientWidth - 15 : window.innerWidth - 15;
|
||||||
|
var docHeight = ie ? iebody.clientHeight - 15 : window.innerHeight - 8;
|
||||||
|
var y = (this.y + tipHeight - scrollTop > docHeight)
|
||||||
|
? this.y - tipHeight - 5 - (yOffset * 2)
|
||||||
|
: this.y; // account for bottom edge
|
||||||
|
|
||||||
|
// account for right edge
|
||||||
|
this.elt.css({ top: this.y + yOffset});
|
||||||
|
|
||||||
|
if (this.x + tipWidth - scrollLeft > docWidth)
|
||||||
|
this.elt.css({ right: docWidth - this.x + xOffset });
|
||||||
|
else
|
||||||
|
this.elt.css({ left: this.x + xOffset });
|
||||||
|
|
||||||
|
this.elt.appendTo('body').fadeIn(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolTip.prototype.remove = function () {
|
||||||
|
if (this.svg)
|
||||||
|
this.svg.remove();
|
||||||
|
this.elt.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolTip.prototype.midpoint = function () {
|
||||||
|
var offset = this.elt.offset();
|
||||||
|
var width = this.elt.width();
|
||||||
|
return { x: offset.left + width / 2, y: offset.top };
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolTip.prototype.onDrag = function (event, ui) {
|
||||||
|
if (!this.line)
|
||||||
|
return;
|
||||||
|
this.line.setAttribute('x2', this.midpoint().x);
|
||||||
|
this.line.setAttribute('y2', this.midpoint().y);
|
||||||
|
this.svg.height($('body').height());
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolTip.prototype.attachLine = function (color) {
|
||||||
|
// Now overlay a line from the point to the tooltip, ya.
|
||||||
|
var ns = "http://www.w3.org/2000/svg";
|
||||||
|
|
||||||
|
var svg = $(document.createElementNS(ns, 'svg'));
|
||||||
|
svg.css({ position: 'absolute',
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
'z-index': -1,
|
||||||
|
overflow: 'visible'
|
||||||
|
});
|
||||||
|
var line = document.createElementNS(ns, 'line');
|
||||||
|
line.setAttribute('x1', this.item.pageX);
|
||||||
|
line.setAttribute('x2', this.midpoint().x);
|
||||||
|
line.setAttribute('y1', this.item.pageY);
|
||||||
|
line.setAttribute('y2', this.midpoint().y);
|
||||||
|
line.setAttribute('stroke', color);
|
||||||
|
line.setAttribute('stroke-width', 2);
|
||||||
|
$(line).appendTo(svg);
|
||||||
|
|
||||||
|
svg.height($('body').height());
|
||||||
|
svg.appendTo('body');
|
||||||
|
this.svg = svg;
|
||||||
|
this.line = line;
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче