// ==UserScript==
// @name Bonk Playlists
// @version 6.0
// @author Salama
// @description Adds map playlists to bonk.io
// @match https://bonk.io/gameframe-release.html
// @run-at document-start
// @grant none
// @supportURL https://discord.gg/Dj6usq7ww3
// @namespace https://greasyfork.org/users/824888
// ==/UserScript==
// for use as a userscript ensure you have Excigma's code injector userscript
// https://greasyfork.org/en/scripts/433861-code-injector-bonk-io
let injector = (str) => {
let newStr = str;
window.playlists = {};
window.playlists.edit = false;
window.playlists.autofav = false;
window.playlists.editing = false;
window.playlists.categoryFunc = ()=>{};
window.playlists.mapLoader = ()=>{};
window.playlists.menuFunctions = {};
window.playlists.toolFunctions = {};
window.playlists.setMapsLoaded = ()=>{};
window.playlists.setMapsLoadFinished = ()=>{};
window.playlists.bigClass = {};
let token = null;
window.playlists.merge = {
enabled: false,
from: {
element: null,
index: null
},
to: {
element: null,
index: null
}
};
let dropdownOption = document.createElement('div');
let playlistsButton = document.createElement('div');
let toolbox = document.createElement('div');
//Insert before favs
document.getElementById("maploadtypedropdown").insertBefore(dropdownOption, document.getElementById("maploadtypedropdownoption1"));
// Monitor style changes
const dropdownObserver = new MutationObserver(() => {
document.getElementById("maploadtypedropdownoptionplaylists").style.display = document.getElementById("maploadtypedropdownoption1").style.display;
document.getElementById("maploadtypedropdownoptionplaylists").onclick = () => {
document.getElementById("maploadtypedropdowntitle").innerHTML = "MY PLAYLISTS";
window.playlists.categoryFunc("blank", true);
window.playlists.categoryFunc("playlists", true);
document.getElementById("maploadwindowsearchoptions").style.visibility = "hidden";
document.getElementById("maploadwindowhotnessslider").style.visibility = "hidden";
document.getElementById("maploadwindowsearchinput").style.visibility = "hidden";
document.getElementById("maploadwindowsearchbutton").style.visibility = "hidden";
document.getElementById("maploadwindowtoolbox").style.display = "flex";
getPlaylists();
};
if(document.getElementById("maploadtypedropdowntitle").innerHTML === "MY PLAYLISTS") {
document.getElementById("maploadwindowplaylistbackbutton").style.display = "block";
document.getElementById("maploadwindowtoolbox").style.display = "flex";
document.getElementById("maploadwindowmapscontainer").style.bottom = "28px";
document.getElementById("maploadwindowmapscontainer").style.height = "calc(100% - 108px - 23px)";
document.getElementById("maploadwindowsearchinput").style.visibility = "hidden";
document.getElementById("maploadwindowsearchbutton").style.visibility = "hidden";
}
else {
// Might conflict with future mods
document.getElementById("maploadwindowplaylistbackbutton").style.display = "none";
document.getElementById("maploadwindowtoolbox").style.display = "none";
document.getElementById("maploadwindowmapscontainer").style.removeProperty("bottom");
document.getElementById("maploadwindowmapscontainer").style.removeProperty("height");
document.getElementById("maploadwindowsearchinput").style.removeProperty("visibility");
document.getElementById("maploadwindowsearchbutton").style.removeProperty("visibility");
if(window.playlists.edit) {
document.getElementById("maploadwindowplaylistedit").click();
}
if(window.playlists.merge.enabled) {
document.getElementById("maploadwindowplaylistmerge").click();
}
}
document.getElementById("maploadwindowplaylistbackbutton").onclick = document.getElementById("maploadtypedropdownoptionplaylists").onclick;
document.getElementById("maploadtypedropdownoptionplaylists").onmouseenter = document.getElementById("maploadtypedropdownoption1").onmouseenter;
document.getElementById("maploadtypedropdownoptionplaylists").onmouseleave = document.getElementById("maploadtypedropdownoption1").onmouseleave;
document.getElementById("maploadtypedropdownoptionplaylists").onmousedown = document.getElementById("maploadtypedropdownoption1").onmousedown;
document.getElementById("maploadwindowplaylistbackbutton").onmouseenter = document.getElementById("maploadtypedropdownoption1").onmouseenter;
document.getElementById("maploadwindowplaylistbackbutton").onmouseleave = document.getElementById("maploadtypedropdownoption1").onmouseleave;
document.getElementById("maploadwindowplaylistbackbutton").onmousedown = document.getElementById("maploadtypedropdownoption1").onmousedown;
});
chatObserver = new MutationObserver(e => {
for(let mutation of e) {
if(mutation.type == "childList") {
for(let node of mutation.addedNodes) {
if(node.textContent === "* Accepted commands are listed above ") {
let helpmsg = document.createElement("div");
mutation.target.insertBefore(helpmsg, node.previousSibling);
helpmsg.outerHTML = '<div><span class="newbonklobby_chat_status" style="color: rgb(181, 48, 48);">/p - commands from playlists mod</span></div>';
}
}
}
}
});
chatObserver.observe(document.getElementById("newbonklobby_chat_content"), {attributes: false, childList: true, subtree: false});
dropdownObserver.observe(document.getElementById("maploadtypedropdownoption1"), {attributes: true, childList: false, subtree: true});
document.getElementById("maploadwindow").appendChild(playlistsButton);
document.getElementById("maploadwindow").appendChild(toolbox);
toolbox.outerHTML = `<div id="maploadwindowtoolbox" style="width: 100%;height: 23px;bottom: 0;position: absolute;background-color: inherit;z-index: 1;display: none;padding: 5px;">
<div class="brownButton brownButton_classic buttonShadow" id="maploadwindowplaylistedit" style="line-height: 23px; height: 23px; width: 75px; margin-right: 5px; display: block;">EDIT</div>
<div class="brownButton brownButton_classic buttonShadow" id="maploadwindowplaylistmerge" style="line-height: 23px; height: 23px; width: 75px; margin-right: 5px; display: block;">MERGE</div>
<div class="brownButton brownButton_classic buttonShadow" id="maploadwindowplaylistimport" style="line-height: 23px; height: 23px; width: 75px; margin-right: 5px; display: block;">IMPORT</div>
<div class="brownButton brownButton_classic buttonShadow" id="maploadwindowplaylistexport" style="line-height: 23px; height: 23px; width: 75px; margin-right: 5px; display: block;">EXPORT</div>
<div class="brownButton brownButton_classic buttonShadow" id="maploadwindowplaylistautofav" style="line-height: 23px; height: 23px; width: 75px; margin-right: 5px; display: block;">AUTOFAV</div>
<div class="brownButton brownButton_classic buttonShadow" id="maploadwindowplaylistdeleteall" style="line-height: 23px; height: 23px; width: 125px; margin-right: 5px; display: none;">DELETE ALL</div>
</div>`;
dropdownOption.outerHTML = `<div class="dropdown-option dropdown_classic" id="maploadtypedropdownoptionplaylists" style="display: none;">MY PLAYLISTS</div>`;
playlistsButton.outerHTML = `<div class="brownButton brownButton_classic buttonShadow" id="maploadwindowplaylistbackbutton" style="position: absolute; left: 210px; line-height: 23px; height: 23px; width: 75px; top: 57px; display: block;">BACK</div>`;
let dbRequest = indexedDB.open("salamaStorage", 1);
let db;
window.playlists.playlists = [];
window.playlists.savePlaylists = playlists => {
try {
let transaction = db.transaction("playlists", "readwrite");
transaction.objectStore("playlists").put(playlists, 1);
}
catch(e) {
console.log("Couln't save playlists to db: ", e)
}
}
dbRequest.onsuccess = e => {
db = e.target.result;
let transaction = db.transaction("playlists");
let getRequest = transaction.objectStore("playlists").get(1);
getRequest.onsuccess = e => {
window.playlists.playlists = e.target.result;
}
}
dbRequest.onupgradeneeded = e => {
db = e.target.result;
let objectStore = db.createObjectStore("playlists");
objectStore.put(JSON.parse(localStorage.playlists || "[]"), 1);
delete localStorage.playlists;
}
window.playlists.setToken = t => {
token = t;
}
//This is mainly meant to prevent you from accidentally importing the wrong file
const validatePlaylists = playlists => {
try {
let newPlaylists = JSON.parse(playlists);
for(let playlist of newPlaylists) {
if(!(
Object.keys(playlist).find(i => !["name","description","image","maps","b1maps"].includes(i)) === undefined &&
typeof(playlist.name) == "string" &&
typeof(playlist.description) == "string" &&
((typeof(playlist.image) == "string" &&
playlist.image.substr(0, 5) == "data:") ||
playlist.image == undefined) &&
playlist.maps.filter(e => {return typeof(e)=="number"}).length == playlist.maps.length
))
return false;
}
return true;
}
catch {
return false;
}
}
document.getElementById("maploadwindowplaylistexport").addEventListener("click", () => {
let a = document.createElement("a");
document.body.appendChild(a);
a.href = URL.createObjectURL(new Blob([JSON.stringify(window.playlists.playlists)], {type: "oclet/stream"}));
a.download = "playlists.txt";
a.click();
document.body.removeChild(a);
})
document.getElementById("maploadwindowplaylistimport").addEventListener("click", () => {
let a = document.createElement("input");
a.type = 'file';
document.body.appendChild(a);
a.onchange = e => {
let file = e.target.files[0];
let reader = new FileReader();
reader.readAsText(file);
reader.onload = readerEvent => {
let newPlaylists = readerEvent.target.result;
if(validatePlaylists(newPlaylists)) {
window.playlists.playlists = window.playlists.playlists.concat(JSON.parse(newPlaylists));
window.playlists.savePlaylists(window.playlists.playlists);
document.getElementById("maploadwindowplaylistautofav").click();
}
}
};
a.click();
document.body.removeChild(a);
})
document.getElementById("maploadwindowplaylistautofav").addEventListener("click", async () => {
let popup = document.createElement('div');
document.getElementById("maploadwindow").appendChild(popup);
popup.outerHTML = `
<div id="maploadwindowplaylistautofavpopup" style="width: calc(100% - 200px);background-color: inherit;z-index: 1;position: absolute;left: 100px;top: 50px;height: calc(100% - 100px);filter: brightness(1.1);border-radius: 7px;">
<div class="windowTopBar_classic" style="height: 40px;border-top-left-radius: 3px;border-top-right-radius: 3px;font-family: futurept_b1;line-height: 40px;color: #f9f9f9;font-size: 28px;text-align: center;">
Autofav
<div id="maploadwindowplaylistautofavprogress" style="height: 40px;font-family: monospace;line-height: 40px;color: #f9f9f9;font-size: 15px;text-align: center;right: 10px;position: absolute;top: 0px;"></div>
</div>
<div class="newbonklobby_chat_msg_txt" id="maploadwindowplaylistautofavstatus" style="text-align: center;top: 20px;position: relative;">Counting maps...</div>
<div class="brownButton brownButton_classic buttonShadow" style="line-height: 23px; height: 23px; width: 75px; margin-right: 5px; display: block;bottom: 10px;position: absolute;left: 20px;" id="maploadwindowplaylistautofavcancel">CANCEL</div>
<div style="line-height: 23px; height: 23px; width: 75px; margin-right: 5px; display: block;bottom: 10px;position: absolute;right: 20px;" id="maploadwindowplaylistautofavstart" class="brownButton brownButton_classic buttonShadow brownButtonDisabled">START</div>
<div class="brownButton brownButton_classic buttonShadow" style="line-height: 23px; height: 23px; width: 75px; margin-right: 5px; display: none;bottom: 10px;position: absolute;left: 20px;" id="maploadwindowplaylistautofavno">NO</div>
<div style="line-height: 23px; height: 23px; width: 75px; margin-right: 5px; display: none;bottom: 10px;position: absolute;right: 20px;" class="brownButton brownButton_classic buttonShadow" id="maploadwindowplaylistautofavyes">YES</div>
</div>`;
let error = false;
document.getElementById("maploadwindowplaylistautofavcancel").addEventListener("click", () => {
error = "cancelled";
document.getElementById("maploadwindowplaylistautofavpopup").remove();
document.getElementById("maploadtypedropdownoptionplaylists").click();
});
let maps = [];
const get = async count => {
return new Promise(resolve => {
window.$.post("https://bonk2.io/scripts/map_getfave.php", {
token: token,
startingfrom: 32 * count
}).done(async e => {
maps = maps.concat(e.maps.map(e => {return e.id}));
error = (e.r == "success" ? false : e.r);
if(e.more && !error) await get(count + 1);
resolve();
}).fail(e => {
error = e.statusText;
resolve();
});
});
}
await get(0);
if(error && error != "cancelled") {
document.getElementById("maploadwindowplaylistautofavstatus").innerText += `\nError: ${error}`;
}
else if(error != "cancelled") {
if(window.playlists.playlists.length > 0) {
maps = [...new Set(window.playlists.playlists.map(e => {return e.maps;}).reduce((a, b) => {return a.concat(b);}).filter(e => {return !maps.includes(e);}))];
}
else {
maps = [];
}
document.getElementById("maploadwindowplaylistautofavstatus").innerText += `\n${maps.length} maps to favorite`;
document.getElementById("maploadwindowplaylistautofavprogress").innerText = `[${'0'.repeat((maps.length+'').length)} / ${maps.length}]`;
if(maps.length == 0) return;
let date = new Date();
if(maps.length > 600) {
document.getElementById("maploadwindowplaylistautofavstatus").innerText += `\nYou will get ratelimited due to the high amount of maps, which means that you will have to continue this after ${((date.getHours() + 1) % 24)}:00. This also means that you can't favorite maps normally before that time.`;
}
else if(maps.length > 580) {
document.getElementById("maploadwindowplaylistautofavstatus").innerText += `\nYou might get ratelimited due to the high amount of maps, which means that you will have to continue this after ${((date.getHours() + 1) % 24)}:00. This would also mean that you can't favorite maps normally before that time.`;
}
document.getElementById("maploadwindowplaylistautofavstart").classList.remove("brownButtonDisabled");
document.getElementById("maploadwindowplaylistautofavstart").addEventListener("click", async () => {
document.getElementById("maploadwindowplaylistautofavstart").classList.add("brownButtonDisabled");
window.playlists.autofav = true;
let count = 0;
let notFound = [];
for(let map of maps) {
if(error) {
window.playlists.autofav = false;
return;
}
await window.$.post("https://bonk2.io/scripts/map_fave.php", {
token: token,
mapid: map,
action: "f"
}).done(e => {
if(e.r === "fail") {
switch(e.e) {
case "map_unpublished":
case "map_not_found":
case "already_faved":
notFound.push(map);
break;
case "token":
document.getElementById("maploadwindowplaylistautofavstatus").innerText += "\nError: invalid token";
break;
case "ratelimited":
document.getElementById("maploadwindowplaylistautofavstatus").innerText += `\nRatelimited! Please continue after ${((date.getHours() + 1) % 24)}:00.`;
error = "ratelimited";
break;
}
}
count++;
if(!error) document.getElementById("maploadwindowplaylistautofavprogress").innerText = `[${'0'.repeat((maps.length+'').length-(count+'').length)}${count} / ${maps.length}]`;
})
}
window.playlists.autofav = false;
document.getElementById("maploadwindowplaylistautofavcancel").classList.add("brownButtonDisabled");
if(notFound.length > 0) {
document.getElementById("maploadwindowplaylistautofavstatus").innerText += `\nSome of the playlists contain ${notFound.length} maps in total that have been hidden or deleted. Do you want to remove them?`;
document.getElementById("maploadwindowplaylistautofavno").style.display = "block";
document.getElementById("maploadwindowplaylistautofavyes").style.display = "block";
document.getElementById("maploadwindowplaylistautofavstart").style.display = "none";
document.getElementById("maploadwindowplaylistautofavcancel").style.display = "none";
document.getElementById("maploadwindowplaylistautofavno").addEventListener("click", () => {
document.getElementById("maploadwindowplaylistautofavpopup").remove();
document.getElementById("maploadtypedropdownoptionplaylists").click();
});
document.getElementById("maploadwindowplaylistautofavyes").addEventListener("click", () => {
for(let list of window.playlists.playlists) {
list.maps = list.maps.filter(e => {return notFound.indexOf(e) === -1;});
}
window.playlists.savePlaylists(window.playlists.playlists);
document.getElementById("maploadwindowplaylistautofavpopup").remove();
document.getElementById("maploadtypedropdownoptionplaylists").click();
});
}
else {
document.getElementById("maploadwindowplaylistautofavyes").innerText = "DONE";
document.getElementById("maploadwindowplaylistautofavyes").style.display = "block";
document.getElementById("maploadwindowplaylistautofavyes").addEventListener("click", () => {
document.getElementById("maploadwindowplaylistautofavpopup").remove();
document.getElementById("maploadtypedropdownoptionplaylists").click();
})
}
});
}
});
document.getElementById("maploadwindowplaylistedit").addEventListener("click", e => {
window.playlists.editing = false;
window.playlists.edit = !window.playlists.edit;
if(window.playlists.edit) {
document.getElementById("maploadwindowplaylistmerge").classList.add("brownButtonDisabled");
document.getElementById("maploadwindowplaylistimport").classList.add("brownButtonDisabled");
document.getElementById("maploadwindowplaylistexport").classList.add("brownButtonDisabled");
document.getElementById("maploadwindowplaylistautofav").classList.add("brownButtonDisabled");
document.getElementById("maploadwindowplaylistdeleteall").style.display = "block";
document.getElementById("maploadwindowplaylistdeleteall").innerText = "DELETE ALL";
e.target.style.filter = "brightness(1.75)";
document.getElementById("maploadtypedropdownoptionplaylists").click();
}
else {
document.getElementById("maploadwindowplaylistedit").style.removeProperty("filter");
document.getElementById("maploadwindowplaylistmerge").classList.remove("brownButtonDisabled");
document.getElementById("maploadwindowplaylistimport").classList.remove("brownButtonDisabled");
document.getElementById("maploadwindowplaylistexport").classList.remove("brownButtonDisabled");
document.getElementById("maploadwindowplaylistautofav").classList.remove("brownButtonDisabled");
document.getElementById("maploadwindowplaylistdeleteall").style.display = "none";
window.playlists.savePlaylists(window.playlists.playlists);
if(document.getElementById("maploadtypedropdowntitle").innerHTML === "MY PLAYLISTS") {
document.getElementById("maploadtypedropdownoptionplaylists").click();
}
}
});
document.getElementById("maploadwindowplaylistmerge").addEventListener("click", e => {
window.playlists.merge.enabled = !window.playlists.merge.enabled;
if(window.playlists.merge.enabled) {
window.playlists.edit = false;
document.getElementById("maploadwindowplaylistedit").style.removeProperty("filter");
e.target.style.filter = "brightness(1.75)";
document.getElementById("maploadwindowplaylistedit").classList.add("brownButtonDisabled");
document.getElementById("maploadwindowplaylistimport").classList.add("brownButtonDisabled");
document.getElementById("maploadwindowplaylistexport").classList.add("brownButtonDisabled");
document.getElementById("maploadwindowplaylistautofav").classList.add("brownButtonDisabled");
}
else {
e.target.style.removeProperty("filter");
if(window.playlists.merge.from.element !== null) {
window.playlists.merge.from.element.style.removeProperty("filter");
window.playlists.merge.from.element.style.visibility = "hidden";
}
if(window.playlists.merge.to.element !== null) {
window.playlists.merge.to.element.style.removeProperty("filter");
window.playlists.merge.to.element.style.visibility = "hidden";
}
window.playlists.merge = {
enabled: false,
from: {
element: null,
index: null
},
to: {
element: null,
index: null
}
};
document.getElementById("maploadwindowplaylistedit").classList.remove("brownButtonDisabled");
document.getElementById("maploadwindowplaylistimport").classList.remove("brownButtonDisabled");
document.getElementById("maploadwindowplaylistexport").classList.remove("brownButtonDisabled");
document.getElementById("maploadwindowplaylistautofav").classList.remove("brownButtonDisabled");
document.getElementById("maploadtypedropdownoptionplaylists").click();
}
});
document.getElementById("maploadwindowplaylistdeleteall").addEventListener("click", e => {
switch(e.target.innerText) {
case "DELETE ALL":
e.target.innerText = "SURE?";
break;
case "SURE?":
e.target.innerText = "100% SURE?";
break;
case "100% SURE?":
e.target.innerText = "FINAL WARNING";
break;
case "FINAL WARNING":
e.target.innerText = "DELETE ALL";
window.playlists.playlists = [];
for(let playlist of [...document.getElementsByClassName("maploadwindowplaylistdiv")]) {
playlist.style.opacity = 0.3;
playlist.style.pointerEvents = "none";
}
break;
}
})
document.getElementById("newbonklobby_mapbutton").addEventListener("click", () => {
if(document.getElementById("maploadtypedropdowntitle").innerText === "MY PLAYLISTS") {
document.getElementById("maploadwindowplaylistbackbutton").style.display = "block";
document.getElementById("maploadwindowtoolbox").style.display = "flex";
document.getElementById("maploadwindowmapscontainer").style.bottom = "28px";
document.getElementById("maploadwindowmapscontainer").style.height = "calc(100% - 108px - 23px)";
}
else {
document.getElementById("maploadwindowplaylistbackbutton").style.display = "none";
document.getElementById("maploadwindowtoolbox").style.display = "none";
document.getElementById("maploadwindowmapscontainer").style.removeProperty("bottom");
document.getElementById("maploadwindowmapscontainer").style.removeProperty("height");
}
});
const chatHandler = e => {
if(e.keyCode === 13) {
if(e.target.value.length > 0) {
if(e.target.value[0] === "/") {
let command = e.target.value.split(" ")[0].substring(1);
let args = e.target.value.split(" ").slice(1);
if(command === "fav") {
console.log("Autofav = " + window.playlists.autofav);
if(window.playlists.autofav) {
e.target.value = "";
window.playlists.menuFunctions.showStatusMessage("* Favoriting maps is disabled while autofav is on", "#b53030");
}
}
else if(command.startsWith("p") && !Number.isNaN(Number(command.substr(1)))) {
e.target.value = "";
if(args[0] === "list" && command === "p") {
window.playlists.menuFunctions.showStatusMessage("Saved playlists", "#b53030");
for(let i = 0; i < window.playlists.playlists.length; i++) {
window.playlists.menuFunctions.showStatusMessage("* [" + (i+1) + "] " + window.playlists.playlists[i].name, "#b53030");
}
return;
}
if(args.length === 0) {
args[0] = parseInt(command.substr(1));
}
if(Number.isNaN(parseInt(args[0]))) {
// Show help
window.playlists.menuFunctions.showStatusMessage("* List of playlist commands:", "#b53030", true);
window.playlists.menuFunctions.showStatusMessage("/p list", "#b53030", true);
window.playlists.menuFunctions.showStatusMessage("/p [index]", "#b53030", true);
return;
}
if(args[0] < 1 || args[0] > window.playlists.playlists.length) {
if(window.playlists.playlists.length === 0) {
window.playlists.menuFunctions.showStatusMessage("You don't have any playlists!", "#b53030");
return;
}
window.playlists.menuFunctions.showStatusMessage("Playlist index must be between 1 and " + (window.playlists.playlists.length), "#b53030");
return;
}
let gameSettings = window.playlists.toolFunctions.getGameSettings();
if(!gameSettings.map.m.pub && gameSettings.map.dbv == 2) {
window.playlists.menuFunctions.showStatusMessage("You can't add a private map to a playlist! If it is a Bonk 1 map, *you* need to select the map from Bonk 1 map list without starting the game. A Bonk 1 map, which is selected from a playlist, cannot be added or removed.", "#b53030");
return;
}
if(gameSettings.map.m.dbv === 2 && gameSettings.map.m.date !== undefined && gameSettings.map.m.date !== null && gameSettings.map.m.date !== "") {
if(window.playlists.playlists[args[0] - 1].maps.includes(gameSettings.map.m.dbid)) {
window.playlists.playlists[args[0] - 1].maps.splice(window.playlists.playlists[args[0] - 1].maps.indexOf(gameSettings.map.m.dbid), 1);
window.playlists.menuFunctions.showStatusMessage("* Map removed from playlist", "#b53030", true);
}
else {
// Hacky way to favorite the map
e.target.value = "/fav";
window.playlists.playlists[args[0] - 1].maps.push(gameSettings.map.m.dbid);
window.playlists.menuFunctions.showStatusMessage("* Map added to playlist", "#b53030", true);
}
}
else {
if(window.playlists.playlists[args[0] - 1].b1maps.map(e => {return e.id}).includes(gameSettings.map.m.dbid)) {
window.playlists.playlists[args[0] - 1].b1maps.splice(window.playlists.playlists[args[0] - 1].b1maps.map(e => {return e.id}).indexOf(gameSettings.map.m.dbid), 1);
window.playlists.menuFunctions.showStatusMessage("* Map removed from playlist", "#b53030", true);
}
else {
let b1map = {
id: gameSettings.map.m.dbid,
name: gameSettings.map.m.n,
authorname: gameSettings.map.m.a,
leveldata: window.playlists.bigClass.encodeToDatabase(gameSettings.map),
vu: gameSettings.map.m.vu,
vd: gameSettings.map.m.vd,
remixname: gameSettings.map.m.rxn,
remixauthor: gameSettings.map.m.rxa,
remixdb: gameSettings.map.m.rxdb,
remixid: gameSettings.map.m.rxid,
publisheddate: gameSettings.map.m.date
}
if(gameSettings.map.m.date === undefined || gameSettings.map.m.date === null || gameSettings.map.m.date === "" || gameSettings.map.m.vu * 1 != gameSettings.map.m.vu || gameSettings.map.m.vd * 1 != gameSettings.map.m.vd) {
window.playlists.menuFunctions.showStatusMessage("* Map could not be added to the playlist! To add Bonk 1 maps, *you* need to select the map from Bonk 1 map list without starting the game. A Bonk 1 map, which is selected from a playlist, cannot be added or removed.", "#b53030", true);
}
else {
window.playlists.playlists[args[0] - 1].b1maps.push(b1map);
window.playlists.menuFunctions.showStatusMessage("* Map added to playlist", "#b53030", true);
}
}
}
window.playlists.savePlaylists(window.playlists.playlists);
}
}
}
}
}
document.getElementById("newbonklobby_chat_input").addEventListener("keydown", chatHandler, true);
document.getElementById("ingamechatinputtext").addEventListener("keydown", chatHandler, true);
const getPlaylists = () => {
window.playlists.setMapsLoaded(false);
window.playlists.setMapsLoadFinished(false);
let newPlaylistButton = {
name: "NEW PLAYLIST",
description: "Click here to create a new playlist",
image: `data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="110.417px" width="160.6px"><rect style="fill:0;height:100%;width:30px;x:65.3px;y:0;"/><rect style="fill:0;height:30px;width:110.417px;x:25.091px;y:40.208px;"/></svg>`,
maps: "new",
b1maps: "new"
};
let playlistCreator = (list = {name: "", description: "", image: ""}, edit = null) => {
let newPlaylist = document.createElement("div");
newPlaylist.classList.add("maploadwindowmapdiv");
newPlaylist.style.height = "200px";
let encodedImage;
let image = document.createElement("input");
image.type = 'file';
image.style.width = "100%";
image.style.height = "110.417px";
image.style.borderWidth = "1px";
image.style.borderStyle = "solid";
image.onchange = e => {
let file = e.target.files[0];
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = readerEvent => {
encodedImage = readerEvent.target.result;
}
};
let title = document.createElement("input");
title.classList.add("maploadwindowtext_picks");
title.classList.add("maploadwindowtextname_picks");
title.placeholder = "Playlist Name";
title.value = list.name;
let description = document.createElement("input");
description.classList.add("maploadwindowtext_picks");
description.classList.add("maploadwindowtextcomment_picks");
description.placeholder = "Playlist Description";
description.style.top = "150px";
description.style.height = "60px";
description.style.height = "19px";
description.value = list.description;
let cancelButton = document.createElement("div");
cancelButton.classList.add("brownButton");
cancelButton.classList.add("brownButton_classic");
cancelButton.classList.add("buttonShadow");
cancelButton.style.width = "calc(50% - 12px)";
cancelButton.style.height = "25px";
cancelButton.style.bottom = "5px";
cancelButton.style.position = "absolute";
cancelButton.innerText = "CANCEL";
cancelButton.onclick = () => {
if(edit !== null) {
edit.style.display = "";
}
newPlaylist.remove();
window.playlists.editing = false;
};
let saveButton = document.createElement("div");
saveButton.classList.add("brownButton");
saveButton.classList.add("brownButton_classic");
saveButton.classList.add("buttonShadow");
saveButton.style.width = "calc(50% - 12px)";
saveButton.style.height = "25px";
saveButton.style.bottom = "5px";
saveButton.style.right = "6px";
saveButton.style.position = "absolute";
saveButton.innerText = "SAVE";
saveButton.onclick = () => {
if(edit === null) {
window.playlists.playlists.push({
name: title.value,
description: description.value,
image: encodedImage,
maps: [],
b1maps: []
});
}
else {
window.playlists.playlists[window.playlists.playlists.indexOf(list)] = Object.assign(window.playlists.playlists[window.playlists.playlists.indexOf(list)], {name: title.value, description: description.value, image: encodedImage === undefined ? list.image : encodedImage});
}
document.getElementById("maploadtypedropdownoptionplaylists").click();
window.playlists.editing = false;
};
newPlaylist.appendChild(image);
newPlaylist.appendChild(title);
newPlaylist.appendChild(description);
newPlaylist.appendChild(cancelButton);
newPlaylist.appendChild(saveButton);
return newPlaylist;
}
window.playlists.setMapsLoaded(true);
for(let list of window.playlists.playlists.concat([newPlaylistButton])) {
let playlist = document.createElement("div");
playlist.classList.add("maploadwindowmapdiv");
if(list != newPlaylistButton) playlist.classList.add("maploadwindowplaylistdiv");
playlist.style.height = "200px";
document.getElementById("maploadwindowmapscontainer").appendChild(playlist);
let image = document.createElement("img");
if(list.image != undefined) {
image.src = list.image;
}
else {
let color = [...list.name.substr(0,32)].reduce((a, b) => a + b.charCodeAt(0), 0) % 360;
image.src = `data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="110.417px" width="160.6px"><rect style="fill:hsl(${color},75%,50%);height:100%;width:100%;"/></svg>`;
}
image.style.width = "160.6px";
image.style.height = "110.417px";
let title = document.createElement("span");
title.classList.add("maploadwindowtext_picks");
title.classList.add("maploadwindowtextname_picks");
title.innerText = list.name;
let description = document.createElement("span");
description.classList.add("maploadwindowtext_picks");
description.classList.add("maploadwindowtextcomment_picks");
description.style.top = "150px";
description.style.height = "60px";
description.innerText = list.description;
if(list.maps !== "new") {
let deleteButton = document.createElement("div");
deleteButton.style.visibility = "hidden";
deleteButton.style.width = "50px";
deleteButton.style.padding = "3px";
deleteButton.style.fontSize = "16px";
deleteButton.classList.add("maploadwindowdeletebutton");
deleteButton.classList.add("brownButton");
deleteButton.classList.add("brownButton_classic");
deleteButton.classList.add("buttonShadow");
deleteButton.innerText = "DELETE";
deleteButton.onclick = () => {
if(deleteButton.innerText === "DELETE") {
deleteButton.innerText = "SURE?";
return;
}
playlist.style.opacity = 0.3;
playlist.style.pointerEvents = "none";
window.playlists.playlists.splice(window.playlists.playlists.indexOf(list), 1);
}
let editButton = document.createElement("div");
editButton.style.visibility = "hidden";
editButton.style.left = "10px";
editButton.style.width = "50px";
editButton.style.padding = "3px";
editButton.style.fontSize = "16px";
editButton.classList.add("maploadwindowdeletebutton");
editButton.classList.add("brownButton");
editButton.classList.add("brownButton_classic");
editButton.classList.add("buttonShadow");
editButton.innerText = "EDIT";
editButton.onclick = () => {
playlist.style.display = "none";
document.getElementById("maploadwindowmapscontainer").insertBefore(playlistCreator(list, playlist), playlist);
window.playlists.editing = true;
}
let leftButton = document.createElement("div");
leftButton.style.visibility = "hidden";
leftButton.classList.add("brownButton");
leftButton.classList.add("brownButton_classic");
leftButton.classList.add("buttonShadow");
leftButton.style.width = "26px";
leftButton.style.height = "26px"
leftButton.style.bottom = "10px";
leftButton.style.left = "10px";
leftButton.style.position = "absolute";
leftButton.style.zIndex = 1;
leftButton.innerText = "<";
let rightButton = document.createElement("div");
rightButton.style.visibility = "hidden";
rightButton.classList.add("brownButton");
rightButton.classList.add("brownButton_classic");
rightButton.classList.add("buttonShadow");
rightButton.style.width = "26px";
rightButton.style.height = "26px"
rightButton.style.bottom = "10px";
rightButton.style.right = "10px";
rightButton.style.position = "absolute";
rightButton.style.zIndex = 1;
rightButton.innerText = ">";
leftButton.onclick = () => {
let index = window.playlists.playlists.indexOf(list);
if(index > 0) {
window.playlists.playlists[index] = window.playlists.playlists.splice(index - 1, 1, window.playlists.playlists[index])[0];
playlist.remove();
document.getElementById("maploadwindowmapscontainer").insertBefore(playlist, document.getElementById("maploadwindowmapscontainer").children[index-1]);
playlist.onmouseleave();
}
}
rightButton.onclick = () => {
let index = window.playlists.playlists.indexOf(list);
if(index < window.playlists.playlists.length - 1) {
window.playlists.playlists[index] = window.playlists.playlists.splice(index + 1, 1, window.playlists.playlists[index])[0];
playlist.remove();
document.getElementById("maploadwindowmapscontainer").insertBefore(playlist, document.getElementById("maploadwindowmapscontainer").children[index+1]);
playlist.onmouseleave();
}
}
let mergeButton = document.createElement("div");
mergeButton.style.visibility = "hidden";
mergeButton.classList.add("brownButton");
mergeButton.classList.add("brownButton_classic");
mergeButton.classList.add("buttonShadow");
mergeButton.style.width = "124px";
mergeButton.style.height = "52px"
mergeButton.style.top = "23px";
mergeButton.style.left = "23px";
mergeButton.style.position = "absolute";
mergeButton.style.zIndex = 1;
mergeButton.style.justifyContent = "center";
mergeButton.style.display = "flex";
mergeButton.style.alignItems = "center";
mergeButton.onclick = () => {
mergeButton.style.filter = "brightness(1.75)";
if(window.playlists.merge.from.index === null) {
window.playlists.merge.from = {
element: mergeButton,
index: window.playlists.playlists.indexOf(list)
}
}
else if(window.playlists.merge.from.element === mergeButton) {
mergeButton.style.removeProperty("filter");
window.playlists.merge.from = {
element: null,
index: null
}
}
else {
if(mergeButton.innerText !== "SURE?") {
mergeButton.innerText = "SURE?";
return;
}
window.playlists.merge.to = {
element: mergeButton,
index: window.playlists.playlists.indexOf(list)
}
window.playlists.playlists[window.playlists.merge.to.index].b1maps = [...new Set(
(window.playlists.playlists[window.playlists.merge.from.index].b1maps.concat(window.playlists.playlists[window.playlists.merge.to.index].b1maps)).map(m => JSON.stringify(m))
)].map(m => JSON.parse(m));
window.playlists.playlists[window.playlists.merge.to.index].maps = [...new Set(window.playlists.playlists[window.playlists.merge.from.index].maps.concat(window.playlists.playlists[window.playlists.merge.to.index].maps))];
window.playlists.playlists.splice(window.playlists.merge.from.index, 1);
window.playlists.merge.from.element.parentElement.style.opacity = 0.3;
window.playlists.merge.from.element.parentElement.style.pointerEvents = "none";
window.playlists.savePlaylists(window.playlists.playlists);
window.playlists.merge.from.element.style.removeProperty("filter");
window.playlists.merge.from.element.style.visibility = "hidden";
window.playlists.merge.to.element.style.removeProperty("filter");
window.playlists.merge.to.element.style.visibility = "hidden";
document.getElementById("maploadwindowplaylistmerge").click();
}
}
playlist.onmouseenter = () => {
if(window.playlists.editing) return;
if(window.playlists.edit) {
deleteButton.style.visibility = "inherit";
editButton.style.visibility = "inherit";
leftButton.style.visibility = "inherit";
rightButton.style.visibility = "inherit";
}
else if(window.playlists.merge.enabled && window.playlists.merge.from.element !== mergeButton) {
if(window.playlists.merge.from.index === null) {
mergeButton.innerText = "MERGE FROM";
}
else {
mergeButton.innerText = "MERGE TO";
mergeButton.style.removeProperty("filter");
}
mergeButton.style.visibility = "inherit";
}
}
playlist.onmouseleave = () => {
deleteButton.style.visibility = "hidden";
editButton.style.visibility = "hidden";
leftButton.style.visibility = "hidden";
rightButton.style.visibility = "hidden";
if(window.playlists.merge.from.element !== mergeButton && window.playlists.merge.to.element !== mergeButton)
mergeButton.style.visibility = "hidden";
}
playlist.appendChild(deleteButton);
playlist.appendChild(editButton);
playlist.appendChild(leftButton);
playlist.appendChild(rightButton);
playlist.appendChild(mergeButton);
}
else {
playlist.id = "maploadwindowplaylistnew";
playlist.style.display = window.playlists.edit ? "inline-block" : "none";
}
playlist.appendChild(image);
playlist.appendChild(title);
playlist.appendChild(description);
playlist.onclick = (e) => {
if(list.maps !== "new") {
if(window.playlists.edit || window.playlists.merge.enabled) return;
while(document.getElementById("maploadwindowmapscontainer").firstChild) {
document.getElementById("maploadwindowmapscontainer").firstChild.remove();
}
document.getElementById("maploadwindowtoolbox").style.display = "none";
document.getElementById("maploadwindowmapscontainer").style.removeProperty("bottom");
document.getElementById("maploadwindowmapscontainer").style.removeProperty("height");
document.getElementById("maploadwindowstatustext").style.visibility = "inherit";
if((list.maps.length + list.b1maps.length) == 0) {
document.getElementById("maploadwindowstatustext").textContent = "No Maps";
return;
}
if(e.target.classList.contains("brownButton")) return;
let foundBonk2Maps = 0;
let addMaps = (offset = 0) => {
$.post("https://bonk2.io/scripts/map_getfave.php", {
token: token,
startingfrom: offset * 32
}).done(function (h0i, Y0i) {
if (arguments[0].r != "success") {
document.getElementById("maploadwindowstatustext").style.visibility = "inherit";
document.getElementById("maploadwindowstatustext").textContent = "Fetch error";
}
else {
let filteredMapList = arguments[0];
filteredMapList.maps = filteredMapList.maps.filter(e => {if(list.maps.includes(e.id)) {return e;}}).sort().slice(0, list.maps.length);
document.getElementById("maploadwindowstatustext").style.visibility = "hidden";
if(filteredMapList.maps.length > 0) {
window.playlists.mapLoader(filteredMapList);
foundBonk2Maps += filteredMapList.maps.length;
}
if(arguments[0].more && foundBonk2Maps < list.maps.length) {
addMaps(offset + 1);
}
else if(list.b1maps.length > 0) {
window.playlists.mapLoader({r: "success", maps: list.b1maps, more: false}, 0);
window.playlists.setMapsLoadFinished(true);
}
else {
window.playlists.setMapsLoadFinished(true);
}
}
});
}
addMaps();
}
else if(list.maps === "new" && !window.playlists.editing) {
playlist.style.display = "none";
document.getElementById("maploadwindowmapscontainer").insertBefore(playlistCreator(), playlist);
}
}
}
document.getElementById("maploadwindowstatustext").style.visibility = "hidden";
}
patchCounter = 0;
const patch = (a, b) => {
c = newStr;
newStr = newStr.replace(a, b);
//console.log(`Patch ${patchCounter++}: ${c === newStr ? 'fail' : 'success'}`);
return c !== newStr;
}
const categoryFunc = newStr.match(/[A-Za-z0-9\$_]{3}\([A-Za-z0-9\$_]{3}\.[A-Za-z0-9\$_]{3}\([0-9]*\),true\)/)[0].substr(0,3);
patch(`function ${categoryFunc}`, `window.playlists.categoryFunc=${categoryFunc};function ${categoryFunc}`);
//Get map loader
let mapLoader = newStr.match(/maploadwindowsearchinput.{0,200}else if\([A-Za-z0-9\$_]{3}\[0\]\[0\]\[[A-Za-z0-9\$_]{3}\[[0-9]+\][[0-9]+\]\] == [A-Za-z0-9\$_]{3}\.[A-Za-z0-9\$_]{3}\([0-9]+\)\)\{[A-Za-z0-9\$_]{3}\([A-Za-z0-9\$_]{3}\[0\]\[0\]\);[A-Za-z0-9\$_]{3}\[[0-9]+\]=[A-Za-z0-9\$_]{3}\[0\]\[0\]\[[A-Za-z0-9\$_]{3}\[[0-9]+\]\[[0-9]+\]\];\}\}\)/g)[0].match(/[A-Za-z0-9\$_]{3}\([A-Za-z0-9\$_]{3}\[0\]\[0\]\);/)[0].slice(0, 3);
patch(`function ${mapLoader}`, `window.playlists.mapLoader=${mapLoader};function ${mapLoader}`);
//Get token
patch('[1,10000,25000,100000,500000,8000000,5000000000];', '[1,10000,25000,100000,500000,8000000,5000000000];' + "window.playlists.setToken(arguments[0]);");
//Mapload ready variable
let readyVar = newStr.match(/\+ 1000 && [A-Za-z0-9\$_]{3}\[[0-9+]+\]/)[0].split(" ")[3];
patch('[1,10000,25000,100000,500000,8000000,5000000000];', '[1,10000,25000,100000,500000,8000000,5000000000];' + `window.playlists.setMapsLoaded=a=>{${readyVar}=a;};`);
//Mapload finish variable
let finishVar = newStr.match(/false\);\}else \{if\([A-Za-z0-9\$_]{3}\[[0-9]+\]\)\{/)[0].split(/[\(\)]/)[2];
//Inverting the value is important
patch('[1,10000,25000,100000,500000,8000000,5000000000];', '[1,10000,25000,100000,500000,8000000,5000000000];' + `window.playlists.setMapsFinished=a=>{${finishVar}=!a;};`);
//Get some useful functions
let menuRegex = newStr.match(/== 13\){...\(\);}}/)[0];
patch(menuRegex, menuRegex + "window.playlists.menuFunctions = this;");
let toolRegex = newStr.match(/=new [A-Za-z0-9\$_]{1,3}\(this,[A-Za-z0-9\$_]{1,3}\[0\]\[0\],[A-Za-z0-9\$_]{1,3}\[0\]\[1\]\);/);
patch(toolRegex, toolRegex + "window.playlists.toolFunctions = this;");
//Big class
let bigClass = newStr.match(/[A-Z]\[[A-Za-z0-9\$_]{1,3}\[[0-9]+\]\[[0-9]+\]\]\([A-Za-z0-9\$_]{1,3}\[0\]\[0\]\);[A-Za-z0-9\$_]{1,3}\[[0-9]+\]\[[A-Za-z0-9\$_]{1,3}\[[0-9]+\]\[[0-9]+\]\]\([A-Za-z0-9\$_]{1,3}\[[0-9]+\],{m:/)[0][0];
patch(`function ${bigClass}(){}`, `function ${bigClass}(){};window.playlists.bigClass=${bigClass};`);
console.log("Bonk Playlists injector run");
return newStr;
}
if(!window.bonkCodeInjectors) window.bonkCodeInjectors = [];
window.bonkCodeInjectors.push(bonkCode => {
try {
return injector(bonkCode);
} catch (error) {
alert(
`Whoops! Bonk Playlists was unable to load.
This may be due to an update to Bonk.io. If so, please report this error!
This could also be because you have an extension that is incompatible with \
Bonk Playlists, such as the Bonk Leagues Client. You would have to disable it to use \
Bonk Playlists.`);
throw error;
}
});
console.log("Bonk Playlists injector loaded");