Skip to content

Instantly share code, notes, and snippets.

@cpudney
Created December 22, 2011 08:27
Show Gist options
  • Save cpudney/1509502 to your computer and use it in GitHub Desktop.
Save cpudney/1509502 to your computer and use it in GitHub Desktop.
D3 Lap Chart
{
"lapCount": 58,
"laps": [
{
"name": "Sebastian Vettel",
"placing": [1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
"pitstops": [9],
"mechanical": [25]
},
{
"name": "Mark Webber",
"placing": [2, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 5, 5, 5, 5, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 9, 9],
"pitstops": [10, 32, 56]
},
{
"name": "Fernando Alonso",
"placing": [3, 18, 18, 18, 18, 15, 13, 13, 15, 13, 10, 10, 10, 9, 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
"pitstops": [8],
"accident": [0]
},
{
"name": "Jenson Button",
"placing": [4, 6, 6, 6, 6, 6, 19, 19, 12, 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
"pitstops": [6, 34],
"accident": [0]
},
{
"name": "Felipe Massa",
"placing": [5, 2, 2, 2, 2, 2, 3, 3, 9, 7, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 5, 5, 6, 6, 6, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
"pitstops": [8]
},
{
"name": "Nico Rosberg",
"placing": [6, 5, 5, 5, 5, 5, 5, 5, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5],
"pitstops": [8, 33]
},
{
"name": "Michael Schumacher",
"placing": [7],
"accident": [0]
},
{
"name": "Rubens Barrichello",
"placing": [8, 9, 9, 9, 9, 9, 8, 7, 10, 8, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8],
"pitstops": [8, 31],
"accident": [55]
},
{
"name": "Robert Kubica",
"placing": [9, 4, 4, 4, 4, 4, 4, 4, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
"pitstops": [8]
},
{
"name": "Adrian Sutil",
"placing": [10, 8, 8, 8, 8, 8, 7, 9, 5, 2],
"mechanical": [9]
},
{
"name": "Lewis Hamilton",
"placing": [11, 7, 7, 7, 7, 7, 6, 6, 11, 11, 8, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6],
"pitstops": [8, 34],
"accident": [55]
},
{
"name": "Sebastien Buemi",
"placing": [12],
"accident": [0]
},
{
"name": "Vitantonio Liuzzi",
"placing": [13, 12, 12, 12, 12, 12, 11, 11, 3, 9, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7],
"pitstops": [9]
},
{
"name": "Pedro De La Rosa",
"placing": [14, 11, 11, 11, 11, 11, 10, 10, 13, 12, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 12, 12],
"pitstops": [8]
},
{
"name": "Nico Hulkenberg",
"placing": [15],
"accident": [0]
},
{
"name": "Kamui Kobayashi",
"placing": [16],
"accident": [0]
},
{
"name": "Jaime Alguersuari",
"placing": [17, 13, 13, 13, 13, 13, 12, 12, 4, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 11, 11],
"pitstops": [9, 27]
},
{
"name": "Vitaly Petrov",
"placing": [18, 10, 10, 10, 10, 10, 9, 8, 14, 14],
"pitstops": [8],
"accident": [9]
},
{
"name": "Heikki Kovalainen",
"placing": [19, 15, 15, 15, 14, 14, 14, 15, 18, 18, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13],
"pitstops": [8]
},
{
"name": "Jarno Trulli",
"placing": [20, 20, 20, 20, 20, 17, 15, 14, 17, 15, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10],
"pitstops": [1, 8, 29],
"mechanical": [0]
},
{
"name": "Bruno Senna",
"placing": [21, 14, 14, 14, 15],
"mechanical": [4]
},
{
"name": "Karun Chandock",
"placing": [22, 16, 16, 16, 16, 19, 18, 18, 19, 19, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 16, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14],
"pitstops": [8, 46]
},
{
"name": "Timo Glock",
"placing": [23, 17, 17, 17, 17, 16, 16, 16, 8, 17, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14],
"pitstops": [8, 9],
"mechanical": [41]
},
{
"name": "Luca di Grassi",
"placing": [24, 19, 19, 19, 19, 18, 17, 17, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 17, 16],
"pitstops": [10, 25],
"mechanical": [26]
}
],
"lapped": [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 17, 16, 16, 16, 16, 16, 16, 16,15, 15, 15, 14, 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13],
"safety": [1, 2, 3, 4]
}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Formula 1 Lap Chart</title>
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<link href="style.css" media="screen" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.7.0"></script>
</head>
<body>
<span class="title">Australian Formula 1 Grand Prix, 2010
<a href="http://creativecommons.org/licenses/by-sa/3.0/" target="_blank"><img align="top"
alt="Creative Commons License"
src="http://i.creativecommons.org/l/by-sa/3.0/80x15.png"/></a>
</span>
<span class="attrib">By <a href="http://vislives.com/" target="_blank">Chris Pudney</a></span>
<div id="chart"></div>
<span class="legend">M = mechanical failure | P = pit stop | X = accident | <span
class="safety">safety-car deployed</span> | <span class="lapped">lapped</span></span>
<script src="lap-chart.js" type="text/javascript"></script>
</body>
</html>
// Dimensions.
const DIMENSIONS = getWindowDimensions();
const WIDTH = DIMENSIONS.width;
const HEIGHT = DIMENSIONS.height - 100;
// Insets.
const INSETS = {'left': 150, 'right': 150, 'top': 30, 'bottom': 30};
// Padding.
const PADDING = {'left': 20, 'right': 20, 'top': 15, 'bottom': 15};
// Tick-mark length.
const TICK_MARK_LENGTH = 8;
// Marker radius.
const MARKER_RADIUS = 12;
// Scales.
const SCALES = {};
// Opacity of dimmed objects.
var DIMMED_OPACITY = 0.3;
var HIGHLIGHT_OPACITY = 1.0;
// Visualize when document has loaded.
//
window.onload = function() {
// Load data.
d3.json("2010au.json", function(data) {
// Check integrity.
integrityCheck(data);
// Sort laps on finishing order.
data.laps.sort(function(a, b) {
var aLaps = a.placing.length;
var bLaps = b.placing.length;
return aLaps == bLaps ? a.placing[aLaps - 1] - b.placing[bLaps - 1] : bLaps - aLaps;
});
// Process lap markers..
data.pitstops = processLapMarkers(data, "pitstops");
data.mechanical = processLapMarkers(data, "mechanical");
data.accident = processLapMarkers(data, "accident");
// Visualize the data.
visualize(data);
});
};
// Check data.
//
// data: the data to check.
//
function integrityCheck(data) {
var laps = data.laps;
var lapCount = data.lapCount;
// Check lap data.
checkLaps(laps, lapCount);
// Check lapped data.
checkLapped(data.lapped, lapCount, laps.length);
// Check safety car data.
checkSafetyCar(data.safety, lapCount);
}
// Check lap data.
//
// laps: the lap data.
// lapCount: number of laps.
//
function checkLaps(laps, lapCount) {
for (var j = 0;
j < laps.length;
j++) {
// Has name?
var name = laps[j].name;
if (name == undefined || name.length == 0) {
alert("Warning: invalid name for element " + j);
}
// Has placings?
var places = laps[j].placing;
if (places == undefined) {
alert("Warning: missing placings for element " + j + " (" + name + ")");
}
else if (places.length == 0 || places.length > lapCount + 1) {
alert("Warning: invalid number of placings (" + places.length + ") for element " + j +
" (" + name + ") - expected between 1 and " + (lapCount - 1));
}
// Check markers.
var maxLaps = places.length;
checkMarker(laps[j].pitstops, "pitstop", maxLaps, j, name);
checkMarker(laps[j].mechanical, "mechanical", maxLaps, j, name);
checkMarker(laps[j].accident, "accident", maxLaps, j, name);
}
for (var i = 0;
i < lapCount;
i++) {
var positions = [];
for (j = 0;
j < laps.length;
j++) {
places = laps[j].placing;
if (places.length > i) {
// Valid placing?
var placing = places[i];
if (isNaN(placing) || placing < 1 || placing % 1 != 0) {
alert("Warning: invalid placing '" + placing + "' for " + laps[j].name)
}
else {
var count = positions[placing];
positions[placing] = isNaN(count) ? 1 : count + 1
}
}
}
// Check for duplicate/missing positions.
for (j = 1;
j < positions.length;
j++) {
count = positions[j];
if (count != 1) {
alert("Warning: data inconsistent: lap " + i + ", position " + j + ", count " + count);
}
}
}
}
// Check integrity of marker data.
//
// marker: marker data.
// name: driver name.
// type: text description of marker.
// max: maximum allowed lap value of marker.
// index: index of driver in list.
//
function checkMarker(marker, type, max, index, name) {
if (marker != undefined) {
// Check marker.
for (var i = 0;
i < marker.length;
i++) {
var stop = marker[i];
if (isNaN(stop) || stop < 0 || stop >= max || stop % 1 != 0) {
alert("Warning: invalid " + type + " (" + stop + ") for element " + index + " (" + name + ")");
}
}
}
}
// Check lapped data.
//
// lapped: the lapped data.
// lapCount: number of laps.
// driverCount: number of drivers.
//
function checkLapped(lapped, lapCount, driverCount) {
if (lapped != undefined) {
var lappedLength = lapped.length;
if (lappedLength != lapCount) {
alert("Lapped array length (" + lappedLength + ") incorrect - expected length " + lapCount);
}
for (var j = 1;
j < lappedLength;
j++) {
// Valid position.
var position = lapped[j];
if (isNaN(position) || position % 1 != 0 || position < -1 || position > driverCount) {
alert("Invalid lapped position: element " + j + " (" + position
+ "); expected integer between -1 and " + driverCount);
}
}
}
}
// Check safety car data.
//
// safety: safety car data.
// lapCount: number of laps.
//
function checkSafetyCar(safety, lapCount) {
if (safety != undefined) {
for (var i = 0;
i < safety.length;
i++) {
// Valid lap?
var lap = safety[i];
if (isNaN(lap) || lap < 0 || lap % 1 != 0 || lap > lapCount) {
alert("Invalid safety car lap: element " + i + " (" + lap
+ "); expected integer between 0 and " + lapCount);
}
}
}
}
// Process lap markers.
//
// data: lap data.
// key: marker key.
//
function processLapMarkers(data, key) {
var markers = [];
var p = 0;
for (var i = 0;
i < data.laps.length;
i++) {
var lapData = data.laps[i];
var laps = lapData[key];
if (laps != undefined) {
for (var j = 0;
j < laps.length;
j++) {
var lap = laps[j];
var marker = {};
marker.start = lapData.placing[0];
marker.lap = lap;
marker.placing = lapData.placing[lap];
marker.name = lapData.name;
markers[p++] = marker;
}
}
}
return markers;
}
// Create the visualization.
//
// data the lap data object.
//
function visualize(data) {
// Configure scales.
configureScales(data);
// Root panel.
var vis = d3.select('#chart')
.append('svg:svg')
.attr('width', WIDTH)
.attr('height', HEIGHT);
// Add safety car element.
addSafetyElement(vis, data.safety);
// Add lapped element.
addLappedElement(vis, data.lapped);
// Lap tick-lines.
addLapTickLines(vis, data.lapCount);
// Lap labels.
addLapLabels(vis, data.lapCount, SCALES.y.range()[0] - PADDING.bottom, '0.0em', 'top');
addLapLabels(vis, data.lapCount, SCALES.y.range()[1] + PADDING.top, '0.35em', 'bottom');
// Add placings poly-lines.
addPlacingsLines(vis, data.laps);
// Add name labels.
addDriverLabels(vis, data.laps, 'pole', SCALES.x(0) - PADDING.right, 'end')
.attr('y', function (d) {
return SCALES.y(d.placing[0] - 1);
});
addDriverLabels(vis, data.laps, 'flag', SCALES.x(data.lapCount) + PADDING.left, 'start')
.attr('y', function (d, i) {
return SCALES.y(i);
});
// Add markers.
addMarkers(vis, data.pitstops, "pitstop", "P");
addMarkers(vis, data.mechanical, "mechanical", "M");
addMarkers(vis, data.accident, "accident", "X");
}
// Configure the scales.
//
// data: data set.
//
function configureScales(data) {
SCALES.x = d3.scale.linear()
.domain([0, data.lapCount])
.range([INSETS.left, WIDTH - INSETS.right]);
SCALES.y = d3.scale.linear()
.domain([0, data.laps.length - 1])
.range([INSETS.top, HEIGHT - INSETS.bottom]);
SCALES.clr = d3.scale.category20();
}
// Highlight driver.
//
// vis: the data visualization root.
// index: index of driver to highlight.
//
function highlight(vis, name) {
// Dim others.
vis.selectAll('polyline')
.style('opacity', function(d) {
return d.name == name ? HIGHLIGHT_OPACITY : DIMMED_OPACITY;
});
vis.selectAll('circle')
.style('opacity', function(d) {
return d.name == name ? HIGHLIGHT_OPACITY : DIMMED_OPACITY;
});
vis.selectAll('text.label')
.style('opacity', function(d) {
return d.name == name ? HIGHLIGHT_OPACITY : DIMMED_OPACITY;
});
}
// Remove highlights.
//
// vis: the data visualization root.
//
function unhighlight(vis) {
// Reset opacity.
vis.selectAll('polyline')
.style('opacity', HIGHLIGHT_OPACITY);
vis.selectAll('circle')
.style('opacity', HIGHLIGHT_OPACITY);
vis.selectAll('text.label')
.style('opacity', HIGHLIGHT_OPACITY);
}
// Add safety car laps (rectanle elements).
//
// vis: the data visualization root.
// data: safety car laps.
//
function addSafetyElement(vis, data) {
if (data != undefined) {
var y = SCALES.y.range()[0];
var height = SCALES.y.range()[1] - y;
var width = SCALES.x(1) - SCALES.x(0);
vis.selectAll('rect.safety')
.data(data)
.enter()
.append('svg:rect')
.attr('class', 'safety')
.attr('x', function(d) {
return SCALES.x(d - 0.5);
})
.attr('y', function() {
return y;
})
.attr('height', function() {
return height;
})
.attr('width', function() {
return width;
});
}
}
// Add lapped rectangle elements.
//
// vis: the data visualization root.
// data: the lapped data.
//
function addLappedElement(vis, data) {
if (data != undefined) {
var width = SCALES.x(1) - SCALES.x(0);
vis.selectAll('rect.lapped')
.data(data)
.enter()
.append('svg:rect')
.attr('class', 'lapped')
.attr('x', function(d, i) {
return SCALES.x(i + 0.5);
})
.attr('y', function(d) {
return SCALES.y(d > 0 ? d - 1.5 : 0);
})
.attr('height', function(d) {
return d > 0 ? SCALES.y.range()[1] - SCALES.y(d - 1.5) : 0;
})
.attr('width', function(d) {
return d > 0 ? width : 0;
});
}
}
// Add lap tick-lines.
//
// vis: the data visualization root.
// lapCount: number of laps.
//
function addLapTickLines(vis, lapCount) {
vis.selectAll('line.tickLine')
.data(SCALES.x.ticks(lapCount))
.enter().append('svg:line')
.attr('class', 'tickLine')
.attr('x1', function(d) {
return SCALES.x(d + 0.5);
})
.attr('x2', function(d) {
return SCALES.x(d + 0.5);
})
.attr('y1', SCALES.y.range()[0] - TICK_MARK_LENGTH)
.attr('y2', SCALES.y.range()[1] + TICK_MARK_LENGTH)
.attr('visibility', function(d) {
return d <= lapCount ? 'visible' : 'hidden'
});
}
// Add lap labels.
//
// vis: the data visualization root.
// data: lap data.
// y: y position of labels.
// dy: y offset.
// cssClass: CSS class id.
//
function addLapLabels(vis, data, y, dy, cssClass) {
vis.selectAll('text.lap.' + cssClass)
.data(SCALES.x.ticks(data))
.enter().append('svg:text')
.attr('class', 'lap ' + cssClass)
.attr('x', function(d) {
return SCALES.x(d);
})
.attr('y', y)
.attr('dy', dy)
.attr('text-anchor', 'middle')
.text(function(d, i) {
return i > 0 ? i : '';
});
}
// Add placings polyline elements.
//
// vis: the visualization root.
// laps: lap data.
//
function addPlacingsLines(vis, laps) {
vis.selectAll('polyline.placing')
.data(laps)
.enter()
.append('svg:polyline')
.attr('class', 'placing')
.attr('points', function(d) {
var points = [];
for (var i = 0;
i < d.placing.length;
i++) {
points[i] = SCALES.x(i) + ',' + SCALES.y(d.placing[i] - 1);
}
if (points.length > 0)
points.push(SCALES.x(i - 0.5) + ',' + SCALES.y(d.placing[i - 1] - 1));
return points.join(' ');
})
.style('stroke', function(d) {
return SCALES.clr(d.placing[0]);
})
.on('mouseover', function(d) {
highlight(vis, d.name);
})
.on('mouseout', function() {
unhighlight(vis);
});
}
// Add driver name labels.
//
// vis: the data visualization root.
// laps: the lap data.
// cssClass: CSS class id.
// textAnchor: text-anchor value.
//
function addDriverLabels(vis, laps, cssClass, x, textAnchor) {
return vis.selectAll('text.label.' + cssClass)
.data(laps)
.enter()
.append('svg:text')
.attr('class', 'label ' + cssClass)
.attr('x', x)
.attr('dy', '0.35em')
.attr('text-anchor', textAnchor)
.text(function(d) {
return d.name;
})
.style('fill', function(d) {
return SCALES.clr(d.placing[0]);
})
.on('mouseover', function(d) {
highlight(vis, d.name);
})
.on('mouseout', function() {
unhighlight(vis);
});
}
// Add markers.
//
// vis: the visualization root.
// data: marker data.
// class: marker sub-class.
// label: marker label.
//
function addMarkers(vis, data, cssClass, label) {
label = label || "P";
// Place circle glyph.
vis.selectAll("circle.marker." + cssClass)
.data(data)
.enter()
.append("svg:circle")
.attr("class", "marker " + cssClass)
.attr("cx", function(d) {
return SCALES.x(d.lap);
})
.attr("cy", function(d) {
return SCALES.y(d.placing - 1);
})
.attr("r", MARKER_RADIUS)
.style("fill", function(d) {
return SCALES.clr(d.start);
})
.on('mouseover', function(d) {
highlight(vis, d.name);
})
.on('mouseout', function() {
unhighlight(vis);
});
// Place text.
vis.selectAll("text.label.marker" + cssClass)
.data(data)
.enter()
.append("svg:text")
.attr("class", "label marker" + cssClass)
.attr("x", function(d) {
return SCALES.x(d.lap);
})
.attr("y", function(d) {
return SCALES.y(d.placing - 1);
})
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(label)
.on('mouseover', function(d) {
highlight(vis, d.name);
})
.on('mouseout', function() {
unhighlight(vis);
});
}
// Gets the window dimensions.
//
function getWindowDimensions() {
var width = 630;
var height = 460;
if (document.body && document.body.offsetWidth) {
width = document.body.offsetWidth;
height = document.body.offsetHeight;
}
if (document.compatMode == 'CSS1Compat' && document.documentElement && document.documentElement.offsetWidth) {
width = document.documentElement.offsetWidth;
height = document.documentElement.offsetHeight;
}
if (window.innerWidth && window.innerHeight) {
width = window.innerWidth;
height = window.innerHeight;
}
return {'width': width, 'height': height};
}
body {
margin: 0;
padding: 0;
background-color: #000000;
font-family: sans-serif;
font-size: 12px;
color: #666666;
overflow: hidden;
}
a:link {
color: #ccccff;
}
a:visited {
color: #cccccc;
}
a:active {
color: #ffccff;
}
a.hover {
color: #ffcccc;
}
.title {
float: left;
font-size: 14px;
font-weight: bold;
color: #ffffff;
}
.attrib {
float: right;
font-size: 14px;
color: #ffffff;
}
text.lap {
fill: #999999;
}
text.label {
fill: #000000;
}
text.label.marker {
font-weight: bold;
}
polyline.placing {
fill: none;
stroke-width: 5;
}
line.tickLine {
stroke: #999999;
}
.lapped {
background-color: #333333;
stroke: #333333;
fill: #333333;
}
.safety{
background-color: #330000;
stroke: #330000;
fill: #330000;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment