OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
 
master @ 332 LINES
 
[ HISTORY ]  [ UP ]
 

---
export interface Props {
  mode: "benchmarks" | "comparison";
  comparisonType?: "trinket" | "race";
  selectedClass?: string;
  selectedSpec?: string;
}

const {
  mode,
  comparisonType = "trinket",
  selectedClass = "",
  selectedSpec = "",
} = Astro.props;
---

<div class="filter-row simulation-controls mt-md mb-md">
  {
    mode === "comparison" && (
      <>
        <div class="filter-group">
          <label for="class">Class</label>
          <select id="class">
            <option value="">Loading...</option>
          </select>
        </div>
        <div class="filter-group">
          <label for="spec">Spec</label>
          <select id="spec" disabled>
            <option value="">Select class first</option>
          </select>
        </div>
        <div class="filter-group">
          <label for="cmpType">Comparison</label>
          <select id="cmpType">
            <option value="">Select Comparison</option>
            <option value="trinket">Trinkets</option>
            <option value="race">Race</option>
          </select>
        </div>
      </>
    )
  }
  <div class="filter-group">
    <label for="phase">Phase</label>
    <select id="phase">
      <option value="preRaid">Pre-Raid</option>
      <option value="p1">Phase 1</option>
    </select>
  </div>
  <div class="filter-group">
    <label for="encounter">Encounter</label>
    <select id="encounter">
      <option value="raid">Raid</option>
    </select>
  </div>

  <div class="filter-group">
    <label for="targets">Targets</label>
    <select id="targets">
      <option value="single">1</option>
      <option value="cleave">2</option>
      <option value="three">3</option>
      <option value="ten">10</option>
    </select>
  </div>
  <div class="filter-group">
    <label for="duration">Duration</label>
    <select id="duration">
      <option value="short">Short (2m)</option>
      <option value="burst">Burst (30s)</option>
      <option value="long" selected>Long (5m)</option>
    </select>
  </div>
  <div class="filter-group">
    <label for="sort">Sort</label>
    <select id="sort">
      <option value="dps">Avg DPS</option>
      <option value="max">Max DPS</option>
      <option value="min">Min DPS</option>
      <option value="stdev">Consistency (Low StdDev)</option>
      <option value="percent" data-percent>Percent vs Baseline</option>
    </select>
  </div>
</div>

<script
  type="module"
  is:inline
  define:vars={{ mode, comparisonType, selectedClass, selectedSpec }}
