scratch extension loader

none

当前为 2025-03-07 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name scratch extension loader
  3. // @version 4
  4. // @description none
  5. // @run-at document-start
  6. // @tag lib loader
  7. // @author rssaromeo
  8. // @license GPLv3
  9. // @match *://*/*
  10. // @include *
  11. // @sandbox dom
  12. // @icon data:image/png;base64,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
  13. // @require https://update.greasyfork.org/scripts/491829/1356221/tampermonkey%20storage%20proxy.js
  14. // @grant GM_getValue
  15. // @grant GM_setValue
  16. // @grant GM_registerMenuCommand
  17. // @grant unsafeWindow
  18. // @namespace https://greasyfork.org/users/1184528
  19. // ==/UserScript==
  20.  
  21. // add get turbomode state
  22. // add way to toggle on and off extensions
  23. // how to change the properties of a menu baised on the value of another menu
  24. //
  25. // add set var
  26. // add get var
  27. // add return project id
  28. ;(async () => {
  29. var menus = {}
  30. var extensionerrors = []
  31. unsafeWindow.ee = extensionerrors
  32. var projectid =
  33. location.href.match(/(?<=\/)[0-9]+(?=\/)/)?.[0] || "local"
  34. // debugger
  35. var sp = new storageproxy("extensionoptions")
  36. // debugger
  37. var bt = {
  38. cmd: "command",
  39. ret: "reporter",
  40. hat: "hat",
  41. bool: "Boolean",
  42. }
  43. var inp = {
  44. int: "number",
  45. num: "number",
  46. str: "string",
  47. }
  48. var menufuncs = class {
  49. menu_varnames(targetid) {
  50. try {
  51. var name = vm.runtime.targets.find((e) => e.id == targetid)
  52. .sprite.name
  53. if (!gettarget(name).runtime.ioDevices.cloud.stage)
  54. return [" "]
  55. var globalvars = Object.values(
  56. gettarget(name).runtime.ioDevices.cloud.stage.variables
  57. )
  58. var localvars = Object.values(gettarget(name).variables)
  59. globalvars = globalvars.filter((e) => e.type == "")
  60. localvars = localvars.filter((e) => e.type == "")
  61. // log(globalvars, localvars)
  62. var arr = [
  63. ...localvars.map((e) => ({
  64. text: "__local " + e.name,
  65. value: JSON.stringify([name, e.name]),
  66. })),
  67. ...globalvars.map((e) => ({
  68. text: "__global " + e.name,
  69. value: JSON.stringify([undefined, e.name]),
  70. })),
  71. ]
  72. return arr.length ? arr : [" "]
  73. } catch (e) {
  74. error("error from menu_varnames", e)
  75. return [" "]
  76. }
  77. }
  78. menu_listnames(targetid) {
  79. try {
  80. // if (!gettarget(name)) return [" "]
  81. var name = vm.runtime.targets.find((e) => e.id == targetid)
  82. .sprite.name
  83. if (!gettarget(name).runtime.ioDevices.cloud.stage)
  84. return [" "]
  85. var globalvars = Object.values(
  86. gettarget(name).runtime.ioDevices.cloud.stage.variables
  87. )
  88. var localvars = Object.values(gettarget(name).variables)
  89. globalvars = globalvars.filter((e) => e.type == "list")
  90. localvars = localvars.filter((e) => e.type == "list")
  91. // log(globalvars, localvars)
  92. var arr = [
  93. ...localvars.map((e) => ({
  94. text: "__local " + e.name,
  95. value: JSON.stringify([name, e.name]),
  96. })),
  97. ...globalvars.map((e) => ({
  98. text: "__global " + e.name,
  99. value: JSON.stringify([undefined, e.name]),
  100. })),
  101. ]
  102.  
  103. return arr.length ? arr : [" "]
  104. } catch (e) {
  105. error("error from menu_listnames", e)
  106. return [" "]
  107. }
  108. }
  109. menu_spritelistwithglobal(targetid) {
  110. try {
  111. var spritenames = Object.values(vm.runtime.targets).map(
  112. (e) => e.sprite.name
  113. )
  114. var name = Object.values(vm.runtime.targets).find(
  115. (e) => e.id == targetid
  116. ).sprite.name
  117. return [
  118. name,
  119. { text: "--- global list ---", value: "" },
  120. ...spritenames.filter((e) => e !== name && e !== "Stage"),
  121. ]
  122. } catch (e) {
  123. error("error from menu_spritelistwithglobal", e)
  124. return [" "]
  125. }
  126. }
  127. menu_spritelistwithoutglobal(targetid) {
  128. try {
  129. var spritenames = Object.values(vm.runtime.targets).map(
  130. (e) => e.sprite.name
  131. )
  132. var name = Object.values(vm.runtime.targets).find(
  133. (e) => e.id == targetid
  134. ).sprite.name
  135. return [
  136. name,
  137. ...spritenames.filter((e) => e !== name && e !== "Stage"),
  138. ]
  139. } catch (e) {
  140. error("error from menu_spritelistwithoutglobal", e)
  141. return [" "]
  142. }
  143. }
  144. menu_fullkeylist() {
  145. return [
  146. "escape",
  147. "enter",
  148. "up arrow",
  149. "down arrow",
  150. "left arrow",
  151. "right arrow",
  152. "tab",
  153. "control",
  154. "alt",
  155. "shift",
  156. "win",
  157. "delete",
  158. "insert",
  159. "home",
  160. "end",
  161. "page up",
  162. "page down",
  163. "caps lock",
  164. "scroll lock",
  165. ...Array.from({ length: 24 }, (_, i) => i + 1).map((e) => ({
  166. text: "F" + e,
  167. value: "f" + e,
  168. })),
  169. { text: "space", value: " " },
  170. ..."~`abcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()-_=+[]\\|;:'\",<.>/?{}",
  171. "contextmenu",
  172. "mediaplaypause",
  173. "audiovolumemute",
  174. "audiovolumedown",
  175. "audiovolumeup",
  176. "launchapplication2",
  177. "launchmediaplayer",
  178. "mediatracknext",
  179. "mediatrackprevious",
  180. "Meta",
  181. ]
  182. }
  183. menu_fullkeylistandany() {
  184. return ["any", ...this.menu_fullkeylist()]
  185. }
  186. }
  187. var a = loadlib("newallfuncs")
  188. var enabledextensions = sp.get() ?? {
  189. extensionmanagercreatedbyrssaromeo: true,
  190. }
  191. var extensionclasses = []
  192. // unsafeWindow.extensionclasses = extensionclasses
  193. newext(
  194. "extension manager",
  195. "rssaromeo",
  196. class {
  197. getlasterror() {
  198. return String(
  199. extensionerrors[extensionerrors.length - 1]?.message ??
  200. false
  201. )
  202. }
  203. getlasterrorextensionid() {
  204. return String(
  205. extensionerrors[extensionerrors.length - 1]?.extensionid ??
  206. false
  207. )
  208. }
  209. getlasterrorblockid() {
  210. return String(
  211. extensionerrors[extensionerrors.length - 1]?.blockid ??
  212. false
  213. )
  214. }
  215. puterrorsintolist({ listname }) {
  216. var [sprite, listname] = JSON.parse(listname)
  217. scratchlist(
  218. listname,
  219. extensionerrors.map((e) => JSON.stringify(e)),
  220. sprite
  221. )
  222. }
  223. },
  224. [
  225. newblock(
  226. bt.ret,
  227. "getlasterrorextensionid",
  228. "lasterror: extension id"
  229. ),
  230. newblock(bt.ret, "getlasterror", "lasterror: message"),
  231. newblock(bt.ret, "getlasterrorblockid", "lasterror: block id"),
  232. newblock(
  233. bt.cmd,
  234. "puterrorsintolist",
  235. "put errors into list [listname]",
  236. [newmenu("listnames", { defaultValue: "" })]
  237. ),
  238. ]
  239. )
  240. loadlib("libloader").savelib("scratchextesnsionmanager", {
  241. newmenu,
  242. newext,
  243. newblock,
  244. bt,
  245. inp,
  246. gettarget,
  247. totype,
  248. scratch_math,
  249. projectid,
  250. canvas,
  251. scratchvar,
  252. scratchlist,
  253. })
  254. // unsafeWindow.sp = sp
  255. await loadlib("libloader").waitforlib("scratch")
  256. await a.waituntil(canvas)
  257.  
  258. var vm = loadlib("scratch").vm
  259. loadallextensions()
  260. function loadallextensions() {
  261. // debugger
  262. for (var __class of extensionclasses) {
  263. var extensionInstance = new __class(
  264. vm.extensionManager.runtime,
  265. __class.thisExtensionIsEnabled
  266. )
  267. vm.extensionManager._loadedExtensions.set(
  268. extensionInstance.getInfo().id,
  269. vm.extensionManager._registerInternalExtension(
  270. extensionInstance
  271. )
  272. )
  273. }
  274. }
  275.  
  276. function newblock(blockType, opcode, text, args) {
  277. var arguments = Object.fromEntries(
  278. [...(text.match(/(?<=\[)\w+(?=\])/g) || [])].map((_, i) => [
  279. _,
  280. typeof args?.[i] == "object"
  281. ? {
  282. type: args?.[i]?.type || args?.[i] || inp.str,
  283. disableMonitor: false,
  284. // defaultValue: false,
  285. // filter: [Scratch.TargetType.SPRITE],
  286. // filter: [Scratch.TargetType.STAGE],
  287. // isTerminal: false,
  288. // shouldRestartExistingThreads: true,
  289. // isEdgeActivated: false,
  290. ...(0 in args[i] && 1 in args[i]
  291. ? {
  292. type: args[i][0] ?? inp.str,
  293. defaultValue: args[i][1],
  294. }
  295. : args[i]),
  296. }
  297. : {
  298. type: args?.[i]?.type || args?.[i] || inp.str,
  299. // defaultValue: false,
  300. disableMonitor: false,
  301. },
  302. ])
  303. )
  304. // log(arguments)
  305. return {
  306. hideFromPalette: false,
  307. blockType,
  308. opcode,
  309. text,
  310. arguments,
  311. }
  312. }
  313.  
  314. function newmenu(name, opts = {}) {
  315. var data = {
  316. acceptReporters: true,
  317. items: "menu_" + name,
  318. ...opts,
  319. }
  320. menus[name] = data
  321. return { menu: name, ...opts }
  322. }
  323.  
  324. function newext(
  325. name,
  326. username,
  327. _class,
  328. blockinfo,
  329. blockcolor = "#777777",
  330. menuicon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDY0IDY0IiB3aWR0aD0iNjQiIGhlaWdodD0iNjQiPgoJPHRpdGxlPk5ldyBQcm9qZWN0PC90aXRsZT4KCTxkZWZzPgoJCTxpbWFnZSAgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiBpZD0iaW1nMSIgaHJlZj0iZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFFQUFBQUJBQkFNQUFBQllSMnp0QUFBQUFYTlNSMElCMmNrc2Z3QUFBQ1JRVEZSRkFBQUFBT05sLy8vL0FPTmwvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL0I2Y0NhQUFBQUF4MFVrNVRBUDhBQUFJRUR4TUJBd1VIM0Q5UFB3QUFBRVZKUkVGVWVKeGpaRUFHakFKQTR2OEhGS0ZSQmFNS1JoWFFRb0VnQTM0d3FvQllCVXJJUEhBb2c4T2JCQVhHWU9yOXFJSkJyMEJaQ2ErQ3RMMmhCd2RGN2g0WkNnRFBCSCtKaSsyWXRnQUFBQUJKUlU1RXJrSmdnZz09Ii8+Cgk8L2RlZnM+Cgk8c3R5bGU+Cgk8L3N0eWxlPgoJPHVzZSBpZD0iQmFja2dyb3VuZCIgaHJlZj0iI2ltZzEiIHg9IjAiIHk9IjAiLz4KPC9zdmc+",
  331. blockimg = ""
  332. ) {
  333. var bg = "#282828"
  334. if (blockcolor[0] != "#") blockcolor = "#" + blockcolor
  335. blockinfo = {
  336. color1: /https?:\/\/turbowarp\.org/.test(location.href)
  337. ? blockcolor
  338. : bg,
  339. color2: bg,
  340. color3: blockcolor,
  341.  
  342. menuIconURI: menuicon,
  343. blockIconURI: blockimg,
  344.  
  345. blocks: blockinfo,
  346. }
  347. blockinfo.id =
  348. name.replaceAll(/[^a-z]+/gi, "") + "createdby" + username
  349. blockinfo.name = name.replaceAll(/ /gi, " ") //+ " - by " + username
  350. blockinfo.menus = menus
  351. // warn(enabledextensions)
  352. GM_registerMenuCommand(
  353. (enabledextensions[blockinfo.id] ? "enabled" : "dissabled") +
  354. (": " + blockinfo.name),
  355. () => {
  356. // warn(enabledextensions[blockinfo.id])
  357. enabledextensions[blockinfo.id] =
  358. !enabledextensions[blockinfo.id]
  359. // warn(enabledextensions[blockinfo.id])
  360. }
  361. )
  362. // log("menus", menus.spritelistwithoutglobal.items)
  363. blockinfo.blocks.unshift(
  364. newblock(
  365. bt.bool,
  366. "thisextensionexists",
  367. "the extension {" + blockinfo.name + "} is enabled"
  368. )
  369. )
  370. function Classes(bases) {
  371. class Bases {
  372. constructor() {
  373. bases.forEach((base) => Object.assign(this, new base()))
  374. }
  375. }
  376. bases.forEach((base) => {
  377. Object.getOwnPropertyNames(base.prototype)
  378. .filter((prop) => prop != "constructor")
  379. .forEach(
  380. (prop) => (Bases.prototype[prop] = base.prototype[prop])
  381. )
  382. })
  383. return Bases
  384. }
  385. var enabled = enabledextensions[blockinfo.id]
  386. if (!enabled) {
  387. const properties = Object.getOwnPropertyNames(_class.prototype)
  388. for (const property of properties) {
  389. if (typeof _class.prototype[property] === "function") {
  390. _class.prototype[property] = function () {
  391. return false
  392. }
  393. }
  394. }
  395. _class.prototype.thisextensionexists = () => {
  396. return false
  397. }
  398. } else {
  399. _class.prototype.thisextensionexists = () => {
  400. return true
  401. }
  402. }
  403. function tryCatchDecorator(constructor) {
  404. const prototype = constructor.prototype
  405. const originalPrototype = Object.getPrototypeOf(prototype)
  406.  
  407. for (let key of [
  408. ...Object.getOwnPropertyNames(prototype),
  409. ...Object.getOwnPropertyNames(originalPrototype),
  410. ]) {
  411. if (typeof prototype[key] === "function") {
  412. let originalMethod = prototype[key]
  413. prototype[key] = function (...args) {
  414. try {
  415. return originalMethod.apply(this, args)
  416. } catch (e) {
  417. extensionerrors.push({
  418. extensionid: blockinfo.id,
  419. blockid: key,
  420. message: e.message,
  421. })
  422. console.error("error from " + key, e)
  423. return false
  424. }
  425. }
  426. }
  427. }
  428.  
  429. return constructor
  430. }
  431. // log("blockinfo.blocks", blockinfo.blocks)
  432. var __class = tryCatchDecorator(
  433. class extends Classes([_class, menufuncs]) {
  434. constructor(runtime, enabled) {
  435. super(runtime)
  436. if (enabled !== undefined)
  437. this.thisExtensionIsEnabled = enabled
  438. this.runtime = runtime
  439. }
  440. getInfo() {
  441. // log("getInfo", blockinfo)
  442. return blockinfo
  443. }
  444. }
  445. )
  446. extensionclasses.push(__class)
  447. sp.enabledextensions = enabledextensions
  448. }
  449.  
  450. function scratchvar(varname, value, spritename) {
  451. if (value !== undefined) {
  452. if (gettarget(spritename)?.getvar(varname))
  453. gettarget(spritename).getvar(varname).value = String(value)
  454. else {
  455. console.warn(`var "${varname}" does not exist`)
  456. }
  457. }
  458. }
  459.  
  460. function totype(inp, type, forced) {
  461. //number, string, list, object, json, bool
  462. inp = String(inp)
  463. try {
  464. switch (type) {
  465. // case "regex":
  466. // try {
  467. // return new RegExp(inp)
  468. // } catch (e) {
  469. // return fail(inp, type, forced)
  470. // }
  471. case "string":
  472. return String(inp)
  473. case "number":
  474. if (inp == "true") inp = 1
  475. if (inp == "false") inp = 0
  476. if (/^-?[0-9]*\.?[0-9]+$/.test(inp)) return Number(inp)
  477. if (inp === "NaN" || inp == "nan") return NaN
  478. return fail(inp, type, forced)
  479. case "list":
  480. if (scratchlist(inp)) return scratchlist(inp)
  481. inp = JSON.parse(inp)
  482. if (inp.reverse) return inp
  483. return fail(inp, type, forced)
  484. case "object":
  485. inp = JSON.parse(inp)
  486. // if (/^[\-0-9]+$/.test(inp) || inp === true || inp === false)
  487. // return undefined
  488. if (
  489. Object.keys(inp).length !== undefined &&
  490. inp.length === undefined &&
  491. !Array.isArray(inp)
  492. )
  493. return inp
  494. return fail(inp, type, forced)
  495. case "bool":
  496. if (inp === "1" || inp === "true") return true
  497. if (inp === "0" || inp === "false") return false
  498. return fail(inp, type)
  499. // case "json":
  500. // if (
  501. // totype(inp, "object") !== undefined ||
  502. // totype(inp, "list") !== undefined
  503. // )
  504. // return totype(inp, "object") || totype(inp, "list")
  505. // else {
  506. // fail(inp, type, forced)
  507. // }
  508. }
  509. } catch (s) {
  510. return fail(inp, type, forced)
  511. }
  512.  
  513. function fail(inp, type, forced) {
  514. if (forced) {
  515. throw new Error(`"${inp}" must be of type "${type}"`)
  516. } else return undefined
  517. }
  518. }
  519.  
  520. // listen(window, "keydown", (e) => {
  521. // var index = vm.runtime.ioDevices.keyboard._keysPressed.indexOf(
  522. // e.key.toUpperCase(),
  523. // )
  524. // if (index !== -1) {
  525. // vm.runtime.ioDevices.keyboard._keysPressed.splice(index, 1)
  526. // }
  527. // vm.runtime.ioDevices.keyboard._keysPressed.push(e.key.toUpperCase())
  528. // })
  529. // listen(window, "keyup", (e) => {
  530. // var index = vm.runtime.ioDevices.keyboard._keysPressed.indexOf(
  531. // e.key.toUpperCase(),
  532. // )
  533. // if (index !== -1) {
  534. // vm.runtime.ioDevices.keyboard._keysPressed.splice(index, 1)
  535. // }
  536. // })
  537.  
  538. function gettarget(sprite) {
  539. if (sprite)
  540. var x =
  541. vm.runtime.getSpriteTargetByName(sprite) ||
  542. vm.runtime.getTargetForStage()
  543. else var x = vm.runtime.getTargetForStage()
  544. x.getvar = x?.lookupVariableByNameAndType
  545. return x
  546. }
  547.  
  548. function scratchlist(listname, value, spritename) {
  549. //fix regex?
  550. if (value === undefined && /^\[\]$/.test(listname))
  551. return JSON.parse(listname)
  552. if (value !== undefined) {
  553. if (gettarget(spritename)?.getvar(listname, "list"))
  554. gettarget(spritename).getvar(listname, "list").value = [
  555. ...value,
  556. ]
  557. else console.warn(`list "${listname}" does not exist`)
  558. } else {
  559. return gettarget(spritename)?.getvar(listname, "list")?.value
  560. }
  561. }
  562.  
  563. function scratch_math(operator, n) {
  564. switch (operator) {
  565. case "sin":
  566. return Math.round(Math.sin((Math.PI * n) / 180) * 1e10) / 1e10
  567. case "cos":
  568. return Math.round(Math.cos((Math.PI * n) / 180) * 1e10) / 1e10
  569. case "asin":
  570. return (Math.asin(n) * 180) / Math.PI
  571. case "acos":
  572. return (Math.acos(n) * 180) / Math.PI
  573. case "atan":
  574. return (Math.atan(n) * 180) / Math.PI
  575. case "log":
  576. return Math.log(n) / Math.LN10
  577. }
  578. return 0
  579. }
  580.  
  581. function canvas() {
  582. return (
  583. window?.vm?.runtime?.renderer?.canvas ||
  584. document.querySelector(
  585. "#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"
  586. ) ||
  587. document.querySelector(
  588. "#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"
  589. ) ||
  590. document.querySelector(
  591. "#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"
  592. ) ||
  593. document.querySelector(
  594. ".stage_stage_yEvd4 > div:nth-child(1) > canvas:nth-child(1)"
  595. )
  596. )
  597. }
  598. })()