import {
  storeInit
} from "./chunk-HKSNADSX.js";
import {
  summaryInit
} from "./chunk-BDIKFVWZ.js";
import {
  navInit
} from "./chunk-CXQ6KS3Z.js";
import {
  AppBase,
  downloadSVGsAsPdf,
  filterRxCompInit,
  newSandboxDiv,
  sayerror,
  vocabInit
} from "./chunk-SA7APTJR.js";
import {
  importPlot
} from "./chunk-SKREEF3H.js";
import {
  Menu
} from "./chunk-HYOEWQ5P.js";
import {
  AppApi,
  getCompInit,
  multiInit
} from "./chunk-M3J4MINX.js";
import {
  select_default
} from "./chunk-I6Y4O3RR.js";

// mass/plot.js
var MassPlot = class _MassPlot {
  static type = "plot";
  constructor(opts) {
    this.type = _MassPlot.type;
    setRenderers(this);
    this.initUi(opts);
  }
  reactsTo(action) {
    if (action.type.includes("cache_termq")) return true;
    if (action.type.endsWith("_group")) return true;
    if (action.type.startsWith("plot_")) {
      return action.id === this.id || action.id == this.parentId || action.config?.parentId === this.id || action.parentId === this.id;
    }
    if (action.type.startsWith("filter")) return true;
    if (action.type.startsWith("cohort")) return true;
    if (action.type == "app_refresh") return true;
    if (action.type.endsWith("customTerm")) return true;
  }
  // !!! NOTE: This getState() method is reused by the plot-specific recover component.
  // When logging something within getState, it may have been called by either the plot or recover instance
  getState(appState) {
    const config = appState.plots.find((p) => p.id === this.id);
    if (!config) {
      throw `No plot with id='${this.id}' found. Did you set this.id before this.api = getComponentApi(this)?`;
    }
    return {
      termfilter: appState.termfilter,
      config,
      groups: appState.groups,
      // quick fix to skip history tracking as needed
      _scope_: appState._scope_
    };
  }
  async main() {
    this.dom.errdiv.style("display", "none").style("background-color", "rgba(255,100,100,0.2)").html("");
    if (!this.components) await this.setComponents(this.opts);
  }
  async setComponents(opts) {
    const _ = await importPlot(opts.chartType);
    const promises = {
      // recover: recoverInit({
      // 	app: this.app,
      // 	holder: this.dom.localRecoverDiv,
      // 	getState: appState => this.getState(appState),
      // 	reactsTo: action =>
      // 		action.id == this.id &&
      // 		(action.type == 'plot_edit' || action.type == 'plot_nestedEdits') &&
      // 		action._track_ != 'none',
      // 	plot_id: this.id,
      // 	maxHistoryLen: 10,
      // 	hideLabel: true
      // }),
      chart: _.componentInit({
        app: this.app,
        holder: this.dom.viz,
        header: this.dom.paneTitleDiv,
        id: this.id,
        plotDiv: select_default(this.dom.holder.app_div.node().parentNode),
        /******* reason for passing plotDiv to chart ********
        				- this plot instance may allow to launch a new plot as a persistent sandbox
        				  inside mass plotDiv, maintaining the uniform plot appearance despite it's ad-hoc
        				  the new plot is not a formal mass plot type, and cannot be done via app.dispatch()
        				  thus the need to directly access plotDiv
        				- example: mds3 tk from genome browser can launch disco etc
        				- having access to plotDiv may offer flexibility for the plot to do stuff
        
        				since plot.js has no access to mass app .dom.plotDiv in which all apps are shown,
        				this workarounds gets the parent node of sandbox.app_div which is app.dom.plotDiv
        				*/
        getFilterImage: async () => this.components.filter.getFilterImage()
      })
    };
    if (!this.state.config.hidePlotFilter) {
      const filterDisabledMsg = this.app.vocabApi.termdbConfig?.plotFilter?.disabledMessage;
      const filterHolder = filterDisabledMsg ? this.dom.filterDiv.append("div").style("pointer-events", "none").style("opacity", 0.5) : this.dom.filterDiv;
      if (filterDisabledMsg) this.dom.filterDiv.attr("title", filterDisabledMsg).style("cursor", "not-allowed");
      promises.filter = filterRxCompInit({
        app: this.app,
        vocabApi: this.app.vocabApi,
        parentId: this.id,
        holder: filterHolder,
        hideLabel: true,
        emptyLabel: "+Add new filter",
        callback: (filter) => {
          this.app.dispatch({
            id: this.id,
            type: "plot_edit",
            config: { filter }
          });
        }
      });
    }
    this.components = await multiInit(promises);
  }
  destroy() {
    this.dom.holder.app_div.selectAll("*").remove();
    this.dom.holder.app_div.remove();
    for (const key in this.dom) {
      delete this.dom[key];
    }
  }
};
var plotInit = getCompInit(MassPlot);
function setRenderers(self) {
  self.initUi = function(opts) {
    const holder = opts.holder;
    opts.holder.app_div.attr("data-testid", "sjpp-massplot-sandbox-" + opts.chartType);
    holder.header.style("padding", 0);
    try {
      self.dom = {
        tip: new Menu({ padding: "0px" }),
        holder,
        paneTitleDiv: holder.header.append("div").style("display", "inline-block").style("color", "#555").style("padding-left", "7px").style("vertical-align", "sub"),
        localRecoverDiv: holder.header.append("div").style("display", "inline-block"),
        filterDiv: holder.header.append("div").style("display", "inline-block").style("zoom", 0.9),
        body: holder.body.style("white-space", "nowrap").style("overflow-x", "auto"),
        // will hold no data notice or the page title in multichart views
        errdiv: holder.body.append("div").style("display", "none").style("padding", "5px").style("background-color", "rgba(255,100,100,0.2)"),
        // dom.viz will hold the rendered view
        viz: holder.body.append("div")
      };
    } catch (e) {
      self.dom.errdiv.style("display", "none").text(e);
    }
  };
}