>
  const K = (window.SimURL && window.SimURL.PARAM) || {
    phase: "phase",
    encounter: "encounter",
    targets: "targets",
    duration: "duration",
    sort: "sort",
    type: "type",
    class: "class",
    spec: "spec",
  };
  const getParam = (name, fallback) =>
    window.SimURL && typeof window.SimURL.getParam === "function"
      ? window.SimURL.getParam(name, fallback)
      : fallback || "";
  function pushParams(params, pathOverride) {
    if (window.SimURL && typeof window.SimURL.pushParams === "function") {
      return window.SimURL.pushParams(params, pathOverride);
    }
    const url = new URL(window.location.href);
    if (pathOverride) url.pathname = pathOverride;
    Object.entries(params).forEach(([k, v]) => {
      if (v == null || v === "") url.searchParams.delete(k);
      else url.searchParams.set(k, v);
    });
    window.history.pushState({}, "", url.toString());
    window.dispatchEvent(new CustomEvent("simulation:statechange"));
  }
  const toTitle = (s) =>
    window.WoWConstants && window.WoWConstants.toTitleCase
      ? window.WoWConstants.toTitleCase(s)
      : String(s || "")
          .replace(/_/g, " ")
          .replace(/\b\w/g, (c) => c.toUpperCase());
  function initControls() {
    const phase = document.getElementById("phase");
    const encounter = document.getElementById("encounter");
    const targets = document.getElementById("targets");
    const duration = document.getElementById("duration");
    const sort = document.getElementById("sort");
    const classSel = document.getElementById("class");
    const specSel = document.getElementById("spec");

    // hydrate from URL
    phase && (phase.value = getParam(K.phase, "p1"));
    encounter && (encounter.value = getParam(K.encounter, "raid"));
    targets && (targets.value = getParam(K.targets, "single"));
    duration && (duration.value = getParam(K.duration, "long"));
    const sortDefault =
      mode === "comparison" && comparisonType === "trinket" ? "percent" : "dps";
    sort && (sort.value = getParam(K.sort, sortDefault));

    if (classSel && specSel && mode === "comparison") {
      const SPEC_OPTIONS =
        (window.WoWConstants && window.WoWConstants.SPEC_OPTIONS) || {};
      // Populate classes
      classSel.innerHTML =
        '<option value="">Select Class</option>' +
        Object.keys(SPEC_OPTIONS)
          .map((cls) => `<option value="${cls}">${toTitle(cls)}</option>`)
          .join("");
      if (selectedClass) classSel.value = selectedClass;

      const fillSpecs = () => {
        const cls = classSel.value;
        const specs = SPEC_OPTIONS[cls] || [];
        specSel.innerHTML =
          '<option value="">Select Spec</option>' +
          specs
            .map((s) => `<option value="${s.value}">${s.label}</option>`)
            .join("");
        specSel.disabled = specs.length === 0;
        if (selectedSpec) specSel.value = selectedSpec;
      };
      fillSpecs();
      // hydrate comparison type select
      const cmpTypeSel = document.getElementById("cmpType");
      const currentType = getParam(K.type, "");
      if (cmpTypeSel) cmpTypeSel.value = currentType;
      // show/hide percent option based on type
      const sortSel0 = document.getElementById("sort");
      const percentOpt0 = sortSel0
        ? sortSel0.querySelector('option[value="percent"]')
        : null;
      if (percentOpt0) {
        percentOpt0.style.display = currentType === "trinket" ? "" : "none";
        if (
          percentOpt0.style.display === "none" &&
          sortSel0 &&
          sortSel0.value === "percent"
        )
          sortSel0.value = "dps";
      }
      // update when type changes
      if (cmpTypeSel)
        cmpTypeSel.addEventListener("change", () => {
          const sortSel = document.getElementById("sort");
          // show/hide percent option dynamically
          const percentOpt = sortSel
            ? sortSel.querySelector('option[value="percent"]')
            : null;
          if (percentOpt) {
            percentOpt.style.display =
              cmpTypeSel.value === "trinket" ? "" : "none";
            if (
              cmpTypeSel.value !== "trinket" &&
              sortSel &&
              sortSel.value === "percent"
            )
              sortSel.value = "dps";
            if (
              cmpTypeSel.value === "trinket" &&
              sortSel &&
              (!sortSel.value || sortSel.value === "dps")
            )
              sortSel.value = "percent";
          }
          // reflect in URL via shared helper
          const base = "/simulation/comparison";
          pushParams(
            {
              class: (classSel && classSel.value) || getParam(K.class, ""),
              spec: (specSel && specSel.value) || getParam(K.spec, ""),
              type: cmpTypeSel.value,
              phase: phase?.value,
              encounter: encounter?.value,
              targets: targets?.value,
              duration: duration?.value,
              sort: (sortSel && sortSel["value"]) || undefined,
            },
            base,
          );
        });
      classSel.addEventListener("change", () => {
        fillSpecs();
        if (classSel.value) {
          const cmpTypeSel = document.getElementById("cmpType");
          const currentType =
            (cmpTypeSel && cmpTypeSel.value) || getParam("type", "");
          pushParams(
            {
              class: classSel.value,
              spec: specSel.value || "",
              type: currentType,
              phase: phase?.value,
              encounter: encounter?.value,
              targets: targets?.value,
              duration: duration?.value,
              sort: sort?.value,
            },
            "/simulation/comparison",
          );
        }
      });
      specSel.addEventListener("change", () => {
        if (classSel.value) {
          const cmpTypeSel = document.getElementById("cmpType");
          const currentType =
            (cmpTypeSel && cmpTypeSel.value) || getParam("type", "");
          pushParams(
            {
              class: classSel.value,
              spec: specSel.value || "",
              type: currentType,
              phase: phase?.value,
              encounter: encounter?.value,
              targets: targets?.value,
              duration: duration?.value,
              sort: sort?.value,
            },
            "/simulation/comparison",
          );
        }
      });
    }

    // hide percent when not trinket comparisons (benchmarks mode)
    if (mode !== "comparison") {
      const percentOpt = sort?.querySelector('option[value="percent"]');
      if (percentOpt) {
        percentOpt.style.display = "none";
        // If URL had sort=percent from previous page, coerce to dps and sync URL
        if (sort && sort.value === "percent") {
          sort.value = "dps";
          const base = "/simulation/benchmark";
          const common = {
            phase: phase?.value,
            encounter: encounter?.value,
            targets: targets?.value,
            duration: duration?.value,
            sort: sort?.value,
          };
          pushParams(common, base);
        }
      }
    }

    // hide preRaid when not benchmarks
    if (phase) {
      const pre = phase.querySelector('option[value="preRaid"]');
      if (pre) pre.style.display = mode === "benchmarks" ? "" : "none";
      if (pre && pre.style.display === "none" && phase.value === "preRaid")
        phase.value = "p1";
    }

    // listeners
    const paramPush = () => {
      const base =
        mode === "comparison"
          ? "/simulation/comparison"
          : "/simulation/benchmark";
      const common = {
        phase: phase?.value,
        encounter: encounter?.value,
        targets: targets?.value,
        duration: duration?.value,
        sort: sort?.value,
      };
      if (mode === "comparison") {
        const cls = (classSel && classSel.value) || getParam(K.class, "");
        const spc = (specSel && specSel.value) || getParam(K.spec, "");
        const cmpTypeSel = document.getElementById("cmpType");
        const typ = (cmpTypeSel && cmpTypeSel.value) || getParam(K.type, "");
        pushParams({ ...common, class: cls, spec: spc, type: typ }, base);
      } else {
        pushParams(common, base);
      }
    };
    phase?.addEventListener("change", paramPush);
    encounter?.addEventListener("change", paramPush);
    targets?.addEventListener("change", paramPush);
    duration?.addEventListener("change", paramPush);
    sort?.addEventListener("change", paramPush);
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", initControls);
  } else {
    initControls();
  }
