Skip to content

Instantly share code, notes, and snippets.

@GarboMuffin
Last active September 15, 2024 05:47
Show Gist options
  • Select an option

  • Save GarboMuffin/8d3a1ee29515f5bc618780511101742b to your computer and use it in GitHub Desktop.

Select an option

Save GarboMuffin/8d3a1ee29515f5bc618780511101742b to your computer and use it in GitHub Desktop.
Archive of old TurboLoader extensions for TurboWarp.

TurboLoader Archive

These are old plugins from the now-defunct TurboLoader with minimal changes to make them run as normal unsandboxed TurboWarp Extensions. Many of them have very significant bugs or very dangerous security bugs.

These are intended primarily to allow porting Turboloader projects to newer equivalents in the official extension library, but technically you can just keep using them as-is.

Regarding license: The TurboLoader creator stated "Feel free to use is as you wish, I'm giving up my rights to it."

How to run them

  1. Find the extension in this list:
  1. Copy the entire JS code (easiest way is pressing the "Raw" button in the top right corner of the extension's code, then from there pressing ctrl+a then ctrl+c)
  2. Go to https://turbowarp.org/editor -> add extension -> click "Custom Extension" -> switch to the text tab -> paste the extension code -> enable "Run extension without sandbox"

Other beta extensions were never published, so we can't list them here.

(function (Scratch) {
/*
Experimental extension for Scratch.
*/
var am_global_loop;
class AudioStream {
constructor(runtime, vm) {
this.runtime = runtime;
this.vm = vm;
this.contextList=[];
this.contextBindings={};
this.assetSourceList={};
}
getInfo() {
return {
"id": "audiostr",
"name": "AudioStream 🔊",
"color1": '#ba45ac',
"color2": '#a8399b',
"color3": '#942c88',
// "menuIconURI": Icon,
"blocks": [
{
opcode:'am_loadasset',
blockType:"command",
text:'load sound from project [SRC]',
arguments:{
"SRC": {
type: "string",
menu: "audAssetList"
}
}
},
{
opcode:'am_playfromurl',
blockType:"command",
text:'load sound from URL/URI [URL]',
arguments:{
"URL": {
"type": "string",
"defaultValue": 'https://cdn.lstv.ml/tw/meow.mp3'
}
}
},
{
opcode:'am_playnew',
blockType:"command",
text:'start playing [SRC] in a new context',
arguments:{
"SRC": {
type: "string",
menu: "audAssetList"
}
}
},
"---",
{
opcode:'am_usecontext',
blockType:"command",
text:'use [SPRITE]\'s context in this sprite',
arguments:{
SPRITE:{type:"string",menu:"spriteList"}
}
},
"---",
{
opcode:'am_play',
blockType:"command",
text:'play',
arguments:{}
},
{
opcode:'am_playandwait',
blockType:"command",
text:'play and wait till the end',
arguments:{}
},
{
opcode:'am_resume',
blockType:"command",
text:'resume',
arguments:{}
},
{
opcode:'am_pause',
blockType:"command",
text:'pause',
arguments:{}
},
{
opcode:'am_stopthis',
blockType:"command",
text:'stop (this context)',
arguments:{}
},
{
opcode:'am_stophim',
blockType: "command",
text: 'stop sounds of [SPRITE]',
arguments:{SPRITE:{type:"string",menu:"spriteList"}}
},
{
opcode:'am_stop',
blockType:"command",
text:'stop all sounds',
arguments:{}
},
{
opcode:'am_hasStopped',
blockType:"Boolean",
text:'has stopped',
arguments:{}
},
{
opcode:'am_isPaused',
blockType:"Boolean",
text:'is paused',
arguments:{}
},
'---',
{
opcode:'am_getanalyser',
blockType:"reporter",
text:'get all visualizer data (slow)',
arguments:{}
},
{
opcode:'am_getanalyserindex',
blockType:"reporter",
text:'get visualizer data at [INDEX]',
arguments:{
"INDEX": {
type: "number"
}
}
},
{
opcode:'am_analyserfft',
blockType:"command",
text:'set visualizer read size to [VAL] (must be power of 2)',
arguments:{
"VAL": {
"type": "number",
"defaultValue": '256'
}
}
},
"---",
{
opcode:'am_setvolume',
blockType:"command",
text:'set volume to [VAL]',
arguments:{
"VAL": {
"type": "number",
"defaultValue": '1.0'
}
}
},
{
opcode:'am_getvolume',
blockType:"reporter",
text:'volume',
arguments:{}
},
"---",
{
opcode:'am_skipToTime',
blockType:"command",
text:'skip to time [VAL]',
arguments:{
"VAL": {
"type": "number",
"defaultValue": '0'
}
}
},
{
opcode:'am_songDuration',
blockType:"reporter",
text:'sound duration',
arguments:{}
},
{
opcode:'am_songCurrent',
blockType:"reporter",
text:'current time',
arguments:{}
},
"---",
{
opcode:'am_setpitch',
blockType:"command",
text:'set speed/pitch to [VAL]',
arguments:{
"VAL": {
"type": "number",
"defaultValue": '0'
}
}
},
{
opcode:'am_setppitch',
blockType:"command",
text:'preservesPitch [VAL]',
arguments:{
"VAL": {
"type": "Boolean",
"defaultValue": 'false'
}
}
},
{
opcode:'am_setstereo',
blockType:"command",
text:'set pan to [VAL] (-1 to 1)',
arguments:{
"VAL": {
"type": "number",
"defaultValue": '0'
}
}
},
{
opcode:'am_setfilter',
blockType:"command",
text:'filter [FIL] set frequency [FQ] quality [Q]',
arguments:{
"FIL": {
"type": "string",
"defaultValue": 'lowpass',
"menu": "filtersmenu"
},
"Q": {
"type": "number",
"defaultValue": '0'
},
"FQ": {
"type": "number",
"defaultValue": '440'
}
}
},
{
opcode:'am_toglefilter',
blockType:"command",
text:'filter [FIL] [STATE]',
arguments:{
"FIL": {
"type": "string",
"defaultValue": 'lowpass',
"menu": "filtersmenu"
},
"STATE": {
"type": "string",
"defaultValue": 'connect',
"menu": "connectOrDisconnect"
}
}
},
{
opcode:'am_freset',
blockType:"command",
text:'reset all filters',
arguments:{}
},
{
opcode:'am_connect',
blockType:"command",
text:'connect [STRING] to track [TRACK]',
arguments:{
"STRING": {
"type": "string",
"defaultValue": 'filter1'
},
"TRACK": {
"type": "string",
"defaultValue": 'track'
}
}
},
{
opcode:'am_disconnect',
blockType:"command",
text:'disconnect [STRING] from track [TRACK]',
arguments:{
"STRING": {
"type": "string",
"defaultValue": 'filter1'
},
"TRACK": {
"type": "string",
"defaultValue": 'track'
}
}
},
],
"menus": {
"filtersmenu": [{
text:"lowpass",
value: "lowpass"
}, {
text:"highpass",
value: "highpass"
}, {
text:"bandpass",
value: "bandpass"
}, {
text:"lowshelf",
value: "lowshelf"
}, {
text:"highshelf",
value: "highshelf"
}, {
text:"peaking",
value: "peaking"
}, {
text:"notch",
value: "notch"
}, {
text:"allpass",
value: "allpass"
}],
"connectOrDisconnect": [{
text:"connect",
value: "connect"
}, {
text:"disconnect",
value: "disconnect"
}],
audAssetList:{acceptReporters:true,items:'getProjectSounds'},
spriteList:{acceptReporters:true,items:'getSprites'}
}
}
}
getProjectSounds() {
let sounds = this.vm.runtime.targets.map(s => s.sprite.sounds.map(a => { a.spriteName = s.sprite.name; return a })).flat(1).map(s=>{return{text:s.spriteName+" - "+s.name,value:s.assetId}});
return sounds?.length ? sounds : [{ text: "empty", value: "empty" }];
}
getSprites(){
return this.vm.runtime.targets.map(s=>{return{text:s.sprite.name,value:s.id}}).flat(1)
}
//the "am" prefix is a remenant from the original (sandboxed) extension "AudioManager" made by me some time back, that i baseed this enhanced version off.
getContext(id,opt){
if(this.contextBindings[id]&&id!=this.contextBindings[id])return this.getContext(this.contextBindings[id],opt);
return this.contextList.find(c=>c.id==id)||(()=>{return this.newContext(id,opt)})()
}
getEditingContext(){}
newContext(id,opt){
if(!opt)opt={};
let ctx={
id:id||this.contextList.length,
isSimple:!!opt?.simple,
source:document.createElement("audio"),
effects:{},
filters:{},
volume:1
}
if(opt?.src)ctx.source.src=opt.src;
ctx.source.crossOrigin="anonymous";
ctx.source.preservesPitch=false;
ctx.source.id="AudioStreamSource_"+id;
document.body.appendChild(ctx.source);
if(!!!opt?.simple){
ctx.audio=new(window.AudioContext||window.webkitAudioContext);
ctx.track=ctx.audio.createMediaElementSource(ctx.source);
ctx.effects.gain=ctx.audio.createGain();
ctx.effects.panner=new StereoPannerNode(ctx.audio,{pan:0});
["lowpass","highpass","bandpass","lowshelf","highshelf","peaking","notch","allpass"].forEach(f=>{
let bf=ctx.audio.createBiquadFilter();
bf.type=f;
ctx.filters[f]=bf;
})
ctx.analyser=ctx.audio.createAnalyser();
ctx.analyser.fftSize=2048;
ctx.dataArray=new Uint8Array(ctx.analyser.frequencyBinCount);
ctx.track.connect(ctx.effects.gain).connect(ctx.effects.panner).connect(ctx.analyser).connect(ctx.audio.destination);
}
this.contextList.push(ctx)
return ctx
}
am_usecontext({SPRITE},u){
this.contextBindings[u.target.id]=SPRITE;
console.log(this.contextBindings)
}
// am_unbind({},u){
// this.contextBindings[u.target.id]=u.target.id;
// }
am_loadasset({SRC},u){
let ctx=(!u?.source?.id)?this.getContext(u.target.id):u;
if(this.assetSourceList[SRC]){
ctx.source.src=this.assetSourceList[SRC];return
};
let asset=vm.assets.find(e=>e.assetId==SRC);
if(!asset){console.log("[AudioStream] Media error: ",asset);return};
let src=URL.createObjectURL(new Blob([asset.data],{'type':asset.assetType.contentType}));
ctx.source.src=src;
this.assetSourceList[SRC]=src;
console.log(this.assetSourceList);
}
am_playfromurl({URL},u) {
let ctx=this.getContext(u.target.id);
ctx.source.src = URL;
}
am_isloaded(){
return
}
am_stopthis({},u){
let ctx=this.getContext(u.target.id);
ctx.source.currentTime=9e20;
}
am_stophim({SPRITE}){
let ctx=this.getContext(SPRITE);
ctx.source.currentTime=9e20;
}
am_stop(){
this.contextList.forEach(ctx=>{
ctx.source.currentTime=9e20;
})
}
am_play({},u) {
let ctx=this.getContext(u.target.id);
ctx.source.currentTime=0.001;
ctx.source.play();
}
am_playandwait({},u) {
let ctx=this.getContext(u.target.id);
return new Promise(r => {
ctx.source.currentTime = 0.001;
ctx.source.play();
ctx.source.addEventListener("ended",r)
})
}
am_playnew({SRC}){
let id=btoa(Math.random()*1e17);
let ctx=this.newContext(id,{simple:true});
this.am_loadasset({SRC:SRC},ctx);
ctx.source.currentTime=0;
ctx.source.play();
ctx.source.addEventListener("ended",()=>{
ctx.source.remove();
this.contextList=this.contextList.filter(c=>c.id!=id)
})
}
am_resume({},u) {
let ctx=this.getContext(u.target.id);
ctx.source.play();
}
am_pause({},u) {
let ctx=this.getContext(u.target.id);
ctx.source.pause();
}
am_skipToTime({VAL},u) {
let ctx=this.getContext(u.target.id);
ctx.source.currentTime = VAL;
}
am_setpitch({VAL},u) {
let ctx=this.getContext(u.target.id);
/*Calculate the pitch value to be closer to original Scratch*/
ctx.source.playbackRate=ctx.source.defaultPlaybackRate=VAL<0?VAL<-659?0.1:Math.abs(VAL)/700:VAL>700?15:VAL/50+1;
}
am_setvolume({VAL},u) {
let ctx=this.getContext(u.target.id);
ctx.volume=+VAL;
if(ctx.volume>1){ctx.source.volume=1}else if(ctx.volume<0){ctx.source.volume=0}else{ctx.source.volume=ctx.volume};
}
am_getvolume({},u){
let ctx=this.getContext(u.target.id);
return ctx.volume
}
am_setstereo({VAL},u){
let ctx=this.getContext(u.target.id);
ctx.effects.panner.pan.value = VAL;
}
am_setppitch({VAL,u}){
let ctx=this.getContext(u.target.id);
ctx.source.preservesPitch = VAL;
}
am_setfilter({FIL,FQ,Q},u){
let ctx=this.getContext(u.target.id);
if (FIL === "lowpass") {
filter1.frequency.value = FQ;
filter1.Q.value = Q;
} else if (FIL === "highpass") {
filter2.frequency.value = FQ;
filter2.Q.value = Q;
} else if (FIL === "bandpass") {
filter3.frequency.value = FQ;
filter3.Q.value = Q;
} else if (FIL === "lowshelf") {
filter4.frequency.value = FQ;
filter4.Q.value = Q;
} else if (FIL === "highshelf") {
filter5.frequency.value = FQ;
filter5.Q.value = Q;
} else if (FIL === "peaking") {
filter6.frequency.value = FQ;
filter6.Q.value = Q;
} else if (FIL === "notch") {
filter7.frequency.value = FQ;
filter7.Q.value = Q;
} else if (FIL === "allpass") {
filter8.frequency.value = FQ;
filter8.Q.value = Q;
}
}
am_toglefilter({FIL,STATE},u){
let ctx=this.getContext(u.target.id);
if (FIL === "lowpass") {
if (STATE === "connect") { track.connect(filter1) } else { filter1.disconnect() }
} else if (FIL === "highpass") {
if (STATE === "connect") { track.connect(filter2) } else { filter2.disconnect() }
} else if (FIL === "bandpass") {
if (STATE === "connect") { track.connect(filter3) } else { filter3.disconnect() }
} else if (FIL === "lowshelf") {
if (STATE === "connect") { track.connect(filter4) } else { filter4.disconnect() }
} else if (FIL === "highshelf") {
if (STATE === "connect") { track.connect(filter5) } else { filter5.disconnect() }
} else if (FIL === "peaking") {
if (STATE === "connect") { track.connect(filter6) } else { filter6.disconnect() }
} else if (FIL === "notch") {
if (STATE === "connect") { track.connect(filter7) } else { filter7.disconnect() }
} else if (FIL === "allpass") {
if (STATE === "connect") { track.connect(filter8) } else { filter8.disconnect() }
}
}
am_freset({},u) {
let ctx=this.getContext(u.target.id);
filter1.frequency.value = FQ;
filter1.Q.value = 0;
filter2.frequency.value = FQ;
filter2.Q.value = 0;
filter3.frequency.value = FQ;
filter3.Q.value = 0;
filter4.frequency.value = FQ;
filter4.Q.value = 0;
filter5.frequency.value = FQ;
filter5.Q.value = 0;
filter6.frequency.value = FQ;
filter6.Q.value = 0;
filter7.frequency.value = FQ;
filter7.Q.value = 0;
filter8.frequency.value = FQ;
filter8.Q.value = 0;
}
am_connect({STRING},u) {
var utl2_theInstructions = "return " + STRING;
var F = new Function(utl2_theInstructions);
track.connect(F());
}
am_disconnect({STRING},u) {
let ctx=this.getContext(u.target.id);
var utl2_theInstructions = "return " + STRING;
var F = new Function(utl2_theInstructions);
F().disconnect;
}
am_analyserfft({VAL},u){
let ctx=this.getContext(u.target.id);
ctx.analyser.fftSize = VAL;
}
am_songDuration({},u) {
let ctx=this.getContext(u.target.id);
return ctx.source.duration;
}
am_getanalyser({},u) {
let ctx=this.getContext(u.target.id);
ctx.analyser.getByteTimeDomainData(ctx.dataArray);
return JSON.stringify(ctx.dataArray);
}
am_getanalyserindex({INDEX},u) {
let ctx=this.getContext(u.target.id);
ctx.analyser.getByteTimeDomainData(ctx.dataArray);
return ctx.dataArray[INDEX];
}
am_songCurrent({},u) {
let ctx=this.getContext(u.target.id);
return ctx.source.currentTime;
}
am_hasStopped({},u) {
let ctx=this.getContext(u.target.id);
return ctx.source.ended;
}
am_isPaused({},u) {
let ctx=this.getContext(u.target.id);
return ctx.source.paused;
}
}
// var Icon;
// TurboLoader.extension(async plugin => {
// Icon=await plugin.imageAsset("icon.svg");
// plugin.set(AudioStream);
// plugin.changelog("- Fixed some CORS issues.")
// })
Scratch.extensions.register(new AudioStream(Scratch.vm.runtime, Scratch.vm));
}(Scratch));
(function (Scratch) {
/*Scratch wrap for GameJolt API
* Made by LSTV
* Version 1.1 (build 2)
* Tested on TurboWarp,but should work on E羊icques too
* May be unstable, report any bugs and feature requests you find as soon as possible
*/
function p(f){return new Promise(f)}
function sleep(ms){return p(r=>setTimeout(r,ms))}
let GJAPI={},GJUserdata_me={},GJUserdata_fetch={},gjapi_fetch_user_finished=false,gjapi_fetch_user_result;
// TurboLoader.extension(plugin=>{
// plugin.set(ScratchGJAPI)
// })
function ScratchGJAPI(){return class{
constructor(){
GJAPI.bAutoLogin=true;
this.E_SESSION="Error: you need to start a session and log-in to use this block. (Also make sure all ID's are correct.)";
this.E_TROPHY="Error: the trophy ID you entered does not seem to exitst";
this.E_TROPHYS="Error: there do not seem to be any trophys available.";
this.icon=/*?editor*/"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAJ1klEQVR4nO3YvW2bWRBAUc6CBRkuybEDl8ASFDh2SYYKEvA23XD/9I3Ee04DMwNR5MWbG0/rnHO2dwDgn5mZuWLOH1cMAQA+FgEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAoPv2AgDwEc3M1uhzxRAvAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAoNleoOCcc7Z3uNqXx4/tFeDpvD5etle43EzyZ+qSo70AAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACC7tsLAHwmr4+X7RVWzMz2Chue+mgvAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAoNle4CrnnLO9w9W+PH5srwDv5vXxsr3C5WYyX9l/lTz6Cl4AACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgKD79gLA5/X6eNleIeWcs73ChrWjZ2a2Zl/BCwAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIGiuHnjOOVfP3Pbl8WNl7q/Hz5W5t9vt9u3xfW321t3Fm7/e3lbm0jFz+c/UR3DJ0V4AACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBc/XAc865eua237f7ytxvj+8rc2+32+3X4+fa7K273XytrZtfHy8rc7fNXP5z8RE89dFeAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQXPOOdtLAP/N79t9e4XLfb29ba9wuZnZXmFD8ugreAEAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAATdtxcAPq+vt7ftFS43M9srbMke/qy8AABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAgmZh5lmYScg5PmIFMxtfX+uSR/M+vAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAILuCzNnYSYtZ3uBipnkv3PyaJ6PFwAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQNBsL8BzOuec7R1KZpL/ysmj4f/iBQAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAEHTfXgCexcxsr7AheTQ8Ay8AABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQNBsL8D7Oeec7R02zCQ/1smjgX/PCwAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIOi+vQDPaWa2V9iQPBr4nLwAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCBAAABAkAAAgSAAAQJAAAIEgAAECQAACAIAEAAEECAACCZnuBgnPO2Zg7k/3zZg8H+Lu8AABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAgmZ7Ad7VWZztswXwgXkBAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAgSAAAQJAAAIAgAQAAQQIAAIIEAAAECQAACBIAABAkAAAg6E8u0lcI814++AAAAABJRU5ErkJggg=="/*?editor:""*/;
}
getInfo(){
return {
id:"gamejoltapiforscratch",name:"GJAPI",color1:'#2f7f6f',color2:'#c6e854',color3:'#ffffff',menuIconURI:this.icon,
blocks:[
{
opcode:"gj_connect",
blockType:"command",
text:"Connect session | Game ID:[gid] Private token:[token]",
blockIconURI:this.icon,
arguments:{gid:{type:"number",defaultValue:"..."},token:{type:"string",defaultValue:"..."}},
},
{opcode:"gj_disconnect",blockType:"command",text:"Close session",arguments:{}},
"---",
{opcode:"gj_login",blockType:"command",text:"Manual login: username [name] game token [token]",arguments:{name:{type:"string",defaultValue:"..."},token:{type:"string",defaultValue:"..."}}},
{opcode:"gj_reload_ud",blockType:"command",text:"Refresh current user's data (do after manual login)",arguments:{}},
{opcode:"gj_logoff",blockType:"command",text:"Logout",arguments:{}},
{opcode:"gj_user_isloggedin",blockType:"Boolean",text:"Is logged in?",arguments:{}},
{opcode:"gj_origin_gj",blockType:"Boolean",text:"Is game running on GameJolt?",arguments:{}},
{opcode:"gj_user_fetch",blockType:"command",text:"Fetch user [name] by [origin]",arguments:{name:{type:"string",defaultValue:"CROS"},origin:{type:"string",defaultValue:"username",menu:"userorid"}}},
{opcode:"gj_user_getinfo",blockType:"reporter",text:"[type] of [origin]",arguments:{origin:{type:"string",defaultValue:"fetched",menu:"user"},type:{type:"string",defaultValue:"username",menu:"userinfo"}}},
{
opcode:"gj_user_getavatar",
blockType:"reporter",
text:"Profile picture [type] of [origin] quality [q] (1-400)",
arguments:{origin:{type:"string",defaultValue:"fetched",menu:"user"},type:{type:"string",menu:"imgformat"},q:{type:"number",defaultValue:"60"}},
},
"---",
{opcode:"gj_trophy_add",blockType:"command",text:"Achieve trophy [id]",arguments:{id:{type:"number",defaultValue:"1234"}}},
{opcode:"gj_trophy_remove",blockType:"command",text:"Remove trophy [id]",arguments:{id:{type:"number",defaultValue:"1234"}}},
{opcode:"gj_trophy_fetch",blockType:"reporter",text:"Get trophy [id]",arguments:{id:{type:"number",defaultValue:"1234"}}},
{opcode:"gj_trophy_getvalue",blockType:"reporter",text:"Trophy [json] get [i]",arguments:{json:{type:"string",defaultValue:"{...}"},i:{type:"string",menu:"trophydetails"}}},
{opcode:"gj_trophy_fetchmulti",blockType:"reporter",text:"Get list of [_type] trophys",arguments:{_type:{type:"string",menu:"trophyachieved"}}},
{opcode:"gj_trophylist_getindex",blockType:"reporter",text:"Trophy list [json] get trophy at [i] (enter 'length' to get list length)",arguments:{json:{type:"string",defaultValue:"[{...}]"},i:{type:"string",defaultValue:"0"}}},
"---",
{opcode:"gj_score_gettables",blockType:"reporter",text:"Get list of all tables"},
{
opcode:"gj_score_add",
blockType:"command",
text:"Score table [id] add score [score] with text [text], extra data [extra]",
arguments:{id:{type:"number",defaultValue:"1234"},score:{type:"number",defaultValue:"100"},text:{type:"string",defaultValue:"100 Points"},extra:{type:"string",defaultValue:"race"}},
},
{
opcode:"gj_score_add_guest",
blockType:"command",
text:"Score table [id] add score as guest name [name] score [score] with text [text], extra data [extra]",
arguments:{id:{type:"number",defaultValue:"1234"},score:{type:"number",defaultValue:"100"},text:{type:"string",defaultValue:"100 Points"},name:{type:"string",defaultValue:"Guest12345"},extra:{type:"string",defaultValue:"race"}},
},
{
opcode:"gj_score_get",
blockType:"reporter",
text:"Score table [id] get list of [all] scores, limit [limit]",
arguments:{id:{type:"number",defaultValue:"1234"},limit:{type:"number",defaultValue:"10"},all:{type:"string",defaultValue:"all",menu:"scores"}},
},
{
opcode:"gj_score_get_guest",
blockType:"reporter",
text:"Score table [id] get score list of guest [name], limit [limit]",
arguments:{id:{type:"number",defaultValue:"1234"},limit:{type:"number",defaultValue:"10"},name:{type:"string",defaultValue:"Guest12345"}},
},
{opcode:"gj_scorelist_getindex",blockType:"reporter",text:"Score list [json] get score at [i] (enter 'length' to get list length)",arguments:{json:{type:"string",defaultValue:"[{...}]"},i:{type:"string",defaultValue:"0"}}},
{opcode:"gj_score_getvalue",blockType:"reporter",text:"Score [json] get [i]",arguments:{json:{type:"string",defaultValue:"{...}"},i:{type:"string",menu:"scoredetails"}}},
"---",
{opcode:"gj_friendlist",blockType:"reporter",text:"Get friendlist (as ID's)"},
"---",
{opcode:"gj_ds_set",blockType:"command",text:"Set [global] data with key [key] data:[data]",arguments:{global:{type:"string",menu:"global"},key:{type:"string",defaultValue:"key"},data:{type:"string",defaultValue:"data"}}},
{opcode:"gj_ds_get",blockType:"reporter",text:"Get [global] data with key [key]",arguments:{global:{type:"string",menu:"global"},key:{type:"string",defaultValue:"key"}}},
{opcode:"gj_ds_update",blockType:"command",text:"[global] data with key [key] do [operation] value [value]",arguments:{global:{type:"string",menu:"global"},key:{type:"string",defaultValue:"key"},operation:{type:"string",menu:"operation"},value:{type:"string",defaultValue:"1"}}},
{opcode:"gj_ds_remove",blockType: "command",text:"Remove [global] data with key [key]",arguments:{global:{type:"string",menu:"global"},key:{type:"string",defaultValue:"key"}}},
{opcode:"gj_ds_keys",blockType:"reporter",text:"Get [global] key list",arguments:{global:{type:"string",menu:"global"},pattern:{type:"string",defaultValue:'*'},index:{type:"number",defaultValue:0}}},
{opcode:"gj_getindex",blockType:"reporter",text:"List [json] get at [i] (enter 'length' for length)",arguments:{json:{type:"string",defaultValue:"[{...}]"},i:{type:"string",defaultValue:"0"}}},
],
menus:{
user:[{text:"fetched",value:"fetched"},{text:"me",value:"my"}],
userorid:[{text:"username",value:"username"},{text:"id",value:"id"}],
scores:[{text:"everyone's",value:"all"},{text:"my",value:"my"}],
imgformat:[{text:"url",value:"url"},{text:"data uri",value:"uri"}],
userinfo:[{text:"Username",value:"username"},{text:"ID",value:"id"},{text:"Profile description",value:"developer_description"},{text:"Status",value:"status"},{text:"Account creation date",value:"signed_up"},{text:"Last login",value:"last_logged_in"}],
trophyachieved:[{text:"all",value:"0"},{text:"achieved",value:"1"},{text:"not achieved",value:"-1"}],
trophydetails:[{text:"Achieved ... ago (false if never)",value:"achieved"},{text:"Description",value:"description"},{text:"Difficulty",value:"difficulty"},{text:"ID",value:"id"},{text:"Title",value:"title"},{text:"Image URL",value:"image_url"}],
scoredetails:[{text:"is guest",value:"guest"},{text:"username",value:"user"},{text:"achieved ... ago",value:"stored"},{text:"achieved (timestamp)",value:"stored_timestamp"},{text:"score value",value:"sort"},{text:"score text",value:"score"},{text:"extra data",value:"extra_data"}],
global:["global","user"],
operation:["add","subtract","multiply","divide","append","prepend"]
}
};
}
gj_connect({gid,token}) {
GJAPI.iGameID=gid;GJAPI.sGameKey=token;GJAPI.bAutoLogin=true;GJAPI.SessionOpen();
}
async gj_login({name,token}) {
GJAPI.UserLoginManual(name,token,function(r){
if(!r?.success){GJAPI.bLoggedIn=false;return}
GJAPI.UserFetchCurrent(function(pResponse){if(!pResponse.users){GJUserdata_me="error"}GJUserdata_me=pResponse})
});
await sleep(530);
}
async gj_reload_ud() {
GJAPI.UserFetchCurrent(function (pResponse) {if (!pResponse.users) {GJUserdata_me = "error"} GJUserdata_me = pResponse;});
await sleep(530);
}
gj_logoff() {
if(GJAPI.bLoggedIn){GJAPI.UserLogout()}
}
gj_user_isloggedin(){
return GJAPI.bLoggedIn;
}
gj_disconnect() {
GJAPI.UserLogout(); GJAPI.SessionClose();
}
gj_origin_gj() {
return GJAPI.bOnGJ;
}
async gj_user_fetch({name,origin}) {
if (origin === "username") {
GJAPI.UserFetchName(name,function (pResponse) {if (!pResponse.users) {GJUserdata_fetch = "error"} GJUserdata_fetch = pResponse;});
} else {
GJAPI.UserFetchID(name,function (pResponse) {if (!pResponse.users) {GJUserdata_fetch = "error"} GJUserdata_fetch = pResponse;});
}
await sleep(250);
}
gj_user_getinfo({origin,type}) {
if (origin === "my" ? GJUserdata_me.users == undefined :GJUserdata_fetch.users == undefined) {return "Error somewhere!"} else {
if (origin === "my") {
return GJUserdata_me.users[0][type]
} else {
return GJUserdata_fetch.users[0][type]
}
}
}
gj_user_getavatar({origin,type,q}) {
let this_url;
if (origin === "me" ? GJUserdata_me.users == undefined :GJUserdata_fetch.users == undefined) {return "Error somewhere!"} else {
if (origin === "me") {this_url = GJUserdata_me.users[0].avatar_url.replaceAll("/60/","/" + q + "/")} else {
if (GJUserdata_fetch === "error") {return "Error somewhere!";} else {
this_url = GJUserdata_fetch.users[0].avatar_url.replaceAll("/60/","/" + q + "/")
}
}
if (type === "url") {
return this_url;
} else {
return getBase64Image(this_url).then(base64Image => base64Image);
}
}
}
gj_trophy_add({id}) {
return p(r=>GJAPI.TrophyAchieve(id,r))
}
gj_trophy_remove({id}) {
return p(r=>GJAPI.TrophyRemove(id,r))
}
async gj_trophy_fetchmulti({_type}){
console.log(+_type);
return await p(r=>{
GJAPI.TrophyFetch(+_type,t=>{
if(t?.success&&t?.trophies?.length){
r(JSON.stringify(t.trophies))
}else{r(this.E_TROPHYS)}
})
})
}
async gj_trophy_fetch({id}) {
return await p(r=>{
GJAPI.TrophyFetchSingle(id,t=>{
if(t?.success&&t?.trophies?.length){
r(JSON.stringify(t.trophies))
}else{r(this.E_TROPHY)}
})
})
}
async gj_trophylist_getindex({json,i}) {
try{json=typeof json=="string"?JSON.parse(json):json}catch(e){return "Error: Invalid trophy list"}
if(!json?.[i])return "Error: invalid index";
return JSON.stringify(json[i])
}
async gj_trophylist_getindex({json,i}) {
try{json=typeof json=="string"?JSON.parse(json):json}catch(e){return "Error: Invalid trophy list"}
if(!json?.[i])return "Error: invalid index";
return JSON.stringify(json[i])
}
async gj_trophy_getvalue({json,i}) {
try{json=typeof json=="string"?JSON.parse(json):json}catch(e){return "Error: Invalid trophy"}
if(!json?.[i])return "Error: invalid value";
return JSON.stringify(json[i])
}
async gj_score_gettables(){
return await p(_r=>{
GJAPI.ScoreGetTables((r)=>{
if(r?.success=='false'||!r?.tables)return _r(this.E_SESSION);
_r(JSON.stringify(r.tables.map(f=>f.id)))
});
})
}
gj_score_add({id,score,text,extra}) {
return p(r=>GJAPI.ScoreAdd(id,score,text,extra||"",r))
}
gj_score_add_guest({id,score,text,name,extra}) {
return p(r=>GJAPI.ScoreAddGuest(id,score,text,name,extra||"",r))
}
async gj_score_get({id,all,limit,isGuest}){
return await p(re=>{
GJAPI[isGuest?"ScoreFetchGuest":"ScoreFetch"](id,isGuest?all:all=="me",+limit,(r)=>{
if(r.success=='false')return re(this.E_SESSION);
if(!r?.scores?.length)return re("Error: no scores");
re(JSON.stringify(r.scores))
});
})
}
async gj_score_get_guest({id,name,limit}){
return await this.gj_score_get({id:id,all:name,limit:limit,isGuest:true})
}
async gj_scorelist_getindex({json,i}) {
try{json=typeof json=="string"?JSON.parse(json):json}catch(e){return "Error: Invalid scores list"}
if(!json?.[i])return "Error: invalid index";
return JSON.stringify(json[i])
}
async gj_score_getvalue({json,i}){
try{json=typeof json=="string"?JSON.parse(json):json}catch(e){return "Error: Invalid score"}
if(i=="guest")return json?.["guest"]?.length?"true":"false";
if(i=="user"&&json?.["guest"]?.length)return json["guest"];
if(!json?.[i])return "Error: invalid value";
return JSON.stringify(json[i])
}
async gj_friendlist(){
return await p(_r=>{
GJAPI.FriendsFetch((r)=>{
if(r?.success=='false'||!r?.friends)return _r(this.E_SESSION);
_r(JSON.stringify(r.friends.map(f=>f.friend_id)))
});
})
}
gj_ds_set({global,key,data}) {
return p(r=>GJAPI.DataStoreSet(global=="global"?1:0,key,data,r));
}
async gj_ds_get({global,key}){
global=global=="global"?1:0;
return await p(_r=>GJAPI.DataStoreFetch(global,key,r=>{if(!r?.success){_r("Error: "+r.message);return}_r(r.data)}))
}
gj_ds_update({key,global,value,operation}){
global=global=="global"?1:0;
return p(r=>GJAPI.DataStoreUpdate(global,key,operation,value,r))
}
gj_ds_remove({global,key}) {
global=global=="global"?1:0;
return p(r=>GJAPI.DataStoreRemove(global,key,r))
}
async gj_ds_keys({global}) {
global=global=="global"?1:0;
return await p(_r=>GJAPI.DataStoreGetKeys(global,r=>{
if (!r?.success){_r("Error: "+r.message);return}
_r(JSON.stringify(r.keys.map(k=>k.key)))
}))
}
async gj_getindex({json,i}) {
try{json=typeof json=="string"?JSON.parse(json):json}catch(e){return "Error: Invalid list"}
if(!json?.[i])return "Error: nothing at this index";
return typeof json[i]=="string"?json[i]:JSON.stringify(json[i])
}
}}
function validateJSON(json){try{var validatethisjson=JSON.parse(json);return true;}catch(e){return false;}}
let getImageBlob=function(e){return new Promise(async a=>{a((await fetch(e)).blob())})},blobToBase64=function(e){return new Promise(a=>{let t=new FileReader;t.onload=function(){let e=t.result;a(e)},t.readAsDataURL(e)})},getBase64Image=async function(e){let a=await getImageBlob(e);return(await blobToBase64(a)).replace("data:application/octet-stream;base64,","data:image/png;base64,")};
0!==GJAPI.iGameID&&""!==GJAPI.sGameKey||alert("Game ID or Game Key missing!");GJAPI.sAPI="https://api.gamejolt.com/api/game/v1_2";GJAPI.sLogName="[Game Jolt API | Scratch]";GJAPI.iLogStack=20;GJAPI.asQueryParam=function(){for(var a={},f=window.location.search.substring(1).split("&"),b=0;b<f.length;++b){var c=f[b].split("=");"undefined"===typeof a[c[0]]?a[c[0]]=c[1]:"string"===typeof a[c[0]]?a[c[0]]=[a[c[0]],c[1]]:a[c[0]].push(c[1])}return a}();
GJAPI.bOnGJ=window.location.hostname.match(/gamejolt/)?!0:!1;GJAPI.LogTrace=function(a){GJAPI.iLogStack&&(--GJAPI.iLogStack||(a="(\u256f\u00b0\u25a1\u00b0\uff09\u256f\ufe35 \u253b\u2501\u253b"),console.warn(GJAPI.sLogName+" "+a),console.trace())};GJAPI.SEND_FOR_USER=!0;GJAPI.SEND_GENERAL=!1;GJAPI.SendRequest=function(a,f,b){GJAPI.SendRequestEx(a,f,"json","","",b)};
GJAPI.SendRequestEx=function(a,f,b,c,d,e){a=GJAPI.sAPI+encodeURI(a)+(-1===a.indexOf("/?")?"?":"&")+"game_id="+GJAPI.iGameID+"&format="+b;GJAPI.bLoggedIn&&f===GJAPI.SEND_FOR_USER&&(a+="&username="+GJAPI.sUserName+"&user_token="+GJAPI.sUserToken);a+="&signature="+hex_md5(a+d+GJAPI.sGameKey);__CreateAjax(a,c,function(g){GJAPI.bDebugMode&&console.info(GJAPI.sLogName+" <"+a+"> "+g);if(""!==g&&"function"===typeof e)switch(b){case "json":e(eval("("+g+")").response);break;case "dump":var h=g.indexOf("\n"),
k=g.substring(0,h-1);g=g.substring(h+1);e({success:"SUCCESS"===k,data:g});break;default:e(g)}})};GJAPI.bLoggedIn=GJAPI.bAutoLogin&&GJAPI.asQueryParam.gjapi_username&&GJAPI.asQueryParam.gjapi_token?!0:!1;GJAPI.sUserName=GJAPI.bLoggedIn?GJAPI.asQueryParam.gjapi_username:"";GJAPI.sUserToken=GJAPI.bLoggedIn?GJAPI.asQueryParam.gjapi_token:"";console.info(GJAPI.asQueryParam);
console.info(GJAPI.sLogName+(GJAPI.bOnGJ?" E":" Not e")+"mbedded on Game Jolt <"+window.location.origin+window.location.pathname+">");console.info(GJAPI.sLogName+(GJAPI.bLoggedIn?" U":" No u")+"ser recognized <"+GJAPI.sUserName+">");window.location.hostname||console.warn(GJAPI.sLogName+" XMLHttpRequest may not work properly on a local environment");GJAPI.bSessionActive=!0;
GJAPI.SessionOpen=function(){GJAPI.bLoggedIn?GJAPI.iSessionHandle||GJAPI.SendRequest("/sessions/open/",GJAPI.SEND_FOR_USER,function(a){"true"===a.success&&(GJAPI.iSessionHandle=window.setInterval(GJAPI.SessionPing,3E4),window.addEventListener("beforeunload",GJAPI.SessionClose,!1))}):GJAPI.LogTrace("SessionOpen() failed:no user logged in")};GJAPI.SessionPing=function(){GJAPI.bLoggedIn?GJAPI.SendRequest("/sessions/ping/?status="+(GJAPI.bSessionActive?"active":"idle"),GJAPI.SEND_FOR_USER):GJAPI.LogTrace("SessionPing() failed:no user logged in")};
GJAPI.SessionClose=function(){GJAPI.bLoggedIn?(GJAPI.iSessionHandle&&(window.clearInterval(GJAPI.iSessionHandle),window.removeEventListener("beforeunload",GJAPI.SessionClose),GJAPI.iSessionHandle=0),GJAPI.SendRequest("/sessions/close/",GJAPI.SEND_FOR_USER)):GJAPI.LogTrace("SessionClose() failed:no user logged in")};GJAPI.SessionCheck=function(a){GJAPI.SendRequest("/sessions/check/",GJAPI.SEND_FOR_USER,a)};GJAPI.bLoggedIn&&GJAPI.SessionOpen();
GJAPI.UserLoginManual=function(a,f,b){GJAPI.bLoggedIn?GJAPI.LogTrace("UserLoginManual("+a+", "+f+") failed:user "+GJAPI.sUserName+" already logged in"):GJAPI.SendRequest("/users/auth/?username="+a+"&user_token="+f,GJAPI.SEND_GENERAL,function(c){"true"===c.success&&(GJAPI.bLoggedIn=!0,GJAPI.sUserName=a,GJAPI.sUserToken=f,GJAPI.SessionOpen());"function"===typeof b&&b(c)},!1)};
GJAPI.UserLogout=function(){GJAPI.bLoggedIn?(GJAPI.SessionClose(),GJAPI.bLoggedIn=!1,GJAPI.sUserName="",GJAPI.sUserToken="",GJAPI.abTrophyCache={}):GJAPI.LogTrace("UserLogout() failed:no user logged in")};GJAPI.UserFetchID=function(a,f){GJAPI.SendRequest("/users/?user_id="+a,GJAPI.SEND_GENERAL,f)};GJAPI.UserFetchName=function(a,f){GJAPI.SendRequest("/users/?username="+a,GJAPI.SEND_GENERAL,f)};GJAPI.UserFetchCurrent=function(a){GJAPI.bLoggedIn?GJAPI.UserFetchName(GJAPI.sUserName,a):GJAPI.LogTrace("UserFetchCurrent() failed:no user logged in")};
GJAPI.abTrophyCache={};GJAPI.TROPHY_ONLY_ACHIEVED=1;GJAPI.TROPHY_ONLY_NOTACHIEVED=-1;GJAPI.TROPHY_ALL=0;GJAPI.TrophyAchieve=function(a,f){GJAPI.bLoggedIn?GJAPI.abTrophyCache[a]||GJAPI.SendRequest("/trophies/add-achieved/?trophy_id="+a,GJAPI.SEND_FOR_USER,function(b){"true"===b.success&&(GJAPI.abTrophyCache[a]=!0);"function"===typeof f&&f(b)}):GJAPI.LogTrace("TrophyAchieve("+a+") failed:no user logged in")};
GJAPI.TrophyRemove=function(a,f){GJAPI.bLoggedIn?GJAPI.abTrophyCache[a]&&GJAPI.SendRequest("/trophies/remove-achieved/?trophy_id="+a,GJAPI.SEND_FOR_USER,function(b){"true"===b.success&&(GJAPI.abTrophyCache[a]=!1);"function"===typeof f&&f(b)}):GJAPI.LogTrace("TrophyRemove("+a+") failed:no user logged in")};
GJAPI.TrophyFetch=function(a,f){GJAPI.bLoggedIn?GJAPI.SendRequest("/trophies/"+(a===GJAPI.TROPHY_ALL?"":"?achieved="+(a>=GJAPI.TROPHY_ONLY_ACHIEVED?"true":"false")),GJAPI.SEND_FOR_USER,f):GJAPI.LogTrace("TrophyFetch("+a+") failed:no user logged in")};GJAPI.TrophyFetchSingle=function(a,f){GJAPI.bLoggedIn?GJAPI.SendRequest("/trophies/?trophy_id="+a,GJAPI.SEND_FOR_USER,f):GJAPI.LogTrace("TrophyFetchSingle("+a+") failed:no user logged in")};GJAPI.SCORE_ONLY_USER=!0;GJAPI.SCORE_ALL=!1;
GJAPI.BETTER_THAN=!0;GJAPI.WORSE_THAN=!1;GJAPI.ScoreAdd=function(a,f,b,c,d){GJAPI.bLoggedIn?GJAPI.ScoreAddGuest(a,f,b,"",c,d):GJAPI.LogTrace("ScoreAdd("+a+", "+f+", "+b+") failed:no user logged in")};GJAPI.ScoreAddGuest=function(a,f,b,c,d,e){var g=c&&c.length?!0:!1;GJAPI.SendRequest("/scores/add/?sort="+f+"&score="+b+(g?"&guest="+c:"")+(a?"&table_id="+a:"")+(d?"&extra_data="+d:""),g?GJAPI.SEND_GENERAL:GJAPI.SEND_FOR_USER,e)};
GJAPI.ScoreFetch=function(a,f,b,c){!GJAPI.bLoggedIn&&f?GJAPI.LogTrace("ScoreFetch("+a+", "+f+", "+b+") failed:no user logged in"):GJAPI.SendRequest("/scores/?limit="+b+(a?"&table_id="+a:""),f!==GJAPI.SCORE_ONLY_USER?GJAPI.SEND_GENERAL:GJAPI.SEND_FOR_USER,c)};GJAPI.ScoreFetchGuest=function(a,f,b,c){GJAPI.SendRequest("/scores/?limit="+b+"&guest="+f+(a?"&table_id="+a:""),GJAPI.SEND_GENERAL,c)};
GJAPI.ScoreFetchEx=function(a,f,b,c,d,e){!GJAPI.bLoggedIn&&f?GJAPI.LogTrace("ScoreFetch("+a+", "+f+", "+b+", "+c+", "+d+") failed:no user logged in"):GJAPI.SendRequest("/scores/?limit="+b+(a?"&table_id="+a:"")+(c?"&better_than=":"&worse_than=")+d,f!==GJAPI.SCORE_ONLY_USER?GJAPI.SEND_GENERAL:GJAPI.SEND_FOR_USER,e)};GJAPI.ScoreFetchGuestEx=function(a,f,b,c,d,e){GJAPI.SendRequest("/scores/?limit="+b+"&guest="+f+(a?"&table_id="+a:"")+(c?"&better_than=":"&worse_than=")+d,GJAPI.SEND_GENERAL,e)};
GJAPI.ScoreGetRank=function(a,f,b){GJAPI.SendRequest("/scores/get-rank/?sort="+f+(a?"&table_id="+a:""),GJAPI.SEND_GENERAL,b)};GJAPI.ScoreGetTables=function(a){GJAPI.SendRequest("/scores/tables/",GJAPI.SEND_GENERAL,a)};GJAPI.FriendsFetch=function(a){GJAPI.bLoggedIn?GJAPI.SendRequest("/friends/",GJAPI.SEND_FOR_USER,a):GJAPI.LogTrace("FriendsFetch() failed:no user logged in")};GJAPI.DATA_STORE_USER=0;GJAPI.DATA_STORE_GLOBAL=1;
GJAPI.DataStoreSet=function(a,f,b,c){GJAPI.SendRequestEx("/data-store/set/?key="+f,a===GJAPI.DATA_STORE_USER,"json","data="+b,"data"+b,c)};GJAPI.DataStoreFetch=function(a,f,b){GJAPI.SendRequestEx("/data-store/?key="+f,a===GJAPI.DATA_STORE_USER,"dump","","",b)};GJAPI.DataStoreUpdate=function(a,f,b,c,d){GJAPI.SendRequest("/data-store/update/?key="+f+"&operation="+b+"&value="+c,a===GJAPI.DATA_STORE_USER,d)};
GJAPI.DataStoreRemove=function(a,f,b){GJAPI.SendRequest("/data-store/remove/?key="+f,a===GJAPI.DATA_STORE_USER,b)};GJAPI.DataStoreGetKeys=function(a,f){GJAPI.SendRequest("/data-store/get-keys/",a===GJAPI.DATA_STORE_USER,f)};GJAPI.TimeFetch=function(a){GJAPI.SendRequest("/time/",GJAPI.SEND_GENERAL,a)};
function __CreateAjax(a,f,b){"string"!==typeof f&&(f="");if(window.XMLHttpRequest){var c=new XMLHttpRequest;c.onreadystatechange=function(){4===c.readyState&&b(c.responseText)};""!==f?(c.open("POST",a),c.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),c.send(f)):(c.open("GET",a),c.send())}else console.error(GJAPI.sLogName+" XMLHttpRequest not supported")}var hexcase=0;function hex_md5(a){return rstr2hex(rstr_md5(str2rstr_utf8(a)))}
function hex_hmac_md5(a,f){return rstr2hex(rstr_hmac_md5(str2rstr_utf8(a),str2rstr_utf8(f)))}function md5_vm_test(){return"900150983cd24fb0d6963f7d28e17f72"==hex_md5("abc").toLowerCase()}function rstr_md5(a){return binl2rstr(binl_md5(rstr2binl(a),8*a.length))}
function rstr_hmac_md5(a,f){var b=rstr2binl(a);16<b.length&&(b=binl_md5(b,8*a.length));for(var c=Array(16),d=Array(16),e=0;16>e;e++)c[e]=b[e]^909522486,d[e]=b[e]^1549556828;b=binl_md5(c.concat(rstr2binl(f)),512+8*f.length);return binl2rstr(binl_md5(d.concat(b),640))}function rstr2hex(a){try{hexcase}catch(e){hexcase=0}for(var f=hexcase?"0123456789ABCDEF":"0123456789abcdef",b="",c,d=0;d<a.length;d++)c=a.charCodeAt(d),b+=f.charAt(c>>>4&15)+f.charAt(c&15);return b}
function str2rstr_utf8(a){for(var f="",b=-1,c,d;++b<a.length;)c=a.charCodeAt(b),d=b+1<a.length?a.charCodeAt(b+1):0,55296<=c&&56319>=c&&56320<=d&&57343>=d&&(c=65536+((c&1023)<<10)+(d&1023),b++),127>=c?f+=String.fromCharCode(c):2047>=c?f+=String.fromCharCode(192|c>>>6&31,128|c&63):65535>=c?f+=String.fromCharCode(224|c>>>12&15,128|c>>>6&63,128|c&63):2097151>=c&&(f+=String.fromCharCode(240|c>>>18&7,128|c>>>12&63,128|c>>>6&63,128|c&63));return f}
function rstr2binl(a){for(var f=Array(a.length>>2),b=0;b<f.length;b++)f[b]=0;for(b=0;b<8*a.length;b+=8)f[b>>5]|=(a.charCodeAt(b/8)&255)<<b%32;return f}function binl2rstr(a){for(var f="",b=0;b<32*a.length;b+=8)f+=String.fromCharCode(a[b>>5]>>>b%32&255);return f}
function binl_md5(a,f){a[f>>5]|=128<<f%32;a[(f+64>>>9<<4)+14]=f;for(var b=1732584193,c=-271733879,d=-1732584194,e=271733878,g=0;g<a.length;g+=16){var h=b,k=c,l=d,m=e;b=md5_ff(b,c,d,e,a[g+0],7,-680876936);e=md5_ff(e,b,c,d,a[g+1],12,-389564586);d=md5_ff(d,e,b,c,a[g+2],17,606105819);c=md5_ff(c,d,e,b,a[g+3],22,-1044525330);b=md5_ff(b,c,d,e,a[g+4],7,-176418897);e=md5_ff(e,b,c,d,a[g+5],12,1200080426);d=md5_ff(d,e,b,c,a[g+6],17,-1473231341);c=md5_ff(c,d,e,b,a[g+7],22,-45705983);b=md5_ff(b,c,d,e,a[g+8],7,
1770035416);e=md5_ff(e,b,c,d,a[g+9],12,-1958414417);d=md5_ff(d,e,b,c,a[g+10],17,-42063);c=md5_ff(c,d,e,b,a[g+11],22,-1990404162);b=md5_ff(b,c,d,e,a[g+12],7,1804603682);e=md5_ff(e,b,c,d,a[g+13],12,-40341101);d=md5_ff(d,e,b,c,a[g+14],17,-1502002290);c=md5_ff(c,d,e,b,a[g+15],22,1236535329);b=md5_gg(b,c,d,e,a[g+1],5,-165796510);e=md5_gg(e,b,c,d,a[g+6],9,-1069501632);d=md5_gg(d,e,b,c,a[g+11],14,643717713);c=md5_gg(c,d,e,b,a[g+0],20,-373897302);b=md5_gg(b,c,d,e,a[g+5],5,-701558691);e=md5_gg(e,b,c,d,a[g+
10],9,38016083);d=md5_gg(d,e,b,c,a[g+15],14,-660478335);c=md5_gg(c,d,e,b,a[g+4],20,-405537848);b=md5_gg(b,c,d,e,a[g+9],5,568446438);e=md5_gg(e,b,c,d,a[g+14],9,-1019803690);d=md5_gg(d,e,b,c,a[g+3],14,-187363961);c=md5_gg(c,d,e,b,a[g+8],20,1163531501);b=md5_gg(b,c,d,e,a[g+13],5,-1444681467);e=md5_gg(e,b,c,d,a[g+2],9,-51403784);d=md5_gg(d,e,b,c,a[g+7],14,1735328473);c=md5_gg(c,d,e,b,a[g+12],20,-1926607734);b=md5_hh(b,c,d,e,a[g+5],4,-378558);e=md5_hh(e,b,c,d,a[g+8],11,-2022574463);d=md5_hh(d,e,b,c,a[g+
11],16,1839030562);c=md5_hh(c,d,e,b,a[g+14],23,-35309556);b=md5_hh(b,c,d,e,a[g+1],4,-1530992060);e=md5_hh(e,b,c,d,a[g+4],11,1272893353);d=md5_hh(d,e,b,c,a[g+7],16,-155497632);c=md5_hh(c,d,e,b,a[g+10],23,-1094730640);b=md5_hh(b,c,d,e,a[g+13],4,681279174);e=md5_hh(e,b,c,d,a[g+0],11,-358537222);d=md5_hh(d,e,b,c,a[g+3],16,-722521979);c=md5_hh(c,d,e,b,a[g+6],23,76029189);b=md5_hh(b,c,d,e,a[g+9],4,-640364487);e=md5_hh(e,b,c,d,a[g+12],11,-421815835);d=md5_hh(d,e,b,c,a[g+15],16,530742520);c=md5_hh(c,d,e,
b,a[g+2],23,-995338651);b=md5_ii(b,c,d,e,a[g+0],6,-198630844);e=md5_ii(e,b,c,d,a[g+7],10,1126891415);d=md5_ii(d,e,b,c,a[g+14],15,-1416354905);c=md5_ii(c,d,e,b,a[g+5],21,-57434055);b=md5_ii(b,c,d,e,a[g+12],6,1700485571);e=md5_ii(e,b,c,d,a[g+3],10,-1894986606);d=md5_ii(d,e,b,c,a[g+10],15,-1051523);c=md5_ii(c,d,e,b,a[g+1],21,-2054922799);b=md5_ii(b,c,d,e,a[g+8],6,1873313359);e=md5_ii(e,b,c,d,a[g+15],10,-30611744);d=md5_ii(d,e,b,c,a[g+6],15,-1560198380);c=md5_ii(c,d,e,b,a[g+13],21,1309151649);b=md5_ii(b,
c,d,e,a[g+4],6,-145523070);e=md5_ii(e,b,c,d,a[g+11],10,-1120210379);d=md5_ii(d,e,b,c,a[g+2],15,718787259);c=md5_ii(c,d,e,b,a[g+9],21,-343485551);b=safe_add(b,h);c=safe_add(c,k);d=safe_add(d,l);e=safe_add(e,m)}return[b,c,d,e]}function md5_cmn(a,f,b,c,d,e){return safe_add(bit_rol(safe_add(safe_add(f,a),safe_add(c,e)),d),b)}function md5_ff(a,f,b,c,d,e,g){return md5_cmn(f&b|~f&c,a,f,d,e,g)}function md5_gg(a,f,b,c,d,e,g){return md5_cmn(f&c|b&~c,a,f,d,e,g)}
function md5_hh(a,f,b,c,d,e,g){return md5_cmn(f^b^c,a,f,d,e,g)}function md5_ii(a,f,b,c,d,e,g){return md5_cmn(b^(f|~c),a,f,d,e,g)}function safe_add(a,f){var b=(a&65535)+(f&65535);return(a>>16)+(f>>16)+(b>>16)<<16|b&65535}function bit_rol(a,f){return a<<f|a>>>32-f};
window.GJAPI=GJAPI;
Scratch.extensions.register(new (ScratchGJAPI())());
})(Scratch);
(function (Scratch) {
/*
$$$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$\
$$ __$$\ $$ | $$ |$$ __$$\ $$ __$$\ \__$$ __|$$ __$$\ $$ _____|\_$$ _|$$ | $$ |
$$ | $$ |$$ | $$ |$$ / \__|$$ / \__| $$ | $$ / $$ | $$ | $$ | \$$\ $$ |
$$$$$$$\ |$$ | $$ |$$ |$$$$\ \$$$$$$\ $$ | $$ | $$ | $$$$$\ $$ | \$$$$ /
$$ __$$\ $$ | $$ |$$ |\_$$ | \____$$\ $$ | $$ | $$ | $$ __| $$ | $$ $$<
$$ | $$ |$$ | $$ |$$ | $$ |$$\ $$ | $$ | $$ | $$ | $$ | $$ | $$ /\$$\
$$$$$$$ |\$$$$$$ |\$$$$$$ |\$$$$$$ | $$ | $$$$$$ | $$ | $$$$$$\ $$ / $$ |
\_______/ \______/ \______/ \______/ \__| \______/ \__| \______|\__| \__|
Should've Fixed Compile HTML
Has mixed results Most computers and browsers work but some reject it?
QUICK FIX added clamping because some textures require it?
*/
if (typeof window === "undefined" || !window.vm) {
isSandboxed = true;
} else {
isSandboxed = false;
}
var Penwidth = 64
var PenHeight = 64
var screenwidth = 480
var screenheight = 360
var stamprotation = 90
//matrix stuff from webglfundamentals.org
var m4 = {
multiply: function(a, b) {
var b00 = b[0 * 4 + 0];
var b01 = b[0 * 4 + 1];
var b02 = b[0 * 4 + 2];
var b03 = b[0 * 4 + 3];
var b10 = b[1 * 4 + 0];
var b11 = b[1 * 4 + 1];
var b12 = b[1 * 4 + 2];
var b13 = b[1 * 4 + 3];
var b20 = b[2 * 4 + 0];
var b21 = b[2 * 4 + 1];
var b22 = b[2 * 4 + 2];
var b23 = b[2 * 4 + 3];
var b30 = b[3 * 4 + 0];
var b31 = b[3 * 4 + 1];
var b32 = b[3 * 4 + 2];
var b33 = b[3 * 4 + 3];
var a00 = a[0 * 4 + 0];
var a01 = a[0 * 4 + 1];
var a02 = a[0 * 4 + 2];
var a03 = a[0 * 4 + 3];
var a10 = a[1 * 4 + 0];
var a11 = a[1 * 4 + 1];
var a12 = a[1 * 4 + 2];
var a13 = a[1 * 4 + 3];
var a20 = a[2 * 4 + 0];
var a21 = a[2 * 4 + 1];
var a22 = a[2 * 4 + 2];
var a23 = a[2 * 4 + 3];
var a30 = a[3 * 4 + 0];
var a31 = a[3 * 4 + 1];
var a32 = a[3 * 4 + 2];
var a33 = a[3 * 4 + 3];
return [
b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
];
},
translation: function(tx, ty, tz) {
return [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tx, ty, tz, 1,
];
},
xRotation: function(angleInRadians) {
var c = Math.cos(angleInRadians);
var s = Math.sin(angleInRadians);
return [
1, 0, 0, 0,
0, c, s, 0,
0, -s, c, 0,
0, 0, 0, 1,
];
},
yRotation: function(angleInRadians) {
var c = Math.cos(angleInRadians);
var s = Math.sin(angleInRadians);
return [
c, 0, -s, 0,
0, 1, 0, 0,
s, 0, c, 0,
0, 0, 0, 1,
];
},
zRotation: function(angleInRadians) {
var c = Math.cos(angleInRadians);
var s = Math.sin(angleInRadians);
return [
c, s, 0, 0,
-s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
];
},
scaling: function(sx, sy, sz) {
return [
sx, 0, 0, 0,
0, sy, 0, 0,
0, 0, sz, 0,
0, 0, 0, 1,
];
},
translate: function(m, tx, ty, tz) {
return m4.multiply(m, m4.translation(tx, ty, tz));
},
xRotate: function(m, angleInRadians) {
return m4.multiply(m, m4.xRotation(angleInRadians));
},
yRotate: function(m, angleInRadians) {
return m4.multiply(m, m4.yRotation(angleInRadians));
},
zRotate: function(m, angleInRadians) {
return m4.multiply(m, m4.zRotation(angleInRadians));
},
scale: function(m, sx, sy, sz) {
return m4.multiply(m, m4.scaling(sx, sy, sz));
},
orthographic: function(left, right, bottom, top, near, far) {
return [
2 / (right - left), 0, 0, 0,
0, 2 / (top - bottom), 0, 0,
0, 0, 2 / (near - far), 0,
(left + right) / (left - right),
(bottom + top) / (bottom - top),
(near + far) / (near - far),
1,
];
}
}
function fetchFrom(URL) {
return fetch(URL).then(res => res.text()).catch(err => '');
}
//bruh why could they not give the stage an id?
var tempcanvas = document.createElement("canvas");
tempcanvas.width = 480;
tempcanvas.height = 360;
var tempcanvas1 = tempcanvas.getContext("2d");
var canvas = document.getElementById("app")
if (document.getElementsByClassName("sc-canvas").length > 0)
{
canvas = document.getElementsByClassName("sc-canvas")[0]
}
else
{
canvas = canvas.children[0]
canvas = canvas.children[0]
canvas = canvas.children[0]
canvas = canvas.children[2]
canvas = canvas.children[0]
canvas = canvas.children[1]
canvas = canvas.children[0]
canvas = canvas.children[1]
canvas = canvas.children[0]
canvas = canvas.children[0]
canvas = canvas.children[0]
canvas = canvas.children[0]
}
var gl = canvas.getContext("webgl");
if (!gl){
gl = canvas.getContext("experimental-webgl")
}
console.log(gl)
var canvaswidth = canvas.width
var canvasheight = canvas.height
var textures = {}
//boring GLSL
var vertexshaderCode = [
'attribute vec4 a_position;',
'attribute vec2 a_texcoord;',
'attribute vec4 aVertexColor;',
'',
'uniform mat4 u_matrix;',
'',
'varying vec2 v_texcoord;',
'varying vec4 vColor;',
'',
'void main() {',
'gl_Position = u_matrix * a_position;',
'v_texcoord = a_texcoord;',
'vColor = aVertexColor;',
'}'
].join('\n');
var fragmentshaderCode = [
'precision mediump float;',
'',
'varying vec2 v_texcoord;',
'varying vec4 vColor;',
'',
'uniform sampler2D u_texture;',
'',
'void main() {',
'gl_FragColor = texture2D(u_texture, v_texcoord) * vColor;',
'}'
].join('\n');
var quadpositions = [
0, 0,
0, 1,
1, 0,
1, 0,
0, 1,
1, 1,
];
var quadcoords = [
0, 0,
0, 1,
1, 0,
1, 0,
0, 1,
1, 1,
];
var quadcolors = [
1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0
];
var tricolors = [
1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0
];
var quadpositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, quadpositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(quadpositions), gl.STATIC_DRAW);
var quadtexcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, quadtexcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(quadcoords), gl.STATIC_DRAW);
var quadcolorBuffer = gl.createBuffer();
var triPosBuffer = gl.createBuffer();
var triUVBuffer = gl.createBuffer();
var tricolorBuffer = gl.createBuffer();
//end of stupid code
//compile glsl shaders
var vertexShader = gl.createShader(gl.VERTEX_SHADER)
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(vertexShader, vertexshaderCode)
gl.shaderSource(fragmentShader, fragmentshaderCode)
gl.enable( gl.BLEND );
gl.blendEquation( gl.FUNC_ADD );
gl.blendFunc( gl.ONE_MINUS_CONSTANT_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.error('ERROR compiling vertex shader!', gl.getShaderInfoLog(vertexShader));
}
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.error('ERROR compiling fragment shader!', gl.getShaderInfoLog(fragmentShader));
}
//done compiling them
//program data
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('ERROR linking program!', gl.getProgramInfoLog(program));
}
gl.validateProgram(program);
if (!gl.getProgramParameter(program, gl.VALIDATE_STATUS)) {
console.error('ERROR validating program!', gl.getProgramInfoLog(program));
}
//end of program data
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
var texcoordLocation = gl.getAttribLocation(program, "a_texcoord");
var colorLocation = gl.getAttribLocation(program, "aVertexColor");
// lookup uniforms
var matrixLocation = gl.getUniformLocation(program, "u_matrix");
var textureLocation = gl.getUniformLocation(program, "u_texture");
//cool drawing functions
function degtorad(deg) {
return deg * 0.0174533
}
function loadImageAndCreateTextureInfo(url,clamp) {
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// Fill the texture with a 1x1 blue pixel.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));
// let's assume all images are not a power of 2
if (clamp == 'true') {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
}
else{
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
}
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
var textureInfo = {
width: 1, // we don't know the size until it loads
height: 1,
texture: tex,
};
var img = new Image();
img.addEventListener('load', function() {
textureInfo.width = img.width;
textureInfo.height = img.height;
gl.bindTexture(gl.TEXTURE_2D, textureInfo.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
});
img.src = url;
return textureInfo;
}
function drawImage(tex, texWidth, texHeight, dstX, dstY ,stamprotation) {
gl.bindTexture(gl.TEXTURE_2D, tex);
// Tell WebGL to use our shader program pair
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, quadcolorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(quadcolors), gl.STATIC_DRAW);
// Setup the attributes to pull data from our buffers
gl.bindBuffer(gl.ARRAY_BUFFER, quadpositionBuffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, quadtexcoordBuffer);
gl.enableVertexAttribArray(texcoordLocation);
gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, quadcolorBuffer);
gl.enableVertexAttribArray(colorLocation); //
gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
// this matrix will convert from pixels to clip space
var matrix = m4.orthographic(0, gl.canvas.width, gl.canvas.height, 0, -1, 1);
// this matrix will translate our quad to dstX, dstY
matrix = m4.translate(matrix, dstX, dstY, 0);
matrix = m4.zRotate(matrix,degtorad(stamprotation))
// this matrix will scale our 1 unit quad
// from 1 unit to texWidth, texHeight units
matrix = m4.scale(matrix, texWidth, texHeight, 1);
// Set the matrix.
gl.uniformMatrix4fv(matrixLocation, false, matrix);
// Tell the shader to get the texture from texture unit 0
gl.uniform1i(textureLocation, 0);
// draw the quad (2 triangles, 6 vertices)
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
function drawTexturedTri(tex, trianglepoints, triangleuvs) {
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.bindBuffer(gl.ARRAY_BUFFER, triPosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(trianglepoints), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, triUVBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleuvs), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, tricolorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tricolors), gl.STATIC_DRAW);
// Tell WebGL to use our shader program pair
gl.useProgram(program);
// Setup the attributes to pull data from our buffers
gl.bindBuffer(gl.ARRAY_BUFFER, triPosBuffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, triUVBuffer);
gl.enableVertexAttribArray(texcoordLocation); //
gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, tricolorBuffer);
gl.enableVertexAttribArray(colorLocation); //
gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
// this matrix will convert from pixels to clip space
var matrix = m4.orthographic(0, gl.canvas.width, gl.canvas.height, 0, -1, 1);
// this matrix will translate our quad to dstX, dstY
// this matrix will scale our 1 unit quad
// from 1 unit to texWidth, texHeight units
// Set the matrix.
gl.uniformMatrix4fv(matrixLocation, false, matrix);
// Tell the shader to get the texture from texture unit 0
gl.uniform1i(textureLocation, 0);
// draw the quad (2 triangles, 6 vertices)
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
var getDataUri = function(url,callback) {
var tempimage = new Image();
tempimage.src = url;
tempimage.crossOrigin="anonymous"
console.log(url)
tempimage.onload = function() {
tempcanvas1.drawImage(this, 0, 0);
// ... or get as Data URI
callback(tempcanvas.toDataURL('image/png'));
}
}
//end of cool drawing functions.
class BetterPen {
constructor(runtime) {
this.runtime = runtime
}
getInfo() {
return {
"id": "betterpen",
"name": "Pen+",
"color1":'#0e9a6b',
"color2":'#0e9a6b',
"color3":'#0e9a6b',
"docsURI": 'https://www.youtube.com/playlist?list=PLdR2VVCBIN3CceUdgKWOUxFEEbLqWgCC9',
"blockIconURI": "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzNy44NjkyMSIgaGVpZ2h0PSI0OC44NTI3MiIgdmlld0JveD0iMCwwLDM3Ljg2OTIxLDQ4Ljg1MjcyIj48ZGVmcz48cmFkaWFsR3JhZGllbnQgY3g9IjIzNy41NDM0IiBjeT0iMTg0LjAwNTYiIHI9IjkuOTg1NDkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBpZD0iY29sb3ItMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZmZmZmZmIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjY2ZkNWU5Ii8+PC9yYWRpYWxHcmFkaWVudD48L2RlZnM+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIyMy4zMDE4MSwtMTYzLjc2MzA5KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0yMjUuMTYzNTYsMTkzLjIwMDIxYzAuNTYxNTMsLTEuMTA3NDYgMi4yMzQwNCwtMy4yODU4MyAyLjIzNDA0LC0zLjI4NTgzYzAsMCAwLjU5NDQyLDEuODIzOTEgMS4yMjQ0OSwyLjU5NTc1YzAuNjMyMTIsMC43NzQzNSAyLjA4ODc4LDEuNDc0ODQgMi4wODg3OCwxLjQ3NDg0YzAsMCAtMi4xOTQ0NiwxLjI4MTQxIC0zLjMyNDIxLDEuNzI2OTVjLTEuMTEwMzIsMC40Mzc4NyAtMy4zOTcsMC45MjM2NyAtMy4zOTcsMC45MjM2N2MwLDAgMC41OTk3NCwtMi4zMDI5OSAxLjE3MzksLTMuNDM1Mzh6IiBmaWxsPSIjNGM5N2ZmIiBzdHJva2U9IiM1NzVlNzUiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMjI3LjYxMTMxLDE4OS4yOTIwM2wxNC45NTE1NCwtMTUuMjcxOTNjMCwwIDIuMjA2LDAuODk1MDUgMi45NTc3NiwxLjYzMDQ3YzAuODY4OCwwLjg0OTkxIDEuOTU0ODksMy4xNzUzOCAxLjk1NDg5LDMuMTc1MzhsLTE2LjEyNjMxLDE1LjE2NTE0YzAsMCAtMi4wMDYzOSwtMS4xMjc4NiAtMi42MDkyMSwtMS44ODU2OGMtMC42NDA4MiwtMC44MDU2IC0xLjEyODY4LC0yLjgxMzM3IC0xLjEyODY4LC0yLjgxMzM3eiIgZmlsbD0idXJsKCNjb2xvci0xKSIgc3Ryb2tlPSIjNTc1ZTc1IiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTIzNy43NTY5OSwxNzIuOTUyMTNjMCwwIDAuOTg2MTksMS4wNTA2MiAyLjM5NjA4LC0wLjI3MDcyYzEuODAzLC0xLjY4OTc3IDQuMjMxMDUsLTUuOTAxNDcgNS40NDc0MywtNi41ODcwN2MxLjM3NDgsLTAuNzc0ODkgMy45MDQxNCwwLjIzNjM5IDMuOTA0MTQsMC4yMzYzOSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNTc1ZTc1IiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxwYXRoIGQ9Ik0yMzYuMDc5ODEsMTcyLjMxMTM1YzAsLTAuNjkwMzYgMC41NTk2NCwtMS4yNSAxLjI1LC0xLjI1YzAuNjkwMzYsMCAxLjI1LDAuNTU5NjQgMS4yNSwxLjI1YzAsMC42OTAzNiAtMC41NTk2NCwxLjI1IC0xLjI1LDEuMjVjLTAuNjkwMzYsMCAtMS4yNSwtMC41NTk2NCAtMS4yNSwtMS4yNXoiIGZpbGw9IiM1NzVlNzUiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTI1MC45OTk3OSwxNjQuNzI4NzdjMCwwIDEuOTEzOTYsLTEuMDUxOTMgNC4yMDAwOSwxLjMyMzU4YzIuNDI2ODUsMi41MjE3MyAwLjYwNTc2LDQuNDQzNDQgMC42MDU3Niw0LjQ0MzQ0bC04LjMzMDE0LDguMjIzMzVjMCwwIC0wLjc1MDQsLTIuMDcxMTIgLTEuNTYyNDksLTIuNzk0OTRjLTAuODI1MjQsLTAuNzM1NTUgLTMuMzUwMTYsLTEuNTgzNzMgLTMuMzUwMTYsLTEuNTgzNzN6IiBmaWxsPSIjNGM5N2ZmIiBzdHJva2U9IiM1NzVlNzUiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48dGV4dCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMzkuODkzMzcsMjAxLjcxMTE4KSBzY2FsZSgwLjg3MjM3LDAuODcyMzcpIiBmb250LXNpemU9IjQwIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmaWxsPSIjZThlYmY0IiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0iIzU3NWU3NSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBmb250LWZhbWlseT0iU2FucyBTZXJpZiIgZm9udC13ZWlnaHQ9Im5vcm1hbCIgdGV4dC1hbmNob3I9InN0YXJ0IiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHRzcGFuIHg9IjAiIGR5PSIwIj4rPC90c3Bhbj48L3RleHQ+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTYuNjk4MTkxNTI3MDE2NDYyOjE2LjIzNjkxNDk5OTk5OTk4Mi0tPg==",
"blocks": [
{
"opcode": "coordBlock",
"blockType": "reporter",
"text": "[c1][c2][c3][c4][c5][c6]",
"arguments": {
"c1": {
"type": "number",
"defaultValue": "0"
},
"c2": {
"type": "number",
"defaultValue": "0"
},
"c3": {
"type": "number",
"defaultValue": "0"
},
"c4": {
"type": "number",
"defaultValue": "0"
},
"c5": {
"type": "number",
"defaultValue": "0"
},
"c6": {
"type": "number",
"defaultValue": "0"
}
}
},
{
"opcode": "precachetextures",
"blockType": "command",
"text": "precache texture [uri] clamp the texture? [clamp]",
"arguments": {
"uri": {
"type": "string",
"defaultValue": "uri here"
},
"clamp": {
"type": "string",
"menu": 'TFmenu'
}
}
},
{
"opcode": "settargetsw",
"blockType": "command",
"text": "Change the target screen size to width[width] and height[height]",
"arguments": {
"width": {
"type": "number",
"defaultValue": "480"
},
"height": {
"type": "number",
"defaultValue": "360"
}
}
},
{
"opcode": "pendrawspritefromurl",
"blockType": "command",
"text": "Stamp the image from url:[url] at x:[x] y:[y]",
"arguments": {
"url": {
"type": "string",
"defaultValue": "https://en.scratch-wiki.info/w/images/thumb/ScratchCat-Small.png/200px-ScratchCat-Small.png"
},
"x": {
"type": "number",
"defaultValue": "240"
},
"y": {
"type": "number",
"defaultValue": "180"
}
}
},
{
"opcode": "rotateStamp",
"blockType": "command",
"text": "Set stamp rotation to [ANGLE]",
"arguments": {
"ANGLE": {
"type": "angle",
"defaultValue": "90"
}
}
},
{
"opcode": "getstamprotation",
"blockType": "reporter",
"text": "Stamp Rotation",
"arguments": {
"ANGLE": {
"type": "angle",
"defaultValue": "90"
}
}
},
{
"opcode": "setpenstrechandsquash",
"blockType": "command",
"text": "Set stamp width to [width] and height to [height]",
"arguments": {
"width": {
"type": "number",
"defaultValue": "64"
},
"height": {
"type": "number",
"defaultValue": "64"
}
}
},
{
"opcode": "getstampwidth",
"blockType": "reporter",
"text": "Stamp Width",
"arguments": {
}
},
{
"opcode": "getstampheight",
"blockType": "reporter",
"text": "Stamp Height",
"arguments": {
}
},
{
"opcode": "setstampcolor",
"blockType": "command",
"text": "Tint stamp by [color] and transparency[T](0-255)",
"arguments": {
"color": {
"type": "color",
"defaultValue": '#ffffff'
},
"T":{
"type": "number",
"defaultValue": '0'
}
}
},
{
"opcode": "getcostumedata",
"blockType": "reporter",
"text": "Get data uri of costume[costu] in sprite[spr] (0 is stage)",
"arguments": {
"costu": {
"type": "number",
"defaultValue": "0"
},
"spr": {
"type": "number",
"defaultValue": "1"
}
}
},
/*{
"opcode": "getimagefromurl",
"blockType": "reporter",
"text": "Get data uri from url:[url]",
"arguments": {
"url": {
"type": "string",
"defaultValue": "https://en.scratch-wiki.info/w/images/thumb/ScratchCat-Small.png/200px-ScratchCat-Small.png"
}
}
},*/
{
"opcode": "pendrawtexturedtrifromurl",
"blockType": "command",
"text": "Draw a triangle with points at(seperated by commas)[trianglepoints] and the uvs of [triangleuvs] with the image from url:[url]",
"arguments": {
"url": {
"type": "string",
"defaultValue": "https://en.scratch-wiki.info/w/images/thumb/ScratchCat-Small.png/200px-ScratchCat-Small.png"
},
"trianglepoints": {
"type": "string",
"defaultValue": "0,0,10,10,0,10"
},
"triangleuvs": {
"type": "string",
"defaultValue": "0,0,1,1,0,1"
}
}
},
{
"opcode": "settripointcolour",
"blockType": "command",
"text": "Tint point [pointmenu] by [color] and transparency[T](0-255)",
"arguments": {
"pointmenu": {
"type": "string",
"menu": 'pointmenu'
},
"color": {
"type": "color",
"defaultValue": '#ffffff'
},
"T":{
"type": "number",
"defaultValue": '0'
}
}
},
{
"opcode": "gettargetstagewidth",
"blockType": "reporter",
"text": "Target Stage Width",
"arguments": {
}
},
{
"opcode": "gettargetstageheight",
"blockType": "reporter",
"text": "Target Stage Height",
"arguments": {
}
},
{
"opcode": "converttocanvascoords",
"blockType": "reporter",
"text": "Convert [scrcoord] to [coordTypes] units on the axis [coordmenu]",
"arguments": {
"coordmenu": {
"type": "string",
"menu": 'coordMenu'
},
"scrcoord": {
"type": "number",
"defaultValue": '0'
},
"coordTypes": {
"type": "string",
"menu": 'coordTypes'
}
}
},
{
"opcode": "rgbtoSColor",
"blockType": "reporter",
"text": "Convert R[R] G[G] B[B] to Hex",
"arguments": {
"R": {
"type": "number",
"defaultValue": '255'
},
"G": {
"type": "number",
"defaultValue": '255'
},
"B": {
"type": "number",
"defaultValue": '255'
}
}
}
],
"menus": {
"coordMenu": {
"items": ['x', 'y']
},
"coordTypes": {
"items": ['Canvas', 'Scratch']
},
"pointmenu": {
"items": ['1', '2', '3']
},
"TFmenu": {
"items": ['true',"false"]
},
//Dynamic Menus
}
};
}
rgbtoSColor({R,G,B}) {
return (((R*256)+G) *256)+B
}
getstampwidth({}) {
return Penwidth;
}
getstampheight({}) {
return PenHeight;
}
converttocanvascoords({coordmenu,scrcoord,coordTypes}) {
if (coordTypes == 'Canvas')
{
if (coordmenu == "x")
{
return scrcoord + (screenwidth/2)
}
else
{
return (scrcoord*-1) + (screenheight/2)
}
}
else
{
if (coordmenu == "x")
{
return scrcoord - (screenwidth/2)
}
else
{
return (scrcoord*-1) - (screenheight/2)
}
}
}
getstamprotation({}) {
return stamprotation
}
rotateStamp({ANGLE}) {
stamprotation = ANGLE
return "done"
}
pendrawspritefromurl({url,x,y}) {
canvaswidth = canvas.width
canvasheight = canvas.height
var scalemultiplyer = canvaswidth/screenwidth
if(!textures.hasOwnProperty(url)){
textures[url] = loadImageAndCreateTextureInfo(url,'false')
console.log(textures[url])
}
drawImage(textures[url].texture, Penwidth * scalemultiplyer, PenHeight * scalemultiplyer, (x) * scalemultiplyer, (y) * scalemultiplyer,stamprotation - 90);
return "stamped"
}
gettargetstagewidth({}) {
return screenwidth
}
gettargetstageheight({}) {
return screenheight
}
pendrawtexturedtrifromurl({url,trianglepoints,triangleuvs}) {
canvaswidth = canvas.width
canvasheight = canvas.height
var scalemultiplyer = canvaswidth/screenwidth
if(!textures.hasOwnProperty(url)){
textures[url] = loadImageAndCreateTextureInfo(url,'false')
console.log(textures[url])
}
var pointsarray = trianglepoints.split(",");
var pointslen = pointsarray.length;
for (var i = 0; i < pointslen; i++) {
pointsarray[i] = pointsarray[i] * scalemultiplyer;
}
var uvarray = triangleuvs.split(",");
drawTexturedTri(textures[url].texture, pointsarray, uvarray);
return "stamped"
}
async precachetextures({uri,clamp}) {
if(!textures.hasOwnProperty(uri)){
textures[uri] = await loadImageAndCreateTextureInfo(uri,clamp)
console.log(textures[uri])
}
}
setpenstrechandsquash({width,height}) {
Penwidth = width;
PenHeight = height;
console.log(Penwidth);
console.log(PenHeight);
return "done"
}
settargetsw({width,height}) {
screenwidth = width;
screenheight = height;
return "done"
}
getcostumedata({spr,costu}) {
let fileData = getspritecostume(spr,costu)
console.log(fileData)
return fileData;
}
coordBlock({c1,c2,c3,c4,c5,c6})
{
return c1 + "," + c2 + "," + c3 + "," + c4 + "," + c5 + "," + c6
}
/*data:image/png;base64,' +
async getimagefromurl({url}) {
var result = await getdatafromimageuri(url)
return result
}*/
//scrapped till I figure this out fully!
settripointcolour({pointmenu,color,T}) {
if(pointmenu == "1") {
tricolors[0] = hexToRgb(color).r / 255
tricolors[1] = hexToRgb(color).g / 255
tricolors[2] = hexToRgb(color).b / 255
tricolors[3] = T / 255
}
else if (pointmenu == "2"){
tricolors[4] = hexToRgb(color).r / 255
tricolors[5] = hexToRgb(color).g / 255
tricolors[6] = hexToRgb(color).b / 255
tricolors[7] = T / 255
}
else{
tricolors[8] = hexToRgb(color).r / 255
tricolors[9] = hexToRgb(color).g / 255
tricolors[10] = hexToRgb(color).b / 255
tricolors[11] = T / 255
}
return "done"
}
setstampcolor({color,T}) {
console.log(hexToRgb(color))
let convertr = hexToRgb(color).r / 255
let convertg = hexToRgb(color).g / 255
let convertb = hexToRgb(color).b / 255
let converta = T / 255
quadcolors[0] = convertr
quadcolors[1] = convertg
quadcolors[2] = convertb
quadcolors[3] = converta
quadcolors[4] = convertr
quadcolors[5] = convertg
quadcolors[6] = convertb
quadcolors[7] = converta
quadcolors[8] = convertr
quadcolors[9] = convertg
quadcolors[10] = convertb
quadcolors[11] = converta
quadcolors[12] = convertr
quadcolors[13] = convertg
quadcolors[14] = convertb
quadcolors[15] = converta
quadcolors[16] = convertr
quadcolors[17] = convertg
quadcolors[18] = convertb
quadcolors[19] = converta
quadcolors[20] = convertr
quadcolors[21] = convertg
quadcolors[22] = convertb
quadcolors[23] = converta
return "done"
}
}
function hexToRgb(hex) {
return {
r: Math.floor(hex/65536),
g: Math.floor(hex/256)%256,
b: hex%256
}
}
function getdatafromimageuri(url)
{
return getDataUri(url,function(b){
return b;
});
}
function getspritecostume(t,c)
{
let ps_sp=vm.runtime.targets[t];
let ps_cs=ps_sp.sprite.costumes[c - 1].asset.encodeDataURI();
console.log(ps_cs)
return ps_cs
}
Scratch.extensions.register(new BetterPen());
})(Scratch);
(function (Scratch) {
// var Icon;
// TurboLoader.extension(async plugin => {
// Icon=await plugin.imageAsset("icon.svg");
// plugin.set(Utilities) //Register extension with loader
// plugin.requires("TinyFramework") //import a library
// plugin.blocks({
// utl2_showText: { status: "replaced,SimplyText" }
// })
// plugin.mode="legacy" //support projects that used the old legacy version. NEVER USE IN NEW PLUGINS TO PREVENT CONFLICTS.
// })
/*
Based on SheepTester's Utilities plugin.
Tested on TurboWarp.
Made by LSTV | License MIT
Version: ---
First update: 1/12/2021
THIS IS A TEMPORARY LEGACY RELEASE! A new version will come out soon!
*/
/* ICONS: https://fontawesome.com/license */
function Utilities() {
return class {
constructor(runtime) {
this.runtime = runtime
}
getInfo() {
return {
"id": "utilities",
"name": "UtilitiesV2",
"color1": '#8BC34A',
"color2": '#7CB342',
"color3": '#689F38',
// "menuIconURI":Icon,
"blocks": [{
"opcode": 'isExactly',
"blockType": "Boolean",
"text": 'is [A] exactly [B]?',
"arguments": {
"A": {
"type": "string",
"defaultValue": 'apple'
},
"B": {
"type": "string",
"defaultValue": 'APPLE'
}
}
},
{
"opcode": 'isLessOrEqual',
"blockType": "Boolean",
"text": '[A] <= [B]',
"arguments": {
"A": {
"type": "number"
},
"B": {
"type": "number",
"defaultValue": 50
}
}
},
{
"opcode": 'isMoreOrEqual',
"blockType": "Boolean",
"text": '[A] >= [B]',
"arguments": {
"A": {
"type": "number"
},
"B": {
"type": "number",
"defaultValue": 50
}
}
},
{
"opcode": 'trueBlock',
"blockType": "Boolean",
"text": 'true'
},
{
"opcode": 'falseBlock',
"blockType": "Boolean",
"text": 'false'
},
{
"opcode": 'exponent',
"blockType": "reporter",
"text": '[A] ^ [B]',
"arguments": {
"A": {
"type": "number"
},
"B": {
"type": "number"
}
}
},
{
"opcode": 'pi',
"blockType": "reporter",
"text": 'pi'
},
{
"opcode": 'j2_btoa',
"blockType": "reporter",
"text": 'text [T] to base64',
"arguments": {
"T": {
"type": "string"
}
}
},
{
"opcode": 'j2_atob',
"blockType": "reporter",
"text": 'base64 [T] to text',
"arguments": {
"T": {
"type": "string"
}
}
},
{
"opcode": 'ternaryOperator',
"blockType": "reporter",
"text": 'if [A] then [B] else [C]',
"arguments": {
"A": {
"type": "Boolean"
},
"B": {
"type": "string",
"defaultValue": 'banana'
},
"C": {
"type": "string",
"defaultValue": 'apple'
}
}
},
{
"opcode": 'letters',
"blockType": "reporter",
"text": '[STRING] substring [START] to [END]',
"arguments": {
"START": {
"type": "number",
"defaultValue": 5
},
"END": {
"type": "number",
"defaultValue": 7
},
"STRING": {
"type": "string",
"defaultValue": 'red apple'
}
}
},
{
"opcode": 'currentMillisecond',
"blockType": "reporter",
"text": 'current millisecond'
},
{
"opcode": 'fetchFrom',
"blockType": "reporter",
"text": 'get content from [URL]',
"arguments": {
"URL": {
"type": "string",
"defaultValue": 'https://translate-service.scratch.mit.edu/translate?language=eo&text=hello'
}
}
},
{
"opcode": 'parseJSON',
"blockType": "reporter",
"text": '[PATH] of [JSON_STRING]',
"arguments": {
"PATH": {
"type": "string",
"defaultValue": 'fruit/apples'
},
"JSON_STRING": {
"type": "string",
"defaultValue": '{"fruit": {"apples": 2, "bananas": 3}, "total_fruit": 5}'
}
}
},
{
"opcode": 'utl2_isValidJson',
"blockType": "Boolean",
"text": 'is valid json [json]',
"arguments": {
"json": {
"type": "string",
"defaultValue": '{}'
}
}
},
{
"opcode": 'stringToBoolean',
"blockType": "Boolean",
"text": '[STRING]',
"arguments": {
"STRING": {
"type": "string",
"defaultValue": 'true'
}
}
},
{
"opcode": 'justString',
"blockType": "reporter",
"text": '[STRING]',
"arguments": {
"STRING": {
"type": "string",
"defaultValue": 'string'
}
}
},
{
"opcode": 'evalString',
"blockType": "reporter",
"text": 'eval [STRING]',
"arguments": {
"STRING": {
"type": "string",
"defaultValue": 'string'
}
}
},
{
"opcode": 'j2_console',
"blockType": "command",
"text": 'console [METHOD] [STRING]',
"arguments": {
STRING: {
type: "string",
defaultValue: 'Hello World'
},
METHOD:{
type:"string",
menu:"console",
defaultValue:"log"
}
}
},
{
"opcode": 'getCurrentURL',
"blockType": "reporter",
"text": 'getProjectLocationURL'
},
{
"opcode": 'temporaryVariableSet',
"blockType": "command",
"text": 'setVariable [eventType] to [STRING]',
"arguments": {
"STRING": {
"type": "string",
"defaultValue": 'apple'
},
"eventType": {
"type": "string",
"defaultValue": '1'
}
},
},
{
"opcode": 'temporaryVariableGet',
"blockType": "reporter",
"text": 'getVariable [eventType]',
"arguments": {
"eventType": {
"type": "string",
"defaultValue": '1'
}
}
},
{
"opcode": 'regexReplace',
"blockType": "reporter",
"text": 'replace [STRING] using the rule [REGEX] with [NEWSTRING]',
"arguments": {
"STRING": {
"type": "string",
"defaultValue": 'bananas are awesome. i like bananas.'
},
"REGEX": {
"type": "string",
"defaultValue": 'banana'
},
"NEWSTRING": {
"type": "string",
"defaultValue": 'apple'
}
}
},
{
"opcode": 'utl2setCursor',
"blockType": "command",
"text": 'set cursor [STRING]',
"arguments": {
"STRING": {
"type": "string",
"defaultValue": 'none'
}
}
},
{
"opcode": 'utl2loadCustomCSS',
"blockType": "command",
"text": 'custom CSS [STRING]',
"arguments": {
"STRING": {
"type": "string",
"defaultValue": 'body{cursor:auto}'
}
}
},
{
"opcode": 'utl2eval',
"blockType": "command",
"text": 'JS [STRING]',
"arguments": {
"STRING": {
"type": "string",
"defaultValue": 'alert("hello world");'
}
}
},
{
"opcode": 'utl2evalerror',
"blockType": "reporter",
"text": 'last eval/js error',
"arguments": {}
},
{
"opcode": 'requestOpenFile',
"blockType": "command",
"text": 'openFileDialog | title: [A] CSS (optional): [B] | (as text) | accept files: [ALW]',
"blockIconURI": icon_openfile,
"arguments": {
"A": {
"type": "string",
"defaultValue": 'Open file'
},
"B": {
"type": "string",
"defaultValue": ''
},
"ALW": {
"type": "string",
"defaultValue": ''
}
}
},
{
"opcode": 'openFile',
"blockIconURI": icon_openfile,
"blockType": "command",
"text": 'openFile | (as text) | accept files: [ALW]',
"arguments": {
"ALW": {
"type": "string",
"defaultValue": ''
}
}
},
{
"opcode": 'openFileData',
"blockIconURI": icon_openfile,
"blockType": "command",
"text": 'openFile | (as data URI) | accept files: [ALW]',
"arguments": {
"ALW": {
"type": "string",
"defaultValue": 'image/*'
}
}
},
{
"opcode": 'u2_fileOpenResult',
"blockType": "reporter",
"text": 'fileResult',
"arguments": {}
},
{
"opcode": 'u2_fileName',
"blockType": "reporter",
"text": 'fileName',
"arguments": {}
},
{
"opcode": 'utl2downloadFile',
"blockIconURI": icon_download,
"blockType": "command",
"text": 'downloadFile | text: [text] filename: [filename]',
"arguments": {
"text": {
"type": "string",
"defaultValue": 'hello world'
},
"filename": {
"type": "string",
"defaultValue": 'hi.txt'
}
}
},
{
"opcode": 'utl2changeUsername',
"blockType": "command",
"text": 'set username to [username]',
"arguments": {
"username": {
"type": "string",
"defaultValue": 'Jon'
}
}
},
{
"opcode": 'utl2_greenFlag',
"blockType": "command",
"text": 'greenFlag',
"arguments": {}
},
"---",
"---",
{
"opcode": 'utl2_setSVG',
"blockType": "command",
"text": 'EXPERIMENTAL: set svg for sprite n. [t] costume n. [c] svg [svg]',
"arguments": {
"t": {
"type": "string",
"defaultValue": '1'
},
"c": {
"type": "string",
"defaultValue": '0'
},
"svg": {
"type": "string",
"defaultValue": '<svg></svg>'
}
}
},
{
"opcode": 'utl2_loadIMG',
"blockType": "command",
"text": 'EXPERIMENTAL: set image for sprite n. [t] costume n. [c] from data URI/link [uri] size [size] blur effect [blurfx]',
"arguments": {
"t": {
"type": "string",
"defaultValue": '1'
},
"c": {
"type": "string",
"defaultValue": '0'
},
"uri": {
"type": "string",
"defaultValue": 'uri'
},
"size": {
"type": "string",
"defaultValue": '150'
},
"blurfx": {
"type": "string",
"defaultValue": '0'
}
}
},
{
"opcode": 'utl2_showText',
"blockType": "command",
"text": 'EXPERIMENTAL: render text on sprite n. [t] costume index [c] text [text] color [co] size [sz] font family [f] | custom font (read http://i--i.cf/l/s1) [cf]',
"arguments": {
"t": {
"type": "string",
"defaultValue": '1'
},
"c": {
"type": "string",
"defaultValue": '0'
},
"text": {
"type": "string",
"defaultValue": 'Hello'
},
"co": {
"type": "string",
"defaultValue": '#000000'
},
"sz": {
"type": "string",
"defaultValue": '40'
},
"f": {
"type": "string",
"defaultValue": 'Sans Serif'
},
"cf": {
"type": "string",
"defaultValue": 'none'
}
}
},
"---",
{
"opcode": 'consoleLogString',
"blockType": "command",
"text": 'LEGACY: consoleLog [STRING]',
"arguments": {
"STRING": {
"type": "string",
"defaultValue": 'hello world'
}
}
},
],
"menus": {
"tvarsMenu": [{
"text": "Variable1",
value: 1
}, {
"text": "Variable2",
value: 2
}, {
"text": "Variable3",
value: 3
}, {
"text": "Variable4",
value: 4
}],
console:[{text:"log",value:"log"},{text:"warn",value:"warn"},{text:"error",value:"error"},{text:"verbose",value:"debug"}]
}
};
}
isExactly({ A, B }) {
return A === B;
}
isLessOrEqual({ A, B }) {
return A <= B;
}
isMoreOrEqual({ A, B }) {
return A >= B;
}
trueBlock() {
return true;
}
falseBlock() {
return false;
}
exponent({ A, B }) {
return Math.pow(A, B);
}
pi() {
return Math.PI;
}
getCurrentURL() {
return geturl;
}
j2_btoa({T}){
try{return btoa(T)}catch(e){return e}
}
j2_atob({T}){
try{return atob(T)}catch(e){return e}
}
ternaryOperator({ A, B, C }) {
return A ? B : C;
}
letters({ STRING, START, END }) {
return STRING.slice(Math.max(1, START) - 1, Math.min(STRING.length, END));
}
currentMillisecond() {
return Date.now() % 1000;
}
fetchFrom({ URL }) {
return fetch(URL).then(res => res.text()).catch(err => '');
}
parseJSON({ PATH, JSON_STRING }) {
try {
const path = PATH.toString().split('/').map(prop => decodeURIComponent(prop));
if (path[0] === '') path.splice(0, 1);
if (path[path.length - 1] === '') path.splice(-1, 1);
let json;
try {
json = JSON.parse(' ' + JSON_STRING);
} catch (e) {
return e.message;
}
path.forEach(prop => json = json[prop]);
if (json === null) return 'null';
else if (json === undefined) return '';
else if (typeof json === 'object') return JSON.stringify(json);
else return json.toString();
} catch (err) {
return '';
}
}
stringToBoolean({ STRING }) {
return STRING;
}
justString({ STRING }) {
return STRING;
}
evalString({ STRING }) {
try {
var utl2_theInstructions = STRING;
var F = new Function(utl2_theInstructions);
return (F());
} catch (e) { utl2_evalerr = e; return utl2_evalerr; }
}
consoleLogString({ STRING }) {
console.log(STRING);
}
j2_console({STRING,METHOD}){
console?.[METHOD]?.(STRING)
}
temporaryVariableSet({ STRING, eventType }) {
if (eventType == 1) {
temp_var_1 = STRING;
} else if (eventType == 2) {
temp_var_2 = STRING;
} else if (eventType == 3) {
temp_var_3 = STRING;
} else {
temp_var_4 = STRING;
}
}
temporaryVariableGet({ eventType }) {
return temp_var_1;
if (eventType == 1) {
return temp_var_1;
} else if (eventType == 2) {
return temp_var_2;
} else if (eventType == 3) {
return temp_var_3;
} else {
return temp_var_4;
}
return "error";
}
regexReplace({ STRING, REGEX, NEWSTRING }) {
return STRING.toString().replace(new RegExp(REGEX, 'gi'), NEWSTRING);
}
utl2setCursor({ STRING }) {
document.body.style.cursor = STRING;
}
utl2loadCustomCSS({ STRING }) {
utl2CustomCSS.innerHTML = STRING;
}
requestOpenFile({ A, B, ALW }) {
u2_el("u2_open_fu").accept = ALW;
u2_el("u2_open_title").innerHTML = A;
if (B.includes("u2_open_box")) { utl2OwnCSS.innerHTML = B; } else { utl2OwnCSS.innerHTML = u2_open_dialog_defaultcss; }
u2_el("u2_fileform").reset(); u2_el("win-filter").style.display = "flex"; u2_el("u2_open_box").style.display = "flex";
}
u2_fileOpenResult() {
return u2_file_loaded;
}
u2_fileName() {
try {
return decodeURI(u2_file_name);
} catch (e) {
return u2_file_name;
}
}
openFile({ ALW }) {
u2_el("u2_open_fu").accept = ALW;
u2_el("u2_open_fu").click();
}
openFileData({ ALW }) {
u2_el("u2_data_open_fu").accept = ALW;
u2_el("u2_data_open_fu").click();
}
utl2eval({ STRING }) {
try {
eval(STRING);
} catch (e) { utl2_evalerr = e }
}
utl2downloadFile({ filename, text }) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
utl2changeUsername({ username }) {
this.runtime.ioDevices.userData._username = username;
}
utl2_greenFlag({ username }) {
this.runtime["greenFlag"]()
}
utl2_isValidJson({ json }) {
try { var validatethisjson = JSON.parse(json); return true; } catch (e) { return false; }
}
utl2_setSVG({ t, c, svg }) {
try {
properSetSVG(t, c, svg);
} catch (e) { console.error(e) }
}
utl2_showText({ t, c, text, co, f, cf, sz }) {
try {
var cfont = "";
if (cf === "none") { } else { cfont = "<style>" + cf + "</style>" }
properSetSVG(t, c, `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="608.54883" height="27.375" viewBox="0,0,608.54883,27.375">
<g transform="translate(-15.98013,-164.26875)">
<g data-paper-data="{&quot;isPaintingLayer&quot;:true}" fill="`+ co + `" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="Sans Serif" font-weight="normal" font-size="40" text-anchor="start" style="mix-blend-mode: normal">` + cfont + `
<text transform="translate(16.23013,185.76875) scale(0.5,0.5)" font-size="`+ sz + `" xml:space="preserve" fill="` + co + `" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="` + f + `" text-anchor="start" style="mix-blend-mode: normal">
<tspan x="0" dy="0">`+ text + `</tspan></text></g></g></svg>`);
} catch (e) { console.error(e) }
}
utl2_loadIMG({ t, c, uri, size, blurfx }) {
try {
properSetSVG(t, c, `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0,0,11,11" width="90" height="90"><defs><filter id="f1" x="0" y="0"><feGaussianBlur in="SourceGraphic" stdDeviation="` + blurfx + `"/></filter></defs><g><image filter="url(#f1)" width="` + size + `" height="` + size + `" transform="scale(1,1)" xlink:href="` + uri + `"/></g></svg>`);
} catch (e) { console.error(e) }
}
utl2evalerror() {
return utl2_evalerr;
}
}
}
var icon_download = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiB2aWV3Qm94PSIwLDAsNTEyLDUxMiI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTY0LDc2KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsPSIjZmZmZmZmIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTI4MCwtNzZoODBjMTMuMywwIDI0LDEwLjcgMjQsMjR2MTY4aDg3LjdjMTcuOCwwIDI2LjcsMjEuNSAxNC4xLDM0LjFsLTE1Mi4xLDE1Mi4yYy03LjUsNy41IC0xOS44LDcuNSAtMjcuMywwbC0xNTIuMywtMTUyLjJjLTEyLjYsLTEyLjYgLTMuNywtMzQuMSAxNC4xLC0zNC4xaDg3Ljh2LTE2OGMwLC0xMy4zIDEwLjcsLTI0IDI0LC0yNHpNNTc2LDMwMHYxMTJjMCwxMy4zIC0xMC43LDI0IC0yNCwyNGgtNDY0Yy0xMy4zLDAgLTI0LC0xMC43IC0yNCwtMjR2LTExMmMwLC0xMy4zIDEwLjcsLTI0IDI0LC0yNGgxNDYuN2w0OSw0OWMyMC4xLDIwLjEgNTIuNSwyMC4xIDcyLjYsMGw0OSwtNDloMTQ2LjdjMTMuMywwIDI0LDEwLjcgMjQsMjR6TTQ1MiwzODhjMCwtMTEgLTksLTIwIC0yMCwtMjBjLTExLDAgLTIwLDkgLTIwLDIwYzAsMTEgOSwyMCAyMCwyMGMxMSwwIDIwLC05IDIwLC0yMHpNNTE2LDM4OGMwLC0xMSAtOSwtMjAgLTIwLC0yMGMtMTEsMCAtMjAsOSAtMjAsMjBjMCwxMSA5LDIwIDIwLDIwYzExLDAgMjAsLTkgMjAsLTIweiIvPjwvZz48L2c+PC9zdmc+",
icon_openfile = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzODQiIGhlaWdodD0iNTEyIiB2aWV3Qm94PSIwLDAsMzg0LDUxMiI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEyOCw3NikiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0iI2ZmZmZmZiIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0zNzYsODRoMTM2djMyOGMwLDEzLjMgLTEwLjcsMjQgLTI0LDI0aC0zMzZjLTEzLjMsMCAtMjQsLTEwLjcgLTI0LC0yNHYtNDY0YzAsLTEzLjMgMTAuNywtMjQgMjQsLTI0aDIwMHYxMzZjMCwxMy4yIDEwLjgsMjQgMjQsMjR6TTQyOC40MywyNDguNjVsLTk2LjQyLC05NS43Yy02LjY1LC02LjYxIC0xNy4zOSwtNi42MSAtMjQuMDQsMGwtOTYuNDIsOTUuN2MtMTAuMTQsMTAuMDcgLTMuMDEsMjcuMzYgMTEuMjcsMjcuMzZoNjUuMTh2ODBjMCw4Ljg0IDcuMTYsMTYgMTYsMTZoMzJjOC44NCwwIDE2LC03LjE2IDE2LC0xNnYtODBoNjUuMThjMTQuMjgsMCAyMS40LC0xNy4yOSAxMS4yNSwtMjcuMzZ6TTUxMiw0NS45djYuMWgtMTI4di0xMjhoNi4xYzYuNCwwIDEyLjUsMi41IDE3LDdsOTcuOSw5OGM0LjUsNC41IDcsMTAuNiA3LDE2Ljl6Ii8+PC9nPjwvZz48L3N2Zz4="
function u2_el(id) { return document.getElementById(id); }
var u2_loadJS = function (a, e, n) { var t = document.createElement("script"); t.src = a, t.onload = e, t.onreadystatechange = e, n.appendChild(t) };
var temp_var_1 = '',
temp_var_2 = '',
temp_var_3 = '',
temp_var_4 = '',
geturl = window.location.href,
utl2_spritescount,
isSandboxed,
utl2CustomCSS = document.createElement("style"),
utl2OwnCSS = document.createElement("style"),
utl2OpenFileDialog = document.createElement("div"),
utl2_evalerr = "",
u2_file_loaded = "Nothing opened yet",
u2_file_name = "Nothing opened yet";
utl2CustomCSS.id = "u2customCSS";
utl2OpenFileDialog.id = "u2_open_filter";
utl2OpenFileDialog.innerHTML = `
<div class="win-filter" id="win-filter">
<div id="u2_open_box" class="u2_centeredVH u2_centeredH"><div>
<h2 id="u2_open_title"></h2><br>
<form id="u2_fileform"><input id="u2_open_fu" type='file' /><input id="u2_data_open_fu" type='file' /></form>
<label for="u2_open_fu" class="u2_open_csUpload">Open file</label>&nbsp;&nbsp;<button onclick='u2_el("win-filter").style.display="none";u2_el("u2_open_box").style.display="none";'>Cancel</button><br>
</div></div>`;
var u2_open_dialog_defaultcss = `
h1,h2,h3,h4{padding:0px;margin:0px;}
input[type="file"] {display: none;}
.u2_centeredVH{text-align:center;display:flex;justify-content:center;align-items:center;}
.u2_open_csUpload, button{border:none;font-size:10px;cursor:pointer;padding:10px;border-radius:10px;background:#2050ee;color:white;}
#u2_open_box{z-index:55;background:#FFF;border:2px #666 solid;border-radius:15px;padding:15px;display:none;color:#000;}
#win-filter{z-index:50;position:fixed;top:0px;bottom:0px;left:0px;right:0px;background:rgba(0,0,0,0.5);display:none;justify-content:center;align-items:center;}`;
utl2OwnCSS.innerHTML = u2_open_dialog_defaultcss;
document.body.appendChild(utl2CustomCSS);
document.body.appendChild(utl2OwnCSS);
document.body.appendChild(utl2OpenFileDialog);
if (typeof window === "undefined" || !window.vm) {
isSandboxed = true;
} else {
isSandboxed = false;
}
function properSetSVG(t, c, svg) {
const ps_sp = vm.runtime.targets[t];
const ps_cs = ps_sp.sprite.costumes[c];
vm.runtime.renderer.updateSVGSkin(ps_cs.skinId, svg);
ps_cs.bitmapResolution = 1;
vm.emitTargetsUpdate();
}
function u2_dataFileLoad(e) {
var a, n = e.target.files, t = new FileReader; t.onload = (a = n[0], function (e) {
u2_file_loaded = e.target.result; u2_file_name = escape(a.name); u2_el("u2_fileform").reset();
}), t.readAsDataURL(n[0])
} u2_el("u2_data_open_fu").addEventListener("change", u2_dataFileLoad, !1);
function u2_fileLoad(e) {
var a, n = e.target.files, t = new FileReader; t.onload = (a = n[0], function (e) {
u2_file_loaded = e.target.result; u2_file_name = escape(a.name); u2_el("u2_fileform").reset(); u2_el("win-filter").style.display = "none"; u2_el("u2_open_box").style.display = "none";
}), t.readAsText(n[0])
} u2_el("u2_open_fu").addEventListener("change", u2_fileLoad, !1);
Scratch.extensions.register(new (Utilities())());
})(Scratch);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment