import {
  PlotBase,
  first_genetrack_tolist,
  sayerror,
  table2col
} from "./chunk-SA7APTJR.js";
import "./chunk-HJ6L54YS.js";
import "./chunk-LSEFWW72.js";
import "./chunk-SKREEF3H.js";
import "./chunk-HYOEWQ5P.js";
import "./chunk-HBW42TDT.js";
import "./chunk-G6O3URDN.js";
import "./chunk-FN5XPUPH.js";
import "./chunk-IIT367QZ.js";
import "./chunk-RZGEKL77.js";
import "./chunk-UI7OCM2A.js";
import "./chunk-YZK37YCC.js";
import {
  dofetch3,
  formatElapsedTime
} from "./chunk-7EMWHCVW.js";
import "./chunk-7IYJZZQI.js";
import {
  copyMerge,
  getCompInit
} from "./chunk-M3J4MINX.js";
import "./chunk-PF4DSFDR.js";
import "./chunk-QH6W3NVF.js";
import "./chunk-NOEAT6CX.js";
import "./chunk-4UWS5Y3N.js";
import "./chunk-DD4R5P6W.js";
import "./chunk-JNITUVXP.js";
import "./chunk-KSGA62R2.js";
import "./chunk-LOZEKOES.js";
import "./chunk-TOU7EVFQ.js";
import "./chunk-OAWQ6LOO.js";
import "./chunk-TLT4YIG3.js";
import "./chunk-KYBIQBXE.js";
import "./chunk-I6Y4O3RR.js";
import "./chunk-OMR2DT66.js";
import "./chunk-DQC5FFGV.js";
import "./chunk-HFNDKYVF.js";

// plots/dmr/settings/defaults.ts
function getDefaultDMRSettings(opts) {
  const overrides = opts.settings || {};
  const defaults = {
    blockWidth: 800,
    pad: 2e3,
    lambda: 1e3,
    C: 2,
    fdr_cutoff: 0.05,
    colors: {
      group1: "#3b5ee6",
      group2: "#c04e00",
      hyper: "#e66101",
      hypo: "#5e81f4"
    },
    maxLoessRegion: 25e4,
    minProbesForCi: 10,
    backend: "rust",
    maxRegionSize: 5e6
  };
  if (overrides.colors) {
    Object.assign(defaults.colors, overrides.colors);
    delete overrides.colors;
  }
  return Object.assign(defaults, overrides);
}

// plots/dmr/model/DmrModel.ts
var DmrModel = class {
  constructor(config, vocab) {
    this.config = config;
    this.vocab = vocab;
  }
  async fetchDmr(chr, start, stop, signal) {
    const { group1, group2, settings } = this.config;
    const { genome, dslabel } = this.vocab;
    return dofetch3("termdb/dmr", {
      signal,
      body: {
        genome,
        dslabel,
        chr,
        start,
        stop,
        group1,
        group2,
        lambda: settings.dmr.lambda,
        C: settings.dmr.C,
        fdr_cutoff: settings.dmr.fdr_cutoff,
        group1Name: this.config.group1Name,
        group2Name: this.config.group2Name,
        blockWidth: settings.dmr.blockWidth,
        devicePixelRatio: typeof window !== "undefined" ? window.devicePixelRatio : 1,
        maxLoessRegion: settings.dmr.maxLoessRegion,
        colors: settings.dmr.colors,
        backend: settings.dmr.backend
      }
    });
  }
};