</script>

---
export interface Props {
  mode: "benchmarks" | "comparison";
  comparisonType?: "trinket" | "race";
  selectedClass?: string;
  selectedSpec?: string;
}

const {
  mode,
  comparisonType = "trinket",
  selectedClass = "",
  selectedSpec = "",
} = Astro.props;
---

<div class="filter-row simulation-controls m
t-md mb-md">
  {
    mode === "comparison" && (
      <>
        <div class="filter-group">
          <label for="class">Class</label>
          <select id="class">
            <option value="">Loading...</opt
ion>
          </select>
        </div>
        <div class="filter-group">
          <label for="spec">Spec</label>
          <select id="spec" disabled>
            <option value="">Select class fi
rst</option>
          </select>
        </div>
        <div class="filter-group">
          <label for="cmpType">Comparison</l
abel>
          <select id="cmpType">
            <option value="">Select Comparis
on</option>
            <option value="trinket">Trinkets
</option>
            <option value="race">Race</optio
n>
          </select>
        </div>
      </>
    )
  }
  <div class="filter-group">
    <label for="phase">Phase</label>
    <select id="phase">
      <option value="preRaid">Pre-Raid</opti
on>
      <option value="p1">Phase 1</option>
    </select>
  </div>
  <div class="filter-group">
    <label for="encounter">Encounter</label>
    <select id="encounter">
      <option value="raid">Raid</option>
    </select>
  </div>

  <div class="filter-group">
    <label for="targets">Targets</label>
    <select id="targets">
      <option value="single">1</option>
      <option value="cleave">2</option>
      <option value="three">3</option>
      <option value="ten">10</option>
    </select>
  </div>
  <div class="filter-group">
    <label for="duration">Duration</label>
    <select id="duration">
      <option value="short">Short (2m)</opti
on>
      <option value="burst">Burst (30s)</opt
ion>
      <option value="long" selected>Long (5m
)</option>
    </select>
  </div>
  <div class="filter-group">
    <label for="sort">Sort</label>
    <select id="sort">
      <option value="dps">Avg DPS</option>
      <option value="max">Max DPS</option>
      <option value="min">Min DPS</option>
      <option value="stdev">Consistency (Low
 StdDev)</option>
      <option value="percent" data-percent>P
ercent vs Baseline</option>
    </select>
  </div>
</div>

