@ywkrlerche/richardwho 1.0.24 → 1.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app.js CHANGED
@@ -3,6 +3,9 @@
3
3
 
4
4
  const CFG = window.RUG_CONFIG || {};
5
5
  const SITE_NAME = "Richards Unblocked Games";
6
+ // The tab/history shows this instead of the real names (set in config.js)
7
+ const TAB_TITLE = CFG.tabTitle || null;
8
+ const tabTitle = (gameName) => TAB_TITLE || (gameName ? `${gameName} · ${SITE_NAME}` : SITE_NAME);
6
9
  const RECENT_KEY = "rug-recent";
7
10
  const CLOCK_KEY = "rug-clock24";
8
11
  const THEME_KEY = "rug-theme";
@@ -63,6 +66,7 @@
63
66
  // games from the extra collection carry their own pinned cover URL
64
67
  function logoUrl(game) {
65
68
  if (game.cover) return game.cover;
69
+ if (typeof game.logo === "string") return `logos/${encodeURIComponent(game.logo)}`; // PNG/JPG/GIF logo
66
70
  return game.logo ? `logos/${encodeURIComponent(game.id)}.webp` : null;
67
71
  }
68
72
  function fmtMB(bytes) {
@@ -201,7 +205,7 @@
201
205
  els.retryBtn.hidden = true;
202
206
  els.barFill.style.width = "0%";
203
207
  els.loaderStatus.textContent = "Connecting…";
204
- document.title = `${game.name} · ${SITE_NAME}`;
208
+ document.title = tabTitle(game.name);
205
209
 
206
210
  const urls = gameUrls(game);
207
211
  const loaded = new Array(urls.length).fill(0);
@@ -220,19 +224,37 @@
220
224
 
221
225
  try {
222
226
  const pieces = await Promise.all(urls.map(async (url, i) => {
223
- const res = await fetch(url, { signal: controller.signal });
224
- if (!res.ok) throw new Error(`HTTP ${res.status} for ${url.split("/").pop()}`);
225
- if (!res.body) { const b = await res.arrayBuffer(); loaded[i] = b.byteLength; update(); return [b]; }
226
- const reader = res.body.getReader();
227
- const chunks = [];
228
- for (;;) {
229
- const { done, value } = await reader.read();
230
- if (done) break;
231
- chunks.push(value);
232
- loaded[i] += value.byteLength;
233
- update();
227
+ // Try the main link, then backups. jsDelivr sometimes fails the first fetch of a file it
228
+ // hasn't cached yet, and a download can drop mid-way, so a retry usually succeeds.
229
+ const sources = mirrorsFor(url);
230
+ let lastErr;
231
+ for (let attempt = 0; attempt < sources.length; attempt++) {
232
+ if (controller.signal.aborted) throw new DOMException("Aborted", "AbortError");
233
+ if (attempt) {
234
+ els.loaderStatus.textContent = `Connection hiccup, trying backup ${attempt} of ${sources.length - 1}…`;
235
+ await new Promise((r) => setTimeout(r, 600 * attempt));
236
+ }
237
+ loaded[i] = 0;
238
+ try {
239
+ const res = await fetch(sources[attempt], { signal: controller.signal });
240
+ if (!res.ok) throw new Error(`HTTP ${res.status} for ${url.split("/").pop()}`);
241
+ if (!res.body) { const b = await res.arrayBuffer(); loaded[i] = b.byteLength; update(); return [b]; }
242
+ const reader = res.body.getReader();
243
+ const chunks = [];
244
+ for (;;) {
245
+ const { done, value } = await reader.read();
246
+ if (done) break;
247
+ chunks.push(value);
248
+ loaded[i] += value.byteLength;
249
+ update();
250
+ }
251
+ return chunks;
252
+ } catch (err) {
253
+ if (err.name === "AbortError") throw err;
254
+ lastErr = err;
255
+ }
234
256
  }
235
- return chunks;
257
+ throw lastErr;
236
258
  }));
237
259
  if (currentLoad?.id !== game.id) return;
238
260
  els.loaderStatus.textContent = "Starting…";
@@ -255,6 +277,22 @@
255
277
  }
256
278
  }
257
279
 
280
+ // Backup sources for a jsDelivr GitHub link, in the order to try them:
281
+ // the same link again, jsDelivr's second network, then GitHub's own file server.
282
+ // The extra collection's files are also published as gn-math/html at the same commit.
283
+ function mirrorsFor(url) {
284
+ const m = url.match(/^https:\/\/cdn\.jsdelivr\.net\/gh\/([^/]+)\/([^@/]+)@([^/]+)\/(.+)$/);
285
+ if (!m) return [url, url];
286
+ const [, user, repo, ref, path] = m;
287
+ const list = [url, url, `https://fastly.jsdelivr.net/gh/${user}/${repo}@${ref}/${path}`,
288
+ `https://raw.githubusercontent.com/${user}/${repo}/${ref}/${path}`];
289
+ if (user === "freebuisness" && repo === "html") {
290
+ list.push(`https://cdn.jsdelivr.net/gh/gn-math/html@${ref}/${path}`,
291
+ `https://raw.githubusercontent.com/gn-math/html/${ref}/${path}`);
292
+ }
293
+ return list;
294
+ }
295
+
258
296
  // The extra collection's launch files embed Google Analytics; remove it so players aren't tracked
259
297
  function stripTracking(html) {
260
298
  return html
@@ -307,7 +345,7 @@
307
345
  stopGame();
308
346
  els.player.hidden = true;
309
347
  document.body.classList.remove("playing");
310
- document.title = SITE_NAME;
348
+ document.title = tabTitle();
311
349
  const view = location.hash === "#games" || location.hash === "#recent" ? "games" : "home";
312
350
  els.homeView.hidden = view !== "home";
313
351
  els.gamesView.hidden = view !== "games";
package/config.js CHANGED
@@ -14,6 +14,11 @@ window.RUG_CONFIG = {
14
14
  // Keys that trigger panic (KeyboardEvent.key values)
15
15
  panicKeys: ["`", "]"],
16
16
 
17
+ // What the browser tab and history show instead of the site/game name.
18
+ // Set tabTitle to null to show the real names again.
19
+ tabTitle: "Google Classroom",
20
+ tabIcon: "https://ssl.gstatic.com/classroom/favicon.png",
21
+
17
22
  // For local testing only: load games from a folder on a local server
18
23
  // instead of jsDelivr, e.g. "/repos". Leave null when published.
19
24
  localBase: null,
package/index.html CHANGED
@@ -3,9 +3,9 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>Richards Unblocked Games</title>
6
+ <title>Google Classroom</title>
7
7
  <meta name="description" content="richardwho: Richards Unblocked Games. Hundreds of games, nothing to install.">
8
- <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' rx='22' fill='black'/><text x='50' y='70' font-size='62' text-anchor='middle' fill='white' font-family='Inter,Segoe UI,Arial' font-weight='800'>r</text></svg>">
8
+ <link rel="icon" id="tab-icon" href="https://ssl.gstatic.com/classroom/favicon.png">
9
9
  <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
10
10
  <link rel="stylesheet" href="style.css">
11
11
  <script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ywkrlerche/richardwho",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
4
4
  "description": "Richards Unblocked Games",
5
5
  "license": "UNLICENSED",
6
6
  "files": [