Gartic phone DRAW bot

Auto drawing bot (DO NOT OVERDO)

  1. // ==UserScript==
  2. // @name Gartic phone DRAW bot
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1.10
  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://greatest.deepsurf.us/ru/scripts/441179-garticphone-draw-bot-mod-v2/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://greatest.deepsurf.us/ru/scripts/441179-gartic-phone-draw-bot',
  54. style: `margin-left:12px;color:aqua;font-family:'Black';font-size:20px;text-align:center;`,
  55. textContent: 'ОБНОВИТЬ',
  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) / 1 - (r2 + g2 + b2) / 43);
  225. }
  226.  
  227. getColorMidDiffRGBA([r1, g1, b1, a1], [r2, g2, b2, a2]) {
  228. return Math.abs((r1 + g1 + b1 + a1) / 1 - (r2 + g2 + b2 + a2) / 3);
  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. console.log("working",r1, g1, b1, a1,r2, g2, b2, a2)
  237. console.log("working2",Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2) + Math.abs(a1 - a2))
  238. return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2) + Math.abs(a1 - a2);
  239. }
  240.  
  241. getColor(flatdata, width, height, x, y) {
  242. let start = y * width * 4 + x * 4;
  243. return [flatdata[start], flatdata[start + 1], flatdata[start + 2], flatdata[start + 3]];
  244. }
  245.  
  246. findEdges(canvas, bound) {
  247. let data = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height).data;
  248. let cords = Array();
  249. for (let x=0; x < canvas.width - 1; x++) {
  250. for (let y=0; y < canvas.height - 1; y++) {
  251. if (this.getColorEachDiffRGB(
  252. this.getColor(data, canvas.width, canvas.height, x, y),
  253. this.getColor(data, canvas.width, canvas.height, x + 1, y + 1)
  254. ) > bound) {
  255. cords.push([x, y]);
  256. }
  257. }
  258. }
  259. return cords;
  260. }
  261.  
  262. out() {
  263. if (document.querySelector('#' + this.id)) return;
  264. document.documentElement.appendChild(this.sketch);
  265. }
  266.  
  267. }
  268.  
  269.  
  270. class AutoDraw {
  271. constructor() {
  272. this.LOGS = new Log(document.documentElement, 1, 1000);
  273. this.constants = {
  274. maxRatio: 1, // 1
  275. minRatio: 3
  276. };
  277. this.selectors = {
  278. drawingTable: '.draw',
  279. fillToolButton: '.tool.fil',
  280. buttom: '.bottom',
  281. header: '.book .header'
  282. };
  283. this.texts = {
  284. cantOpenItHereWarning: 'Панель управления можно открыть только в игре.',
  285. cantUseItInTHisGameMode: 'Выстрел невозможен.',
  286. loadFile: 'Панель управления',
  287. inputOrInsert: 'Выбор снаряда или Ctrl+V',
  288. clearImg: 'Разрядить',
  289. print: 'Ёбнуть!',
  290. ratioDesc: 'Лучше всего ставить 2.',
  291. drewThisThisRound: "Повторный выстрел невозможен.",
  292. nothingTodraw: 'Орудие на заряжено.',
  293. cantPaseOnClosedMenu: 'Невозможно зарядить, панель управления закрыта.',
  294. cannotUploadM20: 'Невозможно зарядиь, неподходящий калибр.',
  295. cannotPasteM20: 'Невозможно зарядить этим файлом',
  296. fileNotAttached: 'Орудие на заряжено.'
  297. };
  298. this.state = {
  299. drewThisRound: false,
  300. currentFile: null,
  301. canUpdateMenu: true,
  302. iCanvas: null,
  303. printRatio: 2,
  304. ws: null,
  305. turnNum: null
  306. };
  307. }
  308.  
  309. init() {
  310. this.setTraps();
  311. this.setKeboardListener();
  312. this.setTriggerOnElement(this.selectors.drawingTable, this.onDrawingTable.bind(this));
  313. this.setTriggerOnElement(this.selectors.drawingTable, this.removeMenu.bind(this), 'removedNodes');
  314. this.addOnResize();
  315. this.addOnPaste();
  316. }
  317.  
  318. addOnPaste() {
  319. window.addEventListener('paste', event => {
  320. let items = (event.clipboardData || event.originalEvent.clipboardData).items;
  321. for (let index in items) {
  322. let item = items[index];
  323. if (item.kind == 'file' && item.type.includes('image')) {
  324. if (this.isMenuOpened()) {
  325. this.imageDataInput(item.getAsFile(), 'paste');
  326. } else {
  327. this.LOGS.log(this.texts.cantPaseOnClosedMenu, '#FF8800');
  328. }
  329. }
  330. }
  331. });
  332. }
  333.  
  334. addOnResize() {
  335. window.addEventListener('resize', this.onWindowResize.bind(this));
  336. }
  337.  
  338. onWindowResize() {
  339. this.updateMenu();
  340. }
  341.  
  342. setTraps() {
  343. this.proxyWebSocket();
  344. }
  345.  
  346. proxyWebSocket() {
  347. const t = this;
  348. window.WebSocket = new Proxy(WebSocket, {
  349. construct(target, args) {
  350. let ws = new target(...args);
  351. t.initWS(ws);
  352. return ws;
  353. }
  354. });
  355. }
  356.  
  357. initWS(ws) {
  358. window._WS = this.state.ws = ws;
  359. // ws.send = new Proxy(ws.send, {
  360. // apply(target, thisArg, args) {
  361. // console.log(args[0]);
  362. // return Reflect.apply(...arguments);
  363. // }
  364. // });
  365. ws.addEventListener('message', this.onWebSocketMessage.bind(this));
  366. }
  367.  
  368. onWebSocketMessage(event) {
  369. if (!event.data.includes('[')) return;
  370. const data = JSON.parse(event.data.replace(/^\d+/g, ''));
  371. switch (data[1]) {
  372. case 11: {
  373. this.state.drewThisRound = false;
  374. this.state.turnNum = data[2].turnNum;
  375. break;
  376. }
  377. }
  378. }
  379.  
  380. getScale() {
  381. return (window.innerWidth - (window.innerWidth < 1920 ? 180 : 320)) / 1150;
  382. }
  383.  
  384. setKeboardListener() {
  385. window.addEventListener('keydown', this.onKeyDown.bind(this));
  386. }
  387.  
  388. onKeyDown(event) {
  389. this.keyDownSwitchTable(event);
  390. }
  391.  
  392. keyDownSwitchTable(event) {
  393. switch (event.which || event.keyCode) {
  394. case 120: this.switchMenu(); break;
  395. case 27: this.removeMenu(); break;
  396. }
  397. }
  398.  
  399. cc(tag, options = {}, parent = false, init = false) {
  400. const children = options.children || [];
  401. delete options.children;
  402. const element = Object.assign(document.createElement(tag), options);
  403. for (const child of children) element.appendChild(child);
  404. if (init) init(element);
  405. return parent ? parent.appendChild(element) : element;
  406. }
  407.  
  408. onDrawingTable(element) {
  409. if (element.querySelector(this.selectors.fillToolButton)) this.generateDrawMenuButton(element);
  410. }
  411.  
  412. generateDrawMenuButton(element) {
  413. const header = element.querySelector(this.selectors.header);
  414. this.cc('button', {
  415. textContent: String.fromCharCode(9423),
  416. style: 'position:absolute;color:red;appearance:none;outline:none;border:none;background-color:transparent;font-size:35px;left:50px;top:6px;cursor:pointer;',
  417. onclick: this.switchMenu.bind(this)
  418. }, header);
  419. }
  420.  
  421. switchMenu() {
  422. const menuBG = document.body.querySelector('.my-bg');
  423. const drawTable = document.body.querySelector(this.selectors.drawingTable);
  424. const fillTool = document.body.querySelector(this.selectors.fillToolButton);
  425. if (menuBG) {
  426. menuBG.remove();
  427. } else {
  428. if (!drawTable) {
  429. this.LOGS.log(this.texts.cantOpenItHereWarning, '#FF6666');
  430. } else if (drawTable && !fillTool) {
  431. this.LOGS.log(this.texts.cantUseItInTHisGameMode, '#FF6666');
  432. } else {
  433. this.generateMenu();
  434. }
  435. }
  436. }
  437.  
  438. removeMenu() {
  439. const menuBG = document.body.querySelector('.my-bg');
  440. if (menuBG) menuBG.remove();
  441. }
  442.  
  443. isMenuOpened() {
  444. const menuBG = document.body.querySelector('.my-bg');
  445. const drawTable = document.body.querySelector(this.selectors.drawingTable);
  446. const fillTool = document.body.querySelector(this.selectors.fillToolButton);
  447.  
  448. if (!drawTable) {
  449. this.removeMenu();
  450. return false;
  451. } else if (drawTable && !fillTool) {
  452. this.removeMenu();
  453. return false;
  454. }
  455.  
  456. return Boolean(menuBG);
  457. }
  458.  
  459. updateMenu() {
  460. if (this.isMenuOpened()) {
  461. const menuBG = document.body.querySelector('.my-bg');
  462. menuBG.remove();
  463. this.generateMenu();
  464. }
  465. }
  466.  
  467. getBase64(file, callback) {
  468. const reader = new FileReader();
  469. reader.readAsDataURL(file);
  470. reader.onload = callback.bind(this, reader);
  471. reader.onerror = error => this.LOGS.log('Error in <AutoDraw.getBase64>: ' + error, '#000000');
  472. }
  473.  
  474. loadImage(reader) {
  475. const image = new Image();
  476. image.src = reader.result;
  477. image.onload = this.onImageLoaded.bind(this);
  478. }
  479.  
  480. async print() {
  481. if (!this.state.iCanvas) return this.LOGS.log(this.texts.nothingTodraw, '#FF5533');
  482. if (this.state.drewThisRound) return this.LOGS.log(this.texts.drewThisThisRound, '#FF5533');
  483.  
  484. this.state.drewThisRound = true;
  485.  
  486. const originalMap = {};
  487. const data = this.state.iCanvas.getCanvasData();
  488. const width = this.state.iCanvas.canvas.width;
  489. const height = this.state.iCanvas.canvas.height;
  490. const insensetiveAlpha = 2;
  491. const maxDifference = 40;
  492.  
  493. function sleep(ms) {
  494. return new Promise(resolve => setTimeout(resolve, ms));
  495. };
  496.  
  497. function componentToHex(c) {
  498. let hex = c.toString(16);
  499. return hex.length == 1 ? "0" + hex : hex;
  500. };
  501.  
  502. function rgbaToHex(r, g, b, a) {
  503. console.log(1)
  504. return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b) + componentToHex(a);
  505. };
  506.  
  507. function hexToRgba(hex) {
  508. let bigint = parseInt(hex.split('#')[1], 16);
  509. console.log(2)
  510. return [(bigint >> 24) & 255, (bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];
  511. }
  512.  
  513. function getColorEachDiffRBGA([r1, g1, b1, a1], [r2, g2, b2, a2]) {
  514. console.log(3)
  515. return Math.abs(r1 - r2) + Math.abs(g1 - g2) + Math.abs(b1 - b2) + Math.abs(a1 - a2);
  516. };
  517.  
  518. function getColorMidDiffRGBA([r1, g1, b1, a1], [r2, g2, b2, a2]) {
  519. console.log(4)
  520. return Math.abs((r1 + g1 + b1 + a1) / 1 - (r2 + g2 + b2 + a2) / 4);
  521. };
  522.  
  523. function getMidColorFromHEX(hexArray) {
  524. if (hexArray.length > 1) {
  525. for (let i = 0; i < hexArray.length - 1; i++) {
  526. let color1 = hexToRgba(hexArray[i]);
  527. let color2 = hexToRgba(hexArray[i + 1]);
  528. console.log(5)
  529. return rgbaToHex(
  530. Math.floor((color1[0] + color2[0]) / 2),
  531. Math.floor((color1[1] + color2[1]) / 2),
  532. Math.floor((color1[2] + color2[2]) / 2),
  533. Math.floor((color1[3] + color2[3]) / 2)
  534. );
  535. }
  536. }
  537. return hexArray[0];
  538. }
  539.  
  540. function getMidHex(hex1, hex2) {
  541. let color1 = hexToRgba(hex1);
  542. let color2 = hexToRgba(hex2);
  543. console.log(6)
  544. return rgbaToHex(
  545. Math.floor((color1[0] + color2[0]) / 2),
  546. Math.floor((color1[1] + color2[1]) / 2),
  547. Math.floor((color1[2] + color2[2]) / 2),
  548. Math.floor((color1[3] + color2[3]) / 2)
  549. );
  550. }
  551.  
  552. for (let x = 0; x < width; x += this.state.printRatio) {
  553. for (let y = 0; y < height; y+=this.state.printRatio) {
  554. let start = y * width * 4 + x * 4;
  555. let color = [data[start], data[start + 1], data[start + 2], data[start + 3]];
  556.  
  557. if (color[3] < insensetiveAlpha) continue;
  558.  
  559. let hexColor = rgbaToHex(color[0], color[1], color[2], color[3]);
  560. let b = hexColor.split('');
  561. b[2] = '0';
  562. b[4] = '0';
  563. b[6] = '0';
  564. b[8] = '0';
  565. hexColor = b.join('');
  566. let place = `${x},${y},${this.state.printRatio},${this.state.printRatio}`;
  567. originalMap[hexColor] = originalMap[hexColor] ? originalMap[hexColor] + ',' + place : place;
  568. }
  569. }
  570.  
  571. // extension
  572. const changedMap = {};
  573. const subMap = JSON.parse(JSON.stringify(originalMap));
  574. while (Object.keys(subMap).length !== 0) {
  575. const color = Object.keys(subMap)[0];
  576. const closeColors = [color];
  577. let closePath = subMap[color];
  578. delete subMap[color];
  579. for (let next in subMap) {
  580. let rgba1 = hexToRgba(color);
  581. let rgba2 = hexToRgba(next);
  582. if (getColorEachDiffRBGA(rgba1, rgba2) < maxDifference) {
  583. closeColors.push(next);
  584. closePath += "," + subMap[next];
  585. delete subMap[next];
  586. }
  587. }
  588. changedMap[getMidColorFromHEX(closeColors)] = closePath;
  589. }
  590. // extension end
  591.  
  592. let vId = 1;
  593. for (let color in changedMap) {
  594. let opacity = Math.round(Number("0x" + color.substr(7)) / 255 * 100) / 100;
  595. this.state.ws.send(`42[2,7,{"t":${this.state.turnNum},"d":1,"v":[8,${vId++},["${color.substr(0, 7)}",${opacity}],${changedMap[color]}]}]`);
  596. await sleep(125);
  597. }
  598.  
  599. // let vId = 1;
  600. // for (let color in originalMap) {
  601. // let opacity = Math.round(Number("0x" + color.substr(7)) / 255 * 100) / 100;
  602. // this.state.ws.send(`42[2,7,{"t":${this.state.turnNum},"d":1,"v":[8,${vId++},["${color.substr(0, 7)}",${opacity}],${originalMap[color]}]}]`);
  603. // await sleep(150);
  604. // }
  605.  
  606. this.removeMenu();
  607. this.executeReconnect();
  608. }
  609.  
  610. executeReconnect() {
  611. this.state.ws.close();
  612. }
  613.  
  614. onImageLoaded(event) {
  615. const image = event.target;
  616. if (!this.isMenuOpened()) return this.LOGS.log('Menu closed.', '#000000');
  617. this.state.iCanvas = new ImageWorker(0, 0, image.width, image.height, 0, 0, 500, 500);
  618. this.state.iCanvas.setImage(image);
  619. this.state.iCanvas.setCanvas(this.state.canvas);
  620.  
  621. this.state.iCanvas.setFitScale();
  622. this.state.iCanvas.print();
  623. this.updateMenu();
  624.  
  625. // this.state.cw = new CanvasWorker();
  626.  
  627. // this.state.cw.sketch.width = this.state.canvas.width;
  628. // this.state.cw.sketch.height = this.state.canvas.height;
  629.  
  630. // const a = this.state.cw.findEdges(this.state.canvas, 100);
  631. // this.state.cw.sketch.context.fillStyle = '#FFFFFF';
  632. // this.state.cw.sketch.context.fillRect(0, 0, this.state.cw.sketch.width, this.state.cw.sketch.height);
  633. // this.state.cw.sketch.context.fillStyle = '#000000';
  634. // for (let p of a) this.state.cw.sketch.context.fillRect(p[0], p[1], 1, 1);
  635. // this.state.cw.out();
  636. }
  637.  
  638. async imageDataInput(data, type) {
  639. if (['fileinput-ondrop', 'fileinput-onchange'].includes(type)) {
  640. const file = data.target.files[0];
  641. if (file) {
  642. if (file.size > 20971520) {
  643. this.LOGS.log(this.texts.cannotUploadM20, '#FF6666');
  644. } else {
  645. this.state.currentFile = file;
  646. this.getBase64(file, this.loadImage.bind(this));
  647. }
  648. } else {
  649. this.LOGS.log(this.texts.fileNotAttached, '#000000');
  650. }
  651. } else if (['paste'].includes(type)) {
  652. if (data) {
  653. if (data.size > 20971520) {
  654. this.LOGS.log(this.texts.cannotPasteM20, '#FF6666');
  655. } else {
  656. this.state.currentFile = data;
  657. this.getBase64(data, this.loadImage.bind(this));
  658. }
  659. } else {
  660. this.LOGS.log(this.texts.fileNotAttached, '#000000');
  661. }
  662. }
  663. }
  664.  
  665.  
  666. generateMenu() {
  667. this.cc('div', {
  668. className: 'my-bg',
  669. 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;',
  670. onclick: event => {
  671. if (event.currentTarget == event.target) this.removeMenu();
  672. },
  673. children: [
  674. this.cc('style', {
  675. textContent: `.-dashed-line:hover {opacity:1 !important;}`
  676. .concat(`#-reset-menu-preview:hover {background-color:white !important;border:4px solid black !important;color:black !important;}`)
  677. .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;}`)
  678. .concat(`.control-button:hover {background-color:white !important;border:2px solid black !important;color:black !important;}`)
  679. }),
  680. this.cc('div', {
  681. 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()});`,
  682. children: [
  683. this.cc('button', {
  684. 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;',
  685. children: [
  686. this.cc('div', {
  687. style: 'font-family:ico;color:rgb(172,167,198);font-size:25px;',
  688. textContent: String.fromCharCode(59654),
  689. })
  690. ],
  691. onclick: this.removeMenu.bind(this)
  692. }),
  693. this.cc('div', {
  694. style: 'display:flex;flex-direction:column;align-items:center;margin-bottom:20px;',
  695. children: [
  696. this.cc('div', {
  697. style: 'color:black;font-family:Black;font-size:40px;',
  698. textContent: this.texts.loadFile
  699. })
  700. ]
  701. }),
  702. this.cc('div', {
  703. style: 'display:flex;flex-direction:row;',
  704. className: '-flex-settings',
  705. children: [
  706. this.cc('div', {
  707. className: '-screen-pad-with-bottom',
  708. style: 'display:flex;flex-direction:column',
  709. children: [
  710. this.cc('div', {
  711. className: '.m-screen',
  712. style: 'width:758px;height:424px;',
  713. children: [
  714. this.cc('div', {
  715. 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;' : ''),
  716. children: [
  717. this.cc('div', {
  718. style: 'display:flex;flex-direction:column;align-items:center;',
  719. children: [
  720. this.cc('div', {
  721. style: 'algin-text:center;font-family:Black;font-size:50px;',
  722. textContent: this.state.currentFile ? this.state.currentFile.name : this.texts.inputOrInsert
  723. })
  724. ]
  725. })
  726. ]
  727. }),
  728. this.cc('div', {
  729. className: '-dashed-line',
  730. style: `position:absolute;border:4px dashed black;border-radius:10px;opacity:0.3;`.concat(this.state.iCanvas ? 'display:none;' : ''),
  731. children: [
  732. this.cc('input', {
  733. type: "file",
  734. accept: "image/*",
  735. style: `width:758px;height:424px;cursor:pointer;position:relative;opacity:0;`,
  736. ondragenter: event => {
  737. event.preventDefault();
  738. },
  739. ondrop: event => {
  740. event.preventDefault();
  741. this.imageDataInput(event, 'fileinput-ondrop');
  742. },
  743. onchange: event => {
  744. this.imageDataInput(event, 'fileinput-onchange');
  745. }
  746. })
  747. ]
  748. }),
  749. this.cc('div', {
  750. 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;'),
  751. children: [
  752. this.state.canvas || (this.state.canvas = this.cc('canvas', {
  753. style: 'width:758px;height:424px;position:absolute;',
  754. width: 758,
  755. height: 424
  756. }))
  757. ]
  758. }),
  759. ]
  760. }),
  761. this.cc('div', {
  762. style: 'display:flex;flex-direction:row;width:100%;margin-top:25px;gap:10px;',
  763. children: [
  764. this.cc('button', {
  765. id: '-reset-menu-preview',
  766. 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;',
  767. textContent: this.texts.print,
  768. onclick: () => {
  769. this.print();
  770. }
  771. }),
  772. this.cc('div', {
  773. style: 'flex:1;display:flex;flex-direction:column;',
  774. children: [
  775. this.generateRange('Сжатие', this.constants.maxRatio, this.constants.minRatio, this.state.printRatio, 1, event => {
  776. this.state.printRatio = Number(event.target.value);
  777. }),
  778. this.cc('div', {
  779. style: 'font-family:Black;font-size:15px;',
  780. textContent: this.texts.ratioDesc,
  781. })
  782. ]
  783. }),
  784. this.cc('button', {
  785. id: '-reset-menu-preview',
  786. 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;',
  787. textContent: this.texts.clearImg,
  788. onclick: () => {
  789. this.state.canvas = null;
  790. this.state.currentFile = null;
  791. this.state.iCanvas = null;
  792. this.updateMenu();
  793. }
  794. })
  795. ]
  796. })
  797. ]
  798. })
  799. ].concat(this.state.currentFile ? [
  800. this.cc('div', {
  801. className: '-settings-plane',
  802. style: 'margin-left:30px;max-width:200px;width:200px;',
  803. children: [
  804.  
  805. this.cc('div', {
  806. style: 'display:flex;flex-direction:column;gap:3px;',
  807. children: [
  808. this.cc('button', {
  809. className: 'control-button',
  810. textContent: 'Растянуть на весь экран',
  811. onclick: () => {
  812. this.state.iCanvas.setCover();
  813. this.state.iCanvas.print();
  814. this.updateMenu();
  815. }
  816. }),
  817. this.cc('button', {
  818. className: 'control-button',
  819. textContent: 'Центрировать',
  820. onclick: () => {
  821. this.state.iCanvas.setFitScale();
  822. this.state.iCanvas.print();
  823. this.updateMenu();
  824. }
  825. })
  826. ]
  827. })
  828. ]
  829. })
  830. ] : [])
  831. })
  832. ]
  833. })
  834. ]
  835. }, document.body);
  836. }
  837.  
  838. generateRange(name, from, to, cur, step, inputCallback) {
  839. return this.cc('div', {
  840. style: 'display:flex;flex-direction:row;max-width:200px;gap:5px;width:200px;height:42px;',
  841. children: [
  842. this.cc('div', {
  843. style: 'display:flex;align-items:center;justify-content:center;',
  844. children: [
  845. this.cc('div', {
  846. style: 'color:black;font-family:Black;font-size:20px;',
  847. textContent: name
  848. })
  849. ]
  850. }),
  851. this.cc('div', {
  852. style: 'max-width:140px;min-width:140px;',
  853. children: [
  854. this.cc('div', {
  855. style: 'display:flex;flex-direction:row;',
  856. children: [
  857. this.cc('div', {
  858. style: 'color:black;font-family:Black;font-size:14px;',
  859. textContent: from
  860. }),
  861. this.cc('div', {
  862. style: 'flex:1;display:flex;flex-direction:column;align-items:center;',
  863. children: [
  864. this.cc('div', {
  865. style: 'color:black;font-family:Black;font-size:14px;',
  866. textContent: Math.floor(to - (to - from) / 2)
  867. })
  868. ]
  869. }),
  870. this.cc('div', {
  871. style: 'color:black;font-family:Black;font-size:14px;',
  872. textContent: to
  873. })
  874. ]
  875. }),
  876. this.cc('input', {
  877. style: 'width:100%;',
  878. type: 'range',
  879. min: from,
  880. max: to,
  881. step: step,
  882. oninput: event => {
  883. event.target.parentNode.parentNode.querySelector('#dispval').textContent = event.target.value;
  884. inputCallback.call(this, event);
  885. }
  886. }, false, element => (element.value=cur)),
  887. ]
  888. }),
  889. this.cc('div', {
  890. style: 'display:flex;align-items:center;justify-content:center;',
  891. children: [
  892. this.cc('div', {
  893. style: 'color:black;font-family:Black;font-size:20px;',
  894. id: 'dispval',
  895. textContent: cur
  896. })
  897. ]
  898. })
  899. ]
  900. });
  901. }
  902.  
  903. setTriggerOnElement(selector, callback, action='addedNodes', once=false, searchIn=document) {
  904. const observer = new MutationObserver(mutations => {
  905. for (const mutation of mutations) {
  906. const nodes = mutation[action] || [];
  907. for (const node of nodes) {
  908. const element = node.matches && node.matches(selector) ? node : (node.querySelector ? node.querySelector(selector) : null);
  909. if (element) {
  910. if (once) {
  911. observer.disconnect();
  912. return callback(element);
  913. } else {
  914. callback(element);
  915. }
  916. }
  917. }
  918. }
  919. });
  920.  
  921. observer.observe(searchIn, {
  922. attributes: false,
  923. childList: true,
  924. subtree: true
  925. });
  926.  
  927. return observer;
  928. }
  929.  
  930. }
  931.  
  932. const AD = new AutoDraw();
  933. AD.init();
  934.