Bubble.am+

Script that adds useful features to the game.

当前为 2021-07-14 提交的版本,查看 最新版本

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