import {
  DownloadMenu,
  PlotBase,
  axisstyle,
  controlsInit,
  fillTermWrapper,
  getCombinedTermFilter,
  getNormalRoot,
  select2Terms,
  table2col,
  term0_term2_defaultQ
} from "./chunk-SA7APTJR.js";
import "./chunk-HJ6L54YS.js";
import "./chunk-LSEFWW72.js";
import "./chunk-SKREEF3H.js";
import {
  Menu
} from "./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
} 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 {
  getColors
} from "./chunk-4UWS5Y3N.js";
import "./chunk-DD4R5P6W.js";
import "./chunk-JNITUVXP.js";
import {
  line_default
} from "./chunk-KSGA62R2.js";
import {
  axisBottom,
  axisLeft
} from "./chunk-LOZEKOES.js";
import "./chunk-TOU7EVFQ.js";
import {
  linear
} from "./chunk-OAWQ6LOO.js";
import {
  roundValueAuto
} from "./chunk-TLT4YIG3.js";
import "./chunk-KYBIQBXE.js";
import "./chunk-I6Y4O3RR.js";
import {
  rgb
} from "./chunk-OMR2DT66.js";
import "./chunk-DQC5FFGV.js";
import "./chunk-HFNDKYVF.js";

// plots/runChart2/defaults.ts
function getDefaultRunChart2Settings(overrides = {}) {
  const defaults = {
    aggregation: "median",
    showCumulativeFrequency: true,
    svgw: 1e3,
    svgh: 500,
    color: "#ce768e",
    opacity: 0.6,
    minXScale: null,
    maxXScale: null,
    minYScale: null,
    maxYScale: null
  };
  return Object.assign(defaults, overrides);
}