<script
  type="module"
  is:inline
  define:vars={{ mode, comparisonType, selec
tedClass, selectedSpec }}
>
  const K = (window.SimURL && window.SimURL.
PARAM) || {
    phase: "phase",
    encounter: "encounter",
    targets: "targets",
    duration: "duration",
    sort: "sort",
    type: "type",
    class: "class",
    spec: "spec",
  };
  const getParam = (name, fallback) =>
    window.SimURL && typeof window.SimURL.ge
tParam === "function"
      ? window.SimURL.getParam(name, fallbac
k)
      : fallback || "";
  function pushParams(params, pathOverride) 
{
    if (window.SimURL && typeof window.SimUR
L.pushParams === "function") {
      return window.SimURL.pushParams(params
, pathOverride);
    }
    const url = new URL(window.location.href
);
    if (pathOverride) url.pathname = pathOve
rride;
    Object.entries(params).forEach(([k, v]) 
=> {
      if (v == null || v === "") url.searchP
arams.delete(k);
      else url.searchParams.set(k, v);
    });
    window.history.pushState({}, "", url.toS
tring());
    window.dispatchEvent(new CustomEvent("si
mulation:statechange"));
  }
  const toTitle = (s) =>
    window.WoWConstants && window.WoWConstan
ts.toTitleCase
      ? window.WoWConstants.toTitleCase(s)
      : String(s || "")
          .replace(/_/g, " ")
          .replace(/\b\w/g, (c) => c.toUpper
Case());
  function initControls() {
    const phase = document.getElementById("p
hase");
    const encounter = document.getElementByI
d("encounter");
    const targets = document.getElementById(
"targets");
    const duration = document.getElementById
("duration");
    const sort = document.getElementById("so
rt");
    const classSel = document.getElementById
("class");
    const specSel = document.getElementById(
"spec");

    // hydrate from URL
    phase && (phase.value = getParam(K.phase
, "p1"));
    encounter && (encounter.value = getParam
(K.encounter, "raid"));
    targets && (targets.value = getParam(K.t
argets, "single"));
    duration && (duration.value = getParam(K
.duration, "long"));
    const sortDefault =
      mode === "comparison" && comparisonTyp
e === "trinket" ? "percent" : "dps";
    sort && (sort.value = getParam(K.sort, s
ortDefault));

    if (classSel && specSel && mode === "com
parison") {
      const SPEC_OPTIONS =
        (window.WoWConstants && window.WoWCo
nstants.SPEC_OPTIONS) || {};
      // Populate classes
      classSel.innerHTML =
        '<option value="">Select Class</opti
on>' +
        Object.keys(SPEC_OPTIONS)
          .map((cls) => `<option value="${cl
s}">${toTitle(cls)}</option>`)
          .join("");
      if (selectedClass) classSel.value = se
lectedClass;

      const fillSpecs = () => {
        const cls = classSel.value;
        const specs = SPEC_OPTIONS[cls] || [
];
        specSel.innerHTML =
          '<option value="">Select Spec</opt
ion>' +
          specs
            .map((s) => `<option value="${s.
value}">${s.label}</option>`)
            .join("");
        specSel.disabled = specs.length === 
0;
        if (selectedSpec) specSel.value = se
lectedSpec;
      };
      fillSpecs();
      // hydrate comparison type select
      const cmpTypeSel = document.getElement
ById("cmpType");
      const currentType = getParam(K.type, "
");
      if (cmpTypeSel) cmpTypeSel.value = cur
rentType;
      // show/hide percent option based on t
ype
      const sortSel0 = document.getElementBy
Id("sort");
      const percentOpt0 = sortSel0
        ? sortSel0.querySelector('option[val
ue="percent"]')
        : null;
      if (percentOpt0) {
        percentOpt0.style.display = currentT
ype === "trinket" ? "" : "none";
        if (
          percentOpt0.style.display === "non
e" &&
          sortSel0 &&
          sortSel0.value === "percent"
        )
          sortSel0.value = "dps";
      }
      // update when type changes
      if (cmpTypeSel)
        cmpTypeSel.addEventListener("change"
, () => {
          const sortSel = document.getElemen
tById("sort");
          // show/hide percent option dynami
cally
          const percentOpt = sortSel
            ? sortSel.querySelector('option[
value="percent"]')
            : null;
          if (percentOpt) {
            percentOpt.style.display =
              cmpTypeSel.value === "trinket"
 ? "" : "none";
            if (
              cmpTypeSel.value !== "trinket"
 &&
              sortSel &&
              sortSel.value === "percent"
            )
              sortSel.value = "dps";
            if (
              cmpTypeSel.value === "trinket"
 &&
              sortSel &&
              (!sortSel.value || sortSel.val
ue === "dps")
            )
              sortSel.value = "percent";
          }
          // reflect in URL via shared helpe
r
          const base = "/simulation/comparis
on";
          pushParams(
            {
              class: (classSel && classSel.v
alue) || getParam(K.class, ""),
              spec: (specSel && specSel.valu
e) || getParam(K.spec, ""),
              type: cmpTypeSel.value,
              phase: phase?.value,
              encounter: encounter?.value,
              targets: targets?.value,
              duration: duration?.value,
              sort: (sortSel && sortSel["val
ue"]) || undefined,
            },
            base,
          );
        });
      classSel.addEventListener("change", ()
 => {
        fillSpecs();
        if (classSel.value) {
          const cmpTypeSel = document.getEle
mentById("cmpType");
          const currentType =
            (cmpTypeSel && cmpTypeSel.value)
 || getParam("type", "");
          pushParams(
            {
              class: classSel.value,
              spec: specSel.value || "",
              type: currentType,
              phase: phase?.value,
              encounter: encounter?.value,
              targets: targets?.value,
              duration: duration?.value,
              sort: sort?.value,
            },
            "/simulation/comparison",
          );
        }
      });
      specSel.addEventListener("change", () 
=> {
        if (classSel.value) {
          const cmpTypeSel = document.getEle
mentById("cmpType");
          const currentType =
            (cmpTypeSel && cmpTypeSel.value)
 || getParam("type", "");
          pushParams(
            {
              class: classSel.value,
              spec: specSel.value || "",
              type: currentType,
              phase: phase?.value,
              encounter: encounter?.value,
              targets: targets?.value,
              duration: duration?.value,
              sort: sort?.value,
            },
            "/simulation/comparison",
          );
        }
      });
    }

    // hide percent when not trinket compari
sons (benchmarks mode)
    if (mode !== "comparison") {
      const percentOpt = sort?.querySelector
('option[value="percent"]');
      if (percentOpt) {
        percentOpt.style.display = "none";
        // If URL had sort=percent from prev
ious page, coerce to dps and sync URL
        if (sort && sort.value === "percent"
) {
          sort.value = "dps";
          const base = "/simulation/benchmar
k";
          const common = {
            phase: phase?.value,
            encounter: encounter?.value,
            targets: targets?.value,
            duration: duration?.value,
            sort: sort?.value,
          };
          pushParams(common, base);
        }
      }
    }

    // hide preRaid when not benchmarks
    if (phase) {
      const pre = phase.querySelector('optio
n[value="preRaid"]');
      if (pre) pre.style.display = mode === 
"benchmarks" ? "" : "none";
      if (pre && pre.style.display === "none
" && phase.value === "preRaid")
        phase.value = "p1";
    }

    // listeners
    const paramPush = () => {
      const base =
        mode === "comparison"
          ? "/simulation/comparison"
          : "/simulation/benchmark";
      const common = {
        phase: phase?.value,
        encounter: encounter?.value,
        targets: targets?.value,
        duration: duration?.value,
        sort: sort?.value,
      };
      if (mode === "comparison") {
        const cls = (classSel && classSel.va
lue) || getParam(K.class, "");
        const spc = (specSel && specSel.valu
e) || getParam(K.spec, "");
        const cmpTypeSel = document.getEleme
ntById("cmpType");
        const typ = (cmpTypeSel && cmpTypeSe
l.value) || getParam(K.type, "");
        pushParams({ ...common, class: cls, 
spec: spc, type: typ }, base);
      } else {
        pushParams(common, base);
      }
    };
    phase?.addEventListener("change", paramP
ush);
    encounter?.addEventListener("change", pa
ramPush);
    targets?.addEventListener("change", para
mPush);
    duration?.addEventListener("change", par
amPush);
    sort?.addEventListener("change", paramPu
sh);
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoa
ded", initControls);
  } else {
    initControls();
  }
</script>
 
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET