File size: 2,952 Bytes
5d90ebb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
955e8d0
 
 
5d90ebb
955e8d0
5d90ebb
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/* An honest, cookie-free tally of who plays.
 *
 * GoatCounter is built for exactly this arrangement: no cookies, no
 * fingerprinting, no personal data, nothing stored in the visitor's browser —
 * so no consent banner is needed — and the dashboard is public, so a player
 * can read everything the page records; the About panel links straight to it.
 * The pings go to GoatCounter's /count endpoint directly (their count.js is
 * never loaded: this page ships every byte it runs). Empty COUNT_URL and the
 * module produces zero network traffic; app.js then also removes the About
 * panel's tally note and the stats button, so the page never points at a
 * tally it does not keep.
 *
 * What gets counted (each a single GET of a 1×1 gif, fired and forgotten):
 *   pageview                              the page was opened
 *   game-start                            tiles were dealt
 *   game-end/<model>/<result>             how a finished game went, per net —
 *                                         result is human-wins | net-wins | draw,
 *                                         with the score line as the hit's title
 *
 * The dashboard should be made PUBLIC in GoatCounter's settings: the page links
 * to it, so every player can see exactly what is recorded. That link appears
 * automatically once COUNT_URL is set.
 */

export const COUNT_URL = "https://faience.goatcounter.com/count";

/** Whether the tally is on at all — the About panel reads this. */
export const analyticsOn = () => !!COUNT_URL;

/** The public dashboard the About panel links to — the /count host itself. */
export function statsUrl() {
  if (!COUNT_URL) return "";
  try {
    return new URL(COUNT_URL).origin;
  } catch (err) {
    return "";
  }
}

/**
 * Count one event. Never throws, never waits, does nothing while COUNT_URL is
 * empty. `pageview` is special-cased to count as a visit rather than an event;
 * `extra.title` becomes the hit's title (detail on the dashboard, e.g. a score
 * line), while the path stays coarse so the counts aggregate.
 */
export function track(name, extra) {
  if (!COUNT_URL) return;
  try {
    // development, the headless test suite and any staging copy of the site
    // play thousands of games; none of them are players, and none of them may
    // touch the public tally
    const host = location.hostname;
    if (host === "localhost" || host === "127.0.0.1" || host === "" || host.includes("staging")) return;
    const page = name === "pageview";
    const path = page ? location.pathname : name;
    const url =
      COUNT_URL +
      "?p=" + encodeURIComponent(path) +
      (page ? "" : "&e=true") +
      (extra && extra.title ? "&t=" + encodeURIComponent(extra.title) : "") +
      "&rnd=" + Date.now().toString(36);
    new Image().src = url; // a GET the browser fires and forgets; no reply is read
  } catch (err) {
    /* an ad blocker or a locked-down browser; the game plays on */
  }
}