// plots/runChart2/RunChart2Controls.ts
function getMinMaxInputs(range, runChart2) {
  const fallback = { xMin: 0, xMax: 1, yMin: 0, yMax: 1 };
  const valid = [range.xMin, range.xMax, range.yMin, range.yMax].every((n) => Number.isFinite(n)) && range.xMin < range.xMax && range.yMin < range.yMax;
  const r = valid ? range : fallback;
  const xMin = roundValueAuto(r.xMin);
  const xMax = roundValueAuto(r.xMax);
  const xStep = (xMax - xMin) / 10 || 0.1;
  const yMin = roundValueAuto(r.yMin);
  const yMax = roundValueAuto(r.yMax);
  const yStep = (yMax - yMin) / 10 || 0.1;
  return [
    {
      label: "X axis minimum",
      type: "number",
      chartType: "runChart2",
      settingsKey: "minXScale",
      title: `Set the minimum X axis value between ${xMin} and ${xMax}`,
      placeholder: `${xMin}`,
      min: xMin,
      max: xMax,
      step: xStep
    },
    {
      label: "X axis maximum",
      type: "number",
      chartType: "runChart2",
      settingsKey: "maxXScale",
      title: `Set the maximum X axis value between ${xMin} and ${xMax}`,
      placeholder: `${xMax}`,
      min: xMin,
      max: xMax,
      step: xStep,
      ...runChart2 && {
        processInput: (value) => {
          const sel = runChart2.dom.controls.selectAll("input").filter(function() {
            return this.placeholder === `${xMax}`;
          });
          if (!sel.node()?.value) return xMax;
          return value;
        }
      }
    },
    {
      label: "Y axis minimum",
      type: "number",
      chartType: "runChart2",
      settingsKey: "minYScale",
      title: `Set the minimum Y axis value between ${yMin} and ${yMax}`,
      placeholder: `${yMin}`,
      min: yMin,
      max: yMax,
      step: yStep
    },
    {
      label: "Y axis maximum",
      type: "number",
      chartType: "runChart2",
      settingsKey: "maxYScale",
      title: `Set the maximum Y axis value between ${yMin} and ${yMax}`,
      placeholder: `${yMax}`,
      min: yMin,
      max: yMax,
      step: yStep,
      ...runChart2 && {
        processInput: (value) => {
          const sel = runChart2.dom.controls.selectAll("input").filter(function() {
            return this.placeholder === `${yMax}`;
          });
          if (!sel.node()?.value) return yMax;
          return value;
        }
      }
    }
  ];
}
function getBaseInputs(app) {
  return [
    {
      type: "term",
      configKey: "xtw",
      chartType: "runChart2",
      usecase: { target: "runChart2", detail: "numeric" },
      label: "X",
      vocabApi: app.vocabApi,
      menuOptions: "edit",
      numericEditMenuVersion: ["continuous", "discrete"]
    },
    {
      type: "term",
      configKey: "ytw",
      chartType: "runChart2",
      usecase: { target: "runChart2", detail: "numeric" },
      label: "Y",
      vocabApi: app.vocabApi,
      menuOptions: "{replace,remove}",
      defaultQ4fillTW: term0_term2_defaultQ,
      numericEditMenuVersion: ["continuous"]
    },
    {
      label: "Aggregation",
      type: "dropdown",
      chartType: "runChart2",
      settingsKey: "aggregation",
      options: [{ label: "Median", value: "median" }],
      getDisplayStyle: (plot) => plot?.ytw != null ? "table-row" : "none"
    },
    {
      label: "Show cumulative frequency",
      boxLabel: "",
      type: "checkbox",
      chartType: "runChart2",
      settingsKey: "showCumulativeFrequency",
      title: "Show the cumulative number of events over time",
      getDisplayStyle: (plot) => plot?.ytw == null ? "table-row" : "none"
    },
    {
      label: "Plot height",
      title: "Set the plot height in pixels, >=200",
      type: "number",
      chartType: "runChart2",
      settingsKey: "svgh",
      debounceInterval: 500,
      min: 200,
      step: 25
    },
    {
      label: "Plot width",
      title: "Set the plot width in pixels, >=200",
      type: "number",
      chartType: "runChart2",
      settingsKey: "svgw",
      debounceInterval: 500,
      min: 200,
      step: 25
    },
    {
      label: "Default color",
      type: "color",
      chartType: "runChart2",
      settingsKey: "color"
    }
  ];
}
function getRunChart2Controls(app, range, runChart2) {
  const inputs = getBaseInputs(app);
  if (range) {
    inputs.push(...getMinMaxInputs(range, runChart2));
  }
  return inputs;
}

// plots/runChart2/model/RunChart2Model.ts
var RunChart2Model = class {
  constructor(runChart2) {
    this.charts = [];
    this.chartData = null;
    this.runChart2 = runChart2;
  }
  async fetchData(config) {
    const result = await dofetch3("termdb/runChart", { body: this.getRequestOpts(config) });
    if (!("status" in result) || result.status !== "ok") {
      throw new Error(`RunChart2Model.fetchData() failed: ${result.error}`);
    }
    return result.series;
  }
  getRequestOpts(config) {
    const state = this.runChart2.state;
    return {
      genome: state.vocab.genome,
      dslabel: state.vocab.dslabel,
      filter: getNormalRoot(state.termfilter?.filter),
      xtw: config.xtw,
      ytw: config.ytw,
      aggregation: config.settings?.runChart2?.aggregation,
      showCumulativeFrequency: config.settings?.runChart2?.showCumulativeFrequency
    };
  }
};

