Bubble.am+

Script that adds useful features to the game.

  1. // ==UserScript==
  2. // @name Bubble.am+
  3. // @namespace https://enderror.pl
  4. // @version 21.07.11
  5. // @description Script that adds useful features to the game.
  6. // @author enderror
  7. // @match *://bubble.am/*
  8. // @run-at document-start
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. class Plus {
  13. constructor() {
  14. this.name = 'plus';
  15. this.version = '21.07.11';
  16. this.author = 'enderror';
  17. this.additionalCoreChanges = [];
  18.  
  19. this.defaultSettings = {
  20. controls: [
  21. { type: '1x', keycode: '32', disabled: false },
  22. { type: 'eject', keycode: '87', disabled: false },
  23. { type: 'setCamera', keycode: '81', disabled: false },
  24. { type: 'holdEject', keycode: '69', disabled: false },
  25. { type: '1x', keycode: '49', disabled: false },
  26. { type: '2x', keycode: '50', disabled: false },
  27. { type: '4x', keycode: '51', disabled: false },
  28. { type: '8x', keycode: '52', disabled: false },
  29. { type: '16x', keycode: '53', disabled: false },
  30. { type: 'holdSplit', keycode: '16', disabled: false },
  31. { type: 'movementUp', keycode: '81', disabled: false },
  32. { type: 'movementLeft', keycode: '65', disabled: false },
  33. { type: 'movementDown', keycode: '83', disabled: false },
  34. { type: 'movementRight', keycode: '68', disabled: false },
  35. ]
  36. };
  37.  
  38. this.settings = {
  39. checkbox: {
  40. transparentVirus: true,
  41. noGrid: false,
  42. customColor: false,
  43. customSkin: false,
  44. bypassSkin: false,
  45. darkMenu: false,
  46. virusSplitCounter: true,
  47. cellsCounter: true,
  48. hideName: false,
  49. showMotherMass: false
  50. },
  51.  
  52. values: {
  53. cellsColor: '#e31e24',
  54. skinUrl: null,
  55. },
  56.  
  57. controls: this.defaultSettings.controls,
  58. chat: [],
  59. accounts: []
  60. }
  61.  
  62. this.helpers = {
  63. skin: {
  64. skinInd: 1,
  65. gid: 0,
  66. },
  67.  
  68. controls: {
  69. splitSwitch: false,
  70. holdSplitSwitch: false,
  71. splitInterval: undefined,
  72. ejectSwitch: false,
  73. ejectInterval: undefined,
  74. isActiveMovement: false,
  75. activeMovement: []
  76. }
  77. }
  78.  
  79. this.run();
  80. }
  81.  
  82. run() {
  83. console.log(`${this.name} ${this.version} created by ${this.author}`);
  84.  
  85. if(location.pathname.includes('.txt')
  86. || location.pathname.includes('.png')
  87. || location.pathname.includes('.jpg')
  88. || location.pathname.includes('.jpeg')) {
  89. return;
  90. }
  91.  
  92. if((location.host === 'bubble.am' && location.pathname === '/') || location.pathname.length > 15) {
  93. window.stop();
  94. location.href = `${location.origin}/${this.name}${location.hash}`;
  95. return;
  96. }
  97.  
  98. document.documentElement.innerHTML = '';
  99.  
  100. const request = new XMLHttpRequest();
  101. const url = 'http://bubble.am';
  102.  
  103. request.open('get', url, true);
  104. request.send();
  105. request.onload = async(e) => {
  106. const newCore = await this.modify(e.target.responseText);
  107.  
  108. document.open();
  109. document.write(newCore);
  110. document.close();
  111.  
  112. let documentCheck = setInterval(() => {
  113. if(document.readyState == 'complete') {
  114. clearInterval(documentCheck);
  115. new UI();
  116. this.addEventListeners();
  117.  
  118. window.modLoaded = true;
  119. }
  120. }, 100);
  121. }
  122. }
  123.  
  124. async modify(core) {
  125. core = core.replace(/(.*)(Ubuntu\:700)(.*)(\n.*){4}/gm, `
  126. <link rel='stylesheet' type='text/css' href='http://fonts.googleapis.com/css?family=Ubuntu:700'>
  127. <link rel="stylesheet" type="text/css" href="http://m.bubble.am/css/bootstrap.min.css?v=3">
  128. <link rel="stylesheet" type="text/css" href="http://bubble.am/css/style.css?v=0.2.50" />
  129. <link rel="stylesheet" type="text/css" href="http://bubble.am/css/font.awesome.css?v5" />
  130.  
  131. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-colorpicker/2.5.3/css/bootstrap-colorpicker.css"/>
  132. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>
  133. <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/nano.min.css"/>
  134.  
  135. <script src="//m.bubble.am/js/jquery.js"></script>
  136. `);
  137.  
  138. core = core.replace(/(.*)(bootstrap\.min\.js)(.*)(\n.*){3}/gm, `
  139. <script src="/js/bootstrap.min.js"></script>
  140. <script src="//m.bubble.am/js/jquery.lazy.js"></script>
  141. <script src="/js/jquery.ui.js"></script>
  142. <script>
  143. $("body").on('click', ".connect", function(event) {
  144. const srv = this.dataset['srv'];
  145. const id_split = srv.split('-');
  146. const gamemode = ':' + id_split[2];
  147. setGameModeTemporary(gamemode);
  148. });
  149. </script>
  150. <script src="/js/bub.js?v=0.2.50"></script>
  151.  
  152. <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-colorpicker/2.5.3/js/bootstrap-colorpicker.min.js" async></script>
  153. <script src="https://cdnjs.cloudflare.com/ajax/libs/smooth-scrollbar/8.6.2/smooth-scrollbar.min.js" async></script>
  154. <script src="https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js" async></script>
  155. <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11" async></script>
  156. `);
  157.  
  158. core = core.replace(/(d\.onkeydown\s=\sf)([^]*?b\s=\s\!1)([^]*?\}\;)/gm, `
  159. d.split = function() {
  160. ba();
  161. K(17);
  162. }
  163.  
  164. d.eject = function() {
  165. ba();
  166. K(21);
  167. }
  168.  
  169. d.setCamera = function() {
  170. K(18);
  171. }
  172.  
  173. d.onkeydown = function(n) {
  174. switch (n.keyCode) {
  175. case 27: // quit
  176. oa(true);
  177. break;
  178.  
  179. case 13:
  180. if (isTyping) {
  181. isTyping = false;
  182. document.getElementById("chat_textbox").blur();
  183. chattxt = document.getElementById("chat_textbox").value;
  184. if (chattxt.length > 0) sendChat(chattxt);
  185. //document.getElementById("chat_textbox").value = "";
  186.  
  187. } else {
  188. if (!hasOverlay) {
  189. document.getElementById("chat_textbox").focus();
  190. isTyping = true;
  191. }
  192. }
  193. break;
  194.  
  195. case 17:
  196. if (!hasOverlay
  197. && !isTyping
  198. && !window.plus.helpers.controls.splitSwitch
  199. && !window.plus.helpers.controls.holdSplitSwitch
  200. && !window.plus.helpers.controls.ejectSwitch) {
  201. document.getElementById("chat_textbox").value = "/g ";
  202. document.getElementById("chat_textbox").focus();
  203. isTyping = true;
  204. }
  205. break;
  206. }
  207. };
  208. `);
  209.  
  210. core = core.replace(/(\,\sI\:\s)(.*)/, `
  211. $&
  212. , counterCache: null
  213. , motherCache: null
  214. `);
  215.  
  216. core = core.replace(/(f.+)(v\[d\]\.)(.*)/, `
  217. $&
  218.  
  219. if(window.plus.settings.checkbox.cellsCounter && h.length !== 0) {
  220. f.restore();
  221.  
  222. let maxCells = '';
  223.  
  224. if(currentMode() == ':5') {
  225. maxCells = '35';
  226. } else {
  227. maxCells = '16';
  228. }
  229.  
  230. const playerCells = h.length;
  231. const playerCellsText = new Ca(24, '#fff');
  232. playerCellsText.u(playerCells + '/' + maxCells);
  233. c = playerCellsText.F();
  234. a = c.width;
  235. f.globalAlpha = .2;
  236. f.fillStyle = "#000000";
  237. f.fillRect(10, 55, a + 10, 34);
  238. f.globalAlpha = 1;
  239. f.drawImage(c, 15, 60);
  240. }
  241. `);
  242.  
  243. core = core.replace(/(bb\s\?)(.*)/, `
  244. if(bb) {
  245. a.fillStyle = "#FFFFFF";
  246. a.strokeStyle = "#AAAAAA";
  247. if(this.f && window.plus.settings.checkbox.transparentVirus === true) a.globalAlpha = 0.2;
  248. } else {
  249. a.fillStyle = this.color;
  250. a.strokeStyle = this.color;
  251. if(this.f && window.plus.settings.checkbox.transparentVirus === true) a.globalAlpha = 0.2;
  252. }
  253. `);
  254.  
  255. core = core.replace(/(.*)(var\sa\s=\sl\s\/\sg)(.+)(\n.*\n.*)/gm, `
  256. if(window.plus.settings.checkbox.noGrid === false) {
  257. $&
  258. }
  259. `);
  260.  
  261. core = core.replace(/(.*)(m\.color)(.*)/, `
  262. $&
  263.  
  264. const amAlive = h.length > 0;
  265.  
  266. const helpers = window.plus.helpers;
  267. const settings = window.plus.settings;
  268. if(amAlive && m.name === h[0].name && !m.f && settings.values.cellsColor !== undefined && settings.values.cellsColor !== null && settings.checkbox.customColor === true) {
  269. m.color = settings.values.cellsColor;
  270. }
  271.  
  272. if(amAlive && (h[0].gid !== 1 && h[0].gid !== 2)) {
  273. helpers.skin.gid = h[0].gid;
  274. }
  275.  
  276. if(settings.checkbox.customSkin === true && settings.values.skinUrl !== null && amAlive) {
  277. for(let i = 0; i < h.length; i++) {
  278. const cell = h[i];
  279.  
  280. if(cell.gid !== helpers.skin.skinInd) {
  281. cell.gid = helpers.skin.skinInd;
  282.  
  283. if(cell.name === '') cell.name = 'enderror';
  284. }
  285. }
  286. }
  287. if(settings.checkbox.customSkin === false && amAlive && h[0].gid !== helpers.skin.gid) {
  288. for(let i = 0; i < h.length; i++) {
  289. const cell = h[i];
  290.  
  291. cell.gid = helpers.skin.gid;
  292. }
  293. }
  294.  
  295. `);
  296.  
  297. core = core.replace(/(.*)(\(c\s\&\&\sFb.*)(\n.*){35}/gm, `
  298. const bypassSkin = (window.plus.settings.checkbox.customSkin && window.plus.settings.checkbox.bypassSkin)
  299. const customSkinGid = (this.gid == 1 || this.gid == 2);
  300. if (c && (Fb || bypassSkin) && !this.j && ':1' != V && ':3' != V && ':8' != V) {
  301. var loadBub = -1;
  302.  
  303. if(Fb && this.gid && this.gid > 0) {
  304. loadBub = this.gid;
  305. } else if(!Fb && this.gid && customSkinGid) {
  306. loadBub = this.gid;
  307. } else if(Fb) {
  308. if(-1 != Jb.indexOf(c)) {
  309. loadBub = c;
  310. }
  311. }
  312. if (loadBub != -1) {
  313. if((Fb || bypassSkin && customSkinGid ) && (!$.hasOwnProperty(c) || $[c].skin != loadBub)) {
  314. $[c] = new Image;
  315.  
  316. if(Fb && loadBub > 100000000) {
  317. $[c].src = "//bubble.am/skins/custom/" + loadBub + '.png?0.2.50';
  318. } else if(loadBub >= 10 && loadBub <= 20 && Fb) {
  319. $[c].src = "//bubble.am/img/battle_" + loadBub + '.png?0.2.50';
  320. } else if(customSkinGid && (Fb || bypassSkin)) {
  321. $[c].src = window.plus.settings.values.skinUrl;
  322. } else if(Fb) {
  323. $[c].src = "//m.bubble.am/skins/" + loadBub + '.png?0.2.50';
  324. }
  325.  
  326. $[c].skin = loadBub;
  327. }
  328. if((Fb || bypassSkin) && (0 != $[c].width && $[c].complete)) {
  329. d = $[c];
  330. } else {
  331. d = null;
  332. }
  333. } else {
  334. d = null;
  335. }
  336. } else {
  337. d = null;
  338. }
  339. `);
  340.  
  341. core = core.replace(/(\,\sd\s\=\sthis\.I\,)(.*)/, `
  342. $&
  343.  
  344. const settings = window.plus.settings;
  345. const isNotSplittable = window.currentMode() == ':2'
  346. || window.currentMode() == ':5'
  347. || window.currentMode() == ':13';
  348. const isBattle = window.currentMode() == ':11' || window.currentMode() == ':12';
  349. const isTeams = window.currentMode() == ':3';
  350.  
  351. let nc;
  352. if(settings.checkbox.virusSplitCounter && !isNotSplittable && this.f && this.color !== '#cd5564') {
  353. if(this.counterCache === null) {
  354. this.counterCache = new Ca(this.i(), '#FFFFFF', !0, "#000000");
  355. }
  356.  
  357. nc = this.counterCache;
  358.  
  359. if(isTeams) {
  360. switch(true) {
  361. case this.size >= 123 && this.size <= 126:
  362. nc.u('7');
  363. break;
  364. case this.size >= 126 && this.size <= 129:
  365. nc.u('6');
  366. break;
  367.  
  368. case this.size >= 129 && this.size <= 134:
  369. nc.u('5');
  370. break;
  371.  
  372. case this.size >= 134 && this.size <= 137:
  373. nc.u('4');
  374. break;
  375.  
  376. case this.size >= 137 && this.size <= 141:
  377. nc.u('3');
  378. break;
  379.  
  380. case this.size >= 142 && this.size <= 145:
  381. nc.u('2');
  382. break;
  383.  
  384. case this.size >= 145 && this.size <= 151:
  385. nc.u('1');
  386. break;
  387. }
  388. } else if(isBattle) {
  389. switch(true) {
  390. case this.size >= 84 && this.size <= 89:
  391. nc.u('7');
  392. break;
  393. case this.size >= 89 && this.size <= 94:
  394. nc.u('6');
  395. break;
  396.  
  397. case this.size >= 94 && this.size <= 99:
  398. nc.u('5');
  399. break;
  400.  
  401. case this.size >= 99 && this.size <= 104:
  402. nc.u('4');
  403. break;
  404.  
  405. case this.size >= 104 && this.size <= 109:
  406. nc.u('3');
  407. break;
  408.  
  409. case this.size >= 109 && this.size <= 115:
  410. nc.u('2');
  411. break;
  412.  
  413. case this.size >= 115 && this.size <= 120:
  414. nc.u('1');
  415. break;
  416. }
  417. } else {
  418. switch(true) {
  419. case this.size >= 114 && this.size <= 119:
  420. nc.u('7');
  421. break;
  422. case this.size >= 119 && this.size <= 123:
  423. nc.u('6');
  424. break;
  425.  
  426. case this.size >= 123 && this.size <= 127:
  427. nc.u('5');
  428. break;
  429.  
  430. case this.size >= 127 && this.size <= 132:
  431. nc.u('4');
  432. break;
  433.  
  434. case this.size >= 132 && this.size <= 136:
  435. nc.u('3');
  436. break;
  437.  
  438. case this.size >= 136 && this.size <= 141:
  439. nc.u('2');
  440. break;
  441.  
  442. case this.size >= 141 && this.size <= 145:
  443. nc.u('1');
  444. break;
  445. }
  446. }
  447.  
  448. nc.G(this.i());
  449. nc.U(4);
  450.  
  451. const rn = nc.F();
  452. a.drawImage(rn, ~~this.x - ~~(rn.width / 2), ~~this.y - ~~(rn.height / 2));
  453. }
  454. `);
  455.  
  456. core = core.replace(/(.*)(\~\~\(l\s\/\s2\)\,\sf\,\sl\)\;)/, `
  457. const settings = window.plus.settings;
  458.  
  459. if(settings.checkbox.hideName && h.length > 0 && this.name === h[0].name) {
  460. } else {
  461. $&
  462. }
  463. `);
  464.  
  465. core = core.replace(/(.*)(chatNick\s\+)(.*)/, `
  466. const settings = window.plus.settings;
  467.  
  468. const rawNick = chatNick.replace(/(<([^>]+)>)/ig, '').replace(/(\\[.*?\\])/, '').trim().toLowerCase();
  469. if(!settings.chat.includes(rawNick)) {
  470. $&
  471. }
  472. `);
  473.  
  474. core = core.replace(/(.*)(isUnlimitedZoom)(.*)(\n.*){7}/gm, ``);
  475.  
  476.  
  477. core = core.replace(/(.*)(\&\&\sGb)(.*)(\n.*){3}/gm, `
  478. if(0 < this.id && Gb && (d || (0 == h.length || Qd) && (!this.f || this.j) && 80 < this.size)) {
  479. if(null == this.I) {
  480. this.I = new Ca(this.i() / 2, "#FFFFFF", true, "#000000");
  481. }
  482. d = this.I;
  483. d.G(this.i() / 2);
  484. d.u(~~(this.size * this.size / 100));
  485. c = Math.ceil(10 * g) / 10;
  486. d.U(c);
  487. e = d.F();
  488. f = ~~(e.width / c);
  489. l = ~~(e.height / c);
  490. a.drawImage(e, ~~this.x - ~~(f / 2), b - ~~(l / 2), f, l);
  491. }
  492.  
  493. if(0 < this.id && window.plus.settings.checkbox.showMotherMass && this.color === '#cd5564') {
  494. if(this.motherCache == null) {
  495. this.motherCache = new Ca(this.i() / 2, "#FFFFFF", true, "#000000");
  496. }
  497.  
  498. d = this.motherCache;
  499. d.G(this.i());
  500. d.u(~~(this.size * this.size / 100));
  501. c = Math.ceil(10 * g) / 10;
  502. d.U(c);
  503. e = d.F();
  504. f = ~~(e.width / c);
  505. l = ~~(e.height / c);
  506. a.drawImage(e, ~~this.x - ~~(f / 2), b - ~~(l / 2), f, l);
  507. }
  508. `);
  509.  
  510. core = core.replace(/(J\.onmousedown)(.*)(\n.*){20}/gm, `
  511. const helpers = window.plus.helpers;
  512. J.onmousedown = function(a) {
  513. if (db) {
  514. var b = a.clientX - (5 + l / 5 / 2)
  515. , c = a.clientY - (5 + l / 5 / 2);
  516. if (Math.sqrt(b * b + c * c) <= l / 5 / 2) {
  517. ba();
  518. K(17);
  519. return
  520. }
  521. }
  522. if(!helpers.controls.isActiveMovement) {
  523. ma = a.clientX;
  524. na = a.clientY;
  525. Ha();
  526. ba()
  527. }
  528. };
  529. J.onmousemove = function(a) {
  530. if(!helpers.controls.isActiveMovement) {
  531. ma = a.clientX;
  532. na = a.clientY;
  533.  
  534. lastacti = Date.now();
  535. }
  536. };
  537.  
  538. d.goTo = function(x, y) {
  539. ma = x;
  540. na = y;
  541. }
  542. `);
  543.  
  544. core = core.replace(/(.*)\.setAcid(.*)/gm, `
  545. d.setGameModeTemporary = function(a) {
  546. currMode = a.substr(0, 3);
  547. return currMode;
  548. }
  549.  
  550. $&
  551. `);
  552.  
  553. await sleep(300);
  554.  
  555. const additionalChanges = this.additionalCoreChanges;
  556. if(additionalChanges.length !== 0) {
  557. for(let i = 0; i < additionalChanges.length; i++) {
  558. const additionalChange = additionalChanges[i];
  559. const hook = additionalChange.hook;
  560. const change = additionalChange.change;
  561.  
  562. core = core.replace(hook, change);
  563. }
  564. }
  565.  
  566. return core;
  567. }
  568.  
  569. keydown = (e) => {
  570. this.controlTypeDown(e);
  571. }
  572.  
  573. keyup = (e) => {
  574. this.controlTypeUp(e);
  575. }
  576.  
  577. mousedown = (e) => {
  578. this.controlTypeDown(e);
  579. }
  580.  
  581. mouseup = (e) => {
  582. this.controlTypeUp(e);
  583. }
  584.  
  585. controlTypeDown(e) {
  586. const settings = window.plus.settings;
  587. const helpers = window.plus.helpers;
  588.  
  589. const key = e.which;
  590. const controls = settings.controls;
  591.  
  592. if(controls.length === 0) return;
  593. if(document.activeElement.nodeName == 'INPUT' && helpers.controls.splitSwitch === false && helpers.controls.ejectSwitch === false) return;
  594.  
  595. for(let i = 0; i < controls.length; i++) {
  596. const control = controls[i];
  597.  
  598. if(control.keycode == key && !control.disabled) {
  599. switch(control.type) {
  600. case '1x':
  601. if(!helpers.controls.splitSwitch) {
  602. helpers.controls.splitSwitch = true;
  603. window.split();
  604. }
  605. break;
  606.  
  607. case 'eject':
  608. if(!helpers.controls.ejectSwitch) {;
  609. helpers.controls.ejectSwitch = true;
  610. window.eject();
  611. }
  612. break;
  613.  
  614. case 'setCamera':
  615. window.setCamera();
  616. break;
  617. case '2x':
  618. if(!helpers.controls.splitSwitch) {
  619. helpers.controls.splitSwitch = true;
  620. this.split(2);
  621. }
  622. break;
  623. case '4x':
  624. if(!helpers.controls.splitSwitch) {
  625. helpers.controls.splitSwitch = true;
  626. this.split(4);
  627. }
  628. break;
  629. case '8x':
  630. if(!helpers.controls.splitSwitch) {
  631. helpers.controls.splitSwitch = true;
  632. this.split(8);
  633. }
  634. break;
  635. case '16x':
  636. if(!helpers.controls.splitSwitch) {
  637. helpers.controls.splitSwitch = true;
  638. this.split(16);
  639. }
  640. break;
  641. case 'holdSplit':
  642. if(!helpers.controls.holdSplitSwitch) {
  643. helpers.controls.holdSplitSwitch = true;
  644.  
  645. helpers.controls.splitInterval = setInterval(() => {
  646. window.split();
  647. }, 4);
  648. }
  649. break;
  650. case 'holdEject':
  651. if(helpers.controls.ejectSwitch) break;
  652. helpers.controls.ejectSwitch = true;
  653. helpers.controls.ejectInterval = setInterval(() => {
  654. window.eject();
  655. }, 4);
  656. break;
  657. case 'mouseLeft':
  658. this.split(1);
  659. break;
  660. case 'mouseRight':
  661. this.split(1);
  662. break;
  663. case 'mouseMiddle':
  664. this.split(1);
  665. break;
  666. case 'movementUp':
  667. this.goTo(3, -0);
  668.  
  669. if(!helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = true;
  670. helpers.controls.activeMovement['up'] = true;
  671. break;
  672. case 'movementRight':
  673. this.goTo(0, 5);
  674.  
  675. if(!helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = true;
  676. helpers.controls.activeMovement['right'] = true;
  677. break;
  678. case 'movementDown':
  679. this.goTo(2, 0.6);
  680.  
  681. if(!helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = true;
  682. helpers.controls.activeMovement['down'] = true;
  683. break;
  684. case 'movementLeft':
  685. this.goTo(-0, 8);
  686.  
  687. if(!helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = true;
  688. helpers.controls.activeMovement['left'] = true;
  689. break;
  690.  
  691. case 'hashLogin':
  692. this.hashLogin();
  693. break;
  694.  
  695. case 'hashShow':
  696. this.hashShow();
  697. break;
  698. }
  699.  
  700. if(control.type.includes('movement')) {
  701. const keys = helpers.controls.activeMovement;
  702.  
  703. switch(true) {
  704. case keys['left'] && keys['up']:
  705. this.goTo(-0, -0);
  706. break;
  707. case keys['left'] && keys["down"]:
  708. this.goTo(-0, 0);
  709. break;
  710. case keys['up'] && keys['right']:
  711. this.goTo(0, -0);
  712. break;
  713. case keys['down'] && keys['right']:
  714. this.goTo(0, 0);
  715. break;
  716. }
  717. }
  718. }
  719. }
  720. }
  721.  
  722. controlTypeUp(e) {
  723. const settings = window.plus.settings;
  724. const helpers = window.plus.helpers;
  725.  
  726. const key = e.which;
  727. const controls = settings.controls;
  728.  
  729. if(controls.length === 0) return;
  730. if(document.activeElement.nodeName == 'INPUT' && helpers.controls.splitSwitch === false && helpers.controls.ejectSwitch === false) return;
  731.  
  732. for(let i = 0; i < controls.length; i++) {
  733. const control = controls[i];
  734.  
  735. if(key == control.keycode && !control.disabled) {
  736. switch(control.type) {
  737. case '1x':
  738. helpers.controls.splitSwitch = false;
  739. break;
  740. case '2x':
  741. helpers.controls.splitSwitch = false;
  742. break;
  743. case '4x':
  744. helpers.controls.splitSwitch = false;
  745. break;
  746. case '8x':
  747. helpers.controls.splitSwitch = false;
  748. break;
  749. case '16x':
  750. helpers.controls.splitSwitch = false;
  751. break;
  752.  
  753. case 'holdSplit':
  754. clearInterval(helpers.controls.splitInterval);
  755. helpers.controls.holdSplitSwitch = false;
  756. break;
  757.  
  758. case 'eject':
  759. helpers.controls.ejectSwitch = false;
  760. break;
  761. case 'holdEject':
  762. clearInterval(window.plus.helpers.controls.ejectInterval);
  763. helpers.controls.ejectSwitch = false;
  764. break;
  765.  
  766. case 'movementUp':
  767. if(helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = false;
  768. delete helpers.controls.activeMovement['up'];
  769. break;
  770. case 'movementRight':
  771. if(helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = false;
  772. delete helpers.controls.activeMovement['right'];
  773. break;
  774. case 'movementDown':
  775. if(helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = false;
  776. delete helpers.controls.activeMovement['down'];
  777. break;
  778. case 'movementLeft':
  779. if(helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = false;
  780. delete helpers.controls.activeMovement['left'];
  781. break;
  782. }
  783. }
  784. }
  785. }
  786.  
  787. split(times) {
  788. for(let i = 0; i < times; i++) {
  789. setTimeout(function() {
  790. window.split();
  791. }, 50 * i);
  792. }
  793. }
  794.  
  795. goTo(x, y) {
  796. x = window.innerWidth / x; y = window.innerHeight / y;
  797. window.goTo(x, y);
  798. }
  799. getCookie(name) {
  800. let v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
  801. return v ? v[2] : null;
  802. }
  803.  
  804. setCookie(name, value, days) {
  805. const d = new Date;
  806. d.setTime(d.getTime() + 24*60*60*1000*days);
  807. document.cookie = name + '=' + value + ';path=/;expires=' + d.toGMTString();
  808. }
  809. deleteCookie(name) {
  810. this.setCookie(name, '', -1);
  811. }
  812.  
  813. setHash(hash) {
  814. this.deleteCookie('user_hash');
  815. this.deleteCookie('PHPSESSID');
  816. this.setCookie('user_hash', hash, 30);
  817.  
  818. window.location.reload();
  819. }
  820.  
  821. async hashLogin() {
  822. const { value: hash } = await Swal.fire({
  823. title: 'Enter your hash',
  824. input: 'text',
  825. inputValue: '',
  826. showCancelButton: true,
  827. inputValidator: (value) => {
  828. if(!value) {
  829. return 'You need to write something!'
  830. }
  831.  
  832. if(value.length !== 40) {
  833. return 'The hash length must be 40 characters.'
  834. }
  835. }
  836. })
  837. if(hash) {
  838. this.setHash(hash);
  839. }
  840. }
  841.  
  842. hashShow() {
  843. const hash = this.getCookie('user_hash');
  844.  
  845. if(hash === null) {
  846. return Swal.fire({
  847. icon: 'error',
  848. text: 'You must be logged in to your account to view its hash.'
  849. });
  850. }
  851.  
  852. Swal.fire({
  853. icon: 'info',
  854. html: `The hash assigned to this account is: <strong>${hash}</strong>.<br><br>Remember to never give it to anyone!`
  855. });
  856. }
  857.  
  858. addEventListeners() {
  859. document.addEventListener('keydown', this.keydown);
  860. document.addEventListener('keyup', this.keyup);
  861. document.addEventListener('mousedown', this.mousedown);
  862. document.addEventListener('mouseup', this.mouseup);
  863.  
  864. document.getElementById('canvas').addEventListener('contextmenu', function(e) {
  865. e.preventDefault();
  866. });
  867. }
  868.  
  869. beforeLoad(callback) {
  870. if(window.modLoaded === false) {
  871. return callback();
  872. }
  873.  
  874. throw new Error('Failed to load callback on beforeLoad method.');
  875. }
  876.  
  877. onLoad(callback) {
  878. let checkLoadedIndex = 0;
  879. let checkLoaded = setInterval(() => {
  880. if(window.modLoaded === true) {
  881. clearInterval(checkLoaded);
  882. window.modLoaded = true;
  883. return callback();
  884. }
  885.  
  886. if(checkLoadedIndex >= 100) {
  887. clearInterval(checkLoaded);
  888. throw new Error('Failed to load callback on onLoad method.');
  889. }
  890.  
  891. checkLoadedIndex++
  892. }, 100);
  893. }
  894. }
  895.  
  896. class UI {
  897. constructor() {
  898. this.loadCSS();
  899. this.loadGUI();
  900. this.loadSettings();
  901. }
  902.  
  903. loadCSS() {
  904. const css = `
  905. #plusSettingsBtn {
  906. float: right;
  907. width: 12%;
  908. }
  909.  
  910. #plusSettings .modal-body {
  911. height: 300px;
  912. user-select: none;
  913. }
  914.  
  915. #plusSettings .modal-footer {
  916. user-select: none;
  917. }
  918.  
  919. #plusContent {
  920. height: 100%;
  921. }
  922.  
  923. #general-content {
  924. display: flex;
  925. flex-flow: column wrap;
  926. height: 160px;
  927. }
  928.  
  929. #plusContent .tab-pane {
  930. height: 100%;
  931. }
  932.  
  933. #plusTabs {
  934. text-align: center;
  935. border-bottom: none !important;
  936. margin-top: 1em;
  937. }
  938.  
  939. #plusTabs > li {
  940. float: none !important;
  941. display: inline-block;
  942. }
  943.  
  944. #plusTabs>li.active>a, #plusTabs>li.active>a:hover, #plusTabs>li.active>a:focus {
  945. border: none !important;
  946. background: #efefef;
  947. color: #27272A;
  948. }
  949.  
  950. #plusTabs>li>a {
  951. border: none !important;
  952. border-radius: 4px;
  953. color: #5f5f67;
  954. }
  955.  
  956. #pLabel {
  957. text-align: center;
  958. }
  959.  
  960. .plus-checkbox {
  961. padding-bottom: 0.5em;
  962. width: 50%;
  963. }
  964.  
  965. .plus-checkbox label {
  966. max-width: 100%;
  967. position: relative;
  968. display: inline-block;
  969. padding-left: 20px;
  970. margin-bottom: 0;
  971. font-weight: 400;
  972. vertical-align: middle;
  973. cursor: pointer;
  974. }
  975.  
  976. .plus-checkbox input[type=radio], .plus-checkbox input[type=checkbox] {
  977. position: absolute;
  978. margin-left: -20px;
  979. }
  980.  
  981. #skinPanel {
  982. float: left;
  983. margin-left: 5px;
  984. }
  985.  
  986. .skinBtn {
  987. margin: 0;
  988. padding: 2px 8px;
  989. }
  990.  
  991. .plusControls {
  992. display: block;
  993. height: 210px;
  994. }
  995.  
  996. .plusBtn {
  997. display: flex;
  998. justify-content: space-between;
  999. margin-top: 8px;
  1000. }
  1001.  
  1002. .plusBtn button {
  1003. width: 100px;
  1004. }
  1005.  
  1006. .plusHeader {
  1007. display: flex;
  1008. justify-content: center;
  1009. width: 100%;
  1010. }
  1011.  
  1012. .plusContent {
  1013. display: flex;
  1014. flex-flow: column nowrap;
  1015. height: 210px;
  1016. padding: 0;
  1017. margin: 0;
  1018. }
  1019.  
  1020. .plusContent li {
  1021. display: flex;
  1022. margin: 0 auto;
  1023. padding: 0.75rem;
  1024. width: 300px;
  1025. height: 34px;
  1026. align-items: center;
  1027. justify-content: space-between;
  1028. border-radius: 0.375rem;
  1029. background: #FAFAFA;
  1030. margin-top: 0.525rem;
  1031. }
  1032.  
  1033. .controlsContent {
  1034. display: flex;
  1035. flex-flow: column nowrap;
  1036. height: 210px;
  1037. padding: 0;
  1038. margin: 0;
  1039. }
  1040.  
  1041. .controlsContent li {
  1042. display: flex;
  1043. justify-content: space-around;
  1044. align-items: center;
  1045. margin-top: 5px;
  1046. }
  1047.  
  1048. .emptyPanel {
  1049. text-align: center;
  1050. padding: 0.5em;
  1051. margin: 0.5em;
  1052. }
  1053.  
  1054. .delete {
  1055. cursor: pointer;
  1056. }
  1057.  
  1058. html,
  1059. body {
  1060. height: 100%;
  1061. }
  1062.  
  1063. .main-panel {
  1064. margin: 0 2px;
  1065. box-shadow: 0 4px 6px -1px rgb(0 0 0 / 10%), 0 2px 4px -1px rgb(0 0 0 / 6%);
  1066. }
  1067.  
  1068. .friends-online {
  1069. border-radius: 10px;
  1070. box-shadow: 0 4px 6px -1px rgb(0 0 0 / 10%), 0 2px 4px -1px rgb(0 0 0 / 6%);
  1071. border: none;
  1072. }
  1073.  
  1074. #playBtn {
  1075. width: 74.8% !important;
  1076. }
  1077. #playBtn.has-spinner {
  1078. width: 62.9% !important;
  1079. }
  1080.  
  1081. #playBtn.btn-danger {
  1082. width: 75.5% !important;
  1083. }
  1084.  
  1085. #spectateBtn {
  1086. height: 34px;
  1087. }
  1088.  
  1089. table.chat-table {
  1090. margin-bottom: 34px !important;
  1091. }
  1092.  
  1093. .btn:focus, .btn:active:focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn.active.focus {
  1094. outline: none;
  1095. }
  1096.  
  1097. .btn-primary {
  1098. background-color: #3B82F6;
  1099. border: none;
  1100. }
  1101. .btn-primary:hover {
  1102. background-color: #2563EB;
  1103. }
  1104.  
  1105. .btn-primary:hover, .btn-primary:focus, .btn-primary.focus, .btn-primary:active, .btn-primary.active, .open>.dropdown-toggle.btn-primary {
  1106. background-color: #2563EB;
  1107. }
  1108. .btn-success {
  1109. background-color: #22C55E;
  1110. border: none;
  1111. }
  1112. .btn-success:hover {
  1113. background-color: #16A34A;
  1114. }
  1115.  
  1116. .btn-success:hover, .btn-success:focus, .btn-success.focus, .btn-success:active, .btn-success.active, .open>.dropdown-toggle.btn-success {
  1117. background-color: #16A34A;
  1118. }
  1119. .btn-settings {
  1120. float: none !important;
  1121. display: inline-block !important;
  1122. width: auto !important;
  1123. height: auto !important;
  1124. border: none;
  1125. background-color: #06B6D4;
  1126. }
  1127. .btn-settings:hover {
  1128. background-color: #0891B2;
  1129. border: none;
  1130. }
  1131.  
  1132. .btn-info:hover, .btn-info:focus, .btn-info.focus, .btn-info:active, .btn-info.active, .open>.dropdown-toggle.btn-info {
  1133. background-color: #0891B2;
  1134. }
  1135. .btn-warning {
  1136. background-color: #EAB308;
  1137. border: none;
  1138. }
  1139. .btn-warning:hover {
  1140. background-color: #CA8A04;
  1141. }
  1142.  
  1143. .btn-warning:hover, .btn-warning:focus, .btn-warning.focus, .btn-warning:active, .btn-warning.active, .open>.dropdown-toggle.btn-warning {
  1144. background-color: #CA8A04;
  1145. }
  1146. .btn-danger {
  1147. background-color: #F43F5E;
  1148. border: none;
  1149. }
  1150. .btn-danger:hover {
  1151. background-color: #E11D48;
  1152. }
  1153.  
  1154. .btn-danger:hover, .btn-danger:focus, .btn-danger.focus, .btn-danger:active, .btn-danger.active, .open>.dropdown-toggle.btn-danger {
  1155. background-color: #E11D48;
  1156. }
  1157.  
  1158. .form-control {
  1159. border: 1px solid #ced4da;
  1160. box-shadow: none;
  1161. }
  1162.  
  1163. #radio_mode .gm-s {
  1164. border: 2px solid #D1D5DB;
  1165. }
  1166.  
  1167. #chat_textbox {
  1168. border-radius: 0.3em;
  1169. }
  1170.  
  1171. .exp-bar {
  1172. border: 2px solid #3B82F6;
  1173. }
  1174.  
  1175. .exp-bar .progress-bar {
  1176. background-color: #60A5FA;
  1177. }
  1178.  
  1179. .modal-content {
  1180. border: none;
  1181. }
  1182.  
  1183. .swal2-popup {
  1184. font-size: 1.5rem !important;
  1185. }
  1186.  
  1187. .swal2-styled.swal2-confirm {
  1188. background-color: #3B82F6 !important;
  1189. }
  1190.  
  1191. .swal2-styled.swal2-confirm:focus {
  1192. box-shadow: 0 0 0 3px rgb(59 130 246 / 50%) !important;
  1193. }
  1194.  
  1195. .pickr {
  1196. float: left;
  1197. margin-left: 5px;
  1198. }
  1199.  
  1200. .pickr .pcr-button {
  1201. height: 1.8em;
  1202. width: 1.8em;
  1203. }
  1204.  
  1205. .pickr .pcr-button:after, .pickr .pcr-button:before {
  1206. border: 1px solid #80808060;
  1207. }
  1208.  
  1209. .hashLogin {
  1210. margin: 0 5px;
  1211. }
  1212.  
  1213. li {
  1214. list-style-type: none;
  1215. }
  1216.  
  1217. @media (min-width: 768px) {
  1218. .modal-content {
  1219. -webkit-box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);
  1220. box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);
  1221. }
  1222. }
  1223.  
  1224. body.dark-mode .plusColor {
  1225. background: #27272A;
  1226. }
  1227.  
  1228. body.dark-mode .plusColor .pcr-result {
  1229. background: #36363a;
  1230. color: #c6c6c8;
  1231. }
  1232.  
  1233. body.dark-mode .main-panel {
  1234. background: #18181B;
  1235. color: #c6c6c8;
  1236. }
  1237.  
  1238. body.dark-mode .form-control[disabled], body.dark-mode .form-control[readonly], body.dark-mode fieldset[disabled] .form-control {
  1239. background: #27272A;
  1240. }
  1241.  
  1242. body.dark-mode .form-control {
  1243. background: #27272A;
  1244. border: none;
  1245. color: #c6c6c8;
  1246. }
  1247.  
  1248. body.dark-mode #radio_mode .gm-s {
  1249. border: 2px solid #5f5f60;
  1250. background: #27272A;
  1251. }
  1252.  
  1253. body.dark-mode .bb-panel {
  1254. background: #18181B;
  1255. }
  1256.  
  1257. body.dark-mode .progress {
  1258. background: #27272A;
  1259. }
  1260.  
  1261. body.dark-mode .dropdown-menu>li>a {
  1262. color: #c6c6c8;
  1263. }
  1264.  
  1265. body.dark-mode .dropdown-menu {
  1266. background: #27272A;
  1267. }
  1268.  
  1269. body.dark-mode hr {
  1270. border-top: 1px solid #5f5f60;
  1271. }
  1272.  
  1273. body.dark-mode .table-striped>tbody>tr:nth-child(odd) {
  1274. background: #1f1f21 !important;
  1275. }
  1276.  
  1277. body.dark-mode .table-striped>tbody>tr:nth-child(even) {
  1278. background-color: #18181B !important;
  1279. }
  1280. body.dark-mode .table>thead>tr>th {
  1281. border-bottom: 2px solid #5f5f60;
  1282. }
  1283.  
  1284. body.dark-mode .table>thead>tr>th, body.dark-mode .table>tbody>tr>th, body.dark-mode .table>tfoot>tr>th, body.dark-mode .table>thead>tr>td, body.dark-mode .table>tbody>tr>td, body.dark-mode .table>tfoot>tr>td {
  1285. border-top: none;
  1286. }
  1287.  
  1288. body.dark-mode .modal-content {
  1289. background: #18181B;
  1290. color: #c6c6c8;
  1291. }
  1292.  
  1293. body.dark-mode .modal-header {
  1294. border-bottom: 1px solid #5f5f60;
  1295. }
  1296.  
  1297. body.dark-mode .bub-table-list {
  1298. border: 1px solid #353636;
  1299. }
  1300.  
  1301. body.dark-mode .modal-footer {
  1302. border-top: 1px solid #5f5f60;
  1303. }
  1304.  
  1305. body.dark-mode .dropdown-menu>li>a:hover, body.dark-mode .dropdown-menu>li>a:focus {
  1306. background: #202023;
  1307. }
  1308.  
  1309. body.dark-mode .user-notif div.info {
  1310. background: #1f1f21;
  1311. }
  1312.  
  1313. body.dark-mode .user-notif div {
  1314. border-bottom: none;
  1315. }
  1316.  
  1317. body.dark-mode .user-notif div.warning {
  1318. background: none;
  1319. border-left: 1px solid #F59E0B;
  1320. }
  1321.  
  1322. body.dark-mode .guild-members2 {
  1323. border-bottom: 1px solid #5f5f60;
  1324. border-top: 1px solid #5f5f60;
  1325. }
  1326.  
  1327. body.dark-mode .table>thead>tr>td.warning, body.dark-mode .table>tbody>tr>td.warning, body.dark-mode .table>tfoot>tr>td.warning, body.dark-mode .table>thead>tr>th.warning, body.dark-mode .table>tbody>tr>th.warning, body.dark-mode .table>tfoot>tr>th.warning, body.dark-mode .table>thead>tr.warning>td, body.dark-mode .table>tbody>tr.warning>td, body.dark-mode .table>tfoot>tr.warning>td, body.dark-mode .table>thead>tr.warning>th, body.dark-mode .table>tbody>tr.warning>th, body.dark-mode .table>tfoot>tr.warning>th {
  1328. color: #FDE047;
  1329. background: none;
  1330. }
  1331.  
  1332. body.dark-mode .nav-tabs {
  1333. border-bottom: none;
  1334. }
  1335.  
  1336. body.dark-mode .nav-tabs>li.active>a, body.dark-mode .nav-tabs>li.active>a:hover, body.dark-mode .nav-tabs>li.active>a:focus {
  1337. color: #c6c6c8;
  1338. background: #2a2a2d;
  1339. border: 1px solid transparent;
  1340. }
  1341.  
  1342. body.dark-mode .nav-tabs>li>a {
  1343. border-radius: 4px;
  1344. }
  1345.  
  1346. body.dark-mode .nav-tabs>li>a:hover {
  1347. border-color: transparent;
  1348. }
  1349.  
  1350. body.dark-mode .nav>li>a:hover, body.dark-mode .nav>li>a:focus {
  1351. background: #212123;
  1352. }
  1353.  
  1354. body.dark-mode .guild-members {
  1355. border-bottom: 1px solid #5f5f60;
  1356. }
  1357.  
  1358. body.dark-mode #tournament-modal .panel-heading {
  1359. color: #c6c6c8 !important;
  1360. border: none;
  1361. }
  1362.  
  1363. body.dark-mode .panel-default>.panel-heading {
  1364. background: #18181B;
  1365. }
  1366.  
  1367. body.dark-mode .panel {
  1368. background: #1f1f21;
  1369. }
  1370.  
  1371. body.dark-mode .panel-default {
  1372. border-color: transparent;
  1373. }
  1374.  
  1375. body.dark-mode #connecting > div {
  1376. background: #18181B !important;
  1377. color: #c6c6c8;
  1378. }
  1379.  
  1380. body.dark-mode #statsChartText, body.dark-mode #statsText {
  1381. color: #c6c6c8;
  1382. }
  1383. body.dark-mode #statsSubtext {
  1384. color: #a5a5a5;
  1385. }
  1386.  
  1387. body.dark-mode input.chat {
  1388. background: #18181B;
  1389. border: none;
  1390. }
  1391.  
  1392. body.dark-mode input:focus-visible {
  1393. outline: none;
  1394. color: #c6c6c8;
  1395. }
  1396.  
  1397. body.dark-mode .swal2-popup {
  1398. background: #18181B;
  1399. }
  1400.  
  1401. body.dark-mode .swal2-title {
  1402. color: #c6c6c8;
  1403. }
  1404.  
  1405. body.dark-mode .swal2-html-container {
  1406. color: #B4B4B5;
  1407. }
  1408.  
  1409. body.dark-mode .swal2-input-label {
  1410. color: #B4B4B5;
  1411. }
  1412.  
  1413. body.dark-mode .swal2-validation-message {
  1414. background: #27272A;
  1415. color: #c6c6c8;
  1416. }
  1417.  
  1418. body.dark-mode .scrollbar-track {
  1419. background: transparent;
  1420. }
  1421.  
  1422. body.dark-mode .scrollbar-track .show {
  1423. opacity: 0;
  1424. }
  1425.  
  1426. body.dark-mode .chatUsers li {
  1427. background: #27272A;
  1428. }
  1429.  
  1430. body.dark-mode .accounts li {
  1431. background: #27272A;
  1432. }
  1433.  
  1434. body.dark-mode #plusTabs>li.active>a, body.dark-mode #plusTabs>li.active>a:hover, body.dark-mode #plusTabs>li.active>a:focus {
  1435. border: none !important;
  1436. background: #27272A;
  1437. color: #c6c6c8;
  1438. }
  1439.  
  1440. body.dark-mode #plusTabs>li>a {
  1441. border: none !important;
  1442. border-radius: 4px;
  1443. color: #a2a2ad;
  1444. }
  1445.  
  1446. body.dark-mode .text-muted {
  1447. color: #a2a2a2;
  1448. }
  1449.  
  1450. body.dark-mode .close {
  1451. color: #c6c6c8;
  1452. text-shadow: none;
  1453. }
  1454.  
  1455. body.dark-mode .scrollbar-thumb {
  1456. background: #6B7280;
  1457. }
  1458.  
  1459. body.dark-mode ::-webkit-scrollbar {
  1460. width: 5px;
  1461. height: 5px;
  1462. }
  1463. body.dark-mode ::-webkit-scrollbar-button {
  1464. width: 0px;
  1465. height: 0px;
  1466. }
  1467. body.dark-mode ::-webkit-scrollbar-thumb {
  1468. background: #71717A;
  1469. border: 0px none #ffffff;
  1470. border-radius: 50px;
  1471. }
  1472. body.dark-mode ::-webkit-scrollbar-thumb:hover {
  1473. background: #52525B;
  1474. }
  1475. body.dark-mode ::-webkit-scrollbar-thumb:active {
  1476. background: #52525B;
  1477. }
  1478. body.dark-mode ::-webkit-scrollbar-track {
  1479. background: transparent;
  1480. border: 0px none #ffffff;
  1481. border-radius: 50px;
  1482. }
  1483. body.dark-mode ::-webkit-scrollbar-track:hover {
  1484. background: transparent;
  1485. }
  1486. body.dark-mode ::-webkit-scrollbar-track:active {
  1487. background: transparent;
  1488. }
  1489. body.dark-mode ::-webkit-scrollbar-corner {
  1490. background: transparent;
  1491. }
  1492.  
  1493. `;
  1494.  
  1495. const style = document.createElement('style');
  1496. style.textContent = css;
  1497. document.head.append(style);
  1498. }
  1499.  
  1500. loadGUI() {
  1501. $('#formStd h2').html('Bubble.am+').css({'display': 'inline-block', 'margin-right': '0.3em'});
  1502. $('#formStd h2').after(`<p style="display: inline-block; vertical-align: middle;">${window.plus.version}</p>`)
  1503. $('.btn-settings').after(`
  1504. <button id="plusSettingsBtn" onclick="return false;" class="btn btn-danger" data-toggle="modal" data-target=".bs-example-modal-lg">
  1505. <i class="fa fa-plus"></i>
  1506. </button>
  1507. `);
  1508. $('#chat_textbox').attr('maxlength', '99');
  1509.  
  1510. $('#overlays').before(`
  1511. <div id="plusSettings" class="modal fade" tabindex="-1" role="dialog">
  1512. <div class="modal-dialog" role="document">
  1513. <div class="modal-content">
  1514. <div class="modal-header">
  1515. <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
  1516. <h4 class="modal-title" id="pLabel">Bubble.am+ ${window.plus.version}</h4>
  1517. <ul id="plusTabs" class="nav nav-tabs" role="tablist">
  1518. <li role="presentation" class="active"><a href="#general" aria-controls="general" role="tab" data-toggle="tab">General</a></li>
  1519. <li role="presentation"><a href="#controls" aria-controls="controls" role="tab" data-toggle="tab">Controls</a></li>
  1520. <li role="presentation"><a href="#chat" aria-controls="chat" role="tab" data-toggle="tab">Chat</a></li>
  1521. <li role="presentation"><a href="#accounts" aria-controls="accounts" role="tab" data-toggle="tab">Accounts</a></li>
  1522. </ul>
  1523. </div>
  1524. <div class="modal-body">
  1525. <div id="plusContent" class="tab-content">
  1526. <div role="tabpanel" class="tab-pane active" id="general"></div>
  1527. <div role="tabpanel" class="tab-pane" id="controls"></div>
  1528. <div role="tabpanel" class="tab-pane" id="chat"></div>
  1529. <div role="tabpanel" class="tab-pane" id="accounts"></div>
  1530. </div>
  1531. </div>
  1532. <div class="modal-footer" style="text-align: center;">
  1533. <a href="http://enderror.pl" target="_blank">
  1534. <img id="enderror-logo" src="https://i.imgur.com/W7FkCgA.png" style="width: 3em;">
  1535. </a>
  1536. </div>
  1537. </div>
  1538. </div>
  1539. </div>
  1540. `);
  1541.  
  1542. const plusSettingsBtn = document.querySelector('#plusSettingsBtn');
  1543. plusSettingsBtn.addEventListener('click', function() {
  1544. $('#plusSettings').modal('toggle');
  1545. });
  1546.  
  1547. this.modsGUI();
  1548. this.controlsGUI();
  1549. this.chatGUI();
  1550. this.accountsGUI();
  1551.  
  1552. $('#plusSettings').on('shown.bs.modal', () => {
  1553. $(document).off('focusin.modal');
  1554. });
  1555.  
  1556. $('#plusSettings').on('hidden.bs.modal', () => {
  1557. const settings = Store.getSettings();
  1558.  
  1559. this.displayControls(settings.controls);
  1560. this.displayUsers(settings.chat);
  1561. this.displayAccounts(settings.accounts);
  1562. });
  1563.  
  1564. const pickr = new Pickr({
  1565. el: '#colorMod',
  1566. theme: 'nano',
  1567. container: 'body',
  1568. default: Store.getSettings().values.cellsColor,
  1569. position: 'right-start',
  1570. appClass: 'plusColor',
  1571.  
  1572. swatches: null,
  1573. components: {
  1574. preview: true,
  1575. opacity: true,
  1576. hue: true,
  1577. interaction: {
  1578. hex: false,
  1579. rgba: false,
  1580. hsla: false,
  1581. hsva: false,
  1582. cmyk: false,
  1583. input: true,
  1584. clear: false,
  1585. save: true
  1586. }
  1587. }
  1588. });
  1589. pickr.on('save', (color, instance) => {
  1590. window.plus.settings.values.cellsColor = color.toRGBA().toString(3);
  1591. Store.setSettings();
  1592. });
  1593.  
  1594. window.Scrollbars = Scrollbar.initAll();
  1595. }
  1596.  
  1597. modsGUI() {
  1598. $('#general').append(`
  1599. <div id="general-content">
  1600. <div class="plus-checkbox">
  1601. <label>
  1602. <input id="plus_transparentVirus" type="checkbox" onchange="setTransparentVirus($(this).is(':checked'));"> Transparent virus
  1603. </label>
  1604. </div>
  1605. <div class="plus-checkbox">
  1606. <label>
  1607. <input id="plus_noGrid" type="checkbox" onchange="setNoGrid($(this).is(':checked'));"> No grid
  1608. </label>
  1609. </div>
  1610. <div class="plus-checkbox" style="display: flex; align-items: center;">
  1611. <label style="float: left;">
  1612. <input id="plus_customColor" type="checkbox" onchange="setCustomColor($(this).is(':checked'));"> Custom cells color
  1613. </label>
  1614. <div id="colorMod" style="float: left;"></div>
  1615. </div>
  1616. <div class="plus-checkbox" style="display: flex; align-items: center;">
  1617. <label style="float: left;">
  1618. <input id="plus_customSkin" type="checkbox" onchange="setCustomSkin($(this).is(':checked'));"> Custom skin
  1619. </label>
  1620. <div id="skinPanel">
  1621. <button id="setSkinBtn" class="btn btn-success skinBtn"><i class="fa fa-plus"></i></button>
  1622. <button id="showSkinBtn" class="btn btn-primary skinBtn"><i class="fa fa-eye"></i></button>
  1623. </div>
  1624. </div>
  1625. <div class="plus-checkbox">
  1626. <label>
  1627. <input id="plus_bypassSkin" type="checkbox" onchange="setBypassSkin($(this).is(':checked'));"> Bypass "No skins"
  1628. </label>
  1629. </div>
  1630. <div class="plus-checkbox">
  1631. <label>
  1632. <input id="plus_darkMenu" type="checkbox" onchange="setDarkMenu($(this).is(':checked'));"> Dark theme menu
  1633. </label>
  1634. </div>
  1635. <div class="plus-checkbox">
  1636. <label>
  1637. <input id="plus_virusSplitCounter" type="checkbox" onchange="setVirusSplitCounter($(this).is(':checked'));"> Virus split counter
  1638. </label>
  1639. </div>
  1640. <div class="plus-checkbox">
  1641. <label>
  1642. <input id="plus_cellsCounter" type="checkbox" onchange="setCellsCounter($(this).is(':checked'));"> Cells counter
  1643. </label>
  1644. </div>
  1645. <div class="plus-checkbox">
  1646. <label>
  1647. <input id="plus_hideName" type="checkbox" onchange="setHideName($(this).is(':checked'));"> Hide your name
  1648. </label>
  1649. </div>
  1650. <div class="plus-checkbox">
  1651. <label>
  1652. <input id="plus_showMotherMass" type="checkbox" onchange="setShowMotherMass($(this).is(':checked'));"> Show mother mass
  1653. </label>
  1654. </div>
  1655. </div>
  1656. `);
  1657.  
  1658. document.querySelector('#setSkinBtn').addEventListener('click', async () => {
  1659. const { value: skinValue } = await Swal.fire({
  1660. title: 'Enter skin url (png, jpg or jpeg)',
  1661. input: 'text',
  1662. showCancelButton: true,
  1663. inputValidator: (value) => {
  1664. if(!value) {
  1665. return 'You need to write something.'
  1666. }
  1667.  
  1668. if(!this.isUriImage(value)) {
  1669. return 'Your skin url is wrong.'
  1670. }
  1671. }
  1672. });
  1673. if(skinValue) {
  1674. Swal.fire({
  1675. title: 'Skin has been successfully set.',
  1676. imageUrl: skinValue,
  1677. imageAlt: 'Skin',
  1678. });
  1679.  
  1680. const settings = window.plus.settings;
  1681. const helpers = window.plus.helpers;
  1682. settings.values.skinUrl = skinValue;
  1683. helpers.skin.skinInd == 1 ? helpers.skin.skinInd++ : helpers.skin.skinInd--;
  1684. Store.setSettings();
  1685. }
  1686. });
  1687.  
  1688. document.querySelector('#showSkinBtn').addEventListener('click', async function() {
  1689. const settings = window.plus.settings;
  1690.  
  1691. if(typeof(settings.values.skinUrl) === 'string') {
  1692. Swal.fire({
  1693. title: 'Current skin',
  1694. imageUrl: settings.values.skinUrl,
  1695. imageAlt: 'Skin',
  1696. });
  1697. } else {
  1698. Swal.fire({
  1699. title: 'Current skin',
  1700. text: 'No skin has been set yet.',
  1701. });
  1702. }
  1703. });
  1704. }
  1705.  
  1706. controlsGUI() {
  1707. $('#controls').append(`
  1708. <div class="plusHeader" style="text-align: center; justify-content: space-around;">
  1709. <strong style="width: 240px;">Type</strong>
  1710. <strong style="width: 200px; padding-left: 10px;">Key</strong>
  1711. <strong style="width: 60px;">Disabled</strong>
  1712. <strong style="width: 40px;"></strong>
  1713. </div>
  1714. <div class="plusContent-wrapper" data-scrollbar>
  1715. <ul class="controlsContent controls"></ul>
  1716. </div>
  1717. <div class="plusBtn">
  1718. <button class="btn btn-warning controlsDefault">Load default</button>
  1719. <button class="btn btn-danger controlsDelete">Delete all</button>
  1720. <button class="btn btn-success controlsNew">Add new</button>
  1721. <button class="btn btn-primary controlsSave">Save</button>
  1722. </div>
  1723. `);
  1724.  
  1725. $('.controls').on('click', (e) => {
  1726. if(!e.target.className.includes('delete')) return;
  1727.  
  1728. const controlsLength = $('.controls li').length;
  1729. const hasEmpty = $('.controls').find('.emptyPanel').length === 1;
  1730.  
  1731. if(controlsLength === 1) {
  1732. this.displayEmpty('.controls', 'controls');
  1733. }
  1734.  
  1735. if(controlsLength !== 1 && hasEmpty) {
  1736. $('.controls .emptyPanel').remove();
  1737. }
  1738.  
  1739. e.target.parentElement.remove();
  1740. });
  1741.  
  1742. $('.controlsDefault').on('click', () => {
  1743. const defaultSettings = window.plus.defaultSettings;
  1744.  
  1745. $('.plusControls tbody').html('');
  1746.  
  1747. this.displayControls(defaultSettings.controls);
  1748. });
  1749.  
  1750. $('.controlsDelete').on('click', () => {
  1751. this.displayEmpty('.controls', 'controls');
  1752. });
  1753.  
  1754. $('.controlsNew').on('click', () => {
  1755. this.addControl();
  1756. });
  1757.  
  1758. $('.controlsSave').on('click', () => {
  1759. const newControls = [];
  1760. const actualControls = document.querySelectorAll('.controls li');
  1761. const settings = window.plus.settings;
  1762.  
  1763. for(let i = 0; i < actualControls.length; i++) {
  1764. const li = actualControls[i];
  1765. const select = li.querySelectorAll('select');
  1766. const checkbox = li.querySelector('input');
  1767.  
  1768. const controlType = select[0].value;
  1769. const controlKey = select[1].value;
  1770. const controlDisabled = checkbox.checked;
  1771.  
  1772. newControls.push({
  1773. type: controlType,
  1774. keycode: controlKey,
  1775. disabled: controlDisabled
  1776. });
  1777. }
  1778.  
  1779. settings.controls = newControls;
  1780. Store.setSettings();
  1781.  
  1782. Swal.fire({
  1783. icon: 'success',
  1784. title: 'Success!',
  1785. text: 'Your new controls configuration has been successfully saved.'
  1786. });
  1787. });
  1788. }
  1789.  
  1790. addControl() {
  1791. const controlsTemplate = `
  1792. <li>
  1793. <select class="form-control" style="border-radius:4px; width: 15em;">
  1794. <option value="1x" selected="selected">Split</option>
  1795. <option value="eject">Eject</option>
  1796. <option value="setCamera">Lock/unlock camera [spectate]</option>
  1797. <option value="2x">2x split</option>
  1798. <option value="4x">4x split</option>
  1799. <option value="8x">8x split</option>
  1800. <option value="16x">16x split</option>
  1801. <option value="holdSplit">Hold split</option>
  1802. <option value="holdEject">Hold eject</option>
  1803. <option value="movementUp">Movement up</option>
  1804. <option value="movementRight">Movement right</option>
  1805. <option value="movementDown">Movement down</option>
  1806. <option value="movementLeft">Movement left</option>
  1807. <option value="hashLogin">Login by hash</option>
  1808. <option value="hashShow">Show hash</option>
  1809. </select>
  1810. <select class="form-control" style="border-radius:4px; width: 12em;">
  1811. <option value="1">Left Click</option>
  1812. <option value="2">Scroll Click</option>
  1813. <option value="3">Right Click</option>
  1814. <option value="9">Tab</option>
  1815. <option value="12">Clear</option>
  1816. <option value="13">Enter</option>
  1817. <option value="16">Shift</option>
  1818. <option value="17">Ctrl</option>
  1819. <option value="18">Alt</option>
  1820. <option value="27">Esc</option>
  1821. <option value="32" selected="selected">Space</option>
  1822. <option value="33">Page Up</option>
  1823. <option value="34">Page Down</option>
  1824. <option value="35">End</option>
  1825. <option value="36">Home</option>
  1826. <option value="37">Left Arrow</option>
  1827. <option value="38">Up Arrow</option>
  1828. <option value="39">Right Arrow</option>
  1829. <option value="40">Down Arrow</option>
  1830. <option value="45">Insert</option>
  1831. <option value="46">Delete</option>
  1832. <option value="48">0</option>
  1833. <option value="49">1</option>
  1834. <option value="50">2</option>
  1835. <option value="51">3</option>
  1836. <option value="52">4</option>
  1837. <option value="53">5</option>
  1838. <option value="54">6</option>
  1839. <option value="55">7</option>
  1840. <option value="56">8</option>
  1841. <option value="57">9</option>
  1842. <option value="65">A</option>
  1843. <option value="66">B</option>
  1844. <option value="67">C</option>
  1845. <option value="68">D</option>
  1846. <option value="69">E</option>
  1847. <option value="70">F</option>
  1848. <option value="71">G</option>
  1849. <option value="72">H</option>
  1850. <option value="73">I</option>
  1851. <option value="74">J</option>
  1852. <option value="75">K</option>
  1853. <option value="76">L</option>
  1854. <option value="77">M</option>
  1855. <option value="78">N</option>
  1856. <option value="79">O</option>
  1857. <option value="80">P</option>
  1858. <option value="81">Q</option>
  1859. <option value="82">R</option>
  1860. <option value="83">S</option>
  1861. <option value="84">T</option>
  1862. <option value="85">U</option>
  1863. <option value="86">V</option>
  1864. <option value="87">W</option>
  1865. <option value="88">X</option>
  1866. <option value="89">Y</option>
  1867. <option value="90">Z</option>
  1868. <option value="96">Numpad 0</option>
  1869. <option value="97">Numpad 1</option>
  1870. <option value="98">Numpad 2</option>
  1871. <option value="99">Numpad 3</option>
  1872. <option value="100">Numpad 4</option>
  1873. <option value="101">Numpad 5</option>
  1874. <option value="102">Numpad 6</option>
  1875. <option value="103">Numpad 7</option>
  1876. <option value="104">Numpad 8</option>
  1877. <option value="105">Numpad 9</option>
  1878. <option value="106">Numpad *</option>
  1879. <option value="107">Numpad +</option>
  1880. <option value="109">Numpad -</option>
  1881. <option value="110">Numpad .</option>
  1882. <option value="111">Numpad /</option>
  1883. <option value="112">F1</option>
  1884. <option value="113">F2</option>
  1885. <option value="114">F3</option>
  1886. <option value="115">F4</option>
  1887. <option value="116">F5</option>
  1888. <option value="117">F6</option>
  1889. <option value="118">F7</option>
  1890. <option value="119">F8</option>
  1891. <option value="120">F9</option>
  1892. <option value="121">F10</option>
  1893. <option value="122">F11</option>
  1894. <option value="123">F12</option>
  1895. <option value="124">F13</option>
  1896. <option value="125">F14</option>
  1897. <option value="126">F15</option>
  1898. <option value="127">F16</option>
  1899. <option value="128">F17</option>
  1900. <option value="129">F18</option>
  1901. <option value="130">F19</option>
  1902. <option value="131">F20</option>
  1903. <option value="132">F21</option>
  1904. <option value="133">F22</option>
  1905. <option value="134">F23</option>
  1906. <option value="135">F24</option>
  1907. <option value="186">;</option>
  1908. <option value="187">=</option>
  1909. <option value="188">,</option>
  1910. <option value="189">-</option>
  1911. <option value="190">.</option>
  1912. <option value="191">/</option>
  1913. <option value="192">&#96;</option>
  1914. <option value="219">[</option>
  1915. <option value="220">&#92;</option>
  1916. <option value="221">]</option>
  1917. <option value="222">'</option>
  1918. </select>
  1919. <input type="checkbox" style="margin: 0;">
  1920. <i class="fa fa-remove delete"></i>
  1921. </li>
  1922. `;
  1923.  
  1924. const hasEmpty = $('.controls').find('.emptyPanel').length === 1;
  1925. if(hasEmpty) {
  1926. $('.controls .emptyPanel').remove();
  1927. }
  1928.  
  1929. $('.controls').append(controlsTemplate);
  1930. this.fixScrollbar(window.Scrollbars[0]);
  1931. }
  1932.  
  1933. displayControls(settings) {
  1934. if(settings.length === 0) {
  1935. return this.displayEmpty('.controls', 'controls');
  1936. };
  1937.  
  1938. $('.controls').html('');
  1939.  
  1940. for(let i = 0; i < settings.length; i++) {
  1941. const setting = settings[i];
  1942. this.addControl();
  1943.  
  1944. const li = document.querySelectorAll('.controls li')[i];
  1945. const select = li.querySelectorAll('select');
  1946. const checkbox = li.querySelector('input');
  1947.  
  1948. select[0].value = setting.type;
  1949. select[1].value = setting.keycode;
  1950. checkbox.checked = setting.disabled;
  1951. }
  1952. }
  1953.  
  1954. chatGUI() {
  1955. $('#chat').append(`
  1956. <div class="plusHeader">
  1957. <strong>Blocked users</strong>
  1958. </div>
  1959. <div class="plusContent-wrapper" data-scrollbar>
  1960. <ul class="plusContent chatUsers"></ul>
  1961. </div>
  1962. <div class="plusBtn">
  1963. <button class="btn btn-danger chatDelete">Delete all</button>
  1964. <button class="btn btn-success chatNew">Add new</button>
  1965. <button class="btn btn-primary chatSave">Save</button>
  1966. </div>
  1967. `);
  1968.  
  1969. $('.chatUsers').on('click', (e) => {
  1970. if(!e.target.className.includes('delete')) return;
  1971.  
  1972. const chatLength = $('.chatUsers li').length;
  1973. const hasEmpty = $('.chatUsers').find('.emptyPanel').length === 1;
  1974.  
  1975. if(chatLength === 1) {
  1976. this.displayEmpty('.chatUsers', 'users');
  1977. }
  1978.  
  1979. if(chatLength !== 1 && hasEmpty) {
  1980. $('.chatUsers .emptyPanel').remove();
  1981. }
  1982.  
  1983. e.target.parentElement.remove();
  1984. });
  1985.  
  1986. $('.chatDelete').on('click', () => {
  1987. this.displayEmpty('.chatUsers', 'users');
  1988. });
  1989.  
  1990. $('.chatNew').on('click', async () => {
  1991. let { value: playerNick } = await Swal.fire({
  1992. title: 'Player nickname',
  1993. text: 'You don\'t need to specify a clan tag.',
  1994. input: 'text',
  1995. inputValue: '',
  1996. showCancelButton: true,
  1997. inputValidator: (value) => {
  1998. if(!value) {
  1999. return 'You need to write something!'
  2000. }
  2001. if(value.length > 15) {
  2002. return 'Player nickname cannot be longer than 15 characters.'
  2003. }
  2004. const currentUsers = this.getCurrentUsers();
  2005. if(currentUsers.includes(value.toLowerCase())) {
  2006. return 'This user is already on the list.';
  2007. }
  2008. }
  2009. });
  2010. if(playerNick) {
  2011. this.addUser(playerNick);
  2012. }
  2013. });
  2014.  
  2015. $('.chatSave').on('click', () => {
  2016. const newChat = [];
  2017. const actualChat = document.querySelectorAll('.chatUsers li');
  2018. const settings = window.plus.settings;
  2019.  
  2020. for(let i = 0; i < actualChat.length; i++) {
  2021. const li = actualChat[i];
  2022. const playerNick = li.querySelectorAll('span')[0].innerHTML;
  2023.  
  2024. newChat.push(playerNick.toLowerCase());
  2025. }
  2026.  
  2027. settings.chat = newChat;
  2028. Store.setSettings();
  2029.  
  2030. Swal.fire({
  2031. icon: 'success',
  2032. title: 'Success!',
  2033. text: 'Your new chat configuration has been successfully saved.'
  2034. });
  2035. });
  2036. }
  2037.  
  2038. addUser(name) {
  2039. const hasEmpty = $('.chatUsers').find('.emptyPanel').length === 1;
  2040. if(hasEmpty) {
  2041. $('.chatUsers .emptyPanel').remove();
  2042. }
  2043.  
  2044. $('.chatUsers').append(`
  2045. <li>
  2046. <span>${name.toLowerCase()}</span>
  2047. <i class="fa fa-remove delete"></i>
  2048. </li>
  2049. `);
  2050.  
  2051. this.fixScrollbar(window.Scrollbars[1]);
  2052. }
  2053.  
  2054. getCurrentUsers() {
  2055. const currentUsers = document.querySelectorAll('.chatUsers li');
  2056. const nicks = [];
  2057.  
  2058. for(let i = 0; i < currentUsers.length; i++) {
  2059. const user = currentUsers[i];
  2060. const nick = user.querySelectorAll('span')[0].innerHTML.toLowerCase();
  2061.  
  2062. nicks.push(nick);
  2063. }
  2064.  
  2065. return nicks;
  2066. }
  2067.  
  2068. displayUsers(users) {
  2069. if(users.length === 0) {
  2070. return this.displayEmpty('.chatUsers', 'users');
  2071. }
  2072.  
  2073. $('.chatUsers').html('');
  2074.  
  2075. for(let i = 0; i < users.length; i++) {
  2076. const playerNick = users[i];
  2077.  
  2078. this.addUser(playerNick);
  2079. }
  2080. }
  2081.  
  2082. accountsGUI() {
  2083. $('#accounts').append(`
  2084. <div class="plusHeader">
  2085. <strong>Your accounts</strong>
  2086. </div>
  2087. <div class="plusContent-wrapper" data-scrollbar>
  2088. <ul class="plusContent accounts"></ul>
  2089. </div>
  2090. <div class="plusBtn">
  2091. <button class="btn btn-danger accountsDelete">Delete all</button>
  2092. <button class="btn btn-success accountsNew">Add new</button>
  2093. <button class="btn btn-primary accountsSave">Save</button>
  2094. </div>
  2095. `);
  2096.  
  2097. $('.accounts').on('click', (e) => {
  2098. if(!e.target.className.includes('delete')) return;
  2099.  
  2100. const accountsLength = $('.accounts li').length;
  2101. const hasEmpty = $('.accounts').find('.emptyPanel').length === 1;
  2102.  
  2103. if(accountsLength === 1) {
  2104. this.displayEmpty('.accounts', 'accounts');
  2105. }
  2106.  
  2107. if(accountsLength !== 1 && hasEmpty) {
  2108. $('.accounts .emptyPanel').remove();
  2109. }
  2110.  
  2111. e.target.parentElement.remove();
  2112. });
  2113.  
  2114. $('.accountsDelete').on('click', () => {
  2115. this.displayEmpty('.accounts', 'accounts');
  2116. });
  2117.  
  2118. $('.accountsNew').on('click', async () => {
  2119. const {value: account} = await Swal.fire({
  2120. title: 'Add account',
  2121. html: `
  2122. <input id="name" class="swal2-input" placeholder="Name" maxlength="15">
  2123. <input id="hash" class="swal2-input" placeholder="Hash" maxlength="40">
  2124. `,
  2125. focusConfirm: false,
  2126. preConfirm: () => {
  2127. const name = document.getElementById('name').value.trim();
  2128. const hash = document.getElementById('hash').value.trim();
  2129. const currentAccounts = this.getCurrentAccounts();
  2130. const currentNames = currentAccounts.names;
  2131. const currentHashes = currentAccounts.hashes;
  2132. if(name.length === 0 || hash.length === 0) {
  2133. return Swal.showValidationMessage('You must complete all fields.');
  2134. }
  2135. if(hash.length !== 40) {
  2136. return Swal.showValidationMessage('The hash length must be 40 characters.');
  2137. }
  2138. if(currentNames.includes(name.toLowerCase()) || currentHashes.includes(hash)) {
  2139. return Swal.showValidationMessage('The name or hash is already on the list.');
  2140. }
  2141. return {
  2142. name,
  2143. hash
  2144. }
  2145. }
  2146. });
  2147. if(account) {
  2148. this.addAccount(account.name, account.hash);
  2149. }
  2150. });
  2151.  
  2152. $('.accountsSave').on('click', () => {
  2153. const newAccounts = [];
  2154. const actualAccounts = document.querySelectorAll('.accounts li');
  2155. const settings = window.plus.settings;
  2156.  
  2157. for(let i = 0; i < actualAccounts.length; i++) {
  2158. const li = actualAccounts[i];
  2159. const accountName = li.querySelectorAll('span')[0].innerHTML;
  2160. const accountHash = li.dataset.hash;
  2161.  
  2162. newAccounts.push({
  2163. name: accountName,
  2164. hash: accountHash
  2165. });
  2166. }
  2167.  
  2168. settings.accounts = newAccounts;
  2169. Store.setSettings();
  2170.  
  2171. Swal.fire({
  2172. icon: 'success',
  2173. title: 'Success!',
  2174. text: 'Your new accounts configuration has been successfully saved.'
  2175. });
  2176. });
  2177. }
  2178.  
  2179. addAccount(name, hash) {
  2180. const hasEmpty = $('.accounts').find('.emptyPanel').length === 1;
  2181. if(hasEmpty) {
  2182. $('.accounts .emptyPanel').remove();
  2183. }
  2184.  
  2185. $('.accounts').append(`
  2186. <li data-hash="${hash}">
  2187. <span>${name}</span>
  2188. <div>
  2189. <span class="hashLogin" style="cursor: pointer;" onclick="window.plus.setHash($(this).parent().parent().data('hash'));">
  2190. <i class="fa fa-arrow-right"></i>
  2191. </span>
  2192. <span class="delete" style="cursor: pointer;" onclick="$(this).parent().parent().remove();">
  2193. <i class="fa fa-remove"></i>
  2194. </span>
  2195. </div>
  2196. </li>
  2197. `);
  2198.  
  2199. this.fixScrollbar(window.Scrollbars[2]);
  2200. }
  2201.  
  2202. displayAccounts(accounts) {
  2203. if(accounts.length === 0) {
  2204. return this.displayEmpty('.accounts', 'accounts');
  2205. }
  2206.  
  2207. $('.accounts').html('');
  2208.  
  2209. for(const account in accounts) {
  2210. const { name, hash } = accounts[account];
  2211. this.addAccount(name, hash);
  2212. }
  2213. }
  2214.  
  2215. getCurrentAccounts() {
  2216. const currentAccounts = document.querySelectorAll('.accounts li');
  2217.  
  2218. const accounts = [];
  2219. const names = [];
  2220. const hashes = [];
  2221.  
  2222. for(let i = 0; i < currentAccounts.length; i++) {
  2223. const account = currentAccounts[i];
  2224. const name = account.querySelectorAll('span')[0].innerHTML.toLowerCase();
  2225. const hash = account.dataset.hash;
  2226.  
  2227. accounts.push({
  2228. name,
  2229. hash
  2230. });
  2231.  
  2232. names.push(name);
  2233. hashes.push(hash);
  2234. }
  2235.  
  2236. return {
  2237. accounts,
  2238. names,
  2239. hashes
  2240. }
  2241. }
  2242.  
  2243. loadSettings() {
  2244. const settings = Store.getSettings();
  2245.  
  2246. window.plus.settings = settings;
  2247.  
  2248. for(const checkbox in settings.checkbox) {
  2249. const name = checkbox;
  2250. const value = settings.checkbox[checkbox];
  2251.  
  2252. $(`#plus_${name}`).prop('checked', value).change();
  2253. }
  2254.  
  2255. this.displayControls(settings.controls);
  2256. this.displayUsers(settings.chat);
  2257. this.displayAccounts(settings.accounts);
  2258. }
  2259.  
  2260. displayEmpty(element, text) {
  2261. $(element).html(`<div class="emptyPanel">There are currently no ${text} in this panel.</div>`);
  2262. }
  2263.  
  2264. fixScrollbar(scrollbar) {
  2265. scrollbar.update();
  2266.  
  2267. const limitY = scrollbar.limit.y;
  2268. scrollbar.setPosition(0, limitY);
  2269. }
  2270.  
  2271. isUriImage = function(uri) {
  2272. uri = uri.split('?')[0];
  2273. const parts = uri.split('.');
  2274. const extension = parts[parts.length - 1];
  2275. const imageTypes = ['png', 'jpg', 'jpeg'];
  2276. if(imageTypes.indexOf(extension) !== -1) {
  2277. return true;
  2278. }
  2279. }
  2280. }
  2281.  
  2282. class Store {
  2283. static getSettings() {
  2284. let settings;
  2285. if(localStorage.getItem('plus_settings') === null) {
  2286. this.setSettings();
  2287. }
  2288.  
  2289. settings = JSON.parse(localStorage.getItem('plus_settings'));
  2290.  
  2291. return settings;
  2292. }
  2293.  
  2294. static setSettings() {
  2295. localStorage.setItem('plus_settings', JSON.stringify(window.plus.settings));
  2296. }
  2297. }
  2298.  
  2299. window.modLoaded = false;
  2300.  
  2301. window.sleep = function(ms) {
  2302. return new Promise(resolve => setTimeout(resolve, ms));
  2303. }
  2304.  
  2305. window.setCustomColor = function(a) {
  2306. window.plus.settings.checkbox.customColor = a;
  2307. Store.setSettings();
  2308. }
  2309. window.setTransparentVirus = function(a) {
  2310. window.plus.settings.checkbox.transparentVirus = a;
  2311. Store.setSettings();
  2312. }
  2313.  
  2314. window.setCustomSkin = function(a) {
  2315. window.plus.settings.checkbox.customSkin = a;
  2316. Store.setSettings();
  2317. }
  2318.  
  2319. window.setBypassSkin = function(a) {
  2320. window.plus.settings.checkbox.bypassSkin = a;
  2321. Store.setSettings();
  2322. }
  2323. window.setNoGrid = function(a) {
  2324. window.plus.settings.checkbox.noGrid = a;
  2325. Store.setSettings();
  2326. }
  2327. window.setDarkMenu = function(a) {
  2328. window.plus.settings.checkbox.darkMenu = a;
  2329. if(a === true) {
  2330. $('body').addClass('dark-mode');
  2331. $('#enderror-logo').attr('src', 'https://i.imgur.com/ewMCLSe.png');
  2332. } else {
  2333. $('body').removeClass('dark-mode')
  2334. $('#enderror-logo').attr('src', 'https://i.imgur.com/W7FkCgA.png');
  2335. }
  2336.  
  2337. Store.setSettings();
  2338. }
  2339.  
  2340. window.setVirusSplitCounter = function(a) {
  2341. window.plus.settings.checkbox.virusSplitCounter = a;
  2342. Store.setSettings();
  2343. }
  2344.  
  2345. window.setCellsCounter = function(a) {
  2346. window.plus.settings.checkbox.cellsCounter = a;
  2347. Store.setSettings();
  2348. }
  2349.  
  2350. window.setHideName = function(a) {
  2351. window.plus.settings.checkbox.hideName = a;
  2352. Store.setSettings();
  2353. }
  2354.  
  2355. window.setShowMotherMass = function(a) {
  2356. window.plus.settings.checkbox.showMotherMass = a;
  2357. Store.setSettings();
  2358. }
  2359.  
  2360. window.plus = new Plus();