"""Interactive All-22 field: a self-contained HTML/SVG/JS document served in an iframe. Python decides *what* (rule profile, formation, ball spot); the field renders and edits it. Edits (dragged players, drawn lines) persist in localStorage keyed by formation pair, so switching NFL <-> NCAA keeps the coach's work and only the geometry changes. """ from __future__ import annotations import html import json from formations import DEFENSE, OFFENSE from rules import PROFILES, RuleProfile, other TEMPLATE = r"""
Drag any player. Pick Route / Motion / Block, then drag from a player to draw his line.
""" def build_field_html(level_key: str, off_name: str, def_name: str, spot: str, yardline: int, show_ghost: bool, show_inel: bool, height: int = 720, replay=None) -> str: p: RuleProfile = PROFILES[level_key] o = other(p) if replay: players = [dict(m, ox=0.0, oy=0.0, anchor="B") for m in replay["meta"]] yardline = int(round(replay["bx"] - 10)) else: players = ([dict(pl, side="O") for pl in OFFENSE[off_name]] + [dict(pl, side="D") for pl in DEFENSE[def_name]]) cfg = dict( levelLabel=p.label, levelShort=p.key, hashHalf=p.hash_half_yd, hashYd=p.hash_width_yd, hashFt=p.hash_width_ft, ghostHashHalf=o.hash_half_yd, ghostName=o.key, showGhost=show_ghost, showInel=show_inel, inel=p.ineligible_downfield_yd, yardline=int(yardline), spot={"Left hash": "L", "Middle": "M", "Right hash": "R"}[spot], spotName=spot.lower(), offName=off_name, defName=def_name, players=players, ) if replay: cfg["ballY"] = replay["by"] cfg["spotName"] = "snap spot" cfg["replay"] = dict(ids=replay["ids"], frames=replay["frames"], events=replay["events"], summary=replay["summary"]) doc = TEMPLATE.replace("__CFG__", json.dumps(cfg)) return (f'')