// plots/runChart2/viewModel/ViewModel.ts
var RunChart2ViewModel = class {
  constructor(settings) {
    this.#horizPad = 70;
    this.#vertPad = 40;
    this.#bottomLabelPad = 72;
    this.#leftLabelPad = 50;
    this.#xAxisLabelOffset = 50;
    this.#yAxisLabelX = 55;
    this.xMin = Infinity;
    this.xMax = -Infinity;
    this.yMin = Infinity;
    this.yMax = -Infinity;
    this.settings = settings;
  }
  #horizPad;
  #vertPad;
  #bottomLabelPad;
  #leftLabelPad;
  #xAxisLabelOffset;
  #yAxisLabelX;
  /** Increase the domain slightly so all data points fit within the plot. */
  setDomain(min, max, percent = 0.1) {
    const rangeInc = (max - min) * percent || 0.1;
    return [min - rangeInc, max + rangeInc];
  }
  map(data) {
    let totalSampleCount = 0;
    for (const series of data) {
      if (!series.points || series.points.length === 0) continue;
      for (const point of series.points) {
        this.xMin = Math.min(point.x, this.xMin);
        this.xMax = Math.max(point.x, this.xMax);
        this.yMin = Math.min(point.y, this.yMin);
        this.yMax = Math.max(point.y, this.yMax);
        totalSampleCount += point.sampleCount ?? 0;
      }
    }
    const hasValidRange = Number.isFinite(this.xMin) && Number.isFinite(this.xMax) && this.xMin < this.xMax && Number.isFinite(this.yMin) && Number.isFinite(this.yMax) && this.yMin < this.yMax;
    let xMinForDomain, xMaxForDomain, yMinForDomain, yMaxForDomain;
    if (hasValidRange) {
      const usePaddingX = this.settings.minXScale == null && this.settings.maxXScale == null;
      const xPadded = usePaddingX ? this.setDomain(this.xMin, this.xMax, 0.02) : null;
      xMinForDomain = this.settings.minXScale ?? (xPadded ? xPadded[0] : this.xMin);
      xMaxForDomain = this.settings.maxXScale ?? (xPadded ? xPadded[1] : this.xMax);
      const usePaddingY = this.settings.minYScale == null && this.settings.maxYScale == null;
      const yPadded = usePaddingY ? this.setDomain(this.yMin, this.yMax, 0.1) : null;
      yMinForDomain = this.settings.minYScale ?? (yPadded ? yPadded[0] : this.yMin);
      yMaxForDomain = this.settings.maxYScale ?? (yPadded ? yPadded[1] : this.yMax);
    } else {
      xMinForDomain = 0;
      xMaxForDomain = 1;
      yMinForDomain = 0;
      yMaxForDomain = 1;
    }
    return {
      series: data,
      totalSampleCount: totalSampleCount > 0 ? totalSampleCount : void 0,
      plotDims: this.getPlotDimensions({
        xMin: xMinForDomain,
        xMax: xMaxForDomain,
        yMin: yMinForDomain,
        yMax: yMaxForDomain
      })
    };
  }
  getPlotDimensions(domains) {
    const { xMin, xMax, yMin, yMax } = domains;
    return {
      svg: {
        height: this.settings.svgh + this.#vertPad + this.#bottomLabelPad,
        width: this.settings.svgw + this.#horizPad + this.#leftLabelPad
      },
      xAxis: {
        scale: linear().domain([xMin, xMax]).range([0, this.settings.svgw]),
        x: this.#horizPad + this.#leftLabelPad,
        y: this.settings.svgh + this.#vertPad,
        labelOffset: this.#xAxisLabelOffset
      },
      yAxis: {
        // range [svgh, 0]: y grows bottom-to-top
        scale: linear().domain([yMin, yMax]).range([this.settings.svgh, 0]),
        x: this.#horizPad + this.#leftLabelPad,
        y: this.#vertPad,
        labelX: this.#yAxisLabelX
      }
    };
  }
};

// plots/runChart2/Settings.ts
function getFrequencyCountLabel(showCumulative) {
  return showCumulative ? "Cumulative count" : "Count";
}

// plots/runChart2/view/SeriesRender.ts
function getCoordinate(val, min, max) {
  if (min != null && val < min) return min;
  if (max != null && val > max) return max;
  return val;
}
function isFrequencyMode(config) {
  return config?.ytw == null;
}
function isCumulativeFrequencyMode(config) {
  return isFrequencyMode(config) && config?.settings?.runChart2?.showCumulativeFrequency === true;
}
function shouldDrawMedianLine(config, series) {
  return !isCumulativeFrequencyMode(config) && series?.median != null && !isNaN(series.median);
}
var SeriesRender = class {
  constructor(series, plotDims, settings, seriesGroup, runChart2, seriesColor) {
    this.series = series;
    this.plotDims = plotDims;
    this.settings = settings;
    this.seriesGroup = seriesGroup;
    this.runChart2 = runChart2;
    this.seriesColor = seriesColor;
    this.render();
  }
  getCoordinates(d) {
    const cx = getCoordinate(d.x, this.settings.minXScale, this.settings.maxXScale);
    const cy = getCoordinate(d.y, this.settings.minYScale, this.settings.maxYScale);
    return {
      x: this.plotDims.xAxis.scale(cx),
      y: this.plotDims.yAxis.scale(cy)
    };
  }
  render() {
    if (!this.series.points || this.series.points.length === 0) return;
    const cfg = this.runChart2?.state?.config;
    const color = this.seriesColor ?? this.settings.color ?? "#1f77b4";
    const medianColor = rgb(color).darker(2);
    const sortedPoints = [...this.series.points].sort((a, b) => a.x - b.x);
    const lineBuilder = line_default().x((d) => this.getCoordinates(d).x).y((d) => this.getCoordinates(d).y);
    const opacity = this.settings.opacity ?? 0.6;
    const seriesG = this.seriesGroup.append("g").attr("data-testid", "sjpp-runChart2-series").attr("data-series-id", this.series.seriesId ?? "");
    if (shouldDrawMedianLine(cfg, this.series)) {
      const yMedian = this.plotDims.yAxis.scale(
        getCoordinate(this.series.median, this.settings.minYScale, this.settings.maxYScale)
      );
      const xStart = this.getCoordinates(sortedPoints[0]).x;
      const xEnd = this.getCoordinates(sortedPoints[sortedPoints.length - 1]).x;
      const medianG = seriesG.append("g").attr("data-testid", "sjpp-runChart2-median-bg");
      medianG.append("line").attr("x1", xStart).attr("y1", yMedian).attr("x2", xEnd).attr("y2", yMedian).attr("stroke", medianColor.toString()).attr("stroke-width", 1).attr("opacity", 0.5);
      medianG.append("text").attr("x", xEnd - 10).attr("y", yMedian - 5).attr("text-anchor", "end").attr("font-size", "12px").attr("fill", medianColor.toString()).attr("pointer-events", "none").text(`M=${roundValueAuto(this.series.median, true, 1)}`);
    }
    seriesG.append("path").datum(sortedPoints).attr("fill", "none").attr("stroke", color).attr("stroke-width", 1).attr("stroke-linejoin", "round").attr("opacity", opacity).attr("d", lineBuilder);
    seriesG.selectAll("circle").data(sortedPoints).enter().append("circle").attr("cx", (d) => this.getCoordinates(d).x).attr("cy", (d) => this.getCoordinates(d).y).attr("r", 2.5).attr("fill", color).attr("stroke", "#fff").attr("stroke-width", 1).style("cursor", "pointer").on("mouseover", (event, d) => this.showHoverTip(event, d)).on("mouseout", () => this.hideHoverTip());
  }
  showHoverTip(event, d) {
    const tip = this.runChart2?.dom?.hovertip;
    if (!tip) return;
    const cfg = this.runChart2?.state?.config;
    tip.clear();
    const table = table2col({ holder: tip.d.append("div") });
    if (this.series.seriesId) table.addRow("Period", this.series.seriesId);
    const xTermName = cfg?.xtw?.term?.name ?? "X";
    table.addRow(xTermName, d.xName ?? String(d.x));
    if (isFrequencyMode(cfg)) {
      table.addRow(getFrequencyCountLabel(cfg?.settings?.runChart2?.showCumulativeFrequency), String(d.y ?? ""));
      if (d.sampleCount != null && d.sampleCount !== d.y) {
        table.addRow("Sample Count", String(d.sampleCount));
      }
    } else {
      table.addRow(cfg?.ytw?.term?.name ?? "Y", roundValueAuto(d.y, true, 2));
      table.addRow("Sample Count", String(d.sampleCount ?? ""));
    }
    tip.show(event.clientX, event.clientY);
  }
  hideHoverTip() {
    this.runChart2?.dom?.hovertip?.hide();
  }
  // Click menu disabled until menu options have real actions (currently only close the menu)
  // showPointMenu(event: any, point: any) {
  // 	if (!this.runChart2 || !this.runChart2.dom.clickMenu) return
  // 	this.hideHoverTip()
  // 	const cfg = this.runChart2.state?.config
  // 	const xTermName = cfg?.term?.term?.name ?? 'X'
  // 	const yTermName = cfg?.term2?.term?.name ?? 'Y'
  // 	const menu = this.runChart2.dom.clickMenu
  // 	menu.clear()
  // 	const menuDiv = menu.d.append('div').attr('class', 'sja_menu_div')
  // 	const options = [
  // 		{ label: `${xTermName}: ${point.xName ?? point.x}`, callback: () => menu.hide() },
  // 		{ label: `${yTermName}: ${roundValueAuto(point.y, true, 2)}`, callback: () => menu.hide() },
  // 		{ label: `Sample Count: ${point.sampleCount}`, callback: () => menu.hide() }
  // 	]
  // 	menuDiv
  // 		.selectAll('div')
  // 		.data(options)
  // 		.enter()
  // 		.append('div')
  // 		.attr('class', 'sja_menuoption sja_sharp_border')
  // 		.text(d => d.label)
  // 		.style('cursor', 'pointer')
  // 		.on('click', (event: any, d: any) => { d.callback() })
  // 	menu.show(event.clientX, event.clientY)
  // }
};

// plots/runChart2/view/View.ts
var RunChart2View = class {
  constructor(viewData, settings, holder, config, runChart2) {
    this.viewData = viewData;
    this.settings = settings;
    this.config = config;
    this.runChart2 = runChart2;
    const svg = holder.append("svg").attr("data-testid", "sjpp-runChart2-svg");
    this.chartDom = {
      svg,
      xAxis: svg.append("g").attr("data-testid", "sjpp-runChart2-xAxis"),
      yAxis: svg.append("g").attr("data-testid", "sjpp-runChart2-yAxis")
    };
    this.render();
  }
  render() {
    const plotDims = this.viewData.plotDims;
    this.chartDom.svg.selectAll("*").remove();
    this.chartDom.xAxis = this.chartDom.svg.append("g").attr("data-testid", "sjpp-runChart2-xAxis");
    this.chartDom.yAxis = this.chartDom.svg.append("g").attr("data-testid", "sjpp-runChart2-yAxis");
    this.chartDom.svg.transition().attr("width", plotDims.svg.width).attr("height", plotDims.svg.height);
    this.renderScale(plotDims.xAxis);
    this.renderScale(plotDims.yAxis, true);
    const seriesGroup = this.chartDom.svg.append("g").attr("data-testid", "sjpp-runChart2-seriesGroup").attr("transform", `translate(${plotDims.xAxis.x}, ${plotDims.yAxis.y})`);
    const seriesList = this.viewData.series || [];
    const cat2Color = seriesList.length > 1 ? getColors(seriesList.length) : null;
    for (let i = 0; i < seriesList.length; i++) {
      const series = seriesList[i];
      const seriesColor = cat2Color ? cat2Color(series.seriesId ?? String(i)) : void 0;
      new SeriesRender(series, plotDims, this.settings, seriesGroup, this.runChart2, seriesColor);
    }
    this.renderAxisLabels(plotDims);
  }
  renderAxisLabels(plotDims) {
    const xName = this.config?.xtw?.term?.name || "X Axis";
    const xLabel = this.viewData.totalSampleCount != null && this.config?.xtw?.q?.mode !== "discrete" ? `${xName}, n=${this.viewData.totalSampleCount.toLocaleString()}` : xName;
    const yLabel = this.config?.ytw?.term?.name ?? getFrequencyCountLabel(this.settings?.showCumulativeFrequency);
    const xAxisLabelY = plotDims.xAxis.y + (plotDims.xAxis.labelOffset ?? 50);
    this.chartDom.svg.append("text").attr("data-testid", "sjpp-runChart2-xAxisLabel").attr("transform", `translate(${plotDims.xAxis.x + this.settings.svgw / 2}, ${xAxisLabelY})`).attr("text-anchor", "middle").style("font-size", "0.9em").text(xLabel);
    const seriesList = this.viewData.series || [];
    if (seriesList.length > 0 && this.config?.xtw?.q?.mode === "discrete") {
      const firstSeries = seriesList[0];
      const firstSeriesId = firstSeries?.seriesId;
      if (firstSeriesId != null) {
        const periodN = firstSeries?.points?.reduce((sum, p) => sum + (Number(p.sampleCount) || 0), 0) ?? 0;
        const labelText = periodN > 0 ? `${String(firstSeriesId)}, n=${periodN.toLocaleString()}` : String(firstSeriesId);
        this.chartDom.svg.append("text").attr("data-testid", "sjpp-runChart2-xAxisSeriesIds").attr("transform", `translate(${plotDims.xAxis.x + this.settings.svgw / 2}, ${xAxisLabelY + 20})`).attr("text-anchor", "middle").style("font-size", "0.9em").style("opacity", 1).text(labelText);
      }
    }
    const yAxisLabelX = plotDims.yAxis.labelX ?? 55;
    this.chartDom.svg.append("text").attr("data-testid", "sjpp-runChart2-yAxisLabel").attr("transform", `translate(${yAxisLabelX}, ${plotDims.yAxis.y + this.settings.svgh / 2}) rotate(-90)`).attr("text-anchor", "middle").style("font-size", "0.9em").text(yLabel);
  }
  renderScale(scale, isLeft = false) {
    const axisGroup = this.chartDom[isLeft ? "yAxis" : "xAxis"];
    const scaleG = axisGroup.append("g").attr("transform", `translate(${scale.x}, ${scale.y})`);
    if (isLeft) {
      scaleG.call(axisLeft(scale.scale));
    } else {
      const allPoints = [];
      for (const series of this.viewData.series || []) {
        if (series.points) allPoints.push(...series.points);
      }
      const yearTickValues = [...new Set(allPoints.map((p) => Math.floor(p.x)))].sort((a, b) => a - b);
      const xAxis = axisBottom(scale.scale).tickFormat((d) => String(Math.floor(Number(d))));
      if (yearTickValues.length > 0) xAxis.tickValues(yearTickValues);
      else xAxis.ticks(6);
      scaleG.call(xAxis);
      scaleG.selectAll("text").style("text-anchor", "middle").style("font-size", "12px");
    }
    axisstyle({
      axis: scaleG,
      color: "black",
      showline: true
    });
  }
};

