Gartic phone DRAW bot

Auto drawing bot (DO NOT OVERDO)

Από την 02/04/2023. Δείτε την τελευταία έκδοση.

  1. // ==UserScript==
  2. // @name Gartic phone DRAW bot
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1.2
  5. // @license LIT
  6. // @description Auto drawing bot (DO NOT OVERDO)
  7. // @author StickySkull & DoctorDeathDDrac
  8. // @source https://t.me/doctordeathddracula
  9. // @source https://t.me/stickyskull
  10. // @supportURL https://discord.gg/sHj5UauJZ4
  11. // @match *://garticphone.com/*
  12. // @icon https://www.google.com/s2/favicons?domain=garticphone.com
  13. // @grant none
  14. // @run-at document-start
  15. // ==/UserScript==
  16.  
  17.  
  18.  
  19. class UpdateUserScript {
  20. constructor() {}
  21.  
  22. cc(tag, options = {}, parent = false, init = false) {
  23. const children = options.children || [];
  24. delete options.children;
  25. const element = Object.assign(document.createElement(tag), options);
  26. for (const child of children) element.appendChild(child);
  27. if (init) init(element);
  28. return parent ? parent.appendChild(element) : element;
  29. }
  30.  
  31. check() {
  32. fetch('https://greasyfork.org/ru/scripts/436728-garticphone-draw-bot/versions.json').then(resp => resp.json().then(json => {
  33. if (json[0].version != GM.info.script.version) {
  34. this.base = this.cc('div', {
  35. style: `width:100%;z-index:10;position:fixed;top:0;transform:scale(${((window.innerWidth - (window.innerWidth < 1920 ? 180 : 320)) / 1150) / 1.5});display:flex;flex-direction:column;align-items:center;transform-origin:top;`,
  36. children: [
  37. this.cc('div', {
  38. style: 'margin-top:5px;display:flex;flex-direction:row;gap:5px;padding:5px 20px;border-radius:5px;border:2px solid white;background-color:#ffffff55;',
  39. children: [
  40. this.cc('div', {
  41. style: `color:red;font-family:'Black';font-size:20px;text-align:center;`,
  42. textContent: GM.info.script.version
  43. }),
  44. this.cc('div', {
  45. style: `color:white;font-family:'Black';font-size:20px;text-align:center;`,
  46. textContent: String.fromCharCode(10140)
  47. }),
  48. this.cc('div', {
  49. style: `color:lime;font-family:'Black';font-size:20px;text-align:center;`,
  50. textContent: json[0].version
  51. }),
  52. this.cc('a', {
  53. href: 'https://greasyfork.org/scripts/436728-garticphone-draw-bot/code/Garticphone%20DRAW%20bot.user.js',
  54. style: `margin-left:12px;color:aqua;font-family:'Black';font-size:20px;text-align:center;`,
  55. textContent: 'UPDATE',
  56. onclick: () => {this.base.remove()}
  57. }),
  58. ]
  59. })
  60. ]
  61. }, document.documentElement);
  62. }
  63. }));
  64. }
  65. }
  66.  
  67. (new UpdateUserScript()).check();
  68.  
  69.  
  70. class Log {
  71. constructor(parent=document.documentElement, maxCount=5, stayTime=3000, disappearanceTime=1000) {
  72. this.Id = Math.random();
  73. this.stayTime = stayTime;
  74. this.maxCount = maxCount;
  75. this.disappearanceTime = disappearanceTime;
  76. this.defaultStyle = '';
  77. this.element = this.cc('div', {
  78. style: `width:100%;z-index:10;position:fixed;top:0;transform:scale(${this.getScale()});display:flex;flex-direction:column;align-items:center;transform-origin:top;`
  79. }, parent);
  80. window.addEventListener('resize', this.update.bind(this));
  81. }
  82.  
  83. cc(tag, options = {}, parent = false, init = false) {
  84. const children = options.children || [];
  85. delete options.children;
  86. const element = Object.assign(document.createElement(tag), options);
  87. for (const child of children) element.appendChild(child);
  88. if (init) init(element);
  89. return parent ? parent.appendChild(element) : element;
  90. }
  91.  
  92. update() {
  93. this.element.style = `width:100%;z-index:2;position:fixed;top:0;transform:scale(${this.getScale()});display:flex;flex-direction:column;align-items:center;transform-origin:top;`;
  94. }
  95.  
  96. getScale() {
  97. return (window.innerWidth - (window.innerWidth < 1920 ? 180 : 320)) / 1150;
  98. }
  99.  
  100. remove() {
  101. window.removeEventListener('resize', this.update.bind(this));
  102. this.element.remove();
  103. }
  104.  
  105. log(text, color='#FFFFFF', timer=this.stayTime, dtimer=this.disappearanceTime) {
  106. if (this.element.childElementCount == this.maxCount) {
  107. clearInterval(this.element.firstChild.__timeout);
  108. this.element.firstChild.remove();
  109. }
  110. this.cc('div', {
  111. style: `display:flex;flex-direction:column;border-radius:10px;color:${color};background-color:${color + '55'};border:solid;padding:10px 50px;margin-top:10px;transition:${dtimer}ms;`,
  112. children: [
  113. this.cc('div', {
  114. style: `color:${color};font-family:'Black';font-size:20px;text-align:center;`,
  115. textContent: text,
  116. })
  117. ]
  118. }, this.element, element => {
  119. element.__timeout = setTimeout(element.remove.bind(element), timer + dtimer);
  120. element.__dtimeout = setTimeout(() => {
  121. element.style.opacity = 0;
  122. }, timer);
  123. });
  124. }
  125. }
  126.  
  127.  
  128. class ImageWorker {
  129. constructor() {
  130. this.sx = 0;
  131. this.sy = 0;
  132. this.sWidth = 0;
  133. this.sHeight = 0;
  134. this.dx = 0;
  135. this.dy = 0;
  136. this.dWidth = 0;
  137. this.dHeight = 0;
  138. this.image = null;
  139. this.canvas = null;
  140. this.context2D = null;
  141. }
  142.  
  143. setRect(sx=0, sy=0, sWidth=0, sHeight=0, dx=0, dy=0, dWidth=0, dHeight=0) {
  144. this.sx = sx;
  145. this.sy = sy;
  146. this.sWidth = sWidth;
  147. this.sHeight = sHeight;
  148. this.dx = dx;
  149. this.dy = dy;
  150. this.dWidth = dWidth;
  151. this.dHeight = dHeight;
  152. }
  153.  
  154. setFitScale() {
  155. if (this.image.width / this.image.height > this.canvas.width / this.canvas.height) {
  156. this.setFitWidth();
  157. } else {
  158. this.setFitHeight();
  159. }
  160. }
  161.  
  162. setFitHeight() {
  163. this.setRect();
  164. const ratio = this.canvas.height / this.image.height;
  165. this.sWidth = this.image.width;
  166. this.sHeight = this.image.height;
  167. this.dHeight = this.canvas.height;
  168. this.dWidth = Math.floor(this.image.width * ratio);
  169. this.dx = Math.floor(this.canvas.width / 2 - (this.image.width * ratio / 2));
  170. }
  171.  
  172. setCover() {
  173. this.setRect();
  174. this.sWidth = this.image.width;
  175. this.sHeight = this.image.height;
  176. this.dWidth = this.canvas.width;
  177. this.dHeight = this.canvas.height;
  178. }
  179.  
  180. setFitWidth() {
  181. this.setRect();
  182. const ratio = this.canvas.width / this.image.width;
  183. this.sWidth = this.image.width;
  184. this.sHeight = this.image.height;
  185. this.dHeight = Math.floor(this.image.height * ratio);
  186. this.dWidth = this.canvas.width;
  187. this.dy = Math.floor(this.canvas.height / 2 - (this.image.height * ratio / 2));
  188. }
  189.  
  190. setImage(image) {
  191. this.image = image;
  192. }
  193.  
  194. setCanvas(canvas) {
  195. this.context2D = (this.canvas = canvas).getContext('2d');
  196. }
  197.  
  198. getCanvasData(x=0, y=0, width=this.canvas.width, height=this.canvas.height) {
  199. return this.context2D.getImageData(x, y, width, height).data;
  200. }
  201.  
  202. getColor(flatdata, width, height, x, y) {
  203. let start = y * width * 4 + x * 4;
  204. return [flatdata[start], flatdata[start + 1], flatdata[start + 2], flatdata[start + 3]];
  205. }
  206.  
  207. print() {
  208. this.context2D.clearRect(0, 0, this.canvas.width, this.canvas.height);
  209. this.context2D.drawImage(this.image, this.sx, this.sy, this.sWidth, this.sHeight, this.dx, this.dy, this.dWidth, this.dHeight);
  210. }
  211. }
  212.  
  213.  
  214. class CanvasWorker {
  215. constructor() {
  216. this.id = Array(16).fill().map(i => 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'[Math.floor(24 * Math.random())]).join('');
  217. this.sketch = document.createElement('canvas');
  218. this.sketch.context = this.sketch.getContext('2d');
  219. this.sketch.id = this.id;
  220. this.sketch.style = 'position:fixed;top:0;left:0;';
  221. }
  222.  
  223. getColorMidDiffRGB([r1, g1, b1], [r2, g2, b2]) {
  224. return Math.abs((r1 + g1 + b1) / 3 - (r2 + g2 + b2) / 3);
  225. }
  226.  
  227. getColorMidDiffRGBA([r1, g1, b1, a1], [r2, g2, b2, a2]) {
  228. return Math.abs((r1 + g1 + b1 + a1) / 4 - (r2 + g2 + b2 + a2) / 4);
  229. }
  230.  
  231. getColorEachDiffRGB([r1, g1, b1], [r2, g2, b2]) {
  232. return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2);
  233. }
  234.  
  235. getColorEachDiffRBGA([r1, g1, b1, a1], [r2, g2, b2, a2]) {
  236. return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2) + Math.abs(a1 - a2);
  237. }
  238.  
  239. getColor(flatdata, width, height, x, y) {
  240. let start = y * width * 4 + x * 4;
  241. return [flatdata[start], flatdata[start + 1], flatdata[start + 2], flatdata[start + 3]];
  242. }
  243.  
  244. findEdges(canvas, bound) {
  245. let data = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height).data;
  246. let cords = Array();
  247. for (let x=0; x < canvas.width - 1; x++) {
  248. for (let y=0; y < canvas.height - 1; y++) {
  249. if (this.getColorEachDiffRGB(
  250. this.getColor(data, canvas.width, canvas.height, x, y),
  251. this.getColor(data, canvas.width, canvas.height, x + 1, y + 1)
  252. ) > bound) {
  253. cords.push([x, y]);
  254. }
  255. }
  256. }
  257. return cords;
  258. }
  259.  
  260. out() {
  261. if (document.querySelector('#' + this.id)) return;
  262. document.documentElement.appendChild(this.sketch);
  263. }
  264.  
  265. }
  266.  
  267.  
  268. class AutoDraw {
  269. constructor() {
  270. this.LOGS = new Log(document.documentElement, 3, 1000);
  271. this.constants = {
  272. maxRatio: 2,
  273. minRatio: 10
  274. };
  275. this.selectors = {
  276. drawingTable: '.draw',
  277. fillToolButton: '.tool.fil',
  278. buttom: '.bottom',
  279. header: '.book .header'
  280. };
  281. this.texts = {
  282. cantOpenItHereWarning: 'You can open menu only in game!',
  283. cantUseItInTHisGameMode: 'Autodrawing cannot be used in this game mode.',
  284. loadFile: 'LOAD IMAGE',
  285. inputOrInsert: 'drop, insert or CTRL + V',
  286. clearImg: 'CLEAR',
  287. print: 'PRINT',
  288. ratioDesc: 'less ratio less effective, but more quality',
  289. drewThisThisRound: "You can't draw more then one pic (or server will exploude!)",
  290. nothingTodraw: 'Nothing to draw.',
  291. cantPaseOnClosedMenu: 'Cannot paste on closed menu.',
  292. cannotUploadM20: 'You cannot upload image more than 20MB!',
  293. cannotPasteM20: 'You cannot paste image more than 20MB!',
  294. fileNotAttached: 'Error, no files attached.'
  295. };
  296. this.state = {
  297. drewThisRound: false,
  298. currentFile: null,
  299. canUpdateMenu: true,
  300. iCanvas: null,
  301. printRatio: 2,
  302. ws: null,
  303. turnNum: null
  304. };
  305. }
  306.  
  307. init() {
  308. this.setTraps();
  309. this.setKeboardListener();
  310. this.setTriggerOnElement(this.selectors.drawingTable, this.onDrawingTable.bind(this));
  311. this.setTriggerOnElement(this.selectors.drawingTable, this.removeMenu.bind(this), 'removedNodes');
  312. this.addOnResize();
  313. this.addOnPaste();
  314. }
  315.  
  316. addOnPaste() {
  317. window.addEventListener('paste', event => {
  318. let items = (event.clipboardData || event.originalEvent.clipboardData).items;
  319. for (let index in items) {
  320. let item = items[index];
  321. if (item.kind == 'file' && item.type.includes('image')) {
  322. if (this.isMenuOpened()) {
  323. this.imageDataInput(item.getAsFile(), 'paste');
  324. } else {
  325. this.LOGS.log(this.texts.cantPaseOnClosedMenu, '#FF8800');
  326. }
  327. }
  328. }
  329. });
  330. }
  331.  
  332. addOnResize() {
  333. window.addEventListener('resize', this.onWindowResize.bind(this));
  334. }
  335.  
  336. onWindowResize() {
  337. this.updateMenu();
  338. }
  339.  
  340. setTraps() {
  341. this.proxyWebSocket();
  342. }
  343.  
  344. proxyWebSocket() {
  345. const t = this;
  346. window.WebSocket = new Proxy(WebSocket, {
  347. construct(target, args) {
  348. let ws = new target(...args);
  349. t.initWS(ws);
  350. return ws;
  351. }
  352. });
  353. }
  354.  
  355. initWS(ws) {
  356. window._WS = this.state.ws = ws;
  357. // ws.send = new Proxy(ws.send, {
  358. // apply(target, thisArg, args) {
  359. // console.log(args[0]);
  360. // return Reflect.apply(...arguments);
  361. // }
  362. // });
  363. ws.addEventListener('message', this.onWebSocketMessage.bind(this));
  364. }
  365.  
  366. onWebSocketMessage(event) {
  367. if (!event.data.includes('[')) return;
  368. const data = JSON.parse(event.data.replace(/^\d+/g, ''));
  369. switch (data[1]) {
  370. case 11: {
  371. this.state.drewThisRound = false;
  372. this.state.turnNum = data[2].turnNum;
  373. break;
  374. }
  375. }
  376. }
  377.  
  378. getScale() {
  379. return (window.innerWidth - (window.innerWidth < 1920 ? 180 : 320)) / 1150;
  380. }
  381.  
  382. setKeboardListener() {
  383. window.addEventListener('keydown', this.onKeyDown.bind(this));
  384. }
  385.  
  386. onKeyDown(event) {
  387. this.keyDownSwitchTable(event);
  388. }
  389.  
  390. keyDownSwitchTable(event) {
  391. switch (event.which || event.keyCode) {
  392. case 120: this.switchMenu(); break;
  393. case 27: this.removeMenu(); break;
  394. }
  395. }
  396.  
  397. cc(tag, options = {}, parent = false, init = false) {
  398. const children = options.children || [];
  399. delete options.children;
  400. const element = Object.assign(document.createElement(tag), options);
  401. for (const child of children) element.appendChild(child);
  402. if (init) init(element);
  403. return parent ? parent.appendChild(element) : element;
  404. }
  405.  
  406. onDrawingTable(element) {
  407. if (element.querySelector(this.selectors.fillToolButton)) this.generateDrawMenuButton(element);
  408. }
  409.  
  410. generateDrawMenuButton(element) {
  411. const header = element.querySelector(this.selectors.header);
  412. this.cc('button', {
  413. textContent: String.fromCharCode(9998),
  414. style: 'position:absolute;color:white;appearance:none;outline:none;border:none;background-color:transparent;font-size:30px;left:50px;top:6px;cursor:pointer;',
  415. onclick: this.switchMenu.bind(this)
  416. }, header);
  417. }
  418.  
  419. switchMenu() {
  420. const menuBG = document.body.querySelector('.my-bg');
  421. const drawTable = document.body.querySelector(this.selectors.drawingTable);
  422. const fillTool = document.body.querySelector(this.selectors.fillToolButton);
  423. if (menuBG) {
  424. menuBG.remove();
  425. } else {
  426. if (!drawTable) {
  427. this.LOGS.log(this.texts.cantOpenItHereWarning, '#FF6666');
  428. } else if (drawTable && !fillTool) {
  429. this.LOGS.log(this.texts.cantUseItInTHisGameMode, '#FF6666');
  430. } else {
  431. this.generateMenu();
  432. }
  433. }
  434. }
  435.  
  436. removeMenu() {
  437. const menuBG = document.body.querySelector('.my-bg');
  438. if (menuBG) menuBG.remove();
  439. }
  440.  
  441. isMenuOpened() {
  442. const menuBG = document.body.querySelector('.my-bg');
  443. const drawTable = document.body.querySelector(this.selectors.drawingTable);
  444. const fillTool = document.body.querySelector(this.selectors.fillToolButton);
  445.  
  446. if (!drawTable) {
  447. this.removeMenu();
  448. return false;
  449. } else if (drawTable && !fillTool) {
  450. this.removeMenu();
  451. return false;
  452. }
  453.  
  454. return Boolean(menuBG);
  455. }
  456.  
  457. updateMenu() {
  458. if (this.isMenuOpened()) {
  459. const menuBG = document.body.querySelector('.my-bg');
  460. menuBG.remove();
  461. this.generateMenu();
  462. }
  463. }
  464.  
  465. getBase64(file, callback) {
  466. const reader = new FileReader();
  467. reader.readAsDataURL(file);
  468. reader.onload = callback.bind(this, reader);
  469. reader.onerror = error => this.LOGS.log('Error in <AutoDraw.getBase64>: ' + error, '#000000');
  470. }
  471.  
  472. loadImage(reader) {
  473. const image = new Image();
  474. image.src = reader.result;
  475. image.onload = this.onImageLoaded.bind(this);
  476. }
  477.  
  478. async print() {
  479. if (!this.state.iCanvas) return this.LOGS.log(this.texts.nothingTodraw, '#FF5533');
  480. if (this.state.drewThisRound) return this.LOGS.log(this.texts.drewThisThisRound, '#FF5533');
  481.  
  482. this.state.drewThisRound = true;
  483.  
  484. const originalMap = {};
  485. const data = this.state.iCanvas.getCanvasData();
  486. const width = this.state.iCanvas.canvas.width;
  487. const height = this.state.iCanvas.canvas.height;
  488. const insensetiveAlpha = 20;
  489. const maxDifference = 50;
  490.  
  491. function sleep(ms) {
  492. return new Promise(resolve => setTimeout(resolve, ms));
  493. };
  494.  
  495. function componentToHex(c) {
  496. let hex = c.toString(16);
  497. return hex.length == 1 ? "0" + hex : hex;
  498. };
  499.  
  500. function rgbaToHex(r, g, b, a) {
  501. return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b) + componentToHex(a);
  502. };
  503.  
  504. function hexToRgba(hex) {
  505. let bigint = parseInt(hex.split('#')[1], 16);
  506. return [(bigint >> 24) & 255, (bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];
  507. }
  508.  
  509. function getColorEachDiffRBGA([r1, g1, b1, a1], [r2, g2, b2, a2]) {
  510. return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2) + Math.abs(a1 - a2);
  511. };
  512.  
  513. function getColorMidDiffRGBA([r1, g1, b1, a1], [r2, g2, b2, a2]) {
  514. return Math.abs((r1 + g1 + b1 + a1) / 4 - (r2 + g2 + b2 + a2) / 4);
  515. };
  516.  
  517. function getMidColorFromHEX(hexArray) {
  518. if (hexArray.length > 1) {
  519. for (let i = 0; i < hexArray.length - 1; i++) {
  520. let color1 = hexToRgba(hexArray[i]);
  521. let color2 = hexToRgba(hexArray[i + 1]);
  522. return rgbaToHex(
  523. Math.floor((color1[0] + color2[0]) / 2),
  524. Math.floor((color1[1] + color2[1]) / 2),
  525. Math.floor((color1[2] + color2[2]) / 2),
  526. Math.floor((color1[3] + color2[3]) / 2)
  527. );
  528. }
  529. }
  530. return hexArray[0];
  531. }
  532.  
  533. function getMidHex(hex1, hex2) {
  534. let color1 = hexToRgba(hex1);
  535. let color2 = hexToRgba(hex2);
  536. return rgbaToHex(
  537. Math.floor((color1[0] + color2[0]) / 2),
  538. Math.floor((color1[1] + color2[1]) / 2),
  539. Math.floor((color1[2] + color2[2]) / 2),
  540. Math.floor((color1[3] + color2[3]) / 2)
  541. );
  542. }
  543.  
  544. for (let x = 0; x < width; x += this.state.printRatio) {
  545. for (let y = 0; y < height; y+=this.state.printRatio) {
  546. let start = y * width * 4 + x * 4;
  547. let color = [data[start], data[start + 1], data[start + 2], data[start + 3]];
  548.  
  549. if (color[3] < insensetiveAlpha) continue;
  550.  
  551. let hexColor = rgbaToHex(color[0], color[1], color[2], color[3]);
  552. let b = hexColor.split('');
  553. b[2] = '0';
  554. b[4] = '0';
  555. b[6] = '0';
  556. b[8] = '0';
  557. hexColor = b.join('');
  558. let place = `${x},${y},${this.state.printRatio},${this.state.printRatio}`;
  559. originalMap[hexColor] = originalMap[hexColor] ? originalMap[hexColor] + ',' + place : place;
  560. }
  561. }
  562.  
  563. // extension
  564. const changedMap = {};
  565. const subMap = JSON.parse(JSON.stringify(originalMap));
  566. while (Object.keys(subMap).length !== 0) {
  567. const color = Object.keys(subMap)[0];
  568. const closeColors = [color];
  569. let closePath = subMap[color];
  570. delete subMap[color];
  571. for (let next in subMap) {
  572. let rgba1 = hexToRgba(color);
  573. let rgba2 = hexToRgba(next);
  574. if (getColorEachDiffRBGA(rgba1, rgba2) < maxDifference) {
  575. closeColors.push(next);
  576. closePath += "," + subMap[next];
  577. delete subMap[next];
  578. }
  579. }
  580. changedMap[getMidColorFromHEX(closeColors)] = closePath;
  581. }
  582. // extension end
  583.  
  584. let vId = 1;
  585. for (let color in changedMap) {
  586. let opacity = Math.round(Number("0x" + color.substr(7)) / 255 * 100) / 100;
  587. this.state.ws.send(`42[2,7,{"t":${this.state.turnNum},"d":1,"v":[8,${vId++},["${color.substr(0, 7)}",${opacity}],${changedMap[color]}]}]`);
  588. await sleep(150);
  589. }
  590.  
  591. // let vId = 1;
  592. // for (let color in originalMap) {
  593. // let opacity = Math.round(Number("0x" + color.substr(7)) / 255 * 100) / 100;
  594. // this.state.ws.send(`42[2,7,{"t":${this.state.turnNum},"d":1,"v":[8,${vId++},["${color.substr(0, 7)}",${opacity}],${originalMap[color]}]}]`);
  595. // await sleep(150);
  596. // }
  597.  
  598. this.removeMenu();
  599. setTimeout(() => this.executeReconnect(), 2e3);
  600. }
  601.  
  602. executeReconnect() {
  603. this.state.ws.close();
  604. }
  605.  
  606. onImageLoaded(event) {
  607. const image = event.target;
  608. if (!this.isMenuOpened()) return this.LOGS.log('Menu closed.', '#000000');
  609. this.state.iCanvas = new ImageWorker(0, 0, image.width, image.height, 0, 0, 500, 500);
  610. this.state.iCanvas.setImage(image);
  611. this.state.iCanvas.setCanvas(this.state.canvas);
  612.  
  613. this.state.iCanvas.setFitScale();
  614. this.state.iCanvas.print();
  615. this.updateMenu();
  616.  
  617. // this.state.cw = new CanvasWorker();
  618.  
  619. // this.state.cw.sketch.width = this.state.canvas.width;
  620. // this.state.cw.sketch.height = this.state.canvas.height;
  621.  
  622. // const a = this.state.cw.findEdges(this.state.canvas, 100);
  623. // this.state.cw.sketch.context.fillStyle = '#FFFFFF';
  624. // this.state.cw.sketch.context.fillRect(0, 0, this.state.cw.sketch.width, this.state.cw.sketch.height);
  625. // this.state.cw.sketch.context.fillStyle = '#000000';
  626. // for (let p of a) this.state.cw.sketch.context.fillRect(p[0], p[1], 1, 1);
  627. // this.state.cw.out();
  628. }
  629.  
  630. async imageDataInput(data, type) {
  631. if (['fileinput-ondrop', 'fileinput-onchange'].includes(type)) {
  632. const file = data.target.files[0];
  633. if (file) {
  634. if (file.size > 20971520) {
  635. this.LOGS.log(this.texts.cannotUploadM20, '#FF6666');
  636. } else {
  637. this.state.currentFile = file;
  638. this.getBase64(file, this.loadImage.bind(this));
  639. }
  640. } else {
  641. this.LOGS.log(this.texts.fileNotAttached, '#000000');
  642. }
  643. } else if (['paste'].includes(type)) {
  644. if (data) {
  645. if (data.size > 20971520) {
  646. this.LOGS.log(this.texts.cannotPasteM20, '#FF6666');
  647. } else {
  648. this.state.currentFile = data;
  649. this.getBase64(data, this.loadImage.bind(this));
  650. }
  651. } else {
  652. this.LOGS.log(this.texts.fileNotAttached, '#000000');
  653. }
  654. }
  655. }
  656.  
  657.  
  658. generateMenu() {
  659. this.cc('div', {
  660. className: 'my-bg',
  661. style: 'width:100%;height:100%;background-color:rgba(0,0,0,0.8);position:absolute;left:0;top:0;z-index:2;-webkit-box-pack:center;justify-content:center;-webkit-box-align:center;align-items:center;display:flex;inset:0;',
  662. onclick: event => {
  663. if (event.currentTarget == event.target) this.removeMenu();
  664. },
  665. children: [
  666. this.cc('style', {
  667. textContent: `.-dashed-line:hover {opacity:1 !important;}`
  668. .concat(`#-reset-menu-preview:hover {background-color:white !important;border:4px solid black !important;color:black !important;}`)
  669. .concat(`.control-button {flex:1;background-color:black;border-radius:7px;border:2px solid black;color:white;font-family:Black;font-size:20px;padding:0 10px;cursor:pointer;}`)
  670. .concat(`.control-button:hover {background-color:white !important;border:2px solid black !important;color:black !important;}`)
  671. }),
  672. this.cc('div', {
  673. style: `position:relative;display:flex;flex-direction:column;-webkit-box-align:center;align-items:center;background-color:rgb(255,255,255);padding:25px 30px;border-radius:12px;transform:scale(${this.getScale()});`,
  674. children: [
  675. this.cc('button', {
  676. style: 'border:none;background:none;position:absolute;top:15px;right:15px;width:30px;height:30px;display:flex;-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;cursor:pointer;',
  677. children: [
  678. this.cc('div', {
  679. style: 'font-family:ico;color:rgb(172,167,198);font-size:25px;',
  680. textContent: String.fromCharCode(59654),
  681. })
  682. ],
  683. onclick: this.removeMenu.bind(this)
  684. }),
  685. this.cc('div', {
  686. style: 'display:flex;flex-direction:column;align-items:center;margin-bottom:20px;',
  687. children: [
  688. this.cc('div', {
  689. style: 'color:black;font-family:Black;font-size:40px;',
  690. textContent: this.texts.loadFile
  691. })
  692. ]
  693. }),
  694. this.cc('div', {
  695. style: 'display:flex;flex-direction:row;',
  696. className: '-flex-settings',
  697. children: [
  698. this.cc('div', {
  699. className: '-screen-pad-with-bottom',
  700. style: 'display:flex;flex-direction:column',
  701. children: [
  702. this.cc('div', {
  703. className: '.m-screen',
  704. style: 'width:758px;height:424px;',
  705. children: [
  706. this.cc('div', {
  707. style: `width:758px;height:424px;position:absolute;display:flex;flex-direction:column;align-items:center;justify-content:center;opacity:0.3;`.concat(this.state.iCanvas ? 'display:none;' : ''),
  708. children: [
  709. this.cc('div', {
  710. style: 'display:flex;flex-direction:column;align-items:center;',
  711. children: [
  712. this.cc('div', {
  713. style: 'algin-text:center;font-family:Black;font-size:50px;',
  714. textContent: this.state.currentFile ? this.state.currentFile.name : this.texts.inputOrInsert
  715. })
  716. ]
  717. })
  718. ]
  719. }),
  720. this.cc('div', {
  721. className: '-dashed-line',
  722. style: `position:absolute;border:4px dashed black;border-radius:10px;opacity:0.3;`.concat(this.state.iCanvas ? 'display:none;' : ''),
  723. children: [
  724. this.cc('input', {
  725. type: "file",
  726. accept: "image/*",
  727. style: `width:758px;height:424px;cursor:pointer;position:relative;opacity:0;`,
  728. ondragenter: event => {
  729. event.preventDefault();
  730. },
  731. ondrop: event => {
  732. event.preventDefault();
  733. this.imageDataInput(event, 'fileinput-ondrop');
  734. },
  735. onchange: event => {
  736. this.imageDataInput(event, 'fileinput-onchange');
  737. }
  738. })
  739. ]
  740. }),
  741. this.cc('div', {
  742. style: 'position:absolute;background-repeat:repeat;background-size:22px;image-rendering:pixelated;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAABb2lDQ1BpY2MAACiRdZHNK0RRGMZ/M4gYLFASNYshC0qUZKVRZjMsxiiDzdw7986o+bjdO5MmW2VjoSzExtfCf8BW2VJKkZKs/AG+Npqu97hqJM7t3PfXc87zds5zwB/N6jmndghy+aIdi4SD84mFYP0TTbTQQQ/jSd2xpmen4vw73m/wqXo9qHr9v+/P0ZQyHB18DcKjumUXhSeEoytFS/GGcLueSaaE94UHbDmg8IXSNY8fFac9flVsx2OT4Fc9g+kfrP1gPWPnhPuFQ7lsSf8+j7pJwMjPzUrtktmNQ4wIYYJolFgmS5FBqXnJ7G/f0JdvhoJ4dPlblLHFkSYj3gFRS9LVkGqKbsiXpaxy/52nY44Me90DYah7cN2XXqjfgsqm634cuG7lEGru4Sxf9Rckp7E30TerWmgPWtfg5Lyqadtwug6dd1bSTn5JNTL9pgnPx9CcgLYraFz0svpe5+gW4qvyRJewswt9sr916RNJMmgsonGPPAAAAAlwSFlzAAAuIwAALiMBeKU/dgAAABhJREFUCNdj3Lp1639RUVEGJhDx+vVrBgA9CAZhgyB+jwAAAABJRU5ErkJggg==);width:758px;height:424px;border:2px solid;'.concat(this.state.iCanvas ? '' : 'display:none;'),
  743. children: [
  744. this.state.canvas || (this.state.canvas = this.cc('canvas', {
  745. style: 'width:758px;height:424px;position:absolute;',
  746. width: 758,
  747. height: 424
  748. }))
  749. ]
  750. }),
  751. ]
  752. }),
  753. this.cc('div', {
  754. style: 'display:flex;flex-direction:row;width:100%;margin-top:25px;gap:10px;',
  755. children: [
  756. this.cc('button', {
  757. id: '-reset-menu-preview',
  758. style: 'background-color:black;border-radius:7px;border:4px solid black;color:white;font-family:Black;font-size:28px;padding:0 30px;cursor:pointer;height:40px;',
  759. textContent: this.texts.print,
  760. onclick: () => {
  761. this.print();
  762. }
  763. }),
  764. this.cc('div', {
  765. style: 'flex:1;display:flex;flex-direction:column;',
  766. children: [
  767. this.generateRange('ratio', this.constants.maxRatio, this.constants.minRatio, this.state.printRatio, 1, event => {
  768. this.state.printRatio = Number(event.target.value);
  769. }),
  770. this.cc('div', {
  771. style: 'font-family:Black;font-size:15px;',
  772. textContent: this.texts.ratioDesc,
  773. })
  774. ]
  775. }),
  776. this.cc('button', {
  777. id: '-reset-menu-preview',
  778. style: 'background-color:black;border-radius:7px;border:4px solid black;color:white;font-family:Black;font-size:28px;padding:0 30px;cursor:pointer;height:40px;',
  779. textContent: this.texts.clearImg,
  780. onclick: () => {
  781. this.state.canvas = null;
  782. this.state.currentFile = null;
  783. this.state.iCanvas = null;
  784. this.updateMenu();
  785. }
  786. })
  787. ]
  788. })
  789. ]
  790. })
  791. ].concat(this.state.currentFile ? [
  792. this.cc('div', {
  793. className: '-settings-plane',
  794. style: 'margin-left:30px;max-width:200px;width:200px;',
  795. children: [
  796. this.generateRange('dx', -this.state.iCanvas.canvas.width, this.state.iCanvas.canvas.width, this.state.iCanvas.dx, 1, event => {
  797. this.state.iCanvas.dx = Number(event.target.value);
  798. this.state.iCanvas.print();
  799. }),
  800. this.generateRange('dy', -this.state.iCanvas.canvas.height, this.state.iCanvas.canvas.height, this.state.iCanvas.dy, 1, event => {
  801. this.state.iCanvas.dy = Number(event.target.value);
  802. this.state.iCanvas.print();
  803. }),
  804. this.generateRange('dw', 10, this.state.iCanvas.canvas.width * 2, this.state.iCanvas.dWidth, 1, event => {
  805. this.state.iCanvas.dWidth = Number(event.target.value);
  806. this.state.iCanvas.print();
  807. }),
  808. this.generateRange('dh', 10, this.state.iCanvas.canvas.height * 2, this.state.iCanvas.dHeight, 1, event => {
  809. this.state.iCanvas.dHeight = Number(event.target.value);
  810. this.state.iCanvas.print();
  811. }),
  812. this.generateRange('sx', 0, this.state.iCanvas.image.width, this.state.iCanvas.sx, 1, event => {
  813. this.state.iCanvas.sx = Number(event.target.value);
  814. this.state.iCanvas.print();
  815. }),
  816. this.generateRange('sy', 0, this.state.iCanvas.image.height, this.state.iCanvas.sy, 1, event => {
  817. this.state.iCanvas.sy = Number(event.target.value);
  818. this.state.iCanvas.print();
  819. }),
  820. this.generateRange('sw', 10, this.state.iCanvas.image.height, this.state.iCanvas.sWidth, 1, event => {
  821. this.state.iCanvas.sWidth = Number(event.target.value);
  822. this.state.iCanvas.print();
  823. }),
  824. this.generateRange('sh', 10, this.state.iCanvas.image.height, this.state.iCanvas.sHeight, 1, event => {
  825. this.state.iCanvas.sHeight = Number(event.target.value);
  826. this.state.iCanvas.print();
  827. }),
  828. this.cc('div', {
  829. style: 'display:flex;flex-direction:column;gap:3px;',
  830. children: [
  831. this.cc('button', {
  832. className: 'control-button',
  833. textContent: 'cover',
  834. onclick: () => {
  835. this.state.iCanvas.setCover();
  836. this.state.iCanvas.print();
  837. this.updateMenu();
  838. }
  839. }),
  840. this.cc('button', {
  841. className: 'control-button',
  842. textContent: 'fit height',
  843. onclick: () => {
  844. this.state.iCanvas.setFitHeight();
  845. this.state.iCanvas.print();
  846. this.updateMenu();
  847. }
  848. }),
  849. this.cc('button', {
  850. className: 'control-button',
  851. textContent: 'fit width',
  852. onclick: () => {
  853. this.state.iCanvas.setFitWidth();
  854. this.state.iCanvas.print();
  855. this.updateMenu();
  856. }
  857. }),
  858. this.cc('button', {
  859. className: 'control-button',
  860. textContent: 'smart fit',
  861. onclick: () => {
  862. this.state.iCanvas.setFitScale();
  863. this.state.iCanvas.print();
  864. this.updateMenu();
  865. }
  866. })
  867. ]
  868. })
  869. ]
  870. })
  871. ] : [])
  872. })
  873. ]
  874. })
  875. ]
  876. }, document.body);
  877. }
  878.  
  879. generateRange(name, from, to, cur, step, inputCallback) {
  880. return this.cc('div', {
  881. style: 'display:flex;flex-direction:row;max-width:200px;gap:5px;width:200px;height:42px;',
  882. children: [
  883. this.cc('div', {
  884. style: 'display:flex;align-items:center;justify-content:center;',
  885. children: [
  886. this.cc('div', {
  887. style: 'color:black;font-family:Black;font-size:20px;',
  888. textContent: name
  889. })
  890. ]
  891. }),
  892. this.cc('div', {
  893. style: 'max-width:140px;min-width:140px;',
  894. children: [
  895. this.cc('div', {
  896. style: 'display:flex;flex-direction:row;',
  897. children: [
  898. this.cc('div', {
  899. style: 'color:black;font-family:Black;font-size:14px;',
  900. textContent: from
  901. }),
  902. this.cc('div', {
  903. style: 'flex:1;display:flex;flex-direction:column;align-items:center;',
  904. children: [
  905. this.cc('div', {
  906. style: 'color:black;font-family:Black;font-size:14px;',
  907. textContent: Math.floor(to - (to - from) / 2)
  908. })
  909. ]
  910. }),
  911. this.cc('div', {
  912. style: 'color:black;font-family:Black;font-size:14px;',
  913. textContent: to
  914. })
  915. ]
  916. }),
  917. this.cc('input', {
  918. style: 'width:100%;',
  919. type: 'range',
  920. min: from,
  921. max: to,
  922. step: step,
  923. oninput: event => {
  924. event.target.parentNode.parentNode.querySelector('#dispval').textContent = event.target.value;
  925. inputCallback.call(this, event);
  926. }
  927. }, false, element => (element.value=cur)),
  928. ]
  929. }),
  930. this.cc('div', {
  931. style: 'display:flex;align-items:center;justify-content:center;',
  932. children: [
  933. this.cc('div', {
  934. style: 'color:black;font-family:Black;font-size:20px;',
  935. id: 'dispval',
  936. textContent: cur
  937. })
  938. ]
  939. })
  940. ]
  941. });
  942. }
  943.  
  944. setTriggerOnElement(selector, callback, action='addedNodes', once=false, searchIn=document) {
  945. const observer = new MutationObserver(mutations => {
  946. for (const mutation of mutations) {
  947. const nodes = mutation[action] || [];
  948. for (const node of nodes) {
  949. const element = node.matches && node.matches(selector) ? node : (node.querySelector ? node.querySelector(selector) : null);
  950. if (element) {
  951. if (once) {
  952. observer.disconnect();
  953. return callback(element);
  954. } else {
  955. callback(element);
  956. }
  957. }
  958. }
  959. }
  960. });
  961.  
  962. observer.observe(searchIn, {
  963. attributes: false,
  964. childList: true,
  965. subtree: true
  966. });
  967.  
  968. return observer;
  969. }
  970.  
  971. }
  972.  
  973. const AD = new AutoDraw();
  974. AD.init();
  975.