diff --git a/web/js/codeworld.js b/web/js/codeworld.js index b5dbdbef..94bbf028 100644 --- a/web/js/codeworld.js +++ b/web/js/codeworld.js @@ -30,7 +30,9 @@ import { run, toggleObsoleteCodeAlert, warnIfUnsaved, - sha256digest, + saveCodeToLocalStorageAndReplaceHash, + tryLoadingCodeFromLocalStorage, + tryFetchCodeFromSourceAndStripURL, } from './codeworld_shared.js'; import * as Alert from './utils/alert.js'; @@ -178,7 +180,7 @@ async function init() { if(window.buildMode === 'codeworld') document.querySelector("#docButton").style.display = "none"; - const savedCode = localStorage.getItem(`${window.buildMode}-${window.location.hash.slice(1)}`); + const savedCode = tryLoadingCodeFromLocalStorage(window.buildMode); if (savedCode) { setCode(savedCode); @@ -192,48 +194,15 @@ picture = ... `); } - const currentUrl = new URL(window.location); - const searchParams = currentUrl.searchParams; - const codeSrc = searchParams.get("loadSrc"); - if(codeSrc) { - const fetchController = new AbortController(); - sweetAlert({ - title: Alert.title('Loading code'), - text: 'The code is being fetched. Please wait...', - onOpen: () => { - sweetAlert.showLoading(); - sweetAlert.getCancelButton().disabled = false; - }, - showConfirmButton: false, - showCancelButton: true, - showCloseButton: false, - allowOutsideClick: false, - allowEscapeKey: false, - allowEnterKey: false, - }).then(() => { - fetchController.abort(); - }); - try { - const response = await fetch(codeSrc, { - signal: fetchController.signal, - }); - const code = await response.text(); - setCode(code); - sweetAlert.close(); - searchParams.delete("loadSrc"); - window.history.replaceState(window.history.state, "", currentUrl.toString()); - } catch (error) { - sweetAlert( - 'Oops!', - 'Could not load the code from source. Please try again.', - 'error' - ); - } - } + await tryFetchCodeFromSourceAndStripURL(async (code) => { + setCode(code); + await saveCodeToLocalStorageAndReplaceHash(code, window.buildMode); + }); if(window.preloadCode && window.buildMode === 'haskell'){ const codeToLoad = new DOMParser().parseFromString(window.preloadCode, 'text/html').documentElement.textContent; setCode(codeToLoad); + await saveCodeToLocalStorageAndReplaceHash(codeToLoad, window.buildMode); }; } @@ -959,7 +928,7 @@ function stopRun() { } window.cancelCompile(); - run('', '', '', false, null); + run(false, '', false, null); } function compile() { @@ -1020,14 +989,10 @@ function compile() { compilerMessage = 'Sorry! Your program couldn\'t be run right now.'; } - const codeHash = await sha256digest(src.trim()); - window.program = compiledProgram; - run(codeHash,"deploy_hash",compilerMessage,false,compileGeneration); - localStorage.setItem(`${window.buildMode}-${codeHash}`, src); - - + run(status === 200,compilerMessage,false,compileGeneration); sweetAlert.close(); + await saveCodeToLocalStorageAndReplaceHash(src, window.buildMode); return; } diff --git a/web/js/codeworld_shared.js b/web/js/codeworld_shared.js index b1451df8..10a70075 100644 --- a/web/js/codeworld_shared.js +++ b/web/js/codeworld_shared.js @@ -949,7 +949,7 @@ function initializeLayoutContainer(options) { } -function run(hash, dhash, msg, error, generation) { +function run(successful, msg, error, generation) { window.runningGeneration = generation; window.debugAvailable = false; window.debugActive = false; @@ -965,17 +965,13 @@ function run(hash, dhash, msg, error, generation) { '*' ); - if (hash) { - window.location.hash = `#${hash}`; - } - runner.contentWindow.location.replace(`run?mode=${window.buildMode}`); document.getElementById('runner').style.display = 'none'; document.getElementById('startRecButton').style.display = 'none'; const layoutHandler = $(LAYOUT_CONTAINER_CLASSNAME).layout(); - if (hash || msg) { + if (successful || msg) { layoutHandler.show('east'); layoutHandler.open('east'); } else { @@ -1097,6 +1093,80 @@ async function sha256digest(data) { }); } +async function saveCodeToLocalStorageAndReplaceHash(code, mode) { + const currentUrl = new URL(window.location); + + try { + const codeHash = await sha256digest(code.trim()); + localStorage.setItem(`${mode}-${codeHash}`, code); + currentUrl.hash = codeHash; + + window.history.replaceState(window.history.state, "", currentUrl.toString()); + } catch (error) { + console.error('Failed to save code to local storage:', error); + sweetAlert( + 'Oops!', + 'Unable to store code in local storage. Quota might have been exceeded.', + 'error' + ); + } +} + +function tryLoadingCodeFromLocalStorage(mode) { + const currentUrl = new URL(window.location); + const codeHash = currentUrl.hash.slice(1); + if(!codeHash) return; + + return localStorage.getItem(`${mode}-${codeHash}`); +} + +async function tryFetchCodeFromSourceAndStripURL(handler){ + const currentUrl = new URL(window.location); + const searchParams = currentUrl.searchParams; + + const codeSrc = searchParams.get("loadSrc"); + if (!codeSrc) return; + + const fetchController = new AbortController(); + sweetAlert({ + title: Alert.title('Loading code'), + text: 'The code is being fetched. Please wait...', + onOpen: () => { + sweetAlert.showLoading(); + sweetAlert.getCancelButton().disabled = false; + }, + showConfirmButton: false, + showCancelButton: true, + showCloseButton: false, + allowOutsideClick: false, + allowEscapeKey: false, + allowEnterKey: false, + }).then(() => { + fetchController.abort(); + }); + try { + const response = await fetch(codeSrc, { + signal: fetchController.signal, + }); + if(response.ok) { + const code = await response.text(); + searchParams.delete("loadSrc"); + window.history.replaceState(window.history.state, "", currentUrl.toString()); + sweetAlert.close(); + await handler(code); + } else { + throw new Error(`Failed to fetch code from source: ${response.statusText}`); + } + + } catch (error) { + sweetAlert( + 'Oops!', + 'Could not load the code from source. Please try again.', + 'error' + ); + } +} + export { clearMessages, definePanelExtension, @@ -1114,4 +1184,7 @@ export { toggleObsoleteCodeAlert, warnIfUnsaved, sha256digest, + saveCodeToLocalStorageAndReplaceHash, + tryLoadingCodeFromLocalStorage, + tryFetchCodeFromSourceAndStripURL, }; diff --git a/web/js/run.js b/web/js/run.js index 6fe7da80..a1390375 100644 --- a/web/js/run.js +++ b/web/js/run.js @@ -15,6 +15,12 @@ */ import { sendHttp } from './utils/network.js'; +import { + saveCodeToLocalStorageAndReplaceHash, + tryLoadingCodeFromLocalStorage, + tryFetchCodeFromSourceAndStripURL, +} from './codeworld_shared.js' +import * as Alert from './utils/alert.js'; // Tracks when the program started, and whether the program has done // anything observable (as best we can tell). This is used to decide @@ -233,41 +239,35 @@ function start() { } async function init() { - let paramList = location.search.slice(1).split('&'); - const params = {}; - for (let i = 0; i < paramList.length; i++) { - const name = decodeURIComponent(paramList[i].split('=')[0]); - const value = decodeURIComponent(paramList[i].slice(name.length + 1)); - params[name] = value; - } - // params from the hash - paramList = location.hash.slice(1).split('&'); - for (let i = 0; i < paramList.length; i++) { - const name = decodeURIComponent(paramList[i].split('=')[0]); - const value = decodeURIComponent(paramList[i].slice(name.length + 1)); - params[name] = value; - } + await Alert.init(); + const searchParams = new URLSearchParams(window.location.search); - let mode = params['mode']; + let mode = searchParams.get('mode'); if(!mode) mode = 'codeworld'; + + const savedCode = tryLoadingCodeFromLocalStorage(mode); + if(savedCode) { + window.preloadCode = savedCode; + } - const codeSrc = params['loadSrc']; - - if(codeSrc || window.preloadCode) { + if(searchParams.has('loadSrc') || window.preloadCode) { try { let code = window.preloadCode; if(!code) { - const response = await fetch(codeSrc); - code = await response.text(); - if(!response.ok) { - throw new Error("Could not load source code from external location. Response code not OK."); - } - } + await tryFetchCodeFromSourceAndStripURL((fetchedCode) => { + code = fetchedCode; + }); + } + + if(code.trim() === '')return; + + await saveCodeToLocalStorageAndReplaceHash(code, mode); + const data = new FormData(); data.append('source', code); data.append('mode', mode); - const enablePreview = params['enablePreview']; + const enablePreview = searchParams.get('enablePreview'); if(enablePreview) data.append('enablePreview',enablePreview); diff --git a/web/run.html b/web/run.html index 98e32db1..ffcdccc3 100644 --- a/web/run.html +++ b/web/run.html @@ -5,10 +5,25 @@ + + + +
+