Added hovertip support back, now with dragging.
This commit is contained in:
Родитель
791bd03f72
Коммит
a5bd6245d6
|
@ -21,5 +21,5 @@ source = mozilla-inbound
|
|||
source = mozilla-inbound
|
||||
|
||||
[im]
|
||||
source = ionmonkey
|
||||
source = mozilla-inbound
|
||||
|
||||
|
|
|
@ -12,6 +12,61 @@ Machines = [8, 9, 10, 11, 12]
|
|||
# Increment is currently 1 day.
|
||||
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
|
||||
# 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
|
||||
|
@ -79,61 +134,6 @@ def aggregate(runs, rows):
|
|||
|
||||
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):
|
||||
lines = [ ]
|
||||
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.machineId = 0;
|
||||
AWFY.refresh = true;
|
||||
AWFY.panes = [];
|
||||
AWFY.queryParams = { };
|
||||
AWFY.drawLegend = function () {
|
||||
}
|
||||
|
@ -53,7 +54,8 @@ AWFY.compute = function (xhr) {
|
|||
aggregate: blobgraph.aggregate,
|
||||
timelist: blobgraph.timelist,
|
||||
timemap: blobgraph.timemap,
|
||||
earliest: blobgraph.earliest
|
||||
earliest: blobgraph.earliest,
|
||||
info: blobgraph.lines
|
||||
};
|
||||
graphs[name] = graph;
|
||||
}
|
||||
|
@ -61,7 +63,13 @@ AWFY.compute = function (xhr) {
|
|||
var data = { graphs: graphs };
|
||||
|
||||
// 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)
|
||||
window.setTimeout(this.query.bind(this), this.refreshTime);
|
||||
|
@ -85,17 +93,25 @@ AWFY.query = function(name, callback) {
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
callback(xhr);
|
||||
} catch (e) {
|
||||
AWFY.onQueryFail();
|
||||
}
|
||||
};
|
||||
xhr.open('GET', url, true);
|
||||
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 items = query.split('&');
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
|
@ -108,6 +124,12 @@ AWFY.startup = function() {
|
|||
else
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
@ -2483,7 +2483,7 @@
|
|||
if (series.points.symbol == "circle")
|
||||
octx.arc(x, y, radius, 0, 2 * Math.PI, false);
|
||||
else
|
||||
series.points.symbol(octx, x, y, radius, false);
|
||||
series.points.symbol(octx, x, y, radius, false, plot);
|
||||
octx.closePath();
|
||||
octx.stroke();
|
||||
}
|
||||
|
|
|
@ -1,21 +1,16 @@
|
|||
// vim: set ts=4 sw=4 tw=99 et:
|
||||
|
||||
function DrawGraph(awfy, elt, data, graph)
|
||||
function Display(awfy, elt, data, graph)
|
||||
{
|
||||
var options = { };
|
||||
this.awfy = awfy;
|
||||
this.data = data;
|
||||
this.graph = graph;
|
||||
this.elt = elt;
|
||||
this.elt.data('awfy-display', this);
|
||||
|
||||
options.lines = { show: true };
|
||||
options.points = { fillColor: "#ffffff", show: true };
|
||||
options.borderWidth = 1.5;
|
||||
options.borderColor = "#BEBEBE";
|
||||
options.legend = { show: false };
|
||||
options.xaxis = { };
|
||||
options.yaxis = { min: 0 };
|
||||
options.grid = { };
|
||||
// The last hovering tooltip we displayed, that has not been clicked.
|
||||
this.hovering = null;
|
||||
|
||||
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
||||
|
||||
var historical;
|
||||
if (graph.aggregate) {
|
||||
// Find the range of historical points.
|
||||
for (var i = 0; i < graph.timelist.length; i++) {
|
||||
|
@ -24,27 +19,15 @@ function DrawGraph(awfy, elt, data, graph)
|
|||
}
|
||||
|
||||
if (i && i != graph.timelist.length)
|
||||
historical = i;
|
||||
this.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 rx = Math.round(axis.xaxis.c2p(x));
|
||||
if (graph.timelist[rx] < graph.earliest) {
|
||||
ctx.strokeRect(x - radius / 2, y - radius / 2, radius, radius);
|
||||
ctx.clearRect(x - radius / 4, y - radius / 4, radius / 2, radius / 2);
|
||||
} else {
|
||||
ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
|
||||
}
|
||||
}
|
||||
Display.Months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
||||
|
||||
// Copy flot's tick algorithm.
|
||||
var sizefn = function (min, max) {
|
||||
var noTicks = 0.3 * Math.sqrt(elt.width());
|
||||
// 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);
|
||||
|
@ -66,57 +49,304 @@ function DrawGraph(awfy, elt, data, graph)
|
|||
|
||||
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,
|
||||
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]);
|
||||
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]);
|
||||
}
|
||||
|
||||
// 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, ""]);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
Display.prototype.draw = function () {
|
||||
var options = { };
|
||||
|
||||
options.lines = { show: true };
|
||||
options.points = { fillColor: "#ffffff", show: true };
|
||||
options.borderWidth = 1.5;
|
||||
options.borderColor = "#BEBEBE";
|
||||
options.legend = { show: false };
|
||||
options.xaxis = { };
|
||||
options.yaxis = { min: 0 };
|
||||
options.grid = { hoverable: true, clickable: true };
|
||||
|
||||
if (this.historical) {
|
||||
// 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.
|
||||
//
|
||||
// To make this work, we modified flot to pass in its plot variable
|
||||
// when invoking this callback, in order to use c2p().
|
||||
options.points.symbol = (function (ctx, x, y, radius, shadow, plot) {
|
||||
var axis = plot.getAxes();
|
||||
var rx = Math.round(axis.xaxis.c2p(x));
|
||||
if (this.graph.timelist[rx] < this.graph.earliest) {
|
||||
ctx.strokeRect(x - radius / 2, y - radius / 2, radius, radius);
|
||||
ctx.clearRect(x - radius / 4, y - radius / 4, radius / 2, radius / 2);
|
||||
} else {
|
||||
ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
|
||||
}
|
||||
}).bind(this);
|
||||
|
||||
options.xaxis.ticks = this.aggregateTicks();
|
||||
}
|
||||
|
||||
if (!options.xaxis.ticks) {
|
||||
options.xaxis.tickFormatter = function (v, axis) {
|
||||
v = Math.round(v);
|
||||
if (v < 0 || v >= graph.timelist.length)
|
||||
if (v < 0 || v >= this.graph.timelist.length)
|
||||
return '';
|
||||
var t = graph.timelist[v];
|
||||
var t = this.graph.timelist[v];
|
||||
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)
|
||||
{
|
||||
var graphs = data.graphs;
|
||||
// Display.prototype.checkHoverDelta(pos) {
|
||||
// if (!this.lastPos)
|
||||
// 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']);
|
||||
DrawGraph(awfy, $("#sunspider-graph"), data, graphs['ss']);
|
||||
DrawGraph(awfy, $("#v8-graph"), data, graphs['v8real']);
|
||||
Display.prototype.createToolTip = function (item, extended) {
|
||||
var so = extended ? '<strong>' : '';
|
||||
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">
|
||||
<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="jquery/css/jquery-ui-1.9.2.custom.css">
|
||||
<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="data/master.js"></script>
|
||||
<script type="text/javascript" src="awfy.js"></script>
|
||||
<script type="text/javascript" src="frontpage.js"></script>
|
||||
<script type="text/javascript" src="tooltip.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
AWFY.refresh = true;
|
||||
AWFY.draw = DrawFrontPage;
|
||||
AWFY.panes = ['kraken', 'ss', 'v8real'];
|
||||
AWFY.startup();
|
||||
});
|
||||
</script>
|
||||
|
@ -28,7 +31,7 @@
|
|||
</div>
|
||||
<div class="graph-container">
|
||||
<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>
|
||||
<br>
|
||||
|
@ -38,7 +41,7 @@
|
|||
<div class="graph-row">
|
||||
<div class="graph-container">
|
||||
<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>
|
||||
<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;
|
||||
}
|
||||
|
Загрузка…
Ссылка в новой задаче