// plots/dmr/viewModel/DmrViewModel.ts
var DmrViewModel = class {
  constructor(dmrResult, config, genomeObj, queryChr, queryStart, queryStop) {
    const { settings } = config;
    const dmrBedItems = this.makeDmrBedItems(dmrResult, settings);
    const sigCpgBedItems = this.makeSigCpgBedItems(dmrResult, settings, queryChr, queryStart, queryStop);
    const xRange = (queryStop ?? 0) - (queryStart ?? 0);
    const loess = dmrResult.diagnostic?.loess;
    const showLoess = !!(loess && loess.group1_fitted.length > 0 && loess.group2_fitted.length > 0);
    const showDots = xRange <= settings.dmr.maxLoessRegion;
    const betaTrackResult = dmrResult.diagnostic ? this.renderBetaTrack(
      dmrResult.diagnostic,
      config,
      settings.dmr.blockWidth,
      showLoess,
      showDots,
      queryStart,
      queryStop
    ) : void 0;
    this.viewData = {
      tklst: this.buildTrackList(dmrBedItems, sigCpgBedItems, genomeObj, betaTrackResult?.img),
      legendRows: this.buildLegendData(
        config,
        dmrResult.dmrs,
        sigCpgBedItems,
        showLoess,
        showDots,
        betaTrackResult?.showCi ?? false
      ),
      diagnostic: dmrResult.diagnostic,
      dmrs: dmrResult.dmrs,
      dmrBedItems,
      showLoess,
      showDots
    };
  }
  buildTrackList(dmrBedItems, sigCpgBedItems, genomeObj, betaTrackImg) {
    const tklst = [];
    first_genetrack_tolist(genomeObj, tklst);
    tklst.push({ type: "bedj", name: "DMRs", bedItems: dmrBedItems });
    tklst.push({ type: "bedj", name: "Sig. CpGs", bedItems: sigCpgBedItems });
    if (betaTrackImg) {
      tklst.push({
        type: "bigwig",
        name: "Per-CpG Means",
        height: 150,
        imgData: betaTrackImg
      });
    }
    return tklst;
  }
  buildLegendData(config, dmrs, sigCpgBedItems, showLoess, showDots, showCi) {
    const { colors } = config.settings.dmr;
    const g1 = config.group1Name || "Group 1";
    const g2 = config.group2Name || "Group 2";
    const meansItems = [];
    if (showDots) {
      meansItems.push({ text: `${g1} (control)`, color: colors.group1 }, { text: `${g2} (case)`, color: colors.group2 });
    }
    if (showLoess) {
      const ciLabel = showCi ? " + 95% CI" : "";
      meansItems.push(
        { text: `${g1} LOESS trend${ciLabel}`, color: colors.group1, style: showCi ? "shaded" : "dashed" },
        { text: `${g2} LOESS trend${ciLabel}`, color: colors.group2, style: showCi ? "shaded" : "dashed" }
      );
    }
    const rows = [{ label: "Per-CpG Means", items: meansItems }];
    const hasHyper = dmrs.some((d) => d.direction === "hyper");
    const hasHypo = dmrs.some((d) => d.direction === "hypo");
    if (hasHyper || hasHypo) {
      const items = [];
      if (hasHyper) items.push({ text: "Hypermethylated", color: colors.hyper });
      if (hasHypo) items.push({ text: "Hypomethylated", color: colors.hypo });
      rows.push({ label: "DMR", items });
    }
    if (sigCpgBedItems.length) {
      const items = [];
      const hasHyperCpg = sigCpgBedItems.some((b) => b.color === colors.hyper);
      const hasHypoCpg = sigCpgBedItems.some((b) => b.color === colors.hypo);
      if (hasHyperCpg) items.push({ text: "Hyper (FDR sig.)", color: colors.hyper });
      if (hasHypoCpg) items.push({ text: "Hypo (FDR sig.)", color: colors.hypo });
      rows.push({ label: "Sig. CpGs", items });
    }
    return rows;
  }
  /**
   * Render the per-CpG means scatter plot to an offscreen canvas and return
   * a data URI suitable for the bigwig imgData track.
   */
  renderBetaTrack(diagnostic, config, blockWidth, showLoess, showDots, queryStart, queryStop) {
    const { probes } = diagnostic;
    if (!probes.positions.length) return void 0;
    const { colors, fdr_cutoff, minProbesForCi } = config.settings.dmr;
    const dpr = typeof window !== "undefined" && window.devicePixelRatio > 1 ? window.devicePixelRatio : 1;
    const width = blockWidth;
    const height = 150;
    const canvas = document.createElement("canvas");
    canvas.width = width * dpr;
    canvas.height = height * dpr;
    const ctx = canvas.getContext("2d");
    if (!ctx) return void 0;
    ctx.scale(dpr, dpr);
    const xMin = queryStart ?? probes.positions[0];
    const xMax = queryStop ?? probes.positions[probes.positions.length - 1];
    const xRange = xMax - xMin || 1;
    const scaleX = (val) => (val - xMin) / xRange * width;
    const scaleY = (val) => height - val * height;
    ctx.clearRect(0, 0, width, height);
    let showCi = false;
    if (showLoess && diagnostic.loess) {
      const { loess } = diagnostic;
      const firstProbePos = probes.positions[0];
      const lastProbePos = probes.positions[probes.positions.length - 1];
      showCi = probes.positions.length >= minProbesForCi;
      for (const [fitted, ciLower, ciUpper, color] of [
        [loess.group1_fitted, loess.group1_ci_lower, loess.group1_ci_upper, colors.group1],
        [loess.group2_fitted, loess.group2_ci_lower, loess.group2_ci_upper, colors.group2]
      ]) {
        if (!fitted.length) continue;
        const lPos = loess.positions;
        let iStart = 0;
        let iEnd = lPos.length - 1;
        while (iStart < lPos.length && lPos[iStart] < firstProbePos) iStart++;
        while (iEnd >= 0 && lPos[iEnd] > lastProbePos) iEnd--;
        if (iStart > iEnd) continue;
        if (showCi) {
          ctx.globalAlpha = 0.12;
          ctx.fillStyle = color;
          ctx.beginPath();
          for (let i = iStart; i <= iEnd; i++) {
            ctx.lineTo(scaleX(lPos[i]), scaleY(Math.max(0, Math.min(1, ciUpper[i]))));
          }
          for (let i = iEnd; i >= iStart; i--) {
            ctx.lineTo(scaleX(lPos[i]), scaleY(Math.max(0, Math.min(1, ciLower[i]))));
          }
          ctx.closePath();
          ctx.fill();
        }
        ctx.globalAlpha = 0.8;
        ctx.strokeStyle = color;
        ctx.lineWidth = 2;
        ctx.setLineDash(showCi ? [] : [6, 4]);
        ctx.beginPath();
        for (let i = iStart; i <= iEnd; i++) {
          ctx.lineTo(scaleX(lPos[i]), scaleY(Math.max(0, Math.min(1, fitted[i]))));
        }
        ctx.stroke();
        ctx.setLineDash([]);
      }
    }
    if (!showDots) {
      ctx.globalAlpha = 1;
      return { img: { minv: 0, maxv: 1, src: canvas.toDataURL("image/png") }, showCi };
    }
    for (let i = 0; i < probes.positions.length; i++) {
      const x = scaleX(probes.positions[i]);
      const isSig = probes.fdr[i] < fdr_cutoff;
      const alpha = isSig ? 0.85 : 0.3;
      ctx.globalAlpha = alpha;
      ctx.fillStyle = colors.group1;
      const m1 = probes.mean_group1[i];
      if (m1 != null) {
        ctx.beginPath();
        ctx.arc(x, scaleY(m1), 4, 0, Math.PI * 2);
        ctx.fill();
      }
      ctx.fillStyle = colors.group2;
      const m2 = probes.mean_group2[i];
      if (m2 != null) {
        ctx.beginPath();
        ctx.arc(x, scaleY(m2), 4, 0, Math.PI * 2);
        ctx.fill();
      }
    }
    ctx.globalAlpha = 1;
    return { img: { minv: 0, maxv: 1, src: canvas.toDataURL("image/png") }, showCi };
  }
  makeDmrBedItems(dmrResult, settings) {
    return dmrResult.dmrs.map((dmr) => {
      const negLog = -Math.log10(Math.max(dmr.min_smoothed_fdr, 1e-300));
      const alpha = Math.round(Math.min(255, Math.max(50, negLog / 10 * 255)));
      const hex = alpha.toString(16).padStart(2, "0");
      const base = dmr.direction === "hyper" ? settings.dmr.colors.hyper : settings.dmr.colors.hypo;
      return { chr: dmr.chr, start: dmr.start, stop: dmr.stop, color: base + hex };
    });
  }
  makeSigCpgBedItems(dmrResult, settings, chr, queryStart, queryStop) {
    const diag = dmrResult.diagnostic;
    if (!diag) return [];
    const { probes } = diag;
    const items = [];
    const minDeltaBeta = 0.05;
    for (let i = 0; i < probes.positions.length; i++) {
      if (probes.fdr[i] >= settings.dmr.fdr_cutoff) continue;
      const pos = probes.positions[i];
      if (queryStart != null && queryStop != null && (pos < queryStart || pos > queryStop)) continue;
      const mg1 = probes.mean_group1[i];
      const mg2 = probes.mean_group2[i];
      if (mg1 == null || mg2 == null) continue;
      const deltaBeta = mg2 - mg1;
      if (Math.abs(deltaBeta) < minDeltaBeta) continue;
      const color = deltaBeta >= 0 ? settings.dmr.colors.hyper : settings.dmr.colors.hypo;
      items.push({ chr, start: pos, stop: pos + 1, color });
    }
    return items;
  }
};

// plots/dmr/view/DmrView.ts
var DmrView = class {
  constructor(dom) {
    this.dom = dom;
  }
  async renderBlock(viewData, genomeObj, settings, chr, start, stop, onCoordinateChange) {
    const { Block } = await import("./block-TTN2IQAH.js");
    return new Block({
      holder: this.dom.holder,
      genome: genomeObj,
      chr,
      start,
      stop,
      tklst: viewData.tklst,
      nobox: true,
      width: settings.blockWidth,
      onCoordinateChange
    });
  }
  updateTracks(viewData, blockInstance) {
    for (const tk of blockInstance.tklst) {
      const updated = viewData.tklst.find((t) => t.name === tk.name);
      if (!updated) continue;
      if (tk.type === "bedj" && updated.bedItems) {
        tk.bedItems = updated.bedItems;
        blockInstance.tk_load(tk);
      } else if (tk.type === "bigwig" && updated.imgData) {
        tk.imgData = updated.imgData;
        blockInstance.tk_load(tk);
      }
    }
  }
  updateLegend(blockInstance, legendRows) {
    if (!blockInstance?.legend?.holder) return;
    const labels = ["Per-CpG Means", "DMR", "Sig. CpGs"];
    blockInstance.legend.holder.selectAll("tr").filter((_d, i, nodes) => {
      const td = nodes[i].querySelector("td");
      return td && labels.includes(td.textContent);
    }).remove();
    this.renderLegend(blockInstance, legendRows);
  }
  renderLegend(blockInstance, legendRows) {
    if (!blockInstance?.legend?.holder) return;
    const { legendcolor, vpad } = blockInstance.legend;
    for (const row of legendRows) {
      const tr = blockInstance.legend.holder.append("tr");
      tr.append("td").text(row.label).attr("style", `padding-right:10px;text-align:right;color:#555;border-right:solid 1px ${legendcolor}`);
      const td = tr.append("td");
      for (const entry of row.items) {
        const item = td.append("div").attr("style", `display:inline-block;white-space:nowrap;padding:${vpad} 20px ${vpad} 0`);
        if (entry.style === "shaded") {
          item.append("div").attr(
            "style",
            `display:inline-block;width:18px;height:10px;background:${entry.color}20;border-top:2px solid ${entry.color};margin-right:5px;vertical-align:middle;border-radius:1px`
          );
        } else if (entry.style === "dashed") {
          item.append("div").attr(
            "style",
            `display:inline-block;width:18px;height:0;border-top:2px dashed ${entry.color};margin-right:5px;vertical-align:middle`
          );
        } else {
          item.append("div").attr(
            "style",
            `display:inline-block;width:12px;height:12px;background:${entry.color};margin-right:5px;border-radius:2px;vertical-align:middle`
          );
        }
        item.append("div").attr("style", "display:inline-block;color:#555;font-size:.8em").text(entry.text);
      }
    }
  }
  renderDiagnostics(diagnostic, dmrs, fdr_cutoff) {
    const panel = this.dom.diagnosticPanel;
    panel.selectAll("*").remove();
    panel.style("display", "block");
    const { probes } = diagnostic;
    const toggle = panel.append("div").attr("style", "cursor:default;font-size:12px;color:#888;padding:2px 0");
    const statsContent = panel.append("div").style("display", "none");
    let expanded = false;
    toggle.text("+ Diagnostic details").on("click", () => {
      expanded = !expanded;
      toggle.text((expanded ? "\u2212 " : "+ ") + "Diagnostic details");
      statsContent.style("display", expanded ? "block" : "none");
    });
    const spacings = diagnostic.probe_spacings;
    const medianSpacing = spacings.length ? spacings.slice().sort((a, b) => a - b)[Math.floor(spacings.length / 2)] : 0;
    const maxGap = spacings.length ? Math.max(...spacings) : 0;
    const gapsOver1kb = spacings.filter((s) => s > 1e3).length;
    const density = probes.positions.length > 1 ? probes.positions.length / ((probes.positions[probes.positions.length - 1] - probes.positions[0]) / 1e3) : 0;
    const sigFdrCount = probes.fdr.filter((f) => f < fdr_cutoff).length;
    const minDeltaBeta = 0.05;
    const sigDualCount = probes.fdr.filter((f, i) => {
      if (f >= fdr_cutoff) return false;
      const m1 = probes.mean_group1[i];
      const m2 = probes.mean_group2[i];
      if (m1 == null || m2 == null) return false;
      return Math.abs(m2 - m1) >= minDeltaBeta;
    }).length;
    const t = table2col({ holder: statsContent, disableScroll: true });
    for (const [k, v] of [
      ["Probes in region", String(probes.positions.length)],
      ["FDR significant", `${sigFdrCount} (FDR < ${fdr_cutoff})`],
      ["FDR + effect size", `${sigDualCount} (FDR < ${fdr_cutoff} & |\u0394\u03B2| \u2265 ${minDeltaBeta})`],
      ["Probe density", `${density.toFixed(1)} probes/kb`],
      ["Median spacing", `${medianSpacing.toFixed(0)} bp`],
      ["Max gap", `${maxGap.toFixed(0)} bp`],
      ["Gaps > 1kb", String(gapsOver1kb)],
      ["DMRs called", String(dmrs.length)],
      ...diagnostic.total_probes_analyzed ? [["Probes analyzed (genome-wide)", diagnostic.total_probes_analyzed.toLocaleString()]] : [],
      ...diagnostic.elapsed_ms != null ? [["Analysis time", formatElapsedTime(diagnostic.elapsed_ms)]] : [],
      ...diagnostic.peak_memory_mb != null ? [["Peak memory", `${diagnostic.peak_memory_mb.toFixed(1)} MB`]] : []
    ]) {
      t.addRow(k, v);
    }
  }
  showOverlay() {
    this.dom.loadingOverlay.style("display", "");
  }
  hideOverlay() {
    this.dom.loadingOverlay.style("display", "none");
  }
  clearDiagnostics() {
    this.dom.diagnosticPanel.selectAll("*").remove();
    this.dom.diagnosticPanel.style("display", "none");
  }
  clearErrors() {
    this.dom.error.selectAll("*").remove();
  }
  showLoessNote(show) {
    this.dom.error.selectAll(".sjpp-loess-note").remove();
    if (show) {
      this.dom.error.append("div").attr("class", "sjpp-loess-note").style("color", "#888").style("font-size", ".8em").style("padding", "4px 0").text("Zoom in to see per-CpG dots.");
    }
  }
};