// plots/runChart2/RunChart2.ts
var RunChart2 = class _RunChart2 extends PlotBase {
  constructor(opts, api) {
    super(opts, api);
    this.configTermKeys = ["xtw", "ytw"];
    this.opts = opts;
    this.api = api;
    this.type = _RunChart2.type;
    if (this.opts.parentId) this.parentId = this.opts.parentId;
    this.components = {
      controls: {}
    };
    const leftDiv = opts.holder.insert("div").style("display", "inline-block");
    const controlsHolder = leftDiv.append("div").style("display", "inline-block");
    const chartHolder = opts.holder.append("div").attr("data-testid", "sjpp-runChart2-chartHolder").style("display", "inline-block").style("vertical-align", "top");
    this.dom = {
      controls: controlsHolder,
      chartHolder,
      error: chartHolder.append("div").attr("data-testid", "sjpp-runChart2-error"),
      hovertip: new Menu({ padding: "3px" })
    };
    if (opts.header) {
      this.dom.headerLabel = opts.header.append("span").attr("class", "sja_runChart2_header").attr("data-testid", "sjpp-runChart2-header").style("font-size", "0.8em").style("opacity", 0.7).text("RUN CHART");
    }
  }
  static {
    this.type = "runChart2";
  }
  reactsTo(action) {
    if (action.type.includes("cache_termq")) return true;
    if (action.type.startsWith("filter")) return true;
    if (action.type.startsWith("cohort")) return true;
    if (action.type == "app_refresh") return true;
    if (action.type.startsWith("plot_")) {
      return (action.id === this.id || action.id == this.parentId) && (!action.config?.childType || action.config?.childType == this.type);
    }
  }
  getState(appState) {
    const config = appState.plots.find((p) => p.id === this.id);
    if (!config) {
      throw new Error(
        `No plot with id='${this.id}' found. Did you set this.id before this.api = getComponentApi(this)?`
      );
    }
    const parentConfig = appState.plots.find((p) => p.id === this.parentId);
    const termfilter = getCombinedTermFilter(appState, config.filter || parentConfig?.filter);
    return {
      termdbConfig: appState.termdbConfig,
      termfilter,
      vocab: appState.vocab,
      config: Object.assign({}, config, {
        settings: {
          runChart2: config.settings?.runChart2
        }
      })
    };
  }
  async setControls(viewModel) {
    const range = { xMin: viewModel.xMin, xMax: viewModel.xMax, yMin: viewModel.yMin, yMax: viewModel.yMax };
    this.dom.controls.selectAll("*").remove();
    this.components.controls = await controlsInit({
      app: this.app,
      id: this.id,
      holder: this.dom.controls.style("display", "inline-block"),
      inputs: getRunChart2Controls(this.app, range, this)
    });
    this.components.controls.on("downloadClick.runChart2", async (event) => {
      await this.download(event);
    });
    const appState = this.app.getState();
    this.components.controls.update?.({ appState });
  }
  async download(event) {
    const chartImages = this.getChartImages();
    if (chartImages.length === 0) {
      console.warn("No chart images available for download");
      return;
    }
    const filename = this.getDownloadFilename();
    const menu = new DownloadMenu(chartImages, filename);
    menu.show(event.clientX, event.clientY, event.target);
  }
  getChartImages() {
    const chartImages = [];
    if (!this.view || !this.view.chartDom) return chartImages;
    const svg = this.view.chartDom.svg;
    if (svg && !svg.empty()) {
      const chartName = this.state?.config?.xtw?.term?.name || "runChart2";
      chartImages.push({ name: chartName, svg });
    }
    return chartImages;
  }
  getDownloadFilename() {
    const xTermName = this.state?.config?.xtw?.term?.name || "runChart2";
    const yTermName = this.state?.config?.ytw?.term?.name || "";
    return yTermName ? `${xTermName}_${yTermName}` : xTermName;
  }
  async main() {
    const c = this.state.config;
    if (c.childType != this.type && c.chartType != this.type) return;
    const config = await this.getMutableConfig();
    if (this.dom.headerLabel) {
      this.dom.headerLabel.text(config.ytw == null ? "FREQUENCY CHART" : "RUN CHART");
    }
    try {
      this.model = new RunChart2Model(this);
      const data = await this.model.fetchData(config);
      if (!data) {
        this.dom.error.text("No data available for the selected terms and filter.");
      }
      const settings = config.settings.runChart2;
      this.viewModel = new RunChart2ViewModel(settings);
      const viewData = this.viewModel.map(data);
      await this.setControls(this.viewModel);
      if (!this.dom.chartHolder.empty()) {
        this.dom.chartHolder.selectAll("*").remove();
      }
      this.view = new RunChart2View(viewData, settings, this.dom.chartHolder, config, this);
    } catch (e) {
      console.error(e);
      throw new Error(`RunChart2.main() failed: ${e}`);
    }
  }
};
var runChart2Init = getCompInit(RunChart2);
var componentInit = runChart2Init;
async function getPlotConfig(opts, app) {
  const xtw = opts.xtw;
  const ytw = opts.ytw ?? null;
  if (!xtw) throw new Error("runChart2 requires xtw (X term wrapper)");
  const raw = opts.settings || {};
  const settings = {
    ...raw,
    runChart2: {
      ...raw.runChart2 ?? {},
      ...raw.frequencyChart?.showCumulativeFrequency !== void 0 && {
        showCumulativeFrequency: raw.frequencyChart.showCumulativeFrequency
      }
    }
  };
  try {
    if (!xtw.q) xtw.q = {};
    xtw.q.mode = xtw.q.mode ?? "continuous";
    await fillTermWrapper(xtw, app.vocabApi);
    if (ytw) {
      if (!ytw.q) ytw.q = {};
      ytw.q.mode = ytw.q.mode ?? "continuous";
      await fillTermWrapper(ytw, app.vocabApi);
    }
  } catch (e) {
    console.error(e);
    throw new Error(`runChart2 getPlotConfig() failed: ${e}`);
  }
  const defaultConfig = app.vocabApi.termdbConfig?.plotConfigByCohort?.default?.[opts.chartType] ?? (opts.chartType === "frequencyChart" ? app.vocabApi.termdbConfig?.plotConfigByCohort?.default?.runChart2 : void 0);
  const config = {
    xtw,
    ytw,
    settings: {
      controls: { isOpen: false },
      runChart2: getDefaultRunChart2Settings({ ...opts, settings })
    }
  };
  return copyMerge(config, defaultConfig, {
    ...opts,
    xtw,
    ytw,
    settings: { ...settings, runChart2: settings.runChart2 ?? {} }
  });
}
function makeChartBtnMenu(holder, chartsInstance) {
  const menuDiv = holder.append("div");
  const callback = (xterm, yterm) => {
    chartsInstance.app.dispatch({
      type: "plot_create",
      config: {
        chartType: "runChart2",
        xtw: { term: xterm, q: { mode: "continuous" } },
        ytw: { term: yterm, q: { mode: "continuous" } },
        name: `${xterm.name} vs ${yterm.name}`
      }
    });
  };
  menuDiv.append("button").style("margin", "5px").style("padding", "10px 15px").style("border-radius", "20px").style("border-color", "#ededed").style("display", "block").text("Select data to plot ...").on("click", () => {
    chartsInstance.dom.tip.clear();
    select2Terms(chartsInstance.dom.tip, chartsInstance.app, "runChart2", "date", callback, "numeric");
    chartsInstance.dom.tip.show();
  });
  const byDefault = chartsInstance.state.termdbConfig?.plotConfigByCohort?.default;
  const runChart2Plots = byDefault?.runChart2?.plots ?? [];
  for (const plot of runChart2Plots) {
    const config = structuredClone(plot);
    const dispatchPlot = () => {
      chartsInstance.app.dispatch({
        type: "plot_create",
        config: {
          chartType: "runChart2",
          ...config
        }
      });
      chartsInstance.dom.tip.hide();
    };
    menuDiv.append("button").attr("class", "sja_menuoption sja_sharp_border").style("margin", "5px").style("padding", "10px 15px").style("border-radius", "20px").style("border-color", "#ededed").style("display", "block").text(plot.name).on("mousedown", (event) => {
      event.preventDefault();
      event.stopPropagation();
      dispatchPlot();
    }).on("click", (event) => {
      event.preventDefault();
      event.stopPropagation();
      chartsInstance.dom.tip.hide();
    });
  }
}
export {
  RunChart2,
  componentInit,
  getPlotConfig,
  makeChartBtnMenu,
  runChart2Init
};
//# sourceMappingURL=RunChart2-N7AZVQXJ.js.map
