function login_then_go_to(url) {
const domain_name = document.location.hostname.substring(document.location.hostname.indexOf('.') + 1);
document.cookie = `login_to=${encodeURIComponent(url)}; path=/; domain=${domain_name}; secure; SameSite=Strict; Max-Age=30000`;
document.location = '/osu_login';
}
function ask_for_manual_lobby_creation(lobby_settings) {
document.querySelector('main .loading-placeholder').replaceWith(
document.querySelector('#manual-lobby-creation-template').content.cloneNode(true),
);
document.querySelector('main .create-lobby-with-ref-btn').addEventListener('click', (evt) => {
evt.preventDefault();
evt.stopPropagation();
// Input shown if bot can't create any more lobbies and needs a user-made lobby
const match_input = document.querySelector('main input[name="tournament-url"]');
if (match_input.value) {
lobby_settings.match_id = parseInt(match_input.value.split('/').reverse()[0], 10);
}
try_creating_lobby(lobby_settings);
});
}
async function try_creating_lobby(lobby_settings) {
document.querySelector('main').innerHTML = `
`;
try {
const res = await fetch('/api/create-lobby/', {
body: JSON.stringify(lobby_settings),
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
});
const json_res = await res.json();
if (json_res.error) {
if (json_res.details == 'Cannot create any more matches.') {
ask_for_manual_lobby_creation(lobby_settings);
return;
}
throw new Error(json_res.details || json_res.error);
}
document.querySelector('main .loading-placeholder').replaceWith(
document.querySelector('#lobby-created-template').content.cloneNode(true),
);
document.querySelector('.lobby').replaceWith(render_lobby(json_res.lobby));
document.querySelector('.info').appendChild(document.querySelector('#command-list-template').content.cloneNode(true));
} catch (err) {
document.querySelector('main').innerHTML = `
An error occurred while creating the lobby:
`;
document.querySelector('.error-msg').innerText = err.message;
}
}
function render_lobby(lobby, user_has_lobby_open) {
const lobby_div = document.createElement('div');
let map_pool_info = 'All maps';
if (lobby.map_pool == 'collection') {
const pool = document.createElement('a');
pool.href = 'https://osucollector.com/collections/' + lobby.collection_id;
pool.target = '_blank';
pool.innerText = lobby.collection_name;
map_pool_info = pool.outerHTML;
}
let map_selection_info = 'Random';
if (lobby.map_selection_algo == 'pp') {
map_selection_info = 'Average player pp';
} else if (lobby.map_selection_algo == 'elo') {
map_selection_info = 'Average player elo';
}
if (lobby.filters.length > 0) {
const filter_span = document.createElement('span');
filter_span.classList = 'cursor-help underline underline-offset-2 decoration-1 decoration-dotted';
filter_span.innerText = lobby.filters.length + ' filters';
filter_span.title = '';
for (const filter of lobby.filters) {
filter_span.title += `${filter.name} between ${filter.min} and ${filter.max}\n`;
}
map_selection_info += ' (' + filter_span.outerHTML + ')';
}
let mods_info = 'None';
if (lobby.mod_list.length > 0) {
mods_info = lobby.mod_list.join(', ');
}
if (lobby.freemod) {
mods_info += ' (freemod)';
}
let status = '';
if (lobby.end_time) {
function reltime(diff) {
const rtf = new Intl.RelativeTimeFormat('en', {numeric: 'auto'});
if (diff > -60) {
return rtf.format(Math.round(diff), 'second');
} else if (diff > -3600) {
return rtf.format(Math.round(diff / 60), 'minute');
} else if (diff > -86400) {
return rtf.format(Math.round(diff / 3600), 'hour');
} else {
return rtf.format(Math.round(diff / 86400), 'day');
}
}
const seconds_ago = reltime((lobby.end_time - Date.now()) / 1000);
const closing_time = new Intl.DateTimeFormat(undefined, {
year: 'numeric', month: 'numeric', day: 'numeric',
hour: 'numeric', minute: 'numeric', second: 'numeric',
}).format(new Date(lobby.end_time));
status = `Closed ${seconds_ago}`;
} else {
status = `${lobby.nb_players}/16 players`;
}
lobby_div.style = `border: solid ${lobby.color} 2px`;
lobby_div.innerHTML += `
Map pool: ${map_pool_info}
Map selection: ${map_selection_info}
Mods: ${mods_info}
Keys: ${lobby.key_counts.join(', ')}
`;
if (lobby.end_time) {
lobby_div.className = 'flex-1 m-2 rounded-md';
lobby_div.innerHTML += `
`;
// TODO: make "Copy settings" buttons instead of too basic "reopen"
lobby_div.querySelector('.go-to-create-lobby').style = `background-color: ${lobby.color}; margin: auto; display: block`;
lobby_div.querySelector('.go-to-create-lobby').addEventListener('click', (evt) => {
evt.preventDefault();
evt.stopPropagation();
if (window.logged_user_id) {
document.location = `/reopen-lobby/${lobby.match_id}`;
} else {
return login_then_go_to(`${location.origin}/reopen-lobby/${lobby.match_id}`);
}
});
} else {
// Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com
// License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
// Copyright 2022 Fonticons, Inc.
const fa_open = '';
const fa_invite = '';
lobby_div.className = 'flex m-2 rounded-md';
lobby_div.innerHTML += `
`;
lobby_div.querySelector('.get-invite-btn').addEventListener('click', (evt) => {
evt.preventDefault();
evt.stopPropagation();
if (!window.logged_user_id) {
return login_then_go_to(`${location.origin}/lobbies/`);
}
async function request_invite() {
try {
const res = await fetch(`/get-invite/${lobby.bancho_id}`, {
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
});
const json_res = await res.json();
if (json_res.error) throw new Error(json_res.error);
alert('An invite to the lobby has been sent. Check your in-game messages.');
} catch (err) {
alert(err.message);
}
}
request_invite();
});
}
lobby_div.querySelector('.lobby-title').innerText = lobby.name;
// Mania: display keycounts
if (window.selected_ruleset == 3) {
lobby_div.querySelector('.mania-keycounts').classList.remove('hidden');
}
return lobby_div;
}
async function render_lobbies() {
const template = document.querySelector('#lobbies-template').content.cloneNode(true);
const res = await fetch(`/api/lobbies/`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
});
const json = await res.json();
let user_has_lobby_open = false;
const open_lobbies = [];
const closed_lobbies = [];
for (const lobby of json) {
if (lobby.end_time) {
closed_lobbies.push(lobby);
} else {
open_lobbies.push(lobby);
if (lobby.creator_id == window.logged_user_id) {
// User already created a lobby: hide the "Create lobby" button
template.querySelector('.lobby-creation-banner').hidden = true;
user_has_lobby_open = true;
}
}
}
if (open_lobbies.length == 0) {
template.querySelector('.lobby-creation-banner span').innerText = 'No lobbies are open right now.';
} else {
template.querySelector('.lobby-creation-banner span').innerText = 'Not satisfied?';
}
template.querySelector('.go-to-create-lobby').addEventListener('click', (evt) => {
evt.preventDefault();
evt.stopPropagation();
if (window.logged_user_id) {
document.location = '/create-lobby/';
} else {
return login_then_go_to(`${location.origin}/create-lobby/`);
}
});
// Display open lobbies
let list = template.querySelector('.open-lobby-list');
for (const lobby of open_lobbies) {
list.appendChild(render_lobby(lobby));
}
// Display closed lobbies
list = template.querySelector('.closed-lobby-list');
for (const lobby of closed_lobbies) {
list.appendChild(render_lobby(lobby, user_has_lobby_open));
}
document.querySelector('main').replaceChildren(template);
setTimeout(render_lobbies, 10000);
}
async function render_create_lobby() {
const rulesets = ['osu', 'taiko', 'catch', 'mania'];
const template = document.querySelector('#lobby-creation-template').content.cloneNode(true);
template.querySelector('h1').innerText = `New ${rulesets[window.selected_ruleset]} lobby`;
document.querySelector('main .loading-placeholder').replaceWith(template);
const filter_list = document.querySelector('.filter-list');
const render_filter = (options) => `
`;
filter_list.innerHTML += render_filter({name: 'pp', label: 'performance points (PP)', min: 0, max: 2000, default_min: 150, default_max: 200, step: 25});
filter_list.innerHTML += render_filter({name: 'sr', label: 'star rating (SR)', min: 0, max: 20, default_min: 5, default_max: 6, step: 0.5});
filter_list.innerHTML += render_filter({name: 'ar', label: 'approach rate (AR)', min: 0, max: 11, default_min: 9, default_max: 9.5, step: 0.1});
filter_list.innerHTML += render_filter({name: 'cs', label: 'circle size (CS)', min: 0, max: 10, default_min: 5, default_max: 6, step: 1});
filter_list.innerHTML += render_filter({name: 'od', label: 'overall difficulty (OD)', min: 0, max: 10, default_min: 6, default_max: 8, step: 1});
filter_list.innerHTML += render_filter({name: 'bpm', label: 'beats per minute (BPM)', min: 0, max: 1000, default_min: 160, default_max: 220, step: 10});
filter_list.innerHTML += render_filter({name: 'length', label: 'length (in seconds)', min: 0, max: 3600, default_min: 30, default_max: 500, step: 30});
document.querySelector('#length_filter').checked = true;
document.querySelector('input[name="title"]').addEventListener('input', (evt) => {
let title_preview = evt.target.value;
const replacements = [
['$min_stars', '0'], ['$avg_stars', '5.5'], ['$max_stars', '11'], ['$stars', '0-11'],
['$min_elo', '1200'], ['$avg_elo', '1500'], ['$max_elo', '1800'], ['$elo', '1500'],
['$min_pp', '100'], ['$avg_pp', '150'], ['$max_pp', '200'], ['$pp', '150'],
];
for (let i = 0; i < replacements.length; i++) {
title_preview = title_preview.replaceAll(replacements[i][0], replacements[i][1]);
}
title_preview = title_preview.substring(0, 50);
document.querySelector('.preview').innerText = title_preview;
});
document.querySelectorAll('.filter').forEach((filter) => {
filter.querySelector('input[type="checkbox"]').addEventListener('change', function() {
const fieldset = filter.querySelector('fieldset');
fieldset.disabled = !fieldset.disabled;
});
});
// Mania-specific mods
if (window.selected_ruleset == 3) {
document.querySelector('.mr').classList.remove('hidden');
document.querySelector('.co').classList.remove('hidden');
document.querySelector('.fi').classList.remove('hidden');
let mods = '';
for (let i = 1; i <= 9; i++) {
mods += `
${i}K
`;
}
document.querySelector('.mania-keycount-settings').innerHTML = `
${mods}
`;
document.querySelectorAll('.mania-keycount-settings .mod-btn').forEach((btn) => btn.addEventListener('click', function() {
this.classList.toggle('mod-btn-selected');
}));
}
// Click to toggle collapse
document.querySelectorAll('.collapser').forEach((collapser) => {
collapser.parentElement.addEventListener('click', () => {
collapser.classList.toggle('rotated');
collapser.parentElement.nextElementSibling.classList.toggle('hidden');
});
});
// Circle size does not apply for taiko/manio
if (window.selected_ruleset == 1 || window.selected_ruleset == 3) {
document.querySelector('#cs_filter').parentElement.parentElement.classList.add('hidden');
}
document.querySelectorAll('.mod-settings .mod-btn').forEach((btn) => btn.addEventListener('click', function() {
const NM = document.querySelector('.nm');
const DT = document.querySelector('.dt');
const NC = document.querySelector('.nc');
const HT = document.querySelector('.ht');
const EZ = document.querySelector('.ez');
const HR = document.querySelector('.hr');
const mod = this.querySelector('div').innerText;
this.classList.toggle('mod-btn-selected');
let selected = this.classList.contains('mod-btn-selected');
if (mod != 'NM' && mod != 'DT' && mod != 'NC' && mod != 'HT') {
NM.classList.remove('mod-btn-selected');
}
if (mod == 'NM' && selected) {
document.querySelectorAll('.mod-settings .mod-btn').forEach((btn) => {
if (btn.classList.contains('dt') || btn.classList.contains('nc') || btn.classList.contains('ht')) return;
btn.classList.remove('mod-btn-selected');
});
this.classList.add('mod-btn-selected');
}
if (mod == 'DT' && !selected) {
DT.classList.toggle('hidden');
NC.classList.toggle('hidden');
NC.classList.add('mod-btn-selected');
selected = true;
}
if (mod == 'NC') {
DT.classList.toggle('hidden');
NC.classList.toggle('hidden');
}
if (selected && (mod == 'DT' || mod == 'NC')) {
HT.classList.remove('mod-btn-selected');
}
if (selected && mod == 'HT') {
DT.classList.remove('mod-btn-selected');
DT.classList.remove('hidden');
NC.classList.remove('mod-btn-selected');
NC.classList.add('hidden');
}
if (selected && mod == 'EZ') {
HR.classList.remove('mod-btn-selected');
}
if (selected && mod == 'HR') {
EZ.classList.remove('mod-btn-selected');
}
}));
if (window.last_match_id) {
const recreate_lobby_div = document.createElement('div');
recreate_lobby_div.className = 'border-2 border-orange-600 rounded-lg p-3 text-center mx-2 mt-4';
recreate_lobby_div.innerHTML = `
You have already created a lobby, but it has been closed due to inactivity.
If you wish to keep the same settings, you can just reopen it:
`;
document.querySelector('main .lobby-settings').append(recreate_lobby_div);
document.querySelector('.reopen-lobby').addEventListener('click', (evt) => {
evt.preventDefault();
evt.stopPropagation();
if (window.logged_user_id) {
document.location = `/reopen-lobby/${window.last_match_id}`;
} else {
return login_then_go_to(`${location.origin}/reopen-lobby/${window.last_match_id}`);
}
});
}
document.querySelector('main .create-lobby-btn').addEventListener('click', (evt) => {
evt.preventDefault();
evt.stopPropagation();
const lobby_settings = {
title: document.querySelector('input[name="title"]').value,
map_selection_algo: document.querySelector('main input[name="map-selection-type"]:checked').value,
map_pool: document.querySelector('main input[name="map-pool"]:checked').value,
collection_id: null,
mod_list: [],
filters: [],
key_count: [],
};
if (lobby_settings.map_pool == 'collection') {
const collection_input = document.querySelector('main input[name="collection-url"]');
lobby_settings.collection_id = parseInt(collection_input.value.split('/').reverse()[0], 10);
}
const selected_mods = document.querySelectorAll('.mod-settings .mod-btn-selected');
for (const mod of selected_mods) {
lobby_settings.mod_list.push(mod.innerText.trim());
}
const filters = document.querySelectorAll('.filter');
for (const filter of filters) {
const checkbox = filter.querySelector('input[type="checkbox"]');
if (!checkbox.checked) continue;
const name = checkbox.id.substring(0, checkbox.id.indexOf('_filter'));
const min = document.querySelector(`input[name="min_${name}"]`).value;
const max = document.querySelector(`input[name="max_${name}"]`).value;
lobby_settings.filters.push({name, min, max});
}
if (window.selected_ruleset == 3) {
const selected_keys = document.querySelectorAll('.mania-keycount-settings .mod-btn-selected');
for (const key of selected_keys) {
lobby_settings.key_count.push(parseInt(key.innerText, 10));
}
}
try_creating_lobby(lobby_settings);
});
const radios = document.querySelectorAll('.radio-area');
for (const area of radios) {
area.addEventListener('click', function() {
this.querySelector('input[type="radio"]').click();
});
}
}
let m;
if (m = location.pathname.match(/\/create-lobby\//)) {
render_create_lobby();
} else if (m = location.pathname.match(/\/reopen-lobby\/(\d+)/)) {
try_creating_lobby({old_match_id: parseInt(m[1], 10)});
} else if (m = location.pathname.match(/\/lobbies\//)) {
render_lobbies();
}