159 lines · 5.4 KB
Raw Download
1
<?php
2
/**
3
 * Markup helpers.
4
 *
5
 * The class names and their cascade order matter: the connector lines are drawn
6
 * by li.game-left/right (side border) and li.game-top/bottom (bottom border),
7
 * where game-top deliberately clears the side border. Keep them as-is.
8
 */
9
10
function e(string $value): string
11
{
12
    return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
13
}
14
15
function li(string $classes, string $content): string
16
{
17
    return "        <li class='" . $classes . "'>" . $content . "</li>\n";
18
}
19
20
function spacer(string $classes = 'spacer', string $content = '&nbsp;'): string
21
{
22
    return li($classes, $content);
23
}
24
25
function team_text(array $team): string
26
{
27
    return "<span class='seed-num'>" . $team['seed'] . "</span> " . e($team['name']);
28
}
29
30
function team_li(array $team, string $classes): string
31
{
32
    $full = $team['seed'] . ' ' . $team['name'];
33
    return "        <li class='" . $classes . "' title='" . e($full) . "'>"
34
        . team_text($team) . "</li>\n";
35
}
36
37
/**
38
 * The two outermost columns. Both teams in a matchup sit adjacent with no
39
 * spacer between them; the spacer separates one matchup from the next.
40
 */
41
function seed_column(array $teams, string $side): string
42
{
43
    $game = "game-$side";
44
    $out  = spacer();
45
    foreach (array_chunk($teams, 2) as [$top, $bottom]) {
46
        $out .= team_li($top, "$game game-top");
47
        $out .= team_li($bottom, "$game game-bottom");
48
        $out .= spacer();
49
    }
50
    return $out;
51
}
52
53
/**
54
 * Rounds 1-4. Each pair of entries feeds one game in the next round, so they
55
 * are joined by a bordered spacer. Round 3 fills that spacer with the region
56
 * name via $labels, keyed by pair index.
57
 */
58
function round_column(array $entries, string $side, array $labels = []): string
59
{
60
    $game = "game-$side";
61
    $edge = $side === 'left' ? 'right' : 'left';
62
    $out  = spacer();
63
64
    foreach (array_chunk($entries, 2) as $pair => [$top, $bottom]) {
65
        $out .= team_li($top, "$game game-top");
66
        $out .= isset($labels[$pair])
67
            ? spacer("$game spacer region region-$edge", e($labels[$pair]))
68
            : spacer("$game spacer");
69
        $out .= team_li($bottom, "$game game-bottom");
70
        $out .= spacer();
71
    }
72
    return $out;
73
}
74
75
/** Final Four and championship columns hold a single entry, so nothing to pair. */
76
function single_column(array $team, string $classes): string
77
{
78
    return spacer() . team_li($team, $classes) . spacer();
79
}
80
81
/** One column of the date band above the bracket. Null renders an empty column. */
82
function header_column(?array $round): string
83
{
84
    if ($round === null) {
85
        return '';
86
    }
87
    return li('', e($round['name'])) . li('', e($round['dates']));
88
}
89
90
/* -------------------------------------------------------------------------
91
   Narrow-screen view. Below 800px the mirrored bracket is replaced by these:
92
   a champion card, then the four regions stacked, then the Final Four. The
93
   two are alternatives, never shown together. See style.css.
94
   ---------------------------------------------------------------------- */
95
96
/** Headline result. */
97
function champion_card(array $t): string
98
{
99
    return "  <div class='champion-card'>\n"
100
        . "    <div class='champion-label'>Champion</div>\n"
101
        . "    <div class='champion-name'>" . team_text($t['champion']) . "</div>\n"
102
        . "    <div class='champion-over'>over " . team_text($t['runnerUp']) . "</div>\n"
103
        . "  </div>\n";
104
}
105
106
/** A section heading carrying the winner that came out of it. */
107
function panel_head(string $name, array $winner): string
108
{
109
    return "    <h2 class='panel-head'><span class='panel-name'>" . e($name) . "</span>"
110
        . "<span class='panel-winner'>" . team_text($winner) . "</span></h2>\n";
111
}
112
113
/**
114
 * A region as a four-column bracket: 16 seeds, then the winners of rounds 1-3.
115
 * The fourth-round winner is the region champion, which goes in the heading
116
 * rather than a fifth column — that is what lets this fit 320px.
117
 */
118
function region_panel(array $region): string
119
{
120
    $columns = "      <ul class='round seed'>\n"
121
        . seed_column($region['rounds'][0], 'left') . "      </ul>\n";
122
    for ($n = 1; $n <= 3; $n++) {
123
        $columns .= "      <ul class='round round-$n'>\n"
124
            . round_column($region['rounds'][$n], 'left') . "      </ul>\n";
125
    }
126
127
    return "  <section class='region-panel'>\n"
128
        . panel_head($region['label'], $region['rounds'][4][0])
129
        . "    <div class='tournament mini'>\n$columns    </div>\n"
130
        . "  </section>\n";
131
}
132
133
/** One game as a two-row card with the winner marked. */
134
function matchup_card(array $game): string
135
{
136
    $rows = '';
137
    foreach (['top', 'bottom'] as $slot) {
138
        $won   = $game[$slot] === $game['winner'];
139
        $rows .= "      <div class='side" . ($won ? ' won' : '') . "'>"
140
            . team_text($game[$slot]) . "</div>\n";
141
    }
142
    return "    <div class='card'>\n$rows    </div>\n";
143
}
144
145
/**
146
 * The Final Four and title game. Three games is too few to be worth a bracket,
147
 * so they are cards instead.
148
 */
149
function final_four_panel(array $t, array $rounds): string
150
{
151
    return "  <section class='region-panel'>\n"
152
        . panel_head('Final Four', $t['champion'])
153
        . "    <h3>" . e($rounds[4]['name']) . " <small>" . e($rounds[4]['dates']) . "</small></h3>\n"
154
        . matchup_card($t['national'][5][0]) . matchup_card($t['national'][5][1])
155
        . "    <h3>" . e($rounds[5]['name']) . " <small>" . e($rounds[5]['dates']) . "</small></h3>\n"
156
        . matchup_card($t['national'][6][0])
157
        . "  </section>\n";
158
}
159