From 1edf9e71f81f6eee3c76056c68a14c70f71c8bbd Mon Sep 17 00:00:00 2001 From: Zach Parks Date: Mon, 24 Jun 2024 17:42:29 -0500 Subject: [PATCH] preliminary loading functionality --- WebHostLib/options.py | 151 +++++------- WebHostLib/requirements.txt | 1 - WebHostLib/static/assets/options/player.js | 46 ++-- WebHostLib/static/assets/options/shared.js | 75 ++++++ WebHostLib/static/styles/options/player.css | 6 + .../static/styles/options/player.css.map | 2 +- WebHostLib/static/styles/options/shared.scss | 8 + WebHostLib/static/styles/options/weighted.css | 6 + .../static/styles/options/weighted.css.map | 2 +- WebHostLib/templates/options/player.html | 46 ---- .../templates/playerOptions/macros.html | 218 +++++++++--------- .../playerOptions/playerOptions.html | 3 +- 12 files changed, 297 insertions(+), 267 deletions(-) create mode 100644 WebHostLib/static/assets/options/shared.js delete mode 100644 WebHostLib/templates/options/player.html diff --git a/WebHostLib/options.py b/WebHostLib/options.py index e65bbdfd6a81..3a077a220eb8 100644 --- a/WebHostLib/options.py +++ b/WebHostLib/options.py @@ -1,29 +1,19 @@ import collections.abc -import json import os from textwrap import dedent -from typing import Dict, Type, Union +from typing import Dict, Union import yaml from docutils.core import publish_parts from flask import Response, redirect, render_template, request -from flask_wtf import FlaskForm -from wtforms import SelectField, StringField -from wtforms.fields.simple import HiddenField -from wtforms.validators import DataRequired import Options from Utils import get_file_safe_name, local_path -from worlds.AutoWorld import AutoWorldRegister, World +from worlds.AutoWorld import AutoWorldRegister from . import app, cache from .generate import get_meta -class PlayerOptionsForm(FlaskForm): - game = HiddenField() - name = StringField("Player Name", validators=[DataRequired()]) - - def create() -> None: target_folder = local_path("WebHostLib", "static", "generated") yaml_folder = os.path.join(target_folder, "configs") @@ -47,37 +37,68 @@ def render_options_page(template: str, world_name: str, is_complex: bool = False for group in world.web.option_groups: start_collapsed[group.name] = group.start_collapsed - # TODO: Testing - form = PlayerOptionsForm(prefix="meta") - return render_template( template, world_name=world_name, - # world=world, - # option_groups=Options.get_option_groups(world, visibility_level=visibility_flag), - # start_collapsed=start_collapsed, - # issubclass=issubclass, - # Options=Options, - form=form, + world=world, + option_groups=Options.get_option_groups(world, visibility_level=visibility_flag), + start_collapsed=start_collapsed, + issubclass=issubclass, + Options=Options, header_theme=f"header/{get_world_theme(world_name)}Header.html", ) -# Test -@app.route("/games//test", methods=["POST"]) -def test_action(world_name: str) -> Union[Response, str]: - return str(",".join([f"{k}: {v}" for k, v in request.form.items()])) - - -@app.route("/games///") -def test_action_2(world_name: str, option_name: str, start_index: int) -> Union[Response, str]: +@app.route("/games//options") +def test_action_2(world_name: str) -> dict: world = AutoWorldRegister.world_types[world_name] - option = world.options_dataclass.type_hints[option_name] - keys = list(option.valid_keys) - keys.sort() + options, presets = {}, {} + for option_name, option in world.options_dataclass.type_hints.items(): + if issubclass(option, Options.VerifyKeys): + if issubclass(option, Options.LocationSet) and option.verify_location_name: + keys = list(sorted(option.valid_keys) if option.valid_keys else sorted(world.location_names)) + elif issubclass(option, (Options.ItemSet, Options.ItemDict)) and option.verify_item_name: + keys = list(sorted(option.valid_keys) if option.valid_keys else sorted(world.item_names)) + else: + keys = list( + option.valid_keys + if isinstance(option.valid_keys, collections.abc.Sequence) + else sorted(option.valid_keys) + ) + options[option_name] = { + "default": list(getattr(option, "default", [])), + "valid_keys": keys, + } + elif issubclass(option, Options.NamedRange): + options[option_name] = { + "default": option.default, + "range_start": option.range_start, + "range_end": option.range_end, + "range_names": option.special_range_names, + } + elif issubclass(option, Options.Range): + options[option_name] = { + "default": option.default, + "range_start": option.range_start, + "range_end": option.range_end, + } + else: + options[option_name] = { + "default": option.name_lookup[option.default] if not isinstance(option.default, str) else option.default + } + + for preset_name, preset_options in world.web.options_presets.items(): + presets[preset_name] = {} + for option_name, value in preset_options.items(): + option = world.options_dataclass.type_hints[option_name] + if issubclass(option, Options.VerifyKeys): + presets[preset_name][option_name] = list(value) + elif issubclass(option, Options.Range): + presets[preset_name][option_name] = value + else: + presets[preset_name][option_name] = option.name_lookup[value] if not isinstance(value, str) else value - return json.dumps(keys[start_index:start_index+100]) - # return "" + return {"options": options, "presets": presets} def generate_game(options: Dict[str, Union[dict, str]]) -> Union[Response, str]: @@ -119,68 +140,11 @@ def filter_rst_to_html(text: str) -> str: )["body"] -@app.template_test("ordered") +@app.template_filter("ordered") def test_ordered(obj): return isinstance(obj, collections.abc.Sequence) -@app.route("/games//option-presets", methods=["GET"]) -@cache.cached() -def option_presets(game: str) -> Response: - world = AutoWorldRegister.world_types[game] - - presets = {} - for preset_name, preset in world.web.options_presets.items(): - presets[preset_name] = {} - for preset_option_name, preset_option in preset.items(): - if preset_option == "random": - presets[preset_name][preset_option_name] = preset_option - continue - - option = world.options_dataclass.type_hints[preset_option_name].from_any(preset_option) - if isinstance(option, Options.NamedRange) and isinstance(preset_option, str): - assert preset_option in option.special_range_names, ( - f"Invalid preset value '{preset_option}' for '{preset_option_name}' in '{preset_name}'. " - f"Expected {option.special_range_names.keys()} or {option.range_start}-{option.range_end}." - ) - - presets[preset_name][preset_option_name] = option.value - elif isinstance( - option, - ( - Options.Range, - Options.OptionSet, - Options.OptionList, - Options.ItemDict, - ), - ): - presets[preset_name][preset_option_name] = option.value - elif isinstance(preset_option, str): - # Ensure the option value is valid for Choice and Toggle options - assert option.name_lookup[option.value] == preset_option, ( - f"Invalid option value '{preset_option}' for '{preset_option_name}' in preset '{preset_name}'. " - f"Values must not be resolved to a different option via option.from_text (or an alias)." - ) - # Use the name of the option - presets[preset_name][preset_option_name] = option.current_key - else: - # Use the name of the option - presets[preset_name][preset_option_name] = option.current_key - - class SetEncoder(json.JSONEncoder): - def default(self, obj): - from collections.abc import Set - - if isinstance(obj, Set): - return list(obj) - return json.JSONEncoder.default(self, obj) - - json_data = json.dumps(presets, cls=SetEncoder) - response = Response(json_data) - response.headers["Content-Type"] = "application/json" - return response - - @app.route("/weighted-options") def weighted_options_old(): return redirect("games", 301) @@ -244,8 +208,7 @@ def generate_weighted_yaml(game: str): @app.route("/games//player-options") @cache.cached() def player_options(game: str): - print("DEBUG: " + str(app.debug)) - return render_options_page("options/player.html", game, is_complex=False) + return render_options_page("playerOptions/playerOptions.html", game, is_complex=False) # YAML generator for player-options diff --git a/WebHostLib/requirements.txt b/WebHostLib/requirements.txt index 531f8d5ad34a..3452c9d416db 100644 --- a/WebHostLib/requirements.txt +++ b/WebHostLib/requirements.txt @@ -5,7 +5,6 @@ waitress>=3.0.0 Flask-Caching>=2.3.0 Flask-Compress>=1.15 Flask-Limiter>=3.7.0 -Flask-WTF>=1.2.1 bokeh>=3.1.1; python_version <= '3.8' bokeh>=3.4.1; python_version >= '3.9' markupsafe>=2.1.5 diff --git a/WebHostLib/static/assets/options/player.js b/WebHostLib/static/assets/options/player.js index 921bb659784a..1e1d539c6db1 100644 --- a/WebHostLib/static/assets/options/player.js +++ b/WebHostLib/static/assets/options/player.js @@ -1,5 +1,6 @@ let game = ''; let presets = {}; +let options = {}; window.addEventListener('load', async () => { game = document.getElementById('player-options').getAttribute('data-game') @@ -9,7 +10,7 @@ window.addEventListener('load', async () => { // Fetch presets, if available. try { - await fetchPresets(); + await fetchOptions(); } catch (error) { console.error('Failed to fetch presets.', error); } @@ -205,24 +206,35 @@ const loadSettings = () => { * * @returns {Promise} */ -const fetchPresets = async () => { - const response = await fetch('option-presets'); - presets = await response.json(); - const presetSelect = document.getElementById('game-options-preset'); - - const game = document.getElementById('player-options').getAttribute('data-game'); - const presetToApply = localStorage.getItem(`${game}-preset`); - const playerName = localStorage.getItem(`${game}-player`); - if (presetToApply) { - localStorage.removeItem(`${game}-preset`); - presetSelect.value = presetToApply; - applyPreset(presetToApply); +const fetchOptions = async () => { + const response = await fetch('options'); + const data = await response.json(); + options = data.options; + presets = data.presets; + console.log(data); + + for (const optionListElement of document.querySelectorAll(".option-list")) { + const option = optionListElement.id.substring(0, optionListElement.id.indexOf("-container")); + options[option].loaded = 0; + + loadItems(option, optionListElement, 50); + createListObserver(optionListElement); } - if (playerName) { - document.getElementById('player-name').value = playerName; - localStorage.removeItem(`${game}-player`); - } + // const presetSelect = document.getElementById('game-options-preset'); + // const game = document.getElementById('player-options').getAttribute('data-game'); + // const presetToApply = localStorage.getItem(`${game}-preset`); + // const playerName = localStorage.getItem(`${game}-player`); + // // if (presetToApply) { + // // localStorage.removeItem(`${game}-preset`); + // // presetSelect.value = presetToApply; + // // applyPreset(presetToApply); + // // } + // + // if (playerName) { + // document.getElementById('player-name').value = playerName; + // localStorage.removeItem(`${game}-player`); + // } }; /** diff --git a/WebHostLib/static/assets/options/shared.js b/WebHostLib/static/assets/options/shared.js new file mode 100644 index 000000000000..18d8790f8e6a --- /dev/null +++ b/WebHostLib/static/assets/options/shared.js @@ -0,0 +1,75 @@ +/** + * Creates an event handler for scroll events to lazy load list elements and save them to memory. + * @param element {HTMLElement} + */ +function createListObserver(element) { + const option = element.id.substring(0, element.id.indexOf("-container")); + const observer = new IntersectionObserver((entries) => { + console.log("Called observer", option) + if (entries[0].intersectionRatio <= 0) { + return; + } + + observer.unobserve(document.getElementById(`${option}-eol`)); + loadItems(option, element, 50); + + const eol = document.getElementById(`${option}-eol`); + if (eol) { + observer.observe(eol); + } + }); + + const eol = document.getElementById(`${option}-eol`); + if (eol) { + observer.observe(eol); + } +} + +function loadItems(option, element, number) { + console.log(option, number, element); + /** @type {number} */ + const loaded = options[option]["loaded"]; + /** @type {number} */ + const length = options[option]["valid_keys"].length; + + // All items are already loaded, return. + if (loaded >= length) { + return; + } + + // Remove the "load-more" element. + if (element.children.length !== 0) { + element.children[element.children.length - 1].remove(); + } + + const max = Math.min(loaded + 50, length); + + /** @type {string[]} */ + const keys = options[option]["valid_keys"].slice(loaded, max); + + options[option]["loaded"] = max; + // TODO: If you see this in version control for the PR, reject it and dunk on me to do this properly. I only used + // this for testing and I better not have left it in. -Phar + let html = ""; + for (const value of keys) { + html += ` +
+ + +
+ `; + } + + if (options[option]["loaded"] < length) { + html += `
Loading...
`; + } + + element.innerHTML += html; + console.log(`Loaded: ${options[option]["loaded"]}`, `Children: ${element.children.length}`); +} diff --git a/WebHostLib/static/styles/options/player.css b/WebHostLib/static/styles/options/player.css index fa4798b7fca1..93383984e0a5 100644 --- a/WebHostLib/static/styles/options/player.css +++ b/WebHostLib/static/styles/options/player.css @@ -139,6 +139,12 @@ overflow-y: auto; resize: vertical; } +#player-options .option-list .loading, #weighted-options .option-list .loading { + padding: 1rem; + font-size: 1rem; + text-align: center; + align-self: center; +} #player-options .option-list .option-divider, #weighted-options .option-list .option-divider { width: 100%; height: 2px; diff --git a/WebHostLib/static/styles/options/player.css.map b/WebHostLib/static/styles/options/player.css.map index bc16f3d80a5b..df9908aad170 100644 --- a/WebHostLib/static/styles/options/player.css.map +++ b/WebHostLib/static/styles/options/player.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["player.scss","shared.scss"],"names":[],"mappings":"AAAQ;ACAR;EACI;;AAEA;EACI;;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;AAGJ;EACI;;AAEA;EACI;;AAIR;EACI;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;AAEA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;EACA;EACA;;;AAMhB;EAEQ;IACI;;EAGJ;IACI;;EAEA;IACI;IACA;;EAGJ;IACI;;EAGJ;IACI;IACA;;EAIR;IACI;IACA;;EAGI;IACI;;EAGJ;IACI;IACA;IACA;;;ADpPpB;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAIR;EACI;;AAGJ;EACI;;AAEA;EACI;;AAIR;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;;;AAIR;EACI;IACI;IACA;;EAEA;IACI;;EAEA;IACI;;EAIR;IACI","file":"player.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["player.scss","shared.scss"],"names":[],"mappings":"AAAQ;ACAR;EACI;;AAEA;EACI;;;AAMJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;AAGJ;EACI;;AAEA;EACI;;AAIR;EACI;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;AAEA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;EACA;EACA;;;AAMhB;EAEQ;IACI;;EAGJ;IACI;;EAEA;IACI;IACA;;EAGJ;IACI;;EAGJ;IACI;IACA;;EAIR;IACI;IACA;;EAGI;IACI;;EAGJ;IACI;IACA;IACA;;;AD5PpB;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAIR;EACI;;AAGJ;EACI;;AAEA;EACI;;AAIR;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;;;AAIR;EACI;IACI;IACA;;EAEA;IACI;;EAEA;IACI;;EAIR;IACI","file":"player.css"} \ No newline at end of file diff --git a/WebHostLib/static/styles/options/shared.scss b/WebHostLib/static/styles/options/shared.scss index cb79a94d9d91..a908e1c3d15d 100644 --- a/WebHostLib/static/styles/options/shared.scss +++ b/WebHostLib/static/styles/options/shared.scss @@ -6,6 +6,7 @@ } } + #player-options, #weighted-options { .alert { width: calc(100% - 1rem); @@ -162,6 +163,13 @@ overflow-y: auto; resize: vertical; + .loading { + padding: 1rem; + font-size: 1rem; + text-align: center; + align-self: center; + } + .option-divider { width: 100%; height: 2px; diff --git a/WebHostLib/static/styles/options/weighted.css b/WebHostLib/static/styles/options/weighted.css index 478b17eed62a..4b376cd6e95f 100644 --- a/WebHostLib/static/styles/options/weighted.css +++ b/WebHostLib/static/styles/options/weighted.css @@ -139,6 +139,12 @@ overflow-y: auto; resize: vertical; } +#player-options .option-list .loading, #weighted-options .option-list .loading { + padding: 1rem; + font-size: 1rem; + text-align: center; + align-self: center; +} #player-options .option-list .option-divider, #weighted-options .option-list .option-divider { width: 100%; height: 2px; diff --git a/WebHostLib/static/styles/options/weighted.css.map b/WebHostLib/static/styles/options/weighted.css.map index 148d151e56e7..93193c580296 100644 --- a/WebHostLib/static/styles/options/weighted.css.map +++ b/WebHostLib/static/styles/options/weighted.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["weighted.scss","shared.scss"],"names":[],"mappings":"AAAQ;ACAR;EACI;;AAEA;EACI;;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;AAGJ;EACI;;AAEA;EACI;;AAIR;EACI;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;AAEA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;EACA;EACA;;;AAMhB;EAEQ;IACI;;EAGJ;IACI;;EAEA;IACI;IACA;;EAGJ;IACI;;EAGJ;IACI;IACA;;EAIR;IACI;IACA;;EAGI;IACI;;EAGJ;IACI;IACA;IACA;;;ADpPpB;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGI;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAKZ;EACI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAMhB;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAEA;EACI;;AAIR;EACI;;AAGJ;EACI;;;AAIR;EACI;;;AAGJ;EAEQ;IACI;;EAGJ;IACI;;EAEA;IACI;IACA;;EAGJ;IACI;;EAEA;IACI;;EAGJ;IACI;IACA;;EAKZ;IACI","file":"weighted.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["weighted.scss","shared.scss"],"names":[],"mappings":"AAAQ;ACAR;EACI;;AAEA;EACI;;;AAMJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;;AAGJ;EACI;;AAEA;EACI;;AAIR;EACI;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;AAEA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;EACA;EACA;;;AAMhB;EAEQ;IACI;;EAGJ;IACI;;EAEA;IACI;IACA;;EAGJ;IACI;;EAGJ;IACI;IACA;;EAIR;IACI;IACA;;EAGI;IACI;;EAGJ;IACI;IACA;IACA;;;AD5PpB;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAGI;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAKZ;EACI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAMhB;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAEA;EACI;;AAIR;EACI;;AAGJ;EACI;;;AAIR;EACI;;;AAGJ;EAEQ;IACI;;EAGJ;IACI;;EAEA;IACI;IACA;;EAGJ;IACI;;EAEA;IACI;;EAGJ;IACI;IACA;;EAKZ;IACI","file":"weighted.css"} \ No newline at end of file diff --git a/WebHostLib/templates/options/player.html b/WebHostLib/templates/options/player.html deleted file mode 100644 index c2ce3f765ca2..000000000000 --- a/WebHostLib/templates/options/player.html +++ /dev/null @@ -1,46 +0,0 @@ -{%- extends "pageWrapper.html" -%} - -{%- block head -%} - {{ world_name }} Options - -{# #} - -{%- endblock -%} - -{%- block body -%} - {%- include header_theme -%} -
- -
- - -{%- endblock -%} diff --git a/WebHostLib/templates/playerOptions/macros.html b/WebHostLib/templates/playerOptions/macros.html index 14350c0d51b3..8f8e97d130a0 100644 --- a/WebHostLib/templates/playerOptions/macros.html +++ b/WebHostLib/templates/playerOptions/macros.html @@ -164,137 +164,143 @@ {% endmacro %} {% macro ItemDict(option_name, option) %} -
+
{{ OptionTitle(option_name, option) }} -
- {% for item_name in (option.valid_keys | sort if (option.valid_keys | length > 0) else world.item_names | sort) %} -
- - -
- {% endfor %} +
{# TODO: needs to be handled differently #} + {# Elements are created via JavaScript to reduce load on browser for large lists. #} +{# {% for item_name in (option.valid_keys | sort if (option.valid_keys | length > 0) else world.item_names | sort) %}#} +{#
#} +{# #} +{# #} +{#
#} +{# {% endfor %}#}
{% endmacro %} {% macro OptionList(option_name, option) %} -
+
{{ OptionTitle(option_name, option) }} -
- {% for key in (option.valid_keys if option.valid_keys is ordered else option.valid_keys | sort) %} -
- - -
- {% endfor %} +
+ {# Elements are created via JavaScript to reduce load on browser for large lists. #} +{# {% for key in (option.valid_keys if option.valid_keys is ordered else option.valid_keys | sort) %}#} +{#
#} +{# #} +{# #} +{#
#} +{# {% endfor %}#}
{% endmacro %} {% macro LocationSet(option_name, option) %} -
+
{{ OptionTitle(option_name, option) }} -
- {% for group_name in world.location_name_groups.keys() | sort %} - {% if group_name != "Everywhere" %} -
- - -
- {% endif %} - {% endfor %} - {% if world.location_name_groups.keys() | length > 1 %} -
 
- {% endif %} - {% for location_name in (option.valid_keys | sort if (option.valid_keys | length > 0) else world.location_names | sort) %} -
- - -
- {% endfor %} +
+ {# Elements are created via JavaScript to reduce load on browser for large lists. #} +{# {% for group_name in world.location_name_groups.keys() | sort %}#} +{# {% if group_name != "Everywhere" %}#} +{#
#} +{# #} +{# #} +{#
#} +{# {% endif %}#} +{# {% endfor %}#} +{# {% if world.location_name_groups.keys() | length > 1 %}#} +{#
 
#} +{# {% endif %}#} +{# {% for location_name in (option.valid_keys | sort if (option.valid_keys | length > 0) else world.location_names | sort) %}#} +{#
#} +{# #} +{# #} +{#
#} +{# {% endfor %}#}
{% endmacro %} {% macro ItemSet(option_name, option) %} -
+
{{ OptionTitle(option_name, option) }} -
- {% for group_name in world.item_name_groups.keys() | sort %} - {% if group_name != "Everything" %} -
- - -
- {% endif %} - {% endfor %} - {% if world.item_name_groups.keys() | length > 1 %} -
 
- {% endif %} - {% for item_name in (option.valid_keys | sort if (option.valid_keys | length > 0) else world.item_names | sort) %} -
- - -
- {% endfor %} +
+ {# Elements are created via JavaScript to reduce load on browser for large lists. #} +
+{# {% for group_name in world.item_name_groups.keys() | sort %}#} +{# {% if group_name != "Everything" %}#} +{#
#} +{# #} +{# #} +{#
#} +{# {% endif %}#} +{# {% endfor %}#} +{# {% if world.item_name_groups.keys() | length > 1 %}#} +{#
 
#} +{# {% endif %}#} +{# {% for item_name in (option.valid_keys | sort if (option.valid_keys | length > 0) else world.item_names | sort) %}#} +{#
#} +{# #} +{# #} +{#
#} +{# {% endfor %}#}
{% endmacro %} {% macro OptionSet(option_name, option) %} -
+
{{ OptionTitle(option_name, option) }} -
- {% for key in (option.valid_keys if option.valid_keys is ordered else option.valid_keys | sort) %} -
- - -
- {% endfor %} +
+ {# Elements are created via JavaScript to reduce load on browser for large lists. #} +{# {% for key in (option.valid_keys if option.valid_keys is ordered else option.valid_keys | sort) %}#} +{#
#} +{# #} +{# #} +{#
#} +{# {% endfor %}#}
{% endmacro %} diff --git a/WebHostLib/templates/playerOptions/playerOptions.html b/WebHostLib/templates/playerOptions/playerOptions.html index 0dde1bacd01f..6eaae0b52212 100644 --- a/WebHostLib/templates/playerOptions/playerOptions.html +++ b/WebHostLib/templates/playerOptions/playerOptions.html @@ -8,6 +8,7 @@ type="text/css" href="{{ url_for("static", filename="styles/options/player.css") }}" > +