Definable ModMenu

Definable is a modular ModMenu for Drawaria.Online

  1. // ==UserScript==
  2. // @name Definable ModMenu
  3. // @namespace Definable
  4. // @version 0.2.6
  5. // @description Definable is a modular ModMenu for Drawaria.Online
  6. // @homepage https://drawaria.online/profile/?uid=63196790-c7da-11ec-8266-c399f90709b7
  7. // @author ≺ᴄᴜʙᴇ³≻
  8. // @match https://drawaria.online/
  9. // @match https://drawaria.online/test
  10. // @match https://drawaria.online/room/*
  11. // @icon https://i.ibb.co/8Xb1gXg/Andrew-T-Austin-brightly-lit-tesseract-hypercube.png
  12. // @grant none
  13. // @license GNU GPLv3
  14. // @run-at document-end
  15. // ==/UserScript==
  16.  
  17. (() => {
  18. if (window.location.hostname === "drawaria.online" && window.location.pathname === "/") window.location.assign("/test");
  19.  
  20. String.prototype.toFunction = function () {
  21. return new Function("(" + this + ")()");
  22. };
  23. String.prototype.__invokeAsFunction = function (...args) {
  24. new Function("(" + this + ")()")();
  25. };
  26.  
  27. Function.prototype.callAsWorker = function (...args) {
  28. return new Promise((resolve, reject) => {
  29. const code = `self.onmessage = e => self.postMessage((${this.toString()}).call(null, ...e.data));`,
  30. blob = new Blob([code], { type: "text/javascript" }),
  31. worker = new Worker((window.URL ?? window.webkitURL).createObjectURL(blob));
  32. worker.onmessage = (e) => (resolve(e.data), worker.terminate());
  33. worker.onerror = (e) => (reject(e.message), worker.terminate());
  34. worker.postMessage(args);
  35. });
  36. };
  37.  
  38. const originalElementPrototypeAppend = Element.prototype.append;
  39. Element.prototype.append = function (...elements) {
  40. elements = elements.filter((appendingElement) => {
  41. if (appendingElement instanceof HTMLElement) return true;
  42. return false;
  43. });
  44. originalElementPrototypeAppend.apply(this, elements);
  45. return this;
  46. };
  47.  
  48. const sockets = [];
  49. const originalWebSocketSend = WebSocket.prototype.send;
  50. WebSocket.prototype.send = function (message) {
  51. let socket = this;
  52. if (sockets.indexOf(socket) === -1) {
  53. sockets.push(socket);
  54. originalWebSocketSend.call(socket, "2");
  55. }
  56. socket.addEventListener("close", function () {
  57. const pos = sockets.indexOf(socket);
  58. if (~pos) sockets.splice(pos, 1);
  59. });
  60. try {
  61. const msgObject = JSON.parse(message.replace(/^\d+/, ""));
  62. if (msgObject[0] === "startplay") {
  63. const width = msgObject[6][2];
  64. const height = msgObject[6][3];
  65. const canvas = document.querySelector("#canvas");
  66. canvas._width = width;
  67. canvas._height = height;
  68. }
  69. } catch (error) {}
  70. return originalWebSocketSend.call(socket, message);
  71. };
  72. globalThis["sockets"] = sockets;
  73. // Function.toString().replace(/^\s*\/\/.+$/gm, "").split("\n").filter((s) => s).map((s) => s.trim()).join("");
  74. })();
  75.  
  76. (async function ($name) {
  77. "use strict";
  78. let canvas = document.querySelector("canvas#canvas");
  79. const cache = {
  80. storage: await caches.open($name),
  81. /**
  82. * @param {string} url
  83. * @param {string} scriptContent
  84. */
  85. saveScript: function (url, scriptContent) {
  86. this.storage.put(url, new Response(scriptContent, { headers: { "Content-Type": "application/javascript" } }));
  87. },
  88. /**
  89. * @param {string} url
  90. * @param {object} json
  91. */
  92. saveObject: function (url, json) {
  93. this.storage.put(url, new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json" } }));
  94. },
  95. /**
  96. * @param {string} url
  97. * @returns {Promise<string>}
  98. */
  99. loadScript: async function (url) {
  100. const response = (await this.storage.match(url)) || (await fetch(url));
  101. return await response.text();
  102. },
  103. /**
  104. * @param {string} url
  105. * @returns {Promise<object>}
  106. */
  107. loadObject: async function (url) {
  108. const response = (await this.storage.match(url)) || (await fetch(url));
  109. return await response.json();
  110. },
  111. };
  112. const UI = {
  113. /**
  114. * Returns the first element that is a descendant of node that matches selectors.
  115. * @param {string} selectors
  116. * @param {ParentNode} parentElement
  117. * @returns {Element|null}
  118. */
  119. querySelect: (selectors, parentElement = document) => parentElement.querySelector(selectors),
  120. /**
  121. * Returns all element descendants of node that match selectors.
  122. * @param {string} selectors
  123. * @param {ParentNode} parentElement
  124. * @returns {NodeListOf<Element>}
  125. */
  126. querySelectAll: (selectors, parentElement = document) => parentElement.querySelectorAll(selectors),
  127. /**
  128. * Create Element and assign properties to it.
  129. * @param {string} tagName
  130. * @param {object} properties
  131. * @returns {Element}
  132. */
  133. createElement: (tagName, properties = {}) => {
  134. /** @type {Element} */
  135. const element = Object.assign(document.createElement(tagName), properties);
  136. if (properties.title) {
  137. try {
  138. element.setAttribute("data-toggle", "tooltip");
  139. element.setAttribute("data-html", "true");
  140. jQuery(element).tooltip({ boundary: "window" });
  141. } catch (error) {}
  142. }
  143. return element;
  144. },
  145. /**
  146. * Assign attributes to element.
  147. * @param {Element} element
  148. * @param {object} attributes
  149. */
  150. setAttributes: (element, attributes) => Object.entries(attributes).forEach(([k, v]) => element.setAttribute(k, v)),
  151. /**
  152. * Assign styles to element.
  153. * @param {Element} element
  154. * @param {object} styles
  155. */
  156. setStyles: (element, styles) => Object.assign(element.style, styles),
  157. /**
  158. * @param {string} name
  159. * @returns {HTMLDivElement}
  160. */
  161. createContainer: function (name = "div") {
  162. return this.createElement(name, { className: "container" });
  163. },
  164. /**
  165. * @returns {HTMLDivElement}
  166. */
  167. createRow: function () {
  168. return this.createElement("div", { className: "row" });
  169. },
  170. /**
  171. * @param {string} name
  172. * @returns {HTMLElement}
  173. */
  174. createIcon: function (name) {
  175. return this.createElement("i", { className: `fas fa-${name}` });
  176. },
  177. /**
  178. * @param {string} type
  179. * @param {object} properties
  180. * @returns {HTMLInputElement}
  181. */
  182. createInput: function (type, properties = {}) {
  183. const input = this.createElement("input", { ...{ className: "form-control" }, ...properties, ...{ type: type } });
  184. if (!input.id) input.id = helpers.uid(8);
  185. return input;
  186. },
  187. /**
  188. * @param {HTMLInputElement} input
  189. * @param {object} properties
  190. * @returns {HTMLLabelElement}
  191. */
  192. createLabelFor: function (input, properties = {}) {
  193. const label = this.createElement("label", { ...{ className: "btn btn-sm btn-outline-secondary" }, ...properties, ...{ htmlFor: input.id } });
  194. label.appendChild(input);
  195. input.hidden = true;
  196. if (input.type === "checkbox" || input.type === "radio") {
  197. input.addEventListener("input", function () {
  198. label.classList[input.checked ? "add" : "remove"]("active");
  199. });
  200. }
  201. return label;
  202. },
  203. /**
  204. * @param {string} className
  205. * @returns {HTMLElement & { show: Function, hide: Function }}
  206. */
  207. createSpinner: function (className = "text-secondary") {
  208. const spinner = this.createElement("div", { className: [className, "spinner-border spinner-border-sm"].join(" ") });
  209. spinner.show = function () {
  210. spinner.classList.remove("d-none");
  211. };
  212. spinner.hide = function () {
  213. spinner.classList.add("d-none");
  214. };
  215. spinner.hide();
  216. return spinner;
  217. },
  218. /**
  219. * @param {HTMLInputElement} input
  220. * @param {HTMLLabelElement|HTMLInputElement|HTMLButtonElement|HTMLElement} addon
  221. * @returns {HTMLDivElement}
  222. */
  223. createGroup: function (input, addon) {
  224. const group = this.createElement("div", { className: "input-group input-group-sm" });
  225. const groupAppend = this.createElement("div", { className: "input-group-append" });
  226.  
  227. if (!(addon instanceof HTMLInputElement || addon instanceof HTMLButtonElement)) {
  228. addon.classList.add("input-group-text");
  229. }
  230. groupAppend.appendChild(addon);
  231.  
  232. group.appendChild(input);
  233. group.appendChild(groupAppend);
  234.  
  235. return group;
  236. },
  237. /**
  238. * @param {Array<HTMLLabelElement|HTMLInputElement|HTMLButtonElement|HTMLElement>} inputs
  239. * @returns {HTMLDivElement}
  240. */
  241. createInputGroup: function (...inputs) {
  242. const group = this.createElement("div", { className: "input-group input-group-sm" });
  243.  
  244. let previousElementSibling = inputs[0];
  245. let appendGroup = this.createElement("div", { className: "input-group-append" });
  246. for (let index = 0; index < inputs.length; index++) {
  247. const input = inputs[index];
  248.  
  249. if (input instanceof HTMLInputElement) {
  250. if (!(previousElementSibling instanceof HTMLInputElement)) {
  251. group.appendChild(appendGroup);
  252. appendGroup = this.createElement("div", { className: `input-group-${index > 0 ? "append" : "prepend"}` });
  253. }
  254. input.hidden = false;
  255. group.appendChild(input);
  256. } else {
  257. input.classList.add("input-group-text");
  258. appendGroup.appendChild(input);
  259. if (index + 1 === inputs.length) {
  260. group.appendChild(appendGroup);
  261. }
  262. }
  263. previousElementSibling = input;
  264. }
  265.  
  266. return group;
  267. },
  268. };
  269. const DrawariaOnlineMessageTypes = {
  270. /**
  271. * @param {string} message
  272. * @returns {string}
  273. */
  274. chatmsg: function (message) {
  275. // 42["chatmsg","a"]
  276. const data = ["chatmsg", message];
  277. return `${42}${JSON.stringify(data)}`;
  278. },
  279. /**
  280. * @returns {string}
  281. */
  282. passturn: function () {
  283. // 42["passturn"]
  284. const data = ["passturn"];
  285. return `${42}${JSON.stringify(data)}`;
  286. },
  287. /**
  288. * @param {number|string} playerid
  289. * @returns {string}
  290. */
  291. pgdrawvote: function (playerid) {
  292. // 42["pgdrawvote",2,0]
  293. const data = ["pgdrawvote", playerid, 0];
  294. return `${42}${JSON.stringify(data)}`;
  295. },
  296. /**
  297. * @returns {string}
  298. */
  299. pgswtichroom: function () {
  300. // 42["pgswtichroom"]
  301. const data = ["pgswtichroom"];
  302. return `${42}${JSON.stringify(data)}`;
  303. },
  304. /**
  305. * @returns {string}
  306. */
  307. playerafk: function () {
  308. // 42["playerafk"]
  309. const data = ["playerafk"];
  310. return `${42}${JSON.stringify(data)}`;
  311. },
  312. /**
  313. * @returns {string}
  314. */
  315. playerrated: function () {
  316. // 42["playerrated"]
  317. const data = ["playerrated"];
  318. return `${42}${JSON.stringify(data)}`;
  319. },
  320. /**
  321. * @param {number|string} gestureid
  322. * @returns {string}
  323. */
  324. sendgesture: function (gestureid) {
  325. // 42["sendgesture",16]
  326. const data = ["sendgesture", gestureid];
  327. return `${42}${JSON.stringify(data)}`;
  328. },
  329. /**
  330. * @returns {string}
  331. */
  332. sendvote: function () {
  333. // 42["sendvote"]
  334. const data = ["sendvote"];
  335. return `${42}${JSON.stringify(data)}`;
  336. },
  337. /**
  338. * @param {number|string} playerid
  339. * @returns {string}
  340. */
  341. sendvotekick: function (playerid) {
  342. // 42["sendvotekick",93]
  343. const data = ["sendvotekick", playerid];
  344. return `${42}${JSON.stringify(data)}`;
  345. },
  346. /**
  347. * @param {number|string} wordid
  348. * @returns {string}
  349. */
  350. wordselected: function (wordid) {
  351. // 42["wordselected",0]
  352. const data = ["sendvotekick", wordid];
  353. return `${42}${JSON.stringify(data)}`;
  354. },
  355. /**
  356. * @param {string} roomID
  357. * @param {string} name
  358. * @param {string} uid
  359. * @param {string} wt
  360. * @returns {string}
  361. */
  362. startplay: function (roomID, name = undefined, uid = undefined, wt = undefined, width = 1000, height = 1000) {
  363. const data = `${420}${JSON.stringify(["startplay", name, 2, "en", roomID, null, [null, "https://drawaria.online/", width, height, [null, uid, wt], null]])}`;
  364. return data;
  365. },
  366. clientcmd: {
  367. /**
  368. * @param {number|string} itemid
  369. * @param {boolean} isactive
  370. * @returns {string}
  371. */
  372. activateitem: function (itemid, isactive) {
  373. const data = ["clientcmd", 12, [itemid, isactive]];
  374. return `${42}${JSON.stringify(data)}`;
  375. },
  376. /**
  377. * @param {number|string} itemid
  378. * @returns {string}
  379. */
  380. buyitem: function (itemid) {
  381. const data = ["clientcmd", 11, [itemid]];
  382. return `${42}${JSON.stringify(data)}`;
  383. },
  384. /**
  385. * @param {number|string} itemid
  386. * @param {"zindex"|"shared"} target
  387. * @param {any} value
  388. * @returns {string}
  389. */
  390. canvasobj_changeattr: function (itemid, target, value) {
  391. const data = ["clientcmd", 234, [itemid, target, value]];
  392. return `${42}${JSON.stringify(data)}`;
  393. },
  394. /**
  395. * @returns {string}
  396. */
  397. canvasobj_getobjects: function () {
  398. const data = ["clientcmd", 233];
  399. return `${42}${JSON.stringify(data)}`;
  400. },
  401. /**
  402. * @param {number|string} itemid
  403. * @returns {string}
  404. */
  405. canvasobj_remove: function (itemid) {
  406. let data = ["clientcmd", 232, [itemid]];
  407. return `${42}${JSON.stringify(data)}`;
  408. },
  409. /**
  410. * @param {number|string} itemid
  411. * @param {number|string} positionX
  412. * @param {number|string} positionY
  413. * @param {number|string} speed
  414. * @returns {string}
  415. */
  416. canvasobj_setposition: function (itemid, positionX, positionY, speed) {
  417. const data = ["clientcmd", 230, [itemid, 100 / positionX, 100 / positionY, { movespeed: speed }]];
  418. return `${42}${JSON.stringify(data)}`;
  419. },
  420. /**
  421. * @param {number|string} itemid
  422. * @param {number|string} rotation
  423. * @returns {string}
  424. */
  425. canvasobj_setrotation: function (itemid, rotation) {
  426. const data = ["clientcmd", 231, [itemid, rotation]];
  427. return `${42}${JSON.stringify(data)}`;
  428. },
  429. /**
  430. * @param {any} value
  431. * @returns {string}
  432. */
  433. customvoting_setvote: function (value) {
  434. const data = ["clientcmd", 301, [value]];
  435. return `${42}${JSON.stringify(data)}`;
  436. },
  437. /**
  438. * @param {any} value
  439. * @returns {string}
  440. */
  441. getfpid: function (value) {
  442. const data = ["clientcmd", 901, [value]];
  443. return `${42}${JSON.stringify(data)}`;
  444. },
  445. /**
  446. * @returns {string}
  447. */
  448. getinventory: function () {
  449. const data = ["clientcmd", 10, [true]];
  450. return `${42}${JSON.stringify(data)}`;
  451. },
  452. /**
  453. * @returns {string}
  454. */
  455. getspawnsstate: function () {
  456. const data = ["clientcmd", 102];
  457. return `${42}${JSON.stringify(data)}`;
  458. },
  459. /**
  460. * @param {number|string} positionX
  461. * @param {number|string} positionY
  462. * @returns {string}
  463. */
  464. moveavatar: function (positionX, positionY) {
  465. const data = ["clientcmd", 103, [1e4 * Math.floor(positionX * 0.01 * 1e4) + Math.floor(positionY * 0.01 * 1e4), false]];
  466. return `${42}${JSON.stringify(data)}`;
  467. },
  468. /**
  469. * @returns {string}
  470. */
  471. setavatarprop: function () {
  472. const data = ["clientcmd", 115];
  473. return `${42}${JSON.stringify(data)}`;
  474. },
  475. /**
  476. * @param {number|string} flagid
  477. * @param {boolean} isactive
  478. * @returns {string}
  479. */
  480. setstatusflag: function (flagid, isactive) {
  481. const data = ["clientcmd", 3, [flagid, isactive]];
  482. return `${42}${JSON.stringify(data)}`;
  483. },
  484. /**
  485. * @param {number|string} playerid
  486. * @param {number|string} tokenid
  487. * @returns {string}
  488. */
  489. settoken: function (playerid, tokenid) {
  490. const data = ["clientcmd", 2, [playerid, tokenid]];
  491. return `${42}${JSON.stringify(data)}`;
  492. },
  493. /**
  494. * @param {number|string} playerid
  495. * @param {any} value
  496. * @returns {string}
  497. */
  498. snapchatmessage: function (playerid, value) {
  499. const data = ["clientcmd", 330, [playerid, value]];
  500. return `${42}${JSON.stringify(data)}`;
  501. },
  502. /**
  503. * @returns {string}
  504. */
  505. spawnavatar: function () {
  506. const data = ["clientcmd", 101];
  507. return `${42}${JSON.stringify(data)}`;
  508. },
  509. /**
  510. * @returns {string}
  511. */
  512. startrollbackvoting: function () {
  513. const data = ["clientcmd", 320];
  514. return `${42}${JSON.stringify(data)}`;
  515. },
  516. /**
  517. * @returns {string}
  518. */
  519. trackforwardvoting: function () {
  520. const data = ["clientcmd", 321];
  521. return `${42}${JSON.stringify(data)}`;
  522. },
  523. /**
  524. * @param {number|string} trackid
  525. * @returns {string}
  526. */
  527. votetrack: function (trackid) {
  528. const data = ["clientcmd", 1, [trackid]];
  529. return `${42}${JSON.stringify(data)}`;
  530. },
  531. },
  532. clientnotify: {
  533. /**
  534. * @param {number|string} playerid
  535. * @returns {string}
  536. */
  537. requestcanvas: function (playerid) {
  538. const data = ["clientnotify", playerid, 10001];
  539. return `${42}${JSON.stringify(data)}`;
  540. },
  541. /**
  542. * @param {number|string} playerid
  543. * @param {string} base64
  544. * @returns {string}
  545. */
  546. respondcanvas: function (playerid, base64) {
  547. const data = ["clientnotify", playerid, 10002, [base64]];
  548. return `${42}${JSON.stringify(data)}`;
  549. },
  550. /**
  551. * @param {number|string} playerid
  552. * @param {number|string} imageid
  553. * @returns {string}
  554. */
  555. galleryupload: function (playerid, imageid) {
  556. const data = ["clientnotify", playerid, 11, [imageid]];
  557. return `${42}${JSON.stringify(data)}`;
  558. },
  559. /**
  560. * @param {number|string} playerid
  561. * @param {any} type
  562. * @returns {string}
  563. */
  564. warning: function (playerid, type) {
  565. const data = ["clientnotify", playerid, 100, [type]];
  566. return `${42}${JSON.stringify(data)}`;
  567. },
  568. /**
  569. * @param {number|string} playerid
  570. * @param {string} targetname
  571. * @param {boolean} mute
  572. * @returns {string}
  573. */
  574. mute: function (playerid, targetname, mute = false) {
  575. const data = ["clientnotify", playerid, 1, [mute, targetname]];
  576. return `${42}${JSON.stringify(data)}`;
  577. },
  578. /**
  579. * @param {number|string} playerid
  580. * @param {string} targetname
  581. * @param {boolean} hide
  582. * @returns {string}
  583. */
  584. hide: function (playerid, targetname, hide = false) {
  585. const data = ["clientnotify", playerid, 3, [hide, targetname]];
  586. return `${42}${JSON.stringify(data)}`;
  587. },
  588. /**
  589. * @param {number|string} playerid
  590. * @param {string} reason
  591. * @param {string} targetname
  592. * @returns {string}
  593. */
  594. report: function (playerid, reason, targetname) {
  595. const data = ["clientnotify", playerid, 2, [targetname, reason]];
  596. return `${42}${JSON.stringify(data)}`;
  597. },
  598. },
  599. drawcmd: {
  600. /**
  601. * @param {number|string} playerid
  602. * @param {number|string} x1
  603. * @param {number|string} y1
  604. * @param {number|string} x2
  605. * @param {number|string} y2
  606. * @param {number|string} size
  607. * @param {number|string} color
  608. * @param {boolean} ispixel
  609. * @returns {string}
  610. */
  611. line: function (x1, y1, x2, y2, color, size = 4, ispixel = true, playerid = 0) {
  612. const data = ["drawcmd", 0, [x1 * (1 / canvas.width), y1 * (1 / canvas.height), x2 * (1 / canvas.width), y2 * (1 / canvas.height), false, -size, color, playerid, ispixel]];
  613. return `${42}${JSON.stringify(data)}`;
  614. },
  615. /**
  616. * @param {number|string} playerid
  617. * @param {number|string} x1
  618. * @param {number|string} y1
  619. * @param {number|string} x2
  620. * @param {number|string} y2
  621. * @param {number|string} size
  622. * @param {number|string} color
  623. * @returns {string}
  624. */
  625. erase: function (x1, y1, x2, y2, color, size, ispixel = true, playerid = 0) {
  626. const data = ["drawcmd", 1, [x1 * 0.01, y1 * 0.01, x2 * 0.01, y2 * 0.01, false, -size, color, playerid, ispixel]];
  627. return `${42}${JSON.stringify(data)}`;
  628. },
  629. /**
  630. * @param {number|string} x
  631. * @param {number|string} y
  632. * @param {number|string} color
  633. * @param {number|string} tolerance
  634. * @param {number|string} r
  635. * @param {number|string} g
  636. * @param {number|string} b
  637. * @param {number|string} a
  638. * @returns {string}
  639. */
  640. flood: function (x, y, color, tolerance, r, g, b, a) {
  641. // 42["drawcmd",2,[x, y,color,{"0":r,"1":g,"2":b,"3":a},size]]
  642. const data = ["drawcmd", 2, [x * 0.01, y * 0.01, color, { 0: r, 1: g, 2: b, 3: a }, tolerance]];
  643. return `${42}${JSON.stringify(data)}`;
  644. },
  645. /**
  646. * @param {number|string} playerid
  647. * @returns {string}
  648. */
  649. undo: function (playerid) {
  650. // 42["drawcmd",3,[playerid]]
  651. const data = ["drawcmd", 3, [playerid]];
  652. return `${42}${JSON.stringify(data)}`;
  653. },
  654. /**
  655. * @returns {string}
  656. */
  657. clear: function () {
  658. // 42["drawcmd",4,[]]
  659. const data = ["drawcmd", 4, []];
  660. return `${42}${JSON.stringify(data)}`;
  661. },
  662. },
  663. };
  664. const helpers = {
  665. /**
  666. * @param {string} message
  667. * @param {string} styles
  668. */
  669. log: (message, styles = "color:#5090C1", application = undefined) => console.log("%c" + [`[${application ?? $name}]`, message].join(" "), styles),
  670.  
  671. /**
  672. * Generate radnow UID
  673. * @param {number} size
  674. * @returns {string}
  675. */
  676. uid: (size = 8) => {
  677. const MASK = 0x3d;
  678. const LETTERS = "abcdefghijklmnopqrstuvwxyz";
  679. const NUMBERS = "1234567890";
  680. const SPECIALS = "-_";
  681. const charset = `${NUMBERS}${LETTERS}${LETTERS.toUpperCase()}${SPECIALS}`.split("");
  682.  
  683. const bytes = new Uint8Array(size);
  684. crypto.getRandomValues(bytes);
  685.  
  686. return bytes.reduce((acc, byte) => `${acc}${charset[byte & MASK]}`, "");
  687. },
  688.  
  689. /**
  690. * @param {number[]} byteArray
  691. * @returns {string}
  692. */
  693. toHexString: (byteArray) => {
  694. return byteArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
  695. },
  696.  
  697. /**
  698. * @param {string} key
  699. * @param {string} value
  700. */
  701. setCookie: (key, value) => {
  702. if (window.Cookie) Cookies.set(key, value);
  703. document.cookie = `${key}=${value}; Secure; Path=/; SameSite=None; Partitioned;`;
  704. },
  705.  
  706. /**
  707. * @returns {Array<*>&{addEventListener:(event:"delete"|"set",handler:(property:string,value:*)=>void))=>}}
  708. */
  709. makeObservableArray: () => {
  710. if (!("Proxy" in window)) {
  711. console.warn("Your browser doesn't support Proxies.");
  712. return [];
  713. }
  714.  
  715. const onDeleteCall = [];
  716. const onSetCall = [];
  717.  
  718. // a proxy for our array
  719. const proxy = new Proxy([], {
  720. deleteProperty: function (target, property) {
  721. onDeleteCall.forEach((callback) => callback(property, target[property]));
  722. delete target[property];
  723. return true;
  724. },
  725. set: function (target, property, value, receiver) {
  726. target[property] = value;
  727. onSetCall.forEach((callback) => callback(property, target[property]));
  728. return true;
  729. },
  730. });
  731.  
  732. proxy.addEventListener = (event, handler) => {
  733. switch (event) {
  734. case "set":
  735. onSetCall.push(handler);
  736. return;
  737. case "delete":
  738. onDeleteCall.push(handler);
  739. return;
  740.  
  741. default:
  742. break;
  743. }
  744. };
  745.  
  746. return proxy;
  747. },
  748.  
  749. /**
  750. * @param {string} message
  751. * @returns {Array<any>|object}
  752. */
  753. tryParseJSON: (message) => {
  754. try {
  755. return JSON.parse(message);
  756. } catch (error) {
  757. return [];
  758. }
  759. },
  760. };
  761.  
  762. const clearScripts = (remove = true) => {
  763. try {
  764. let array = document.querySelectorAll('script[src]:not([data-codemaid="ignore"])');
  765. array.forEach((script) => {
  766. if (script.src != "") document.head.appendChild(script);
  767. });
  768. } catch (error) {
  769. console.error(error);
  770. }
  771.  
  772. try {
  773. let unifiedScript = UI.createElement("script");
  774.  
  775. let scripts = document.querySelectorAll('script:not([src]):not([data-codemaid="ignore"])');
  776. let unifiedScriptContent = "";
  777. scripts.forEach((script) => {
  778. let content = script.textContent; //.replaceAll(/\s/g, '');
  779.  
  780. unifiedScriptContent += `try{${content}}catch(e){console.warn(e);}`;
  781. script.remove();
  782. });
  783.  
  784. unifiedScript.textContent = unifiedScriptContent;
  785.  
  786. if (!remove) document.head.appendChild(unifiedScript);
  787. } catch (error) {
  788. console.error(error);
  789. }
  790. };
  791.  
  792. const clearStyles = (remove = false) => {
  793. try {
  794. let unifiedStyles = UI.createElement("style");
  795. unifiedStyles.textContet = "";
  796.  
  797. let styles = document.querySelectorAll('style:not([data-codemaid="ignore"])');
  798. styles.forEach((style) => {
  799. unifiedStyles.textContent += style.textContent;
  800. style.remove();
  801. });
  802. if (!remove) document.head.appendChild(unifiedStyles);
  803. } catch (error) {
  804. console.error(error);
  805. }
  806. };
  807.  
  808. const clearEmbeds = () => {
  809. try {
  810. let array = document.querySelectorAll("iframe");
  811. array.forEach((iframe) => {
  812. iframe.remove();
  813. });
  814. } catch (error) {
  815. console.error(error);
  816. }
  817. };
  818.  
  819. const clearAll = () => {
  820. clearEmbeds();
  821. clearScripts();
  822. clearStyles();
  823. UI.querySelectAll(".extimages").forEach((e) => e.remove());
  824. UI.querySelectAll(".promlinks").forEach((e) => e.remove());
  825. };
  826.  
  827. class Player {
  828. /** @type {Array<Player>} */
  829. static instances = [];
  830. static get noConflict() {
  831. return Player.instances[0] ?? new Player(helpers.uid(12));
  832. }
  833. /**
  834. * @param {string|undefined} inviteLink
  835. * @returns {URL}
  836. */
  837. static getSocketServerURL(inviteLink) {
  838. if (typeof inviteLink === "undefined") return `wss://sv3.drawaria.online/socket.io/?sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`;
  839. const roomID = this.getRoomID(inviteLink);
  840. const [_voidable, serverPrefix] = roomID.split(".");
  841. return new URL(`wss://${typeof serverPrefix === "undefined" ? "" : "sv".concat(serverPrefix, ".")}drawaria.online/socket.io/?sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`);
  842. }
  843. /**
  844. * @param {string|undefined} inviteLink
  845. * @returns {string}
  846. */
  847. static getRoomID(inviteLink) {
  848. const inviteURL = new URL(inviteLink);
  849. return inviteURL.pathname.slice(6);
  850. }
  851. /**
  852. * @param {MessageEvent} messageEvent
  853. * @returns {[string, string, Array<any>|object]}
  854. */
  855. static parseMessageEventData(messageEvent) {
  856. const rawData = String(messageEvent.data);
  857. const messageType = (rawData.match(/^\d+/i) ?? [""])[0];
  858. const messageData = messageType.length === rawData.length ? [] : helpers.tryParseJSON(rawData.slice(messageType.length));
  859. return [messageType, Array.isArray(messageData) ? messageData.shift() : "", messageData];
  860. }
  861. static parseMessage = DrawariaOnlineMessageTypes;
  862.  
  863. /** @type {string} */
  864. name;
  865. /** @type {string} */
  866. uid;
  867. /** @type {string} */
  868. wt;
  869. /** @type {string} */
  870. roomID;
  871.  
  872. /** @type {WebSocket} */
  873. socket;
  874.  
  875. /** @type {Map<string, Function[]>} */
  876. events;
  877.  
  878. /**
  879. * @param {string} name
  880. */
  881. constructor(name = undefined) {
  882. this.name = name;
  883. this.uid = "_";
  884. this.wt = undefined;
  885. this.roomID = undefined;
  886.  
  887. this.events = new Map();
  888.  
  889. Player.instances.push(this);
  890. }
  891.  
  892. /**
  893. * @param {string} inviteLink
  894. */
  895. connect = (inviteLink) => {
  896. if (this.isConnected) return;
  897. this.#createNewConnection(inviteLink);
  898. };
  899.  
  900. disconnect = () => {
  901. this.send("41");
  902. if (this.isConnected) this.socket.close();
  903. };
  904.  
  905. reconnect = () => {
  906. if (!this.isConnected) {
  907. this.connect(`https://drawaria.online/room/${this.roomID}`);
  908. return;
  909. }
  910. this.send("41");
  911. this.send("40");
  912. };
  913.  
  914. /**
  915. * @param {string} inviteLink
  916. */
  917. enterRoom = (inviteLink = undefined) => {
  918. this.roomID = Player.getRoomID(inviteLink ?? document.querySelector("#invurl").value);
  919. this.reconnect();
  920. };
  921.  
  922. nextRoom = () => {
  923. this.send(Player.parseMessage.pgswtichroom());
  924. };
  925.  
  926. leaveRoom = () => {
  927. this.send("41");
  928. };
  929.  
  930. /**
  931. * @param {string} payload
  932. */
  933. send = (payload) => {
  934. if (this.isConnected) this.socket.send(payload);
  935. else debug(`send failed! Connection is closed`);
  936. };
  937.  
  938. /**
  939. * @returns {boolean}
  940. */
  941. get isConnected() {
  942. return typeof this.socket !== "undefined" && this.socket instanceof WebSocket && (this.socket.readyState === WebSocket.OPEN || this.socket.readyState === WebSocket.CONNECTING);
  943. }
  944.  
  945. #sendHeartbeat = () => {
  946. if (this.isConnected) this.socket.send(2);
  947. };
  948.  
  949. /**
  950. * @param {string} inviteLink
  951. */
  952. #createNewConnection = (inviteLink) => {
  953. this.socket = new WebSocket(Player.getSocketServerURL(inviteLink));
  954. this.roomID = Player.getRoomID(inviteLink);
  955. this.socket.addEventListener("open", this.#startHeartbeat);
  956. this.socket.addEventListener("message", this.#handleMessage);
  957. };
  958.  
  959. #startHeartbeat = () => {
  960. this.heartbeatInterval = setInterval(this.#sendHeartbeat, 25000);
  961. };
  962.  
  963. #handleMessage = (rawMessage) => {
  964. const [eventType, eventName, eventData] = Player.parseMessageEventData(rawMessage);
  965.  
  966. switch (eventType) {
  967. case "40":
  968. this.send(Player.parseMessage.startplay(this.roomID, this.name, this.uid, this.wt));
  969. break;
  970. case "430":
  971. break;
  972. case "42":
  973. this.__invokeEvent(eventName, eventData);
  974. break;
  975. default:
  976. }
  977. };
  978.  
  979. /**
  980. * @param {DrawariaOnlineEvents} event
  981. * @param {Function} callback
  982. */
  983. addEventListener = (event, callback, id = undefined) => {
  984. if (!this.hasEventListener(event)) {
  985. this.events.set(event, []);
  986. }
  987. callback.id = id ?? this.addEventListener.caller.name ?? helpers.uid(8);
  988. try {
  989. this.events.get(event).push(callback);
  990. } catch (error) {
  991. debug(`addEventListener returned error \"${error}\"`, "color:firebrick");
  992. }
  993. };
  994.  
  995. /**
  996. * @param {DrawariaOnlineEvents} event
  997. * @param {*} data
  998. */
  999. __invokeEvent = (event, data) => {
  1000. if (this.hasEventListener(event)) {
  1001. const listeners = this.events.get(event);
  1002. listeners.forEach((listener) => listener(data));
  1003. }
  1004. };
  1005.  
  1006. /**
  1007. * @param {DrawariaOnlineEvents} name
  1008. * @returns {boolean}
  1009. */
  1010. hasEventListener = (name) => {
  1011. return this.events.has(name);
  1012. };
  1013. }
  1014.  
  1015. class DefinableCore {
  1016. static UI = UI;
  1017. static Player = Player;
  1018. static helper = helpers;
  1019. static runtime = [];
  1020.  
  1021. constructor(moduleName, moduleIcon = "code") {
  1022. this.name = moduleName;
  1023. this.__initializeHead(moduleIcon);
  1024. this.__initializeBody();
  1025. }
  1026.  
  1027. __initializeHead(moduleIcon) {
  1028. const input = this.UI.createInput("checkbox");
  1029. this.label = this.UI.createLabelFor(input, { title: this.name });
  1030. const icon = this.UI.createIcon(moduleIcon);
  1031. this.label.appendChild(icon);
  1032. }
  1033.  
  1034. __initializeBody() {
  1035. const submoduleSelectionContainer = this.UI.createElement("nav", { className: "row" });
  1036. const wrapper = this.UI.createElement("div");
  1037. this.wrapper = wrapper;
  1038. this.__contaier = this.UI.createElement("div");
  1039. this.__contaier.appendChild(this.UI.createElement("b", { textContent: this.name }));
  1040. // this.wrapper.appendChild(submoduleSelectionContainer);
  1041. this.__contaier.appendChild(submoduleSelectionContainer);
  1042. this.wrapper.appendChild(this.__contaier);
  1043. const input = this.UI.querySelect("&>input", this.label);
  1044. input.addEventListener("input", function () {
  1045. wrapper.classList[input.checked ? "remove" : "add"]("d-none");
  1046. });
  1047. wrapper.classList.add("d-none");
  1048. }
  1049.  
  1050. createRow() {
  1051. const row = this.UI.createRow();
  1052. this.__contaier.appendChild(row);
  1053. return row;
  1054. }
  1055.  
  1056. /**
  1057. * @param {DefinableCore} submodule
  1058. */
  1059. registerModule(submodule) {
  1060. // const submoduleSelectionContainer = this.UI.querySelect("&>nav", this.wrapper);
  1061. const submoduleSelectionContainer = this.UI.querySelect("&>nav", this.__contaier);
  1062. submoduleSelectionContainer.appendChild(submodule.label);
  1063. this.wrapper.appendChild(submodule.wrapper);
  1064. DefinableCore.runtime.push(submodule);
  1065. }
  1066.  
  1067. get UI() {
  1068. return DefinableCore.UI;
  1069. }
  1070. get Player() {
  1071. return DefinableCore.Player;
  1072. }
  1073. }
  1074.  
  1075. window.addEventListener("definable:core:init", function () {
  1076. const ui = DefinableCore.UI;
  1077. const definable = new DefinableCore("Definable", "code");
  1078. const container = UI.createElement("section", { style: "position: relative" });
  1079.  
  1080. const chatbox = ui.querySelect("#chatbox_messages");
  1081. /** @type {HTMLElement} */
  1082. const devider = chatbox.previousElementSibling;
  1083. devider.before(container);
  1084. container.appendChild(definable.wrapper);
  1085. definable.wrapper.style.position = "absolute";
  1086. container.before(devider.cloneNode(false));
  1087.  
  1088. setTimeout(() => {
  1089. window.dispatchEvent(new CustomEvent("definable:init", { detail: { main: definable, core: DefinableCore } }));
  1090. definable.wrapper.classList.remove("d-none");
  1091. definable.wrapper.classList.add("container");
  1092. definable.wrapper.id = "definable";
  1093. definable.wrapper.ModMenu = definable;
  1094. }, 500);
  1095.  
  1096. globalThis["$" + $name] = definable;
  1097. });
  1098.  
  1099. setTimeout(() => {
  1100. window.dispatchEvent(new CustomEvent("definable:core:init"));
  1101. clearEmbeds();
  1102. clearScripts();
  1103. clearStyles();
  1104. document.head.appendChild(
  1105. UI.createElement("style", {
  1106. id: "definableStyles",
  1107. textContent: [
  1108. "#definable { position: relative; isolation: isolate; }",
  1109. "#definable::before { position: absolute; content: ''; inset: 0; background-color: var(--light); z-index: -1; }",
  1110. "#definable .row { gap: 0.125rem; margin-bottom: 0.125rem; }",
  1111. "#definable label { margin: 0; }",
  1112. "#definable .row>label { min-width: 2rem; }",
  1113. ].join("\n"),
  1114. })
  1115. );
  1116. jQuery.getScript("/3rd/lib/moveable.min.js?" + window.VERID, function (data, textStatus, jqxhr) {
  1117. globalThis["transformable"] = initTransformable(Moveable);
  1118. });
  1119. jQuery.getScript("/pageres/stencils.js?" + window.VERID);
  1120. // console.clear();
  1121. }, 500);
  1122.  
  1123. function initTransformable(TransformationController) {
  1124. const transformable = new TransformationController(document.body, {
  1125. target: null,
  1126. container: document.body,
  1127. draggable: true,
  1128. scalable: true,
  1129. rotatable: true,
  1130. snappable: true,
  1131. origin: true,
  1132. keepRatio: true,
  1133. });
  1134.  
  1135. transformable.snapGridWidth = 64;
  1136. transformable.snapGridHeight = 64;
  1137.  
  1138. /* draggable */
  1139. transformable.on("drag", ({ target, transform, left, top, right, bottom, beforeDelta, beforeDist, delta, dist, clientX, clientY }) => {
  1140. target.style.left = `${left}px`;
  1141. target.style.top = `${top}px`;
  1142. });
  1143.  
  1144. /* scalable */
  1145. transformable.on("scale", ({ target, scale, dist, delta, transform, clientX, clientY }) => {
  1146. target.style.transform = transform;
  1147. });
  1148.  
  1149. /* rotatable */
  1150. transformable.on("rotate", ({ target, beforeDelta, delta, dist, transform, clientX, clientY }) => {
  1151. target.style.transform = transform;
  1152. });
  1153.  
  1154. /* Handle Transformable target selection */
  1155. window.addEventListener("mousedown", function (event) {
  1156. /** @type {HTMLElement} */
  1157. const target = event.target;
  1158. if (!target) return;
  1159.  
  1160. if (target.classList.contains("moveable-control-box") || this.document.querySelector(".moveable-control-box").contains(target)) return;
  1161.  
  1162. if (target.classList.contains("transformable")) {
  1163. transformable.target = target;
  1164. return;
  1165. }
  1166. transformable.target = null;
  1167. });
  1168.  
  1169. /* Handle ZIndex changes via keybinds */
  1170. window.addEventListener("keydown", function (event) {
  1171. /** @type {HTMLElement|HTMLImageElement} */
  1172. const target = transformable.target;
  1173. if (!target) return;
  1174.  
  1175. // console.log(event.key);
  1176.  
  1177. const zIndex = Number(target.style.zIndex ?? "0");
  1178. switch (event.key) {
  1179. case "+":
  1180. target.style.zIndex = zIndex + 1;
  1181. event.preventDefault();
  1182. event.stopPropagation();
  1183. break;
  1184.  
  1185. case "-":
  1186. target.style.zIndex = zIndex - 1;
  1187. event.preventDefault();
  1188. event.stopPropagation();
  1189. break;
  1190.  
  1191. case "Delete":
  1192. transformable.target = null;
  1193. target.remove();
  1194. break;
  1195.  
  1196. case "ArrowUp":
  1197. target.style.top = parseFloat(target.style.top) - 1 + "px";
  1198. transformable.updateRect();
  1199. break;
  1200.  
  1201. case "ArrowDown":
  1202. target.style.top = parseFloat(target.style.top) + 1 + "px";
  1203. transformable.updateRect();
  1204. break;
  1205.  
  1206. case "ArrowLeft":
  1207. target.style.left = parseFloat(target.style.left) - 1 + "px";
  1208. transformable.updateRect();
  1209. break;
  1210.  
  1211. case "ArrowRight":
  1212. target.style.left = parseFloat(target.style.left) + 1 + "px";
  1213. transformable.updateRect();
  1214. break;
  1215.  
  1216. default:
  1217. break;
  1218. }
  1219. if (event.ctrlKey && event.key == "d") {
  1220. event.preventDefault();
  1221. event.stopPropagation();
  1222.  
  1223. const duplicate = new Image();
  1224. duplicate.src = target.src;
  1225. duplicate.classList.add("transformable");
  1226. duplicate.style.top = parseFloat(target.style.top) + 20 + "px";
  1227. duplicate.style.left = parseFloat(target.style.left) + 20 + "px";
  1228. transformable.target = duplicate;
  1229. transformable.updateRect();
  1230. document.body.appendChild(duplicate);
  1231. }
  1232. });
  1233.  
  1234. return transformable;
  1235. }
  1236.  
  1237. function launchScriptManager() {
  1238. const ui = DefinableCore.UI;
  1239. const container = ui.createContainer();
  1240.  
  1241. {
  1242. const row = ui.createRow();
  1243.  
  1244. const scriptSourceLinkInput = ui.createInput("text", { className: "form-control" });
  1245. const requestSourceInput = ui.createInput("button");
  1246. const requestSourceLabel = ui.createLabelFor(requestSourceInput);
  1247. const group = ui.createGroup(scriptSourceLinkInput, requestSourceLabel);
  1248. const spinnerIcon = ui.createSpinner("");
  1249. const defaultIcon = ui.createIcon("external-link-alt");
  1250.  
  1251. requestSourceLabel.appendChild(defaultIcon);
  1252. requestSourceLabel.appendChild(spinnerIcon);
  1253.  
  1254. requestSourceInput.addEventListener("click", function () {
  1255. defaultIcon.classList.add("d-none");
  1256. spinnerIcon.show();
  1257.  
  1258. if (!scriptSourceLinkInput.value.startsWith("http")) {
  1259. helpers.log("Not a valid Link: ".concat(scriptSourceLinkInput.value), "color: firebrick;");
  1260. spinnerIcon.hide();
  1261. defaultIcon.classList.remove("d-none");
  1262. return;
  1263. }
  1264.  
  1265. const requestSourceLink = new URL(scriptSourceLinkInput.value);
  1266. helpers.log("Requesting Module: ".concat(requestSourceLink), "color: rebeccapurple;");
  1267.  
  1268. fetch(requestSourceLink).then(async (response) => {
  1269. const copy = response.clone();
  1270. const content = await copy.text();
  1271.  
  1272. if (copy.status === 200 && content) {
  1273. cache.saveScript(requestSourceLink, content);
  1274. helpers.log("Module installed!", "color: forestgreen;");
  1275. spinnerIcon.hide();
  1276. defaultIcon.classList.remove("d-none");
  1277. scriptSourceLinkInput.value = "";
  1278. }
  1279. });
  1280. });
  1281.  
  1282. row.appendChild(group);
  1283. container.appendChild(row);
  1284. }
  1285.  
  1286. DefinableCore.UI.querySelect("#login-leftcol").appendChild(container);
  1287. }
  1288. })("definable");