'use strict';
+function jsonclone(x)
+{
+ return JSON.parse(JSON.stringify(x));
+}
+
+// Log with deep clone, so that the browser will show the object at time of log,
+// instead of what it looks like at time of view.
+function dlog()
+{
+ let args = [];
+ for (const arg of arguments) {
+ args.push(jsonclone(arg));
+ }
+ console.log(args);
+}
+
function addheading(carousel, colspan, content)
{
let thead = document.createElement("thead");
tr.appendChild(th);
thead.appendChild(tr);
carousel.appendChild(thead);
-};
+}
+
function addtd(tr, className, content) {
let td = document.createElement("td");
td.appendChild(document.createTextNode(content));
td.className = className;
tr.appendChild(td);
-};
+}
+
function addth(tr, className, content) {
let th = document.createElement("th");
th.appendChild(document.createTextNode(content));
th.className = className;
tr.appendChild(th);
-};
+}
-function subrank_partitions(games, parts, start_rank, tiebreakers) {
+function subrank_partitions(games, parts, start_rank, tiebreakers, func) {
let result = [];
for (let i = 0; i < parts.length; ++i) {
- let part = rank(games, parts[i], start_rank, tiebreakers);
+ let part = func(games, parts[i], start_rank, tiebreakers);
for (let j = 0; j < part.length; ++j) {
result.push(part[j]);
}
// Rule #0: Partition the teams by score.
let score_parts = partition(teams, function(a, b) { return b.pts - a.pts });
if (score_parts.length > 1) {
- return subrank_partitions(games, score_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, score_parts, start_rank, tiebreakers, rank);
}
// Rule #1: Head-to-head wins.
});
if (beat_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(beat_parts, 'head-to-head'));
- return subrank_partitions(games, beat_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, beat_parts, start_rank, tiebreakers, rank);
}
// Rule #2: Number of games played (fewer is better).
let nplayed_parts = partition(teams, function(a, b) { return a.nplayed - b.nplayed });
if (nplayed_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(nplayed_parts, 'fewer losses'));
- return subrank_partitions(games, nplayed_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, nplayed_parts, start_rank, tiebreakers, rank);
}
// Rule #3: Head-to-head goal difference (if all have played).
let h2h_gd_parts = partition(teams, function(a, b) { return b.h2h_gd - a.h2h_gd });
if (h2h_gd_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(h2h_gd_parts, 'head-to-head goal difference'));
- return subrank_partitions(games, h2h_gd_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, h2h_gd_parts, start_rank, tiebreakers, rank);
}
}
});
if (gd_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(gd_parts, 'goal difference versus common opponents'));
- return subrank_partitions(games, gd_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, gd_parts, start_rank, tiebreakers, rank);
}
// Rule #5: Head-to-head scored goals (if all have played).
let h2h_goals_parts = partition(teams, function(a, b) { return b.h2h_goals - a.h2h_goals });
if (h2h_goals_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(h2h_goals_parts, 'head-to-head scored goals'));
- return subrank_partitions(games, h2h_goals_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, h2h_goals_parts, start_rank, tiebreakers, rank);
}
}
});
if (goals_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(goals_parts, 'goals scored against common opponents'));
- return subrank_partitions(games, goals_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, goals_parts, start_rank, tiebreakers, rank);
+ }
+
+ // OK, it's a tie. Give them all the same rank.
+ let result = [];
+ for (let i = 0; i < teams.length; ++i) {
+ result.push(teams[i]);
+ result[i].rank = start_rank;
+ }
+ return result;
+};
+
+// Same, but with the simplified rules for ranking thirds. games isn't used and can be empty.
+function rank_thirds(games, teams, start_rank, tiebreakers) {
+ if (teams.length <= 1) {
+ // Only one team, so trivial.
+ teams[0].rank = start_rank;
+ return teams;
+ }
+
+ // Rule #1: Partition the teams by score.
+ let score_parts = partition(teams, function(a, b) { return b.pts - a.pts });
+ if (score_parts.length > 1) {
+ tiebreakers.push(explain_tiebreaker(score_parts, 'most games won'));
+ return subrank_partitions(games, score_parts, start_rank, tiebreakers, rank_thirds);
+ }
+
+ // Rule #2: Goal difference against common opponents.
+ let gd_parts = partition(teams, function(a, b) { return b.gd - a.gd });
+ if (gd_parts.length > 1) {
+ tiebreakers.push(explain_tiebreaker(gd_parts, 'goal difference'));
+ return subrank_partitions(games, gd_parts, start_rank, tiebreakers, rank_thirds);
+ }
+
+ // Rule #3: Goals scored.
+ let goal_parts = partition(teams, function(a, b) { return b.goals - a.goals });
+ if (goal_parts.length > 1) {
+ tiebreakers.push(explain_tiebreaker(goal_parts, 'goals scored'));
+ return subrank_partitions(games, goal_parts, start_rank, tiebreakers, rank_thirds);
}
// OK, it's a tie. Give them all the same rank.
"name": response.values[i][0],
"mediumname": response.values[i][1],
"shortname": response.values[i][2],
- "tags": response.values[i][3],
+ //"tags": response.values[i][3],
+ "ngames": 0,
"nplayed": 0,
"gd": 0,
"pts": 0,
return games;
};
-function display_group(response, group_name)
+function apply_games_to_teams(games, teams, ignored_teams, ret_ignored_games)
{
- let teams = parse_teams_from_spreadsheet(response);
- let games = parse_games_from_spreadsheet(response, group_name, false);
- display_group_parsed(teams, games, group_name);
-};
-
-function display_group_parsed(teams, games, group_name)
-{
- document.getElementById('entire-bug').style.display = 'none';
-
let teams_to_idx = make_teams_to_idx(teams);
+ let ignored_teams_idx;
+ if (ignored_teams === undefined) {
+ ignored_teams_idx = [];
+ } else {
+ ignored_teams_idx = make_teams_to_idx(ignored_teams);
+ }
+ for (let i = 0; i < teams.length; ++i) {
+ teams[i].nplayed = 0;
+ teams[i].goals = 0;
+ teams[i].gd = 0;
+ teams[i].pts = 0;
+ }
for (let i = 0; i < games.length; ++i) {
let idx1 = teams_to_idx[games[i].name1];
let idx2 = teams_to_idx[games[i].name2];
games[i].score1 == games[i].score2) {
continue;
}
+
+ let ignored_idx1 = ignored_teams_idx[games[i].name1];
+ let ignored_idx2 = ignored_teams_idx[games[i].name2];
+ if (ignored_idx1 !== undefined || ignored_idx2 !== undefined) {
+ if (ret_ignored_games !== undefined) {
+ // Figure out whether the fifth we're ignoring was only picked out arbitrarily
+ // (ie., there's a tie for 5th); if so, mark it as such.
+ let arbitrary = false;
+ if (ignored_idx1 !== undefined && ignored_teams[ignored_idx1].rank < 5) {
+ arbitrary = true;
+ } else if (ignored_idx2 !== undefined && ignored_teams[ignored_idx2].rank < 5) {
+ arbitrary = true;
+ }
+ ret_ignored_games.push([teams[idx1].shortname, teams[idx2].shortname, arbitrary]);
+ }
+ continue;
+ }
++teams[idx1].nplayed;
++teams[idx2].nplayed;
teams[idx1].goals += games[i].score1;
teams[idx2].pts += 2;
}
}
+}
+// So that we can just have one team list, and let membership be defined by games.
+function filter_teams(teams, response)
+{
+ let teams_to_idx = make_teams_to_idx(teams);
+ let games = parse_games_from_spreadsheet(response, 'irrelevant group name', true);
+ for (let i = 0; i < games.length; ++i) {
+ let idx1 = teams_to_idx[games[i].name1];
+ let idx2 = teams_to_idx[games[i].name2];
+ if (idx1 !== undefined) {
+ ++teams[idx1].ngames; // FIXME: shouldn't nplayed be just as good?
+ }
+ if (idx2 !== undefined) {
+ ++teams[idx2].ngames;
+ }
+ }
+ return teams.filter(function(team) { return team.ngames > 0; });
+}
+
+function display_group_parsed(teams, games, group_name)
+{
+ document.getElementById('entire-bug').style.display = 'none';
+
+ apply_games_to_teams(games, teams);
let tiebreakers = [];
teams = rank(games, teams, 1, tiebreakers);
let carousel = document.getElementById('carousel');
clear_carousel(carousel);
- addheading(carousel, 5, "Current standings<br />" + group_name);
+ addheading(carousel, 5, "Current standings, " + ultimateconfig['tournament_title'] + "<br />" + group_name);
let tr = document.createElement("tr");
tr.className = "subfooter";
addth(tr, "rank", "");
let footer_tr = document.createElement("tr");
footer_tr.className = "footer";
let td = document.createElement("td");
- td.appendChild(document.createTextNode("Norwegian Ultimate Championships 2018 | #ultimatenm"));
+ td.appendChild(document.createTextNode(ultimateconfig['tournament_footer']));
td.setAttribute("colspan", "5");
footer_tr.appendChild(td);
carousel.appendChild(footer_tr);
};
// Stream schedule
-let max_list_len = 8;
+let max_list_len = 7;
function display_stream_schedule(response, group_name) {
let teams = parse_teams_from_spreadsheet(response);
let carousel = document.getElementById('carousel');
clear_carousel(carousel);
- addheading(carousel, 3, "Match schedule<br />" + covered_days.join('/') + " (all times CET)");
+ addheading(carousel, 3, "Stream schedule, " + ultimateconfig['tournament_title'] + "<br />" + covered_days.join('/') + " (all times CET)");
let teams_to_idx = make_teams_to_idx(teams);
row_num = 0;
req.onload = function(e) {
cb(JSON.parse(req.responseText), group_name);
};
- req.open('GET', 'https://sheets.googleapis.com/v4/spreadsheets/1uh7kr5v_hyD072b1G2tbQlhqd_8ldS_6j30CBocQ-4E/values/\'' + group_name + '\'!A1:J50?key=AIzaSyAuP9yQn8g0bSay6r_RpGtpFeIbwprH1TU');
+ req.open('GET', 'https://sheets.googleapis.com/v4/spreadsheets/' + ultimateconfig['score_sheet_id'] + '/values/\'' + group_name + '\'!A1:J50?key=' + ultimateconfig['api_key']);
req.send();
-};
+}
function showgroup(group_name)
{
- get_group(group_name, display_group);
-};
+ get_group(group_name, function(response, group_name) {
+ let teams = parse_teams_from_spreadsheet(response);
+ let games = parse_games_from_spreadsheet(response, group_name, false);
+ teams = filter_teams(teams, response);
+ display_group_parsed(teams, games, group_name);
+ publish_group_rank(response, group_name); // Update the spreadsheet in the background.
+ });
+}
+
function showgroup_from_state()
{
showgroup(state['group_name']);
-};
+}
let carousel_timeout = null;
{
let teams = [];
let games = [];
- let num_left = 3;
+ let groups_to_get = [
+ 'Group A',
+ 'Group B',
+ // 'Group C',
+ // 'Playoffs 9th-13th',
+ 'Playoffs'
+ ];
+ let num_left = groups_to_get.length;
let cb = function(response, group_name) {
teams = teams.concat(parse_teams_from_spreadsheet(response));
}
};
- get_group('Group A', cb);
- get_group('Group B', cb);
- get_group('Playoffs', cb);
+ for (const group of groups_to_get) {
+ get_group(group, cb);
+ }
};
function do_series(series)
let games_per_group = [];
let combined_teams = [];
let combined_games = [];
- let num_left = 3;
+ let groups_to_get = [
+ 'Group A',
+ 'Group B',
+ // 'Group C',
+ // 'Playoffs 9th-13th',
+ 'Playoffs'
+ ];
+ let num_left = groups_to_get.length;
let cb = function(response, group_name) {
let teams = parse_teams_from_spreadsheet(response);
let games = parse_games_from_spreadsheet(response, group_name, true);
+ teams = filter_teams(teams, response);
teams_per_group[group_name] = teams;
games_per_group[group_name] = games;
}
};
- get_group('Group A', cb);
- get_group('Group B', cb);
- get_group('Playoffs', cb);
+ for (const group of groups_to_get) {
+ get_group(group, cb);
+ }
};
function stopcarousel()
function showscorebug()
{
document.getElementById('entire-bug').style.display = null;
-};
+}
+function showmatch2()
+{
+ let css = "-webkit-animation: fade-in 1.0s ease; -webkit-animation-fill-mode: both;";
+ document.getElementById('scorebug2').style = css;
+ document.getElementById('clockbug2').style = css;
+}
+
+function hidematch2()
+{
+ let css = "-webkit-animation: fade-out 1.0s ease; -webkit-animation-fill-mode: both;";
+ document.getElementById('scorebug2').style = css;
+ document.getElementById('clockbug2').style = css;
+}
+
+function showmatch3()
+{
+ let css = "-webkit-animation: fade-in 1.0s ease; -webkit-animation-fill-mode: both;";
+ document.getElementById('scorebug3').style = css;
+ document.getElementById('clockbug3').style = css;
+}
+
+function hidematch3()
+{
+ let css = "-webkit-animation: fade-out 1.0s ease; -webkit-animation-fill-mode: both;";
+ document.getElementById('scorebug3').style = css;
+ document.getElementById('clockbug3').style = css;
+}