diff --git a/tunictracker/tracker/static/tracker/assets/fetch-updates.js b/tunictracker/tracker/static/tracker/assets/fetch-updates.js new file mode 100644 index 0000000..4162b13 --- /dev/null +++ b/tunictracker/tracker/static/tracker/assets/fetch-updates.js @@ -0,0 +1,39 @@ +async function refresh_overview(server_address) { + try { + const response = await fetch(`${server_address}overview`); + if (response.ok) { + const data = await response.json(); + if (!("error" in data)) { + return { overview: data, error: null }; + } + } + return { overview: null, error: null }; + } catch (e) { + return { overview: null, error: e }; + } +} + +async function refresh_hints(server_address) { + const response = await fetch(`${server_address}hints`); + const data = await response.json(); + return data; +} + +async function refresh_checks(server_address) { + const response = await fetch(`${server_address}items`); + const data = await response.json(); + return data; +} + +async function refresh_entrances(server_address) { + const response = await fetch(`${server_address}doors`); + const data = await response.json(); + return data; +} + +export default { + refresh_overview, + refresh_checks, + refresh_entrances, + refresh_hints, +}; diff --git a/tunictracker/tracker/static/tracker/assets/initialize.js b/tunictracker/tracker/static/tracker/assets/initialize.js new file mode 100644 index 0000000..e69de29 diff --git a/tunictracker/tracker/static/tracker/assets/main.js b/tunictracker/tracker/static/tracker/assets/main.js index 2cc9d55..8d29460 100644 --- a/tunictracker/tracker/static/tracker/assets/main.js +++ b/tunictracker/tracker/static/tracker/assets/main.js @@ -1,4 +1,5 @@ import translate from "./translate-hints.js"; +import update from "./fetch-updates.js"; // Global state for overview var current_scene = ""; @@ -11,7 +12,7 @@ var total_checks = 0; var total_entrances = 0; // Global state internal -var server_address = "localhost:51111"; +var server_address = "localhost:51111/"; var cross_codes = {}; var can_access_api_server = true; @@ -31,280 +32,57 @@ window.onload = async () => { ); }; +async function get_updated_server_address() { + fetch(`${document.URL}get/address`) + .then( + (response) => response.json(), + (error) => { + console.log("Are you sure the front end is up?"); + } + ) + .then( + (data) => { + const parsed_data = JSON.parse(JSON.stringify(data)); + server_address = parsed_data["listen_address"]; + }, + (error) => { + console.log(error); + } + ); +} + // A function to call when the page loads to ensure that we have the correct data when we connect to the server. async function initialize_elements() { // Grab all updates from the backend. - let overview = await refresh_overview(); + let overview = await update.refresh_overview(server_address); if (!overview.overview) { return; } - const checks = await refresh_checks(); - const entrances = await refresh_entrances(); - const hints = await refresh_hints(); + const checks = await update.refresh_checks(server_address); + const entrances = await update.refresh_entrances(server_address); + const hints = await update.refresh_hints(server_address); overview = overview.overview; - // Initialize overview data. + // Initialize global state current_checks = checks.collected; current_entrances = entrances.found; total_checks = checks.total; total_entrances = entrances.total; - update_overview(overview, true); + await update_overview(overview, false); // Initialize breakdown list let breakdown_list = document .getElementById("breakdown-list") .cloneNode(true); - let breakdown_list_item = breakdown_list.firstElementChild.cloneNode(true); - breakdown_list.innerHTML = ""; - breakdown_list.appendChild(breakdown_list_item.cloneNode(true)); - breakdown_list_item.classList.remove("hidden"); - - // Initialize summary list let summary_list = document.getElementById("summary-list").cloneNode(true); - let summary_list_item = - summary_list.firstElementChild.firstElementChild.cloneNode(true); - summary_list.firstElementChild.innerHTML = ""; - summary_list.firstElementChild.appendChild(summary_list_item.cloneNode(true)); - summary_list_item.classList.remove("hidden"); - // Initialize codes list - let codes_list = document.getElementById("codes-list").cloneNode(true); - let codes_list_item = codes_list.firstElementChild.cloneNode(true); - codes_list.innerHTML = ""; - codes_list.appendChild(codes_list_item.cloneNode(true)); - codes_list_item.classList.remove("hidden"); - - // Initialize hints list - let hints_list = document.getElementById("hints-list").cloneNode(true); - let hints_list_item = hints_list.firstElementChild.cloneNode(true); - hints_list.innerHTML = ""; - hints_list.appendChild(hints_list_item.cloneNode(true)); - hints_list_item.classList.remove("hidden"); - - // Populate the breakdown list - Object.keys(checks.scenes).forEach((scene) => { - // One-time update the title for each scene. - if (current_scene == scene) { - breakdown_list_item.dataset.current = "true"; - breakdown_list_item.classList.remove("hidden", "order-last"); - breakdown_list_item.classList.add("order-first"); - } else { - breakdown_list_item.dataset.current = "false"; - breakdown_list_item.classList.add("hidden", "order-last"); - breakdown_list_item.classList.remove("order-first"); - } - breakdown_list_item.dataset.breakdownScene = scene; - breakdown_list_item.querySelector(".breakdown-block-title").textContent = - scene; - - // Populate checks data - breakdown_list_item.querySelector( - ".breakdown-block-checks-title" - ).textContent = `Checks: ${checks.scenes[scene].collected}/${checks.scenes[scene].total}/(${checks.scenes[scene].remaining} left)`; - breakdown_list_item.querySelector( - ".breakdown-block-checks-title" - ).dataset.checksCollected = checks.scenes[scene].collected; - breakdown_list_item.querySelector( - ".breakdown-block-checks-title" - ).dataset.checksRemaining = checks.scenes[scene].remaining; - breakdown_list_item.querySelector( - ".breakdown-block-checks-title" - ).dataset.checksTotal = checks.scenes[scene].total; - - // Populate entrances data - breakdown_list_item.querySelector( - ".breakdown-block-entrances-title" - ).textContent = `Entrances: ${entrances.scenes[scene].found}/${entrances.scenes[scene].total}/(${entrances.scenes[scene].remaining} left)`; - breakdown_list_item.querySelector( - ".breakdown-block-entrances-title" - ).dataset.entrancesFound = entrances.scenes[scene].found; - breakdown_list_item.querySelector( - ".breakdown-block-entrances-title" - ).dataset.entrancesRemaining = entrances.scenes[scene].remaining; - breakdown_list_item.querySelector( - ".breakdown-block-entrances-title" - ).dataset.entrancesTotal = entrances.scenes[scene].total; - - // Initialize checks list - let new_breakdown_block_checks_list = breakdown_list_item - .querySelector(".breakdown-block-checks-list") - .cloneNode(true); - let new_breakdown_block_checks_list_item = - new_breakdown_block_checks_list.firstElementChild.cloneNode(true); - new_breakdown_block_checks_list.innerHTML = ""; - new_breakdown_block_checks_list.appendChild( - new_breakdown_block_checks_list_item.cloneNode(true) - ); - new_breakdown_block_checks_list_item.classList.remove("hidden"); - - // Initialize entrances list - let new_breakdown_block_entrances_list = breakdown_list_item - .querySelector(".breakdown-block-entrances-list") - .cloneNode(true); - let new_breakdown_block_entrances_list_item = - new_breakdown_block_entrances_list.firstElementChild.cloneNode(true); - new_breakdown_block_entrances_list.innerHTML = ""; - new_breakdown_block_entrances_list.appendChild( - new_breakdown_block_entrances_list_item.cloneNode(true) - ); - new_breakdown_block_entrances_list_item.classList.remove("hidden"); - - // Initialize mapped list - let new_breakdown_block_mapped_list = breakdown_list_item - .querySelector(".breakdown-block-mapped-list") - .cloneNode(true); - let new_breakdown_block_mapped_list_item = - new_breakdown_block_mapped_list.firstElementChild.cloneNode(true); - new_breakdown_block_mapped_list.innerHTML = ""; - new_breakdown_block_mapped_list.appendChild( - new_breakdown_block_mapped_list_item.cloneNode(true) - ); - new_breakdown_block_mapped_list_item.classList.remove("hidden"); - - // Create checks list - Object.keys(checks.scenes[scene].checks).forEach((check) => { - new_breakdown_block_checks_list_item.textContent = `❌ ${check}`; - if (!checks.scenes[scene].checks[check].name) { - new_breakdown_block_checks_list.appendChild( - new_breakdown_block_checks_list_item.cloneNode(true) - ); - } - }); - - // Create entrances list and mapped list - Object.keys(entrances.scenes[scene].doors).forEach((entrance) => { - if (!entrances.scenes[scene].doors[entrance].door) { - new_breakdown_block_entrances_list_item.textContent = `❌ ${entrance}`; - new_breakdown_block_entrances_list.appendChild( - new_breakdown_block_entrances_list_item.cloneNode(true) - ); - } else { - new_breakdown_block_mapped_list_item.dataset.scene = - entrances.scenes[scene].doors[entrance].scene; - new_breakdown_block_mapped_list_item.textContent = `✔️ ${entrance} -> ${entrances.scenes[scene].doors[entrance].door}`; - new_breakdown_block_mapped_list.appendChild( - new_breakdown_block_mapped_list_item.cloneNode(true) - ); - } - }); - - // Replace lists in breakdown - breakdown_list_item - .querySelector(".breakdown-block-checks-list") - .replaceWith(new_breakdown_block_checks_list.cloneNode(true)); - breakdown_list_item - .querySelector(".breakdown-block-entrances-list") - .replaceWith(new_breakdown_block_entrances_list.cloneNode(true)); - breakdown_list_item - .querySelector(".breakdown-block-mapped-list") - .replaceWith(new_breakdown_block_mapped_list.cloneNode(true)); - - // Add breakdown to list - breakdown_list.appendChild(breakdown_list_item.cloneNode(true)); - }); - - // Populate the summary list - for (const scene of Object.keys(checks.scenes)) { - // One-time update data for scene - summary_list_item.dataset.scene = scene; - summary_list_item.querySelector(".summary-title").textContent = scene; - - // Update checks and entrances - summary_list_item.dataset.checksCollected = checks.scenes[scene].collected; - summary_list_item.dataset.checksRemaining = checks.scenes[scene].remaining; - summary_list_item.dataset.checksTotal = checks.scenes[scene].total; - summary_list_item.querySelector( - ".summary-checks" - ).textContent = `Checks: ${checks.scenes[scene].collected}/${checks.scenes[scene].total} (${checks.scenes[scene].total})`; - summary_list_item.dataset.entrancesFound = entrances.scenes[scene].found; - summary_list_item.dataset.entrancesRemaining = - entrances.scenes[scene].remaining; - summary_list_item.dataset.entrancesTotal = entrances.scenes[scene].total; - summary_list_item.querySelector( - ".summary-entrances" - ).textContent = `Entrances: ${entrances.scenes[scene].found}/${entrances.scenes[scene].total} (${entrances.scenes[scene].total})`; - - // Hide summaries for scenes with no entrances - if (entrances.scenes[scene].total <= 0) { - summary_list_item.classList.add("hidden"); - } else { - summary_list_item.classList.remove("hidden"); - } - - // Apply color coding and add summary to list - summary_list_item.firstElementChild.replaceWith( - await apply_summary_colors({ - element: summary_list_item.firstElementChild.cloneNode(true), - checks_collected: checks.scenes[scene].collected, - checks_total: checks.scenes[scene].total, - entrances_found: entrances.scenes[scene].found, - entrances_total: entrances.scenes[scene].total, - }) - ); - summary_list.firstElementChild.appendChild( - summary_list_item.cloneNode(true) - ); - } - - // Populate the codes list - Object.keys(overview.codes) - .sort((i, j) => { - return overview.codes[i].Distance - overview.codes[j].Distance; - }) - .forEach((codename, index) => { - // One-time update data for code - codes_list_item.dataset.codename = codename; - codes_list_item.dataset.order = index; - codes_list_item.querySelector(".codes-list-item-title").textContent = - codename; - - // Update code - if (overview.codes[codename].Global) { - codes_list_item.querySelector(".codes-list-item-code").textContent = - cross_codes["Global"][codename] - .replace(/U/g, "⬆️") - .replace(/R/g, "➡️") - .replace(/D/g, "⬇️") - .replace(/L/g, "⬅️"); - } else { - codes_list_item.querySelector(".codes-list-item-code").textContent = - cross_codes[overview.scene][codename] - .replace(/U/g, "⬆️") - .replace(/R/g, "➡️") - .replace(/D/g, "⬇️") - .replace(/L/g, "⬅️"); - } - - // Add code to list - codes_list.appendChild(codes_list_item.cloneNode(true)); - }); - Object.keys(cross_codes.Default).forEach((codename) => { - // One-time update data for default code - codes_list_item.dataset.codename = codename; - codes_list_item.dataset.order = "last"; - codes_list_item.querySelector(".codes-list-item-title").textContent = - codename; - - // Update code - codes_list_item.querySelector(".codes-list-item-code").textContent = - cross_codes.Default[codename] - .replace(/U/g, "⬆️") - .replace(/R/g, "➡️") - .replace(/D/g, "⬇️") - .replace(/L/g, "⬅️"); - - // Add code to list - codes_list.appendChild(codes_list_item.cloneNode(true)); - }); - - // Populate the hints list - Object.keys(hints).forEach((hint) => { - // Update hint - hints_list_item.firstElementChild.textContent = hints[hint]; - - // Add hint to list - hints_list.appendChild(hints_list_item.cloneNode(true)); - }); + // Initialize the breakdown and summary lists + breakdown_list = await initialize_breakdown_list( + breakdown_list, + checks, + entrances + ); + summary_list = await initialize_summary_list(summary_list, checks, entrances); // Replace breakdown list document @@ -316,21 +94,16 @@ async function initialize_elements() { .getElementById("summary-list") .replaceWith(summary_list.cloneNode(true)); - // Replace codes list - document.getElementById("codes-list").replaceWith(codes_list.cloneNode(true)); + // Update codes list + update_codes(overview.codes); - // Replace hints list - document.getElementById("hints-list").replaceWith(hints_list.cloneNode(true)); - - // update_if_changes(overview); - // update_checks(checks); - // update_entrances(entrances); - // update_hints(hints); + // Update hints list + update_hints(hints); return; } async function refresh_elements() { - const response = await refresh_overview(); + const response = await update.refresh_overview(server_address); if (response.overview) { if (!can_access_api_server) { @@ -338,7 +111,7 @@ async function refresh_elements() { can_access_api_server = true; await initialize_elements(); } - update_if_changes(response.overview); + await update_if_changes(response.overview); } else { if (can_access_api_server) { console.log("Could not access the API server."); @@ -370,7 +143,7 @@ async function update_if_changes(overview) { changed_entrances, changed_hints ); - update_overview(overview, changed_scene); + await update_overview(overview, changed_scene); } await update_codes(overview.codes); } @@ -383,78 +156,30 @@ async function perform_updates( ) { if (changed_seed) { // TODO: this at some point *needs* to initialize all the data when the seed changes, instead of relying on the initial HTML. - update_checks(await refresh_checks()); - update_entrances(await refresh_entrances()); - update_hints(await refresh_hints()); + // update_checks(await update.refresh_checks(server_address)); + // update_entrances(await update.refresh_entrances(server_address)); + // update_hints(await update.refresh_hints(server_address)); + initialize_elements(); console.log(`Seed changed to: ${current_seed}`); } else { if (changed_checks) { - refresh_checks().then((data) => update_checks(data)); + update.refresh_checks(server_address).then((data) => { + update_breakdown_list(data, { entrances: false, checks: true }); + update_summary_list(data, { entrances: false, checks: true }); + }); } if (changed_entrances) { - refresh_entrances().then((data) => update_entrances(data)); + update.refresh_entrances(server_address).then((data) => { + update_breakdown_list(data, { entrances: true, checks: false }); + update_summary_list(data, { entrances: true, checks: false }); + }); } if (changed_hints) { - refresh_hints().then((data) => update_hints(data)); + update.refresh_hints(server_address).then((data) => update_hints(data)); } } } -async function get_updated_server_address() { - fetch(`${document.URL}get/address`) - .then( - (response) => response.json(), - (error) => { - console.log("Are you sure the front end is up?"); - } - ) - .then( - (data) => { - const parsed_data = JSON.parse(JSON.stringify(data)); - server_address = parsed_data["listen_address"]; - // if (parsed_data["backend_filepath_updated"]) { - // get_updated_filepath(); - // } - }, - (error) => { - console.log(error); - } - ); -} - -async function refresh_overview() { - try { - const response = await fetch(`${server_address}overview`); - if (response.ok) { - const data = await response.json(); - if (!("error" in data)) { - return { overview: data, error: null }; - } - } - return { overview: null, error: null }; - } catch (e) { - return { overview: null, error: e }; - } -} - -async function refresh_hints() { - const response = await fetch(`${server_address}hints`); - const data = await response.json(); - return data; -} - -async function refresh_checks() { - const response = await fetch(`${server_address}items`); - const data = await response.json(); - return data; -} - -async function refresh_entrances() { - const response = await fetch(`${server_address}doors`); - const data = await response.json(); - return data; -} - async function update_overview(overview, changed_scene) { let overview_checks_title = document .getElementById("overview-totals") @@ -471,6 +196,7 @@ async function update_overview(overview, changed_scene) { overview.entrances * 2 }/${total_entrances} (${total_entrances - overview.entrances * 2} left)`; + // Set global state current_seed = overview.seed; current_scene = overview.scene; current_checks = overview.items; @@ -486,9 +212,6 @@ async function update_codes(codes) { // Codes that are always active const default_cross_codes = cross_codes.Default; - // One time codes independent of scene - const global_cross_codes = cross_codes.Global; - let new_cross_codes_block_list = document .getElementById("codes-list") .cloneNode(true); @@ -620,7 +343,6 @@ async function apply_summary_colors(summary) { "text-highlight-empty-text", "text-highlight-undiscovered-text" ); - if (summary.checks_total > 0 && summary.entrances_total > 0) { if ( summary.checks_collected == summary.checks_total && @@ -671,222 +393,473 @@ async function apply_summary_colors(summary) { return summary.element; } -async function update_summary(updates) { - if ("checks" in updates) { - const summary_checks = document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-checks"); - summary_checks.textContent = `Checks: ${updates.checks.collected}/${summary_checks.dataset.checksTotal} (${updates.checks.remaining})`; - summary_checks.dataset.checksUndiscovered = updates.checks.collected; - apply_summary_colors({ - element: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`).firstElementChild, - checks_collected: updates.checks.collected, - checks_total: summary_checks.dataset.checksTotal, - entrances_found: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-entrances").dataset.entrancesUndiscovered, - entrances_total: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-entrances").dataset.entrancesTotal, - }); - } else if ("entrances" in updates) { - const summary_entrances = document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-entrances"); - summary_entrances.textContent = `Entrances: ${updates.entrances.found}/${summary_entrances.dataset.entrancesTotal} (${updates.entrances.remaining})`; - summary_entrances.dataset.entrancesUndiscovered = updates.entrances.found; - apply_summary_colors({ - element: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`).firstElementChild, - checks_collected: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-checks").dataset.checksUndiscovered, - checks_total: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-checks").dataset.checksTotal, - entrances_found: updates.entrances.found, - entrances_total: summary_entrances.dataset.entrancesTotal, - }); +async function initialize_breakdown_list(breakdown_list, checks, entrances) { + // Initialize breakdown list + let breakdown_list_item = breakdown_list.firstElementChild.cloneNode(true); + breakdown_list.innerHTML = ""; + breakdown_list.appendChild(breakdown_list_item.cloneNode(true)); + breakdown_list_item.classList.remove("hidden"); + + for (const scene of Object.keys(checks.scenes)) { + // Initialize breakdown element + breakdown_list_item = await initialize_breakdown( + breakdown_list_item, + scene, + checks.scenes[scene], + entrances.scenes[scene] + ); + + // Add breakdown element to breakdown list + breakdown_list.appendChild(breakdown_list_item.cloneNode(true)); + } + + // Return initialized breakdown list + return breakdown_list; +} + +async function initialize_breakdown( + breakdown_element, + scene, + checks, + entrances +) { + // Initialize breakdown data + breakdown_element.dataset.scene = scene; + breakdown_element.querySelector(".breakdown-block-title").textContent = scene; + + // Populate breakdown lists + const breakdown_checks_list = + breakdown_element.querySelector(".breakdown-checks"); + const breakdown_entrances_list = breakdown_element.querySelector( + ".breakdown-entrances" + ); + const breakdown_mapped_list = breakdown_element.querySelector( + ".breakdown-block-mapped-list" + ); + const new_breakdown_checks_list = await initialize_checks_list( + breakdown_checks_list, + checks + ); + const new_entrances_lists = await initialize_entrances_list( + breakdown_entrances_list, + breakdown_mapped_list, + entrances + ); + + // Replace checks, entrances, and mapped lists + breakdown_element + .querySelector(".breakdown-checks") + .replaceWith(new_breakdown_checks_list); + breakdown_element + .querySelector(".breakdown-entrances") + .replaceWith(new_entrances_lists.entrances_list); + breakdown_element + .querySelector(".breakdown-block-mapped-list") + .replaceWith(new_entrances_lists.mapped_list); + + // Hide breakdowns that aren't for the current scene + if (current_scene == scene) { + breakdown_element.classList.add("order-first"); + breakdown_element.classList.remove("hidden", "order-last"); + breakdown_element.dataset.current = true; } else { - console.log("What the heck is this"); + breakdown_element.classList.add("hidden", "order-last"); + breakdown_element.classList.remove("order-first"); + breakdown_element.dataset.current = false; + } + + // Return initialized breakdown + return breakdown_element; +} + +async function initialize_checks_list(checks_list, checks) { + // Initialize new checks list for the breakdown + let new_breakdown_block_checks_list = checks_list + .querySelector(".breakdown-block-checks-list") + .cloneNode(true); + let breakdown_block_checks_list_item = + new_breakdown_block_checks_list.firstElementChild.cloneNode(true); + + // Clear new checks list and append blank entry + new_breakdown_block_checks_list.innerHTML = ""; + new_breakdown_block_checks_list.appendChild( + breakdown_block_checks_list_item.cloneNode(true) + ); + breakdown_block_checks_list_item.classList.remove("hidden"); + + // Update the check totals + checks_list.querySelector( + ".breakdown-block-checks-title" + ).textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining} left)`; + checks_list.querySelector( + ".breakdown-block-checks-title" + ).dataset.checksCollected = checks.collected; + checks_list.querySelector( + ".breakdown-block-checks-title" + ).dataset.checksRemaining = checks.remaining; + checks_list.querySelector( + ".breakdown-block-checks-title" + ).dataset.checksTotal = checks.total; + + // Populate new checks list + Object.keys(checks.checks).forEach((check) => { + if (checks.checks[check].name == "") { + breakdown_block_checks_list_item.classList.remove("hidden"); + } else { + breakdown_block_checks_list_item.classList.add("hidden"); + } + breakdown_block_checks_list_item.textContent = `❌ ${check}`; + breakdown_block_checks_list_item.dataset.check = check; + new_breakdown_block_checks_list.appendChild( + breakdown_block_checks_list_item.cloneNode(true) + ); + }); + + // Replace checks list + checks_list + .querySelector(".breakdown-block-checks-list") + .replaceWith(new_breakdown_block_checks_list); + + return checks_list; +} + +async function initialize_entrances_list( + entrances_list, + mapped_list, + entrances +) { + // Create new entrances and mapped lists and append empty entrance and mapped element + let new_breakdown_block_entrances_list = entrances_list + .querySelector(".breakdown-block-entrances-list") + .cloneNode(true); + let breakdown_block_entrances_list_item = + new_breakdown_block_entrances_list.firstElementChild.cloneNode(true); + new_breakdown_block_entrances_list.innerHTML = ""; + new_breakdown_block_entrances_list.appendChild( + breakdown_block_entrances_list_item.cloneNode(true) + ); + breakdown_block_entrances_list_item.classList.remove("hidden"); + + let new_breakdown_block_mapped_list = mapped_list.cloneNode(true); + let breakdown_block_mapped_list_item = + new_breakdown_block_mapped_list.firstElementChild.cloneNode(true); + new_breakdown_block_mapped_list.innerHTML = ""; + new_breakdown_block_mapped_list.appendChild( + breakdown_block_mapped_list_item.cloneNode(true) + ); + breakdown_block_mapped_list_item.classList.remove("hidden"); + + // Update the entrance totals + entrances_list.querySelector( + ".breakdown-block-entrances-title" + ).textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining} left)`; + entrances_list.querySelector( + ".breakdown-block-entrances-title" + ).dataset.entrancesFound = entrances.found; + entrances_list.querySelector( + ".breakdown-block-entrances-title" + ).dataset.entrancesRemaining = entrances.remaining; + entrances_list.querySelector( + ".breakdown-block-entrances-title" + ).dataset.entrancesTotal = entrances.total; + + // Populate new entrances and mapped lists + Object.keys(entrances.doors).forEach((entrance) => { + breakdown_block_entrances_list_item.dataset.entrance = entrance; + breakdown_block_entrances_list_item.textContent = `❌ ${entrance}`; + breakdown_block_mapped_list_item.dataset.mapped = entrance; + if (entrances.doors[entrance].door == "") { + breakdown_block_entrances_list_item.classList.remove("hidden"); + new_breakdown_block_entrances_list.appendChild( + breakdown_block_entrances_list_item.cloneNode(true) + ); + breakdown_block_mapped_list_item.textContent = `✔️ ${entrance} -> `; + breakdown_block_mapped_list_item.dataset.scene = ""; + breakdown_block_mapped_list_item.classList.add("hidden"); + new_breakdown_block_mapped_list.appendChild( + breakdown_block_mapped_list_item.cloneNode(true) + ); + } else { + breakdown_block_entrances_list_item.classList.add("hidden"); + new_breakdown_block_entrances_list.appendChild( + breakdown_block_entrances_list_item.cloneNode(true) + ); + breakdown_block_mapped_list_item.textContent = `✔️ ${entrance} -> ${entrances.doors[entrance].door}`; + breakdown_block_mapped_list_item.dataset.scene = + entrances.doors[entrance].scene; + breakdown_block_mapped_list_item.classList.remove("hidden"); + new_breakdown_block_mapped_list.appendChild( + breakdown_block_mapped_list_item.cloneNode(true) + ); + } + }); + + entrances_list + .querySelector(".breakdown-block-entrances-list") + .replaceWith(new_breakdown_block_entrances_list); + + // Return entrance and mapped list elements + return { + entrances_list: entrances_list, + mapped_list: new_breakdown_block_mapped_list, + }; +} + +async function initialize_summary_list(summary_list, checks, entrances) { + // Initialize summary list + let summary_list_item = + summary_list.firstElementChild.firstElementChild.cloneNode(true); + summary_list.firstElementChild.innerHTML = ""; + summary_list.firstElementChild.appendChild(summary_list_item.cloneNode(true)); + summary_list_item.classList.remove("hidden"); + + for (const scene of Object.keys(checks.scenes)) { + // Initialize summary element + summary_list_item = await initialize_summary( + summary_list_item, + scene, + checks.scenes[scene], + entrances.scenes[scene] + ); + + // Add summary element to summary list + summary_list.firstElementChild.appendChild( + summary_list_item.cloneNode(true) + ); + } + + // Return initialized summary list + return summary_list; +} + +async function initialize_summary(summary_element, scene, checks, entrances) { + // Initialize summary data + summary_element.dataset.scene = scene; + summary_element.querySelector(".summary-title").textContent = scene; + + const summary_checks = summary_element.querySelector(".summary-checks"); + summary_checks.dataset.checksCollected = checks.collected; + summary_checks.dataset.checksRemaining = checks.remaining; + summary_checks.dataset.checksTotal = checks.total; + summary_checks.textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining})`; + + const summary_entrances = summary_element.querySelector(".summary-entrances"); + summary_entrances.dataset.entrancesFound = entrances.found; + summary_entrances.dataset.entrancesRemaining = entrances.remaining; + summary_entrances.dataset.entrancesTotal = entrances.total; + summary_entrances.textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining})`; + + // Apply summary color coding + summary_element.firstElementChild.replaceWith( + await apply_summary_colors({ + element: summary_element.firstElementChild, + checks_collected: checks.collected, + checks_total: checks.total, + entrances_found: entrances.found, + entrances_total: entrances.total, + }) + ); + + // Hide summaries for scenes with no entrances and the current scene + if (entrances.total <= 0 || current_scene == scene) { + summary_element.dataset.current = current_scene == scene; + summary_element.classList.add("hidden"); + } else { + summary_element.dataset.current = false; + summary_element.classList.remove("hidden"); + } + + // Return initialized summary + return summary_element; +} + +async function update_summary(scene, checks, entrances) { + // Grab summary checks and entrances + const summary_checks = document.querySelector( + `.summary[data-scene="${scene}"] .summary-checks` + ); + const summary_entrances = document.querySelector( + `.summary[data-scene="${scene}"] .summary-entrances` + ); + + // Make variables for whether checks or entrances updated + const checks_changed = + summary_checks.dataset.checksCollected != checks.collected; + const entrances_changed = + summary_entrances.dataset.entrancesFound != entrances.found; + + // Check for changes, and if so, update + if (checks_changed) { + summary_checks.dataset.checksCollected = checks.collected; + summary_checks.dataset.checksRemaining = checks.remaining; + summary_checks.dataset.checksTotal = checks.total; + summary_checks.textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining})`; + } + if (entrances_changed) { + summary_entrances.dataset.entrancesFound = entrances.found; + summary_entrances.dataset.entrancesRemaining = entrances.remaining; + summary_entrances.dataset.entrancesTotal = entrances.total; + summary_entrances.textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining})`; + } + + // Apply color coding + if (checks_changed || entrances_changed) { + document + .querySelector(`.summary[data-scene="${scene}"]`) + .firstElementChild.replaceWith( + await apply_summary_colors({ + element: document.querySelector(`.summary[data-scene="${scene}"]`) + .firstElementChild, + checks_collected: checks.collected, + checks_total: checks.total, + entrances_found: entrances.found, + entrances_total: entrances.total, + }) + ); } } -async function update_checks(checks) { - total_checks = checks.total; - - let new_breakdown_block_checks_list = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-checks-list") - .cloneNode(true); - let breakdown_block_checks_list_item = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-checks-list") - .firstElementChild.cloneNode(true); - - // Create new lists with updated data. - let breakdown_list = Array.from( - document.getElementById("breakdown-list").children - ); - breakdown_list.forEach((scene) => { - if (scene.querySelector(".breakdown-block-title").textContent) { - // Create variables for element pointers. - let scene_title = scene.querySelector( - ".breakdown-block-title" - ).textContent; - if (scene_title) { - const scene_checks = parseInt( - scene.querySelector(".breakdown-block-checks-title").dataset.checks - ); - if (scene_checks != checks.scenes[scene_title].collected) { - scene.querySelector(".breakdown-block-checks-title").dataset.checks = - scene_checks; - update_summary({ - scene: scene_title, - checks: { - collected: checks.scenes[scene_title].collected, - remaining: checks.scenes[scene_title].remaining, - }, - }); - scene.querySelector( - ".breakdown-block-checks-title" - ).textContent = `Checks: ${checks.scenes[scene_title].collected}/${checks.scenes[scene_title].total} (${checks.scenes[scene_title].remaining})`; - - new_breakdown_block_checks_list.innerHTML = ""; - new_breakdown_block_checks_list.appendChild( - breakdown_block_checks_list_item.cloneNode(true) - ); - breakdown_block_checks_list_item.classList.remove("hidden"); - Object.keys(checks.scenes[scene_title].checks).forEach((check) => { - if (checks.scenes[scene_title].checks[check].name == "") { - breakdown_block_checks_list_item.textContent = `❌ ${check}`; - new_breakdown_block_checks_list.appendChild( - breakdown_block_checks_list_item.cloneNode(true) - ); - } - }); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-checks-list").innerHTML = ""; - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-checks-list") - .replaceWith(new_breakdown_block_checks_list.cloneNode(true)); - } - } - } - }); - // current_checks = checks.collected; +async function update_summary_list(data, changed) { + // Check whether entrances or checks changed + if (changed.checks) { + Object.keys(data.scenes).forEach((scene) => + update_summary(scene, data.scenes[scene], { + found: document.querySelector( + `.summary[data-scene="${scene}"] .summary-entrances` + ).dataset.entrancesFound, + remaining: document.querySelector( + `.summary[data-scene="${scene}"] .summary-entrances` + ).dataset.entrancesRemaining, + total: document.querySelector( + `.summary[data-scene="${scene}"] .summary-entrances` + ).dataset.entrancesTotal, + }) + ); + } else if (changed.entrances) { + Object.keys(data.scenes).forEach((scene) => + update_summary( + scene, + { + collected: document.querySelector( + `.summary[data-scene="${scene}"] .summary-checks` + ).dataset.checksCollected, + remaining: document.querySelector( + `.summary[data-scene="${scene}"] .summary-checks` + ).dataset.checksRemaining, + total: document.querySelector( + `.summary[data-scene="${scene}"] .summary-checks` + ).dataset.checksTotal, + }, + data.scenes[scene] + ) + ); + } } -async function update_entrances(entrances) { - total_entrances = entrances.total; +async function update_breakdown_checks(scene, checks) { + // Update the check totals + document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` + ).textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining} left)`; + document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` + ).dataset.checksCollected = checks.collected; + document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` + ).dataset.checksRemaining = checks.remaining; + document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` + ).dataset.checksTotal = checks.total; - let new_breakdown_block_entrances_list = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-entrances-list") - .cloneNode(true); - let breakdown_block_entrances_list_item = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-entrances-list") - .firstElementChild.cloneNode(true); - let new_breakdown_block_mapped_list = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-mapped-list") - .cloneNode(true); - let breakdown_block_mapped_list_item = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-mapped-list") - .firstElementChild.cloneNode(true); - // Create new lists with updated data. - let breakdown_list = Array.from( - document.getElementById("breakdown-list").children - ); - breakdown_list.forEach((scene) => { - if (scene.querySelector(".breakdown-block-title").textContent) { - // Create variables for element pointers. - let scene_title = scene.querySelector( - ".breakdown-block-title" - ).textContent; - if (scene_title) { - const scene_entrances = parseInt( - scene.querySelector(".breakdown-block-entrances-title").dataset - .entrances - ); - if (scene_entrances != entrances.scenes[scene_title].found) { - scene.querySelector( - ".breakdown-block-checks-title" - ).dataset.entrances = scene_entrances; - update_summary({ - scene: scene_title, - entrances: { - found: entrances.scenes[scene_title].found, - remaining: entrances.scenes[scene_title].remaining, - }, - }); - scene.querySelector( - ".breakdown-block-entrances-title" - ).textContent = `Entrances: ${entrances.scenes[scene_title].found}/${entrances.scenes[scene_title].total} (${entrances.scenes[scene_title].remaining})`; - - new_breakdown_block_entrances_list.innerHTML = ""; - new_breakdown_block_entrances_list.appendChild( - breakdown_block_entrances_list_item.cloneNode(true) - ); - breakdown_block_entrances_list_item.classList.remove("hidden"); - new_breakdown_block_mapped_list.innerHTML = ""; - breakdown_block_mapped_list_item.classList.remove("hidden"); - - Object.keys(entrances.scenes[scene_title].doors).forEach( - (entrance) => { - if (entrances.scenes[scene_title].doors[entrance].door == "") { - breakdown_block_entrances_list_item.textContent = `❌ ${entrance}`; - new_breakdown_block_entrances_list.appendChild( - breakdown_block_entrances_list_item.cloneNode(true) - ); - } else { - breakdown_block_mapped_list_item.textContent = `✔️ ${entrance} -> ${entrances.scenes[scene_title].doors[entrance].door}`; - breakdown_block_mapped_list_item.id = `${entrance}-mapped`; - breakdown_block_mapped_list_item.dataset.scene = - entrances.scenes[scene_title].doors[entrance].scene; - new_breakdown_block_mapped_list.appendChild( - breakdown_block_mapped_list_item.cloneNode(true) - ); - } - } - ); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-entrances-list").innerHTML = ""; - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-entrances-list") - .replaceWith(new_breakdown_block_entrances_list.cloneNode(true)); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-mapped-list").innerHTML = ""; - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-mapped-list") - .replaceWith(new_breakdown_block_mapped_list.cloneNode(true)); - } - } + // Hide collected checks + for (const check of Object.keys(checks.checks)) { + const check_item = document.querySelector( + `.breakdown[data-scene="${scene}"] [data-check="${check}"]` + ); + if (checks.checks[check].name == "") { + check_item.classList.remove("hidden"); + } else { + check_item.classList.add("hidden"); } - }); + } +} + +async function update_breakdown_entrances(scene, entrances) { + // Update the entrance totals + document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` + ).textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining} left)`; + document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` + ).dataset.entrancesFound = entrances.found; + document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` + ).dataset.entrancesRemaining = entrances.remaining; + document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` + ).dataset.entrancesTotal = entrances.total; + + // Map found entrances + for (const entrance of Object.keys(entrances.doors)) { + const entrance_item = document.querySelector( + `.breakdown[data-scene="${scene}"] [data-entrance="${entrance}"]` + ); + const mapped_item = document.querySelector( + `.breakdown[data-scene="${scene}"] [data-mapped="${entrance}"]` + ); + if (entrances.doors[entrance].door == "") { + entrance_item.classList.remove("hidden"); + mapped_item.classList.add("hidden"); + } else { + entrance_item.classList.add("hidden"); + mapped_item.classList.remove("hidden"); + mapped_item.dataset.scene = entrances.doors[entrance].scene; + mapped_item.textContent = `✔️ ${entrance} -> ${entrances.doors[entrance].scene}`; + } + } +} + +async function update_breakdown(scene, checks, entrances) { + // Grab breakdown checks and entrances + const breakdown_checks = document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` + ); + const breakdown_entrances = document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` + ); + + // Check for changes, and if so, update + if (breakdown_checks.dataset.checksCollected != checks.collected) { + update_breakdown_checks(scene, checks); + } + if (breakdown_entrances.dataset.entrancesFound != entrances.found) { + update_breakdown_entrances(scene, entrances); + } +} + +async function update_breakdown_list(data, changed) { + // Check whether entrances or checks changed + if (changed.checks) { + Object.keys(data.scenes).forEach((scene) => + update_breakdown(scene, data.scenes[scene], { + found: document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` + ).dataset.entrancesFound, + }) + ); + } else if (changed.entrances) { + Object.keys(data.scenes).forEach((scene) => + update_breakdown( + scene, + { + collected: document.querySelector( + `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` + ).dataset.checksCollected, + }, + data.scenes[scene] + ) + ); + } } async function update_hints(hints) { @@ -940,62 +913,31 @@ async function update_hints(hints) { } async function update_scene(scene) { - Array.from(document.getElementById("breakdown-list").children).forEach( - (breakdown) => { - const breakdown_title = breakdown.dataset.breakdownScene; - if (breakdown_title == scene) { - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene}"]`) - .classList.remove("hidden"); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene}"]`).dataset.current = - "true"; - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene}"]`) - .classList.remove("order-last"); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene}"]`) - .classList.add("order-first"); - } else { - document - .getElementById("breakdown-list") - .querySelector( - `[data-breakdown-scene="${breakdown_title}"]` - ).dataset.current = "false"; - - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${breakdown_title}"]`) - .classList.add("hidden"); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${breakdown_title}"]`) - .classList.add("order-last"); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${breakdown_title}"]`) - .classList.remove("order-first"); - } - } + // Grab current scene and updated scene breakdown/summary elements + const current_breakdown_old = document.querySelector( + `.breakdown[data-current="true"]` ); - Array.from( - document.getElementById("summary-list").firstElementChild.children - ).forEach((summary) => { - const summary_scene = summary.dataset.scene; - if (summary_scene == scene) { - document - .getElementById("summary-list") - .querySelector(`[data-scene="${scene}"]`) - .classList.add("hidden"); - } else if (!(summary_scene == "")) { - document - .getElementById("summary-list") - .querySelector(`[data-scene="${summary_scene}"]`) - .classList.remove("hidden"); - } - }); + const current_breakdown = document.querySelector( + `.breakdown[data-scene="${scene}"]` + ); + const current_summary_old = document.querySelector( + `.summary[data-current="true"]` + ); + const current_summary = document.querySelector( + `.summary[data-scene="${scene}"]` + ); + + // Update the current breakdown + current_breakdown_old.dataset.current = false; + current_breakdown_old.classList.add("hidden", "order-last"); + current_breakdown_old.classList.remove("order-first"); + current_breakdown.dataset.current = true; + current_breakdown.classList.add("order-first"); + current_breakdown.classList.remove("hidden", "order-last"); + + // Update the current summary + current_summary_old.dataset.current = false; + current_summary_old.classList.remove("hidden"); + current_summary.dataset.current = true; + current_summary.classList.add("hidden"); } diff --git a/tunictracker/tracker/templates/index.html b/tunictracker/tracker/templates/index.html index 2300a3d..9c6feaa 100644 --- a/tunictracker/tracker/templates/index.html +++ b/tunictracker/tracker/templates/index.html @@ -14,6 +14,7 @@ + diff --git a/tunictracker/tracker/templates/tracker/breakdown/block.html b/tunictracker/tracker/templates/tracker/breakdown/block.html index 02f1dfd..0f63991 100644 --- a/tunictracker/tracker/templates/tracker/breakdown/block.html +++ b/tunictracker/tracker/templates/tracker/breakdown/block.html @@ -1,10 +1,10 @@ -
+ data-scene="{{ scene_title }}">
{{ scene_title }}
-
+
@@ -24,7 +24,7 @@ {% endfor %}
-
+
@@ -49,7 +49,8 @@
+ onclick="open_breakdown(this)" + data-scene=""> {% for entrance_origin, entrance_destination in scene_data.entrances.doors.items %} {% if entrance_destination.door %}