Bubble.am+

Script that adds useful features to the game.

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

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