您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
none
当前为
// ==UserScript== // @name scratch extension loader // @version 2 // @description none // @run-at document-start // @tag lib loader // @author rssaromeo // @license GPLv3 // @match *://*/* // @sandbox dom // @icon data:image/png;base64,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 // @require https://update.greasyfork.org/scripts/491829/1356221/tampermonkey%20storage%20proxy.js // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant unsafeWindow // @namespace https://greasyfork.org/users/1184528 // ==/UserScript== // add get turbomode state // add way to toggle on and off extensions // how to change the properties of a menu baised on the value of another menu // // add set var // add get var // add return project id ;(async () => { var menus = {} var extensionerrors = [] unsafeWindow.ee = extensionerrors var projectid = location.href.match(/(?<=\/)[0-9]+(?=\/)/)?.[0] || "local" // debugger var sp = new storageproxy("extensionoptions") // debugger var bt = { cmd: "command", ret: "reporter", hat: "hat", bool: "Boolean", } var inp = { int: "number", num: "number", str: "string", } var menufuncs = class { menu_varnames(targetid) { try { var name = vm.runtime.targets.find((e) => e.id == targetid) .sprite.name if (!gettarget(name).runtime.ioDevices.cloud.stage) return [" "] var globalvars = Object.values( gettarget(name).runtime.ioDevices.cloud.stage.variables ) var localvars = Object.values(gettarget(name).variables) globalvars = globalvars.filter((e) => e.type == "") localvars = localvars.filter((e) => e.type == "") // log(globalvars, localvars) var arr = [ ...localvars.map((e) => ({ text: "__local " + e.name, value: JSON.stringify([name, e.name]), })), ...globalvars.map((e) => ({ text: "__global " + e.name, value: JSON.stringify([undefined, e.name]), })), ] return arr.length ? arr : [" "] } catch (e) { error("error from menu_varnames", e) return [" "] } } menu_listnames(targetid) { try { // if (!gettarget(name)) return [" "] var name = vm.runtime.targets.find((e) => e.id == targetid) .sprite.name if (!gettarget(name).runtime.ioDevices.cloud.stage) return [" "] var globalvars = Object.values( gettarget(name).runtime.ioDevices.cloud.stage.variables ) var localvars = Object.values(gettarget(name).variables) globalvars = globalvars.filter((e) => e.type == "list") localvars = localvars.filter((e) => e.type == "list") // log(globalvars, localvars) var arr = [ ...localvars.map((e) => ({ text: "__local " + e.name, value: JSON.stringify([name, e.name]), })), ...globalvars.map((e) => ({ text: "__global " + e.name, value: JSON.stringify([undefined, e.name]), })), ] return arr.length ? arr : [" "] } catch (e) { error("error from menu_listnames", e) return [" "] } } menu_spritelistwithglobal(targetid) { try { var spritenames = Object.values(vm.runtime.targets).map( (e) => e.sprite.name ) var name = Object.values(vm.runtime.targets).find( (e) => e.id == targetid ).sprite.name return [ name, { text: "--- global list ---", value: "" }, ...spritenames.filter((e) => e !== name && e !== "Stage"), ] } catch (e) { error("error from menu_spritelistwithglobal", e) return [" "] } } menu_spritelistwithoutglobal(targetid) { try { var spritenames = Object.values(vm.runtime.targets).map( (e) => e.sprite.name ) var name = Object.values(vm.runtime.targets).find( (e) => e.id == targetid ).sprite.name return [ name, ...spritenames.filter((e) => e !== name && e !== "Stage"), ] } catch (e) { error("error from menu_spritelistwithoutglobal", e) return [" "] } } menu_fullkeylist() { return [ "escape", "enter", "up arrow", "down arrow", "left arrow", "right arrow", "tab", "control", "alt", "shift", "win", "delete", "insert", "home", "end", "page up", "page down", "caps lock", "scroll lock", ...Array.from({ length: 24 }, (_, i) => i + 1).map((e) => ({ text: "F" + e, value: "f" + e, })), { text: "space", value: " " }, ..."~`abcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()-_=+[]\\|;:'\",<.>/?{}", "contextmenu", "mediaplaypause", "audiovolumemute", "audiovolumedown", "audiovolumeup", "launchapplication2", "launchmediaplayer", "mediatracknext", "mediatrackprevious", "Meta", ] } menu_fullkeylistandany() { return ["any", ...this.menu_fullkeylist()] } } var a = loadlib("allfuncs") var enabledextensions = sp.get() ?? { extensionmanagercreatedbyrssaromeo: true, } var extensionclasses = [] // unsafeWindow.extensionclasses = extensionclasses newext( "extension manager", "rssaromeo", class { getlasterror() { return String( extensionerrors[extensionerrors.length - 1]?.message ?? false ) } getlasterrorextensionid() { return String( extensionerrors[extensionerrors.length - 1]?.extensionid ?? false ) } getlasterrorblockid() { return String( extensionerrors[extensionerrors.length - 1]?.blockid ?? false ) } puterrorsintolist({ listname }) { var [sprite, listname] = JSON.parse(listname) scratchlist( listname, extensionerrors.map((e) => JSON.stringify(e)), sprite ) } }, [ newblock( bt.ret, "getlasterrorextensionid", "lasterror: extension id" ), newblock(bt.ret, "getlasterror", "lasterror: message"), newblock(bt.ret, "getlasterrorblockid", "lasterror: block id"), newblock( bt.cmd, "puterrorsintolist", "put errors into list [listname]", [newmenu("listnames", { defaultValue: "" })] ), ] ) loadlib("libloader").savelib("scratchextesnsionmanager", { newmenu, newext, newblock, bt, inp, gettarget, totype, scratch_math, projectid, canvas, scratchvar, scratchlist, }) // unsafeWindow.sp = sp await loadlib("libloader").waitforlib("scratch") await a(canvas).waituntil() var vm = loadlib("scratch").vm // await a(100).wait() loadallextensions() function loadallextensions() { // debugger for (var __class of extensionclasses) { var extensionInstance = new __class( vm.extensionManager.runtime, __class.thisExtensionIsEnabled ) vm.extensionManager._loadedExtensions.set( extensionInstance.getInfo().id, vm.extensionManager._registerInternalExtension( extensionInstance ) ) } } function newblock(blockType, opcode, text, args) { var arguments = Object.fromEntries( [...(text.match(/(?<=\[)\w+(?=\])/g) || [])].map((_, i) => [ _, typeof args?.[i] == "object" ? { type: args?.[i]?.type || args?.[i] || inp.str, disableMonitor: false, // defaultValue: false, // filter: [Scratch.TargetType.SPRITE], // filter: [Scratch.TargetType.STAGE], // isTerminal: false, // shouldRestartExistingThreads: true, // isEdgeActivated: false, ...(0 in args[i] && 1 in args[i] ? { type: args[i][0] ?? inp.str, defaultValue: args[i][1], } : args[i]), } : { type: args?.[i]?.type || args?.[i] || inp.str, // defaultValue: false, disableMonitor: false, }, ]) ) // log(arguments) return { hideFromPalette: false, blockType, opcode, text, arguments, } } function newmenu(name, opts = {}) { var data = { acceptReporters: true, items: "menu_" + name, ...opts, } menus[name] = data return { menu: name, ...opts } } function newext( name, username, _class, blockinfo, blockcolor = "#777777", menuicon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDY0IDY0IiB3aWR0aD0iNjQiIGhlaWdodD0iNjQiPgoJPHRpdGxlPk5ldyBQcm9qZWN0PC90aXRsZT4KCTxkZWZzPgoJCTxpbWFnZSAgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiBpZD0iaW1nMSIgaHJlZj0iZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFFQUFBQUJBQkFNQUFBQllSMnp0QUFBQUFYTlNSMElCMmNrc2Z3QUFBQ1JRVEZSRkFBQUFBT05sLy8vL0FPTmwvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL0I2Y0NhQUFBQUF4MFVrNVRBUDhBQUFJRUR4TUJBd1VIM0Q5UFB3QUFBRVZKUkVGVWVKeGpaRUFHakFKQTR2OEhGS0ZSQmFNS1JoWFFRb0VnQTM0d3FvQllCVXJJUEhBb2c4T2JCQVhHWU9yOXFJSkJyMEJaQ2ErQ3RMMmhCd2RGN2g0WkNnRFBCSCtKaSsyWXRnQUFBQUJKUlU1RXJrSmdnZz09Ii8+Cgk8L2RlZnM+Cgk8c3R5bGU+Cgk8L3N0eWxlPgoJPHVzZSBpZD0iQmFja2dyb3VuZCIgaHJlZj0iI2ltZzEiIHg9IjAiIHk9IjAiLz4KPC9zdmc+", blockimg = "" ) { var bg = "#282828" if (blockcolor[0] != "#") blockcolor = "#" + blockcolor blockinfo = { color1: /https?:\/\/turbowarp\.org/.test(location.href) ? blockcolor : bg, color2: bg, color3: blockcolor, menuIconURI: menuicon, blockIconURI: blockimg, blocks: blockinfo, } blockinfo.id = name.replaceAll(/[^a-z]+/gi, "") + "createdby" + username blockinfo.name = name.replaceAll(/ /gi, " ") //+ " - by " + username blockinfo.menus = menus // warn(enabledextensions) GM_registerMenuCommand( (enabledextensions[blockinfo.id] ? "enabled" : "dissabled") + (": " + blockinfo.name), () => { // warn(enabledextensions[blockinfo.id]) enabledextensions[blockinfo.id] = !enabledextensions[blockinfo.id] // warn(enabledextensions[blockinfo.id]) } ) // log("menus", menus.spritelistwithoutglobal.items) blockinfo.blocks.unshift( newblock( bt.bool, "thisextensionexists", "the extension {" + blockinfo.name + "} is enabled" ) ) function Classes(bases) { class Bases { constructor() { bases.forEach((base) => Object.assign(this, new base())) } } bases.forEach((base) => { Object.getOwnPropertyNames(base.prototype) .filter((prop) => prop != "constructor") .forEach( (prop) => (Bases.prototype[prop] = base.prototype[prop]) ) }) return Bases } var enabled = enabledextensions[blockinfo.id] if (!enabled) { const properties = Object.getOwnPropertyNames(_class.prototype) for (const property of properties) { if (typeof _class.prototype[property] === "function") { _class.prototype[property] = function () { return false } } } _class.prototype.thisextensionexists = () => { return false } } else { _class.prototype.thisextensionexists = () => { return true } } function tryCatchDecorator(constructor) { const prototype = constructor.prototype const originalPrototype = Object.getPrototypeOf(prototype) for (let key of [ ...Object.getOwnPropertyNames(prototype), ...Object.getOwnPropertyNames(originalPrototype), ]) { if (typeof prototype[key] === "function") { let originalMethod = prototype[key] prototype[key] = function (...args) { try { return originalMethod.apply(this, args) } catch (e) { extensionerrors.push({ extensionid: blockinfo.id, blockid: key, message: e.message, }) console.error("error from " + key, e) return false } } } } return constructor } // log("blockinfo.blocks", blockinfo.blocks) var __class = tryCatchDecorator( class extends Classes([_class, menufuncs]) { constructor(runtime, enabled) { super(runtime) if (enabled !== undefined) this.thisExtensionIsEnabled = enabled this.runtime = runtime } getInfo() { // log("getInfo", blockinfo) return blockinfo } } ) extensionclasses.push(__class) sp.enabledextensions = enabledextensions } function scratchvar(varname, value, spritename) { if (value !== undefined) { if (gettarget(spritename)?.getvar(varname)) gettarget(spritename).getvar(varname).value = String(value) else { console.warn(`var "${varname}" does not exist`) } } } function totype(inp, type, forced) { //number, string, list, object, json, bool inp = String(inp) try { switch (type) { // case "regex": // try { // return new RegExp(inp) // } catch (e) { // return fail(inp, type, forced) // } case "string": return String(inp) case "number": if (inp == "true") inp = 1 if (inp == "false") inp = 0 if (/^-?[0-9]*\.?[0-9]+$/.test(inp)) return Number(inp) if (inp === "NaN" || inp == "nan") return NaN return fail(inp, type, forced) case "list": if (scratchlist(inp)) return scratchlist(inp) inp = JSON.parse(inp) if (inp.reverse) return inp return fail(inp, type, forced) case "object": inp = JSON.parse(inp) // if (/^[\-0-9]+$/.test(inp) || inp === true || inp === false) // return undefined if ( Object.keys(inp).length !== undefined && inp.length === undefined && !Array.isArray(inp) ) return inp return fail(inp, type, forced) case "bool": if (inp === "1" || inp === "true") return true if (inp === "0" || inp === "false") return false return fail(inp, type) // case "json": // if ( // totype(inp, "object") !== undefined || // totype(inp, "list") !== undefined // ) // return totype(inp, "object") || totype(inp, "list") // else { // fail(inp, type, forced) // } } } catch (s) { return fail(inp, type, forced) } function fail(inp, type, forced) { if (forced) { throw new Error(`"${inp}" must be of type "${type}"`) } else return undefined } } // listen(window, "keydown", (e) => { // var index = vm.runtime.ioDevices.keyboard._keysPressed.indexOf( // e.key.toUpperCase(), // ) // if (index !== -1) { // vm.runtime.ioDevices.keyboard._keysPressed.splice(index, 1) // } // vm.runtime.ioDevices.keyboard._keysPressed.push(e.key.toUpperCase()) // }) // listen(window, "keyup", (e) => { // var index = vm.runtime.ioDevices.keyboard._keysPressed.indexOf( // e.key.toUpperCase(), // ) // if (index !== -1) { // vm.runtime.ioDevices.keyboard._keysPressed.splice(index, 1) // } // }) function gettarget(sprite) { if (sprite) var x = vm.runtime.getSpriteTargetByName(sprite) || vm.runtime.getTargetForStage() else var x = vm.runtime.getTargetForStage() x.getvar = x?.lookupVariableByNameAndType return x } function scratchlist(listname, value, spritename) { //fix regex? if (value === undefined && /^\[\]$/.test(listname)) return JSON.parse(listname) if (value !== undefined) { if (gettarget(spritename)?.getvar(listname, "list")) gettarget(spritename).getvar(listname, "list").value = [ ...value, ] else console.warn(`list "${listname}" does not exist`) } else { return gettarget(spritename)?.getvar(listname, "list")?.value } } function scratch_math(operator, n) { switch (operator) { case "sin": return Math.round(Math.sin((Math.PI * n) / 180) * 1e10) / 1e10 case "cos": return Math.round(Math.cos((Math.PI * n) / 180) * 1e10) / 1e10 case "asin": return (Math.asin(n) * 180) / Math.PI case "acos": return (Math.acos(n) * 180) / Math.PI case "atan": return (Math.atan(n) * 180) / Math.PI case "log": return Math.log(n) / Math.LN10 } return 0 } function canvas() { return ( window?.vm?.runtime?.renderer?.canvas || document.querySelector( "#app > div > div.gui_body-wrapper_-N0sA.box_box_2jjDp > div > div.gui_stage-and-target-wrapper_69KBf.box_box_2jjDp > div.stage-wrapper_stage-wrapper_2bejr.box_box_2jjDp > div.stage-wrapper_stage-canvas-wrapper_3ewmd.box_box_2jjDp > div > div.stage_stage_1fD7k.box_box_2jjDp > div:nth-child(1) > canvas" ) || document.querySelector( "#view > div > div.inner > div:nth-child(2) > div.guiPlayer > div.stage-wrapper_stage-wrapper_2bejr.box_box_2jjDp > div.stage-wrapper_stage-canvas-wrapper_3ewmd.box_box_2jjDp > div > div.stage_stage_1fD7k.box_box_2jjDp > div:nth-child(1) > canvas" ) || document.querySelector( "#app > div > div > div > div.gui_body-wrapper_-N0sA.box_box_2jjDp > div > div.gui_stage-and-target-wrapper_69KBf.box_box_2jjDp > div.stage-wrapper_stage-wrapper_2bejr.box_box_2jjDp > div.stage-wrapper_stage-canvas-wrapper_3ewmd.box_box_2jjDp > div > div.stage_stage_1fD7k.box_box_2jjDp > div:nth-child(1) > canvas" ) || document.querySelector( ".stage_stage_yEvd4 > div:nth-child(1) > canvas:nth-child(1)" ) ) } })()