Files
TheEvilList/js/content.js
2026-04-17 12:29:41 -04:00

251 lines
8.1 KiB
JavaScript

import { round, score } from './score.js';
import { resetList } from './pages/List.js';
import { resetLeaderboard } from './pages/Leaderboard.js';
import { resetPacks } from './pages/ListPacks.js';
import { store } from './main.js';
/**
* Path to directory containing `_list.json` and all levels
*/
const dir = '/data';
var theList = "dl";
// Thing at the beginning of the file name to signify that the level is pending
const pendingMark = "(u)_";
export async function fetchList() {
const listResult = await fetch(`${dir}/${theList}/_list.json`);
const packResult = await fetch(`${dir}/${theList}/_packlist.json`);
try {
const list = await listResult.json();
const packsList = await packResult.json();
// Create a lookup dictionary for ranks
const ranksEntries = list
.filter((path) => !path.startsWith(pendingMark))
.map((path, index) => [path, index + 1]);
const ranks = Object.fromEntries(ranksEntries);
return await Promise.all(
list.map(async (path) => {
const rank = ranks[path] || null;
try {
const levelResult = await fetch(`${dir}/${theList}/${path.startsWith(pendingMark) ? path.substring(4) : path}.json`);
const level = await levelResult.json();
let packs = packsList.filter((x) =>
x.levels.includes(path)
);
let enjoymentSum = 0;
for(let i = 0; i < level.records.length; i++){
enjoymentSum += level.records[i].enjoyment;
}
return [
null,
rank,
{
...level,
rank,
packs,
path,
records: level.records.sort(
(a, b) => b.percent - a.percent,
),
enjoyment: round((level.verifierEnjoyment + enjoymentSum)/(level.records.length + 1)),
},
null,
];
} catch {
console.error(`Failed to load level #${rank} ${path}.`);
return [path, rank, null];
}
}),
);
} catch {
console.error(`Failed to load list.`);
return null;
}
}
export async function fetchEditors() {
try {
const editorsResults = await fetch(`${dir}/_editors.json`);
const editors = await editorsResults.json();
return editors;
} catch {
return null;
}
}
export async function fetchLeaderboard() {
const list = await fetchList();
const packResult = await (await fetch(`${dir}/${theList}/_packlist.json`)).json();
const scoreMap = {};
const errs = [];
if (list === null) {
return [null, ["Failed to load list."]];
}
list.forEach(([err, rank, level]) => {
if (err) {
errs.push(err);
return;
}
if (rank === null) {
return;
}
// Verification
const verifier = Object.keys(scoreMap).find(
(u) => u.toLowerCase() === level.verifier.toLowerCase(),
) || level.verifier;
scoreMap[verifier] ??= {
verified: [],
completed: [],
progressed: [],
packs: [],
};
const { verified } = scoreMap[verifier];
verified.push({
rank: rank,
level: level.name,
score: score(rank, 100, level.percentToQualify) * 1.0,
link: level.verification,
path: level.path
});
// Records
level.records.forEach((record, index) => {
const user = Object.keys(scoreMap).find(
(u) => u.toLowerCase() === record.user.toLowerCase(),
) || record.user;
scoreMap[user] ??= {
verified: [],
completed: [],
progressed: [],
packs: [],
path: level.path
};
const { completed, progressed } = scoreMap[user];
if (record.percent === 100) {
completed.push({
rank: rank,
level: level.name,
score:
index < 2
? score(rank, 100, level.percentToQualify) * 1.0
: score(rank, 100, level.percentToQualify),
link: record.link,
path: level.path,
});
return;
}
progressed.push({
rank: rank,
level: level.name,
percent: record.percent,
score: score(rank, record.percent, level.percentToQualify),
link: record.link,
path: level.path
});
});
});
for (let user of Object.entries(scoreMap)) {
let levels = [...user[1]["verified"], ...user[1]["completed"]].map(
(x) => x["path"]
);
for (let pack of packResult) {
if (pack.levels.every((e1) => levels.includes(e1))) {
user[1]["packs"].push(pack);
}
}
}
// Wrap in extra Object containing the user and total score
const res = Object.entries(scoreMap).map(([user, scores]) => {
const { verified, completed, progressed } = scores;
const total = [verified, completed, progressed]
.flat()
.reduce((prev, cur) => prev + cur.score, 0);
return {
user,
total: round(total),
...scores,
};
});
// Sort by total score
console.log(scoreMap)
return [res.sort((a, b) => b.total - a.total), errs];
}
export async function fetchPacks() {
try {
const packResult = await fetch(`${dir}/${theList}/_packlist.json`);
const packsList = await packResult.json();
return packsList;
} catch {
return null;
}
}
export async function fetchPackLevels(packname) {
const packResult = await fetch(`${dir}/${theList}/_packlist.json`);
const packsList = await packResult.json();
const selectedPack = await packsList.find((pack) => pack.name == packname);
const listResult = await await fetch(`${dir}/${theList}/_list.json`);
try {
const list = await listResult.json();
// Create a lookup dictionary for ranks
const ranksEntries = list
.filter((path) => !path.startsWith(pendingMark))
.map((path, index) => [path, index + 1]);
const ranks = Object.fromEntries(ranksEntries);
return await Promise.all(
selectedPack.levels.map(async (path, rank) => {
const levelResult = await fetch(`${dir}/${theList}/${path}.json`);
try {
const level = await levelResult.json();
const listRank = ranks[path] || null;
return [
{
level,
listRank,
path,
records: level.records.sort(
(a, b) => b.percent - a.percent,
),
},
null,
];
} catch {
console.error(`Failed to load level #${rank} ${path} (${packname}).`);
return [path, rank, null];
}
})
);
} catch (e) {
console.error(`Failed to load packs.`, e);
return null;
}
}
export async function changeList(){
var newList = document.getElementById("list_dropdown").value;
console.log("theList: " + theList.toString() + ", dropdown value: " + document.getElementById("list_dropdown").value.toString());
store.listType = newList;
theList = newList;
resetList();
resetLeaderboard();
resetPacks();
}