├── .gitignore ├── CHANGELOG ├── LICENSE ├── README.md ├── backend ├── SulfurousOffline.zip ├── admintools │ ├── admintools.js │ └── logs.json ├── cloud.js ├── cloudSave │ └── 237126447.json ├── package.js ├── package.json ├── sb2 │ ├── .gitkeep │ ├── 168825763project.json │ ├── 235462997project.json │ └── 237126447project.json ├── sb3 │ └── .gitkeep ├── sb3converter.js ├── server.js └── ssl │ └── .gitkeep ├── docker-compose.yml ├── docker-composeSULFSER.yml ├── frontend ├── main.js ├── package.json └── public │ ├── admintools │ └── index.html │ ├── css │ ├── app.css │ ├── embed.css │ ├── index.css │ └── player.css │ ├── fonts │ ├── DSEG7Classic-Light.ttf │ └── fonts.js │ ├── html │ ├── app.html │ ├── embed.html │ └── embedtest.html │ ├── img │ ├── favicon.ico │ ├── icons.svg │ └── sulfurouslogo.png │ ├── index.html │ ├── js │ ├── app.js │ ├── embed.js │ ├── fonts.js │ ├── gl-matrix-min.js │ ├── gyronorm.complete.min.js │ ├── index.js │ ├── jquery.qrcode.js │ ├── phosphorus.js │ ├── player.js │ ├── qrcode.min.js │ ├── shaders.js │ ├── socket.io.js │ ├── socket.io.js.map │ └── websocket.js │ └── soundbank │ ├── Instr.as │ ├── drums │ ├── BassDrum(1b)_22k.wav │ ├── Bongo_22k.wav │ ├── Cabasa(1)_22k.wav │ ├── Clap(1)_22k.wav │ ├── Claves(1)_22k.wav │ ├── Conga(1)_22k.wav │ ├── Cowbell(3)_22k.wav │ ├── Crash(2)_22k.wav │ ├── Cuica(2)_22k.wav │ ├── GuiroLong(1)_22k.wav │ ├── GuiroShort(1)_22k.wav │ ├── HiHatClosed(1)_22k.wav │ ├── HiHatOpen(2)_22k.wav │ ├── HiHatPedal(1)_22k.wav │ ├── Maracas(1)_22k.wav │ ├── SideStick(1)_22k.wav │ ├── SnareDrum(1)_22k.wav │ ├── Tambourine(3)_22k.wav │ ├── Tom(1)_22k.wav │ ├── Triangle(1)_22k.wav │ ├── Vibraslap(1)_22k.wav │ └── WoodBlock(1)_22k.wav │ └── instruments │ ├── AcousticGuitar_F3_22k.wav │ ├── AcousticPiano(5)_A%233_22k.wav │ ├── AcousticPiano(5)_A3_22k.wav │ ├── AcousticPiano(5)_C4_22k.wav │ ├── AcousticPiano(5)_C6_22k.wav │ ├── AcousticPiano(5)_D6_22k.wav │ ├── AcousticPiano(5)_D7_22k.wav │ ├── AcousticPiano(5)_F5_22k.wav │ ├── AcousticPiano(5)_G4_22k.wav │ ├── AltoSax(3)_C6_22k.wav │ ├── AltoSax_A3_22K.wav │ ├── BassTrombone_A2(2)_22k.wav │ ├── BassTrombone_A2(3)_22k.wav │ ├── Bassoon_C3_22k.wav │ ├── Cello(3)_A2_22k.wav │ ├── Cello(3b)_C2_22k.wav │ ├── Choir(4)_F3_22k.wav │ ├── Choir(4)_F4_22k.wav │ ├── Choir(4)_F5_22k.wav │ ├── Clarinet_C4_22k.wav │ ├── ElectricBass(2)_G1_22k.wav │ ├── ElectricGuitar(2)_F3(1)_22k.wav │ ├── ElectricPiano_C2_22k.wav │ ├── ElectricPiano_C4_22k.wav │ ├── EnglishHorn(1)_D4_22k.wav │ ├── EnglishHorn(1)_F3_22k.wav │ ├── Flute(3)_B5(1)_22k.wav │ ├── Flute(3)_B5(2)_22k.wav │ ├── Marimba_C4_22k.wav │ ├── MusicBox_C4_22k.wav │ ├── Organ(2)_G2_22k.wav │ ├── Pizz(2)_A3_22k.wav │ ├── Pizz(2)_E4_22k.wav │ ├── Pizz(2)_G2_22k.wav │ ├── SteelDrum_D5_22k.wav │ ├── SynthLead(6)_C4_22k.wav │ ├── SynthLead(6)_C6_22k.wav │ ├── SynthPad(2)_A3_22k.wav │ ├── SynthPad(2)_C6_22k.wav │ ├── TenorSax(1)_C3_22k.wav │ ├── Trombone_B3_22k.wav │ ├── Trumpet_E5_22k.wav │ ├── Vibraphone_C3_22k.wav │ ├── Violin(2)_D4_22K.wav │ ├── Violin(3)_A4_22k.wav │ ├── Violin(3b)_E5_22k.wav │ └── WoodenFlute_C5_22k.wav ├── imgs ├── img0.png ├── img1.png ├── img10.png ├── img11.png ├── img2.png ├── img3.png ├── img4.png ├── img5.png ├── img6.png ├── img7.png ├── img8.png └── img9.png ├── sb3tosb2 ├── dockerfile ├── sb2 │ └── .gitkeep ├── sb3 │ ├── .gitkeep │ └── converted │ │ └── .gitkeep └── src │ ├── LICENSE │ ├── README.md │ ├── run.sh │ └── sb3tosb2.py └── start ├── startSulurousCloud.bat ├── startSulurousCloud.sh ├── startSulurousSB2.bat ├── startSulurousSB2.sh ├── startSulurousSB3.bat └── startSulurousSB3.sh /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.sb2 3 | *.sb3 4 | package-lock.json 5 | .vscode/ -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Sulfurous Changelog 2 | 3 | 4 | Bugfixes: 5 | 6 | * SVGs (vector sprites) now load. (canvg was dropped) 7 | * SVGs render correctly in most cases. (text still doesn't work). 8 | * All sound is supported, doesn't always work in Firefox. (Chrome works) 9 | * Support for touchscreen and mouse on hybrid devices. 10 | * Costume selection emulates Scratch more closely now. 11 | * Sprite does not draw while being dragged. 12 | * UI is visible in fullscreen in Firefox. 13 | * SVGs without content no longer cause problems. 14 | * Loaded SVGs are hidden from DOM. 15 | * Default pen color is set (blue). 16 | 17 | Improvements: 18 | 19 | * Bitmaps render without image smoothing. 20 | * Vector sprites are rendered according to screen resolution, so they never pixelate. 21 | * Bitmaps embedded in SVG also render without image smoothing. 22 | 23 | New Features: 24 | 25 | * Some graphic effects were added: Pixelate, Mosaic, Brightness. 26 | * Adjustable screen size: Package and Embed options allow setting a fixed screen size. 27 | 28 | Planned/Ideas: 29 | 30 | * Add all visual effects. 31 | * Cloud vars, possibly using cookies. 32 | * Networking between projects (like mesh). 33 | * Insertion of custom code in Scratch project. 34 | * Access to rendering options through Scratch project. 35 | * Import/export of local files into Scratch project. 36 | * Compiled Javascript code viewer. 37 | * WebGL rendering. 38 | * Different rendering of speech bubbles. (not in DOM). 39 | * Fix SVG text and font. 40 | * Render lists (show list/hide list). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014 Nathan Dinsmore 4 | Copyright (c) 2016 Mittagskogel 5 | Copyright (c) 2017-2020 FRALEX 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /backend/SulfurousOffline.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/backend/SulfurousOffline.zip -------------------------------------------------------------------------------- /backend/admintools/admintools.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | let logs; 4 | 5 | var setupAdminTools = function (io) { 6 | 7 | console.log(__dirname) 8 | 9 | logs = JSON.parse(fs.readFileSync(__dirname + "/logs.json")) 10 | 11 | io = io.of('/admintools') 12 | io.on('connection', function (socket) { 13 | console.log("client connected") 14 | 15 | socket.on("getLogs", function () { 16 | 17 | calculateLogs() 18 | socket.emit("logs", { 19 | "lastHour": logs.requests.lastHour, 20 | "last24Hours": logs.requests.last24Hours, 21 | "last30Days": logs.requests.last30Days, 22 | "currentConnections": logs.currentConnections, 23 | "SB2IDs": logs.lastSB2IDs, 24 | "SB3IDs": logs.lastSB3IDs, 25 | "landingPageLoad": logs.landingPageLoad, 26 | "SB3Load": logs.SB3Load, 27 | "SB2Load": logs.SB2Load 28 | }) 29 | }) 30 | 31 | 32 | 33 | }); 34 | 35 | 36 | 37 | calculateLogs() 38 | console.log(logs) 39 | 40 | console.log("ADMINTOOLS SCRIPT") 41 | } 42 | 43 | 44 | function calculateLogs() { 45 | console.log({ "lastHour": logs.requests.lastHour, "last24Hours": logs.requests.last24Hours, "last30Days": logs.requests.last30Days, "currentConnections": logs.currentConnections }) 46 | logs.requests.lastHour = 0; 47 | logs.requests.last24Hours = 0; 48 | logs.requests.last30Days = 0; 49 | logs.lastSB2IDs = {}; 50 | logs.lastSB3IDs = {}; 51 | logs.landingPageLoad = 0; 52 | logs.SB2Load = 0; 53 | logs.SB3Load = 0; 54 | logs.requests.all.forEach(element => { 55 | // console.log(element) 56 | //console.log(new Date(element.timestamp).getTime() + " " + (new Date().getTime() + (3600 * 1000)) + " " + (new Date() - new Date(element.timestamp).getTime())) 57 | 58 | if ((new Date() - new Date(element.timestamp).getTime()) < (3600 * 1000)) { 59 | logs.requests.lastHour++; 60 | } 61 | if ((new Date() - new Date(element.timestamp).getTime()) < (3600 * 1000 * 24)) { 62 | logs.requests.last24Hours++; 63 | } 64 | if ((new Date() - new Date(element.timestamp).getTime()) < (3600 * 1000 * 24 * 30)) { 65 | logs.requests.last30Days++; 66 | } 67 | 68 | 69 | 70 | if (element.data.id != "none" && element.data.id.length < 8 || element.data.id.length > 9) { 71 | console.log("WRONG ID WRONG") 72 | console.log(element.data.id) 73 | } else { 74 | if (element.data.version == 2) { 75 | 76 | if (logs.lastSB2IDs[element.data.id] == undefined) { 77 | logs.lastSB2IDs[element.data.id] = 1 78 | } else { 79 | logs.lastSB2IDs[element.data.id]++; 80 | } 81 | logs.SB2Load++; 82 | } else if (element.data.version == 3) { 83 | 84 | if (logs.lastSB3IDs[element.data.id] == undefined) { 85 | logs.lastSB3IDs[element.data.id] = 1 86 | } else { 87 | logs.lastSB3IDs[element.data.id]++; 88 | } 89 | logs.SB3Load++; 90 | } else { 91 | logs.landingPageLoad++; 92 | } 93 | } 94 | 95 | }); 96 | 97 | } 98 | 99 | 100 | var setCurrentConnections = function (data) { 101 | logs.currentConnections = data; 102 | } 103 | 104 | var logRequest = function (data) { 105 | console.log("log ------") 106 | logs.requests.all.push({ "data": data, "timestamp": new Date() }) 107 | 108 | 109 | calculateLogs(); 110 | 111 | } 112 | 113 | setInterval(() => { 114 | 115 | fs.writeFileSync(__dirname + "/logs.json", JSON.stringify(logs)) 116 | }, 10000); 117 | 118 | module.exports = { setupAdminTools, logRequest, setCurrentConnections } -------------------------------------------------------------------------------- /backend/admintools/logs.json: -------------------------------------------------------------------------------- 1 | {"requests":{"lastHour":31,"last24Hours":69,"last30Days":102,"all":[{"data":{"id":"none","version":"-1"},"timestamp":"2022-10-23T09:38:43.619Z"},{"data":{"id":"none","version":"-1"},"timestamp":"2022-10-26T21:27:25.277Z"},{"data":{"id":"none","version":"-1"},"timestamp":"2022-11-26T09:47:59.716Z"},{"data":{"id":"16207935","version":"2"},"timestamp":"2023-05-22T18:02:58.494Z"},{"data":{"id":"16207935","version":"2"},"timestamp":"2023-05-22T18:05:18.439Z"},{"data":{"id":"16207935","version":"2"},"timestamp":"2023-05-22T18:11:59.477Z"},{"data":{"id":"16207935","version":"2"},"timestamp":"2023-05-22T18:19:15.394Z"},{"data":{"id":"16207935","version":"2"},"timestamp":"2023-05-22T18:19:36.393Z"},{"data":{"id":"16207935","version":"2"},"timestamp":"2023-05-22T18:20:28.725Z"},{"data":{"id":"16207935","version":"2"},"timestamp":"2023-05-22T18:22:12.665Z"},{"data":{"id":"16207935","version":"2"},"timestamp":"2023-05-22T18:31:37.044Z"},{"data":{"id":"none","version":"-1"},"timestamp":"2023-05-22T18:32:01.307Z"},{"data":{"id":"34791164","version":"2"},"timestamp":"2023-05-22T18:32:30.719Z"},{"data":{"id":"10128407","version":"2"},"timestamp":"2023-05-29T09:31:33.610Z"},{"data":{"id":"10128407","version":"2"},"timestamp":"2023-05-29T09:31:41.602Z"},{"data":{"id":"16205373","version":"2"},"timestamp":"2023-05-29T11:17:06.930Z"},{"data":{"id":"10128407","version":"2"},"timestamp":"2023-05-29T11:17:24.363Z"},{"data":{"id":"16207935","version":"2"},"timestamp":"2023-05-29T11:18:30.758Z"},{"data":{"id":"none","version":"-1"},"timestamp":"2023-06-02T08:47:30.703Z"},{"data":{"id":"15945630","version":"2"},"timestamp":"2023-06-02T08:47:53.515Z"},{"data":{"id":"16205373","version":"2"},"timestamp":"2023-06-02T08:48:14.001Z"},{"data":{"id":"802434827","version":"2"},"timestamp":"2023-06-02T08:49:08.998Z"},{"data":{"id":"802434827","version":"2"},"timestamp":"2023-06-02T08:53:10.290Z"},{"data":{"id":"802434827","version":"2"},"timestamp":"2023-06-02T08:53:59.443Z"},{"data":{"id":"802434827","version":"2"},"timestamp":"2023-06-02T08:54:27.389Z"},{"data":{"id":"802434827","version":"2"},"timestamp":"2023-06-02T08:57:09.838Z"},{"data":{"id":"802434827","version":"2"},"timestamp":"2023-06-02T08:58:16.507Z"},{"data":{"id":"16207935","version":"2"},"timestamp":"2023-06-02T08:58:25.113Z"},{"data":{"id":"16207935","version":"2"},"timestamp":"2023-06-02T09:00:02.132Z"},{"data":{"id":"802434827","version":"3"},"timestamp":"2023-06-02T09:04:34.499Z"},{"data":{"id":"802434827","version":"3"},"timestamp":"2023-06-02T09:04:51.858Z"},{"data":{"id":"802434827","version":"3"},"timestamp":"2023-06-02T09:06:37.102Z"},{"data":{"id":"802434827","version":"3"},"timestamp":"2023-06-02T09:07:57.897Z"},{"data":{"id":"802434827","version":"3"},"timestamp":"2023-06-02T09:12:13.708Z"},{"data":{"id":"400185398","version":"3"},"timestamp":"2023-06-02T09:13:16.981Z"},{"data":{"id":"278296619","version":"3"},"timestamp":"2023-06-02T09:14:32.901Z"},{"data":{"id":"none","version":"-1"},"timestamp":"2023-06-06T10:23:04.871Z"},{"data":{"id":"861888652","version":"3"},"timestamp":"2023-06-06T10:23:13.323Z"},{"data":{"id":"861888652","version":"3"},"timestamp":"2023-06-06T10:41:44.580Z"},{"data":{"id":"861888652","version":"3"},"timestamp":"2023-06-06T10:43:52.142Z"},{"data":{"id":"861888652","version":"3"},"timestamp":"2023-06-06T10:44:01.762Z"},{"data":{"id":"861888652","version":"3"},"timestamp":"2023-06-06T10:45:10.317Z"},{"data":{"id":"861888652","version":"3"},"timestamp":"2023-06-06T10:46:23.833Z"},{"data":{"id":"235462997","version":"3"},"timestamp":"2023-06-06T11:37:14.786Z"},{"data":{"id":"235462997","version":"3"},"timestamp":"2023-06-06T11:44:06.135Z"},{"data":{"id":"235462997","version":"3"},"timestamp":"2023-06-06T11:45:59.748Z"},{"data":{"id":"235462997","version":"3"},"timestamp":"2023-06-06T11:46:24.098Z"},{"data":{"id":"235462997","version":"3"},"timestamp":"2023-06-06T11:46:57.605Z"},{"data":{"id":"174772777","version":"2"},"timestamp":"2023-06-06T11:55:13.656Z"},{"data":{"id":"168825763","version":"3"},"timestamp":"2023-06-06T12:00:42.041Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:01:25.203Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:01:55.875Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:02:47.056Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:08:06.680Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:08:33.519Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:10:27.853Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:10:43.182Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:11:51.353Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:13:47.229Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:14:54.237Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:16:16.290Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:17:18.294Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:19:43.292Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:20:32.774Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:21:34.348Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:21:54.836Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:22:21.199Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:23:39.806Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:24:21.826Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:25:39.760Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:26:35.834Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:26:55.039Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:27:53.964Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T12:28:25.412Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:07:34.140Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:10:17.329Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:10:34.806Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:11:04.817Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:11:43.043Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:12:21.219Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:12:56.446Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:13:51.536Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:21:08.232Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:21:43.257Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:22:13.128Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:23:56.549Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:24:06.341Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:24:24.164Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:24:44.493Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:25:02.173Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:29:13.903Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:30:40.202Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:33:43.722Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:34:08.452Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:35:31.807Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:36:36.216Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:36:52.290Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:37:45.813Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:38:37.440Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:38:52.765Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:39:24.180Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:41:16.997Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:41:59.016Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:42:10.218Z"},{"data":{"id":"237126447","version":"3"},"timestamp":"2023-06-06T14:42:25.349Z"}]},"currentConnections":1,"lastSB2IDs":{"10128407":3,"15945630":1,"16205373":2,"16207935":11,"34791164":1,"174772777":1,"802434827":6},"lastSB3IDs":{"168825763":1,"235462997":5,"237126447":55,"278296619":1,"400185398":1,"802434827":5,"861888652":6},"landingPageLoad":6,"SB2Load":25,"SB3Load":74} -------------------------------------------------------------------------------- /backend/cloud.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | var autoSaveInterval = 100000 4 | var ttlInterval = 1000 5 | 6 | var CLOUDSAVE = {} 7 | 8 | var loadJSON = function (projectID) { 9 | 10 | console.log("[CLOUD] loading JSON for " + projectID); 11 | 12 | try { 13 | var content = fs.readFileSync('./cloudSave/' + projectID + '.json'); 14 | CLOUDSAVE[projectID] = JSON.parse(content); 15 | console.log("[CLOUD] DONE loading JSON for " + projectID); 16 | } catch (err) { 17 | 18 | CLOUDSAVE[projectID] = { "vars": {} }; 19 | createProjectJSON(projectID, CLOUDSAVE[projectID]) 20 | console.log("[CLOUD] DONE loading JSON for " + projectID); 21 | } 22 | 23 | } 24 | 25 | var getReq = function (data) { 26 | 27 | data.sulfCloudVarsChanged = data.sulfCloudVars; 28 | 29 | delete data.sulfCloudVars 30 | 31 | 32 | if (typeof CLOUDSAVE[data.projectID] == 'undefined') { 33 | 34 | loadJSON(data.projectID); 35 | 36 | } 37 | 38 | if (data.sulfCloudVarsChanged == undefined) { 39 | return 40 | } 41 | 42 | if (typeof Object.keys(data.sulfCloudVarsChanged)[0] != 'undefined') { 43 | 44 | console.log("got request") 45 | console.log(data) 46 | for (var i = 0; i < Object.keys(data.sulfCloudVarsChanged).length; i++) { 47 | //console.log(Object.keys(data.sulfCloudVarsChanged)[i]); 48 | 49 | CLOUDSAVE[data.projectID].vars[Object.keys(data.sulfCloudVarsChanged)[i]] = data.sulfCloudVarsChanged[Object.keys(data.sulfCloudVarsChanged)[i]]; 50 | 51 | } 52 | 53 | } 54 | 55 | CLOUDSAVE[data.projectID].ttl = 10; 56 | } 57 | 58 | 59 | setInterval(async function () { 60 | console.log("[CLOUD] Auto saving"); 61 | Object.keys(CLOUDSAVE).forEach(element => { 62 | saveProjectJSON(element); 63 | }); 64 | 65 | }, autoSaveInterval); 66 | 67 | setInterval(function () { 68 | Object.keys(CLOUDSAVE).forEach(element => { 69 | CLOUDSAVE[element].ttl -= 1; 70 | if (CLOUDSAVE[element].ttl == 0) { 71 | 72 | saveProjectJSON(element, true); 73 | console.log("[CLOUD] deleted project JSON from RAM for " + element); 74 | } 75 | }); 76 | }, ttlInterval); 77 | 78 | function saveProjectJSON(projectID, delFlag) { 79 | var tempTTL = CLOUDSAVE[projectID].ttl; 80 | delete CLOUDSAVE[projectID].ttl; 81 | fs.writeFile('./cloudSave/' + projectID + '.json', JSON.stringify(CLOUDSAVE[projectID]), function (err) { 82 | if (err) { 83 | return console.log(err); 84 | } 85 | if (delFlag) { 86 | delete CLOUDSAVE[projectID]; 87 | } else { 88 | CLOUDSAVE[projectID].ttl = tempTTL; 89 | } 90 | console.log("[CLOUD] project file saved for " + projectID); 91 | }); 92 | } 93 | 94 | 95 | function createProjectJSON(projectID, data) { 96 | 97 | fs.writeFile('./cloudSave/' + projectID + '.json', JSON.stringify(data), function (err) { 98 | if (err) { 99 | return console.log(err); 100 | } 101 | 102 | console.log("[CLOUD] Created project file for " + projectID); 103 | }); 104 | 105 | } 106 | 107 | 108 | 109 | module.exports = { CLOUDSAVE, getReq }; -------------------------------------------------------------------------------- /backend/cloudSave/237126447.json: -------------------------------------------------------------------------------- 1 | {"vars":{"sulf.c.liste":["sss","awdawdawdad","awdaw","adawd","awd "]}} -------------------------------------------------------------------------------- /backend/package.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | var JSZip = require("jszip"); 3 | var generatePackageFromZip = function (project, settings, callback) { 4 | var zip = new JSZip(); 5 | fs.readFile("./SulfurousOffline.zip", function (err, data) { 6 | zip.loadAsync(data) 7 | .then(function (zip) { 8 | //console.log(zip) 9 | // you now have every files contained in the loaded zip 10 | 11 | zip.file("settings.json", JSON.stringify(settings)) 12 | zip.file("project.sb2", project); // a promise of "Hello World\n" 13 | 14 | zip.generateAsync({ type: "arraybuffer" }).then(function (base64) { 15 | 16 | callback(base64); 17 | }); 18 | }); 19 | }) 20 | } 21 | 22 | const fetch = require('node-fetch'); 23 | 24 | 25 | var getProjectDATA = async function (projectID) { 26 | //console.log(projectJSONBaseURL + projectID) 27 | const req = await fetch('https://api.scratch.mit.edu/projects/' + projectID); 28 | return req.json() 29 | } 30 | 31 | var getProjectJSON = async function (projectID,token) { 32 | //console.log(projectJSONBaseURL + projectID) 33 | const req = await fetch("https://projects.scratch.mit.edu/" + projectID + "?token="+token); 34 | return req.json() 35 | } 36 | 37 | var getAsset = async function (md5) { 38 | //console.log(projectJSONBaseURL + projectID) 39 | const req = await fetch("https://cdn.assets.scratch.mit.edu/internalapi/asset/" + md5 + "/get"); 40 | return req 41 | } 42 | 43 | var generatePackageFromID = function (id, settings, callback) { 44 | var zip = new JSZip(); 45 | var project = new JSZip(); 46 | fs.readFile("./SulfurousOffline.zip", function (err, data) { 47 | 48 | zip.loadAsync(data).then(async function (zip) { 49 | //console.log(zip) 50 | // you now have every files contained in the loaded zip 51 | 52 | let projectDATA = await getProjectDATA(id) 53 | console.log(projectDATA) 54 | 55 | 56 | getProjectJSON(id,projectDATA.project_token).then(async res => { 57 | 58 | // var filemap = parseMap(res.targets); 59 | console.log(id); 60 | let costumeId = 0; 61 | let soundId = 0; 62 | let totalAssets = 0; 63 | let assets = { sounds: [], costumes: [] } 64 | 65 | 66 | if (res.hasOwnProperty("costumes")) { 67 | for (let index = 0; index < res.costumes.length; index++) { 68 | var current = res.costumes[index]; 69 | 70 | current.baseLayerID = costumeId; 71 | costumeId++; 72 | totalAssets++; 73 | assets.costumes.push(current); 74 | 75 | } 76 | } 77 | if (res.hasOwnProperty("sounds")) { 78 | for (let index = 0; index < res.sounds.length - 1; index++) { 79 | var current = res.sounds[index]; 80 | current.soundID = soundId; 81 | soundId++; 82 | totalAssets++; 83 | assets.sounds.push(current); 84 | } 85 | } 86 | 87 | if (res.hasOwnProperty("children")) { 88 | res.children.forEach(element => { 89 | if (element.hasOwnProperty("costumes")) { 90 | for (var i = 0; i < element.costumes.length; i++) { 91 | var current = element.costumes[i]; 92 | current.baseLayerID = costumeId; 93 | costumeId++; 94 | totalAssets++; 95 | assets.costumes.push(current); 96 | } 97 | } 98 | if (element.hasOwnProperty("sounds")) { 99 | for (var i = 0; i < element.sounds.length; i++) { 100 | var current = element.sounds[i]; 101 | current.soundID = soundId; 102 | soundId++; 103 | totalAssets++; 104 | assets.sounds.push(current); 105 | } 106 | } 107 | 108 | }); 109 | 110 | } 111 | 112 | 113 | 114 | for (let index = 0; index < assets.sounds.length; index++) { 115 | const element = assets.sounds[index]; 116 | let data = await getAsset(element.md5) 117 | project.file(element.soundID + "." + element.md5.split(".")[1], data.buffer()); 118 | } 119 | for (let index = 0; index < assets.costumes.length; index++) { 120 | const element = assets.costumes[index]; 121 | let data = await getAsset(element.baseLayerMD5) 122 | project.file(element.baseLayerID + "." + element.baseLayerMD5.split(".")[1], data.buffer()); 123 | } 124 | 125 | 126 | 127 | project.file("project.json", JSON.stringify(res)); 128 | 129 | 130 | // fs.writeFileSync("./sb2/" + projectID + "project.json", outJSON) 131 | // newZip.addFile("project.json", Buffer.alloc(outJSON.length, outJSON)); 132 | 133 | }).then(() => { 134 | 135 | project.generateAsync({ type: "arraybuffer" }).then(function (base64) { 136 | 137 | //console.log("-------------------------------------------") 138 | //console.log(base64) 139 | 140 | zip.file("settings.json", JSON.stringify(settings)) 141 | 142 | zip.file("project.sb2", base64); // a promise of "Hello World\n" 143 | // console.log(project) 144 | zip.generateAsync({ type: "arraybuffer" }).then(function (base64) { 145 | callback(base64); 146 | }); 147 | }); 148 | 149 | 150 | }) 151 | 152 | }) 153 | }) 154 | } 155 | 156 | module.exports = { generatePackageFromZip, generatePackageFromID } -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sulfurous-backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "npm i && node server.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Mittagskogel/Sulfurous.git" 12 | }, 13 | "author": "Alexander Pichler", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/Mittagskogel/Sulfurous/issues" 17 | }, 18 | "homepage": "https://github.com/Mittagskogel/Sulfurous#readme", 19 | "dependencies": { 20 | "adm-zip": "^0.4.16", 21 | "axios": "^1.4.0", 22 | "express": "^4.17.1", 23 | "jszip": "^3.5.0", 24 | "node-fetch": "^2.6.0", 25 | "socket.io": "^2.3.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/sb2/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/backend/sb2/.gitkeep -------------------------------------------------------------------------------- /backend/sb2/168825763project.json: -------------------------------------------------------------------------------- 1 | {"targets":[{"isStage":true,"name":"Stage","variables":{"{wQSVFJ;-}_(VQqvyvBw-x-":["x","-190"],"{wQSVFJ;-}_(VQqvyvBw-y-":["y",-225],"{wQSVFJ;-}_(VQqvyvBw-c-":["c",210],"{wQSVFJ;-}_(VQqvyvBw-i-":["i",0]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"assetId":"797b03bdb8cf6ccfc30c0692d533d998","name":"backdrop1","bitmapResolution":2,"md5ext":"797b03bdb8cf6ccfc30c0692d533d998.png","dataFormat":"png","rotationCenterX":480,"rotationCenterY":360}],"sounds":[{"assetId":"83a9787d4cb6f3b7632b4ddfebf74367","name":"pop","dataFormat":"wav","format":"","rate":48000,"sampleCount":1123,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"}],"volume":100,"layerOrder":0,"tempo":60,"videoTransparency":50,"videoState":"off","textToSpeechLanguage":null},{"isStage":false,"name":"Sprite1","variables":{},"lists":{},"broadcasts":{},"blocks":{"rG]J%Yds`O_.63h~+R0t":{"opcode":"control_start_as_clone","next":";x~K/LC^RPFJuh+Uhyh[","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":657,"y":246},";x~K/LC^RPFJuh+Uhyh[":{"opcode":"looks_setsizeto","next":"T57jyys]]#}5ZsCi@;-h","parent":"rG]J%Yds`O_.63h~+R0t","inputs":{"SIZE":[1,[4,"80"]]},"fields":{},"shadow":false,"topLevel":false},"T57jyys]]#}5ZsCi@;-h":{"opcode":"motion_gotoxy","next":"5|l+n*%LW*:%:z#/*/{3","parent":";x~K/LC^RPFJuh+Uhyh[","inputs":{"X":[3,[12,"x","{wQSVFJ;-}_(VQqvyvBw-x-"],[4,"10"]],"Y":[3,[12,"y","{wQSVFJ;-}_(VQqvyvBw-y-"],[4,"10"]]},"fields":{},"shadow":false,"topLevel":false},"5|l+n*%LW*:%:z#/*/{3":{"opcode":"looks_seteffectto","next":"yY?Q1JmUjMY*bjp_7[3G","parent":"T57jyys]]#}5ZsCi@;-h","inputs":{"VALUE":[3,[12,"c","{wQSVFJ;-}_(VQqvyvBw-c-"],[4,"10"]]},"fields":{"EFFECT":["color",null]},"shadow":false,"topLevel":false},"yY?Q1JmUjMY*bjp_7[3G":{"opcode":"data_changevariableby","next":"|}rPW*eFpYSSpWeAkyB,","parent":"5|l+n*%LW*:%:z#/*/{3","inputs":{"VALUE":[1,[4,"10"]]},"fields":{"VARIABLE":["c","{wQSVFJ;-}_(VQqvyvBw-c-"]},"shadow":false,"topLevel":false},"|}rPW*eFpYSSpWeAkyB,":{"opcode":"data_changevariableby","next":"ArD];;-IPj.3Kf7Gxrx}","parent":"yY?Q1JmUjMY*bjp_7[3G","inputs":{"VALUE":[1,[4,"80"]]},"fields":{"VARIABLE":["x","{wQSVFJ;-}_(VQqvyvBw-x-"]},"shadow":false,"topLevel":false},"ArD];;-IPj.3Kf7Gxrx}":{"opcode":"data_changevariableby","next":"lS~KCI#X#zUhbf(7A-Q;","parent":"|}rPW*eFpYSSpWeAkyB,","inputs":{"VALUE":[1,[4,"1"]]},"fields":{"VARIABLE":["i","{wQSVFJ;-}_(VQqvyvBw-i-"]},"shadow":false,"topLevel":false},"lS~KCI#X#zUhbf(7A-Q;":{"opcode":"control_if","next":null,"parent":"ArD];;-IPj.3Kf7Gxrx}","inputs":{"CONDITION":[2,"umpfA{L9e9-xxYcmutvm"],"SUBSTACK":[2,"or7!fqKC!jMRP`TLig,s"]},"fields":{},"shadow":false,"topLevel":false},"umpfA{L9e9-xxYcmutvm":{"opcode":"operator_equals","next":null,"parent":"lS~KCI#X#zUhbf(7A-Q;","inputs":{"OPERAND1":[3,[12,"i","{wQSVFJ;-}_(VQqvyvBw-i-"],[10,""]],"OPERAND2":[1,[10,"6"]]},"fields":{},"shadow":false,"topLevel":false},"or7!fqKC!jMRP`TLig,s":{"opcode":"data_setvariableto","next":"6cstwa9S!Z23%g|aG~wj","parent":"lS~KCI#X#zUhbf(7A-Q;","inputs":{"VALUE":[1,[10,"0"]]},"fields":{"VARIABLE":["i","{wQSVFJ;-}_(VQqvyvBw-i-"]},"shadow":false,"topLevel":false},"6cstwa9S!Z23%g|aG~wj":{"opcode":"data_setvariableto","next":".+|OC!8HU1voxb*2-]QB","parent":"or7!fqKC!jMRP`TLig,s","inputs":{"VALUE":[1,[10,"-190"]]},"fields":{"VARIABLE":["x","{wQSVFJ;-}_(VQqvyvBw-x-"]},"shadow":false,"topLevel":false},".+|OC!8HU1voxb*2-]QB":{"opcode":"data_setvariableto","next":null,"parent":"6cstwa9S!Z23%g|aG~wj","inputs":{"VALUE":[3,";%[D,O7zQL`s??7MuAp2",[10,""]]},"fields":{"VARIABLE":["y","{wQSVFJ;-}_(VQqvyvBw-y-"]},"shadow":false,"topLevel":false},";%[D,O7zQL`s??7MuAp2":{"opcode":"operator_subtract","next":null,"parent":".+|OC!8HU1voxb*2-]QB","inputs":{"NUM1":[3,[12,"y","{wQSVFJ;-}_(VQqvyvBw-y-"],[4,"10"]],"NUM2":[1,[4,"90"]]},"fields":{},"shadow":false,"topLevel":false},"rO@J5sCw8}duwcYUQlD2":{"opcode":"event_whenflagclicked","next":"`En:-Y|D@XlyFKTHiEx)","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":187,"y":290},"`En:-Y|D@XlyFKTHiEx)":{"opcode":"looks_setsizeto","next":"gym!UCxYOg_MhuF2Cp?q","parent":"rO@J5sCw8}duwcYUQlD2","inputs":{"SIZE":[1,[4,"80"]]},"fields":{},"shadow":false,"topLevel":false},"gym!UCxYOg_MhuF2Cp?q":{"opcode":"data_setvariableto","next":"I;ta2=fx?|@ZocAtLw)H","parent":"`En:-Y|D@XlyFKTHiEx)","inputs":{"VALUE":[1,[10,"-195"]]},"fields":{"VARIABLE":["x","{wQSVFJ;-}_(VQqvyvBw-x-"]},"shadow":false,"topLevel":false},"I;ta2=fx?|@ZocAtLw)H":{"opcode":"data_setvariableto","next":"x:T.dt,[?E^1~GLr4~Tl","parent":"gym!UCxYOg_MhuF2Cp?q","inputs":{"VALUE":[1,[10,"135"]]},"fields":{"VARIABLE":["y","{wQSVFJ;-}_(VQqvyvBw-y-"]},"shadow":false,"topLevel":false},"x:T.dt,[?E^1~GLr4~Tl":{"opcode":"motion_gotoxy","next":"TMScQo`|IGe{T@]IYa}P","parent":"I;ta2=fx?|@ZocAtLw)H","inputs":{"X":[3,[12,"x","{wQSVFJ;-}_(VQqvyvBw-x-"],[4,"10"]],"Y":[3,[12,"y","{wQSVFJ;-}_(VQqvyvBw-y-"],[4,"10"]]},"fields":{},"shadow":false,"topLevel":false},"TMScQo`|IGe{T@]IYa}P":{"opcode":"data_setvariableto","next":"Ua`2W!KVCsaVQzrYvO{8","parent":"x:T.dt,[?E^1~GLr4~Tl","inputs":{"VALUE":[1,[10,"-110"]]},"fields":{"VARIABLE":["x","{wQSVFJ;-}_(VQqvyvBw-x-"]},"shadow":false,"topLevel":false},"Ua`2W!KVCsaVQzrYvO{8":{"opcode":"data_setvariableto","next":"h0[Cyb2FIAVYQP~pQy;/","parent":"TMScQo`|IGe{T@]IYa}P","inputs":{"VALUE":[1,[10,"10"]]},"fields":{"VARIABLE":["c","{wQSVFJ;-}_(VQqvyvBw-c-"]},"shadow":false,"topLevel":false},"h0[Cyb2FIAVYQP~pQy;/":{"opcode":"control_repeat","next":null,"parent":"Ua`2W!KVCsaVQzrYvO{8","inputs":{"TIMES":[1,[6,"20"]],"SUBSTACK":[2,"(FZ7uW#!=qrLAy)n(:cX"]},"fields":{},"shadow":false,"topLevel":false},"(FZ7uW#!=qrLAy)n(:cX":{"opcode":"control_create_clone_of","next":null,"parent":"h0[Cyb2FIAVYQP~pQy;/","inputs":{"CLONE_OPTION":[1,"@RE`NUl1=A`ZE{s``V/2"]},"fields":{},"shadow":false,"topLevel":false},"@RE`NUl1=A`ZE{s``V/2":{"opcode":"control_create_clone_of_menu","next":null,"parent":"(FZ7uW#!=qrLAy)n(:cX","inputs":{},"fields":{"CLONE_OPTION":["_myself_",null]},"shadow":true,"topLevel":false},"^cz3L|Ol!+Q!sksfF#1q":{"opcode":"event_whenflagclicked","next":"{+2R[8Iy)A8Rt2!|nzzl","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":1481,"y":746},"{+2R[8Iy)A8Rt2!|nzzl":{"opcode":"looks_switchcostumeto","next":";4sqpeUiZWt;v:e)IKKA","parent":"^cz3L|Ol!+Q!sksfF#1q","inputs":{"COSTUME":[1,"VdVMlq@Z]8a1_ChBX?KW"]},"fields":{},"shadow":false,"topLevel":false},"VdVMlq@Z]8a1_ChBX?KW":{"opcode":"looks_costume","next":null,"parent":"{+2R[8Iy)A8Rt2!|nzzl","inputs":{},"fields":{"COSTUME":["costume1",null]},"shadow":true,"topLevel":false},";4sqpeUiZWt;v:e)IKKA":{"opcode":"control_wait","next":"EeE6pW%.k3a29D)y^YAe","parent":"{+2R[8Iy)A8Rt2!|nzzl","inputs":{"DURATION":[1,[5,"5"]]},"fields":{},"shadow":false,"topLevel":false},"EeE6pW%.k3a29D)y^YAe":{"opcode":"looks_switchcostumeto","next":null,"parent":";4sqpeUiZWt;v:e)IKKA","inputs":{"COSTUME":[1,"@iKzM%u7`b1aG6c;+9yH"]},"fields":{},"shadow":false,"topLevel":false},"@iKzM%u7`b1aG6c;+9yH":{"opcode":"looks_costume","next":null,"parent":"EeE6pW%.k3a29D)y^YAe","inputs":{},"fields":{"COSTUME":["costume2",null]},"shadow":true,"topLevel":false}},"comments":{},"currentCostume":1,"costumes":[{"assetId":"bcaaa8547a07cfe572c0967ba829e99d","name":"costume1","bitmapResolution":1,"md5ext":"bcaaa8547a07cfe572c0967ba829e99d.svg","dataFormat":"svg","rotationCenterX":47,"rotationCenterY":55},{"assetId":"11d6c5fbd91e433a1b85a00fd9dd43b6","name":"costume2","bitmapResolution":1,"md5ext":"11d6c5fbd91e433a1b85a00fd9dd43b6.svg","dataFormat":"svg","rotationCenterX":47,"rotationCenterY":55}],"sounds":[{"assetId":"83c36d806dc92327b9e7049a565c6bff","name":"meow","dataFormat":"wav","format":"","rate":48000,"sampleCount":40681,"md5ext":"83c36d806dc92327b9e7049a565c6bff.wav"}],"volume":100,"layerOrder":1,"visible":true,"x":-78,"y":-26,"size":80,"direction":90,"draggable":false,"rotationStyle":"all around"}],"monitors":[{"id":"{wQSVFJ;-}_(VQqvyvBw-x-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"x"},"spriteName":null,"value":"","width":0,"height":0,"x":5,"y":5,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"{wQSVFJ;-}_(VQqvyvBw-y-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"y"},"spriteName":null,"value":"","width":0,"height":0,"x":5,"y":32,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"{wQSVFJ;-}_(VQqvyvBw-c-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"c"},"spriteName":null,"value":"","width":0,"height":0,"x":5,"y":59,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"{wQSVFJ;-}_(VQqvyvBw-i-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"i"},"spriteName":null,"value":"","width":0,"height":0,"x":5,"y":86,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true}],"extensions":[],"meta":{"semver":"3.0.0","vm":"0.2.0-prerelease.20200622143012","agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"}} -------------------------------------------------------------------------------- /backend/sb2/235462997project.json: -------------------------------------------------------------------------------- 1 | {"targets":[{"isStage":true,"name":"Stage","variables":{"Gi)RmvR(uaikyu({2_T.-sulf.time-":["sulf.time",0],"Gi)RmvR(uaikyu({2_T.-sulf.resolutionX-":["sulf.resolutionX",0],"Gi)RmvR(uaikyu({2_T.-sulf.resolutionY-":["sulf.resolutionY",0],"Gi)RmvR(uaikyu({2_T.-sulf.hasTouchEvents-":["sulf.hasTouchEvents",0],"Gi)RmvR(uaikyu({2_T.-sulf.version-":["sulf.version",0],"Gi)RmvR(uaikyu({2_T.-sulf.p.peterpan-":["sulf.p.username","fritz"],"Gi)RmvR(uaikyu({2_T.-test-":["test",""],"Gi)RmvR(uaikyu({2_T.-sulf.p.plackbeard-":["sulf.p.plackbeard",0],"Gi)RmvR(uaikyu({2_T.-Space-":["Space",0],"Gi)RmvR(uaikyu({2_T.-silf.p.-":["silf.p.",0]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"name":"backdrop1","bitmapResolution":2,"dataFormat":"png","assetId":"797b03bdb8cf6ccfc30c0692d533d998","md5ext":"797b03bdb8cf6ccfc30c0692d533d998.png","rotationCenterX":480,"rotationCenterY":360}],"sounds":[{"name":"pop","assetId":"83a9787d4cb6f3b7632b4ddfebf74367","dataFormat":"wav","format":"","rate":48000,"sampleCount":1123,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"}],"volume":100,"layerOrder":0,"tempo":60,"videoTransparency":50,"videoState":"off","textToSpeechLanguage":null},{"isStage":false,"name":"Sprite1","variables":{},"lists":{},"broadcasts":{},"blocks":{"?{RbpKF)P`+-uFJVD3E+":{"opcode":"event_whenkeypressed","next":"av=nwti+2clho:nUIQDD","parent":null,"inputs":{},"fields":{"KEY_OPTION":["space"]},"shadow":false,"topLevel":true,"x":15,"y":22},"av=nwti+2clho:nUIQDD":{"opcode":"data_showvariable","next":"4.q!tXy0O0BKkoHEJ?ed","parent":"?{RbpKF)P`+-uFJVD3E+","inputs":{},"fields":{"VARIABLE":["sulf.p.username","Gi)RmvR(uaikyu({2_T.-sulf.p.peterpan-"]},"shadow":false,"topLevel":false},"4.q!tXy0O0BKkoHEJ?ed":{"opcode":"data_hidevariable","next":"yElCIscy-P!rW/rnG1FD","parent":"av=nwti+2clho:nUIQDD","inputs":{},"fields":{"VARIABLE":["sulf.time","Gi)RmvR(uaikyu({2_T.-sulf.time-"]},"shadow":false,"topLevel":false},"yElCIscy-P!rW/rnG1FD":{"opcode":"data_hidevariable","next":"|JGVIJl4j0:[tZCTIy?+","parent":"4.q!tXy0O0BKkoHEJ?ed","inputs":{},"fields":{"VARIABLE":["sulf.resolutionX","Gi)RmvR(uaikyu({2_T.-sulf.resolutionX-"]},"shadow":false,"topLevel":false},"|JGVIJl4j0:[tZCTIy?+":{"opcode":"data_hidevariable","next":"r0IL/,4{V/JE2%n7-pTU","parent":"yElCIscy-P!rW/rnG1FD","inputs":{},"fields":{"VARIABLE":["sulf.resolutionY","Gi)RmvR(uaikyu({2_T.-sulf.resolutionY-"]},"shadow":false,"topLevel":false},"r0IL/,4{V/JE2%n7-pTU":{"opcode":"data_hidevariable","next":"gb|3GS1_+1)_HBLPV.-2","parent":"|JGVIJl4j0:[tZCTIy?+","inputs":{},"fields":{"VARIABLE":["sulf.hasTouchEvents","Gi)RmvR(uaikyu({2_T.-sulf.hasTouchEvents-"]},"shadow":false,"topLevel":false},"gb|3GS1_+1)_HBLPV.-2":{"opcode":"data_hidevariable","next":null,"parent":"r0IL/,4{V/JE2%n7-pTU","inputs":{},"fields":{"VARIABLE":["sulf.version","Gi)RmvR(uaikyu({2_T.-sulf.version-"]},"shadow":false,"topLevel":false},"l/UC^,A)JbE4!OpbneOP":{"opcode":"event_whenkeypressed","next":"hLERj3WnvfLGnODX@:rx","parent":null,"inputs":{},"fields":{"KEY_OPTION":["right arrow"]},"shadow":false,"topLevel":true,"x":803,"y":550},"hLERj3WnvfLGnODX@:rx":{"opcode":"data_showvariable","next":"9!0,|ID]xUb6=|Q66@r^","parent":"l/UC^,A)JbE4!OpbneOP","inputs":{},"fields":{"VARIABLE":["sulf.resolutionY","Gi)RmvR(uaikyu({2_T.-sulf.resolutionY-"]},"shadow":false,"topLevel":false},"9!0,|ID]xUb6=|Q66@r^":{"opcode":"data_hidevariable","next":"cV(dN?_521y*xZoP)Ixv","parent":"hLERj3WnvfLGnODX@:rx","inputs":{},"fields":{"VARIABLE":["sulf.time","Gi)RmvR(uaikyu({2_T.-sulf.time-"]},"shadow":false,"topLevel":false},"cV(dN?_521y*xZoP)Ixv":{"opcode":"data_hidevariable","next":"c-kpnzp[7d(cAHl`b#Ia","parent":"9!0,|ID]xUb6=|Q66@r^","inputs":{},"fields":{"VARIABLE":["sulf.resolutionX","Gi)RmvR(uaikyu({2_T.-sulf.resolutionX-"]},"shadow":false,"topLevel":false},"c-kpnzp[7d(cAHl`b#Ia":{"opcode":"data_hidevariable","next":"hm,K5uQf.j]|1L%#}A.X","parent":"cV(dN?_521y*xZoP)Ixv","inputs":{},"fields":{"VARIABLE":["sulf.hasTouchEvents","Gi)RmvR(uaikyu({2_T.-sulf.hasTouchEvents-"]},"shadow":false,"topLevel":false},"hm,K5uQf.j]|1L%#}A.X":{"opcode":"data_hidevariable","next":"u![4^_!K)Xqsi9d-mCno","parent":"c-kpnzp[7d(cAHl`b#Ia","inputs":{},"fields":{"VARIABLE":["sulf.version","Gi)RmvR(uaikyu({2_T.-sulf.version-"]},"shadow":false,"topLevel":false},"u![4^_!K)Xqsi9d-mCno":{"opcode":"data_hidevariable","next":null,"parent":"hm,K5uQf.j]|1L%#}A.X","inputs":{},"fields":{"VARIABLE":["sulf.p.username","Gi)RmvR(uaikyu({2_T.-sulf.p.peterpan-"]},"shadow":false,"topLevel":false},"nmIwW~4[m~55S-KBE1vy":{"opcode":"event_whenkeypressed","next":"!W._]wfh0q_4yRgD6^b=","parent":null,"inputs":{},"fields":{"KEY_OPTION":["down arrow"]},"shadow":false,"topLevel":true,"x":781,"y":1050},"!W._]wfh0q_4yRgD6^b=":{"opcode":"data_showvariable","next":"yMfNK:`~V{Pqfz5Pxp{v","parent":"nmIwW~4[m~55S-KBE1vy","inputs":{},"fields":{"VARIABLE":["sulf.hasTouchEvents","Gi)RmvR(uaikyu({2_T.-sulf.hasTouchEvents-"]},"shadow":false,"topLevel":false},"yMfNK:`~V{Pqfz5Pxp{v":{"opcode":"data_hidevariable","next":"vhE5Td0VcDeAD+q5yvfG","parent":"!W._]wfh0q_4yRgD6^b=","inputs":{},"fields":{"VARIABLE":["sulf.time","Gi)RmvR(uaikyu({2_T.-sulf.time-"]},"shadow":false,"topLevel":false},"vhE5Td0VcDeAD+q5yvfG":{"opcode":"data_hidevariable","next":":+%lyoDlY*yY@T49sF}j","parent":"yMfNK:`~V{Pqfz5Pxp{v","inputs":{},"fields":{"VARIABLE":["sulf.resolutionX","Gi)RmvR(uaikyu({2_T.-sulf.resolutionX-"]},"shadow":false,"topLevel":false},":+%lyoDlY*yY@T49sF}j":{"opcode":"data_hidevariable","next":"?XAeoJ[b.`x=kGC:cC(l","parent":"vhE5Td0VcDeAD+q5yvfG","inputs":{},"fields":{"VARIABLE":["sulf.resolutionY","Gi)RmvR(uaikyu({2_T.-sulf.resolutionY-"]},"shadow":false,"topLevel":false},"?XAeoJ[b.`x=kGC:cC(l":{"opcode":"data_hidevariable","next":"=T~Y?(y#xUV;0?/*X%hm","parent":":+%lyoDlY*yY@T49sF}j","inputs":{},"fields":{"VARIABLE":["sulf.version","Gi)RmvR(uaikyu({2_T.-sulf.version-"]},"shadow":false,"topLevel":false},"=T~Y?(y#xUV;0?/*X%hm":{"opcode":"data_hidevariable","next":null,"parent":"?XAeoJ[b.`x=kGC:cC(l","inputs":{},"fields":{"VARIABLE":["sulf.p.username","Gi)RmvR(uaikyu({2_T.-sulf.p.peterpan-"]},"shadow":false,"topLevel":false},"ZFv]sktSwFeN-@APDw9E":{"opcode":"event_whenkeypressed","next":"1Z,)#jocqq]1PE}#`w(4","parent":null,"inputs":{},"fields":{"KEY_OPTION":["up arrow"]},"shadow":false,"topLevel":true,"x":763,"y":23},"1Z,)#jocqq]1PE}#`w(4":{"opcode":"data_showvariable","next":"J[s%e}pbTQ~QP9.76v8I","parent":"ZFv]sktSwFeN-@APDw9E","inputs":{},"fields":{"VARIABLE":["sulf.time","Gi)RmvR(uaikyu({2_T.-sulf.time-"]},"shadow":false,"topLevel":false},"J[s%e}pbTQ~QP9.76v8I":{"opcode":"data_hidevariable","next":"Z,N-y,3ule/lxS?cNB3h","parent":"1Z,)#jocqq]1PE}#`w(4","inputs":{},"fields":{"VARIABLE":["sulf.resolutionX","Gi)RmvR(uaikyu({2_T.-sulf.resolutionX-"]},"shadow":false,"topLevel":false},"Z,N-y,3ule/lxS?cNB3h":{"opcode":"data_hidevariable","next":".-[E9(vH+~nS+ZN.Hj}d","parent":"J[s%e}pbTQ~QP9.76v8I","inputs":{},"fields":{"VARIABLE":["sulf.resolutionY","Gi)RmvR(uaikyu({2_T.-sulf.resolutionY-"]},"shadow":false,"topLevel":false},".-[E9(vH+~nS+ZN.Hj}d":{"opcode":"data_hidevariable","next":"OgMcH.!6Tb8x,DYb,PGw","parent":"Z,N-y,3ule/lxS?cNB3h","inputs":{},"fields":{"VARIABLE":["sulf.hasTouchEvents","Gi)RmvR(uaikyu({2_T.-sulf.hasTouchEvents-"]},"shadow":false,"topLevel":false},"OgMcH.!6Tb8x,DYb,PGw":{"opcode":"data_hidevariable","next":"nLz:4IV7t1C-W68~9e~E","parent":".-[E9(vH+~nS+ZN.Hj}d","inputs":{},"fields":{"VARIABLE":["sulf.version","Gi)RmvR(uaikyu({2_T.-sulf.version-"]},"shadow":false,"topLevel":false},"nLz:4IV7t1C-W68~9e~E":{"opcode":"data_hidevariable","next":null,"parent":"OgMcH.!6Tb8x,DYb,PGw","inputs":{},"fields":{"VARIABLE":["sulf.p.username","Gi)RmvR(uaikyu({2_T.-sulf.p.peterpan-"]},"shadow":false,"topLevel":false},"j9!GC:(40?XRT_0Qo^8+":{"opcode":"event_whenthisspriteclicked","next":"F0YsLE_H)yPrRs=DOy6%","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":34,"y":991},"F0YsLE_H)yPrRs=DOy6%":{"opcode":"data_showvariable","next":"%#Y~g=h~VNg]=dGIkPM{","parent":"j9!GC:(40?XRT_0Qo^8+","inputs":{},"fields":{"VARIABLE":["sulf.version","Gi)RmvR(uaikyu({2_T.-sulf.version-"]},"shadow":false,"topLevel":false},"%#Y~g=h~VNg]=dGIkPM{":{"opcode":"data_hidevariable","next":"Ap[]7)tlL49{R3%Qh,Re","parent":"F0YsLE_H)yPrRs=DOy6%","inputs":{},"fields":{"VARIABLE":["sulf.p.username","Gi)RmvR(uaikyu({2_T.-sulf.p.peterpan-"]},"shadow":false,"topLevel":false,"comment":"[)MDr^+uyyFI7~O`KH4S"},"Ap[]7)tlL49{R3%Qh,Re":{"opcode":"data_hidevariable","next":"VUz9Z{@kV%`TSrDwMJ99","parent":"%#Y~g=h~VNg]=dGIkPM{","inputs":{},"fields":{"VARIABLE":["sulf.resolutionX","Gi)RmvR(uaikyu({2_T.-sulf.resolutionX-"]},"shadow":false,"topLevel":false},"VUz9Z{@kV%`TSrDwMJ99":{"opcode":"data_hidevariable","next":"8e]llBQq=lq%34SaBMY}","parent":"Ap[]7)tlL49{R3%Qh,Re","inputs":{},"fields":{"VARIABLE":["sulf.resolutionY","Gi)RmvR(uaikyu({2_T.-sulf.resolutionY-"]},"shadow":false,"topLevel":false},"8e]llBQq=lq%34SaBMY}":{"opcode":"data_hidevariable","next":"nZR=x%?~G)HT2Au3/#JJ","parent":"VUz9Z{@kV%`TSrDwMJ99","inputs":{},"fields":{"VARIABLE":["sulf.hasTouchEvents","Gi)RmvR(uaikyu({2_T.-sulf.hasTouchEvents-"]},"shadow":false,"topLevel":false},"nZR=x%?~G)HT2Au3/#JJ":{"opcode":"data_hidevariable","next":null,"parent":"8e]llBQq=lq%34SaBMY}","inputs":{},"fields":{"VARIABLE":["sulf.time","Gi)RmvR(uaikyu({2_T.-sulf.time-"]},"shadow":false,"topLevel":false},"9`!:Lppt,TXa;#XZ!^(#":{"opcode":"event_whenkeypressed","next":"4aLN~M@`do[}WVOnGN06","parent":null,"inputs":{},"fields":{"KEY_OPTION":["left arrow"]},"shadow":false,"topLevel":true,"x":7,"y":516},"4aLN~M@`do[}WVOnGN06":{"opcode":"data_showvariable","next":"gij1I3b~U5jMIzfGb;+N","parent":"9`!:Lppt,TXa;#XZ!^(#","inputs":{},"fields":{"VARIABLE":["sulf.resolutionX","Gi)RmvR(uaikyu({2_T.-sulf.resolutionX-"]},"shadow":false,"topLevel":false},"gij1I3b~U5jMIzfGb;+N":{"opcode":"data_hidevariable","next":"l*w(8l5/HLx#k(3n5hrc","parent":"4aLN~M@`do[}WVOnGN06","inputs":{},"fields":{"VARIABLE":["sulf.time","Gi)RmvR(uaikyu({2_T.-sulf.time-"]},"shadow":false,"topLevel":false},"l*w(8l5/HLx#k(3n5hrc":{"opcode":"data_hidevariable","next":"]So4Dtwo*|@ON2w+_e4t","parent":"gij1I3b~U5jMIzfGb;+N","inputs":{},"fields":{"VARIABLE":["sulf.resolutionY","Gi)RmvR(uaikyu({2_T.-sulf.resolutionY-"]},"shadow":false,"topLevel":false},"]So4Dtwo*|@ON2w+_e4t":{"opcode":"data_hidevariable","next":"RNf.X]pX.AgLw3RR]5P!","parent":"l*w(8l5/HLx#k(3n5hrc","inputs":{},"fields":{"VARIABLE":["sulf.hasTouchEvents","Gi)RmvR(uaikyu({2_T.-sulf.hasTouchEvents-"]},"shadow":false,"topLevel":false,"comment":"0s-Itw3Yl57zvEolVsFe"},"RNf.X]pX.AgLw3RR]5P!":{"opcode":"data_hidevariable","next":"o^uw|4LcINE^7lDOPlE5","parent":"]So4Dtwo*|@ON2w+_e4t","inputs":{},"fields":{"VARIABLE":["sulf.version","Gi)RmvR(uaikyu({2_T.-sulf.version-"]},"shadow":false,"topLevel":false},"o^uw|4LcINE^7lDOPlE5":{"opcode":"data_hidevariable","next":null,"parent":"RNf.X]pX.AgLw3RR]5P!","inputs":{},"fields":{"VARIABLE":["sulf.p.username","Gi)RmvR(uaikyu({2_T.-sulf.p.peterpan-"]},"shadow":false,"topLevel":false},"T_5cEK(+7gty:[K`l8,.":{"opcode":"event_whenflagclicked","next":"#w.JNhW^;2JN(ly.}]A.","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":1454,"y":25},"#w.JNhW^;2JN(ly.}]A.":{"opcode":"data_hidevariable","next":"?#,knBqwsgna-8z((3Ht","parent":"T_5cEK(+7gty:[K`l8,.","inputs":{},"fields":{"VARIABLE":["sulf.time","Gi)RmvR(uaikyu({2_T.-sulf.time-"]},"shadow":false,"topLevel":false},"?#,knBqwsgna-8z((3Ht":{"opcode":"data_hidevariable","next":"TUa;T`=]`}F?#CZ|krMT","parent":"#w.JNhW^;2JN(ly.}]A.","inputs":{},"fields":{"VARIABLE":["sulf.resolutionX","Gi)RmvR(uaikyu({2_T.-sulf.resolutionX-"]},"shadow":false,"topLevel":false},"TUa;T`=]`}F?#CZ|krMT":{"opcode":"data_hidevariable","next":"QrG/k;Cl+BEp%MPFzwZx","parent":"?#,knBqwsgna-8z((3Ht","inputs":{},"fields":{"VARIABLE":["sulf.resolutionY","Gi)RmvR(uaikyu({2_T.-sulf.resolutionY-"]},"shadow":false,"topLevel":false},"QrG/k;Cl+BEp%MPFzwZx":{"opcode":"data_hidevariable","next":"R(!@@0^EvLe;h[}K6/T3","parent":"TUa;T`=]`}F?#CZ|krMT","inputs":{},"fields":{"VARIABLE":["sulf.hasTouchEvents","Gi)RmvR(uaikyu({2_T.-sulf.hasTouchEvents-"]},"shadow":false,"topLevel":false},"R(!@@0^EvLe;h[}K6/T3":{"opcode":"data_hidevariable","next":"ren#]:Hv^dS]y%AOyFst","parent":"QrG/k;Cl+BEp%MPFzwZx","inputs":{},"fields":{"VARIABLE":["sulf.version","Gi)RmvR(uaikyu({2_T.-sulf.version-"]},"shadow":false,"topLevel":false},"ren#]:Hv^dS]y%AOyFst":{"opcode":"data_hidevariable","next":null,"parent":"R(!@@0^EvLe;h[}K6/T3","inputs":{},"fields":{"VARIABLE":["sulf.p.username","Gi)RmvR(uaikyu({2_T.-sulf.p.peterpan-"]},"shadow":false,"topLevel":false},"5un-)1#y5`r.KzVJY8}[":{"opcode":"data_setvariableto","next":null,"parent":null,"inputs":{"VALUE":[1,[10,"fritz"]]},"fields":{"VARIABLE":["sulf.p.username","Gi)RmvR(uaikyu({2_T.-sulf.p.peterpan-"]},"shadow":false,"topLevel":true,"x":510,"y":586}},"comments":{"[)MDr^+uyyFI7~O`KH4S":{"blockId":"%#Y~g=h~VNg]=dGIkPM{","x":428.03703703703707,"y":1176.4814814814813,"width":229.5,"height":446.6,"minimized":false,"text":"DEUTSCH: Wenn die Leertaste gedrückt wird wird die Variable sulf.p.peterpan angezeigt.\r\rENGLISH: If Space is pressed the sulf.p.peterpan variable is shown."},"6g.kZ?pls85v`BLPe8nh":{"blockId":null,"x":1168.8000000000002,"y":29.92,"width":229.5,"height":446.6,"minimized":false,"text":"DEUTSCH: Wenn Pfeil nach oben gedrückt wird wird die Variable sulf.time angezeigt.\r\rENGLISH: If the Arrow up key is pressed the sulf.time variable is shown."},"fl_+KeK^5+vY!2^6N3z2":{"blockId":null,"x":1185.6750000000002,"y":544.72,"width":229.5,"height":446.6,"minimized":false,"text":"DEUTSCH: Wenn Pfeil nach rechts Taste gedrückt wird wird die Variable sulf.resolutionY angezeigt.\r\rENGLISH: If the arrow right key is pressed the sulf.resolutionY is shown."},"X=s+^E|(GsS^1Dnf9iqn":{"blockId":null,"x":1186.35,"y":1063.3700000000001,"width":229.5,"height":446.6,"minimized":false,"text":"DEUTSCH: Wenn die Pfeil nach unten Taste gedrückt wird, wird die sulf.hasTouchEvents angezeigt.\r\rENGLISH: If the arrow down key is pressed, the sulf.hasTouchEvents variable is shown."},"GKhrbmAi,zH,.f{dGCpy":{"blockId":null,"x":451.57500000000005,"y":41.800000000000004,"width":229.5,"height":446.6,"minimized":false,"text":"DEUTSCH: Wenn die Pfeil nach links Taste gedrückt wird wird die Variable sulf.resolutionX angezeigt.\r\rENGLISH: If the arrow left key is pressed the sulf.resolutionX variable is shown."},"0s-Itw3Yl57zvEolVsFe":{"blockId":"]So4Dtwo*|@ON2w+_e4t","x":378.33333333333337,"y":671.5555555555555,"width":229.5,"height":446.6,"minimized":false,"text":"DEUTSCH: Wenn die Figur angeklickt wird, wird ide sulf.version Variable angezeigt.\r\rENGLISH: When the figure is clicked, the sulf.version variable is shown."}},"currentCostume":1,"costumes":[{"name":"costume1","bitmapResolution":1,"dataFormat":"svg","assetId":"bcaaa8547a07cfe572c0967ba829e99d","md5ext":"bcaaa8547a07cfe572c0967ba829e99d.svg","rotationCenterX":47,"rotationCenterY":55},{"name":"costume2","bitmapResolution":1,"dataFormat":"svg","assetId":"11d6c5fbd91e433a1b85a00fd9dd43b6","md5ext":"11d6c5fbd91e433a1b85a00fd9dd43b6.svg","rotationCenterX":47,"rotationCenterY":55}],"sounds":[{"name":"meow","assetId":"83c36d806dc92327b9e7049a565c6bff","dataFormat":"wav","format":"","rate":48000,"sampleCount":40681,"md5ext":"83c36d806dc92327b9e7049a565c6bff.wav"}],"volume":100,"layerOrder":1,"visible":true,"x":4,"y":-22,"size":100,"direction":90,"draggable":false,"rotationStyle":"all around"}],"monitors":[{"id":"Gi)RmvR(uaikyu({2_T.-test-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"test"},"spriteName":null,"value":"","width":0,"height":0,"x":5,"y":5,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"Gi)RmvR(uaikyu({2_T.-sulf.time-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"sulf.time"},"spriteName":null,"value":"","width":0,"height":0,"x":165.3,"y":51.45,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"Gi)RmvR(uaikyu({2_T.-sulf.hasTouchEvents-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"sulf.hasTouchEvents"},"spriteName":null,"value":"","width":0,"height":0,"x":130.95,"y":48.85,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"Gi)RmvR(uaikyu({2_T.-sulf.resolutionX-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"sulf.resolutionX"},"spriteName":null,"value":0,"width":0,"height":0,"x":141.7,"y":46.45,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"Gi)RmvR(uaikyu({2_T.-sulf.version-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"sulf.version"},"spriteName":null,"value":"","width":0,"height":0,"x":152,"y":51.4,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"Gi)RmvR(uaikyu({2_T.-sulf.resolutionY-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"sulf.resolutionY"},"spriteName":null,"value":"","width":0,"height":0,"x":145.1,"y":50.4,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"Gi)RmvR(uaikyu({2_T.-sulf.p.plackbeard-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"sulf.p.plackbeard"},"spriteName":null,"value":"","width":0,"height":0,"x":5,"y":75,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"Gi)RmvR(uaikyu({2_T.-sulf.p.peterpan-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"sulf.p.username"},"spriteName":null,"value":"fritz","width":0,"height":0,"x":141.9,"y":46.9,"visible":true,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"Gi)RmvR(uaikyu({2_T.-Space-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"Space"},"spriteName":null,"value":0,"width":0,"height":0,"x":5,"y":32,"visible":true,"sliderMin":0,"sliderMax":100,"isDiscrete":true},{"id":"Gi)RmvR(uaikyu({2_T.-silf.p.-","mode":"default","opcode":"data_variable","params":{"VARIABLE":"silf.p."},"spriteName":null,"value":"","width":0,"height":0,"x":5,"y":102,"visible":false,"sliderMin":0,"sliderMax":100,"isDiscrete":true}],"extensions":[],"meta":{"semver":"3.0.0","vm":"1.5.68","agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"}} -------------------------------------------------------------------------------- /backend/sb2/237126447project.json: -------------------------------------------------------------------------------- 1 | {"targets":[{"isStage":true,"name":"Stage","variables":{},"lists":{".:u*s9D5|y9-7h1rjuQk-sulf.c.liste-list":["sulf.c.liste",["sss"]],".:u*s9D5|y9-7h1rjuQk-test.liste-list":["test.liste",["hi",""]]},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"assetId":"797b03bdb8cf6ccfc30c0692d533d998","name":"backdrop1","bitmapResolution":2,"md5ext":"797b03bdb8cf6ccfc30c0692d533d998.png","dataFormat":"png","rotationCenterX":480,"rotationCenterY":360}],"sounds":[{"assetId":"83a9787d4cb6f3b7632b4ddfebf74367","name":"pop","dataFormat":"wav","format":"","rate":48000,"sampleCount":1123,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"}],"volume":100,"layerOrder":0,"tempo":60,"videoTransparency":50,"videoState":"off","textToSpeechLanguage":null},{"isStage":false,"name":"Sprite1","variables":{},"lists":{},"broadcasts":{},"blocks":{"{nVu}t(WHB.IMp_W5bFN":{"opcode":"event_whenflagclicked","next":"w7Bg1iTyeN@b+11UOU1T","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":495,"y":407},"w7Bg1iTyeN@b+11UOU1T":{"opcode":"data_showlist","next":"lD**(23E|u9A};|I65-o","parent":"{nVu}t(WHB.IMp_W5bFN","inputs":{},"fields":{"LIST":["sulf.c.liste",".:u*s9D5|y9-7h1rjuQk-sulf.c.liste-list"]},"shadow":false,"topLevel":false},"lD**(23E|u9A};|I65-o":{"opcode":"data_showlist","next":null,"parent":"w7Bg1iTyeN@b+11UOU1T","inputs":{},"fields":{"LIST":["test.liste",".:u*s9D5|y9-7h1rjuQk-test.liste-list"]},"shadow":false,"topLevel":false},"A~(U`Kw}754h3jf_3Ot4":{"opcode":"event_whenkeypressed","next":"sm:64.u~E(a-KF]6^oBR","parent":null,"inputs":{},"fields":{"KEY_OPTION":["space"]},"shadow":false,"topLevel":true,"x":820,"y":556},"sm:64.u~E(a-KF]6^oBR":{"opcode":"sensing_askandwait","next":"PWGN/{q/9I!xu4![g34K","parent":"A~(U`Kw}754h3jf_3Ot4","inputs":{"QUESTION":[1,[10,"next item in list"]]},"fields":{},"shadow":false,"topLevel":false},"PWGN/{q/9I!xu4![g34K":{"opcode":"data_addtolist","next":"5=Za`E@(T%j|P(Ze2[Wv","parent":"sm:64.u~E(a-KF]6^oBR","inputs":{"ITEM":[3,",8bt;8=CA]ovX:h~):wF",[10,""]]},"fields":{"LIST":["sulf.c.liste",".:u*s9D5|y9-7h1rjuQk-sulf.c.liste-list"]},"shadow":false,"topLevel":false},",8bt;8=CA]ovX:h~):wF":{"opcode":"sensing_answer","next":null,"parent":"PWGN/{q/9I!xu4![g34K","inputs":{},"fields":{},"shadow":false,"topLevel":false},"5=Za`E@(T%j|P(Ze2[Wv":{"opcode":"looks_sayforsecs","next":"WT9MpU{X.`Sc/j:y^|Sf","parent":"PWGN/{q/9I!xu4![g34K","inputs":{"MESSAGE":[3,"z_ie%^H8T?oN9)MwZiHD",[10,""]],"SECS":[1,[4,2]]},"fields":{},"shadow":false,"topLevel":false},"z_ie%^H8T?oN9)MwZiHD":{"opcode":"data_itemoflist","next":null,"parent":"5=Za`E@(T%j|P(Ze2[Wv","inputs":{"INDEX":[1,[7,1]]},"fields":{"LIST":["sulf.c.liste",".:u*s9D5|y9-7h1rjuQk-sulf.c.liste-list"]},"shadow":false,"topLevel":false},"WT9MpU{X.`Sc/j:y^|Sf":{"opcode":"control_wait","next":"t.IZ?QA1-c?UOw:G6bxr","parent":"5=Za`E@(T%j|P(Ze2[Wv","inputs":{"DURATION":[1,[5,2]]},"fields":{},"shadow":false,"topLevel":false},"t.IZ?QA1-c?UOw:G6bxr":{"opcode":"looks_sayforsecs","next":"T((!CtV_+6Nbp?cW)CAO","parent":"WT9MpU{X.`Sc/j:y^|Sf","inputs":{"MESSAGE":[3,[13,"sulf.c.liste",".:u*s9D5|y9-7h1rjuQk-sulf.c.liste-list"],[10,""]],"SECS":[1,[4,2]]},"fields":{},"shadow":false,"topLevel":false},"T((!CtV_+6Nbp?cW)CAO":{"opcode":"control_wait","next":"tl[blZDBgS6Dl_g{E[?U","parent":"t.IZ?QA1-c?UOw:G6bxr","inputs":{"DURATION":[1,[5,2]]},"fields":{},"shadow":false,"topLevel":false},"tl[blZDBgS6Dl_g{E[?U":{"opcode":"looks_sayforsecs","next":null,"parent":"T((!CtV_+6Nbp?cW)CAO","inputs":{"MESSAGE":[3,"H3hS=MuP9e@sws+}6m^x",[10,""]],"SECS":[1,[4,2]]},"fields":{},"shadow":false,"topLevel":false},"H3hS=MuP9e@sws+}6m^x":{"opcode":"data_lengthoflist","next":null,"parent":"tl[blZDBgS6Dl_g{E[?U","inputs":{},"fields":{"LIST":["sulf.c.liste",".:u*s9D5|y9-7h1rjuQk-sulf.c.liste-list"]},"shadow":false,"topLevel":false},"8lTYe)F?`,Goza.neDi,":{"opcode":"event_whenkeypressed","next":"|@he/W_ocP^X{vnXlye~","parent":null,"inputs":{},"fields":{"KEY_OPTION":["up arrow"]},"shadow":false,"topLevel":true,"x":289,"y":662},"|@he/W_ocP^X{vnXlye~":{"opcode":"sensing_askandwait","next":"uE)=Q6)/HB`|kDV)KtW}","parent":"8lTYe)F?`,Goza.neDi,","inputs":{"QUESTION":[1,[10,"next item in list"]]},"fields":{},"shadow":false,"topLevel":false},"uE)=Q6)/HB`|kDV)KtW}":{"opcode":"data_addtolist","next":"]ZFsHjH@q~SrsR6;Qg=[","parent":"|@he/W_ocP^X{vnXlye~","inputs":{"ITEM":[3,"o/A=ybvy`Mhc/k{(-O;|",[10,""]]},"fields":{"LIST":["test.liste",".:u*s9D5|y9-7h1rjuQk-test.liste-list"]},"shadow":false,"topLevel":false},"o/A=ybvy`Mhc/k{(-O;|":{"opcode":"sensing_answer","next":null,"parent":"uE)=Q6)/HB`|kDV)KtW}","inputs":{},"fields":{},"shadow":false,"topLevel":false},"]ZFsHjH@q~SrsR6;Qg=[":{"opcode":"looks_sayforsecs","next":"r0j3`EeRa)_=`Jr{0CmY","parent":"uE)=Q6)/HB`|kDV)KtW}","inputs":{"MESSAGE":[3,"iPz2hjc{?-2Ge;:(pC.G",[10,""]],"SECS":[1,[4,2]]},"fields":{},"shadow":false,"topLevel":false},"iPz2hjc{?-2Ge;:(pC.G":{"opcode":"data_itemoflist","next":null,"parent":"]ZFsHjH@q~SrsR6;Qg=[","inputs":{"INDEX":[1,[7,1]]},"fields":{"LIST":["test.liste",".:u*s9D5|y9-7h1rjuQk-test.liste-list"]},"shadow":false,"topLevel":false},"r0j3`EeRa)_=`Jr{0CmY":{"opcode":"control_wait","next":"arXe.oz,/WiTUuuGf/?Z","parent":"]ZFsHjH@q~SrsR6;Qg=[","inputs":{"DURATION":[1,[5,2]]},"fields":{},"shadow":false,"topLevel":false},"arXe.oz,/WiTUuuGf/?Z":{"opcode":"looks_sayforsecs","next":"i,:KK;,vC{BX4PtsHTI/","parent":"r0j3`EeRa)_=`Jr{0CmY","inputs":{"MESSAGE":[3,[13,"test.liste",".:u*s9D5|y9-7h1rjuQk-test.liste-list"],[10,""]],"SECS":[1,[4,2]]},"fields":{},"shadow":false,"topLevel":false},"i,:KK;,vC{BX4PtsHTI/":{"opcode":"control_wait","next":",l}5H),^Bj0x3+N}oaj`","parent":"arXe.oz,/WiTUuuGf/?Z","inputs":{"DURATION":[1,[5,2]]},"fields":{},"shadow":false,"topLevel":false},",l}5H),^Bj0x3+N}oaj`":{"opcode":"looks_sayforsecs","next":null,"parent":"i,:KK;,vC{BX4PtsHTI/","inputs":{"MESSAGE":[3,"aG=4(3K2I-J=[x3U1}p|",[10,""]],"SECS":[1,[4,2]]},"fields":{},"shadow":false,"topLevel":false},"aG=4(3K2I-J=[x3U1}p|":{"opcode":"data_lengthoflist","next":null,"parent":",l}5H),^Bj0x3+N}oaj`","inputs":{},"fields":{"LIST":["test.liste",".:u*s9D5|y9-7h1rjuQk-test.liste-list"]},"shadow":false,"topLevel":false}},"comments":{},"currentCostume":0,"costumes":[{"assetId":"bcaaa8547a07cfe572c0967ba829e99d","name":"costume1","bitmapResolution":1,"md5ext":"bcaaa8547a07cfe572c0967ba829e99d.svg","dataFormat":"svg","rotationCenterX":47,"rotationCenterY":55},{"assetId":"11d6c5fbd91e433a1b85a00fd9dd43b6","name":"costume2","bitmapResolution":1,"md5ext":"11d6c5fbd91e433a1b85a00fd9dd43b6.svg","dataFormat":"svg","rotationCenterX":47,"rotationCenterY":55}],"sounds":[{"assetId":"83c36d806dc92327b9e7049a565c6bff","name":"meow","dataFormat":"wav","format":"","rate":48000,"sampleCount":40681,"md5ext":"83c36d806dc92327b9e7049a565c6bff.wav"}],"volume":100,"layerOrder":1,"visible":true,"x":2,"y":0,"size":100,"direction":90,"draggable":false,"rotationStyle":"all around"}],"monitors":[{"id":".:u*s9D5|y9-7h1rjuQk-test.liste-list","mode":"list","opcode":"data_listcontents","params":{"LIST":"test.liste"},"spriteName":null,"value":["hi",""],"width":102,"height":202,"x":352,"y":69,"visible":true},{"id":".:u*s9D5|y9-7h1rjuQk-sulf.c.liste-list","mode":"list","opcode":"data_listcontents","params":{"LIST":"sulf.c.liste"},"spriteName":null,"value":["sss"],"width":106,"height":206,"x":35,"y":72,"visible":true}],"extensions":[],"meta":{"semver":"3.0.0","vm":"0.2.0-prerelease.20190619042313","agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"}} -------------------------------------------------------------------------------- /backend/sb3/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/backend/sb3/.gitkeep -------------------------------------------------------------------------------- /backend/sb3converter.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require("fs"); 3 | const fetch = require('node-fetch'); 4 | var JSZip = require("jszip"); 5 | const axios = require("axios") 6 | 7 | const projectJSONBaseURL = "https://projects.scratch.mit.edu/" 8 | const assetsBaseURL = "https://cdn.assets.scratch.mit.edu/internalapi/asset/" 9 | 10 | var getProjectJSON = async function (projectID) { 11 | //console.log(projectJSONBaseURL + projectID) 12 | let resp = await axios.get('https://api.scratch.mit.edu/projects/' + projectID) 13 | console.log(resp.data.project_token) 14 | const req = await fetch(projectJSONBaseURL + projectID+"?token="+resp.data.project_token); 15 | return req.json() 16 | } 17 | 18 | var getAssets = async function (sb3Name) { 19 | 20 | //console.log(assetsBaseURL + sb3Name+"/get/") 21 | const req = await fetch(assetsBaseURL + sb3Name + "/get/"); 22 | return req.buffer() 23 | 24 | } 25 | 26 | 27 | 28 | var convertFromFile = function (file) { 29 | console.log(file) 30 | return new Promise((resolve, reject) => { 31 | //console.log("[CONVERTER] started converting " + projectID + " from FILE") 32 | newZip.generateNodeStream({ type: 'nodebuffer', streamFiles: true }) 33 | .pipe(fs.createWriteStream("./sb3/" + projectID + ".sb3")) 34 | .on('finish', function () { 35 | // JSZip generates a readable stream with a "end" event, 36 | // but is piped here in a writable stream which emits a "finish" event. 37 | console.log("out.zip written."); 38 | }); 39 | 40 | let findsb2 = setInterval(() => { 41 | var files = fs.readdirSync('./sb2/'); 42 | // console.log(files) 43 | files.forEach(element => { 44 | if (element == projectID + ".sb2") { 45 | // console.log(projectID + ".sb2") 46 | fs.readFile('./sb2/' + projectID + ".sb2", function (err, data) { 47 | if (err) throw err; 48 | // console.log(data.buffer); 49 | resolve(data.buffer); 50 | }); 51 | clearInterval(findsb2); 52 | } 53 | }); 54 | }, 1000); 55 | 56 | 57 | 58 | }); 59 | } 60 | 61 | var convertFromID = function (projectID) { 62 | return new Promise((resolve, reject) => { 63 | console.log("[CONVERTER] started converting " + projectID + " from ID") 64 | 65 | 66 | 67 | 68 | fs.readFile('./sb2/' + projectID + ".sb2", function (err, data) { 69 | if (err) { 70 | // throw err; 71 | var newZip = new JSZip(); 72 | 73 | getProjectJSON(projectID).then(res => { 74 | 75 | var filemap = parseMap(res.targets); 76 | 77 | 78 | let outJSON = JSON.stringify(res).replace("☁", "\u2601") 79 | 80 | 81 | fs.writeFileSync("./sb2/" + projectID + "project.json", outJSON) 82 | 83 | 84 | 85 | newZip.file("project.json", outJSON); 86 | 87 | 88 | 89 | 90 | 91 | Object.keys(filemap).forEach(sb3Name => { 92 | 93 | getAssets(sb3Name).then(asset => { 94 | newZip.file(sb3Name, asset); 95 | 96 | }).then(function () { 97 | if (Object.keys(filemap).length == Object.keys(newZip.files).length - 1) { 98 | // console.log(newZip) 99 | newZip.generateNodeStream({ type: 'nodebuffer', streamFiles: true }) 100 | .pipe(fs.createWriteStream("./sb3/" + projectID + ".sb3")) 101 | .on('finish', function () { 102 | // JSZip generates a readable stream with a "end" event, 103 | // but is piped here in a writable stream which emits a "finish" event. 104 | console.log("out.zip written."); 105 | }); 106 | 107 | let findsb2 = setInterval(() => { 108 | var files = fs.readdirSync('./sb2/'); 109 | // console.log(files) 110 | files.forEach(element => { 111 | if (element == projectID + ".sb2") { 112 | // console.log(projectID + ".sb2") 113 | fs.readFile('./sb2/' + projectID + ".sb2", function (err, data) { 114 | if (err) throw err; 115 | // console.log(data.buffer); 116 | resolve(data.buffer); 117 | }); 118 | clearInterval(findsb2); 119 | } 120 | }); 121 | }, 1000); 122 | 123 | 124 | console.log("[CONVERTER] finished converting " + projectID + " from ID") 125 | } 126 | }) 127 | }) 128 | }) 129 | return 130 | } 131 | console.log("[CONVERTER] found converted project " + projectID) 132 | resolve(data.buffer); 133 | }); 134 | 135 | 136 | 137 | 138 | 139 | }); 140 | } 141 | 142 | function parseMap(targets) { 143 | var out = {}; 144 | var costumeList = {}; 145 | var soundList = {}; 146 | targets.forEach(target => { 147 | target.costumes.forEach(costume => { 148 | if (costumeList[costume.assetId] == null) 149 | costumeList[costume.assetId] = Object.keys(costumeList).length; 150 | 151 | if (costume.md5ext == undefined) { 152 | out[costume.assetId + "." + costume.dataFormat] = costumeList[costume.assetId] + "." + costume.dataFormat; 153 | } else { 154 | out[costume.md5ext] = costumeList[costume.assetId] + "." + costume.dataFormat; 155 | } 156 | 157 | }); 158 | 159 | target.sounds.forEach(sound => { 160 | if (soundList[sound.assetId] == null) 161 | soundList[sound.assetId] = Object.keys(soundList).length; 162 | 163 | out[sound.md5ext] = soundList[sound.assetId] + "." + sound.dataFormat; 164 | }); 165 | }); 166 | return out; 167 | } 168 | 169 | module.exports = { convertFromFile, convertFromID, getProjectJSON, getAssets, parseMap }; -------------------------------------------------------------------------------- /backend/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | var https = require('https'); 4 | var http = require('http'); 5 | var fs = require('fs'); 6 | const axios = require('axios'); 7 | 8 | var cloudSave = require("./cloud.js") 9 | var sb3converter = require("./sb3converter.js") 10 | var packager = require("./package.js") 11 | var admintools = require("./admintools/admintools.js") 12 | 13 | 14 | process.env.ADMINTOOLS = "true" 15 | 16 | console.log(process.env.ADMINTOOLS) 17 | 18 | 19 | 20 | 21 | if (process.env.ISSULFSERVER == "true") { 22 | var files = fs.readdirSync('./ssl/'); 23 | console.log(files) 24 | 25 | var server = https.createServer({ 26 | key: fs.readFileSync('./ssl/privkey.pem'), cert: fs.readFileSync('./ssl/cert.pem'), 27 | requestCert: false, 28 | rejectUnauthorized: false 29 | }, app).listen(8082); 30 | console.log("RUNNING ON SULF SERVER") 31 | } else { 32 | var server = http.createServer(app).listen(8082); 33 | } 34 | 35 | var io = require('socket.io').listen(server); 36 | 37 | io.on('connection', function (socket) { 38 | 39 | 40 | admintools.setCurrentConnections(Object.keys(io.sockets.sockets).length) 41 | socket.on('sendSB3file', function (data) { 42 | var file = sb3converter.convertFromFile(data).then(function (file) { 43 | socket.emit("sendSB2file", file); 44 | }); 45 | }); 46 | socket.on('sendSB3ID', function (data) { 47 | var file = sb3converter.convertFromID(data).then(function (file) { 48 | socket.emit("sendSB2file", file); 49 | }) 50 | }); 51 | socket.on('getReq', function (data) { 52 | // console.log("DATA FROM PLAYER: ",data) 53 | cloudSave.getReq(data); 54 | console.log(cloudSave.CLOUDSAVE[data.projectID]) 55 | socket.emit('getRes', cloudSave.CLOUDSAVE[data.projectID].vars); 56 | }); 57 | socket.on('getPackage', function (data) { 58 | console.log("packager") 59 | //console.log(data) 60 | if (data.zip != undefined) { 61 | console.log("package from file") 62 | var b64string = data.zip; 63 | var buf = Buffer.from(b64string, 'base64'); 64 | packager.generatePackageFromZip(buf, data.settings, function (output) { 65 | console.log("done Converting") 66 | socket.emit('sendPackage', output); 67 | }); 68 | } 69 | if (data.id != undefined) { 70 | console.log("package from id") 71 | packager.generatePackageFromID(data.id, data.settings, function (output) { 72 | console.log("done Converting") 73 | socket.emit('sendPackage', output); 74 | }); 75 | } 76 | }); 77 | socket.on('getProjectData', async function (data,cb) { 78 | let resp = await axios.get('https://api.scratch.mit.edu/projects/' + data) 79 | .then(function (response) { 80 | // handle success 81 | // console.log(response); 82 | socket.emit('getProjectDataReturn', response.data); 83 | cb(response.data) 84 | }) 85 | .catch(function (error) { 86 | // handle error 87 | console.log(error); 88 | }) 89 | .finally(function () { 90 | // always executed 91 | }); 92 | }) 93 | socket.on('logRequest', function (data) { 94 | admintools.logRequest(data); 95 | }) 96 | socket.on('disconnect', function () { 97 | admintools.setCurrentConnections(Object.keys(io.sockets.sockets).length) 98 | }) 99 | }); 100 | 101 | 102 | if (process.env.ADMINTOOLS == "true") { 103 | console.log("LOADING ADMINTOOLS!") 104 | admintools.setupAdminTools(io); 105 | } else { 106 | console.log("NO ADMINTOOLS") 107 | } -------------------------------------------------------------------------------- /backend/ssl/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/backend/ssl/.gitkeep -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | sulfurous-sb3tosb2: 4 | build: ./sb3tosb2 5 | volumes: 6 | - ./sb3tosb2/src:/work/src 7 | - ./backend/sb2:/work/sb2 8 | - ./backend/sb3:/work/sb3 9 | container_name: sulfurous-sb3tosb2 10 | sulfurous-frontend: 11 | image: "node:10-alpine" 12 | user: "root" 13 | working_dir: /home/node/app 14 | environment: 15 | - NODE_ENV=production 16 | ports: 17 | - "3000:3000" 18 | volumes: 19 | - ./frontend:/home/node/app 20 | command: "npm start" 21 | container_name: sulfurous-frontend 22 | restart: unless-stopped 23 | sulfurous-backend: 24 | image: "node:10-alpine" 25 | user: "root" 26 | working_dir: /home/node/app 27 | environment: 28 | - NODE_ENV=production 29 | ports: 30 | - "8082:8082" 31 | volumes: 32 | - ./backend:/home/node/app 33 | - ./sb3tosb2/sb3:/home/node/app/sb3 34 | - ./sb3tosb2/sb2:/home/node/app/sb2 35 | command: "npm start" 36 | container_name: sulfurous-backend 37 | restart: unless-stopped 38 | -------------------------------------------------------------------------------- /docker-composeSULFSER.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | sulfurous-sb3tosb2: 4 | build: ./sb3tosb2 5 | volumes: 6 | - ./sb3tosb2/src:/work/src 7 | - ./sb3tosb2/sb2:/work/sb2 8 | - ./sb3tosb2/sb3:/work/sb3 9 | container_name: sulfurous-sb3tosb2 10 | sulfurous-backend: 11 | image: "node:14-alpine" 12 | user: "root" 13 | working_dir: /home/node/app 14 | environment: 15 | - NODE_ENV=production 16 | - ISSULFSERVER=true 17 | ports: 18 | - "8082:8082" 19 | volumes: 20 | - ./backend:/home/node/app 21 | - ./sb3tosb2/sb3:/home/node/app/sb3 22 | - ./sb3tosb2/sb2:/home/node/app/sb2 23 | - ./backend/ssl:/home/node/app/ssl 24 | command: "npm start" 25 | container_name: sulfurous-backend 26 | restart: unless-stopped 27 | -------------------------------------------------------------------------------- /frontend/main.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | 4 | app.listen(3000, () => console.log('listening at 3000')); 5 | app.use(express.static('./public')); -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sulfurous-frontend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "main.js", 6 | "scripts": { 7 | "start": "npm i && node main" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Mittagskogel/Sulfurous.git" 12 | }, 13 | "author": "Alexander Pichler", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/Mittagskogel/Sulfurous/issues" 17 | }, 18 | "homepage": "https://github.com/Mittagskogel/Sulfurous#readme", 19 | "dependencies": { 20 | "express": "^4.17.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frontend/public/admintools/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Sulfurous-Admintools 8 | 9 | 118 | 119 | 120 |
121 |
122 |

Current Connections

123 |

00000

124 |
125 | 126 |
127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 |
Last HourLast 24hLast 30Days
000000000000000000
139 |
140 |
141 |
142 |
143 |

SB2 IDs Log

144 |
145 | 146 |
147 |
148 |
149 |

SB3 IDs Log

150 |
151 | 152 |
153 |
154 |
155 | This is a test where am I 156 |
157 |
158 | 159 | 160 | 161 | 208 | 209 | -------------------------------------------------------------------------------- /frontend/public/css/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #000; 3 | margin: 0; 4 | overflow: hidden; 5 | } 6 | .player { 7 | position: absolute; 8 | } 9 | .splash, 10 | .error { 11 | position: absolute; 12 | top: 0; 13 | left: 0; 14 | width: 100%; 15 | height: 100%; 16 | background: #000; 17 | display: table; 18 | color: #fff; 19 | } 20 | .error { 21 | display: none; 22 | } 23 | .webgl-error { 24 | position: absolute; 25 | top: 0; 26 | left: 0; 27 | width: 100%; 28 | height: 100%; 29 | background: #000; 30 | display: table; 31 | color: #fff; 32 | display: none; 33 | } 34 | .splash > div, 35 | .error > div { 36 | display: table-cell; 37 | height: 100%; 38 | text-align: center; 39 | vertical-align: middle; 40 | } 41 | .webgl-error > div { 42 | display: table-cell; 43 | height: 100%; 44 | text-align: center; 45 | vertical-align: middle; 46 | } 47 | .progress { 48 | width: 80%; 49 | height: 16px; 50 | border: 1px solid #fff; 51 | margin: 0 auto; 52 | } 53 | .progress-bar { 54 | background: #fff; 55 | width: 10%; 56 | height: 100%; 57 | } 58 | h1 { 59 | font: 300 72px Helvetica Neue, Helvetica, Arial, sans-serif; 60 | margin: 0 0 16px; 61 | } 62 | p { 63 | font: 300 24px/1.5 Helvetica Neue, Helvetica, Arial, sans-serif; 64 | margin: 0; 65 | color: rgba(255, 255, 255, .6); 66 | } 67 | .error a { 68 | color: #fff; 69 | } 70 | -------------------------------------------------------------------------------- /frontend/public/css/embed.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 480px; 3 | margin: 0 auto; 4 | } 5 | 6 | .progress-bar { 7 | padding: 0; 8 | } 9 | 10 | .internal-error { 11 | position: absolute; 12 | left: 1px; 13 | bottom: 1px; 14 | right: 1px; 15 | background: #fff; 16 | box-shadow: 0 -1px rgba(0, 0, 0, .4); 17 | } 18 | 19 | .controls { 20 | display: none; 21 | } 22 | 23 | .has-ui .controls { 24 | display: block; 25 | } 26 | 27 | .hide-ui .controls { 28 | position: absolute; 29 | width: 100%; 30 | } 31 | .hide-ui .controls span { 32 | display: none; 33 | } 34 | .hide-ui .player { 35 | box-shadow: none; 36 | } 37 | -------------------------------------------------------------------------------- /frontend/public/css/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 100vw; 3 | height: 98vh; 4 | margin: 0px; 5 | margin-top: 1vh; 6 | background-color: #424242; 7 | 8 | color: black; 9 | /*image-rendering: -moz-crisp-edges;*/ 10 | /*image-rendering: pixelated;*/ 11 | overflow-x: hidden; /* Hide scrollbars */ 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | display: none; 16 | } 17 | 18 | .featured-image { 19 | display: none; 20 | } 21 | 22 | .version { 23 | position: absolute; 24 | top: 80%; 25 | left: 80%; 26 | transform: translate(-80%, -80%); 27 | font-size: 80%; 28 | /* use same color as url */ 29 | color: rgba(0, 0, 0, .4); 30 | } 31 | 32 | .logocontainer{ 33 | position: relative; 34 | text-align: center; 35 | color: white; 36 | } 37 | 38 | .url { 39 | 40 | background: 0; 41 | border: 0; 42 | margin: 0; 43 | padding: 0 0 0 32px; 44 | outline: 0; 45 | font: 300 23px/32px Helvetica Neue, Helvetica, Arial, sans-serif; 46 | display: block; 47 | width: 100%; 48 | -moz-box-sizing: border-box; 49 | box-sizing: border-box; 50 | color: #000; 51 | position: absolute; 52 | top: 50%; 53 | -ms-transform: translateY(-50%); 54 | transform: translateY(-50%); 55 | } 56 | 57 | .project-link { 58 | position: absolute; 59 | top: 50%; 60 | -ms-transform: translateY(-50%); 61 | transform: translateY(-50%); 62 | 63 | background-position: -160px 0; 64 | } 65 | 66 | a { 67 | color: #25d; 68 | text-decoration: underline; 69 | } 70 | 71 | a:visited { 72 | color: #73c; 73 | } 74 | 75 | a:active { 76 | color: #03a; 77 | } 78 | 79 | .dropdown { 80 | display: inline-block; 81 | position: relative; 82 | } 83 | 84 | .dropdown>select { 85 | -webkit-appearance: none; 86 | font: inherit; 87 | position: absolute; 88 | opacity: 0; 89 | cursor: pointer; 90 | top: 0; 91 | left: 0; 92 | width: 100%; 93 | height: 100%; 94 | } 95 | 96 | .area { 97 | overflow: hidden; 98 | margin: 0 -24px; 99 | padding: 0 24px; 100 | margin: 0 auto 0; 101 | width: 60%; 102 | max-width: 880px; 103 | min-width: 480px; 104 | border-radius: 20px; 105 | background-color: #bcbcbc; 106 | } 107 | 108 | #title { 109 | height: 4vh; 110 | position: relative; 111 | } 112 | 113 | #playercontainer{ 114 | float: left; 115 | } 116 | 117 | #qrdiv{ 118 | float: right; 119 | 120 | text-align: center; 121 | } 122 | #qrcode{ 123 | margin: 0 auto 0; 124 | width: 256px; 125 | } 126 | 127 | 128 | #project-area { 129 | display: none; 130 | 131 | margin-top: 1vh; 132 | } 133 | 134 | #title-area { 135 | margin-bottom: 1vh; 136 | } 137 | 138 | #credits-area { 139 | margin-top: 1vh; 140 | border-radius: 20px; 141 | padding-bottom: 2px; 142 | } 143 | 144 | a,a:visited{ 145 | color: #1f63ff; 146 | 147 | text-decoration:none 148 | } 149 | 150 | .privlink{ 151 | text-align: right; 152 | } 153 | 154 | .progress-bar{ 155 | background-color: #ff9900; 156 | } 157 | 158 | 159 | .fs #player-area { 160 | overflow: visible; 161 | } 162 | 163 | 164 | .package{ 165 | 166 | float: left; 167 | } 168 | .embed{ 169 | 170 | float: right; 171 | } 172 | 173 | 174 | h1, 175 | p { 176 | font: 300 16px/1.5 Helvetica Neue, Helvetica, Arial, sans-serif; 177 | margin: 0 0 16px; 178 | } 179 | 180 | h1 { 181 | font: 300 24px/32px Helvetica Neue, Helvetica, Arial, sans-serif; 182 | margin: 16px 0 0; 183 | } 184 | 185 | h1.title { 186 | font: 300 54px/72px Helvetica Neue, Helvetica, Arial, sans-serif; 187 | margin: 0; 188 | } 189 | 190 | code { 191 | font: 12px Menlo, Monaco, Consolas, Courier New, monospace; 192 | background: #f5f5f5; 193 | border-radius: 3px; 194 | padding: 3px; 195 | } 196 | 197 | .package a { 198 | padding: 2px 8px; 199 | background: linear-gradient(#fafafa, #e8e8e8); 200 | box-shadow: 0 0 0 1px rgba(0, 0, 0, .35), 0 1px 4px rgba(0, 0, 0, .2); 201 | border-radius: 3px; 202 | cursor: pointer; 203 | color: #000; 204 | text-decoration: none; 205 | -webkit-tap-highlight-color: transparent; 206 | } 207 | 208 | .package a:hover { 209 | background: linear-gradient(#fff, #eaeaea); 210 | box-shadow: 0 0 0 1px rgba(0, 0, 0, .4), 0 1px 4px rgba(0, 0, 0, .3); 211 | } 212 | 213 | .package a:active { 214 | background: linear-gradient(#ddd, #eaeaea); 215 | box-shadow: 0 0 0 1px rgba(0, 0, 0, .5), inset 0 2px 5px rgba(0, 0, 0, .15); 216 | } 217 | 218 | .package label, 219 | .package input, 220 | .package a, 221 | .package span { 222 | display: inline-block; 223 | vertical-align: middle; 224 | } 225 | 226 | .package input[type=checkbox] { 227 | margin: 0 0 0 16px; 228 | } 229 | 230 | .xyinput { 231 | width: 8ch; 232 | } 233 | 234 | #embed-code { 235 | background: 0; 236 | border: 1px solid rgba(0, 0, 0, .4); 237 | margin: 0; 238 | padding: 4px; 239 | outline: 0; 240 | width: 80px; 241 | font: 12px/16px Menlo, Monaco, Consolas, Courier New, monospace; 242 | -moz-box-sizing: border-box; 243 | box-sizing: border-box; 244 | color: rgba(0, 0, 0, .7); 245 | } -------------------------------------------------------------------------------- /frontend/public/css/player.css: -------------------------------------------------------------------------------- 1 | .progress-bar { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | bottom: 0; 6 | width: 0; 7 | background: #cde; 8 | -webkit-transition: .3s; 9 | -moz-transition: .3s; 10 | -o-transition: .3s; 11 | transition: .3s; 12 | } 13 | .light-content .progress-bar { 14 | background: #468; 15 | } 16 | .progress-bar.error { 17 | background: #ecc; 18 | } 19 | .light-content .progress-bar.error { 20 | background: #844; 21 | } 22 | .controls { 23 | position: relative; 24 | height: 32px; 25 | } 26 | .controls span, 27 | .project-link { 28 | width: 32px; 29 | height: 32px; 30 | float: right; 31 | cursor: pointer; 32 | text-align: center; 33 | opacity: .4; 34 | background-image: url(../img/icons.svg); 35 | text-decoration: none; 36 | } 37 | .controls .flag { 38 | background-position: 0 0; 39 | } 40 | .controls .stop { 41 | background-position: -96px 0; 42 | } 43 | .controls .pause { 44 | background-position: -32px 0; 45 | } 46 | .controls .play { 47 | background-position: -64px 0; 48 | } 49 | .controls .full-screen { 50 | float: left; 51 | background-position: -128px 0; 52 | } 53 | .controls span:active, 54 | .project-link:active { 55 | opacity: 1 !important; 56 | } 57 | .controls .turbo { 58 | float: right; 59 | display: none; 60 | cursor: default; 61 | color: rgba(0, 0, 0, .4); 62 | font: 500 12px/32px Helvetica Neue, Helvetica, Arial, sans-serif; 63 | padding: 0 8px; 64 | } 65 | .player { 66 | box-shadow: 0 0 0 1px rgba(0, 0, 0, .4); 67 | width: 480px; 68 | height: 360px; 69 | position: relative; 70 | -webkit-transform-origin: 0 0; 71 | -moz-transform-origin: 0 0; 72 | -ms-transform-origin: 0 0; 73 | -o-transform-origin: 0 0; 74 | transform-origin: 0 0; 75 | } 76 | .light-content .player { 77 | box-shadow: 0 0 0 1px rgba(255, 255, 255, .4); 78 | } 79 | 80 | .internal-error { 81 | color: rgba(128, 0, 0, .6); 82 | font: 500 12px Helvetica Neue, Helvetica, Arial, sans-serif; 83 | padding: 8px; 84 | display: none; 85 | } 86 | .internal-error a { 87 | color: rgba(128, 0, 0, .8); 88 | } 89 | 90 | .webgl-error { 91 | color: rgba(128, 0, 0, .6); 92 | font: 500 12px Helvetica Neue, Helvetica, Arial, sans-serif; 93 | padding: 8px; 94 | display: none; 95 | } 96 | .webgl-error a { 97 | color: rgba(128, 0, 0, .8); 98 | } 99 | 100 | .fs { 101 | background: #000; 102 | width: 100%; 103 | height: 100%; 104 | } 105 | .fs body { 106 | margin: 0; 107 | padding: 0; 108 | overflow: hidden; 109 | } 110 | .fs .title, 111 | .fs section { 112 | display: none; 113 | } 114 | .fs .controls span, 115 | .light-content .controls span { 116 | background-position-y: -32px; 117 | opacity: .6; 118 | } 119 | /* 120 | .fs .controls .flag, 121 | .light-content .controls .flag{ 122 | background-position: 0 -32px; 123 | } 124 | .fs .controls .stop, 125 | .light-content .controls .stop{ 126 | background-position: -96px -32px; 127 | } 128 | .fs .controls .pause, 129 | .light-content .controls .pause{ 130 | background-position: -32px -32px; 131 | } 132 | .fs .controls .play, 133 | .light-content .controls .play{ 134 | background-position: -64px -32px; 135 | } 136 | .fs .controls .full-screen, 137 | .light-content .controls .full-screen{ 138 | float: left; 139 | background-position: -128px -32px; 140 | }*/ 141 | .light-content .controls .full-screen { 142 | background-position-y: -64px; 143 | } 144 | .fs .light-content .controls .full-screen { 145 | background-position-y: -32px; 146 | } 147 | .fs .controls .turbo, 148 | .light-content .controls .turbo { 149 | color: rgba(255, 255, 255, .6); 150 | } 151 | .fs .player { 152 | box-shadow: none; 153 | } 154 | -------------------------------------------------------------------------------- /frontend/public/fonts/DSEG7Classic-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/fonts/DSEG7Classic-Light.ttf -------------------------------------------------------------------------------- /frontend/public/html/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | sulfurous 7 | 8 | 9 |
10 |
11 |
12 |

sulfurous

13 |
14 |
15 |
16 |

17 |
18 |
19 |
20 |
21 |

sulfurous

22 |

An error has occurred. Click here to 23 | file a bug report on GitHub.

24 |
25 |
26 |
27 |
28 |

sulfurous

29 |

It looks like your device doesn't support WebGL. You can try viewing this project in the legacy player here.

31 |
32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /frontend/public/html/embed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | phosphorus 5 | 6 | 7 |
8 |
9 | 10 | 11 | 12 |
Turbo Mode
13 | 14 |
15 |
16 |
17 | An internal error occurred. Click here to file a bug report. 18 |
19 |
20 | It looks like your device doesn't support WebGL. You can try viewing this project in the legacy player here. 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /frontend/public/html/embedtest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

If embed works, you should see a project below:

7 | 8 | 9 | -------------------------------------------------------------------------------- /frontend/public/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/img/favicon.ico -------------------------------------------------------------------------------- /frontend/public/img/icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 39 | 46 | 57 | 58 | 60 | 61 | 63 | image/svg+xml 64 | 66 | 67 | 68 | 69 | 70 | 75 | 81 | 87 | 93 | 99 | 105 | 111 | 116 | 122 | 128 | 134 | 140 | 146 | 152 | 158 | 164 | 170 | 176 | 182 | 188 | 194 | 200 | 206 | 216 | 222 | 228 | 234 | 240 | 246 | 256 | 266 | 276 | 286 | 296 | 306 | 312 | 318 | 324 | 330 | 331 | 332 | -------------------------------------------------------------------------------- /frontend/public/img/sulfurouslogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/img/sulfurouslogo.png -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Sulfurous 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 |
20 |
21 | sulfurous 22 |

WebGL v 0.98

23 |
24 |

Sulfurous, just like Phosphorus, runs your Scratch projects really fast by compiling them to JavaScript. Try it 25 | out 26 | by pasting a URL or project ID into the field below or choosing an example. You can also drag a local .sb2 or .sb3 project onto this page.

37 |
38 | 41 |
42 |
43 |
44 | 45 | 46 | 47 |
Turbo Mode
48 | 49 |
50 |
51 |
52 | An internal error occurred. Click here to file a bug report.
54 |
55 | It looks like your device doesn't support WebGL. You can try viewing this project in the legacy player here .
57 |
58 |

59 | 65 |
66 |
67 |
68 | 70 | 71 |
72 | 75 |
76 |
77 |

Package this project

78 |

Get a link to a web page that automatically runs your project.

79 |

80 | Package 81 | 82 | 83 | 84 |
85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |

103 |
104 |
105 |

Embed this project

106 |

Include the sulfurous player in your web site.

107 |

108 | 109 | 110 | 111 | 112 | 113 |
114 | 115 | 116 | 117 | 118 | 119 | 120 |
121 | 122 | 123 | 124 | 125 | 126 |

127 |
128 |
129 | 132 |
133 |

Credits

134 |

Sulfurous was created by Mittagskogel and further developed by 135 | FRALEX as part of their work at the Alpen-Adria-University Klagenfurt. Sulfurous is based off Phosphorus, which 137 | was created by Nathan Dinsmore. Its CPS-style compilation and overall 138 | design was inspired by Rhys Simpson's sb2.js . It would have more bugs if not for Truman Kilen. It uses the JSZip 141 | library, created by Stuart Knightley, David Duponchel, Franz Buchinger, and António Afonso, to read .sb2 files and compressed projects, 144 | and the canvg library, created by Gabe Lerner, to render SVGs in 146 | <canvas> elements. 147 |

Code

148 |

Sulfurous is released under the MIT license, the source code for Sulfurous is available on GitHub. See Phosphorus for the original version.

151 |

Report a problem

152 |

Sulfurous is still in development. Click here to report a problem with this 154 | project. If you are the creator of this Project please publish your project on scratch that we can see the 155 | code and debug it.

156 | 161 |
162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /frontend/public/js/app.js: -------------------------------------------------------------------------------- 1 | var splash = document.querySelector('.splash'); 2 | var progressBar = document.querySelector('.progress-bar'); 3 | var error = document.querySelector('.error'); 4 | var bugLink = document.querySelector('#bug-link'); 5 | var webGLError = document.querySelector('.webgl-error'); 6 | var legacyLink = document.querySelector('#legacy-link'); 7 | var player = document.querySelector('.player'); 8 | 9 | var stage; 10 | 11 | var projectId = 17088932; 12 | var projectTitle = ''; 13 | var turbo = false; 14 | var fullScreen = true; 15 | var aspectX = 4; 16 | var aspectY = 3; 17 | var resolutionX = 480; 18 | var resolutionY = 360; 19 | let isPackage = false; 20 | 21 | (function () { 22 | 'use strict'; 23 | 24 | 25 | setupWebsocket("app") 26 | 27 | 28 | 29 | 30 | 31 | 32 | /* 33 | if (location.protocol === 'https:') { 34 | location.replace(('' + location).replace(/^https:/, 'https:')); 35 | } 36 | */ 37 | 38 | 39 | 40 | var params = location.search.substr(1).split('&'); 41 | params.forEach(function (p) { 42 | var parts = p.split('='); 43 | if (parts.length > 1) { 44 | switch (parts[0]) { 45 | case 'id': 46 | if (parts[1] == "zip") { 47 | isPackage = true; 48 | } 49 | projectId = Number(parts[1]); 50 | break; 51 | case 'turbo': 52 | turbo = parts[1] !== 'false'; 53 | break; 54 | case 'full-screen': 55 | fullScreen = parts[1] !== 'false'; 56 | break; 57 | case 'aspect-x': 58 | aspectX = Number(parts[1]); 59 | break; 60 | case 'aspect-y': 61 | aspectY = Number(parts[1]); 62 | break; 63 | case 'resolution-x': 64 | resolutionX = Number(parts[1]); 65 | break; 66 | case 'resolution-y': 67 | resolutionY = Number(parts[1]); 68 | break; 69 | default: 70 | console.log('Skipping unknown option: ' + parts[0] + '=' + parts[1]); 71 | } 72 | } 73 | }); 74 | 75 | P.resolution = resolutionX; 76 | 77 | 78 | 79 | 80 | 81 | function mobileFullScreen(e) { 82 | if (e) e.preventDefault(); 83 | document.documentElement.classList.toggle('fs'); 84 | 85 | if (!e) { 86 | var el = document.documentElement; 87 | if (el.requestFullScreenWithKeys) { 88 | el.requestFullScreenWithKeys(); 89 | } else if (el.webkitRequestFullScreen) { 90 | el.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); 91 | } else if (el.mozRequestFullScreen) { 92 | el.mozRequestFullScreen(); 93 | } else { 94 | console.warn('No full screen available.'); 95 | } 96 | } 97 | updateFullScreen(); 98 | } 99 | 100 | function updateFullScreen() { 101 | if (!stage) return; 102 | 103 | resolutionX = !resolutionX ? 480 : resolutionX; 104 | resolutionY = !resolutionY ? 360 : resolutionY; 105 | 106 | window.scrollTo(0, 0); 107 | var padding = 8; 108 | var w = window.innerWidth - padding * 2; 109 | var h = window.innerHeight - padding; 110 | w = Math.min(w, h * resolutionX / resolutionY); 111 | h = w * resolutionY / resolutionX; 112 | document.body.style.width = w + 'px'; 113 | document.body.style.height = h + 'px'; 114 | document.body.style.marginLeft = (window.innerWidth - w) / 2 + 'px'; 115 | document.body.style.marginTop = (window.innerHeight - h + padding) / 2 + 'px'; 116 | stage.setZoom(w / 480, w * resolutionY / resolutionX / 360); 117 | } 118 | 119 | 120 | if (P.hasTouchEvents) { 121 | window.addEventListener('load', function () { 122 | document.getElementById('fullscreenWarning').textContent = 'On mobile browsers, use two finger tap in portrait orientation and turn to landscape orientation for full screen.'; 123 | }); 124 | 125 | document.addEventListener('touchstart', function (e) { 126 | if (e.targetTouches.length >= 2) 127 | mobileFullScreen(e); 128 | }); 129 | } 130 | 131 | 132 | 133 | window.addEventListener('resize', layout); 134 | 135 | if (P.hasTouchEvents) { 136 | document.addEventListener('touchmove', function (e) { 137 | e.preventDefault(); 138 | }); 139 | } 140 | 141 | 142 | 143 | 144 | 145 | load(projectId) 146 | }()); 147 | 148 | function layout() { 149 | if (!stage) return; 150 | var w = Math.min(window.innerWidth, window.innerHeight * aspectX / aspectY); 151 | if (!fullScreen) w = resolutionX; 152 | var h = w * aspectY / aspectX; 153 | if (!fullScreen) h = resolutionY; 154 | player.style.left = (window.innerWidth - w) / 2 + 'px'; 155 | player.style.top = (window.innerHeight - h) / 2 + 'px'; 156 | stage.setZoom(w / 480, h / 360); 157 | stage.draw(); 158 | } 159 | 160 | function showError(e) { 161 | error.style.display = 'table'; 162 | bugLink.href = 'https://github.com/mittagskogel/sulfurous/issues/new?title=' + encodeURIComponent(projectTitle || '') + '&body=' + encodeURIComponent('\n\n\nhttps://scratch.mit.edu/projects/' + projectId + '\nhttps://newton.nes.aau.at/~sulfurous/#' + projectId + (e.stack ? '\n\n```\n' + e.stack + '\n```' : '')); 163 | console.error(e.stack); 164 | } 165 | 166 | function showWebGLError(e) { 167 | webGLError.style.display = 'table'; 168 | legacyLink.href = location; 169 | legacyLink.href = legacyLink.href.replace('html', 'legacy/html'); 170 | console.error(e); 171 | } 172 | 173 | function isSB2(id) { 174 | 175 | return true 176 | 177 | return new Promise(function (resolve, reject) { 178 | fetch('https://api.scratch.mit.edu/projects/' + id) 179 | .then(function (response) { 180 | console.log(response.data) 181 | if (response.status == 200) { 182 | 183 | resolve(true); 184 | } else if (response.status == 404) { 185 | 186 | resolve(false); 187 | } 188 | }) 189 | }) 190 | 191 | 192 | } 193 | 194 | async function load(id) { 195 | 196 | if (isPackage) { 197 | 198 | fetch("../project.sb2").then(async (res) => { 199 | 200 | var request = P.IO.loadSB2File(await res.blob()); 201 | 202 | request.onload = function (s) { 203 | splash.style.display = 'none'; 204 | 205 | stage = s; 206 | layout(); 207 | 208 | stage.isTurbo = turbo; 209 | stage.start(); 210 | stage.triggerGreenFlag(); 211 | 212 | player.appendChild(stage.root); 213 | stage.focus(); 214 | stage.handleError = showError; 215 | }; 216 | request.onerror = showError; 217 | request.onprogress = function (e) { 218 | progressBar.style.width = (10 + e.loaded / e.total * 90) + '%'; 219 | }; 220 | 221 | 222 | 223 | 224 | }) 225 | 226 | 227 | } else if (await isSB2(id)) { 228 | console.log("SB2") 229 | 230 | socket.emit('getProjectData', id, async function (projectData) { 231 | 232 | var request = P.IO.loadScratchr2Project(id, projectData.project_token); 233 | 234 | request.onload = function (s) { 235 | splash.style.display = 'none'; 236 | 237 | stage = s; 238 | layout(); 239 | 240 | stage.isTurbo = turbo; 241 | stage.start(); 242 | stage.triggerGreenFlag(); 243 | 244 | player.appendChild(stage.root); 245 | stage.focus(); 246 | stage.handleError = showError; 247 | }; 248 | request.onerror = showError; 249 | request.onprogress = function (e) { 250 | progressBar.style.width = (10 + e.loaded / e.total * 90) + '%'; 251 | }; 252 | }) 253 | } else { 254 | console.log("SB3") 255 | socket.emit("sendSB3ID", id); 256 | } 257 | } 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | function loadFromSocket(data) { 266 | 267 | console.log(data) 268 | 269 | var blob = new Blob([data]); 270 | 271 | var request = P.IO.loadSB2File(blob); 272 | 273 | request.onload = function (s) { 274 | splash.style.display = 'none'; 275 | 276 | stage = s; 277 | layout(); 278 | 279 | stage.isTurbo = turbo; 280 | stage.start(); 281 | stage.triggerGreenFlag(); 282 | 283 | player.appendChild(stage.root); 284 | stage.focus(); 285 | stage.handleError = showError; 286 | }; 287 | request.onerror = showError; 288 | request.onprogress = function (e) { 289 | progressBar.style.width = (10 + e.loaded / e.total * 90) + '%'; 290 | }; 291 | } -------------------------------------------------------------------------------- /frontend/public/js/embed.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var script = document.currentScript || (function(scripts) { 5 | return scripts[scripts.length - 1]; 6 | })(document.getElementsByTagName('script')); 7 | 8 | var hasUI = true; 9 | var resolutionX = 480; 10 | var resolutionY = 360; 11 | var params = script.src.split('?')[1].split('&'); 12 | params.forEach(function(p) { 13 | var parts = p.split('='); 14 | if (parts.length > 1 && parts[0] === 'ui') { 15 | hasUI = parts[1] !== 'false'; 16 | } 17 | if (parts.length > 1 && parts[0] === 'resolution-x'){ 18 | resolutionX = Number(parts[1]); 19 | } 20 | if (parts.length > 1 && parts[0] === 'resolution-y'){ 21 | resolutionY = Number(parts[1]); 22 | } 23 | }); 24 | 25 | var iframe = document.createElement('iframe'); 26 | iframe.setAttribute('allowfullscreen', true); 27 | iframe.setAttribute('allowtransparency', true); 28 | iframe.src = script.src.replace(/^https:/, 'https:').replace(/js\/embed\.js/, 'html/embed.html'); 29 | //width: 482, height: 393 30 | iframe.width = hasUI ? resolutionX + 2 : resolutionX; 31 | iframe.height = hasUI ? resolutionY + 33 : resolutionY; 32 | iframe.style.border = '0'; 33 | iframe.className = 'phosphorus'; 34 | 35 | script.parentNode.replaceChild(iframe, script); 36 | 37 | }(this)); -------------------------------------------------------------------------------- /frontend/public/js/fonts.js: -------------------------------------------------------------------------------- 1 | WebFontConfig = { 2 | google: { families: [ 'Donegal+One::latin', 'Gloria+Hallelujah::latin', 'Permanent+Marker::latin', 'Mystery+Quest::latin' ] } 3 | }; 4 | (function() { 5 | var wf = document.createElement('script'); 6 | wf.src = ('https:' == document.location.protocol ? 'https' : 'http') + 7 | '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js'; 8 | wf.type = 'text/javascript'; 9 | wf.async = 'true'; 10 | var s = document.getElementsByTagName('script')[0]; 11 | s.parentNode.insertBefore(wf, s); 12 | })(); 13 | -------------------------------------------------------------------------------- /frontend/public/js/gyronorm.complete.min.js: -------------------------------------------------------------------------------- 1 | /*! Full Tilt v0.5.3 / http://github.com/richtr/Full-Tilt */ 2 | !function(a){function b(a){return a=+a,0===a||isNaN(a)?a:a>0?1:-1}function c(a){var b=new Promise(function(b,c){var d=function(e){setTimeout(function(){a&&a.data?b():e>=20?c():d(++e)},50)};d(0)});return b}function d(){o=n?(a.screen.orientation.angle||0)*j:(a.orientation||0)*j}function e(a){l.orientation.data=a;for(var b in l.orientation.callbacks)l.orientation.callbacks[b].call(this)}function f(a){l.motion.data=a;for(var b in l.motion.callbacks)l.motion.callbacks[b].call(this)}if(void 0===a.FULLTILT||null===a.FULLTILT){var g=Math.PI,h=g/2,i=2*g,j=g/180,k=180/g,l={orientation:{active:!1,callbacks:[],data:void 0},motion:{active:!1,callbacks:[],data:void 0}},m=!1,n=a.screen&&a.screen.orientation&&void 0!==a.screen.orientation.angle&&null!==a.screen.orientation.angle?!0:!1,o=(n?a.screen.orientation.angle:a.orientation||0)*j,p=h,q=g,r=i/3,s=-h,t={};t.version="0.5.3",t.getDeviceOrientation=function(a){var b=new Promise(function(b,d){var e=new t.DeviceOrientation(a);e.start();var f=new c(l.orientation);f.then(function(){e._alphaAvailable=l.orientation.data.alpha&&null!==l.orientation.data.alpha,e._betaAvailable=l.orientation.data.beta&&null!==l.orientation.data.beta,e._gammaAvailable=l.orientation.data.gamma&&null!==l.orientation.data.gamma,b(e)})["catch"](function(){e.stop(),d("DeviceOrientation is not supported")})});return b},t.getDeviceMotion=function(a){var b=new Promise(function(b,d){var e=new t.DeviceMotion(a);e.start();var f=new c(l.motion);f.then(function(){e._accelerationXAvailable=l.motion.data.acceleration&&l.motion.data.acceleration.x,e._accelerationYAvailable=l.motion.data.acceleration&&l.motion.data.acceleration.y,e._accelerationZAvailable=l.motion.data.acceleration&&l.motion.data.acceleration.z,e._accelerationIncludingGravityXAvailable=l.motion.data.accelerationIncludingGravity&&l.motion.data.accelerationIncludingGravity.x,e._accelerationIncludingGravityYAvailable=l.motion.data.accelerationIncludingGravity&&l.motion.data.accelerationIncludingGravity.y,e._accelerationIncludingGravityZAvailable=l.motion.data.accelerationIncludingGravity&&l.motion.data.accelerationIncludingGravity.z,e._rotationRateAlphaAvailable=l.motion.data.rotationRate&&l.motion.data.rotationRate.alpha,e._rotationRateBetaAvailable=l.motion.data.rotationRate&&l.motion.data.rotationRate.beta,e._rotationRateGammaAvailable=l.motion.data.rotationRate&&l.motion.data.rotationRate.gamma,b(e)})["catch"](function(){e.stop(),d("DeviceMotion is not supported")})});return b},t.Quaternion=function(a,c,d,e){var f;this.set=function(a,b,c,d){this.x=a||0,this.y=b||0,this.z=c||0,this.w=d||1},this.copy=function(a){this.x=a.x,this.y=a.y,this.z=a.z,this.w=a.w},this.setFromEuler=function(){var a,b,c,d,e,f,g,h,i,k,l,m;return function(n){return n=n||{},c=(n.alpha||0)*j,a=(n.beta||0)*j,b=(n.gamma||0)*j,f=c/2,d=a/2,e=b/2,g=Math.cos(d),h=Math.cos(e),i=Math.cos(f),k=Math.sin(d),l=Math.sin(e),m=Math.sin(f),this.set(k*h*i-g*l*m,g*l*i+k*h*m,g*h*m+k*l*i,g*h*i-k*l*m),this.normalize(),this}}(),this.setFromRotationMatrix=function(){var a;return function(c){return a=c.elements,this.set(.5*Math.sqrt(1+a[0]-a[4]-a[8])*b(a[7]-a[5]),.5*Math.sqrt(1-a[0]+a[4]-a[8])*b(a[2]-a[6]),.5*Math.sqrt(1-a[0]-a[4]+a[8])*b(a[3]-a[1]),.5*Math.sqrt(1+a[0]+a[4]+a[8])),this}}(),this.multiply=function(a){return f=t.Quaternion.prototype.multiplyQuaternions(this,a),this.copy(f),this},this.rotateX=function(a){return f=t.Quaternion.prototype.rotateByAxisAngle(this,[1,0,0],a),this.copy(f),this},this.rotateY=function(a){return f=t.Quaternion.prototype.rotateByAxisAngle(this,[0,1,0],a),this.copy(f),this},this.rotateZ=function(a){return f=t.Quaternion.prototype.rotateByAxisAngle(this,[0,0,1],a),this.copy(f),this},this.normalize=function(){return t.Quaternion.prototype.normalize(this)},this.set(a,c,d,e)},t.Quaternion.prototype={constructor:t.Quaternion,multiplyQuaternions:function(){var a=new t.Quaternion;return function(b,c){var d=b.x,e=b.y,f=b.z,g=b.w,h=c.x,i=c.y,j=c.z,k=c.w;return a.set(d*k+g*h+e*j-f*i,e*k+g*i+f*h-d*j,f*k+g*j+d*i-e*h,g*k-d*h-e*i-f*j),a}}(),normalize:function(a){var b=Math.sqrt(a.x*a.x+a.y*a.y+a.z*a.z+a.w*a.w);return 0===b?(a.x=0,a.y=0,a.z=0,a.w=1):(b=1/b,a.x*=b,a.y*=b,a.z*=b,a.w*=b),a},rotateByAxisAngle:function(){var a,b,c=new t.Quaternion,d=new t.Quaternion;return function(e,f,g){return a=(g||0)/2,b=Math.sin(a),d.set((f[0]||0)*b,(f[1]||0)*b,(f[2]||0)*b,Math.cos(a)),c=t.Quaternion.prototype.multiplyQuaternions(e,d),t.Quaternion.prototype.normalize(c)}}()},t.RotationMatrix=function(a,b,c,d,e,f,g,h,i){var k;this.elements=new Float32Array(9),this.identity=function(){return this.set(1,0,0,0,1,0,0,0,1),this},this.set=function(a,b,c,d,e,f,g,h,i){this.elements[0]=a||1,this.elements[1]=b||0,this.elements[2]=c||0,this.elements[3]=d||0,this.elements[4]=e||1,this.elements[5]=f||0,this.elements[6]=g||0,this.elements[7]=h||0,this.elements[8]=i||1},this.copy=function(a){this.elements[0]=a.elements[0],this.elements[1]=a.elements[1],this.elements[2]=a.elements[2],this.elements[3]=a.elements[3],this.elements[4]=a.elements[4],this.elements[5]=a.elements[5],this.elements[6]=a.elements[6],this.elements[7]=a.elements[7],this.elements[8]=a.elements[8]},this.setFromEuler=function(){var a,b,c,d,e,f,g,h,i;return function(k){return k=k||{},c=(k.alpha||0)*j,a=(k.beta||0)*j,b=(k.gamma||0)*j,d=Math.cos(a),e=Math.cos(b),f=Math.cos(c),g=Math.sin(a),h=Math.sin(b),i=Math.sin(c),this.set(f*e-i*g*h,-d*i,e*i*g+f*h,e*i+f*g*h,f*d,i*h-f*e*g,-d*h,g,d*e),this.normalize(),this}}(),this.setFromQuaternion=function(){var a,b,c,d;return function(e){return a=e.w*e.w,b=e.x*e.x,c=e.y*e.y,d=e.z*e.z,this.set(a+b-c-d,2*(e.x*e.y-e.w*e.z),2*(e.x*e.z+e.w*e.y),2*(e.x*e.y+e.w*e.z),a-b+c-d,2*(e.y*e.z-e.w*e.x),2*(e.x*e.z-e.w*e.y),2*(e.y*e.z+e.w*e.x),a-b-c+d),this}}(),this.multiply=function(a){return k=t.RotationMatrix.prototype.multiplyMatrices(this,a),this.copy(k),this},this.rotateX=function(a){return k=t.RotationMatrix.prototype.rotateByAxisAngle(this,[1,0,0],a),this.copy(k),this},this.rotateY=function(a){return k=t.RotationMatrix.prototype.rotateByAxisAngle(this,[0,1,0],a),this.copy(k),this},this.rotateZ=function(a){return k=t.RotationMatrix.prototype.rotateByAxisAngle(this,[0,0,1],a),this.copy(k),this},this.normalize=function(){return t.RotationMatrix.prototype.normalize(this)},this.set(a,b,c,d,e,f,g,h,i)},t.RotationMatrix.prototype={constructor:t.RotationMatrix,multiplyMatrices:function(){var a,b,c=new t.RotationMatrix;return function(d,e){return a=d.elements,b=e.elements,c.set(a[0]*b[0]+a[1]*b[3]+a[2]*b[6],a[0]*b[1]+a[1]*b[4]+a[2]*b[7],a[0]*b[2]+a[1]*b[5]+a[2]*b[8],a[3]*b[0]+a[4]*b[3]+a[5]*b[6],a[3]*b[1]+a[4]*b[4]+a[5]*b[7],a[3]*b[2]+a[4]*b[5]+a[5]*b[8],a[6]*b[0]+a[7]*b[3]+a[8]*b[6],a[6]*b[1]+a[7]*b[4]+a[8]*b[7],a[6]*b[2]+a[7]*b[5]+a[8]*b[8]),c}}(),normalize:function(a){var b=a.elements,c=b[0]*b[4]*b[8]-b[0]*b[5]*b[7]-b[1]*b[3]*b[8]+b[1]*b[5]*b[6]+b[2]*b[3]*b[7]-b[2]*b[4]*b[6];return b[0]/=c,b[1]/=c,b[2]/=c,b[3]/=c,b[4]/=c,b[5]/=c,b[6]/=c,b[7]/=c,b[8]/=c,a.elements=b,a},rotateByAxisAngle:function(){var a,b,c=new t.RotationMatrix,d=new t.RotationMatrix,e=!1;return function(f,g,h){return d.identity(),e=!1,a=Math.sin(h),b=Math.cos(h),1===g[0]&&0===g[1]&&0===g[2]?(e=!0,d.elements[4]=b,d.elements[5]=-a,d.elements[7]=a,d.elements[8]=b):1===g[1]&&0===g[0]&&0===g[2]?(e=!0,d.elements[0]=b,d.elements[2]=a,d.elements[6]=-a,d.elements[8]=b):1===g[2]&&0===g[0]&&0===g[1]&&(e=!0,d.elements[0]=b,d.elements[1]=-a,d.elements[3]=a,d.elements[4]=b),e?(c=t.RotationMatrix.prototype.multiplyMatrices(f,d),c=t.RotationMatrix.prototype.normalize(c)):c=f,c}}()},t.Euler=function(a,b,c){this.set=function(a,b,c){this.alpha=a||0,this.beta=b||0,this.gamma=c||0},this.copy=function(a){this.alpha=a.alpha,this.beta=a.beta,this.gamma=a.gamma},this.setFromRotationMatrix=function(){var a,b,c,d;return function(e){a=e.elements,a[8]>0?(b=Math.atan2(-a[1],a[4]),c=Math.asin(a[7]),d=Math.atan2(-a[6],a[8])):a[8]<0?(b=Math.atan2(a[1],-a[4]),c=-Math.asin(a[7]),c+=c>=0?-g:g,d=Math.atan2(a[6],-a[8])):a[6]>0?(b=Math.atan2(-a[1],a[4]),c=Math.asin(a[7]),d=-h):a[6]<0?(b=Math.atan2(a[1],-a[4]),c=-Math.asin(a[7]),c+=c>=0?-g:g,d=-h):(b=Math.atan2(a[3],a[0]),c=a[7]>0?h:-h,d=0),0>b&&(b+=i),b*=k,c*=k,d*=k,this.set(b,c,d)}}(),this.setFromQuaternion=function(){var a,b,c;return function(d){var e=d.w*d.w,f=d.x*d.x,j=d.y*d.y,l=d.z*d.z,m=e+f+j+l,n=d.w*d.x+d.y*d.z,o=1e-6;if(n>(.5-o)*m)a=2*Math.atan2(d.y,d.w),b=h,c=0;else if((-.5+o)*m>n)a=-2*Math.atan2(d.y,d.w),b=-h,c=0;else{var p=e-f+j-l,q=2*(d.w*d.z-d.x*d.y),r=e-f-j+l,s=2*(d.w*d.y-d.x*d.z);r>0?(a=Math.atan2(q,p),b=Math.asin(2*n/m),c=Math.atan2(s,r)):(a=Math.atan2(-q,-p),b=-Math.asin(2*n/m),b+=0>b?g:-g,c=Math.atan2(-s,-r))}0>a&&(a+=i),a*=k,b*=k,c*=k,this.set(a,b,c)}}(),this.rotateX=function(a){return t.Euler.prototype.rotateByAxisAngle(this,[1,0,0],a),this},this.rotateY=function(a){return t.Euler.prototype.rotateByAxisAngle(this,[0,1,0],a),this},this.rotateZ=function(a){return t.Euler.prototype.rotateByAxisAngle(this,[0,0,1],a),this},this.set(a,b,c)},t.Euler.prototype={constructor:t.Euler,rotateByAxisAngle:function(){var a=new t.RotationMatrix;return function(b,c,d){return a.setFromEuler(b),a=t.RotationMatrix.prototype.rotateByAxisAngle(a,c,d),b.setFromRotationMatrix(a),b}}()},t.DeviceOrientation=function(b){this.options=b||{};var c=0,d=200,e=0,f=10;if(this.alphaOffsetScreen=0,this.alphaOffsetDevice=void 0,"game"===this.options.type){var g=function(b){return null!==b.alpha&&(this.alphaOffsetDevice=new t.Euler(b.alpha,0,0),this.alphaOffsetDevice.rotateZ(-o),++e>=f)?void a.removeEventListener("deviceorientation",g,!1):void(++c>=d&&a.removeEventListener("deviceorientation",g,!1))}.bind(this);a.addEventListener("deviceorientation",g,!1)}else if("world"===this.options.type){var h=function(b){return b.absolute!==!0&&void 0!==b.webkitCompassAccuracy&&null!==b.webkitCompassAccuracy&&+b.webkitCompassAccuracy>=0&&+b.webkitCompassAccuracy<50&&(this.alphaOffsetDevice=new t.Euler(b.webkitCompassHeading,0,0),this.alphaOffsetDevice.rotateZ(o),this.alphaOffsetScreen=o,++e>=f)?void a.removeEventListener("deviceorientation",h,!1):void(++c>=d&&a.removeEventListener("deviceorientation",h,!1))}.bind(this);a.addEventListener("deviceorientation",h,!1)}},t.DeviceOrientation.prototype={constructor:t.DeviceOrientation,start:function(b){b&&"[object Function]"==Object.prototype.toString.call(b)&&l.orientation.callbacks.push(b),m||(n?a.screen.orientation.addEventListener("change",d,!1):a.addEventListener("orientationchange",d,!1)),l.orientation.active||(a.addEventListener("deviceorientation",e,!1),l.orientation.active=!0)},stop:function(){l.orientation.active&&(a.removeEventListener("deviceorientation",e,!1),l.orientation.active=!1)},listen:function(a){this.start(a)},getFixedFrameQuaternion:function(){var a=new t.Euler,b=new t.RotationMatrix,c=new t.Quaternion;return function(){var d=l.orientation.data||{alpha:0,beta:0,gamma:0},e=d.alpha;return this.alphaOffsetDevice&&(b.setFromEuler(this.alphaOffsetDevice),b.rotateZ(-this.alphaOffsetScreen),a.setFromRotationMatrix(b),a.alpha<0&&(a.alpha+=360),a.alpha%=360,e-=a.alpha),a.set(e,d.beta,d.gamma),c.setFromEuler(a),c}}(),getScreenAdjustedQuaternion:function(){var a;return function(){return a=this.getFixedFrameQuaternion(),a.rotateZ(-o),a}}(),getFixedFrameMatrix:function(){var a=new t.Euler,b=new t.RotationMatrix;return function(){var c=l.orientation.data||{alpha:0,beta:0,gamma:0},d=c.alpha;return this.alphaOffsetDevice&&(b.setFromEuler(this.alphaOffsetDevice),b.rotateZ(-this.alphaOffsetScreen),a.setFromRotationMatrix(b),a.alpha<0&&(a.alpha+=360),a.alpha%=360,d-=a.alpha),a.set(d,c.beta,c.gamma),b.setFromEuler(a),b}}(),getScreenAdjustedMatrix:function(){var a;return function(){return a=this.getFixedFrameMatrix(),a.rotateZ(-o),a}}(),getFixedFrameEuler:function(){var a,b=new t.Euler;return function(){return a=this.getFixedFrameMatrix(),b.setFromRotationMatrix(a),b}}(),getScreenAdjustedEuler:function(){var a,b=new t.Euler;return function(){return a=this.getScreenAdjustedMatrix(),b.setFromRotationMatrix(a),b}}(),isAbsolute:function(){return l.orientation.data&&l.orientation.data.absolute===!0?!0:!1},getLastRawEventData:function(){return l.orientation.data||{}},_alphaAvailable:!1,_betaAvailable:!1,_gammaAvailable:!1,isAvailable:function(a){switch(a){case this.ALPHA:return this._alphaAvailable;case this.BETA:return this._betaAvailable;case this.GAMMA:return this._gammaAvailable}},ALPHA:"alpha",BETA:"beta",GAMMA:"gamma"},t.DeviceMotion=function(a){this.options=a||{}},t.DeviceMotion.prototype={constructor:t.DeviceMotion,start:function(b){b&&"[object Function]"==Object.prototype.toString.call(b)&&l.motion.callbacks.push(b),m||(n?a.screen.orientation.addEventListener("change",d,!1):a.addEventListener("orientationchange",d,!1)),l.motion.active||(a.addEventListener("devicemotion",f,!1),l.motion.active=!0)},stop:function(){l.motion.active&&(a.removeEventListener("devicemotion",f,!1),l.motion.active=!1)},listen:function(a){this.start(a)},getScreenAdjustedAcceleration:function(){var a=l.motion.data&&l.motion.data.acceleration?l.motion.data.acceleration:{x:0,y:0,z:0},b={};switch(o){case p:b.x=-a.y,b.y=a.x;break;case q:b.x=-a.x,b.y=-a.y;break;case r:case s:b.x=a.y,b.y=-a.x;break;default:b.x=a.x,b.y=a.y}return b.z=a.z,b},getScreenAdjustedAccelerationIncludingGravity:function(){var a=l.motion.data&&l.motion.data.accelerationIncludingGravity?l.motion.data.accelerationIncludingGravity:{x:0,y:0,z:0},b={};switch(o){case p:b.x=-a.y,b.y=a.x;break;case q:b.x=-a.x,b.y=-a.y;break;case r:case s:b.x=a.y,b.y=-a.x;break;default:b.x=a.x,b.y=a.y}return b.z=a.z,b},getScreenAdjustedRotationRate:function(){var a=l.motion.data&&l.motion.data.rotationRate?l.motion.data.rotationRate:{alpha:0,beta:0,gamma:0},b={};switch(o){case p:b.beta=-a.gamma,b.gamma=a.beta;break;case q:b.beta=-a.beta,b.gamma=-a.gamma;break;case r:case s:b.beta=a.gamma,b.gamma=-a.beta;break;default:b.beta=a.beta,b.gamma=a.gamma}return b.alpha=a.alpha,b},getLastRawEventData:function(){return l.motion.data||{}},_accelerationXAvailable:!1,_accelerationYAvailable:!1,_accelerationZAvailable:!1,_accelerationIncludingGravityXAvailable:!1,_accelerationIncludingGravityYAvailable:!1,_accelerationIncludingGravityZAvailable:!1,_rotationRateAlphaAvailable:!1,_rotationRateBetaAvailable:!1,_rotationRateGammaAvailable:!1,isAvailable:function(a){switch(a){case this.ACCELERATION_X:return this._accelerationXAvailable;case this.ACCELERATION_Y:return this._accelerationYAvailable;case this.ACCELERATION_Z:return this._accelerationZAvailable;case this.ACCELERATION_INCLUDING_GRAVITY_X:return this._accelerationIncludingGravityXAvailable;case this.ACCELERATION_INCLUDING_GRAVITY_Y:return this._accelerationIncludingGravityYAvailable;case this.ACCELERATION_INCLUDING_GRAVITY_Z:return this._accelerationIncludingGravityZAvailable;case this.ROTATION_RATE_ALPHA:return this._rotationRateAlphaAvailable;case this.ROTATION_RATE_BETA:return this._rotationRateBetaAvailable;case this.ROTATION_RATE_GAMMA:return this._rotationRateGammaAvailable}},ACCELERATION_X:"accelerationX",ACCELERATION_Y:"accelerationY",ACCELERATION_Z:"accelerationZ",ACCELERATION_INCLUDING_GRAVITY_X:"accelerationIncludingGravityX",ACCELERATION_INCLUDING_GRAVITY_Y:"accelerationIncludingGravityY",ACCELERATION_INCLUDING_GRAVITY_Z:"accelerationIncludingGravityZ",ROTATION_RATE_ALPHA:"rotationRateAlpha",ROTATION_RATE_BETA:"rotationRateBeta",ROTATION_RATE_GAMMA:"rotationRateGamma"},a.FULLTILT=t}}(window); 3 | 4 | /* gyronorm.js v2.0.6 - https://github.com/dorukeker/gyronorm.git*/ 5 | !function(a,b){var c={GyroNorm:b()};"function"==typeof define&&define.amd?define(function(){return c}):"object"==typeof module&&module.exports?module.exports=c:a.GyroNorm=c.GyroNorm}(this,function(){function a(a){return Math.round(a*Math.pow(10,t))/Math.pow(10,t)}function b(){var b={};b=v?o.getScreenAdjustedEuler():o.getFixedFrameEuler();var c=p.getScreenAdjustedAcceleration(),e=p.getScreenAdjustedAccelerationIncludingGravity(),f=p.getScreenAdjustedRotationRate(),g=0;s===d?(g=b.alpha-k,g=0>g?360-Math.abs(g):g):g=b.alpha;var h={"do":{alpha:a(g),beta:a(b.beta),gamma:a(b.gamma),absolute:o.isAbsolute()},dm:{x:a(c.x),y:a(c.y),z:a(c.z),gx:a(e.x),gy:a(e.y),gz:a(e.z),alpha:a(f.alpha),beta:a(f.beta),gamma:a(f.gamma)}};return r&&(h.dm.gx*=l,h.dm.gy*=l,h.dm.gz*=l),h}function c(a){u&&("string"==typeof a&&(a={message:a,code:0}),u(a))}var d="game",e="world",f="deviceorientation",g="acceleration",h="accelerationinludinggravity",i="rotationrate",j=null,k=0,l=0,m=!1,n=!1,o=null,p=null,q=50,r=!0,s=d,t=2,u=null,v=!1,w=function(a){};return w.GAME=d,w.WORLD=e,w.DEVICE_ORIENTATION=f,w.ACCELERATION=g,w.ACCELERATION_INCLUDING_GRAVITY=h,w.ROTATION_RATE=i,w.prototype.init=function(a){a&&a.frequency&&(q=a.frequency),a&&a.gravityNormalized&&(r=a.gravityNormalized),a&&a.orientationBase&&(s=a.orientationBase),a&&"number"==typeof a.decimalCount&&a.decimalCount>=0&&(t=parseInt(a.decimalCount)),a&&a.logger&&(u=a.logger),a&&a.screenAdjusted&&(v=a.screenAdjusted);var b=new FULLTILT.getDeviceOrientation({type:s}).then(function(a){o=a}),c=(new FULLTILT.getDeviceMotion).then(function(a){p=a,l=p.getScreenAdjustedAccelerationIncludingGravity().z>0?-1:1});return Promise.all([b,c]).then(function(){n=!0})},w.prototype.end=function(){try{n=!1,this.stop(),p.stop(),o.stop()}catch(a){c(a)}},w.prototype.start=function(a){return n?(j=setInterval(function(){a(b())},q),void(m=!0)):void c({message:'GyroNorm is not initialized yet. First call the "init()" function.',code:1})},w.prototype.stop=function(){j&&(clearInterval(j),m=!1)},w.prototype.normalizeGravity=function(a){r=a?!0:!1},w.prototype.setHeadDirection=function(){return v||s===e?!1:(k=o.getFixedFrameEuler().alpha,!0)},w.prototype.startLogging=function(a){a&&(u=a)},w.prototype.stopLogging=function(){u=null},w.prototype.isAvailable=function(a){var b=o.getScreenAdjustedEuler(),c=p.getScreenAdjustedAcceleration(),d=p.getScreenAdjustedAccelerationIncludingGravity(),e=p.getScreenAdjustedRotationRate();switch(a){case f:return b.alpha&&null!==b.alpha&&b.beta&&null!==b.beta&&b.gamma&&null!==b.gamma;case g:return c&&c.x&&c.y&&c.z;case h:return d&&d.x&&d.y&&d.z;case i:return e&&e.alpha&&e.beta&&e.gamma;default:return{deviceOrientationAvailable:b.alpha&&null!==b.alpha&&b.beta&&null!==b.beta&&b.gamma&&null!==b.gamma,accelerationAvailable:c&&c.x&&c.y&&c.z,accelerationIncludingGravityAvailable:d&&d.x&&d.y&&d.z,rotationRateAvailable:e&&e.alpha&&e.beta&&e.gamma}}},w.prototype.isRunning=function(){return m},w}); -------------------------------------------------------------------------------- /frontend/public/js/jquery.qrcode.js: -------------------------------------------------------------------------------- 1 | (function( $ ){ 2 | $.fn.qrcode = function(options) { 3 | // if options is string, 4 | if( typeof options === 'string' ){ 5 | options = { text: options }; 6 | } 7 | 8 | // set default values 9 | // typeNumber < 1 for automatic calculation 10 | options = $.extend( {}, { 11 | render : "canvas", 12 | width : 256, 13 | height : 256, 14 | typeNumber : -1, 15 | correctLevel : QRErrorCorrectLevel.H, 16 | background : "#ffffff", 17 | foreground : "#000000" 18 | }, options); 19 | 20 | var createCanvas = function(){ 21 | // create the qrcode itself 22 | var qrcode = new QRCode(options.typeNumber, options.correctLevel); 23 | qrcode.addData(options.text); 24 | qrcode.make(); 25 | 26 | // create canvas element 27 | var canvas = document.createElement('canvas'); 28 | canvas.width = options.width; 29 | canvas.height = options.height; 30 | var ctx = canvas.getContext('2d'); 31 | 32 | // compute tileW/tileH based on options.width/options.height 33 | var tileW = options.width / qrcode.getModuleCount(); 34 | var tileH = options.height / qrcode.getModuleCount(); 35 | 36 | // draw in the canvas 37 | for( var row = 0; row < qrcode.getModuleCount(); row++ ){ 38 | for( var col = 0; col < qrcode.getModuleCount(); col++ ){ 39 | ctx.fillStyle = qrcode.isDark(row, col) ? options.foreground : options.background; 40 | var w = (Math.ceil((col+1)*tileW) - Math.floor(col*tileW)); 41 | var h = (Math.ceil((row+1)*tileW) - Math.floor(row*tileW)); 42 | ctx.fillRect(Math.round(col*tileW),Math.round(row*tileH), w, h); 43 | } 44 | } 45 | // return just built canvas 46 | return canvas; 47 | } 48 | 49 | // from Jon-Carlos Rivera (https://github.com/imbcmdth) 50 | var createTable = function(){ 51 | // create the qrcode itself 52 | var qrcode = new QRCode(options.typeNumber, options.correctLevel); 53 | qrcode.addData(options.text); 54 | qrcode.make(); 55 | 56 | // create table element 57 | var $table = $('
') 58 | .css("width", options.width+"px") 59 | .css("height", options.height+"px") 60 | .css("border", "0px") 61 | .css("border-collapse", "collapse") 62 | .css('background-color', options.background); 63 | 64 | // compute tileS percentage 65 | var tileW = options.width / qrcode.getModuleCount(); 66 | var tileH = options.height / qrcode.getModuleCount(); 67 | 68 | // draw in the table 69 | for(var row = 0; row < qrcode.getModuleCount(); row++ ){ 70 | var $row = $('').css('height', tileH+"px").appendTo($table); 71 | 72 | for(var col = 0; col < qrcode.getModuleCount(); col++ ){ 73 | $('') 74 | .css('width', tileW+"px") 75 | .css('background-color', qrcode.isDark(row, col) ? options.foreground : options.background) 76 | .appendTo($row); 77 | } 78 | } 79 | // return just built canvas 80 | return $table; 81 | } 82 | 83 | 84 | return this.each(function(){ 85 | var element = options.render == "canvas" ? createCanvas() : createTable(); 86 | jQuery(element).appendTo(this); 87 | }); 88 | }; 89 | })( jQuery ); 90 | -------------------------------------------------------------------------------- /frontend/public/js/player.js: -------------------------------------------------------------------------------- 1 | P.player = (function () { 2 | 'use strict'; 3 | 4 | var aspectX; 5 | var aspectY; 6 | var resolutionX = 480; 7 | var resolutionY = 360; 8 | 9 | var stage; 10 | var frameId = null; 11 | var isFullScreen = false; 12 | 13 | var progressBar = document.querySelector('.progress-bar'); 14 | var player = document.querySelector('.player'); 15 | var projectLink = document.querySelector('.project-link'); 16 | var bugLink = document.querySelector('#bug-link'); 17 | 18 | var controls = document.querySelector('.controls'); 19 | var flag = document.querySelector('.flag'); 20 | var turbo = document.querySelector('.turbo'); 21 | var pause = document.querySelector('.pause'); 22 | var stop = document.querySelector('.stop'); 23 | var fullScreen = document.querySelector('.full-screen'); 24 | 25 | var error = document.querySelector('.internal-error'); 26 | var errorBugLink = document.querySelector('#error-bug-link'); 27 | 28 | var webGLError = document.querySelector('.webgl-error'); 29 | var legacyLink = document.querySelector('#legacy-link'); 30 | 31 | var flagTouchTimeout; 32 | 33 | function setResolution(resX, resY) { 34 | resolutionX = resX; 35 | resolutionY = resY; 36 | player.style.width = resolutionX + 'px'; 37 | player.style.height = resolutionY + 'px' 38 | } 39 | 40 | function flagTouchStart() { 41 | flagTouchTimeout = setTimeout(function () { 42 | turboClick(); 43 | flagTouchTimeout = true; 44 | }, 500); 45 | } 46 | function turboClick() { 47 | stage.isTurbo = !stage.isTurbo; 48 | flag.title = stage.isTurbo ? 'Turbo mode enabled. Shift+click to disable.' : 'Shift+click to enable turbo mode.'; 49 | turbo.style.display = stage.isTurbo ? 'block' : 'none'; 50 | } 51 | function flagClick(e) { 52 | if (!stage) return; 53 | if (flagTouchTimeout === true) return; 54 | if (flagTouchTimeout) { 55 | clearTimeout(flagTouchTimeout); 56 | } 57 | if (e.shiftKey) { 58 | turboClick(); 59 | } else { 60 | stage.start(); 61 | pause.className = 'pause'; 62 | stage.stopAll(); 63 | stage.triggerGreenFlag(); 64 | } 65 | stage.focus(); 66 | e.preventDefault(); 67 | } 68 | 69 | function pauseClick(e) { 70 | if (!stage) return; 71 | if (stage.isRunning) { 72 | stage.pause(); 73 | pause.className = 'play'; 74 | } else { 75 | stage.start(); 76 | pause.className = 'pause'; 77 | } 78 | stage.focus(); 79 | e.preventDefault(); 80 | } 81 | 82 | function stopClick(e) { 83 | 84 | console.log('stop'); 85 | 86 | if (!stage) return; 87 | stage.start(); 88 | pause.className = 'pause'; 89 | stage.stopAll(); 90 | stage.focus(); 91 | e.preventDefault(); 92 | } 93 | 94 | function fullScreenClick(e) { 95 | if (e) e.preventDefault(); 96 | if (!stage) return; 97 | document.documentElement.classList.toggle('fs'); 98 | isFullScreen = !isFullScreen; 99 | if (!e || !e.shiftKey) { 100 | if (isFullScreen) { 101 | var el = document.documentElement; 102 | if (el.requestFullScreenWithKeys) { 103 | el.requestFullScreenWithKeys(); 104 | } else if (el.webkitRequestFullScreen) { 105 | el.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); 106 | } 107 | else if (el.mozRequestFullScreen) { 108 | el.mozRequestFullScreen(); 109 | } 110 | else { 111 | console.warn("No full screen available."); 112 | } 113 | } else { 114 | if (document.exitFullscreen) { 115 | document.exitFullscreen(); 116 | } else if (document.mozCancelFullScreen) { 117 | document.mozCancelFullScreen(); 118 | } else if (document.webkitCancelFullScreen) { 119 | document.webkitCancelFullScreen(); 120 | } 121 | } 122 | } 123 | if (!isFullScreen) { 124 | document.body.style.width = resolutionX + 'px'; 125 | document.body.style.height = 126 | document.body.style.marginLeft = 127 | document.body.style.marginTop = ''; 128 | } 129 | updateFullScreen(); 130 | if (!stage.isRunning) { 131 | stage.draw(); 132 | } 133 | stage.focus(); 134 | } 135 | 136 | function exitFullScreen(e) { 137 | if (isFullScreen && e.keyCode === 27) { 138 | fullScreenClick(e); 139 | } 140 | } 141 | 142 | function updateFullScreen() { 143 | if (!stage) return; 144 | if (isFullScreen) { 145 | window.scrollTo(0, 0); 146 | var padding = 8; 147 | var w = window.innerWidth - padding * 2; 148 | var h = window.innerHeight - padding - controls.offsetHeight; 149 | w = Math.min(w, h * resolutionX / resolutionY); 150 | h = w * resolutionY / resolutionX + controls.offsetHeight; 151 | document.body.style.width = w + 'px'; 152 | document.body.style.height = h + 'px'; 153 | document.body.style.marginLeft = (window.innerWidth - w) / 2 + 'px'; 154 | document.body.style.marginTop = (window.innerHeight - h + padding) / 2 + 'px'; 155 | stage.setZoom(w / 480, w * resolutionY / resolutionX / 360); 156 | } else { 157 | stage.setZoom(resolutionX ? resolutionX / 480 : 1, resolutionY ? resolutionY / 360 : 1); 158 | } 159 | } 160 | 161 | function preventDefault(e) { 162 | e.preventDefault(); 163 | } 164 | 165 | window.addEventListener('resize', updateFullScreen); 166 | if (P.hasTouchEvents) { 167 | flag.addEventListener('touchstart', flagTouchStart); 168 | flag.addEventListener('touchend', flagClick); 169 | pause.addEventListener('touchend', pauseClick); 170 | stop.addEventListener('touchend', stopClick); 171 | fullScreen.addEventListener('touchend', fullScreenClick); 172 | 173 | flag.addEventListener('touchstart', preventDefault); 174 | pause.addEventListener('touchstart', preventDefault); 175 | stop.addEventListener('touchstart', preventDefault); 176 | fullScreen.addEventListener('touchstart', preventDefault); 177 | 178 | document.addEventListener('touchmove', function (e) { 179 | if (isFullScreen) e.preventDefault(); 180 | }); 181 | } 182 | 183 | flag.addEventListener('click', flagClick); 184 | pause.addEventListener('click', pauseClick); 185 | stop.addEventListener('click', stopClick); 186 | fullScreen.addEventListener('click', fullScreenClick); 187 | 188 | 189 | document.addEventListener("fullscreenchange", function () { 190 | if (isFullScreen !== document.fullscreen) fullScreenClick(); 191 | }); 192 | document.addEventListener("mozfullscreenchange", function () { 193 | if (isFullScreen !== document.mozFullScreen) fullScreenClick(); 194 | }); 195 | document.addEventListener("webkitfullscreenchange", function () { 196 | if (isFullScreen !== document.webkitIsFullScreen) fullScreenClick(); 197 | }); 198 | 199 | async function load(id, cb, titleCallback) { 200 | 201 | 202 | socket.emit('getProjectData', id, async function (projectData) { 203 | 204 | 205 | 206 | // console.log(projectData) 207 | 208 | 209 | P.player.projectId = id; 210 | P.player.projectURL = id ? 'https://scratch.mit.edu/projects/' + id + '?token=' + projectData.project_token : ''; 211 | 212 | if (stage) { 213 | stage.stopAll(); 214 | stage.pause(); 215 | } 216 | while (player.firstChild) player.removeChild(player.lastChild); 217 | turbo.style.display = 'none'; 218 | error.style.display = 'none'; 219 | webGLError.style.display = 'none'; 220 | pause.className = 'pause'; 221 | progressBar.style.display = 'none'; 222 | 223 | 224 | 225 | 226 | if (await isSB2(projectData)) { 227 | socket.emit("logRequest", { "id": id, "version": "2" }) 228 | 229 | showProgress(P.IO.loadScratchr2Project(id, projectData.project_token), cb); 230 | P.IO.loadScratchr2ProjectTitle(id, function (title) { 231 | if (titleCallback) titleCallback(P.player.projectTitle = title); 232 | }); 233 | } else { 234 | socket.emit("logRequest", { "id": id, "version": "3" }) 235 | socket.emit("sendSB3ID", id); 236 | document.getElementById("sb3loading").innerHTML = "loading...." 237 | if (titleCallback) setTimeout(function () { 238 | titleCallback(''); 239 | }); 240 | } 241 | }); 242 | } 243 | 244 | function showError(e) { 245 | error.style.display = 'block'; 246 | errorBugLink.href = 'https://github.com/nathan/phosphorus/issues/new?title=' + encodeURIComponent(P.player.projectTitle || P.player.projectURL) + '&body=' + encodeURIComponent('\n\n\n' + P.player.projectURL + '\nhttps://phosphorus.github.io/#' + P.player.projectId + '\n' + navigator.userAgent + (e.stack ? '\n\n```\n' + e.stack + '\n```' : '')); 247 | console.error(e); 248 | } 249 | 250 | function showWebGLError(e) { 251 | webGLError.style.display = 'block'; 252 | if (document.querySelector('#player-area')) 253 | document.querySelector('#player-area').style.height = 'auto'; 254 | legacyLink.href = 'https://sulfurous.aau.at/legacy/#' + P.player.projectId; 255 | console.error(e); 256 | } 257 | 258 | function showProgress(request, loadCallback) { 259 | progressBar.style.display = 'none'; 260 | setTimeout(function () { 261 | progressBar.style.width = '10%'; 262 | progressBar.className = 'progress-bar'; 263 | progressBar.style.opacity = 1; 264 | progressBar.style.display = 'block'; 265 | }); 266 | request.onload = function (s) { 267 | progressBar.style.width = '100%'; 268 | setTimeout(function () { 269 | progressBar.style.opacity = 0; 270 | setTimeout(function () { 271 | progressBar.style.display = 'none'; 272 | }, 300); 273 | }, 100); 274 | 275 | var zoomX = stage ? stage.zoomX : 1; 276 | var zoomY = stage ? stage.zoomY : 1; 277 | zoomX = resolutionX ? resolutionX / 480 : zoomX; 278 | zoomY = resolutionY ? resolutionY / 360 : zoomY; 279 | 280 | window.stage = stage = s; 281 | stage.start(); 282 | stage.setZoom(zoomX, zoomY); 283 | 284 | stage.root.addEventListener('keydown', exitFullScreen); 285 | stage.handleError = showError; 286 | 287 | player.appendChild(stage.root); 288 | stage.focus(); 289 | if (loadCallback) { 290 | loadCallback(stage); 291 | loadCallback = null; 292 | } 293 | }; 294 | request.onerror = function (e) { 295 | progressBar.style.width = '100%'; 296 | progressBar.className = 'progress-bar error'; 297 | console.error(e.stack); 298 | }; 299 | request.onprogress = function (e) { 300 | progressBar.style.width = (10 + e.loaded / e.total * 90) + '%'; 301 | }; 302 | } 303 | 304 | P.showWebGLError = showWebGLError; 305 | 306 | return { 307 | load: load, 308 | showProgress: showProgress, 309 | setResolution: setResolution, 310 | }; 311 | 312 | 313 | async function isSB2(projectData) { 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | return new Promise(async function (resolve, reject) { 322 | // console.log(projectData) 323 | 324 | 325 | const response = await fetch("https://projects.scratch.mit.edu/" + projectData.id + "?token=" + projectData.project_token); 326 | const jsonData = await response.json(); 327 | 328 | if(jsonData.targets == undefined){ 329 | resolve(true) 330 | }else{ 331 | resolve(false) 332 | } 333 | 334 | }) 335 | 336 | 337 | } 338 | 339 | }()); -------------------------------------------------------------------------------- /frontend/public/js/shaders.js: -------------------------------------------------------------------------------- 1 | Shader = {}; 2 | 3 | // Pen 4 | 5 | Shader.penVert = ` 6 | precision mediump float; 7 | 8 | attribute vec4 vertexData; 9 | attribute vec2 lineData; 10 | attribute vec4 colorData; 11 | 12 | varying vec4 fragColor; 13 | 14 | 15 | //vertexData: 16 | //[0] = x1 17 | //[1] = y1 18 | //[2] = x2 19 | //[3] = y2 20 | 21 | //lineData: 22 | //[0] = thickened vertex direction 23 | //[1] = thickened vertex distance 24 | 25 | //colorData: 26 | //[0] = red 27 | //[1] = green 28 | //[2] = blue 29 | //[3] = alpha 30 | 31 | 32 | 33 | void main(){ 34 | 35 | vec2 lineDir = normalize(vertexData.zw - vertexData.xy); 36 | 37 | mat2 rot; 38 | rot[0] = vec2(cos(lineData.x), sin(lineData.x)); 39 | rot[1] = vec2(-sin(lineData.x), cos(lineData.x)); 40 | 41 | lineDir *= rot * lineData.y; 42 | 43 | vec2 p = (vertexData.xy + lineDir); 44 | p.x /= 240.0; 45 | p.y /= 180.0; 46 | 47 | gl_Position = vec4(p, 0.0, 1.0); 48 | fragColor = colorData; 49 | } 50 | `; 51 | 52 | 53 | Shader.penFrag = ` 54 | precision mediump float; 55 | 56 | varying vec4 fragColor; 57 | 58 | void main(){ 59 | 60 | gl_FragColor = vec4(fragColor.xyz / 255.0, fragColor.w); 61 | } 62 | `; 63 | 64 | 65 | // DrawImage 66 | 67 | Shader.imgVert = ` 68 | attribute vec2 position; 69 | attribute vec2 texcoord; 70 | 71 | uniform mat4 u_matrix; 72 | 73 | varying vec2 fragTexcoord; 74 | 75 | void main(){ 76 | gl_Position = u_matrix * vec4(position, 0, 1); 77 | fragTexcoord = texcoord; 78 | } 79 | `; 80 | 81 | Shader.imgFrag = ` 82 | precision mediump float; 83 | 84 | varying vec2 fragTexcoord; 85 | 86 | uniform sampler2D u_texture; 87 | 88 | uniform vec2 texSize; 89 | 90 | uniform vec2 colorEffect; 91 | uniform mat4 colorMatrix; 92 | // colorEffect[0] = brightness 93 | // colorEffect[1] = ghost 94 | 95 | uniform vec4 texEffect; 96 | // texEffect[0] = fisheye 97 | // texEffect[1] = whirl 98 | // texEffect[2] = pixelate 99 | // texEffect[3] = mosaic 100 | 101 | void main(){ 102 | 103 | vec2 texCoord = fragTexcoord; 104 | 105 | vec2 m = vec2(0.5, 0.5); 106 | float d = distance(texCoord, m); 107 | 108 | 109 | //fisheye 110 | if(texEffect[0] != 0.0){ 111 | texCoord = m + normalize(texCoord - m) * pow(0.5 / d, texEffect[0]) / 2.0; 112 | } 113 | 114 | //whirl 115 | if(texEffect[1] != 0.0){ 116 | float d2 = max(0.5 - d, 0.0); 117 | float phi = atan(0.5 - texCoord.x, 0.5 - texCoord.y); 118 | texCoord = vec2(0.5 - sin(phi + texEffect[1] * d2) * d, 119 | 0.5 - cos(phi + texEffect[1] * d2) * d); 120 | } 121 | 122 | //pixelate 123 | if(texEffect[2] != 1.0){ 124 | texCoord = vec2(floor(texCoord.x * texSize.x / texEffect[2]) / texSize.x * texEffect[2], 125 | floor(texCoord.y * texSize.y / texEffect[2]) / texSize.y * texEffect[2]); 126 | } 127 | 128 | //mosaic 129 | if(texEffect[3] != 1.0){ 130 | texCoord = vec2(mod(texCoord.x * texEffect[3], 1.0), 131 | mod(texCoord.y * texEffect[3], 1.0)); 132 | } 133 | 134 | vec4 c = texture2D(u_texture, texCoord); 135 | 136 | gl_FragColor = vec4(c.xyz + colorEffect.x, c.w) * colorEffect.y * colorMatrix; 137 | } 138 | `; 139 | 140 | /* 141 | Shader.imgVert = ` 142 | attribute vec2 position; 143 | attribute vec2 texcoord; 144 | 145 | uniform mat4 u_matrix; 146 | 147 | varying vec2 fragTexcoord; 148 | 149 | void main(){ 150 | gl_Position = u_matrix * vec4(position, 0, 1); 151 | fragTexcoord = texcoord; 152 | } 153 | `; 154 | 155 | Shader.imgFrag = ` 156 | precision mediump float; 157 | 158 | varying vec2 fragTexcoord; 159 | 160 | uniform sampler2D u_texture; 161 | 162 | void main(){ 163 | gl_FragColor = texture2D(u_texture, fragTexcoord); 164 | //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 165 | } 166 | `; 167 | */ 168 | 169 | Shader.touchingVert = ` 170 | attribute vec2 position; 171 | attribute vec2 texcoord; 172 | 173 | uniform mat4 u_matrix; 174 | 175 | varying vec2 fragTexcoord; 176 | 177 | void main(){ 178 | gl_Position = u_matrix * vec4(position, 0, 1); 179 | fragTexcoord = texcoord; 180 | } 181 | `; 182 | 183 | Shader.touchingFrag = ` 184 | precision mediump float; 185 | 186 | varying vec2 fragTexcoord; 187 | 188 | uniform sampler2D u_texture; 189 | 190 | uniform vec4 tColor; 191 | 192 | uniform vec2 texSize; 193 | 194 | uniform vec2 colorEffect; 195 | uniform mat4 colorMatrix; 196 | // colorEffect[0] = brightness 197 | // colorEffect[1] = ghost 198 | 199 | uniform vec4 texEffect; 200 | // texEffect[0] = fisheye 201 | // texEffect[1] = whirl 202 | // texEffect[2] = pixelate 203 | // texEffect[3] = mosaic 204 | 205 | void main(){ 206 | vec2 texCoord = fragTexcoord; 207 | 208 | vec2 m = vec2(0.5, 0.5); 209 | float d = distance(texCoord, m); 210 | 211 | //fisheye 212 | if(texEffect[0] != 0.0){ 213 | texCoord = m + normalize(texCoord - m) * pow(0.5 / d, texEffect[0]) / 2.0; 214 | } 215 | 216 | //whirl 217 | if(texEffect[1] != 0.0){ 218 | float d2 = max(0.5 - d, 0.0); 219 | float phi = atan(0.5 - texCoord.x, 0.5 - texCoord.y); 220 | texCoord = vec2(0.5 - sin(phi + texEffect[1] * d2) * d, 221 | 0.5 - cos(phi + texEffect[1] * d2) * d); 222 | } 223 | 224 | //pixelate 225 | if(texEffect[2] != 1.0){ 226 | texCoord = vec2(floor(texCoord.x * texSize.x / texEffect[2]) / texSize.x * texEffect[2], 227 | floor(texCoord.y * texSize.y / texEffect[2]) / texSize.y * texEffect[2]); 228 | } 229 | 230 | //mosaic 231 | if(texEffect[3] != 1.0){ 232 | texCoord = vec2(mod(texCoord.x * texEffect[3], 1.0), 233 | mod(texCoord.y * texEffect[3], 1.0)); 234 | } 235 | 236 | vec4 c = texture2D(u_texture, texCoord); 237 | 238 | if(tColor.w == 1.0 && c.xyz == tColor.xyz && c.w != 0.0){ 239 | c = vec4(1.0, 1.0, 1.0, 1.0); 240 | } 241 | else if(tColor.w == 0.0 && c.w > 0.0){ 242 | c = vec4(1.0, 1.0, 1.0, 1.0); 243 | } 244 | else{ 245 | c = vec4(0.0, 0.0, 0.0, 0.0); 246 | } 247 | 248 | gl_FragColor = c; 249 | } 250 | `; -------------------------------------------------------------------------------- /frontend/public/js/websocket.js: -------------------------------------------------------------------------------- 1 | var socket; 2 | 3 | var projectData = undefined 4 | 5 | function setupWebsocket(type) { 6 | 7 | if (type == "extern") { 8 | socket = io.connect("https://sulfurous.aau.at" + ':8082'); 9 | } else if (type == "intern" || type == "app") { 10 | socket = io.connect(window.location.hostname + ':8082'); 11 | } 12 | 13 | socket.on("getProjectDataReturn", function (data) { 14 | projectData = data.project_token 15 | // console.log(data) 16 | }); 17 | 18 | socket.on("sendSB2file", function (data) { 19 | 20 | if (type != "app") { 21 | loadSP2FileFromSocket(data); 22 | } else { 23 | loadFromSocket(data); 24 | } 25 | }); 26 | 27 | socket.on("sendPackage", function (data) { 28 | console.log("tset") 29 | console.log(data) 30 | var zip = new JSZip(data); 31 | console.log(zip) 32 | var content = zip.generate({ type: "blob" }); 33 | saveData(content, "OUTPUT.zip") 34 | }) 35 | } -------------------------------------------------------------------------------- /frontend/public/soundbank/Instr.as: -------------------------------------------------------------------------------- 1 | /* 2 | * Scratch Project Editor and Player 3 | * Copyright (C) 2014 Massachusetts Institute of Technology 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | // Instr.as 21 | // John Maloney, April 2012 22 | // 23 | // This class embeds the sound data for Scratch instruments and drums. 24 | // The static variable 'samples' is a dictionary of named sound buffers. 25 | // Call initSamples() to initialize 'samples' before using. 26 | // 27 | // All instrument and drum samples were created for Scratch by: 28 | // 29 | // Paul Madden, paulmatthewmadden@yahoo.com 30 | // 31 | // Paul is an excellent sound designer and we appreciate all the effort 32 | // he put into this project. 33 | 34 | package soundbank { 35 | import flash.utils.*; 36 | import sound.WAVFile; 37 | 38 | public class Instr { 39 | 40 | public static var samples:Object; 41 | 42 | public static function initSamples():void { 43 | // Initialize the dictionary of named sound buffers. 44 | // Details: Build the dictionary by enumerating all the embedded sounds in this file 45 | // (i.e. constants with a value of type 'class'), extracting the sound data from the 46 | // WAV file, and adding an entry for it to the 'samples' object. 47 | 48 | if (samples) return; // already initialized 49 | 50 | samples = {}; 51 | var classDescription:XML = describeType(Instr); 52 | for each (var k:XML in classDescription.elements('constant')) { 53 | if (k.attribute('type') == 'Class') { 54 | var instrName:String = k.attribute('name'); 55 | samples[instrName] = getWAVSamples(new Instr[instrName]); 56 | } 57 | } 58 | } 59 | 60 | private static function getWAVSamples(wavData:ByteArray):ByteArray { 61 | // Extract a sound buffer from a WAV file. Assume the WAV file contains 16-bit, uncompressed sound data. 62 | var info:Object = WAVFile.decode(wavData); 63 | var soundBuffer:ByteArray = new ByteArray(); 64 | soundBuffer.endian = Endian.LITTLE_ENDIAN; 65 | wavData.position = info.sampleDataStart; 66 | wavData.readBytes(soundBuffer, 0, 2 * info.sampleCount); 67 | return soundBuffer; 68 | } 69 | 70 | /* Instruments */ 71 | 72 | [Embed(source='instruments/AcousticGuitar_F3_22k.wav', mimeType='application/octet-stream')] 73 | public static const AcousticGuitar_F3:Class; 74 | 75 | [Embed(source='instruments/AcousticPiano(5)_A#3_22k.wav', mimeType='application/octet-stream')] 76 | public static const AcousticPiano_As3:Class; 77 | 78 | [Embed(source='instruments/AcousticPiano(5)_C4_22k.wav', mimeType='application/octet-stream')] 79 | public static const AcousticPiano_C4:Class; 80 | 81 | [Embed(source='instruments/AcousticPiano(5)_G4_22k.wav', mimeType='application/octet-stream')] 82 | public static const AcousticPiano_G4:Class; 83 | 84 | [Embed(source='instruments/AcousticPiano(5)_F5_22k.wav', mimeType='application/octet-stream')] 85 | public static const AcousticPiano_F5:Class; 86 | 87 | [Embed(source='instruments/AcousticPiano(5)_C6_22k.wav', mimeType='application/octet-stream')] 88 | public static const AcousticPiano_C6:Class; 89 | 90 | [Embed(source='instruments/AcousticPiano(5)_D#6_22k.wav', mimeType='application/octet-stream')] 91 | public static const AcousticPiano_Ds6:Class; 92 | 93 | [Embed(source='instruments/AcousticPiano(5)_D7_22k.wav', mimeType='application/octet-stream')] 94 | public static const AcousticPiano_D7:Class; 95 | 96 | [Embed(source='instruments/AltoSax_A3_22K.wav', mimeType='application/octet-stream')] 97 | public static const AltoSax_A3:Class; 98 | 99 | [Embed(source='instruments/AltoSax(3)_C6_22k.wav', mimeType='application/octet-stream')] 100 | public static const AltoSax_C6:Class; 101 | 102 | [Embed(source='instruments/Bassoon_C3_22k.wav', mimeType='application/octet-stream')] 103 | public static const Bassoon_C3:Class; 104 | 105 | [Embed(source='instruments/BassTrombone_A2(2)_22k.wav', mimeType='application/octet-stream')] 106 | public static const BassTrombone_A2_2:Class; 107 | 108 | [Embed(source='instruments/BassTrombone_A2(3)_22k.wav', mimeType='application/octet-stream')] 109 | public static const BassTrombone_A2_3:Class; 110 | 111 | [Embed(source='instruments/Cello(3b)_C2_22k.wav', mimeType='application/octet-stream')] 112 | public static const Cello_C2:Class; 113 | 114 | [Embed(source='instruments/Cello(3)_A#2_22k.wav', mimeType='application/octet-stream')] 115 | public static const Cello_As2:Class; 116 | 117 | [Embed(source='instruments/Choir(4)_F3_22k.wav', mimeType='application/octet-stream')] 118 | public static const Choir_F3:Class; 119 | 120 | [Embed(source='instruments/Choir(4)_F4_22k.wav', mimeType='application/octet-stream')] 121 | public static const Choir_F4:Class; 122 | 123 | [Embed(source='instruments/Choir(4)_F5_22k.wav', mimeType='application/octet-stream')] 124 | public static const Choir_F5:Class; 125 | 126 | [Embed(source='instruments/Clarinet_C4_22k.wav', mimeType='application/octet-stream')] 127 | public static const Clarinet_C4:Class; 128 | 129 | [Embed(source='instruments/ElectricBass(2)_G1_22k.wav', mimeType='application/octet-stream')] 130 | public static const ElectricBass_G1:Class; 131 | 132 | [Embed(source='instruments/ElectricGuitar(2)_F3(1)_22k.wav', mimeType='application/octet-stream')] 133 | public static const ElectricGuitar_F3:Class; 134 | 135 | [Embed(source='instruments/ElectricPiano_C2_22k.wav', mimeType='application/octet-stream')] 136 | public static const ElectricPiano_C2:Class; 137 | 138 | [Embed(source='instruments/ElectricPiano_C4_22k.wav', mimeType='application/octet-stream')] 139 | public static const ElectricPiano_C4:Class; 140 | 141 | [Embed(source='instruments/EnglishHorn(1)_D4_22k.wav', mimeType='application/octet-stream')] 142 | public static const EnglishHorn_D4:Class; 143 | 144 | [Embed(source='instruments/EnglishHorn(1)_F3_22k.wav', mimeType='application/octet-stream')] 145 | public static const EnglishHorn_F3:Class; 146 | 147 | [Embed(source='instruments/Flute(3)_B5(1)_22k.wav', mimeType='application/octet-stream')] 148 | public static const Flute_B5_1:Class; 149 | 150 | [Embed(source='instruments/Flute(3)_B5(2)_22k.wav', mimeType='application/octet-stream')] 151 | public static const Flute_B5_2:Class; 152 | 153 | [Embed(source='instruments/Marimba_C4_22k.wav', mimeType='application/octet-stream')] 154 | public static const Marimba_C4:Class; 155 | 156 | [Embed(source='instruments/MusicBox_C4_22k.wav', mimeType='application/octet-stream')] 157 | public static const MusicBox_C4:Class; 158 | 159 | [Embed(source='instruments/Organ(2)_G2_22k.wav', mimeType='application/octet-stream')] 160 | public static const Organ_G2:Class; 161 | 162 | [Embed(source='instruments/Pizz(2)_A3_22k.wav', mimeType='application/octet-stream')] 163 | public static const Pizz_A3:Class; 164 | 165 | [Embed(source='instruments/Pizz(2)_E4_22k.wav', mimeType='application/octet-stream')] 166 | public static const Pizz_E4:Class; 167 | 168 | [Embed(source='instruments/Pizz(2)_G2_22k.wav', mimeType='application/octet-stream')] 169 | public static const Pizz_G2:Class; 170 | 171 | [Embed(source='instruments/SteelDrum_D5_22k.wav', mimeType='application/octet-stream')] 172 | public static const SteelDrum_D5:Class; 173 | 174 | [Embed(source='instruments/SynthLead(6)_C4_22k.wav', mimeType='application/octet-stream')] 175 | public static const SynthLead_C4:Class; 176 | 177 | [Embed(source='instruments/SynthLead(6)_C6_22k.wav', mimeType='application/octet-stream')] 178 | public static const SynthLead_C6:Class; 179 | 180 | [Embed(source='instruments/SynthPad(2)_A3_22k.wav', mimeType='application/octet-stream')] 181 | public static const SynthPad_A3:Class; 182 | 183 | [Embed(source='instruments/SynthPad(2)_C6_22k.wav', mimeType='application/octet-stream')] 184 | public static const SynthPad_C6:Class; 185 | 186 | [Embed(source='instruments/TenorSax(1)_C3_22k.wav', mimeType='application/octet-stream')] 187 | public static const TenorSax_C3:Class; 188 | 189 | [Embed(source='instruments/Trombone_B3_22k.wav', mimeType='application/octet-stream')] 190 | public static const Trombone_B3:Class; 191 | 192 | [Embed(source='instruments/Trumpet_E5_22k.wav', mimeType='application/octet-stream')] 193 | public static const Trumpet_E5:Class; 194 | 195 | [Embed(source='instruments/Vibraphone_C3_22k.wav', mimeType='application/octet-stream')] 196 | public static const Vibraphone_C3:Class; 197 | 198 | [Embed(source='instruments/Violin(2)_D4_22K.wav', mimeType='application/octet-stream')] 199 | public static const Violin_D4:Class; 200 | 201 | [Embed(source='instruments/Violin(3)_A4_22k.wav', mimeType='application/octet-stream')] 202 | public static const Violin_A4:Class; 203 | 204 | [Embed(source='instruments/Violin(3b)_E5_22k.wav', mimeType='application/octet-stream')] 205 | public static const Violin_E5:Class; 206 | 207 | [Embed(source='instruments/WoodenFlute_C5_22k.wav', mimeType='application/octet-stream')] 208 | public static const WoodenFlute_C5:Class; 209 | 210 | /* Drums */ 211 | 212 | [Embed(source='drums/BassDrum(1b)_22k.wav', mimeType='application/octet-stream')] 213 | public static const BassDrum:Class; 214 | 215 | [Embed(source='drums/Bongo_22k.wav', mimeType='application/octet-stream')] 216 | public static const Bongo:Class; 217 | 218 | [Embed(source='drums/Cabasa(1)_22k.wav', mimeType='application/octet-stream')] 219 | public static const Cabasa:Class; 220 | 221 | [Embed(source='drums/Clap(1)_22k.wav', mimeType='application/octet-stream')] 222 | public static const Clap:Class; 223 | 224 | [Embed(source='drums/Claves(1)_22k.wav', mimeType='application/octet-stream')] 225 | public static const Claves:Class; 226 | 227 | [Embed(source='drums/Conga(1)_22k.wav', mimeType='application/octet-stream')] 228 | public static const Conga:Class; 229 | 230 | [Embed(source='drums/Cowbell(3)_22k.wav', mimeType='application/octet-stream')] 231 | public static const Cowbell:Class; 232 | 233 | [Embed(source='drums/Crash(2)_22k.wav', mimeType='application/octet-stream')] 234 | public static const Crash:Class; 235 | 236 | [Embed(source='drums/Cuica(2)_22k.wav', mimeType='application/octet-stream')] 237 | public static const Cuica:Class; 238 | 239 | [Embed(source='drums/GuiroLong(1)_22k.wav', mimeType='application/octet-stream')] 240 | public static const GuiroLong:Class; 241 | 242 | [Embed(source='drums/GuiroShort(1)_22k.wav', mimeType='application/octet-stream')] 243 | public static const GuiroShort:Class; 244 | 245 | [Embed(source='drums/HiHatClosed(1)_22k.wav', mimeType='application/octet-stream')] 246 | public static const HiHatClosed:Class; 247 | 248 | [Embed(source='drums/HiHatOpen(2)_22k.wav', mimeType='application/octet-stream')] 249 | public static const HiHatOpen:Class; 250 | 251 | [Embed(source='drums/HiHatPedal(1)_22k.wav', mimeType='application/octet-stream')] 252 | public static const HiHatPedal:Class; 253 | 254 | [Embed(source='drums/Maracas(1)_22k.wav', mimeType='application/octet-stream')] 255 | public static const Maracas:Class; 256 | 257 | [Embed(source='drums/SideStick(1)_22k.wav', mimeType='application/octet-stream')] 258 | public static const SideStick:Class; 259 | 260 | [Embed(source='drums/SnareDrum(1)_22k.wav', mimeType='application/octet-stream')] 261 | public static const SnareDrum:Class; 262 | 263 | [Embed(source='drums/Tambourine(3)_22k.wav', mimeType='application/octet-stream')] 264 | public static const Tambourine:Class; 265 | 266 | [Embed(source='drums/Tom(1)_22k.wav', mimeType='application/octet-stream')] 267 | public static const Tom:Class; 268 | 269 | [Embed(source='drums/Triangle(1)_22k.wav', mimeType='application/octet-stream')] 270 | public static const Triangle:Class; 271 | 272 | [Embed(source='drums/Vibraslap(1)_22k.wav', mimeType='application/octet-stream')] 273 | public static const Vibraslap:Class; 274 | 275 | [Embed(source='drums/WoodBlock(1)_22k.wav', mimeType='application/octet-stream')] 276 | public static const WoodBlock:Class; 277 | 278 | }} 279 | -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/BassDrum(1b)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/BassDrum(1b)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Bongo_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Bongo_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Cabasa(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Cabasa(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Clap(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Clap(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Claves(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Claves(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Conga(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Conga(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Cowbell(3)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Cowbell(3)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Crash(2)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Crash(2)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Cuica(2)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Cuica(2)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/GuiroLong(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/GuiroLong(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/GuiroShort(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/GuiroShort(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/HiHatClosed(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/HiHatClosed(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/HiHatOpen(2)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/HiHatOpen(2)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/HiHatPedal(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/HiHatPedal(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Maracas(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Maracas(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/SideStick(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/SideStick(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/SnareDrum(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/SnareDrum(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Tambourine(3)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Tambourine(3)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Tom(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Tom(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Triangle(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Triangle(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/Vibraslap(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/Vibraslap(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/drums/WoodBlock(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/drums/WoodBlock(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/AcousticGuitar_F3_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/AcousticGuitar_F3_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/AcousticPiano(5)_A%233_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/AcousticPiano(5)_A%233_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/AcousticPiano(5)_A3_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/AcousticPiano(5)_A3_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/AcousticPiano(5)_C4_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/AcousticPiano(5)_C4_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/AcousticPiano(5)_C6_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/AcousticPiano(5)_C6_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/AcousticPiano(5)_D6_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/AcousticPiano(5)_D6_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/AcousticPiano(5)_D7_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/AcousticPiano(5)_D7_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/AcousticPiano(5)_F5_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/AcousticPiano(5)_F5_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/AcousticPiano(5)_G4_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/AcousticPiano(5)_G4_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/AltoSax(3)_C6_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/AltoSax(3)_C6_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/AltoSax_A3_22K.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/AltoSax_A3_22K.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/BassTrombone_A2(2)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/BassTrombone_A2(2)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/BassTrombone_A2(3)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/BassTrombone_A2(3)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Bassoon_C3_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Bassoon_C3_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Cello(3)_A2_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Cello(3)_A2_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Cello(3b)_C2_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Cello(3b)_C2_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Choir(4)_F3_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Choir(4)_F3_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Choir(4)_F4_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Choir(4)_F4_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Choir(4)_F5_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Choir(4)_F5_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Clarinet_C4_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Clarinet_C4_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/ElectricBass(2)_G1_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/ElectricBass(2)_G1_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/ElectricGuitar(2)_F3(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/ElectricGuitar(2)_F3(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/ElectricPiano_C2_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/ElectricPiano_C2_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/ElectricPiano_C4_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/ElectricPiano_C4_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/EnglishHorn(1)_D4_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/EnglishHorn(1)_D4_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/EnglishHorn(1)_F3_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/EnglishHorn(1)_F3_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Flute(3)_B5(1)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Flute(3)_B5(1)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Flute(3)_B5(2)_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Flute(3)_B5(2)_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Marimba_C4_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Marimba_C4_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/MusicBox_C4_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/MusicBox_C4_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Organ(2)_G2_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Organ(2)_G2_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Pizz(2)_A3_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Pizz(2)_A3_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Pizz(2)_E4_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Pizz(2)_E4_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Pizz(2)_G2_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Pizz(2)_G2_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/SteelDrum_D5_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/SteelDrum_D5_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/SynthLead(6)_C4_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/SynthLead(6)_C4_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/SynthLead(6)_C6_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/SynthLead(6)_C6_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/SynthPad(2)_A3_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/SynthPad(2)_A3_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/SynthPad(2)_C6_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/SynthPad(2)_C6_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/TenorSax(1)_C3_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/TenorSax(1)_C3_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Trombone_B3_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Trombone_B3_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Trumpet_E5_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Trumpet_E5_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Vibraphone_C3_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Vibraphone_C3_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Violin(2)_D4_22K.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Violin(2)_D4_22K.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Violin(3)_A4_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Violin(3)_A4_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/Violin(3b)_E5_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/Violin(3b)_E5_22k.wav -------------------------------------------------------------------------------- /frontend/public/soundbank/instruments/WoodenFlute_C5_22k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/frontend/public/soundbank/instruments/WoodenFlute_C5_22k.wav -------------------------------------------------------------------------------- /imgs/img0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img0.png -------------------------------------------------------------------------------- /imgs/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img1.png -------------------------------------------------------------------------------- /imgs/img10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img10.png -------------------------------------------------------------------------------- /imgs/img11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img11.png -------------------------------------------------------------------------------- /imgs/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img2.png -------------------------------------------------------------------------------- /imgs/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img3.png -------------------------------------------------------------------------------- /imgs/img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img4.png -------------------------------------------------------------------------------- /imgs/img5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img5.png -------------------------------------------------------------------------------- /imgs/img6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img6.png -------------------------------------------------------------------------------- /imgs/img7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img7.png -------------------------------------------------------------------------------- /imgs/img8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img8.png -------------------------------------------------------------------------------- /imgs/img9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/imgs/img9.png -------------------------------------------------------------------------------- /sb3tosb2/dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y python3 5 | 6 | WORKDIR /work 7 | 8 | #./src/run.sh 9 | CMD [ "bash","./src/run.sh" ] -------------------------------------------------------------------------------- /sb3tosb2/sb2/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/sb3tosb2/sb2/.gitkeep -------------------------------------------------------------------------------- /sb3tosb2/sb3/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/sb3tosb2/sb3/.gitkeep -------------------------------------------------------------------------------- /sb3tosb2/sb3/converted/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mittagskogel/Sulfurous/213e7d930affff87943f2e0f4fa2dac0310b25cc/sb3tosb2/sb3/converted/.gitkeep -------------------------------------------------------------------------------- /sb3tosb2/src/README.md: -------------------------------------------------------------------------------- 1 | SB3 to SB2 Converter 2 | ============== 3 | 4 | A simple Python 3 program that converts .sb3 files to .sb2 files 5 | 6 | Requirements 7 | -------------- 8 | - Python 3 (preferably 3.6.2 or later) 9 | 10 | Installation 11 | -------------- 12 | Download and extract the ZIP file and move the sb3tosb2.py file to wherever you want. 13 | 14 | Usage 15 | -------------- 16 | 1. Run sb3tosb2.py with Python 17 | 2. Select the SB3 file to open 18 | 3. Either select the SB2 file to save to or type in a new file name 19 | 4. Click OK to exit 20 | 21 | Usage (command line) 22 | -------------- 23 | 1. Open the terminal or command prompt and navigate to the directory of the sb3tosb2.py file. 24 | 2. Enter the following command: `python sb3tosb2.py [unordered options] sb3path [sb2path]`
Options and sb2path are not necessary. 25 | 3. If an error is given, make sure you entered a valid sb3 file. 26 | 27 | Arguments 28 | -------------- 29 | Options may or may not be separated by a space.
30 | List of options: 31 | - `-h`: Displays the program arguments and list of options 32 | - `-c`: This enables compatibility mode. Workarounds for the following blocks will be added to sprites: 33 | - glide to [ v] 34 | - costume [number v] 35 | - set drag mode [ v] 36 | - <[] contains []?> (may result in performance loss) 37 | - (item # of [] in [ v]) (may result in performance loss) 38 | - pen color blocks (including HSV and shade blocks) 39 | - timer blocks 40 | - `-j`: Automatically enables compatibility mode and adds an unlimited join workaround (may result in significant performance loss) 41 | - `-l`: Automatically enables compatibility mode and adds custom blocks to automatically limit list length to 200,000 (may result in performance loss) 42 | - `-p`: Tries to insert blocks to fill the screen when the pen size is set to a value greater than 255 43 | 44 | Known Issues 45 | -------------- 46 | - MP3 audio files cannot be converted 47 | - Compatibility mode changes variable monitor labels 48 | - Compatibility mode allows ([ v] of [ v]) to access only variables (not attributes like x position, backdrop #, etc.) 49 | - Dragging in projects converted with compatibility mode does not have the same pen behavior as in 3.0 50 | - Unlimited join does not check case when checking string equality -------------------------------------------------------------------------------- /sb3tosb2/src/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | while true 3 | do 4 | #echo "CHECKING FOR WORK..." 5 | for entry in ./sb3/*.sb3; do 6 | if [ "$entry" != "./sb3/*.sb3" ] 7 | then 8 | sb3name="$(cut -d'/' -f3 <<<"$entry")" 9 | sb2name="./sb2/$(cut -d'.' -f1 <<<"$sb3name").sb2" 10 | echo "WORK: $sb3name" 11 | echo "$sb2name" 12 | python3 ./src/sb3tosb2.py "$entry" "$sb2name" 13 | mv "$entry" "./sb3/converted/$sb3name" 14 | fi 15 | #echo "NO WORK" 16 | done 17 | sleep 1 18 | done -------------------------------------------------------------------------------- /start/startSulurousCloud.bat: -------------------------------------------------------------------------------- 1 | docker-compose -f ../docker-compose.yml start sulfurous-frontend sulfurous-backend -------------------------------------------------------------------------------- /start/startSulurousCloud.sh: -------------------------------------------------------------------------------- 1 | sudo docker-compose -f ../docker-compose.yml start sulfurous-frontend sulfurous-backend -------------------------------------------------------------------------------- /start/startSulurousSB2.bat: -------------------------------------------------------------------------------- 1 | docker-compose -f ../docker-compose.yml start sulfurous-frontend -------------------------------------------------------------------------------- /start/startSulurousSB2.sh: -------------------------------------------------------------------------------- 1 | sudo docker-compose -f ../docker-compose.yml start sulfurous-frontend -------------------------------------------------------------------------------- /start/startSulurousSB3.bat: -------------------------------------------------------------------------------- 1 | docker-compose -f ../docker-compose.yml start sulfurous-frontend sulfurous-backend sulfurous-sb3tosb2 -------------------------------------------------------------------------------- /start/startSulurousSB3.sh: -------------------------------------------------------------------------------- 1 | sudo docker-compose -f ../docker-compose.yml start sulfurous-frontend sulfurous-backend sulfurous-sb3tosb2 --------------------------------------------------------------------------------