Spaces:
Sleeping
Sleeping
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; connect-src * data: blob:"> | |
<meta name="robots" content="noindex"> | |
<title>Scaffolding Demo</title> | |
<style> | |
body { | |
background-color: black; | |
color: white; | |
font-family: sans-serif; | |
overflow: hidden; | |
} | |
[hidden] { | |
display: none ; | |
} | |
h1 { | |
font-weight: normal; | |
} | |
a { | |
color: inherit; | |
text-decoration: underline; | |
cursor: pointer; | |
} | |
#app, #loading, #error, #launch { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
} | |
.screen { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
text-align: center; | |
cursor: default; | |
user-select: none; | |
-webkit-user-select: none; | |
background-color: black; | |
} | |
#launch { | |
background-color: rgba(0, 0, 0, 0.7); | |
cursor: pointer; | |
} | |
.green-flag { | |
width: 80px; | |
height: 80px; | |
padding: 16px; | |
border-radius: 100%; | |
background: rgba(255, 255, 255, 0.75); | |
border: 3px solid hsla(0, 100%, 100%, 1); | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
box-sizing: border-box; | |
} | |
.progress-bar-outer { | |
border: 1px solid currentColor; | |
height: 10px; | |
width: 200px; | |
max-width: 200px; | |
} | |
.progress-bar-inner { | |
height: 100%; | |
width: 0; | |
background-color: currentColor; | |
} | |
.control-button { | |
width: 2rem; | |
height: 2rem; | |
padding: 0.375rem; | |
border-radius: 0.25rem; | |
margin-top: 0.5rem; | |
margin-bottom: 0.5rem; | |
user-select: none; | |
-webkit-user-select: none; | |
cursor: pointer; | |
border: 0; | |
border-radius: 4px; | |
} | |
.control-button:hover { | |
background: hsla(0, 100%, 65%, 0.15); | |
} | |
.control-button.active { | |
background: hsla(0, 100%, 65%, 0.35); | |
} | |
.fullscreen-button { | |
background: white ; | |
} | |
#error-information { | |
font-family: monospace; | |
} | |
</style> | |
</head> | |
<body> | |
<noscript>Enable JavaScript</noscript> | |
<div id="app"></div> | |
<div id="launch" class="screen" hidden title="Click to start"> | |
<div class="green-flag"> | |
<svg viewBox="0 0 16.63 17.5" width="42" height="44"> | |
<defs><style>.cls-1,.cls-2{fill:#4cbf56;stroke:#45993d;stroke-linecap:round;stroke-linejoin:round;}.cls-2{stroke-width:1.5px;}</style></defs> | |
<path class="cls-1" d="M.75,2A6.44,6.44,0,0,1,8.44,2h0a6.44,6.44,0,0,0,7.69,0V12.4a6.44,6.44,0,0,1-7.69,0h0a6.44,6.44,0,0,0-7.69,0"/> | |
<line class="cls-2" x1="0.75" y1="16.75" x2="0.75" y2="0.75"/> | |
</svg> | |
</div> | |
</div> | |
<div id="loading" class="screen"> | |
<div class="progress-bar-outer"><div class="progress-bar-inner" id="loading-inner"></div></div> | |
</div> | |
<div id="error" class="screen" hidden> | |
<h1>Error</h1> | |
<p id="error-information"></p> | |
</div> | |
<!-- If using Scaffolding as a library, update this path to where your scaffolding-[full|min].js is located. --> | |
<!-- This could also be a jsdelivr URL. --> | |
<script src="scaffolding/scaffolding-full.js"></script> | |
<script> | |
const PROJECT_ID = location.hash.substr(1) || '1'; | |
const appElement = document.getElementById('app'); | |
const launchScreen = document.getElementById('launch'); | |
const loadingScreen = document.getElementById('loading'); | |
const loadingInner = document.getElementById('loading-inner'); | |
const errorScreen = document.getElementById('error'); | |
const errorInformation = document.getElementById('error-information'); | |
const scaffolding = new Scaffolding.Scaffolding(); | |
scaffolding.setup(); | |
scaffolding.appendTo(appElement); | |
const {storage, vm} = scaffolding; | |
storage.addWebStore( | |
[storage.AssetType.Project], | |
(asset) => `https://projects.penguinmod.com/api/projects/getPublished?type=file&id=${asset.assetId}` | |
); | |
storage.addWebStore( | |
[storage.AssetType.ImageVector, storage.AssetType.ImageBitmap, storage.AssetType.Sound], | |
(asset) => `https://assets.scratch.mit.edu/internalapi/asset/${asset.assetId}.${asset.dataFormat}/get/` | |
); | |
const setProgress = (progress) => { | |
loadingInner.style.width = `${progress * 100}%`; | |
} | |
storage.onprogress = (total, loaded) => { | |
setProgress(0.2 + (loaded / total) * 0.8) | |
}; | |
setProgress(0.1); | |
const greenFlagButton = document.createElement('img'); | |
greenFlagButton.src = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16.63 17.5"><path d="M.75 2a6.44 6.44 0 017.69 0h0a6.44 6.44 0 007.69 0v10.4a6.44 6.44 0 01-7.69 0h0a6.44 6.44 0 00-7.69 0" fill="#007AF4" stroke="#003080" stroke-linecap="round" stroke-linejoin="round"/><path stroke-width="1.5" fill="#007AF4" stroke="#003080" stroke-linecap="round" stroke-linejoin="round" d="M.75 16.75v-16"/></svg>'); | |
greenFlagButton.className = 'control-button'; | |
greenFlagButton.addEventListener('click', () => { | |
scaffolding.greenFlag(); | |
}); | |
scaffolding.addEventListener('PROJECT_RUN_START', () => { | |
greenFlagButton.classList.add('active'); | |
}); | |
scaffolding.addEventListener('PROJECT_RUN_STOP', () => { | |
greenFlagButton.classList.remove('active'); | |
}); | |
scaffolding.addControlButton({ | |
element: greenFlagButton, | |
where: 'top-left' | |
}); | |
const stopAllButton = document.createElement('img'); | |
stopAllButton.src = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14"><path fill="#ec5959" stroke="#b84848" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M4.3.5h5.4l3.8 3.8v5.4l-3.8 3.8H4.3L.5 9.7V4.3z"/></svg>'); | |
stopAllButton.className = 'control-button'; | |
stopAllButton.addEventListener('click', () => { | |
scaffolding.stopAll(); | |
}); | |
scaffolding.addControlButton({ | |
element: stopAllButton, | |
where: 'top-left' | |
}); | |
if (document.fullscreenEnabled || document.webkitFullscreenEnabled) { | |
let isFullScreen = !!(document.fullscreenElement || document.webkitFullscreenElement); | |
const fullscreenButton = document.createElement('img'); | |
fullscreenButton.className = 'control-button fullscreen-button'; | |
fullscreenButton.addEventListener('click', () => { | |
if (isFullScreen) { | |
if (document.exitFullscreen) { | |
document.exitFullscreen(); | |
} else if (document.webkitExitFullscreen) { | |
document.webkitExitFullscreen(); | |
} | |
} else { | |
if (document.body.requestFullscreen) { | |
document.body.requestFullscreen() | |
} else if (document.body.webkitRequestFullscreen) { | |
document.body.webkitRequestFullscreen(); | |
} | |
} | |
}); | |
const updateFullScreen = () => { | |
isFullScreen = !!(document.fullscreenElement || document.webkitFullscreenElement); | |
if (isFullScreen) { | |
fullscreenButton.src = 'data:image/svg+xml,' + encodeURIComponent('<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><g fill="#575E75" fill-rule="evenodd"><path d="M12.662 3.65l.89.891 3.133-2.374a.815.815 0 011.15.165.819.819 0 010 .986L15.467 6.46l.867.871c.25.25.072.664-.269.664L12.388 8A.397.397 0 0112 7.611V3.92c0-.341.418-.514.662-.27M7.338 16.35l-.89-.89-3.133 2.374a.817.817 0 01-1.15-.166.819.819 0 010-.985l2.37-3.143-.87-.871a.387.387 0 01.27-.664L7.612 12a.397.397 0 01.388.389v3.692a.387.387 0 01-.662.27M7.338 3.65l-.89.891-3.133-2.374a.815.815 0 00-1.15.165.819.819 0 000 .986l2.37 3.142-.87.871a.387.387 0 00.27.664L7.612 8A.397.397 0 008 7.611V3.92a.387.387 0 00-.662-.27M12.662 16.35l.89-.89 3.133 2.374a.817.817 0 001.15-.166.819.819 0 000-.985l-2.368-3.143.867-.871a.387.387 0 00-.269-.664L12.388 12a.397.397 0 00-.388.389v3.692c0 .342.418.514.662.27"/></g></svg>'); | |
} else { | |
fullscreenButton.src = 'data:image/svg+xml,' + encodeURIComponent('<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><g fill="#575E75" fill-rule="evenodd"><path d="M16.338 7.35l-.89-.891-3.133 2.374a.815.815 0 01-1.15-.165.819.819 0 010-.986l2.368-3.142-.867-.871a.387.387 0 01.269-.664L16.612 3a.397.397 0 01.388.389V7.08a.387.387 0 01-.662.27M3.662 12.65l.89.89 3.133-2.374a.817.817 0 011.15.166.819.819 0 010 .985l-2.37 3.143.87.871c.248.25.071.664-.27.664L3.388 17A.397.397 0 013 16.611V12.92c0-.342.418-.514.662-.27M3.662 7.35l.89-.891 3.133 2.374a.815.815 0 001.15-.165.819.819 0 000-.986L6.465 4.54l.87-.871a.387.387 0 00-.27-.664L3.388 3A.397.397 0 003 3.389V7.08c0 .341.418.514.662.27M16.338 12.65l-.89.89-3.133-2.374a.817.817 0 00-1.15.166.819.819 0 000 .985l2.368 3.143-.867.871a.387.387 0 00.269.664l3.677.005a.397.397 0 00.388-.389V12.92a.387.387 0 00-.662-.27"/></g></svg>'); | |
} | |
}; | |
updateFullScreen(); | |
document.addEventListener('fullscreenchange', updateFullScreen); | |
document.addEventListener('webkitfullscreenchange', updateFullScreen); | |
scaffolding.addControlButton({ | |
element: fullscreenButton, | |
where: 'top-right' | |
}); | |
} | |
scaffolding.addCloudProvider(new Scaffolding.Cloud.WebSocketProvider('wss://clouddata.turbowarp.org', PROJECT_ID)); | |
const getProjectData = async () => { | |
const metadataResponse = await fetch(`https://trampoline.turbowarp.org/api/projects/${PROJECT_ID}`); | |
if (!metadataResponse.ok) { | |
throw new Error('Failed to load project metadata. It is probably unshared.'); | |
} | |
const projectMetadata = await metadataResponse.json(); | |
const token = projectMetadata.project_token; | |
const dataResponse = await fetch(`https://projects.scratch.mit.edu/${PROJECT_ID}?token=${token}`); | |
if (!dataResponse.ok) { | |
throw new Error('Failed to load project data.'); | |
} | |
return dataResponse.arrayBuffer(); | |
}; | |
const run = async () => { | |
const projectData = await getProjectData(); | |
setProgress(0.1); | |
await scaffolding.loadProject(projectData); | |
scaffolding.setUsername('123'); | |
setProgress(1); | |
loadingScreen.hidden = true; | |
launchScreen.hidden = false; | |
launchScreen.addEventListener('click', () => { | |
launchScreen.hidden = true; | |
scaffolding.start(); | |
}); | |
launchScreen.focus(); | |
}; | |
const handleError = (error) => { | |
console.error(error); | |
errorScreen.hidden = false; | |
errorInformation.textContent = `${error}`; | |
}; | |
run().catch(handleError); | |
</script> | |
</body> | |
</html> | |