var v = (n) => {
  throw TypeError(n);
};
var C = (n, t, e) => t.has(n) || v("Cannot " + e);
var u = (n, t, e) => (C(n, t, "read from private field"), e ? e.call(n) : t.get(n)), w = (n, t, e) => t.has(n) ? v("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(n) : t.set(n, e), y = (n, t, e, s) => (C(n, t, "write to private field"), s ? s.call(n, e) : t.set(n, e), e);
const E = Object.freeze({
  SMALL: "sm",
  DEFAULT: "md",
  LARGE: "lg",
  EXTRA_LARGE: "xl",
  AUTO: "auto"
}), P = "html-modal", k = `
    <div data-header>
      <span data-title></span>
      <button type="button" data-btn-close aria-label="Close"></button>
    </div>
    <div data-body></div>
    <div data-footer>
      <button type="button" data-btn-cancel></button>
      <button type="button" data-btn-ok></button>
    </div>
`;
class M {
  constructor(t) {
    if (!t) {
      const e = document.createElement("template");
      e.innerHTML = k, t = e;
    }
    this.dialog = null, this.resolve = null, this.reject = null, this.initializeDialog(t), this.addEventListeners(), document.body.appendChild(this.dialog);
  }
  initializeDialog(t) {
    let e;
    if (typeof t == "string") {
      if (e = document.querySelector(t), !e)
        throw new Error(`Template with selector "${t}" not found`);
    } else if (t instanceof HTMLTemplateElement)
      e = t;
    else
      throw new Error("Invalid template provided");
    const s = e.content.cloneNode(!0);
    console.log(s), this.dialog = document.createElement("dialog"), this.dialog.classList.add(P), this.dialog.setAttribute("data-modal", !0), this.dialog.appendChild(s);
  }
  openDialog() {
    this.dialog && (this.dialog.setAttribute("opening", !0), this.dialog.showModal(), this.dialog.addEventListener(
      "animationend",
      () => {
        this.dialog.removeAttribute("opening");
      },
      { once: !0 }
    ));
  }
  closeDialog(t = !1) {
    this.dialog && (this.dialog.setAttribute("closing", !0), this.dialog.addEventListener(
      "animationend",
      () => {
        this.dialog.removeAttribute("closing"), this.dialog.close(), this.resolve(t);
      },
      { once: !0 }
    ));
  }
  setSize(t) {
    this.dialog && (Object.values(E).forEach((e) => {
      e && this.dialog.classList.remove(`modal-${e}`);
    }), t || (t = "md"), t && Object.values(E).includes(t) && this.dialog.classList.add(`modal-${t}`));
  }
  handleBackdropClick(t) {
    t.target === this.dialog && this.closeDialog(!1);
  }
  addEventListeners() {
    const t = this.dialog.querySelector("[data-btn-close]"), e = this.dialog.querySelector("[data-btn-ok]"), s = this.dialog.querySelector("[data-btn-cancel]");
    t == null || t.addEventListener("click", () => this.closeDialog(!1)), e == null || e.addEventListener("click", () => this.closeDialog(!0)), s == null || s.addEventListener("click", () => this.closeDialog(!1)), this.dialog.addEventListener("cancel", () => this.closeDialog(!1)), this.dialog.addEventListener("click", this.handleBackdropClick.bind(this));
  }
  show(t = {}) {
    const { title: e, body: s, okText: i = "Ok", cancelText: o = "Cancel", size: a = void 0 } = t;
    return e !== void 0 && this.setTitle(e), s !== void 0 && this.setBody(s), i !== void 0 && this.setOkText(i), o !== void 0 && this.setCancelText(o), this.setSize(a), new Promise((l, c) => {
      this.resolve = l, this.reject = c, this.openDialog();
    });
  }
  setTitle(t) {
    const e = this.dialog.querySelector("[data-title]");
    e && (e.textContent = t);
  }
  setBody(t) {
    const e = this.dialog.querySelector("[data-body]");
    e && (e.innerHTML = t);
  }
  setOkText(t) {
    const e = this.dialog.querySelector("[data-btn-ok]");
    e && (e.textContent = t, e.style.display = t ? "inline" : "none");
  }
  setCancelText(t) {
    const e = this.dialog.querySelector("[data-btn-cancel]");
    e && (e.textContent = t, e.style.display = t ? "inline" : "none");
  }
  confirm(t = {}) {
    const {
      title: e = "Confirm",
      body: s = "Are you sure?",
      okText: i = "Ok",
      cancelText: o = "Cancel",
      size: a = "md"
    } = t;
    return this.show({ title: e, body: s, okText: i, cancelText: o, size: a });
  }
  alert(t = {}) {
    const {
      title: e = "Alert",
      body: s = "Something happened.",
      okText: i = "Ok",
      size: o = "md"
    } = t;
    return this.show({ title: e, body: s, okText: i, cancelText: void 0, size: o });
  }
  open(t = {}) {
    const s = { ...{ size: "" }, ...t };
    return new Promise((i, o) => {
      this.resolve = i, this.reject = o, this.setSize(s.size), this.openDialog();
    });
  }
  destroy() {
    this.dialog.parentNode.removeChild(this.dialog);
  }
}
const $ = (n = null) => new M(n), D = (n, t = 300) => {
  let e;
  return (...s) => {
    clearTimeout(e), e = setTimeout(() => {
      n.apply(void 0, s);
    }, t);
  };
}, U = () => ("10000000-1000-4000-8000" + -1e11).replace(
  /[018]/g,
  (n) => (n ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> n / 4).toString(16)
), B = (n) => Object.keys(n).length < 1, x = (n) => {
  let t;
  if (typeof n == "string") {
    if (t = document.querySelector(n), !t)
      throw new Error(`Element with selector "${n}" not found`);
  } else if (n instanceof HTMLElement)
    t = n;
  else
    throw new Error("Invalid element provided - must be a selector string or HTML element");
  return t;
};
class q {
  constructor(t, e = {}) {
    this.options = {
      size: e.size || 120,
      strokeWidth: e.strokeWidth || 12,
      bgColor: e.bgColor || "#e6e6e6",
      progressColor: e.progressColor || "#3b82f6",
      progress: e.progress || 0
    }, t = x(t), this.init(t);
  }
  init(t) {
    this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"), this.svg.setAttribute("width", this.options.size), this.svg.setAttribute("height", this.options.size);
    const e = this.options.size / 2, s = (this.options.size - this.options.strokeWidth) / 2;
    this.circumference = 2 * Math.PI * s;
    const i = document.createElementNS("http://www.w3.org/2000/svg", "circle");
    i.setAttribute("cx", e), i.setAttribute("cy", e), i.setAttribute("r", s), i.setAttribute("fill", "none"), i.setAttribute("stroke", this.options.bgColor), i.setAttribute("stroke-width", this.options.strokeWidth), this.progressCircle = document.createElementNS("http://www.w3.org/2000/svg", "circle"), this.progressCircle.setAttribute("class", "progress-ring__circle"), this.progressCircle.setAttribute("cx", e), this.progressCircle.setAttribute("cy", e), this.progressCircle.setAttribute("r", s), this.progressCircle.setAttribute("fill", "none"), this.progressCircle.setAttribute("stroke", this.options.progressColor), this.progressCircle.setAttribute("stroke-width", this.options.strokeWidth), this.progressCircle.setAttribute("stroke-linecap", "round"), this.progressCircle.setAttribute("stroke-dasharray", this.circumference), this.text = document.createElement("span"), this.text.setAttribute("class", "progress-ring__text"), this.container = document.createElement("div"), this.container.setAttribute("class", "progress-ring"), this.svg.appendChild(i), this.svg.appendChild(this.progressCircle), this.container.appendChild(this.svg), this.container.appendChild(this.text), t.appendChild(this.container), this.setProgress(this.options.progress);
  }
  setProgress(t) {
    const e = Math.min(100, Math.max(0, t)), s = this.circumference - e / 100 * this.circumference;
    this.progressCircle.style.strokeDashoffset = s, this.text.textContent = `${Math.round(e)}%`;
  }
}
const _ = (n, t = {}) => new q(n, t), T = Object.freeze({
  autoClose: 5e3,
  position: "top-right"
}), R = [
  "primary",
  "secondary",
  "success",
  "danger",
  "warning",
  "info",
  "light",
  "dark"
];
class A {
  constructor() {
    this.containers = {};
  }
  ensureContainerExists(t) {
    const [e, s] = t.split("-"), i = `rc-toast-container-${e}-${s}`;
    if (!this.containers[i]) {
      const o = document.createElement("div");
      o.id = i, o.className = "rc-toast-container", o.setAttribute("data-horizontal", s), o.setAttribute("data-vertical", e), document.body.appendChild(o), this.containers[i] = o;
    }
    return this.containers[i];
  }
  createToast(t, e = {}) {
    const s = `rc-toast-${Date.now()}`, {
      position: i = T.position,
      // Default to top-right
      autoClose: o = T.autoClose,
      title: a = "",
      type: l = ""
    } = e, c = this.ensureContainerExists(i), r = document.createElement("div");
    return r.id = s, r.className = "rc-toast", l && r.setAttribute("data-type", l), r.innerHTML = `
            <div class='rc-toast-header'>
                ${a ? `<strong>${a}</strong>` : "&nbsp;"}
                <button class='rc-toast-close'>&times;</button>
            </div>
            <div class='rc-toast-body'>${t}</div>`, c.appendChild(r), r.querySelector(".rc-toast-close").addEventListener("click", () => this.hideToast(r)), o > 0 && this.addAutoCloseBehavior(r, o), r;
  }
  addAutoCloseBehavior(t, e) {
    let s = null;
    const i = () => {
      s = setTimeout(() => {
        this.hideToast(t);
      }, e);
    }, o = () => {
      s && (clearTimeout(s), s = null);
    };
    i(), t.addEventListener("mouseenter", o), t.addEventListener("mouseleave", i);
  }
  showToast(t) {
    setTimeout(() => {
      t.classList.add("rc-toast-visible");
    }, 50);
  }
  hideToast(t) {
    if (t) {
      t.classList.remove("rc-toast-visible");
      const e = () => {
        t.parentNode && t.parentNode.removeChild(t);
      };
      t.addEventListener("transitionend", e), setTimeout(() => {
        e();
      }, 1e3);
    }
  }
  toast(t, e = {}) {
    const s = this.createToast(t, e);
    this.showToast(s);
  }
}
R.forEach((n) => {
  A.prototype[n] = function(t, e = {}) {
    this.toast(t, { ...e, type: n });
  };
});
let g = null;
const j = () => (g || (g = new A()), g), N = () => `
<div data-pagination>
    <button class="pagination-button" data-page="first">«</button>
    <button class="pagination-button" data-page="prev">‹</button>
    <span data-pages></span>
    <button class="pagination-button" data-page="next">›</button>
    <button class="pagination-button" data-page="last">»</button>
</div>
`;
class F {
  constructor(t, e, s, i = 10, o = { max: 5 }) {
    this.currentPage = e, this.totalPages = s, this.perPage = i, this.paginationContainer = t instanceof HTMLElement ? t : document.querySelector(t), this.paginationContainer.innerHTML = N(), this.pageNumbersContainer = this.paginationContainer.querySelector("[data-pages]"), this.options = o, this.renderPageNumbers(), this.updateButtonState(), this.init();
  }
  init() {
    this.paginationContainer.addEventListener("click", (t) => {
      const e = t.target.closest("[data-page]");
      if (e) {
        const s = e.getAttribute("data-page");
        let i;
        switch (s) {
          case "first":
            i = 1;
            break;
          case "prev":
            i = this.currentPage - 1;
            break;
          case "next":
            i = this.currentPage + 1;
            break;
          case "last":
            i = this.totalPages;
            break;
          default:
            i = parseInt(s);
        }
        this.goToPage(i);
      }
    });
  }
  goToPage(t) {
    t < 1 && (t = 1), t > this.totalPages && (t = this.totalPages), this.currentPage = t;
    const e = new URL(window.location);
    e.searchParams.set("page", this.currentPage), e.searchParams.set("per-page", this.perPage), window.history.pushState({}, "", e), this.updateButtonState(), this.renderPageNumbers(), location.reload();
  }
  updateButtonState() {
    const t = this.totalPages === 0;
    this.paginationContainer.querySelector('[data-page="first"]').disabled = t || this.currentPage === 1, this.paginationContainer.querySelector('[data-page="prev"]').disabled = t || this.currentPage === 1, this.paginationContainer.querySelector('[data-page="next"]').disabled = t || this.currentPage === this.totalPages, this.paginationContainer.querySelector('[data-page="last"]').disabled = t || this.currentPage === this.totalPages;
  }
  // Function to calculate startPage and endPage
  calculatePageRange(t, e, s) {
    const i = Math.ceil(s / 2);
    let o, a;
    return e <= s ? (o = 1, a = e) : t <= i ? (o = 1, a = Math.min(e, s - 1)) : t + i >= e ? (o = Math.max(1, e - s + 1), a = e) : (o = Math.max(1, t - i + 2), a = Math.min(e, t + i - 2)), { startPage: o, endPage: a };
  }
  renderPageNumbers() {
    this.pageNumbersContainer.innerHTML = "";
    const t = this.options.max, { startPage: e, endPage: s } = this.calculatePageRange(this.currentPage, this.totalPages, t), i = (o, a = o, l = !1, c = !1) => {
      const r = document.createElement("button");
      return r.className = `pagination-button${l ? " active" : ""}`, r.setAttribute("data-page", o), r.style.pointerEvents = c ? "none" : "all", r.textContent = a, r;
    };
    if (!(e < 1 || s > this.totalPages)) {
      if (e > 1) {
        const o = i(0, "...", !1, !0);
        this.pageNumbersContainer.appendChild(o);
      }
      for (let o = e; o <= s; o++)
        this.pageNumbersContainer.appendChild(i(o, o, o === this.currentPage));
      if (s < this.totalPages) {
        const o = i(0, "...", !1, !0);
        this.pageNumbersContainer.appendChild(o);
      }
    }
  }
}
var h;
class z {
  constructor() {
    w(this, h);
    this.loadedStylesheets = /* @__PURE__ */ new Set(), this.loadedModules = /* @__PURE__ */ new Map();
  }
  loadStylesheet(t) {
    if (this.loadedStylesheets.has(t))
      return;
    const e = document.createElement("link");
    e.rel = "stylesheet", e.href = this.getFinalURL(t), document.head.appendChild(e), this.loadedStylesheets.add(t);
  }
  setCacheBuster(t) {
    y(this, h, t);
  }
  getFinalURL(t) {
    if (!u(this, h))
      return t;
    const e = t.includes("?") ? "&" : "?";
    return `${t}${e}v=${u(this, h)}`;
  }
  /**
   * Dynamically imports a JavaScript module with cache-busting.
   * @param {string} src - The module URL.
   * @returns {Promise<any>} The imported module.
   */
  async loadModule(t) {
    if (this.loadedModules.has(t))
      return this.loadedModules.get(t);
    const e = this.resolvePath(t), i = await import(this.getFinalURL(e));
    return this.loadedModules.set(t, i), i;
  }
  /**
   * Resolves a relative path to an absolute path based on the current page location.
   * @param {string} relativePath - The relative path to resolve.
   * @returns {string} - The resolved absolute path.
   */
  resolvePath(t) {
    const e = new URL(window.location.href);
    return new URL(t, e).href;
  }
}
h = new WeakMap();
let p;
const I = () => (p || (p = new z()), p);
class L extends EventTarget {
  /**
  * Dispatches a custom event with the given event name, data, and sender.
  * The event includes a timestamp and an optional sender to provide context.
  *
  * @param {string} eventName - The name of the event to dispatch.
  * @param {any} [data=null] - Additional data to include in the event.
  * @param {Object} [sender=null] - The sender of the event (optional).
  */
  notify(t, e = null, s = null) {
    const i = new CustomEvent(t, {
      detail: {
        data: e,
        sender: s,
        timestamp: Date.now()
      }
    });
    this.dispatchEvent(i);
  }
}
const f = /* @__PURE__ */ new Map(), W = (n) => typeof n != "string" ? new L() : (f.has(n) || f.set(n, new L()), f.get(n));
class H {
  constructor(t) {
    this.baseURL = t, this.defaultHeaders = {
      "Content-Type": "application/json",
      "X-Requested-With": "XMLHttpRequest"
    };
  }
  // Generic method to update default headers
  setHeader(t, e) {
    e ? this.defaultHeaders[t] = e : delete this.defaultHeaders[t];
  }
  // Method to add CSRF token
  addCsrfToken() {
    window.redcap_csrf_token ? this.setHeader("X-Csrf-Token", window.redcap_csrf_token) : console.warn("CSRF token is not available.");
  }
  buildURL(t, e = {}) {
    const s = new URLSearchParams(location.search);
    return s.append("route", t), Object.entries(e).forEach(([i, o]) => {
      s.set(i, o);
    }), `${this.baseURL}?${s.toString()}`;
  }
  async request(t, e = "GET", s = null, i = {}) {
    const { headers: o = {}, params: a = {}, controller: l, ...c } = i, r = l == null ? void 0 : l.signal, b = { ...this.defaultHeaders, ...o }, S = this.buildURL(t, a), m = {
      method: e,
      headers: b,
      signal: r,
      // Include signal if available
      ...c
      // Include any additional config
    };
    s && (m.body = JSON.stringify(s));
    try {
      const d = await fetch(S, m);
      if (!d.ok)
        throw new Error(`HTTP error! status: ${d.status}`);
      return d;
    } catch (d) {
      throw console.error("Error:", d), d;
    }
  }
  // Method for each HTTP verb
  async get(t, e = {}) {
    return this.request(t, "GET", null, e);
  }
  async post(t, e, s = {}) {
    return this.request(t, "POST", e, s);
  }
  async put(t, e, s = {}) {
    return this.request(t, "PUT", e, s);
  }
  async delete(t, e, s = {}) {
    return this.request(t, "DELETE", e, s);
  }
}
const G = (n) => new H(n);
class X {
  constructor() {
    this.channels = /* @__PURE__ */ new Map(), this.channels.set("*", /* @__PURE__ */ new Map());
  }
  /**
   * Adds an observer to a channel for a specific sender.
   * If no channel is provided, subscribes to the "*" channel.
   * @param {Object} sender - The sender this observer is interested in.
   * @param {Function} observer - The observer function to be notified.
   * @param {string} [channel="*"] - The channel to subscribe to.
   */
  addObserver(t, e, s = "*") {
    this.channels.has(s) || this.channels.set(s, /* @__PURE__ */ new Map());
    const i = this.channels.get(s);
    i.has(t) || i.set(t, /* @__PURE__ */ new Set()), i.get(t).add(e);
  }
  /**
   * Removes an observer from a channel for a specific sender.
   * If no channel is provided, unsubscribes from the "*" channel.
   * @param {Object} sender - The sender this observer is interested in.
   * @param {Function} observer - The observer function to remove.
   * @param {string} [channel="*"] - The channel to unsubscribe from.
   */
  removeObserver(t, e, s = "*") {
    if (this.channels.has(s)) {
      const i = this.channels.get(s);
      i.has(t) && (i.get(t).delete(e), i.get(t).size === 0 && i.delete(t));
    }
  }
  /**
   * Sends a notification to observers of a specific channel and sender.
   * Also notifies observers of the "*" channel, regardless of the specific channel.
   * @param {Object} sender - The sender of the event.
   * @param {string} channel - The channel to send notifications on.
   * @param {...any} data - The data to pass to the observers.
   */
  notify(t, e, ...s) {
    if (this.channels.has(e)) {
      const o = this.channels.get(e);
      o.has(t) && o.get(t).forEach((a) => a(...s));
    }
    const i = this.channels.get("*");
    i.has(t) && i.get(t).forEach((o) => o(...s));
  }
}
export {
  z as AssetLoader,
  q as CircleProgress,
  L as EventBus,
  H as FetchClient,
  X as Mediator,
  M as Modal,
  F as Pagination,
  R as TOAST_TYPES,
  A as ToastManager,
  D as debounce,
  x as getElement,
  B as objectIsEmpty,
  I as useAssetLoader,
  _ as useCircleProgress,
  W as useEventBus,
  G as useFetch,
  $ as useModal,
  j as useToaster,
  U as uuidv4
};
