diff --git a/pr-preview/pr-19/scp-3211/en/3211@2.0.3.js b/pr-preview/pr-19/scp-3211/en/3211@2.0.3.js index 720b7ab..db359d1 100644 --- a/pr-preview/pr-19/scp-3211/en/3211@2.0.3.js +++ b/pr-preview/pr-19/scp-3211/en/3211@2.0.3.js @@ -1,5 +1,5 @@ /*! - * 3211.js version 2.0.3, generated Mon Sep 09 2024 08:20:03 GMT+0000 (Coordinated Universal Time) + * 3211.js version 2.0.3, generated Tue Sep 10 2024 12:57:41 GMT+0000 (Coordinated Universal Time) * This file was generated for SCP-3211 and its translations in the international SCP community. * SCP-3211-EN: https://scp-wiki.wikidot.com/scp-3211 * Source: https://github.com/rossjrw/scp/tree/main/source/scp-3211 diff --git a/pr-preview/pr-19/scp-3211/jp/3211@2.0.3.js b/pr-preview/pr-19/scp-3211/jp/3211@2.0.3.js index 720b7ab..db359d1 100644 --- a/pr-preview/pr-19/scp-3211/jp/3211@2.0.3.js +++ b/pr-preview/pr-19/scp-3211/jp/3211@2.0.3.js @@ -1,5 +1,5 @@ /*! - * 3211.js version 2.0.3, generated Mon Sep 09 2024 08:20:03 GMT+0000 (Coordinated Universal Time) + * 3211.js version 2.0.3, generated Tue Sep 10 2024 12:57:41 GMT+0000 (Coordinated Universal Time) * This file was generated for SCP-3211 and its translations in the international SCP community. * SCP-3211-EN: https://scp-wiki.wikidot.com/scp-3211 * Source: https://github.com/rossjrw/scp/tree/main/source/scp-3211 diff --git a/pr-preview/pr-19/scp-3211/zh/3211@2.0.3.js b/pr-preview/pr-19/scp-3211/zh/3211@2.0.3.js index 720b7ab..db359d1 100644 --- a/pr-preview/pr-19/scp-3211/zh/3211@2.0.3.js +++ b/pr-preview/pr-19/scp-3211/zh/3211@2.0.3.js @@ -1,5 +1,5 @@ /*! - * 3211.js version 2.0.3, generated Mon Sep 09 2024 08:20:03 GMT+0000 (Coordinated Universal Time) + * 3211.js version 2.0.3, generated Tue Sep 10 2024 12:57:41 GMT+0000 (Coordinated Universal Time) * This file was generated for SCP-3211 and its translations in the international SCP community. * SCP-3211-EN: https://scp-wiki.wikidot.com/scp-3211 * Source: https://github.com/rossjrw/scp/tree/main/source/scp-3211 diff --git a/pr-preview/pr-19/scp-head/controller@0.0.0.html b/pr-preview/pr-19/scp-head/controller@0.0.0.html index 5928c69..b80de1d 100644 --- a/pr-preview/pr-19/scp-head/controller@0.0.0.html +++ b/pr-preview/pr-19/scp-head/controller@0.0.0.html @@ -14,10 +14,9 @@ window.debug = (new URLSearchParams(location.search).get("debug") || "") === "true"; - // TODO: Predict what the next resize request will be and prefetch it - /** * @typedef {Object} scoutReport + * @property {string} eventName * @property {string} scoutName * @property {boolean} isIntersecting * @property {string | null} direction @@ -58,6 +57,9 @@ /** @type {number} */ this.#activeAssertion = this.minId; + + /** @type {Set.} */ + this.knownUpcomingStages = new Set(); } /** @param {number} value */ @@ -73,6 +75,15 @@ get activeAssertion() { return this.#activeAssertion; } + + /** @returns {number | null} */ + get expectedNextAssertion() { + const upcomingAssertions = [...this.knownUpcomingStages].filter( + (n) => n > this.#activeAssertion + ); + if (upcomingAssertions.length === 0) return null; + return Math.min(...upcomingAssertions); + } }; /** @type {Object.} */ @@ -194,24 +205,107 @@ } function sendAssertionState(resize) { - const message = [ + // Remove all preloaders + document + .querySelectorAll(".preload-iframe") + .forEach((iframe) => iframe.remove()); + + const assertionState = [ assertionChannels["A"].activeAssertion, ".", assertionChannels["B"].activeAssertion, "0", assertionChannels["C"].activeAssertion, ].join(""); - console.debug("Updating assertion state to", message); - resize(message); + console.debug("Updating assertion state to", assertionState); + resize(assertionState); + + preloadNextAssertionState(); + } + + function preloadNextAssertionState() { + // Work out each of the possible next assertion states, assuming that each channel is equally likely to advance to its next stage + const possibleAssertionStates = []; + for (const [channelName, channel] of Object.entries( + assertionChannels + )) { + if (channel.expectedNextAssertion) { + possibleAssertionStates.push( + [ + assertionChannels["A"][ + channelName === "A" + ? "activeAssertion" + : "expectedNextAssertion" + ], + ".", + assertionChannels["B"][ + channelName === "B" + ? "activeAssertion" + : "expectedNextAssertion" + ], + "0", + assertionChannels["C"][ + channelName === "C" + ? "activeAssertion" + : "expectedNextAssertion" + ], + ].join("") + ); + } + } + for (const possibleAssertionState of possibleAssertionStates) { + console.debug(`Preloading assertion state ${possibleAssertionState}`); + const preloadIframe = document.createElement("iframe"); + // Iframe is fully sandboxed - allow-same-origin being implicitly false should prevent the controller being resized + preloadIframe.sandbox = true; + preloadIframe.classList.add("preload-iframe"); + document.body.appendChild(preloadIframe); + preloadIframe.src = + document.referrer + + "/common--javascript/resize-iframe.html?" + + "#" + + possibleAssertionState + + "/" + + location.href.replace(/^.*\//, "/"); + } } + /** + * @param {MessageEvent} message + * @param {function} resize + */ function processScoutReport(message, resize) { if (message.origin !== location.origin) return; - if (!message.data.scoutName) return; - - /** @type {scoutReport} */ const scoutReport = message.data; + if (!scoutReport.scoutName) return; + + switch (scoutReport.eventName) { + case "ScoutRegistration": + processScoutRegistrationEvent(scoutReport); + break; + case "ScoutIntersectionStateChange": + processScoutIntersectionEvent(scoutReport, resize); + break; + default: + throw new Error(`Unknown event: ${scoutReport.eventName}`); + } + } + + /** + * @param {scoutReport} scoutReport + */ + function processScoutRegistrationEvent(scoutReport) { + // Store contradiction IDs in the corresponding channel to be used for preloading + for (const c of scoutReport.contradictions) { + assertionChannels[c.channelName].knownUpcomingStages.push(c.id); + } + } + /** + * @param {scoutReport} scoutReport + * @param {function} resize + */ + function processScoutIntersectionEvent(scoutReport, resize) { // Receive list of assertions const scoutAssertions = scoutReport.assertions; diff --git a/pr-preview/pr-19/scp-head/scout@0.0.0.html b/pr-preview/pr-19/scp-head/scout@0.0.0.html index 8d1ace2..0d5a56c 100644 --- a/pr-preview/pr-19/scp-head/scout@0.0.0.html +++ b/pr-preview/pr-19/scp-head/scout@0.0.0.html @@ -100,6 +100,14 @@ if (!controller) throw new Error(`Scout ${scoutName} failed to get controller`); + // Let the controller know that this scout exists + controller.postMessage({ + eventName: "ScoutRegistration", + scoutName, + assertions, + contradictions, + }); + // Scout is split into two, like poles of a magnet, for direction detection const poleTop = { element: document.querySelector(".scout__top"), @@ -173,6 +181,7 @@ if (entries.length === 2) { if (window.debug) console.debug(`${scoutName} sending`); controller.postMessage({ + eventName: "ScoutIntersectionStateChange", scoutName, isIntersecting: poleTop.intersectionRatio + poleBottom.intersectionRatio >= 1,