// plots/dmr/DmrPlot.ts
var DmrPlot = class _DmrPlot extends PlotBase {
  constructor(opts, api) {
    super(opts, api);
    this.type = _DmrPlot.type;
    this.blockInstance = null;
    this.analyzedRegion = null;
    const wrapper = opts.holder.append("div").style("position", "relative");
    const loadingOverlay = wrapper.append("div").attr("class", "sjpp-spinner").style("display", "none").style("position", "absolute").style("z-index", "10").style("background-color", "rgba(255,255,255,0.65)");
    const toggleDiv = opts.holder.append("div").style("padding", "2px 0");
    const initBackend = opts.state?.config?.settings?.dmr?.backend || "rust";
    const toggleBtn = toggleDiv.append("button").style("font-size", "11px").text(`Backend: ${initBackend === "rust" ? "Rust" : "R (DMRCate)"}`).on("click", () => {
      const config = this.state.config;
      const curr = config.settings.dmr.backend || "rust";
      const next = curr === "rust" ? "r" : "rust";
      toggleBtn.text(`Backend: ${next === "rust" ? "Rust" : "R (DMRCate)"}`);
      this.app.dispatch({
        type: "plot_edit",
        id: this.id,
        config: { settings: { dmr: { ...config.settings.dmr, backend: next } } }
      });
    });
    this.dom = {
      header: opts?.header,
      holder: wrapper.append("div"),
      loadingOverlay,
      error: opts.holder.append("div"),
      loading: opts.holder.append("div").text("Running DMR analysis\u2026"),
      diagnosticPanel: opts.holder.append("div").style("display", "none")
    };
    this.view = new DmrView(this.dom);
  }
  static {
    this.type = "dmr";
  }
  getState(appState) {
    const config = appState.plots.find((p) => p.id === this.id);
    if (!config) throw new Error(`No plot with id='${this.id}' found`);
    return { config };
  }
  async init(appState) {
    const { config } = this.getState(appState);
    validateConfig(config);
    if (this.dom.header) this.dom.header.text(config.headerText || "DMR Analysis");
    this.genomeObj = this.app.opts.genome;
    this.model = new DmrModel(config, this.app.vocabApi.vocab);
    this.dom.loading.style("display", "block");
    try {
      const pad = config.settings.dmr.pad;
      const chr = config.coordinateOverride.chr;
      const start = Math.max(0, Number(config.coordinateOverride.start) - pad);
      const stop = Number(config.coordinateOverride.stop) + pad;
      checkRegionSize(stop - start, config.settings.dmr.maxRegionSize);
      const dmrResult = await this.model.fetchDmr(chr, start, stop, this.api?.getAbortSignal());
      if ("error" in dmrResult) {
        sayerror(this.dom.error, dmrResult.error);
        throw new Error(dmrResult.error);
      }
      this.analyzedRegion = { chr, start, stop };
      const vm = new DmrViewModel(dmrResult, config, this.genomeObj, chr, start, stop);
      this.blockInstance = await this.view.renderBlock(
        vm.viewData,
        this.genomeObj,
        config.settings.dmr,
        chr,
        start,
        stop,
        (rglst) => this.onBlockCoordinateChange(rglst)
      );
      this.view.renderLegend(this.blockInstance, vm.viewData.legendRows);
      this.view.showLoessNote(!vm.viewData.showDots);
      if (vm.viewData.diagnostic)
        this.view.renderDiagnostics(vm.viewData.diagnostic, vm.viewData.dmrs, config.settings.dmr.fdr_cutoff);
    } catch (e) {
      if (this.app.isAbortError(e)) return;
      const msg = e instanceof Error ? e.message : String(e);
      sayerror(this.dom.error, msg);
    }
    this.dom.loading.style("display", "none");
  }
  async main() {
    if (!this.analyzedRegion) return;
    const config = this.state.config;
    this.model = new DmrModel(config, this.app.vocabApi.vocab);
    const c = config.coordinateOverride;
    if (!c) return;
    const pad = config.settings.dmr.pad;
    const chr = c.chr;
    const start = Math.max(0, Number(c.start) - pad);
    const stop = Number(c.stop) + pad;
    const a = this.analyzedRegion;
    const coordsChanged = chr !== a.chr || start !== a.start || stop !== a.stop;
    if (coordsChanged) {
      this.view.showOverlay();
      this.view.clearErrors();
      try {
        checkRegionSize(stop - start, config.settings.dmr.maxRegionSize);
        const dmrResult = await this.model.fetchDmr(chr, start, stop, this.api?.getAbortSignal());
        if ("error" in dmrResult) {
          sayerror(this.dom.error, dmrResult.error);
          throw new Error(dmrResult.error);
        }
        this.analyzedRegion = { chr, start, stop };
        const blkRegion = this.blockInstance?.rglst?.[0];
        const viewStart = blkRegion?.start ?? start;
        const viewStop = blkRegion?.stop ?? stop;
        const vm = new DmrViewModel(dmrResult, config, this.genomeObj, chr, viewStart, viewStop);
        this.view.updateTracks(vm.viewData, this.blockInstance);
        this.view.updateLegend(this.blockInstance, vm.viewData.legendRows);
        this.view.showLoessNote(!vm.viewData.showDots);
        this.view.clearDiagnostics();
        if (vm.viewData.diagnostic)
          this.view.renderDiagnostics(vm.viewData.diagnostic, vm.viewData.dmrs, config.settings.dmr.fdr_cutoff);
      } catch (e) {
        if (this.app.isAbortError(e)) return;
        const msg = e instanceof Error ? e.message : String(e);
        sayerror(this.dom.error, msg);
      }
      this.view.hideOverlay();
    } else {
      this.dom.holder.selectAll("*").remove();
      this.view.clearErrors();
      this.dom.loading.style("display", "block");
      this.blockInstance = null;
      try {
        checkRegionSize(stop - start, config.settings.dmr.maxRegionSize);
        const dmrResult = await this.model.fetchDmr(chr, start, stop, this.api?.getAbortSignal());
        if ("error" in dmrResult) {
          sayerror(this.dom.error, dmrResult.error);
          throw new Error(dmrResult.error);
        }
        this.analyzedRegion = { chr, start, stop };
        const vm = new DmrViewModel(dmrResult, config, this.genomeObj, chr, start, stop);
        this.blockInstance = await this.view.renderBlock(
          vm.viewData,
          this.genomeObj,
          config.settings.dmr,
          chr,
          start,
          stop,
          (rglst) => this.onBlockCoordinateChange(rglst)
        );
        this.view.renderLegend(this.blockInstance, vm.viewData.legendRows);
        this.view.showLoessNote(!vm.viewData.showDots);
        if (vm.viewData.diagnostic)
          this.view.renderDiagnostics(vm.viewData.diagnostic, vm.viewData.dmrs, config.settings.dmr.fdr_cutoff);
      } catch (e) {
        if (this.app.isAbortError(e)) return;
        const msg = e instanceof Error ? e.message : String(e);
        sayerror(this.dom.error, msg);
      }
      this.dom.loading.style("display", "none");
    }
  }
  onBlockCoordinateChange(rglst) {
    if (!this.analyzedRegion || !rglst.length) return;
    const r = rglst[0];
    if (r.start >= r.stop || r.start < 0) return;
    this.view.clearErrors();
    const a = this.analyzedRegion;
    if (r.chr === a.chr && r.start === a.start && r.stop === a.stop) return;
    this.app.dispatch({
      type: "plot_edit",
      id: this.id,
      config: { coordinateOverride: { chr: r.chr, start: r.start, stop: r.stop } }
    });
  }
};
var componentInit = getCompInit(DmrPlot);
function getPlotConfig(opts) {
  validateConfig(opts);
  const config = {
    settings: {
      dmr: getDefaultDMRSettings(opts)
    }
  };
  return copyMerge(config, opts);
}
function validateConfig(opts) {
  if (!opts.coordinateOverride) throw new Error("coordinateOverride (chr/start/stop) is required for DMR plot");
  if (!opts.group1) throw new Error("group1 is required for DMR plot");
  if (!opts.group2) throw new Error("group2 is required for DMR plot");
}
function checkRegionSize(span, maxRegionSize) {
  if (span > maxRegionSize) {
    const mbLimit = (maxRegionSize / 1e6).toFixed(0);
    const mbSpan = (span / 1e6).toFixed(1);
    throw new Error(`Region too large for DMR analysis (${mbSpan} Mb). Maximum is ${mbLimit} Mb.`);
  }
}
export {
  componentInit,
  getPlotConfig
};
//# sourceMappingURL=DmrPlot-FPJJHTM5.js.map
