diff --git a/www/points.json b/www/points.json
new file mode 100644
index 0000000..f840e8c
--- /dev/null
+++ b/www/points.json
@@ -0,0 +1,339 @@
+{
+ "points": [
+[1429583964, "1", "nocode", 1],
+[1429583994, "1", "nocode", 2],
+[1429583998, "2", "nocode", 1],
+[1429584021, "1", "nocode", 3],
+[1429584035, "1", "nocode", 4],
+[1429584085, "2", "js", 1],
+[1429584118, "3", "codebreaking", 1],
+[1429584201, "1", "nocode", 10],
+[1429584403, "1", "js", 1],
+[1429584466, "3", "net-re", 1],
+[1429584776, "4", "nocode", 1],
+[1429584851, "4", "nocode", 2],
+[1429585153, "4", "nocode", 3],
+[1429585279, "4", "nocode", 4],
+[1429585297, "5", "nocode", 1],
+[1429585326, "5", "nocode", 2],
+[1429585381, "6", "nocode", 1],
+[1429585386, "5", "nocode", 3],
+[1429585440, "6", "nocode", 2],
+[1429585521, "4", "nocode", 10],
+[1429585664, "6", "nocode", 3],
+[1429585689, "6", "nocode", 4],
+[1429585744, "6", "nocode", 10],
+[1429585819, "1", "net-re", 1],
+[1429586010, "3", "js", 1],
+[1429586189, "6", "js", 1],
+[1429586546, "7", "nocode", 1],
+[1429586574, "7", "nocode", 2],
+[1429586648, "7", "nocode", 3],
+[1429586656, "3", "js", 2],
+[1429586661, "8", "nocode", 2],
+[1429586700, "7", "nocode", 4],
+[1429586756, "7", "nocode", 10],
+[1429586770, "8", "nocode", 1],
+[1429586844, "8", "js", 1],
+[1429587055, "6", "net-re", 1],
+[1429587072, "3", "nocode", 1],
+[1429587088, "3", "nocode", 2],
+[1429587102, "3", "nocode", 3],
+[1429587117, "3", "nocode", 4],
+[1429587137, "3", "nocode", 10],
+[1429587268, "1", "codebreaking", 1],
+[1429587514, "3", "nocode", 20],
+[1429587821, "9", "nocode", 1],
+[1429587843, "9", "nocode", 2],
+[1429587892, "8", "codebreaking", 1],
+[1429587983, "9", "nocode", 3],
+[1429588070, "9", "nocode", 4],
+[1429588116, "1", "codebreaking", 2],
+[1429588141, "3", "net-re", 2],
+[1429588196, "4", "nocode", 20],
+[1429588236, "10", "nocode", 1],
+[1429588260, "10", "nocode", 2],
+[1429588376, "7", "nocode", 20],
+[1429588386, "10", "nocode", 3],
+[1429588519, "7", "js", 1],
+[1429588613, "10", "nocode", 4],
+[1429588960, "9", "nocode", 10],
+[1429589154, "11", "nocode", 1],
+[1429589189, "11", "nocode", 2],
+[1429589215, "11", "nocode", 3],
+[1429589325, "11", "nocode", 10],
+[1429589534, "11", "nocode", 4],
+[1429589605, "4", "bonus", 30],
+[1429589805, "3", "net-re", 3],
+[1429590136, "10", "net-re", 1],
+[1429589817, "10", "js", 1],
+[1429589867, "10", "codebreaking", 1],
+[1429590137, "9", "js", 1],
+[1429590009, "1", "net-re", 2],
+[1429590285, "10", "net-re", 2],
+[1429590406, "11", "js", 1],
+[1429590574, "4", "nocode", 30],
+[1429590610, "3", "codebreaking", 2],
+[1429590642, "9", "codebreaking", 1],
+[1429590666, "11", "nocode", 30],
+[1429590775, "3", "codebreaking", 4],
+[1429590900, "1", "net-re", 3],
+[1429590909, "11", "net-re", 1],
+[1429591066, "1", "bonus", 10],
+[1429591073, "11", "net-re", 2],
+[1429591159, "11", "net-re", 3],
+[1429591392, "1", "net-re", 4],
+[1429591600, "11", "net-re", 4],
+[1429591615, "11", "js", 2],
+[1429591620, "11", "codebreaking", 1],
+[1429591953, "3", "net-re", 4],
+[1429592104, "1", "net-re", 5],
+[1429592528, "12", "nocode", 1],
+[1429592555, "12", "nocode", 3],
+[1429592595, "12", "nocode", 2],
+[1429592634, "12", "nocode", 4],
+[1429592777, "1", "net-re", 6],
+[1429592818, "11", "codebreaking", 2],
+[1429592954, "12", "nocode", 10],
+[1429593070, "11", "codebreaking", 4],
+[1429593288, "11", "nocode", 20],
+[1429593393, "9", "nocode", 20],
+[1429593815, "11", "bonus", 10],
+[1429593885, "3", "net-re", 5],
+[1429594213, "12", "js", 1],
+[1429594226, "3", "net-re", 6],
+[1429594312, "12", "codebreaking", 1],
+[1429594645, "1", "bonus", 10],
+[1429594642, "12", "codebreaking", 2],
+[1429595082, "1", "net-re", 7],
+[1429595683, "3", "net-re", 7],
+[1429596041, "9", "nocode", 30],
+[1429596823, "3", "nocode", 50],
+[1429597683, "1", "net-re", 8],
+[1429594279, "10", "codebreaking", 2],
+[1429594357, "10", "codebreaking", 4],
+[1429594399, "10", "codebreaking", 5],
+[1429594404, "9", "net-re", 2],
+[1429594431, "9", "net-re", 1],
+[1429594458, "13", "nocode", 1],
+[1429594529, "13", "js", 1],
+[1429595408, "10", "net-re", 4],
+[1429595408, "13", "net-re", 1],
+[1429595431, "13", "codebreaking", 1],
+[1429595560, "13", "nocode", 2],
+[1429595692, "10", "codebreaking", 6],
+[1429596133, "14", "js", 1],
+[1429597686, "13", "codebreaking", 2],
+[1429597777, "13", "codebreaking", 4],
+[1429598384, "9", "net-re", 3],
+[1429598816, "14", "js", 2],
+[1429598849, "13", "js", 2],
+[1429599165, "15", "nocode", 1],
+[1429599169, "11", "net-re", 5],
+[1429599246, "15", "nocode", 2],
+[1429599491, "11", "net-re", 6],
+[1429599556, "14", "codebreaking", 1],
+[1429599570, "15", "nocode", 4],
+[1429599617, "15", "nocode", 3],
+[1429599687, "11", "net-re", 7],
+[1429599708, "15", "nocode", 10],
+[1429600044, "11", "net-re", 10],
+[1429600115, "11", "bonus", 10],
+[1429600186, "14", "codebreaking", 2],
+[1429600409, "11", "net-re", 20],
+[1429600635, "11", "net-re", 30],
+[1429600960, "15", "js", 1],
+[1429593857, "14", "codebreaking", 4],
+[1429594041, "16", "nocode", 1],
+[1429594056, "16", "nocode", 2],
+[1429594100, "16", "nocode", 3],
+[1429594113, "16", "nocode", 4],
+[1429594126, "14", "net-re", 1],
+[1429594238, "14", "net-re", 2],
+[1429594326, "9", "codebreaking", 2],
+[1429594335, "1", "net-re", 10],
+[1429594387, "16", "nocode", 10],
+[1429594721, "14", "nocode", 1],
+[1429594776, "14", "nocode", 2],
+[1429594989, "14", "nocode", 3],
+[1429595014, "14", "nocode", 4],
+[1429595105, "14", "nocode", 10],
+[1429595464, "15", "js", 2],
+[1429596277, "6", "codebreaking", 1],
+[1429596292, "14", "nocode", 20],
+[1429596364, "17", "nocode", 1],
+[1429596400, "17", "js", 1],
+[1429596776, "15", "net-re", 1],
+[1429597027, "15", "net-re", 2],
+[1429597044, "1", "net-re", 20],
+[1429597067, "16", "net-re", 20],
+[1429597214, "16", "js", 1],
+[1429597234, "16", "codebreaking", 1],
+[1429597287, "17", "nocode", 2],
+[1429597423, "16", "codebreaking", 2],
+[1429597488, "17", "nocode", 3],
+[1429597627, "17", "nocode", 4],
+[1429597659, "16", "codebreaking", 4],
+[1429597676, "1", "codebreaking", 4],
+[1429597757, "1", "codebreaking", 5],
+[1429597772, "16", "codebreaking", 5],
+[1429597844, "17", "nocode", 10],
+[1429597882, "1", "codebreaking", 6],
+[1429597895, "16", "codebreaking", 6],
+[1429598048, "1", "codebreaking", 7],
+[1429598063, "16", "codebreaking", 7],
+[1429598111, "1", "codebreaking", 8],
+[1429598123, "16", "codebreaking", 8],
+[1429598176, "16", "codebreaking", 20],
+[1429598188, "1", "codebreaking", 20],
+[1429598227, "14", "net-re", 4],
+[1429599449, "1", "nocode", 20],
+[1429599466, "16", "nocode", 20],
+[1429600544, "17", "nocode", 20],
+[1429600741, "14", "net-re", 5],
+[1429600892, "14", "net-re", 6],
+[1429601090, "11", "net-re", 100],
+[1429601316, "11", "net-re", 200],
+[1429601600, "11", "net-re", 250],
+[1429601700, "14", "net-re", 7],
+[1429601907, "11", "net-re", 300],
+[1429602270, "11", "js", 3],
+[1429602581, "14", "net-re", 10],
+[1429603096, "18", "codebreaking", 1],
+[1429603148, "18", "nocode", 1],
+[1429603173, "18", "nocode", 2],
+[1429603273, "18", "js", 1],
+[1429603277, "18", "nocode", 3],
+[1429603325, "18", "nocode", 4],
+[1429603478, "9", "codebreaking", 4],
+[1429603509, "18", "nocode", 10],
+[1429603853, "9", "codebreaking", 5],
+[1429603979, "18", "codebreaking", 2],
+[1429604211, "18", "codebreaking", 4],
+[1429604515, "9", "codebreaking", 6],
+[1429604535, "18", "codebreaking", 5],
+[1429605613, "15", "net-re", 4],
+[1429605764, "13", "nocode", 4],
+[1429605779, "3", "nocode", 30],
+[1429606493, "13", "nocode", 10],
+[1429606596, "3", "nocode", 80],
+[1429606712, "11", "nocode", 50],
+[1429606728, "3", "nocode", 90],
+[1429606762, "11", "nocode", 80],
+[1429606877, "16", "nocode", 50],
+[1429606962, "16", "nocode", 30],
+[1429607144, "4", "nocode", 50],
+[1429607428, "19", "nocode", 1],
+[1429607453, "19", "nocode", 2],
+[1429607877, "11", "nocode", 90],
+[1429607943, "19", "nocode", 3],
+[1429607997, "19", "nocode", 4],
+[1429608039, "16", "nocode", 80],
+[1429608054, "19", "js", 1],
+[1429608083, "19", "nocode", 10],
+[1429608226, "16", "nocode", 90],
+[1429608430, "13", "nocode", 3],
+[1429608475, "3", "codebreaking", 7],
+[1429608514, "20", "nocode", 1],
+[1429608537, "20", "nocode", 2],
+[1429608614, "20", "nocode", 4],
+[1429608689, "19", "nocode", 20],
+[1429608707, "20", "js", 1],
+[1429608843, "3", "net-re", 250],
+[1429609088, "20", "net-re", 1],
+[1429609128, "20", "codebreaking", 1],
+[1429609272, "19", "codebreaking", 1],
+[1429609341, "20", "nocode", 10],
+[1429609586, "19", "net-re", 1],
+[1429609876, "19", "nocode", 30],
+[1429609981, "16", "net-re", 2],
+[1429610335, "16", "net-re", 1],
+[1429610755, "20", "js", 2],
+[1429610777, "19", "js", 2],
+[1429610922, "1", "js", 2],
+[1429611472, "16", "js", 2],
+[1429611695, "3", "net-re", 400],
+[1429612382, "13", "net-re", 250],
+[1429612600, "20", "codebreaking", 7],
+[1429612771, "16", "nocode", 100],
+[1429612773, "1", "nocode", 100],
+[1429612818, "16", "bonus", 10],
+[1429612825, "1", "bonus", 10],
+[1429613065, "1", "net-re", 200],
+[1429613837, "10", "net-re", 200],
+[1429613970, "1", "net-re", 250],
+[1429614300, "13", "net-re", 10],
+[1429614363, "16", "net-re", 3],
+[1429614610, "10", "codebreaking", 20],
+[1429614873, "13", "net-re", 20],
+[1429614939, "16", "net-re", 4],
+[1429615440, "16", "net-re", 5],
+[1429594321, "10", "net-re", 100],
+[1429593963, "10", "net-re", 250],
+[1429598332, "16", "net-re", 6],
+[1429598544, "21", "nocode", 1],
+[1429598631, "21", "nocode", 4],
+[1429598680, "21", "nocode", 10],
+[1429598834, "21", "nocode", 20],
+[1429598884, "21", "nocode", 30],
+[1429598978, "21", "codebreaking", 1],
+[1429598994, "11", "codebreaking", 6],
+[1429599012, "11", "codebreaking", 7],
+[1429599029, "11", "codebreaking", 8],
+[1429599154, "21", "codebreaking", 2],
+[1429599164, "11", "net-re", 400],
+[1429599813, "21", "codebreaking", 4],
+[1429600483, "11", "js", 10],
+[1429600712, "21", "codebreaking", 5],
+[1429601019, "21", "js", 1],
+[1429601089, "21", "js", 2],
+[1429601324, "21", "nocode", 2],
+[1429601491, "21", "nocode", 3],
+[1429601511, "22", "codebreaking", 1],
+[1429602117, "11", "nocode", 100],
+[1429602389, "1", "net-re", 100],
+[1429602462, "23", "nocode", 200],
+[1429602515, "23", "nocode", 1],
+[1429602525, "23", "nocode", 2],
+[1429602540, "23", "nocode", 3],
+[1429602550, "23", "nocode", 4],
+[1429602582, "23", "nocode", 10],
+[1429602685, "23", "codebreaking", 1],
+[1429602737, "21", "nocode", 200],
+[1429602747, "23", "codebreaking", 2],
+[1429602767, "23", "codebreaking", 4],
+[1429602824, "11", "net-re", 700],
+[1429602900, "16", "net-re", 100],
+[1429602990, "16", "net-re", 200],
+[1429603039, "22", "nocode", 200],
+[1429603308, "23", "codebreaking", 5],
+[1429603374, "23", "js", 1],
+[1429603607, "16", "net-re", 250],
+[1429603931, "11", "net-re", 800]
+ ],
+ "teams": {
+"1": "INNSOC",
+"2": "Lost",
+"3": "TeamSoloTF",
+"4": "BEnergy",
+"5": "Dreamweaver",
+"6": "Ghirahim",
+"7": "Awesome ",
+"8": "sn3",
+"9": "CybrK0pS",
+"10": "dio1911",
+"11": "InvaderZed",
+"12": "Energy.gov",
+"13": "PPPL",
+"14": "OR_Ninjas",
+"15": "Placebo",
+"16": "WiFI-Ninja",
+"17": "OST-1",
+"18": "WestCoast3",
+"19": "Dathcha",
+"20": "Last Mohigan",
+"21": "cloud",
+"22": "Stephen",
+"23": "Prima"
+ }
+}
diff --git a/www/scoreboard-llnl-all.html b/www/scoreboard-llnl-all.html
new file mode 100644
index 0000000..4050e3f
--- /dev/null
+++ b/www/scoreboard-llnl-all.html
@@ -0,0 +1,50 @@
+
+
+
+ Scoreboard
+
+
+
+
+
+
+
+ Scoreboard
+
+
+
+
diff --git a/www/scoreboard-llnl-timeline.html b/www/scoreboard-llnl-timeline.html
new file mode 100644
index 0000000..3943290
--- /dev/null
+++ b/www/scoreboard-llnl-timeline.html
@@ -0,0 +1,50 @@
+
+
+
+ Scoreboard
+
+
+
+
+
+
+
+ Scoreboard
+
+
+
+
diff --git a/www/scoreboard-llnl.js b/www/scoreboard-llnl.js
new file mode 100644
index 0000000..62b14e8
--- /dev/null
+++ b/www/scoreboard-llnl.js
@@ -0,0 +1,531 @@
+function loadJSON(url, callback) {
+ function loaded(e) {
+ callback(e.target.response);
+ }
+ var xhr = new XMLHttpRequest()
+ xhr.onload = loaded;
+ xhr.open("GET", url, true);
+ xhr.responseType = "json";
+ xhr.send();
+}
+
+function toObject(arr) {
+ var rv = {};
+ for (var i = 0; i < arr.length; ++i)
+ if (arr[i] !== undefined) rv[i] = arr[i];
+ return rv;
+}
+
+var updateInterval;
+
+function scoreboard(element, continuous, mode, interval) {
+ if(updateInterval)
+ {
+ clearInterval(updateInterval);
+ }
+ function update(state) {
+ console.log("Updating");
+ var teamnames = state["teams"];
+ var pointslog = state["points"];
+ var highscore = {};
+ var teams = {};
+
+ function pointsCompare(a, b) {
+ return a[0] - b[0];
+ }
+ pointslog.sort(pointsCompare);
+ var minTime = pointslog[0][0];
+ var maxTime = pointslog[pointslog.length - 1][0];
+
+ var allQuestions = {};
+
+ for (var i in pointslog)
+ {
+ var entry = pointslog[i];
+ var timestamp = entry[0];
+ var teamhash = entry[1];
+ var category = entry[2];
+ var points = entry[3];
+
+ var catPoints = {};
+ if(category in allQuestions)
+ {
+ catPoints = allQuestions[category];
+ }
+ else
+ {
+ catPoints["total"] = 0;
+ }
+
+ if(!(points in catPoints))
+ {
+ catPoints[points] = 1;
+ catPoints["total"] = catPoints["total"] + points;
+ }
+ else
+ {
+ catPoints[points] = catPoints[points] + 1;
+ }
+
+ allQuestions[category] = catPoints;
+ }
+
+ // Dole out points
+ for (var i in pointslog) {
+ var entry = pointslog[i];
+ var timestamp = entry[0];
+ var teamhash = entry[1];
+ var category = entry[2];
+ var points = entry[3];
+
+ var team = teams[teamhash] || {__hash__: teamhash};
+
+ // Add points to team's points for that category
+ team[category] = (team[category] || 0) + points;
+
+ // Record highest score in a category
+ highscore[category] = Math.max(highscore[category] || 0, team[category]);
+
+ teams[teamhash] = team;
+ }
+
+ // Sort by team score
+ function teamScore(t) {
+ var score = 0;
+
+ for (var category in highscore) {
+ score += (t[category] || 0) / highscore[category];
+ }
+ // XXX: This function really shouldn't have side effects.
+ t.__score__ = score;
+ return score;
+ }
+ function pointScore(points, category)
+ {
+ return points / highscore[category]
+ }
+ function teamCompare(a, b) {
+ return teamScore(a) - teamScore(b);
+ }
+
+ var winners = [];
+ for (var i in teams) {
+ winners.push(teams[i]);
+ }
+ if (winners.length == 0) {
+ // No teams!
+ return;
+ }
+ winners.sort(teamCompare);
+ winners.reverse();
+
+ // Clear out the element we're about to populate
+ while (element.lastChild) {
+ element.removeChild(element.lastChild);
+ }
+
+ // Populate!
+ var topActualScore = winners[0].__score__;
+
+
+ if(mode == "time")
+ {
+ var colorScale = d3.schemeCategory10;
+
+ var teamLines = {};
+ var reverseTeam = {};
+ for(var i in pointslog)
+ {
+ var entry = pointslog[i];
+ var timestamp = entry[0];
+ var teamhash = entry[1];
+ var category = entry[2];
+ var points = entry[3];
+ var teamname = teamnames[teamhash];
+ reverseTeam[teamname] = teamhash;
+ points = pointScore(points, category);
+
+ if(!(teamname in teamLines))
+ {
+ var teamHistory = [[timestamp, points, category, entry[3], [minTime, 0, category, 0]]];
+ teamLines[teamname] = teamHistory;
+ }
+ else
+ {
+ var teamHistory = teamLines[teamname];
+ teamHistory.push([timestamp, points + teamHistory[teamHistory.length - 1][1], category, entry[3], teamHistory[teamHistory.length - 1]]);
+ }
+ }
+
+ //console.log(teamLines);
+
+ var graph = document.createElement("svg");
+ graph.id = "graph";
+ graph.style.width="90%";
+ graph.style.height="40em";
+ graph.style.backgroundColor = "white";
+ graph.style.display = "table";
+ var holdingDiv = document.createElement("div");
+ holdingDiv.align="center";
+ holdingDiv.id="holding";
+ element.appendChild(holdingDiv);
+ holdingDiv.appendChild(graph);
+
+ var margins = 40;
+
+ var width = graph.offsetWidth;
+ var height = graph.offsetHeight;
+
+ //var xScale = d3.scaleLinear().range([minTime, maxTime]);
+ //var yScale = d3.scaleLinear().range([0, topActualScore]);
+ var originTime = (maxTime - minTime) / 60;
+ var xScale = d3.scaleLinear().range([margins, width - margins]);
+ xScale.domain([0, originTime]);
+ var yScale = d3.scaleLinear().range([height - margins, margins]);
+ yScale.domain([0, topActualScore]);
+
+ graph = d3.select("#graph");
+ graph.remove();
+ graph = d3.select("#holding").append("svg")
+ .attr("width", width)
+ .attr("height", height);
+ //.attr("style", "background: white");
+
+
+ //graph.append("g")
+ // .attr("transform", "translate(" + margins + ", 0)")
+ // .call(d3.axisLeft(yScale))
+ // .style("stroke", "white");;
+
+ var maxNumEntry = 10;
+ //var curEntry = 0;
+ var winningTeams = [];
+ for(entry in winners)
+ {
+ var curEntry = entry;
+ if(curEntry >= maxNumEntry)
+ {
+ break;
+ }
+ entry = teamnames[winners[entry].__hash__];
+ winningTeams.push(entry);
+ //console.log(curEntry);
+ //console.log(entry);
+
+ //var isTop = false;
+ //for(var x=0; x < maxNumEntry; x++)
+ //{
+ // var teamhash = reverseTeam[entry];
+ // if(winners[x].__hash__ == teamhash)
+ // {
+ // curEntry = x;
+ // isTop = true;
+ // break;
+ // }
+ //}
+ //if(!isTop)
+ //{
+ // continue;
+ //}
+
+ var curTeam = teamLines[entry];
+ var lastEntry = curTeam[curTeam.length - 1];
+ //curTeam.append()
+ curTeam.push([maxTime, lastEntry[1], lastEntry[2], lastEntry[3], lastEntry]);
+ var curLayer = graph.append("g");
+ curLayer.selectAll("line")
+ .data(curTeam)
+ .enter()
+ .append("line")
+ .style("stroke", colorScale[curEntry])
+ .attr("stroke-width", 4)
+ .attr("class", "team_" + entry)
+ .style("z-index", maxNumEntry - curEntry)
+ .attr("x1",
+ function(d)
+ {
+ return xScale((d[4][0] - minTime) / 60);
+ })
+ .attr("x2",
+ function(d)
+ {
+ return xScale((d[0] - minTime) / 60);
+ })
+ .attr("y1",
+ function(d)
+ {
+ return yScale(d[4][1]);
+ })
+ .attr("y2",
+ function(d)
+ {
+ return yScale(d[1]);
+ })
+ .on("mouseover", handleMouseover)
+ .on("mouseout", handleMouseout);
+
+ curLayer.selectAll("circle")
+ .data(curTeam)
+ .enter()
+ .append("circle")
+ .style("fill", colorScale[curEntry])
+ .style("z-index", maxNumEntry - curEntry)
+ .attr("class", "team_" + entry)
+ .attr("r", 5)
+ .attr("cx",
+ function(d)
+ {
+ return xScale((d[0] - minTime) / 60);
+ })
+ .attr("cy",
+ function(d)
+ {
+ return yScale(d[1]);
+ })
+ .on("mouseover", handleMouseoverCircle)
+ .on("mouseout", handleMouseoutCircle);
+
+ curEntry++;
+ }
+
+ var axisG = graph.append("g");
+ axisG
+ .attr("transform", "translate(0," + (height - margins) + ")")
+ .call(d3.axisBottom(xScale));
+ //.style("stroke", "white");
+ axisG.selectAll("path").style("stroke", "white");
+ axisG.selectAll("line").style("stroke", "white");
+ axisG.selectAll("text").style("fill", "white");
+
+ graph.append("text")
+ .attr("text-anchor", "middle")
+ .attr("transform", "translate(" + (width / 2) + ", " + (height - margins / 8) + ")")
+ .style("fill", "white")
+ .text("Time (minutes)");
+
+ var legend = graph.append("g");
+ var legendRowHeight = 40;
+ legend.selectAll("rect")
+ .data(winningTeams)
+ .enter()
+ .append("rect")
+ .attr("class", function(d){ return "team_" + d; })
+ .attr("fill", function(d, i){ return colorScale[i]; })
+ .style("z-index", function(d, i){ return i; })
+ .attr("x", margins)
+ .attr("y", function(d, i){ return margins + legendRowHeight * i; })
+ .attr("height", legendRowHeight)
+ .attr("width", 150)
+ .on("mouseover", handleMouseoverLegend)
+ .on("mouseout", handleMouseoutLegend);
+
+ legend.selectAll("text")
+ .data(winningTeams)
+ .enter()
+ .append("text")
+ //.attr("class", function(d){ return "team_" + d; })
+ .attr("fill", "black")
+ .style("z-index", function(d, i){ return i; })
+ .attr("dx", margins)
+ .attr("dy", function(d, i){ return margins + legendRowHeight * (i + .5); })
+ .text(function(d){ return d; })
+ .attr("dominant-baseline", "central")
+ .style("pointer-events", "none");
+
+
+ function handleMouseover(d, i)
+ {
+ d3.select("body").selectAll(".tooltip").remove();
+ var curClass = d3.select(this).attr("class");
+ d3.select("body").selectAll("." + curClass)
+ .style("stroke", "white")
+ .style("fill", "white");
+ d3.select("body").selectAll("text")
+ .style("stroke-width", 0);
+ }
+
+ function handleMouseout(d, i)
+ {
+ d3.select("body").selectAll(".tooltip").remove();
+ var curClass = d3.select(this).attr("class");
+ var zIndex = d3.select(this).style("z-index");
+ d3.select("body").selectAll("." + curClass)
+ .style("stroke", colorScale[maxNumEntry - zIndex])
+ .style("fill", colorScale[maxNumEntry - zIndex]);
+ d3.select("body").selectAll("text")
+ .style("stroke-width", 0);
+ }
+
+ var tooltipPadding = 10;
+ function handleMouseoverCircle(d, i)
+ {
+ d3.select("body").selectAll(".tooltip").remove();
+ var curClass = d3.select(this).attr("class");
+ d3.select("body").selectAll("." + curClass)
+ .style("stroke", "white")
+ .style("fill", "white");
+ d3.select("body").selectAll("text")
+ .style("stroke-width", 0);
+
+ graph.append("g").append("text")
+ .attr("class", "tooltip")
+ .attr("text-anchor", "middle")
+ .style("fill", "red")
+ .style("stroke-width", -4)
+ .style("stroke", "black")
+ .style("font-weight", "bolder")
+ .style("font-size", "large")
+ .attr("dx",
+ function()
+ {
+ return xScale((d[0] - minTime) / 60);
+ })
+ .attr("dy",
+ function()
+ {
+ return yScale(d[1]) - tooltipPadding;
+ })
+ .text(function(){ return d[2] + " " + d[3]; })
+ .style("pointer-events", "none");
+
+ }
+
+ function handleMouseoutCircle(d, i)
+ {
+ d3.select("body").selectAll(".tooltip").remove();
+ var curClass = d3.select(this).attr("class");
+ var zIndex = d3.select(this).style("z-index");
+ d3.select("body").selectAll("." + curClass)
+ .style("stroke", colorScale[maxNumEntry - zIndex])
+ .style("fill", colorScale[maxNumEntry - zIndex]);
+ d3.select("body").selectAll("text")
+ .style("stroke-width", 0);
+ }
+
+ function handleMouseoverLegend(d, i)
+ {
+ d3.select("body").selectAll(".tooltip").remove();
+ var curClass = d3.select(this).attr("class");
+ d3.select("body").selectAll("." + curClass)
+ .style("stroke", "white")
+ .style("fill", "white");
+ d3.select("body").selectAll("text")
+ .style("stroke-width", 0);
+ }
+
+ function handleMouseoutLegend(d, i)
+ {
+ d3.select("body").selectAll(".tooltip").remove();
+ var curClass = d3.select(this).attr("class");
+ var zIndex = d3.select(this).style("z-index");
+ d3.select("body").selectAll("." + curClass)
+ .style("stroke", colorScale[zIndex])
+ .style("fill", colorScale[zIndex]);
+ d3.select("body").selectAll("text")
+ .style("stroke-width", 0);
+ }
+
+
+ }
+ else if(mode == "original")
+ {
+ // (100 / ncats) * (ncats / topActualScore);
+ var maxWidth = 100 / topActualScore;
+ for (var i in winners) {
+ var team = winners[i];
+ var row = document.createElement("div");
+ var ncat = 0;
+ for (var category in highscore) {
+ var catHigh = highscore[category];
+ var catTeam = team[category] || 0;
+ var catPct = catTeam / catHigh;
+ var width = maxWidth * catPct;
+
+ var bar = document.createElement("span");
+ bar.classList.add("cat" + ncat);
+ bar.style.width = width + "%";
+ bar.textContent = category + ": " + catTeam;
+ bar.title = bar.textContent;
+
+ row.appendChild(bar);
+ ncat += 1;
+ }
+
+ var te = document.createElement("span");
+ te.classList.add("teamname");
+ te.textContent = teamnames[team.__hash__];
+ row.appendChild(te);
+
+ element.appendChild(row);
+ }
+ }
+ if(mode == "total")
+ {
+ var colorScale = d3.schemeCategory20;
+
+ var numCats = 0;
+ for(entry in allQuestions)
+ {
+ numCats++;
+ }
+ var maxWidth = 100 / (0.0 + numCats);
+ //console.log(maxWidth);
+
+ for (var i in winners) {
+ var team = winners[i];
+ var row = document.createElement("div");
+ var ncat = 0;
+ for (var category in allQuestions) {
+ var catHigh = allQuestions[category];
+ var catTeam = team[category] || 0;
+ var catPct = (0.0 + catTeam) / (0.0 + catHigh["total"]);
+ var width = maxWidth * catPct;
+ var bar = document.createElement("span");
+
+ var numLeft = catHigh["total"] - catTeam;
+
+ //bar.classList.add("cat" + ncat);
+ bar.style.backgroundColor = colorScale[ncat];
+ bar.style.color = "white";
+ bar.style.width = width + "%";
+ bar.textContent = category + ": " + catTeam;
+ bar.title = bar.textContent;
+
+ row.appendChild(bar);
+
+ ncat++;
+
+ width = maxWidth * (1 - catPct);
+ if(width > 0)
+ {
+ var noBar = document.createElement("span");
+ //noBar.classList.add("cat" + ncat);
+ noBar.style.backgroundColor = colorScale[ncat];
+ noBar.style.width = width + "%";
+ noBar.textContent = numLeft;
+ noBar.title = bar.textContent;
+
+ row.appendChild(noBar);
+ }
+ ncat += 1;
+ }
+
+ var te = document.createElement("span");
+ te.classList.add("teamname");
+ te.textContent = teamnames[team.__hash__];
+ row.appendChild(te);
+
+ element.appendChild(row);
+ }
+ }
+ }
+
+ function once() {
+ loadJSON("points.json", update);
+ }
+ if (continuous) {
+ updateInterval = setInterval(once, interval);
+ }
+ once();
+}
+
diff --git a/www/scoreboard-proj.html b/www/scoreboard-proj.html
new file mode 100644
index 0000000..ad32504
--- /dev/null
+++ b/www/scoreboard-proj.html
@@ -0,0 +1,33 @@
+
+
+
+ Scoreboard
+
+
+
+
+
+
+
+
diff --git a/www/scoreboard.html b/www/scoreboard.html
index 6fe1d57..b0a336f 100644
--- a/www/scoreboard.html
+++ b/www/scoreboard.html
@@ -3,75 +3,18 @@
Scoreboard
-
-
- Cyber Fire
-
- Scoreboard Type:
- Scored Points
- All Points
- Timeline
-
+ Scoreboard