// ==UserScript==
// @name Bubble.am+
// @namespace https://enderror.pl
// @version 21.07.11
// @description Script that adds useful features to the game.
// @author enderror
// @match *://bubble.am/*
// @run-at document-start
// @grant none
// ==/UserScript==
class Plus {
constructor() {
this.name = 'plus';
this.version = '21.07.11';
this.author = 'enderror';
this.additionalCoreChanges = [];
this.defaultSettings = {
controls: [
{ type: '1x', keycode: '32', disabled: false },
{ type: 'eject', keycode: '87', disabled: false },
{ type: 'setCamera', keycode: '81', disabled: false },
{ type: 'holdEject', keycode: '69', disabled: false },
{ type: '1x', keycode: '49', disabled: false },
{ type: '2x', keycode: '50', disabled: false },
{ type: '4x', keycode: '51', disabled: false },
{ type: '8x', keycode: '52', disabled: false },
{ type: '16x', keycode: '53', disabled: false },
{ type: 'holdSplit', keycode: '16', disabled: false },
{ type: 'movementUp', keycode: '81', disabled: false },
{ type: 'movementLeft', keycode: '65', disabled: false },
{ type: 'movementDown', keycode: '83', disabled: false },
{ type: 'movementRight', keycode: '68', disabled: false },
]
};
this.settings = {
checkbox: {
transparentVirus: true,
noGrid: false,
customColor: false,
customSkin: false,
bypassSkin: false,
darkMenu: false,
virusSplitCounter: true,
cellsCounter: true,
hideName: false,
showMotherMass: false
},
values: {
cellsColor: '#e31e24',
skinUrl: null,
},
controls: this.defaultSettings.controls,
chat: [],
accounts: []
}
this.helpers = {
skin: {
skinInd: 1,
gid: 0,
},
controls: {
splitSwitch: false,
holdSplitSwitch: false,
splitInterval: undefined,
ejectSwitch: false,
ejectInterval: undefined,
isActiveMovement: false,
activeMovement: []
}
}
this.run();
}
run() {
console.log(`${this.name} ${this.version} created by ${this.author}`);
if(location.pathname.includes('.txt')
|| location.pathname.includes('.png')
|| location.pathname.includes('.jpg')
|| location.pathname.includes('.jpeg')) {
return;
}
if((location.host === 'bubble.am' && location.pathname === '/') || location.pathname.length > 15) {
window.stop();
location.href = `${location.origin}/${this.name}${location.hash}`;
return;
}
document.documentElement.innerHTML = '';
const request = new XMLHttpRequest();
const url = 'http://bubble.am';
request.open('get', url, true);
request.send();
request.onload = async(e) => {
const newCore = await this.modify(e.target.responseText);
document.open();
document.write(newCore);
document.close();
let documentCheck = setInterval(() => {
if(document.readyState == 'complete') {
clearInterval(documentCheck);
new UI();
this.addEventListeners();
window.modLoaded = true;
}
}, 100);
}
}
async modify(core) {
core = core.replace(/(.*)(Ubuntu\:700)(.*)(\n.*){4}/gm, `
<link rel='stylesheet' type='text/css' href='http://fonts.googleapis.com/css?family=Ubuntu:700'>
<link rel="stylesheet" type="text/css" href="http://m.bubble.am/css/bootstrap.min.css?v=3">
<link rel="stylesheet" type="text/css" href="http://bubble.am/css/style.css?v=0.2.50" />
<link rel="stylesheet" type="text/css" href="http://bubble.am/css/font.awesome.css?v5" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-colorpicker/2.5.3/css/bootstrap-colorpicker.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/nano.min.css"/>
<script src="//m.bubble.am/js/jquery.js"></script>
`);
core = core.replace(/(.*)(bootstrap\.min\.js)(.*)(\n.*){3}/gm, `
<script src="/js/bootstrap.min.js"></script>
<script src="//m.bubble.am/js/jquery.lazy.js"></script>
<script src="/js/jquery.ui.js"></script>
<script>
$("body").on('click', ".connect", function(event) {
const srv = this.dataset['srv'];
const id_split = srv.split('-');
const gamemode = ':' + id_split[2];
setGameModeTemporary(gamemode);
});
</script>
<script src="/js/bub.js?v=0.2.50"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-colorpicker/2.5.3/js/bootstrap-colorpicker.min.js" async></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/smooth-scrollbar/8.6.2/smooth-scrollbar.min.js" async></script>
<script src="https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js" async></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11" async></script>
`);
core = core.replace(/(d\.onkeydown\s=\sf)([^]*?b\s=\s\!1)([^]*?\}\;)/gm, `
d.split = function() {
ba();
K(17);
}
d.eject = function() {
ba();
K(21);
}
d.setCamera = function() {
K(18);
}
d.onkeydown = function(n) {
switch (n.keyCode) {
case 27: // quit
oa(true);
break;
case 13:
if (isTyping) {
isTyping = false;
document.getElementById("chat_textbox").blur();
chattxt = document.getElementById("chat_textbox").value;
if (chattxt.length > 0) sendChat(chattxt);
//document.getElementById("chat_textbox").value = "";
} else {
if (!hasOverlay) {
document.getElementById("chat_textbox").focus();
isTyping = true;
}
}
break;
case 17:
if (!hasOverlay
&& !isTyping
&& !window.plus.helpers.controls.splitSwitch
&& !window.plus.helpers.controls.holdSplitSwitch
&& !window.plus.helpers.controls.ejectSwitch) {
document.getElementById("chat_textbox").value = "/g ";
document.getElementById("chat_textbox").focus();
isTyping = true;
}
break;
}
};
`);
core = core.replace(/(\,\sI\:\s)(.*)/, `
$&
, counterCache: null
, motherCache: null
`);
core = core.replace(/(f.+)(v\[d\]\.)(.*)/, `
$&
if(window.plus.settings.checkbox.cellsCounter && h.length !== 0) {
f.restore();
let maxCells = '';
if(currentMode() == ':5') {
maxCells = '35';
} else {
maxCells = '16';
}
const playerCells = h.length;
const playerCellsText = new Ca(24, '#fff');
playerCellsText.u(playerCells + '/' + maxCells);
c = playerCellsText.F();
a = c.width;
f.globalAlpha = .2;
f.fillStyle = "#000000";
f.fillRect(10, 55, a + 10, 34);
f.globalAlpha = 1;
f.drawImage(c, 15, 60);
}
`);
core = core.replace(/(bb\s\?)(.*)/, `
if(bb) {
a.fillStyle = "#FFFFFF";
a.strokeStyle = "#AAAAAA";
if(this.f && window.plus.settings.checkbox.transparentVirus === true) a.globalAlpha = 0.2;
} else {
a.fillStyle = this.color;
a.strokeStyle = this.color;
if(this.f && window.plus.settings.checkbox.transparentVirus === true) a.globalAlpha = 0.2;
}
`);
core = core.replace(/(.*)(var\sa\s=\sl\s\/\sg)(.+)(\n.*\n.*)/gm, `
if(window.plus.settings.checkbox.noGrid === false) {
$&
}
`);
core = core.replace(/(.*)(m\.color)(.*)/, `
$&
const amAlive = h.length > 0;
const helpers = window.plus.helpers;
const settings = window.plus.settings;
if(amAlive && m.name === h[0].name && !m.f && settings.values.cellsColor !== undefined && settings.values.cellsColor !== null && settings.checkbox.customColor === true) {
m.color = settings.values.cellsColor;
}
if(amAlive && (h[0].gid !== 1 && h[0].gid !== 2)) {
helpers.skin.gid = h[0].gid;
}
if(settings.checkbox.customSkin === true && settings.values.skinUrl !== null && amAlive) {
for(let i = 0; i < h.length; i++) {
const cell = h[i];
if(cell.gid !== helpers.skin.skinInd) {
cell.gid = helpers.skin.skinInd;
if(cell.name === '') cell.name = 'enderror';
}
}
}
if(settings.checkbox.customSkin === false && amAlive && h[0].gid !== helpers.skin.gid) {
for(let i = 0; i < h.length; i++) {
const cell = h[i];
cell.gid = helpers.skin.gid;
}
}
`);
core = core.replace(/(.*)(\(c\s\&\&\sFb.*)(\n.*){35}/gm, `
const bypassSkin = (window.plus.settings.checkbox.customSkin && window.plus.settings.checkbox.bypassSkin)
const customSkinGid = (this.gid == 1 || this.gid == 2);
if (c && (Fb || bypassSkin) && !this.j && ':1' != V && ':3' != V && ':8' != V) {
var loadBub = -1;
if(Fb && this.gid && this.gid > 0) {
loadBub = this.gid;
} else if(!Fb && this.gid && customSkinGid) {
loadBub = this.gid;
} else if(Fb) {
if(-1 != Jb.indexOf(c)) {
loadBub = c;
}
}
if (loadBub != -1) {
if((Fb || bypassSkin && customSkinGid ) && (!$.hasOwnProperty(c) || $[c].skin != loadBub)) {
$[c] = new Image;
if(Fb && loadBub > 100000000) {
$[c].src = "//bubble.am/skins/custom/" + loadBub + '.png?0.2.50';
} else if(loadBub >= 10 && loadBub <= 20 && Fb) {
$[c].src = "//bubble.am/img/battle_" + loadBub + '.png?0.2.50';
} else if(customSkinGid && (Fb || bypassSkin)) {
$[c].src = window.plus.settings.values.skinUrl;
} else if(Fb) {
$[c].src = "//m.bubble.am/skins/" + loadBub + '.png?0.2.50';
}
$[c].skin = loadBub;
}
if((Fb || bypassSkin) && (0 != $[c].width && $[c].complete)) {
d = $[c];
} else {
d = null;
}
} else {
d = null;
}
} else {
d = null;
}
`);
core = core.replace(/(\,\sd\s\=\sthis\.I\,)(.*)/, `
$&
const settings = window.plus.settings;
const isNotSplittable = window.currentMode() == ':2'
|| window.currentMode() == ':5'
|| window.currentMode() == ':13';
const isBattle = window.currentMode() == ':11' || window.currentMode() == ':12';
const isTeams = window.currentMode() == ':3';
let nc;
if(settings.checkbox.virusSplitCounter && !isNotSplittable && this.f && this.color !== '#cd5564') {
if(this.counterCache === null) {
this.counterCache = new Ca(this.i(), '#FFFFFF', !0, "#000000");
}
nc = this.counterCache;
if(isTeams) {
switch(true) {
case this.size >= 123 && this.size <= 126:
nc.u('7');
break;
case this.size >= 126 && this.size <= 129:
nc.u('6');
break;
case this.size >= 129 && this.size <= 134:
nc.u('5');
break;
case this.size >= 134 && this.size <= 137:
nc.u('4');
break;
case this.size >= 137 && this.size <= 141:
nc.u('3');
break;
case this.size >= 142 && this.size <= 145:
nc.u('2');
break;
case this.size >= 145 && this.size <= 151:
nc.u('1');
break;
}
} else if(isBattle) {
switch(true) {
case this.size >= 84 && this.size <= 89:
nc.u('7');
break;
case this.size >= 89 && this.size <= 94:
nc.u('6');
break;
case this.size >= 94 && this.size <= 99:
nc.u('5');
break;
case this.size >= 99 && this.size <= 104:
nc.u('4');
break;
case this.size >= 104 && this.size <= 109:
nc.u('3');
break;
case this.size >= 109 && this.size <= 115:
nc.u('2');
break;
case this.size >= 115 && this.size <= 120:
nc.u('1');
break;
}
} else {
switch(true) {
case this.size >= 114 && this.size <= 119:
nc.u('7');
break;
case this.size >= 119 && this.size <= 123:
nc.u('6');
break;
case this.size >= 123 && this.size <= 127:
nc.u('5');
break;
case this.size >= 127 && this.size <= 132:
nc.u('4');
break;
case this.size >= 132 && this.size <= 136:
nc.u('3');
break;
case this.size >= 136 && this.size <= 141:
nc.u('2');
break;
case this.size >= 141 && this.size <= 145:
nc.u('1');
break;
}
}
nc.G(this.i());
nc.U(4);
const rn = nc.F();
a.drawImage(rn, ~~this.x - ~~(rn.width / 2), ~~this.y - ~~(rn.height / 2));
}
`);
core = core.replace(/(.*)(\~\~\(l\s\/\s2\)\,\sf\,\sl\)\;)/, `
const settings = window.plus.settings;
if(settings.checkbox.hideName && h.length > 0 && this.name === h[0].name) {
} else {
$&
}
`);
core = core.replace(/(.*)(chatNick\s\+)(.*)/, `
const settings = window.plus.settings;
const rawNick = chatNick.replace(/(<([^>]+)>)/ig, '').replace(/(\\[.*?\\])/, '').trim().toLowerCase();
if(!settings.chat.includes(rawNick)) {
$&
}
`);
core = core.replace(/(.*)(isUnlimitedZoom)(.*)(\n.*){7}/gm, ``);
core = core.replace(/(.*)(\&\&\sGb)(.*)(\n.*){3}/gm, `
if(0 < this.id && Gb && (d || (0 == h.length || Qd) && (!this.f || this.j) && 80 < this.size)) {
if(null == this.I) {
this.I = new Ca(this.i() / 2, "#FFFFFF", true, "#000000");
}
d = this.I;
d.G(this.i() / 2);
d.u(~~(this.size * this.size / 100));
c = Math.ceil(10 * g) / 10;
d.U(c);
e = d.F();
f = ~~(e.width / c);
l = ~~(e.height / c);
a.drawImage(e, ~~this.x - ~~(f / 2), b - ~~(l / 2), f, l);
}
if(0 < this.id && window.plus.settings.checkbox.showMotherMass && this.color === '#cd5564') {
if(this.motherCache == null) {
this.motherCache = new Ca(this.i() / 2, "#FFFFFF", true, "#000000");
}
d = this.motherCache;
d.G(this.i());
d.u(~~(this.size * this.size / 100));
c = Math.ceil(10 * g) / 10;
d.U(c);
e = d.F();
f = ~~(e.width / c);
l = ~~(e.height / c);
a.drawImage(e, ~~this.x - ~~(f / 2), b - ~~(l / 2), f, l);
}
`);
core = core.replace(/(J\.onmousedown)(.*)(\n.*){20}/gm, `
const helpers = window.plus.helpers;
J.onmousedown = function(a) {
if (db) {
var b = a.clientX - (5 + l / 5 / 2)
, c = a.clientY - (5 + l / 5 / 2);
if (Math.sqrt(b * b + c * c) <= l / 5 / 2) {
ba();
K(17);
return
}
}
if(!helpers.controls.isActiveMovement) {
ma = a.clientX;
na = a.clientY;
Ha();
ba()
}
};
J.onmousemove = function(a) {
if(!helpers.controls.isActiveMovement) {
ma = a.clientX;
na = a.clientY;
lastacti = Date.now();
}
};
d.goTo = function(x, y) {
ma = x;
na = y;
}
`);
core = core.replace(/(.*)\.setAcid(.*)/gm, `
d.setGameModeTemporary = function(a) {
currMode = a.substr(0, 3);
return currMode;
}
$&
`);
await sleep(300);
const additionalChanges = this.additionalCoreChanges;
if(additionalChanges.length !== 0) {
for(let i = 0; i < additionalChanges.length; i++) {
const additionalChange = additionalChanges[i];
const hook = additionalChange.hook;
const change = additionalChange.change;
core = core.replace(hook, change);
}
}
return core;
}
keydown = (e) => {
this.controlTypeDown(e);
}
keyup = (e) => {
this.controlTypeUp(e);
}
mousedown = (e) => {
this.controlTypeDown(e);
}
mouseup = (e) => {
this.controlTypeUp(e);
}
controlTypeDown(e) {
const settings = window.plus.settings;
const helpers = window.plus.helpers;
const key = e.which;
const controls = settings.controls;
if(controls.length === 0) return;
if(document.activeElement.nodeName == 'INPUT' && helpers.controls.splitSwitch === false && helpers.controls.ejectSwitch === false) return;
for(let i = 0; i < controls.length; i++) {
const control = controls[i];
if(control.keycode == key && !control.disabled) {
switch(control.type) {
case '1x':
if(!helpers.controls.splitSwitch) {
helpers.controls.splitSwitch = true;
window.split();
}
break;
case 'eject':
if(!helpers.controls.ejectSwitch) {;
helpers.controls.ejectSwitch = true;
window.eject();
}
break;
case 'setCamera':
window.setCamera();
break;
case '2x':
if(!helpers.controls.splitSwitch) {
helpers.controls.splitSwitch = true;
this.split(2);
}
break;
case '4x':
if(!helpers.controls.splitSwitch) {
helpers.controls.splitSwitch = true;
this.split(4);
}
break;
case '8x':
if(!helpers.controls.splitSwitch) {
helpers.controls.splitSwitch = true;
this.split(8);
}
break;
case '16x':
if(!helpers.controls.splitSwitch) {
helpers.controls.splitSwitch = true;
this.split(16);
}
break;
case 'holdSplit':
if(!helpers.controls.holdSplitSwitch) {
helpers.controls.holdSplitSwitch = true;
helpers.controls.splitInterval = setInterval(() => {
window.split();
}, 4);
}
break;
case 'holdEject':
if(helpers.controls.ejectSwitch) break;
helpers.controls.ejectSwitch = true;
helpers.controls.ejectInterval = setInterval(() => {
window.eject();
}, 4);
break;
case 'mouseLeft':
this.split(1);
break;
case 'mouseRight':
this.split(1);
break;
case 'mouseMiddle':
this.split(1);
break;
case 'movementUp':
this.goTo(3, -0);
if(!helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = true;
helpers.controls.activeMovement['up'] = true;
break;
case 'movementRight':
this.goTo(0, 5);
if(!helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = true;
helpers.controls.activeMovement['right'] = true;
break;
case 'movementDown':
this.goTo(2, 0.6);
if(!helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = true;
helpers.controls.activeMovement['down'] = true;
break;
case 'movementLeft':
this.goTo(-0, 8);
if(!helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = true;
helpers.controls.activeMovement['left'] = true;
break;
case 'hashLogin':
this.hashLogin();
break;
case 'hashShow':
this.hashShow();
break;
}
if(control.type.includes('movement')) {
const keys = helpers.controls.activeMovement;
switch(true) {
case keys['left'] && keys['up']:
this.goTo(-0, -0);
break;
case keys['left'] && keys["down"]:
this.goTo(-0, 0);
break;
case keys['up'] && keys['right']:
this.goTo(0, -0);
break;
case keys['down'] && keys['right']:
this.goTo(0, 0);
break;
}
}
}
}
}
controlTypeUp(e) {
const settings = window.plus.settings;
const helpers = window.plus.helpers;
const key = e.which;
const controls = settings.controls;
if(controls.length === 0) return;
if(document.activeElement.nodeName == 'INPUT' && helpers.controls.splitSwitch === false && helpers.controls.ejectSwitch === false) return;
for(let i = 0; i < controls.length; i++) {
const control = controls[i];
if(key == control.keycode && !control.disabled) {
switch(control.type) {
case '1x':
helpers.controls.splitSwitch = false;
break;
case '2x':
helpers.controls.splitSwitch = false;
break;
case '4x':
helpers.controls.splitSwitch = false;
break;
case '8x':
helpers.controls.splitSwitch = false;
break;
case '16x':
helpers.controls.splitSwitch = false;
break;
case 'holdSplit':
clearInterval(helpers.controls.splitInterval);
helpers.controls.holdSplitSwitch = false;
break;
case 'eject':
helpers.controls.ejectSwitch = false;
break;
case 'holdEject':
clearInterval(window.plus.helpers.controls.ejectInterval);
helpers.controls.ejectSwitch = false;
break;
case 'movementUp':
if(helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = false;
delete helpers.controls.activeMovement['up'];
break;
case 'movementRight':
if(helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = false;
delete helpers.controls.activeMovement['right'];
break;
case 'movementDown':
if(helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = false;
delete helpers.controls.activeMovement['down'];
break;
case 'movementLeft':
if(helpers.controls.isActiveMovement) helpers.controls.isActiveMovement = false;
delete helpers.controls.activeMovement['left'];
break;
}
}
}
}
split(times) {
for(let i = 0; i < times; i++) {
setTimeout(function() {
window.split();
}, 50 * i);
}
}
goTo(x, y) {
x = window.innerWidth / x; y = window.innerHeight / y;
window.goTo(x, y);
}
getCookie(name) {
let v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
return v ? v[2] : null;
}
setCookie(name, value, days) {
const d = new Date;
d.setTime(d.getTime() + 24*60*60*1000*days);
document.cookie = name + '=' + value + ';path=/;expires=' + d.toGMTString();
}
deleteCookie(name) {
this.setCookie(name, '', -1);
}
setHash(hash) {
this.deleteCookie('user_hash');
this.deleteCookie('PHPSESSID');
this.setCookie('user_hash', hash, 30);
window.location.reload();
}
async hashLogin() {
const { value: hash } = await Swal.fire({
title: 'Enter your hash',
input: 'text',
inputValue: '',
showCancelButton: true,
inputValidator: (value) => {
if(!value) {
return 'You need to write something!'
}
if(value.length !== 40) {
return 'The hash length must be 40 characters.'
}
}
})
if(hash) {
this.setHash(hash);
}
}
hashShow() {
const hash = this.getCookie('user_hash');
if(hash === null) {
return Swal.fire({
icon: 'error',
text: 'You must be logged in to your account to view its hash.'
});
}
Swal.fire({
icon: 'info',
html: `The hash assigned to this account is: <strong>${hash}</strong>.<br><br>Remember to never give it to anyone!`
});
}
addEventListeners() {
document.addEventListener('keydown', this.keydown);
document.addEventListener('keyup', this.keyup);
document.addEventListener('mousedown', this.mousedown);
document.addEventListener('mouseup', this.mouseup);
document.getElementById('canvas').addEventListener('contextmenu', function(e) {
e.preventDefault();
});
}
beforeLoad(callback) {
if(window.modLoaded === false) {
return callback();
}
throw new Error('Failed to load callback on beforeLoad method.');
}
onLoad(callback) {
let checkLoadedIndex = 0;
let checkLoaded = setInterval(() => {
if(window.modLoaded === true) {
clearInterval(checkLoaded);
window.modLoaded = true;
return callback();
}
if(checkLoadedIndex >= 100) {
clearInterval(checkLoaded);
throw new Error('Failed to load callback on onLoad method.');
}
checkLoadedIndex++
}, 100);
}
}
class UI {
constructor() {
this.loadCSS();
this.loadGUI();
this.loadSettings();
}
loadCSS() {
const css = `
#plusSettingsBtn {
float: right;
width: 12%;
}
#plusSettings .modal-body {
height: 300px;
user-select: none;
}
#plusSettings .modal-footer {
user-select: none;
}
#plusContent {
height: 100%;
}
#general-content {
display: flex;
flex-flow: column wrap;
height: 160px;
}
#plusContent .tab-pane {
height: 100%;
}
#plusTabs {
text-align: center;
border-bottom: none !important;
margin-top: 1em;
}
#plusTabs > li {
float: none !important;
display: inline-block;
}
#plusTabs>li.active>a, #plusTabs>li.active>a:hover, #plusTabs>li.active>a:focus {
border: none !important;
background: #efefef;
color: #27272A;
}
#plusTabs>li>a {
border: none !important;
border-radius: 4px;
color: #5f5f67;
}
#pLabel {
text-align: center;
}
.plus-checkbox {
padding-bottom: 0.5em;
width: 50%;
}
.plus-checkbox label {
max-width: 100%;
position: relative;
display: inline-block;
padding-left: 20px;
margin-bottom: 0;
font-weight: 400;
vertical-align: middle;
cursor: pointer;
}
.plus-checkbox input[type=radio], .plus-checkbox input[type=checkbox] {
position: absolute;
margin-left: -20px;
}
#skinPanel {
float: left;
margin-left: 5px;
}
.skinBtn {
margin: 0;
padding: 2px 8px;
}
.plusControls {
display: block;
height: 210px;
}
.plusBtn {
display: flex;
justify-content: space-between;
margin-top: 8px;
}
.plusBtn button {
width: 100px;
}
.plusHeader {
display: flex;
justify-content: center;
width: 100%;
}
.plusContent {
display: flex;
flex-flow: column nowrap;
height: 210px;
padding: 0;
margin: 0;
}
.plusContent li {
display: flex;
margin: 0 auto;
padding: 0.75rem;
width: 300px;
height: 34px;
align-items: center;
justify-content: space-between;
border-radius: 0.375rem;
background: #FAFAFA;
margin-top: 0.525rem;
}
.controlsContent {
display: flex;
flex-flow: column nowrap;
height: 210px;
padding: 0;
margin: 0;
}
.controlsContent li {
display: flex;
justify-content: space-around;
align-items: center;
margin-top: 5px;
}
.emptyPanel {
text-align: center;
padding: 0.5em;
margin: 0.5em;
}
.delete {
cursor: pointer;
}
html,
body {
height: 100%;
}
.main-panel {
margin: 0 2px;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 10%), 0 2px 4px -1px rgb(0 0 0 / 6%);
}
.friends-online {
border-radius: 10px;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 10%), 0 2px 4px -1px rgb(0 0 0 / 6%);
border: none;
}
#playBtn {
width: 74.8% !important;
}
#playBtn.has-spinner {
width: 62.9% !important;
}
#playBtn.btn-danger {
width: 75.5% !important;
}
#spectateBtn {
height: 34px;
}
table.chat-table {
margin-bottom: 34px !important;
}
.btn:focus, .btn:active:focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn.active.focus {
outline: none;
}
.btn-primary {
background-color: #3B82F6;
border: none;
}
.btn-primary:hover {
background-color: #2563EB;
}
.btn-primary:hover, .btn-primary:focus, .btn-primary.focus, .btn-primary:active, .btn-primary.active, .open>.dropdown-toggle.btn-primary {
background-color: #2563EB;
}
.btn-success {
background-color: #22C55E;
border: none;
}
.btn-success:hover {
background-color: #16A34A;
}
.btn-success:hover, .btn-success:focus, .btn-success.focus, .btn-success:active, .btn-success.active, .open>.dropdown-toggle.btn-success {
background-color: #16A34A;
}
.btn-settings {
float: none !important;
display: inline-block !important;
width: auto !important;
height: auto !important;
border: none;
background-color: #06B6D4;
}
.btn-settings:hover {
background-color: #0891B2;
border: none;
}
.btn-info:hover, .btn-info:focus, .btn-info.focus, .btn-info:active, .btn-info.active, .open>.dropdown-toggle.btn-info {
background-color: #0891B2;
}
.btn-warning {
background-color: #EAB308;
border: none;
}
.btn-warning:hover {
background-color: #CA8A04;
}
.btn-warning:hover, .btn-warning:focus, .btn-warning.focus, .btn-warning:active, .btn-warning.active, .open>.dropdown-toggle.btn-warning {
background-color: #CA8A04;
}
.btn-danger {
background-color: #F43F5E;
border: none;
}
.btn-danger:hover {
background-color: #E11D48;
}
.btn-danger:hover, .btn-danger:focus, .btn-danger.focus, .btn-danger:active, .btn-danger.active, .open>.dropdown-toggle.btn-danger {
background-color: #E11D48;
}
.form-control {
border: 1px solid #ced4da;
box-shadow: none;
}
#radio_mode .gm-s {
border: 2px solid #D1D5DB;
}
#chat_textbox {
border-radius: 0.3em;
}
.exp-bar {
border: 2px solid #3B82F6;
}
.exp-bar .progress-bar {
background-color: #60A5FA;
}
.modal-content {
border: none;
}
.swal2-popup {
font-size: 1.5rem !important;
}
.swal2-styled.swal2-confirm {
background-color: #3B82F6 !important;
}
.swal2-styled.swal2-confirm:focus {
box-shadow: 0 0 0 3px rgb(59 130 246 / 50%) !important;
}
.pickr {
float: left;
margin-left: 5px;
}
.pickr .pcr-button {
height: 1.8em;
width: 1.8em;
}
.pickr .pcr-button:after, .pickr .pcr-button:before {
border: 1px solid #80808060;
}
.hashLogin {
margin: 0 5px;
}
li {
list-style-type: none;
}
@media (min-width: 768px) {
.modal-content {
-webkit-box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);
box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);
}
}
body.dark-mode .plusColor {
background: #27272A;
}
body.dark-mode .plusColor .pcr-result {
background: #36363a;
color: #c6c6c8;
}
body.dark-mode .main-panel {
background: #18181B;
color: #c6c6c8;
}
body.dark-mode .form-control[disabled], body.dark-mode .form-control[readonly], body.dark-mode fieldset[disabled] .form-control {
background: #27272A;
}
body.dark-mode .form-control {
background: #27272A;
border: none;
color: #c6c6c8;
}
body.dark-mode #radio_mode .gm-s {
border: 2px solid #5f5f60;
background: #27272A;
}
body.dark-mode .bb-panel {
background: #18181B;
}
body.dark-mode .progress {
background: #27272A;
}
body.dark-mode .dropdown-menu>li>a {
color: #c6c6c8;
}
body.dark-mode .dropdown-menu {
background: #27272A;
}
body.dark-mode hr {
border-top: 1px solid #5f5f60;
}
body.dark-mode .table-striped>tbody>tr:nth-child(odd) {
background: #1f1f21 !important;
}
body.dark-mode .table-striped>tbody>tr:nth-child(even) {
background-color: #18181B !important;
}
body.dark-mode .table>thead>tr>th {
border-bottom: 2px solid #5f5f60;
}
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 {
border-top: none;
}
body.dark-mode .modal-content {
background: #18181B;
color: #c6c6c8;
}
body.dark-mode .modal-header {
border-bottom: 1px solid #5f5f60;
}
body.dark-mode .bub-table-list {
border: 1px solid #353636;
}
body.dark-mode .modal-footer {
border-top: 1px solid #5f5f60;
}
body.dark-mode .dropdown-menu>li>a:hover, body.dark-mode .dropdown-menu>li>a:focus {
background: #202023;
}
body.dark-mode .user-notif div.info {
background: #1f1f21;
}
body.dark-mode .user-notif div {
border-bottom: none;
}
body.dark-mode .user-notif div.warning {
background: none;
border-left: 1px solid #F59E0B;
}
body.dark-mode .guild-members2 {
border-bottom: 1px solid #5f5f60;
border-top: 1px solid #5f5f60;
}
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 {
color: #FDE047;
background: none;
}
body.dark-mode .nav-tabs {
border-bottom: none;
}
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 {
color: #c6c6c8;
background: #2a2a2d;
border: 1px solid transparent;
}
body.dark-mode .nav-tabs>li>a {
border-radius: 4px;
}
body.dark-mode .nav-tabs>li>a:hover {
border-color: transparent;
}
body.dark-mode .nav>li>a:hover, body.dark-mode .nav>li>a:focus {
background: #212123;
}
body.dark-mode .guild-members {
border-bottom: 1px solid #5f5f60;
}
body.dark-mode #tournament-modal .panel-heading {
color: #c6c6c8 !important;
border: none;
}
body.dark-mode .panel-default>.panel-heading {
background: #18181B;
}
body.dark-mode .panel {
background: #1f1f21;
}
body.dark-mode .panel-default {
border-color: transparent;
}
body.dark-mode #connecting > div {
background: #18181B !important;
color: #c6c6c8;
}
body.dark-mode #statsChartText, body.dark-mode #statsText {
color: #c6c6c8;
}
body.dark-mode #statsSubtext {
color: #a5a5a5;
}
body.dark-mode input.chat {
background: #18181B;
border: none;
}
body.dark-mode input:focus-visible {
outline: none;
color: #c6c6c8;
}
body.dark-mode .swal2-popup {
background: #18181B;
}
body.dark-mode .swal2-title {
color: #c6c6c8;
}
body.dark-mode .swal2-html-container {
color: #B4B4B5;
}
body.dark-mode .swal2-input-label {
color: #B4B4B5;
}
body.dark-mode .swal2-validation-message {
background: #27272A;
color: #c6c6c8;
}
body.dark-mode .scrollbar-track {
background: transparent;
}
body.dark-mode .scrollbar-track .show {
opacity: 0;
}
body.dark-mode .chatUsers li {
background: #27272A;
}
body.dark-mode .accounts li {
background: #27272A;
}
body.dark-mode #plusTabs>li.active>a, body.dark-mode #plusTabs>li.active>a:hover, body.dark-mode #plusTabs>li.active>a:focus {
border: none !important;
background: #27272A;
color: #c6c6c8;
}
body.dark-mode #plusTabs>li>a {
border: none !important;
border-radius: 4px;
color: #a2a2ad;
}
body.dark-mode .text-muted {
color: #a2a2a2;
}
body.dark-mode .close {
color: #c6c6c8;
text-shadow: none;
}
body.dark-mode .scrollbar-thumb {
background: #6B7280;
}
body.dark-mode ::-webkit-scrollbar {
width: 5px;
height: 5px;
}
body.dark-mode ::-webkit-scrollbar-button {
width: 0px;
height: 0px;
}
body.dark-mode ::-webkit-scrollbar-thumb {
background: #71717A;
border: 0px none #ffffff;
border-radius: 50px;
}
body.dark-mode ::-webkit-scrollbar-thumb:hover {
background: #52525B;
}
body.dark-mode ::-webkit-scrollbar-thumb:active {
background: #52525B;
}
body.dark-mode ::-webkit-scrollbar-track {
background: transparent;
border: 0px none #ffffff;
border-radius: 50px;
}
body.dark-mode ::-webkit-scrollbar-track:hover {
background: transparent;
}
body.dark-mode ::-webkit-scrollbar-track:active {
background: transparent;
}
body.dark-mode ::-webkit-scrollbar-corner {
background: transparent;
}
`;
const style = document.createElement('style');
style.textContent = css;
document.head.append(style);
}
loadGUI() {
$('#formStd h2').html('Bubble.am+').css({'display': 'inline-block', 'margin-right': '0.3em'});
$('#formStd h2').after(`<p style="display: inline-block; vertical-align: middle;">${window.plus.version}</p>`)
$('.btn-settings').after(`
<button id="plusSettingsBtn" onclick="return false;" class="btn btn-danger" data-toggle="modal" data-target=".bs-example-modal-lg">
<i class="fa fa-plus"></i>
</button>
`);
$('#chat_textbox').attr('maxlength', '99');
$('#overlays').before(`
<div id="plusSettings" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="pLabel">Bubble.am+ ${window.plus.version}</h4>
<ul id="plusTabs" class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#general" aria-controls="general" role="tab" data-toggle="tab">General</a></li>
<li role="presentation"><a href="#controls" aria-controls="controls" role="tab" data-toggle="tab">Controls</a></li>
<li role="presentation"><a href="#chat" aria-controls="chat" role="tab" data-toggle="tab">Chat</a></li>
<li role="presentation"><a href="#accounts" aria-controls="accounts" role="tab" data-toggle="tab">Accounts</a></li>
</ul>
</div>
<div class="modal-body">
<div id="plusContent" class="tab-content">
<div role="tabpanel" class="tab-pane active" id="general"></div>
<div role="tabpanel" class="tab-pane" id="controls"></div>
<div role="tabpanel" class="tab-pane" id="chat"></div>
<div role="tabpanel" class="tab-pane" id="accounts"></div>
</div>
</div>
<div class="modal-footer" style="text-align: center;">
<a href="http://enderror.pl" target="_blank">
<img id="enderror-logo" src="https://i.imgur.com/W7FkCgA.png" style="width: 3em;">
</a>
</div>
</div>
</div>
</div>
`);
const plusSettingsBtn = document.querySelector('#plusSettingsBtn');
plusSettingsBtn.addEventListener('click', function() {
$('#plusSettings').modal('toggle');
});
this.modsGUI();
this.controlsGUI();
this.chatGUI();
this.accountsGUI();
$('#plusSettings').on('shown.bs.modal', () => {
$(document).off('focusin.modal');
});
$('#plusSettings').on('hidden.bs.modal', () => {
const settings = Store.getSettings();
this.displayControls(settings.controls);
this.displayUsers(settings.chat);
this.displayAccounts(settings.accounts);
});
const pickr = new Pickr({
el: '#colorMod',
theme: 'nano',
container: 'body',
default: Store.getSettings().values.cellsColor,
position: 'right-start',
appClass: 'plusColor',
swatches: null,
components: {
preview: true,
opacity: true,
hue: true,
interaction: {
hex: false,
rgba: false,
hsla: false,
hsva: false,
cmyk: false,
input: true,
clear: false,
save: true
}
}
});
pickr.on('save', (color, instance) => {
window.plus.settings.values.cellsColor = color.toRGBA().toString(3);
Store.setSettings();
});
window.Scrollbars = Scrollbar.initAll();
}
modsGUI() {
$('#general').append(`
<div id="general-content">
<div class="plus-checkbox">
<label>
<input id="plus_transparentVirus" type="checkbox" onchange="setTransparentVirus($(this).is(':checked'));"> Transparent virus
</label>
</div>
<div class="plus-checkbox">
<label>
<input id="plus_noGrid" type="checkbox" onchange="setNoGrid($(this).is(':checked'));"> No grid
</label>
</div>
<div class="plus-checkbox" style="display: flex; align-items: center;">
<label style="float: left;">
<input id="plus_customColor" type="checkbox" onchange="setCustomColor($(this).is(':checked'));"> Custom cells color
</label>
<div id="colorMod" style="float: left;"></div>
</div>
<div class="plus-checkbox" style="display: flex; align-items: center;">
<label style="float: left;">
<input id="plus_customSkin" type="checkbox" onchange="setCustomSkin($(this).is(':checked'));"> Custom skin
</label>
<div id="skinPanel">
<button id="setSkinBtn" class="btn btn-success skinBtn"><i class="fa fa-plus"></i></button>
<button id="showSkinBtn" class="btn btn-primary skinBtn"><i class="fa fa-eye"></i></button>
</div>
</div>
<div class="plus-checkbox">
<label>
<input id="plus_bypassSkin" type="checkbox" onchange="setBypassSkin($(this).is(':checked'));"> Bypass "No skins"
</label>
</div>
<div class="plus-checkbox">
<label>
<input id="plus_darkMenu" type="checkbox" onchange="setDarkMenu($(this).is(':checked'));"> Dark theme menu
</label>
</div>
<div class="plus-checkbox">
<label>
<input id="plus_virusSplitCounter" type="checkbox" onchange="setVirusSplitCounter($(this).is(':checked'));"> Virus split counter
</label>
</div>
<div class="plus-checkbox">
<label>
<input id="plus_cellsCounter" type="checkbox" onchange="setCellsCounter($(this).is(':checked'));"> Cells counter
</label>
</div>
<div class="plus-checkbox">
<label>
<input id="plus_hideName" type="checkbox" onchange="setHideName($(this).is(':checked'));"> Hide your name
</label>
</div>
<div class="plus-checkbox">
<label>
<input id="plus_showMotherMass" type="checkbox" onchange="setShowMotherMass($(this).is(':checked'));"> Show mother mass
</label>
</div>
</div>
`);
document.querySelector('#setSkinBtn').addEventListener('click', async () => {
const { value: skinValue } = await Swal.fire({
title: 'Enter skin url (png, jpg or jpeg)',
input: 'text',
showCancelButton: true,
inputValidator: (value) => {
if(!value) {
return 'You need to write something.'
}
if(!this.isUriImage(value)) {
return 'Your skin url is wrong.'
}
}
});
if(skinValue) {
Swal.fire({
title: 'Skin has been successfully set.',
imageUrl: skinValue,
imageAlt: 'Skin',
});
const settings = window.plus.settings;
const helpers = window.plus.helpers;
settings.values.skinUrl = skinValue;
helpers.skin.skinInd == 1 ? helpers.skin.skinInd++ : helpers.skin.skinInd--;
Store.setSettings();
}
});
document.querySelector('#showSkinBtn').addEventListener('click', async function() {
const settings = window.plus.settings;
if(typeof(settings.values.skinUrl) === 'string') {
Swal.fire({
title: 'Current skin',
imageUrl: settings.values.skinUrl,
imageAlt: 'Skin',
});
} else {
Swal.fire({
title: 'Current skin',
text: 'No skin has been set yet.',
});
}
});
}
controlsGUI() {
$('#controls').append(`
<div class="plusHeader" style="text-align: center; justify-content: space-around;">
<strong style="width: 240px;">Type</strong>
<strong style="width: 200px; padding-left: 10px;">Key</strong>
<strong style="width: 60px;">Disabled</strong>
<strong style="width: 40px;"></strong>
</div>
<div class="plusContent-wrapper" data-scrollbar>
<ul class="controlsContent controls"></ul>
</div>
<div class="plusBtn">
<button class="btn btn-warning controlsDefault">Load default</button>
<button class="btn btn-danger controlsDelete">Delete all</button>
<button class="btn btn-success controlsNew">Add new</button>
<button class="btn btn-primary controlsSave">Save</button>
</div>
`);
$('.controls').on('click', (e) => {
if(!e.target.className.includes('delete')) return;
const controlsLength = $('.controls li').length;
const hasEmpty = $('.controls').find('.emptyPanel').length === 1;
if(controlsLength === 1) {
this.displayEmpty('.controls', 'controls');
}
if(controlsLength !== 1 && hasEmpty) {
$('.controls .emptyPanel').remove();
}
e.target.parentElement.remove();
});
$('.controlsDefault').on('click', () => {
const defaultSettings = window.plus.defaultSettings;
$('.plusControls tbody').html('');
this.displayControls(defaultSettings.controls);
});
$('.controlsDelete').on('click', () => {
this.displayEmpty('.controls', 'controls');
});
$('.controlsNew').on('click', () => {
this.addControl();
});
$('.controlsSave').on('click', () => {
const newControls = [];
const actualControls = document.querySelectorAll('.controls li');
const settings = window.plus.settings;
for(let i = 0; i < actualControls.length; i++) {
const li = actualControls[i];
const select = li.querySelectorAll('select');
const checkbox = li.querySelector('input');
const controlType = select[0].value;
const controlKey = select[1].value;
const controlDisabled = checkbox.checked;
newControls.push({
type: controlType,
keycode: controlKey,
disabled: controlDisabled
});
}
settings.controls = newControls;
Store.setSettings();
Swal.fire({
icon: 'success',
title: 'Success!',
text: 'Your new controls configuration has been successfully saved.'
});
});
}
addControl() {
const controlsTemplate = `
<li>
<select class="form-control" style="border-radius:4px; width: 15em;">
<option value="1x" selected="selected">Split</option>
<option value="eject">Eject</option>
<option value="setCamera">Lock/unlock camera [spectate]</option>
<option value="2x">2x split</option>
<option value="4x">4x split</option>
<option value="8x">8x split</option>
<option value="16x">16x split</option>
<option value="holdSplit">Hold split</option>
<option value="holdEject">Hold eject</option>
<option value="movementUp">Movement up</option>
<option value="movementRight">Movement right</option>
<option value="movementDown">Movement down</option>
<option value="movementLeft">Movement left</option>
<option value="hashLogin">Login by hash</option>
<option value="hashShow">Show hash</option>
</select>
<select class="form-control" style="border-radius:4px; width: 12em;">
<option value="1">Left Click</option>
<option value="2">Scroll Click</option>
<option value="3">Right Click</option>
<option value="9">Tab</option>
<option value="12">Clear</option>
<option value="13">Enter</option>
<option value="16">Shift</option>
<option value="17">Ctrl</option>
<option value="18">Alt</option>
<option value="27">Esc</option>
<option value="32" selected="selected">Space</option>
<option value="33">Page Up</option>
<option value="34">Page Down</option>
<option value="35">End</option>
<option value="36">Home</option>
<option value="37">Left Arrow</option>
<option value="38">Up Arrow</option>
<option value="39">Right Arrow</option>
<option value="40">Down Arrow</option>
<option value="45">Insert</option>
<option value="46">Delete</option>
<option value="48">0</option>
<option value="49">1</option>
<option value="50">2</option>
<option value="51">3</option>
<option value="52">4</option>
<option value="53">5</option>
<option value="54">6</option>
<option value="55">7</option>
<option value="56">8</option>
<option value="57">9</option>
<option value="65">A</option>
<option value="66">B</option>
<option value="67">C</option>
<option value="68">D</option>
<option value="69">E</option>
<option value="70">F</option>
<option value="71">G</option>
<option value="72">H</option>
<option value="73">I</option>
<option value="74">J</option>
<option value="75">K</option>
<option value="76">L</option>
<option value="77">M</option>
<option value="78">N</option>
<option value="79">O</option>
<option value="80">P</option>
<option value="81">Q</option>
<option value="82">R</option>
<option value="83">S</option>
<option value="84">T</option>
<option value="85">U</option>
<option value="86">V</option>
<option value="87">W</option>
<option value="88">X</option>
<option value="89">Y</option>
<option value="90">Z</option>
<option value="96">Numpad 0</option>
<option value="97">Numpad 1</option>
<option value="98">Numpad 2</option>
<option value="99">Numpad 3</option>
<option value="100">Numpad 4</option>
<option value="101">Numpad 5</option>
<option value="102">Numpad 6</option>
<option value="103">Numpad 7</option>
<option value="104">Numpad 8</option>
<option value="105">Numpad 9</option>
<option value="106">Numpad *</option>
<option value="107">Numpad +</option>
<option value="109">Numpad -</option>
<option value="110">Numpad .</option>
<option value="111">Numpad /</option>
<option value="112">F1</option>
<option value="113">F2</option>
<option value="114">F3</option>
<option value="115">F4</option>
<option value="116">F5</option>
<option value="117">F6</option>
<option value="118">F7</option>
<option value="119">F8</option>
<option value="120">F9</option>
<option value="121">F10</option>
<option value="122">F11</option>
<option value="123">F12</option>
<option value="124">F13</option>
<option value="125">F14</option>
<option value="126">F15</option>
<option value="127">F16</option>
<option value="128">F17</option>
<option value="129">F18</option>
<option value="130">F19</option>
<option value="131">F20</option>
<option value="132">F21</option>
<option value="133">F22</option>
<option value="134">F23</option>
<option value="135">F24</option>
<option value="186">;</option>
<option value="187">=</option>
<option value="188">,</option>
<option value="189">-</option>
<option value="190">.</option>
<option value="191">/</option>
<option value="192">`</option>
<option value="219">[</option>
<option value="220">\</option>
<option value="221">]</option>
<option value="222">'</option>
</select>
<input type="checkbox" style="margin: 0;">
<i class="fa fa-remove delete"></i>
</li>
`;
const hasEmpty = $('.controls').find('.emptyPanel').length === 1;
if(hasEmpty) {
$('.controls .emptyPanel').remove();
}
$('.controls').append(controlsTemplate);
this.fixScrollbar(window.Scrollbars[0]);
}
displayControls(settings) {
if(settings.length === 0) {
return this.displayEmpty('.controls', 'controls');
};
$('.controls').html('');
for(let i = 0; i < settings.length; i++) {
const setting = settings[i];
this.addControl();
const li = document.querySelectorAll('.controls li')[i];
const select = li.querySelectorAll('select');
const checkbox = li.querySelector('input');
select[0].value = setting.type;
select[1].value = setting.keycode;
checkbox.checked = setting.disabled;
}
}
chatGUI() {
$('#chat').append(`
<div class="plusHeader">
<strong>Blocked users</strong>
</div>
<div class="plusContent-wrapper" data-scrollbar>
<ul class="plusContent chatUsers"></ul>
</div>
<div class="plusBtn">
<button class="btn btn-danger chatDelete">Delete all</button>
<button class="btn btn-success chatNew">Add new</button>
<button class="btn btn-primary chatSave">Save</button>
</div>
`);
$('.chatUsers').on('click', (e) => {
if(!e.target.className.includes('delete')) return;
const chatLength = $('.chatUsers li').length;
const hasEmpty = $('.chatUsers').find('.emptyPanel').length === 1;
if(chatLength === 1) {
this.displayEmpty('.chatUsers', 'users');
}
if(chatLength !== 1 && hasEmpty) {
$('.chatUsers .emptyPanel').remove();
}
e.target.parentElement.remove();
});
$('.chatDelete').on('click', () => {
this.displayEmpty('.chatUsers', 'users');
});
$('.chatNew').on('click', async () => {
let { value: playerNick } = await Swal.fire({
title: 'Player nickname',
text: 'You don\'t need to specify a clan tag.',
input: 'text',
inputValue: '',
showCancelButton: true,
inputValidator: (value) => {
if(!value) {
return 'You need to write something!'
}
if(value.length > 15) {
return 'Player nickname cannot be longer than 15 characters.'
}
const currentUsers = this.getCurrentUsers();
if(currentUsers.includes(value.toLowerCase())) {
return 'This user is already on the list.';
}
}
});
if(playerNick) {
this.addUser(playerNick);
}
});
$('.chatSave').on('click', () => {
const newChat = [];
const actualChat = document.querySelectorAll('.chatUsers li');
const settings = window.plus.settings;
for(let i = 0; i < actualChat.length; i++) {
const li = actualChat[i];
const playerNick = li.querySelectorAll('span')[0].innerHTML;
newChat.push(playerNick.toLowerCase());
}
settings.chat = newChat;
Store.setSettings();
Swal.fire({
icon: 'success',
title: 'Success!',
text: 'Your new chat configuration has been successfully saved.'
});
});
}
addUser(name) {
const hasEmpty = $('.chatUsers').find('.emptyPanel').length === 1;
if(hasEmpty) {
$('.chatUsers .emptyPanel').remove();
}
$('.chatUsers').append(`
<li>
<span>${name.toLowerCase()}</span>
<i class="fa fa-remove delete"></i>
</li>
`);
this.fixScrollbar(window.Scrollbars[1]);
}
getCurrentUsers() {
const currentUsers = document.querySelectorAll('.chatUsers li');
const nicks = [];
for(let i = 0; i < currentUsers.length; i++) {
const user = currentUsers[i];
const nick = user.querySelectorAll('span')[0].innerHTML.toLowerCase();
nicks.push(nick);
}
return nicks;
}
displayUsers(users) {
if(users.length === 0) {
return this.displayEmpty('.chatUsers', 'users');
}
$('.chatUsers').html('');
for(let i = 0; i < users.length; i++) {
const playerNick = users[i];
this.addUser(playerNick);
}
}
accountsGUI() {
$('#accounts').append(`
<div class="plusHeader">
<strong>Your accounts</strong>
</div>
<div class="plusContent-wrapper" data-scrollbar>
<ul class="plusContent accounts"></ul>
</div>
<div class="plusBtn">
<button class="btn btn-danger accountsDelete">Delete all</button>
<button class="btn btn-success accountsNew">Add new</button>
<button class="btn btn-primary accountsSave">Save</button>
</div>
`);
$('.accounts').on('click', (e) => {
if(!e.target.className.includes('delete')) return;
const accountsLength = $('.accounts li').length;
const hasEmpty = $('.accounts').find('.emptyPanel').length === 1;
if(accountsLength === 1) {
this.displayEmpty('.accounts', 'accounts');
}
if(accountsLength !== 1 && hasEmpty) {
$('.accounts .emptyPanel').remove();
}
e.target.parentElement.remove();
});
$('.accountsDelete').on('click', () => {
this.displayEmpty('.accounts', 'accounts');
});
$('.accountsNew').on('click', async () => {
const {value: account} = await Swal.fire({
title: 'Add account',
html: `
<input id="name" class="swal2-input" placeholder="Name" maxlength="15">
<input id="hash" class="swal2-input" placeholder="Hash" maxlength="40">
`,
focusConfirm: false,
preConfirm: () => {
const name = document.getElementById('name').value.trim();
const hash = document.getElementById('hash').value.trim();
const currentAccounts = this.getCurrentAccounts();
const currentNames = currentAccounts.names;
const currentHashes = currentAccounts.hashes;
if(name.length === 0 || hash.length === 0) {
return Swal.showValidationMessage('You must complete all fields.');
}
if(hash.length !== 40) {
return Swal.showValidationMessage('The hash length must be 40 characters.');
}
if(currentNames.includes(name.toLowerCase()) || currentHashes.includes(hash)) {
return Swal.showValidationMessage('The name or hash is already on the list.');
}
return {
name,
hash
}
}
});
if(account) {
this.addAccount(account.name, account.hash);
}
});
$('.accountsSave').on('click', () => {
const newAccounts = [];
const actualAccounts = document.querySelectorAll('.accounts li');
const settings = window.plus.settings;
for(let i = 0; i < actualAccounts.length; i++) {
const li = actualAccounts[i];
const accountName = li.querySelectorAll('span')[0].innerHTML;
const accountHash = li.dataset.hash;
newAccounts.push({
name: accountName,
hash: accountHash
});
}
settings.accounts = newAccounts;
Store.setSettings();
Swal.fire({
icon: 'success',
title: 'Success!',
text: 'Your new accounts configuration has been successfully saved.'
});
});
}
addAccount(name, hash) {
const hasEmpty = $('.accounts').find('.emptyPanel').length === 1;
if(hasEmpty) {
$('.accounts .emptyPanel').remove();
}
$('.accounts').append(`
<li data-hash="${hash}">
<span>${name}</span>
<div>
<span class="hashLogin" style="cursor: pointer;" onclick="window.plus.setHash($(this).parent().parent().data('hash'));">
<i class="fa fa-arrow-right"></i>
</span>
<span class="delete" style="cursor: pointer;" onclick="$(this).parent().parent().remove();">
<i class="fa fa-remove"></i>
</span>
</div>
</li>
`);
this.fixScrollbar(window.Scrollbars[2]);
}
displayAccounts(accounts) {
if(accounts.length === 0) {
return this.displayEmpty('.accounts', 'accounts');
}
$('.accounts').html('');
for(const account in accounts) {
const { name, hash } = accounts[account];
this.addAccount(name, hash);
}
}
getCurrentAccounts() {
const currentAccounts = document.querySelectorAll('.accounts li');
const accounts = [];
const names = [];
const hashes = [];
for(let i = 0; i < currentAccounts.length; i++) {
const account = currentAccounts[i];
const name = account.querySelectorAll('span')[0].innerHTML.toLowerCase();
const hash = account.dataset.hash;
accounts.push({
name,
hash
});
names.push(name);
hashes.push(hash);
}
return {
accounts,
names,
hashes
}
}
loadSettings() {
const settings = Store.getSettings();
window.plus.settings = settings;
for(const checkbox in settings.checkbox) {
const name = checkbox;
const value = settings.checkbox[checkbox];
$(`#plus_${name}`).prop('checked', value).change();
}
this.displayControls(settings.controls);
this.displayUsers(settings.chat);
this.displayAccounts(settings.accounts);
}
displayEmpty(element, text) {
$(element).html(`<div class="emptyPanel">There are currently no ${text} in this panel.</div>`);
}
fixScrollbar(scrollbar) {
scrollbar.update();
const limitY = scrollbar.limit.y;
scrollbar.setPosition(0, limitY);
}
isUriImage = function(uri) {
uri = uri.split('?')[0];
const parts = uri.split('.');
const extension = parts[parts.length - 1];
const imageTypes = ['png', 'jpg', 'jpeg'];
if(imageTypes.indexOf(extension) !== -1) {
return true;
}
}
}
class Store {
static getSettings() {
let settings;
if(localStorage.getItem('plus_settings') === null) {
this.setSettings();
}
settings = JSON.parse(localStorage.getItem('plus_settings'));
return settings;
}
static setSettings() {
localStorage.setItem('plus_settings', JSON.stringify(window.plus.settings));
}
}
window.modLoaded = false;
window.sleep = function(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
window.setCustomColor = function(a) {
window.plus.settings.checkbox.customColor = a;
Store.setSettings();
}
window.setTransparentVirus = function(a) {
window.plus.settings.checkbox.transparentVirus = a;
Store.setSettings();
}
window.setCustomSkin = function(a) {
window.plus.settings.checkbox.customSkin = a;
Store.setSettings();
}
window.setBypassSkin = function(a) {
window.plus.settings.checkbox.bypassSkin = a;
Store.setSettings();
}
window.setNoGrid = function(a) {
window.plus.settings.checkbox.noGrid = a;
Store.setSettings();
}
window.setDarkMenu = function(a) {
window.plus.settings.checkbox.darkMenu = a;
if(a === true) {
$('body').addClass('dark-mode');
$('#enderror-logo').attr('src', 'https://i.imgur.com/ewMCLSe.png');
} else {
$('body').removeClass('dark-mode')
$('#enderror-logo').attr('src', 'https://i.imgur.com/W7FkCgA.png');
}
Store.setSettings();
}
window.setVirusSplitCounter = function(a) {
window.plus.settings.checkbox.virusSplitCounter = a;
Store.setSettings();
}
window.setCellsCounter = function(a) {
window.plus.settings.checkbox.cellsCounter = a;
Store.setSettings();
}
window.setHideName = function(a) {
window.plus.settings.checkbox.hideName = a;
Store.setSettings();
}
window.setShowMotherMass = function(a) {
window.plus.settings.checkbox.showMotherMass = a;
Store.setSettings();
}
window.plus = new Plus();