|
| 1 | +<!DOCTYPE html> |
| 2 | +<html> |
| 3 | + <head> |
| 4 | + <meta charset="utf-8"> |
| 5 | + <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 6 | + <meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; connect-src * data: blob:"> |
| 7 | + <meta name="robots" content="noindex"> |
| 8 | + <title>Scaffolding Demo</title> |
| 9 | + <style> |
| 10 | + body { |
| 11 | + background-color: black; |
| 12 | + color: white; |
| 13 | + font-family: sans-serif; |
| 14 | + overflow: hidden; |
| 15 | + } |
| 16 | + [hidden] { |
| 17 | + display: none !important; |
| 18 | + } |
| 19 | + h1 { |
| 20 | + font-weight: normal; |
| 21 | + } |
| 22 | + a { |
| 23 | + color: inherit; |
| 24 | + text-decoration: underline; |
| 25 | + cursor: pointer; |
| 26 | + } |
| 27 | + |
| 28 | + #app, #loading, #error, #launch { |
| 29 | + position: absolute; |
| 30 | + top: 0; |
| 31 | + left: 0; |
| 32 | + width: 100%; |
| 33 | + height: 100%; |
| 34 | + } |
| 35 | + .screen { |
| 36 | + display: flex; |
| 37 | + flex-direction: column; |
| 38 | + align-items: center; |
| 39 | + justify-content: center; |
| 40 | + text-align: center; |
| 41 | + cursor: default; |
| 42 | + user-select: none; |
| 43 | + -webkit-user-select: none; |
| 44 | + background-color: black; |
| 45 | + } |
| 46 | + #launch { |
| 47 | + background-color: rgba(0, 0, 0, 0.7); |
| 48 | + cursor: pointer; |
| 49 | + } |
| 50 | + .green-flag { |
| 51 | + width: 80px; |
| 52 | + height: 80px; |
| 53 | + padding: 16px; |
| 54 | + border-radius: 100%; |
| 55 | + background: rgba(255, 255, 255, 0.75); |
| 56 | + border: 3px solid hsla(0, 100%, 100%, 1); |
| 57 | + display: flex; |
| 58 | + justify-content: center; |
| 59 | + align-items: center; |
| 60 | + box-sizing: border-box; |
| 61 | + } |
| 62 | + .progress-bar-outer { |
| 63 | + border: 1px solid currentColor; |
| 64 | + height: 10px; |
| 65 | + width: 200px; |
| 66 | + max-width: 200px; |
| 67 | + } |
| 68 | + .progress-bar-inner { |
| 69 | + height: 100%; |
| 70 | + width: 0; |
| 71 | + background-color: currentColor; |
| 72 | + } |
| 73 | + .control-button { |
| 74 | + width: 2rem; |
| 75 | + height: 2rem; |
| 76 | + padding: 0.375rem; |
| 77 | + border-radius: 0.25rem; |
| 78 | + margin-top: 0.5rem; |
| 79 | + margin-bottom: 0.5rem; |
| 80 | + user-select: none; |
| 81 | + -webkit-user-select: none; |
| 82 | + cursor: pointer; |
| 83 | + border: 0; |
| 84 | + border-radius: 4px; |
| 85 | + } |
| 86 | + .control-button:hover { |
| 87 | + background: hsla(0, 100%, 65%, 0.15); |
| 88 | + } |
| 89 | + .control-button.active { |
| 90 | + background: hsla(0, 100%, 65%, 0.35); |
| 91 | + } |
| 92 | + .fullscreen-button { |
| 93 | + background: white !important; |
| 94 | + } |
| 95 | + #error-information { |
| 96 | + font-family: monospace; |
| 97 | + } |
| 98 | + </style> |
| 99 | + </head> |
| 100 | + <body> |
| 101 | + <noscript>Enable JavaScript</noscript> |
| 102 | + |
| 103 | + <div id="app"></div> |
| 104 | + |
| 105 | + <div id="launch" class="screen" hidden title="Click to start"> |
| 106 | + <div class="green-flag"> |
| 107 | + <svg viewBox="0 0 16.63 17.5" width="42" height="44"> |
| 108 | + <defs><style>.cls-1,.cls-2{fill:#4cbf56;stroke:#45993d;stroke-linecap:round;stroke-linejoin:round;}.cls-2{stroke-width:1.5px;}</style></defs> |
| 109 | + <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"/> |
| 110 | + <line class="cls-2" x1="0.75" y1="16.75" x2="0.75" y2="0.75"/> |
| 111 | + </svg> |
| 112 | + </div> |
| 113 | + </div> |
| 114 | + |
| 115 | + <div id="loading" class="screen"> |
| 116 | + <div class="progress-bar-outer"><div class="progress-bar-inner" id="loading-inner"></div></div> |
| 117 | + </div> |
| 118 | + |
| 119 | + <div id="error" class="screen" hidden> |
| 120 | + <h1>Error</h1> |
| 121 | + <p id="error-information"></p> |
| 122 | + </div> |
| 123 | + |
| 124 | + <!-- If using Scaffolding as a library, update this path to where your scaffolding-[full|min].js is located. --> |
| 125 | + <!-- This could also be a jsdelivr URL. --> |
| 126 | + <script src="scaffolding/scaffolding-full.js"></script> |
| 127 | + |
| 128 | + <script> |
| 129 | + const PROJECT_ID = location.hash.substr(1) || '1'; |
| 130 | + |
| 131 | + const appElement = document.getElementById('app'); |
| 132 | + const launchScreen = document.getElementById('launch'); |
| 133 | + const loadingScreen = document.getElementById('loading'); |
| 134 | + const loadingInner = document.getElementById('loading-inner'); |
| 135 | + const errorScreen = document.getElementById('error'); |
| 136 | + const errorInformation = document.getElementById('error-information'); |
| 137 | + |
| 138 | + const scaffolding = new Scaffolding.Scaffolding(); |
| 139 | + scaffolding.setup(); |
| 140 | + scaffolding.appendTo(appElement); |
| 141 | + |
| 142 | + const {storage, vm} = scaffolding; |
| 143 | + storage.addWebStore( |
| 144 | + [storage.AssetType.ImageVector, storage.AssetType.ImageBitmap, storage.AssetType.Sound], |
| 145 | + (asset) => `https://assets.scratch.mit.edu/internalapi/asset/${asset.assetId}.${asset.dataFormat}/get/` |
| 146 | + ); |
| 147 | + const setProgress = (progress) => { |
| 148 | + loadingInner.style.width = `${progress * 100}%`; |
| 149 | + } |
| 150 | + storage.onprogress = (total, loaded) => { |
| 151 | + setProgress(0.2 + (loaded / total) * 0.8) |
| 152 | + }; |
| 153 | + setProgress(0.1); |
| 154 | + |
| 155 | + const greenFlagButton = document.createElement('img'); |
| 156 | + 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="#4cbf56" stroke="#45993d" stroke-linecap="round" stroke-linejoin="round"/><path stroke-width="1.5" fill="#4cbf56" stroke="#45993d" stroke-linecap="round" stroke-linejoin="round" d="M.75 16.75v-16"/></svg>'); |
| 157 | + greenFlagButton.className = 'control-button'; |
| 158 | + greenFlagButton.addEventListener('click', () => { |
| 159 | + scaffolding.greenFlag(); |
| 160 | + }); |
| 161 | + scaffolding.addEventListener('PROJECT_RUN_START', () => { |
| 162 | + greenFlagButton.classList.add('active'); |
| 163 | + }); |
| 164 | + scaffolding.addEventListener('PROJECT_RUN_STOP', () => { |
| 165 | + greenFlagButton.classList.remove('active'); |
| 166 | + }); |
| 167 | + scaffolding.addControlButton({ |
| 168 | + element: greenFlagButton, |
| 169 | + where: 'top-left' |
| 170 | + }); |
| 171 | + |
| 172 | + const stopAllButton = document.createElement('img'); |
| 173 | + 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>'); |
| 174 | + stopAllButton.className = 'control-button'; |
| 175 | + stopAllButton.addEventListener('click', () => { |
| 176 | + scaffolding.stopAll(); |
| 177 | + }); |
| 178 | + scaffolding.addControlButton({ |
| 179 | + element: stopAllButton, |
| 180 | + where: 'top-left' |
| 181 | + }); |
| 182 | + |
| 183 | + if (document.fullscreenEnabled || document.webkitFullscreenEnabled) { |
| 184 | + let isFullScreen = !!(document.fullscreenElement || document.webkitFullscreenElement); |
| 185 | + const fullscreenButton = document.createElement('img'); |
| 186 | + fullscreenButton.className = 'control-button fullscreen-button'; |
| 187 | + fullscreenButton.addEventListener('click', () => { |
| 188 | + if (isFullScreen) { |
| 189 | + if (document.exitFullscreen) { |
| 190 | + document.exitFullscreen(); |
| 191 | + } else if (document.webkitExitFullscreen) { |
| 192 | + document.webkitExitFullscreen(); |
| 193 | + } |
| 194 | + } else { |
| 195 | + if (document.body.requestFullscreen) { |
| 196 | + document.body.requestFullscreen() |
| 197 | + } else if (document.body.webkitRequestFullscreen) { |
| 198 | + document.body.webkitRequestFullscreen(); |
| 199 | + } |
| 200 | + } |
| 201 | + }); |
| 202 | + const updateFullScreen = () => { |
| 203 | + isFullScreen = !!(document.fullscreenElement || document.webkitFullscreenElement); |
| 204 | + if (isFullScreen) { |
| 205 | + 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>'); |
| 206 | + } else { |
| 207 | + 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>'); |
| 208 | + } |
| 209 | + }; |
| 210 | + updateFullScreen(); |
| 211 | + document.addEventListener('fullscreenchange', updateFullScreen); |
| 212 | + document.addEventListener('webkitfullscreenchange', updateFullScreen); |
| 213 | + scaffolding.addControlButton({ |
| 214 | + element: fullscreenButton, |
| 215 | + where: 'top-right' |
| 216 | + }); |
| 217 | + } |
| 218 | + |
| 219 | + scaffolding.addCloudProvider(new Scaffolding.Cloud.WebSocketProvider('wss://clouddata.turbowarp.org', PROJECT_ID)); |
| 220 | + |
| 221 | + const getProjectData = async () => { |
| 222 | + const metadataResponse = await fetch(`https://trampoline.turbowarp.org/api/projects/${PROJECT_ID}`); |
| 223 | + if (!metadataResponse.ok) { |
| 224 | + throw new Error('Failed to load project metadata. It is probably unshared.'); |
| 225 | + } |
| 226 | + const projectMetadata = await metadataResponse.json(); |
| 227 | + const token = projectMetadata.project_token; |
| 228 | + const dataResponse = await fetch(`https://projects.scratch.mit.edu/${PROJECT_ID}?token=${token}`); |
| 229 | + if (!dataResponse.ok) { |
| 230 | + throw new Error('Failed to load project data.'); |
| 231 | + } |
| 232 | + return dataResponse.arrayBuffer(); |
| 233 | + }; |
| 234 | + |
| 235 | + const run = async () => { |
| 236 | + const projectData = await getProjectData(); |
| 237 | + setProgress(0.1); |
| 238 | + await scaffolding.loadProject(projectData); |
| 239 | + scaffolding.setUsername('123'); |
| 240 | + setProgress(1); |
| 241 | + loadingScreen.hidden = true; |
| 242 | + launchScreen.hidden = false; |
| 243 | + launchScreen.addEventListener('click', () => { |
| 244 | + launchScreen.hidden = true; |
| 245 | + scaffolding.start(); |
| 246 | + }); |
| 247 | + launchScreen.focus(); |
| 248 | + }; |
| 249 | + |
| 250 | + const handleError = (error) => { |
| 251 | + console.error(error); |
| 252 | + errorScreen.hidden = false; |
| 253 | + errorInformation.textContent = `${error}`; |
| 254 | + }; |
| 255 | + |
| 256 | + run().catch(handleError); |
| 257 | + </script> |
| 258 | + </body> |
| 259 | +</html> |
0 commit comments