// mass/app.ts
var MassApp = class extends AppBase {
  // expected class-specific props
  constructor(opts, api) {
    super(opts);
    this.components = {};
    this.wasDestroyed = false;
    this.api = api;
    if (opts.addLoginCallback) {
      opts.addLoginCallback(() => this.api.dispatch({ type: "app_refresh" }));
    }
    this.type = "app";
    this.dom = {
      holder: opts.holder,
      // do not modify holder style
      topbar: opts.holder.append("div"),
      errdiv: opts.holder.append("div"),
      plotDiv: opts.holder.append("div")
    };
    this.plotIdToSandboxId = {};
  }
  static {
    this.type = "app";
  }
  async preApiFreeze(api) {
    try {
      api.tip = new Menu({ padding: "5px" });
      api.tip.d.on("keyup", (event) => {
        if (event.key == "Escape") api.tip.hide();
      });
      api.printError = (e) => this.printError(e);
      api.vocabApi = await vocabInit({
        app: api,
        state: { vocab: this.opts.state.vocab },
        fetchOpts: this.opts.fetchOpts,
        getDatasetAccessToken: this.opts.getDatasetAccessToken
      });
      api.hasWebGL = function() {
        try {
          const canvas = document.createElement("canvas");
          return !!(window.WebGLRenderingContext && (canvas.getContext("webgl") || canvas.getContext("experimental-webgl")));
        } catch (_) {
          return false;
        }
      };
      this.opts.state.vocab = api.vocabApi.vocab;
    } catch (e) {
      console.log(`preApiFreeze error`, e);
      throw e;
    }
  }
  async init() {
    try {
      const debounceInterval = "debounceInterval" in this.opts ? this.opts.debounceInterval : 0;
      const embeddedSessionState = this.opts.embeddedSessionState;
      if (embeddedSessionState) {
        Object.assign(this.opts.state, embeddedSessionState);
      }
      this.store = await storeInit({ app: this.api, state: this.opts.state, debounceInterval });
      this.state = await this.store.copyState();
      this.components = {};
      if (this.state.nav.header_mode != "hidden") {
        this.components.nav = await navInit({
          app: this.api,
          holder: this.dom.topbar,
          header_mode: this.state && this.state.nav && this.state.nav.header_mode,
          vocab: this.state.vocab,
          massSessionDuration: this.state.termdbConfig.massSessionDuration,
          // this.opts.massSessionDuration
          pkgver: this.opts.pkgver,
          downloadPlots: () => {
            this.downloadPlots();
          }
        });
      }
      this.components.plots = {};
      if (this.opts.app?.doNotAwaitInitRender) {
        this.api.dispatch();
      } else {
        await this.api.dispatch();
      }
    } catch (e) {
      this.printError(e);
      throw e;
    }
  }
  async main() {
    await this.api.vocabApi.main();
    this.dom.plotDiv?.style(
      "display",
      this.state.nav?.header_mode != "hidden" && this.state.nav?.activeTab == 0 ? "none" : "block"
    );
    const newPlots = {};
    let sandbox;
    for (const plot of this.state.plots) {
      if (plot.parentId) continue;
      if (this.components.plots && !(plot.id in this.components.plots)) {
        sandbox = newSandboxDiv(this.dom.plotDiv, {
          close: () => {
            this.api.dispatch({
              type: "plot_delete",
              id: plot.id
            });
          },
          plotId: plot.id,
          beforePlotId: plot.insertBefore || null,
          style: {
            width: "98.5%"
          }
        });
        if (plot.chartType == "summary")
          newPlots[plot.id] = summaryInit(Object.assign({ app: this.api, holder: sandbox }, plot));
        else newPlots[plot.id] = plotInit(Object.assign({ app: this.api, holder: sandbox }, plot));
      }
    }
    const numNewPlots = Object.keys(newPlots).length;
    if (numNewPlots) {
      await Promise.all(Object.values(newPlots));
      for (const plotId in newPlots) {
        this.components.plots[plotId] = await newPlots[plotId];
      }
    }
    for (const plotId in this.components.plots) {
      if (!this.state.plots.find((p) => p.id === plotId)) {
        this.components.plots[plotId].destroy();
        delete this.components.plots[plotId];
      }
    }
  }
  printError(e) {
    const errdiv = e.errdiv || this.dom.errdiv;
    if (errdiv) errdiv.style("display", "").html("").style("background-color", "");
    sayerror(errdiv || this.opts.holder, "Error: " + (e.message || e.error || e));
    if (e.stack) console.log(e.stack);
    this.bus.emit("error");
    if (this.opts?.debug) {
      console.groupCollapsed("Stack trace from MassApp.printError() call.");
      console.trace();
      console.groupEnd();
    }
  }
  skipPrevActionAbort(action) {
    if (!action) return false;
    if (action.type.startsWith("filter")) return false;
    if (action.type.startsWith("cohort")) return false;
    if (action.type == "app_refresh") {
      if (action.subactions) {
        return action.subactions.find((a) => a.type.startsWith("filter") || a.type.startsWith("cohort")) ? false : true;
      }
    }
    return true;
  }
  async downloadPlots() {
    const chartImagesAll = [];
    let i = 1;
    const values = Object.values(this.components.plots);
    for (const plot of values) {
      const chart = plot.type == "plot" ? plot.getComponents("chart") : plot;
      const chartImages = chart.getChartImages ? chart.getChartImages() : null;
      if (!chartImages) {
        console.log(`The ${chart.type} does not support downloading images yet`);
        continue;
      }
      for (const chartImage of chartImages) {
        if (values.length > 1) chartImage.name = `${i}. ${chartImage.name}`;
        chartImagesAll.push(chartImage);
      }
      i++;
    }
    if (chartImagesAll.length > 0) {
      const filters = [];
      const globalFilterImg = await this.components.nav.getComponents("filter").getFilterImage();
      if (globalFilterImg) filters.push(globalFilterImg);
      downloadSVGsAsPdf(chartImagesAll, "plots", "landscape", filters);
    } else alert("No chart images available for download");
  }
};
var appInit = AppApi.getInitFxn(MassApp);

export {
  appInit
};
//# sourceMappingURL=chunk-QNETLNPB.js.map
