'use strict';

// src/morphlex.ts
var SUPPORTS_MOVE_BEFORE = "moveBefore" in Element.prototype;
var ELEMENT_NODE_TYPE = 1;
var TEXT_NODE_TYPE = 3;
var IS_PARENT_NODE_TYPE = [
  0,
  1,
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  1,
  0,
  1,
  0
];
var Operation = {
  EqualNode: 0,
  SameElement: 1,
  SameNode: 2
};
var candidateNodes = new Set;
var candidateElements = new Set;
var unmatchedNodes = new Set;
var unmatchedElements = new Set;
var whitespaceNodes = new Set;
function morph(from, to, options = {}) {
  if (typeof to === "string")
    to = parseFragment(to).childNodes;
  if (!options.preserveChanges && isParentNode(from))
    flagDirtyInputs(from);
  new Morph(options).morph(from, to);
}
function morphInner(from, to, options = {}) {
  if (typeof to === "string") {
    const fragment = parseFragment(to);
    if (fragment.firstChild && fragment.childNodes.length === 1 && fragment.firstChild.nodeType === ELEMENT_NODE_TYPE) {
      to = fragment.firstChild;
    } else {
      throw new Error("[Morphlex] The string was not a valid HTML element.");
    }
  }
  if (from.nodeType === ELEMENT_NODE_TYPE && to.nodeType === ELEMENT_NODE_TYPE && from.localName === to.localName) {
    if (isParentNode(from))
      flagDirtyInputs(from);
    new Morph(options).visitChildNodes(from, to);
  } else {
    throw new Error("[Morphlex] You can only do an inner morph with matching elements.");
  }
}
function flagDirtyInputs(node) {
  for (const input of node.querySelectorAll("input")) {
    if (input.name && input.value !== input.defaultValue || input.checked !== input.defaultChecked) {
      input.setAttribute("morphlex-dirty", "");
    }
  }
  for (const element of node.querySelectorAll("option")) {
    if (element.value && element.selected !== element.defaultSelected) {
      element.setAttribute("morphlex-dirty", "");
    }
  }
  for (const element of node.querySelectorAll("textarea")) {
    if (element.value !== element.defaultValue) {
      element.setAttribute("morphlex-dirty", "");
    }
  }
}
function parseFragment(string) {
  const template = document.createElement("template");
  template.innerHTML = string.trim();
  return template.content;
}
function moveBefore(parent, node, insertionPoint) {
  if (node === insertionPoint)
    return;
  if (node.parentNode === parent) {
    if (node.nextSibling === insertionPoint)
      return;
    if (SUPPORTS_MOVE_BEFORE) {
      parent.moveBefore(node, insertionPoint);
      return;
    }
  }
  parent.insertBefore(node, insertionPoint);
}

class Morph {
  #idArrayMap = new WeakMap;
  #idSetMap = new WeakMap;
  #options;
  constructor(options = {}) {
    this.#options = options;
  }
  morph(from, to) {
    if (isParentNode(from)) {
      this.#mapIdSets(from);
    }
    if (to instanceof NodeList) {
      this.#mapIdArraysForEach(to);
      this.#morphOneToMany(from, to);
    } else if (isParentNode(to)) {
      this.#mapIdArrays(to);
      this.#morphOneToOne(from, to);
    }
  }
  #morphOneToMany(from, to) {
    const length = to.length;
    if (length === 0) {
      this.#removeNode(from);
    } else if (length === 1) {
      this.#morphOneToOne(from, to[0]);
    } else if (length > 1) {
      const newNodes = [...to];
      this.#morphOneToOne(from, newNodes.shift());
      const insertionPoint = from.nextSibling;
      const parent = from.parentNode || document;
      for (let i = 0;i < newNodes.length; i++) {
        const newNode = newNodes[i];
        if (this.#options.beforeNodeAdded?.(parent, newNode, insertionPoint) ?? true) {
          parent.insertBefore(newNode, insertionPoint);
          this.#options.afterNodeAdded?.(newNode);
        }
      }
    }
  }
  #morphOneToOne(from, to) {
    if (from === to)
      return;
    if (from.isEqualNode(to))
      return;
    if (from.nodeType === ELEMENT_NODE_TYPE && to.nodeType === ELEMENT_NODE_TYPE) {
      if (from.localName === to.localName) {
        this.#morphMatchingElements(from, to);
      } else {
        this.#morphNonMatchingElements(from, to);
      }
    } else {
      this.#morphOtherNode(from, to);
    }
  }
  #morphMatchingElements(from, to) {
    if (!(this.#options.beforeNodeVisited?.(from, to) ?? true))
      return;
    if (from.hasAttributes() || to.hasAttributes()) {
      this.#visitAttributes(from, to);
    }
    if (from.localName === "textarea" && to.localName === "textarea") {
      this.#visitTextArea(from, to);
    } else if (from.hasChildNodes() || to.hasChildNodes()) {
      this.visitChildNodes(from, to);
    }
    this.#options.afterNodeVisited?.(from, to);
  }
  #morphNonMatchingElements(from, to) {
    if (!(this.#options.beforeNodeVisited?.(from, to) ?? true))
      return;
    this.#replaceNode(from, to);
    this.#options.afterNodeVisited?.(from, to);
  }
  #morphOtherNode(from, to) {
    if (!(this.#options.beforeNodeVisited?.(from, to) ?? true))
      return;
    if (from.nodeType === to.nodeType && from.nodeValue !== null && to.nodeValue !== null) {
      from.nodeValue = to.nodeValue;
    } else {
      this.#replaceNode(from, to);
    }
    this.#options.afterNodeVisited?.(from, to);
  }
  #visitAttributes(from, to) {
    if (from.hasAttribute("morphlex-dirty")) {
      from.removeAttribute("morphlex-dirty");
    }
    for (const { name, value } of to.attributes) {
      if (name === "value") {
        if (isInputElement(from) && from.value !== value) {
          if (!this.#options.preserveChanges || from.value === from.defaultValue) {
            from.value = value;
          }
        }
      }
      if (name === "selected") {
        if (isOptionElement(from) && !from.selected) {
          if (!this.#options.preserveChanges || from.selected === from.defaultSelected) {
            from.selected = true;
          }
        }
      }
      if (name === "checked") {
        if (isInputElement(from) && !from.checked) {
          if (!this.#options.preserveChanges || from.checked === from.defaultChecked) {
            from.checked = true;
          }
        }
      }
      const oldValue = from.getAttribute(name);
      if (oldValue !== value && (this.#options.beforeAttributeUpdated?.(from, name, value) ?? true)) {
        from.setAttribute(name, value);
        this.#options.afterAttributeUpdated?.(from, name, oldValue);
      }
    }
    for (const { name, value } of from.attributes) {
      if (!to.hasAttribute(name)) {
        if (name === "selected") {
          if (isOptionElement(from) && from.selected) {
            if (!this.#options.preserveChanges || from.selected === from.defaultSelected) {
              from.selected = false;
            }
          }
        }
        if (name === "checked") {
          if (isInputElement(from) && from.checked) {
            if (!this.#options.preserveChanges || from.checked === from.defaultChecked) {
              from.checked = false;
            }
          }
        }
        if (this.#options.beforeAttributeUpdated?.(from, name, null) ?? true) {
          from.removeAttribute(name);
          this.#options.afterAttributeUpdated?.(from, name, value);
        }
      }
    }
  }
  #visitTextArea(from, to) {
    const newTextContent = to.textContent || "";
    const isModified = from.value !== from.defaultValue;
    if (from.textContent !== newTextContent) {
      from.textContent = newTextContent;
    }
    if (this.#options.preserveChanges && isModified)
      return;
    from.value = from.defaultValue;
  }
  visitChildNodes(from, to) {
    if (!(this.#options.beforeChildrenVisited?.(from) ?? true))
      return;
    const parent = from;
    const fromChildNodes = [...from.childNodes];
    const toChildNodes = [...to.childNodes];
    candidateNodes.clear();
    candidateElements.clear();
    unmatchedNodes.clear();
    unmatchedElements.clear();
    whitespaceNodes.clear();
    const matches = [];
    const op = [];
    const nodeTypeMap = [];
    const candidateNodeTypeMap = [];
    const localNameMap = [];
    const candidateLocalNameMap = [];
    for (let i = 0;i < fromChildNodes.length; i++) {
      const candidate = fromChildNodes[i];
      const nodeType = candidate.nodeType;
      candidateNodeTypeMap[i] = nodeType;
      if (nodeType === ELEMENT_NODE_TYPE) {
        candidateLocalNameMap[i] = candidate.localName;
        candidateElements.add(i);
      } else if (nodeType === TEXT_NODE_TYPE && candidate.textContent?.trim() === "") {
        whitespaceNodes.add(i);
      } else {
        candidateNodes.add(i);
      }
    }
    for (let i = 0;i < toChildNodes.length; i++) {
      const node = toChildNodes[i];
      const nodeType = node.nodeType;
      nodeTypeMap[i] = nodeType;
      if (nodeType === ELEMENT_NODE_TYPE) {
        localNameMap[i] = node.localName;
        unmatchedElements.add(i);
      } else if (nodeType === TEXT_NODE_TYPE && node.textContent?.trim() === "") {
        continue;
      } else {
        unmatchedNodes.add(i);
      }
    }
    for (const unmatchedIndex of unmatchedElements) {
      const localName = localNameMap[unmatchedIndex];
      const element = toChildNodes[unmatchedIndex];
      for (const candidateIndex of candidateElements) {
        if (localName !== candidateLocalNameMap[candidateIndex])
          continue;
        const candidate = fromChildNodes[candidateIndex];
        if (candidate.isEqualNode(element)) {
          matches[unmatchedIndex] = candidateIndex;
          op[unmatchedIndex] = Operation.EqualNode;
          candidateElements.delete(candidateIndex);
          unmatchedElements.delete(unmatchedIndex);
          break;
        }
      }
    }
    for (const unmatchedIndex of unmatchedElements) {
      const element = toChildNodes[unmatchedIndex];
      const id = element.id;
      const idArray = this.#idArrayMap.get(element);
      if (id === "" && !idArray)
        continue;
      candidateLoop:
        for (const candidateIndex of candidateElements) {
          const candidate = fromChildNodes[candidateIndex];
          if (localNameMap[unmatchedIndex] === candidateLocalNameMap[candidateIndex]) {
            if (id !== "" && id === candidate.id) {
              matches[unmatchedIndex] = candidateIndex;
              op[unmatchedIndex] = Operation.SameElement;
              candidateElements.delete(candidateIndex);
              unmatchedElements.delete(unmatchedIndex);
              break candidateLoop;
            }
            if (idArray) {
              const candidateIdSet = this.#idSetMap.get(candidate);
              if (candidateIdSet) {
                for (let i = 0;i < idArray.length; i++) {
                  const arrayId = idArray[i];
                  if (candidateIdSet.has(arrayId)) {
                    matches[unmatchedIndex] = candidateIndex;
                    op[unmatchedIndex] = Operation.SameElement;
                    candidateElements.delete(candidateIndex);
                    unmatchedElements.delete(unmatchedIndex);
                    break candidateLoop;
                  }
                }
              }
            }
          }
        }
    }
    for (const unmatchedIndex of unmatchedElements) {
      const element = toChildNodes[unmatchedIndex];
      const name = element.getAttribute("name");
      const href = element.getAttribute("href");
      const src = element.getAttribute("src");
      for (const candidateIndex of candidateElements) {
        const candidate = fromChildNodes[candidateIndex];
        if (localNameMap[unmatchedIndex] === candidateLocalNameMap[candidateIndex] && (name && name === candidate.getAttribute("name") || href && href === candidate.getAttribute("href") || src && src === candidate.getAttribute("src"))) {
          matches[unmatchedIndex] = candidateIndex;
          op[unmatchedIndex] = Operation.SameElement;
          candidateElements.delete(candidateIndex);
          unmatchedElements.delete(unmatchedIndex);
          break;
        }
      }
    }
    for (const unmatchedIndex of unmatchedElements) {
      const element = toChildNodes[unmatchedIndex];
      const localName = localNameMap[unmatchedIndex];
      for (const candidateIndex of candidateElements) {
        const candidate = fromChildNodes[candidateIndex];
        const candidateLocalName = candidateLocalNameMap[candidateIndex];
        if (localName === candidateLocalName) {
          if (localName === "input" && candidate.type !== element.type) {
            continue;
          }
          matches[unmatchedIndex] = candidateIndex;
          op[unmatchedIndex] = Operation.SameElement;
          candidateElements.delete(candidateIndex);
          unmatchedElements.delete(unmatchedIndex);
          break;
        }
      }
    }
    for (const unmatchedIndex of unmatchedNodes) {
      const node = toChildNodes[unmatchedIndex];
      for (const candidateIndex of candidateNodes) {
        const candidate = fromChildNodes[candidateIndex];
        if (candidate.isEqualNode(node)) {
          matches[unmatchedIndex] = candidateIndex;
          op[unmatchedIndex] = Operation.EqualNode;
          candidateNodes.delete(candidateIndex);
          unmatchedNodes.delete(unmatchedIndex);
          break;
        }
      }
    }
    for (const unmatchedIndex of unmatchedNodes) {
      const nodeType = nodeTypeMap[unmatchedIndex];
      for (const candidateIndex of candidateNodes) {
        if (nodeType === candidateNodeTypeMap[candidateIndex]) {
          matches[unmatchedIndex] = candidateIndex;
          op[unmatchedIndex] = Operation.SameNode;
          candidateNodes.delete(candidateIndex);
          unmatchedNodes.delete(unmatchedIndex);
          break;
        }
      }
    }
    for (const i of candidateNodes)
      this.#removeNode(fromChildNodes[i]);
    for (const i of whitespaceNodes)
      this.#removeNode(fromChildNodes[i]);
    for (const i of candidateElements)
      this.#removeNode(fromChildNodes[i]);
    const lisIndices = longestIncreasingSubsequence(matches);
    const shouldNotMove = new Array(fromChildNodes.length);
    for (let i = 0;i < lisIndices.length; i++) {
      shouldNotMove[matches[lisIndices[i]]] = true;
    }
    let insertionPoint = parent.firstChild;
    for (let i = 0;i < toChildNodes.length; i++) {
      const node = toChildNodes[i];
      const matchInd = matches[i];
      if (matchInd !== undefined) {
        const match = fromChildNodes[matchInd];
        const operation = op[i];
        if (!shouldNotMove[matchInd]) {
          moveBefore(parent, match, insertionPoint);
        }
        if (operation === Operation.EqualNode) ; else if (operation === Operation.SameElement) {
          this.#morphMatchingElements(match, node);
        } else {
          this.#morphOneToOne(match, node);
        }
        insertionPoint = match.nextSibling;
      } else {
        if (this.#options.beforeNodeAdded?.(parent, node, insertionPoint) ?? true) {
          parent.insertBefore(node, insertionPoint);
          this.#options.afterNodeAdded?.(node);
          insertionPoint = node.nextSibling;
        }
      }
    }
    this.#options.afterChildrenVisited?.(from);
  }
  #replaceNode(node, newNode) {
    const parent = node.parentNode || document;
    const insertionPoint = node;
    if ((this.#options.beforeNodeRemoved?.(node) ?? true) && (this.#options.beforeNodeAdded?.(parent, newNode, insertionPoint) ?? true)) {
      parent.insertBefore(newNode, insertionPoint);
      this.#options.afterNodeAdded?.(newNode);
      node.remove();
      this.#options.afterNodeRemoved?.(node);
    }
  }
  #removeNode(node) {
    if (this.#options.beforeNodeRemoved?.(node) ?? true) {
      node.remove();
      this.#options.afterNodeRemoved?.(node);
    }
  }
  #mapIdArraysForEach(nodeList) {
    for (const childNode of nodeList) {
      if (isParentNode(childNode)) {
        this.#mapIdArrays(childNode);
      }
    }
  }
  #mapIdArrays(node) {
    const idArrayMap = this.#idArrayMap;
    for (const element of node.querySelectorAll("[id]")) {
      const id = element.id;
      if (id === "")
        continue;
      let currentElement = element;
      while (currentElement) {
        const idArray = idArrayMap.get(currentElement);
        if (idArray) {
          idArray.push(id);
        } else {
          idArrayMap.set(currentElement, [id]);
        }
        if (currentElement === node)
          break;
        currentElement = currentElement.parentElement;
      }
    }
  }
  #mapIdSets(node) {
    const idSetMap = this.#idSetMap;
    for (const element of node.querySelectorAll("[id]")) {
      const id = element.id;
      if (id === "")
        continue;
      let currentElement = element;
      while (currentElement) {
        const idSet = idSetMap.get(currentElement);
        if (idSet) {
          idSet.add(id);
        } else {
          idSetMap.set(currentElement, new Set([id]));
        }
        if (currentElement === node)
          break;
        currentElement = currentElement.parentElement;
      }
    }
  }
}
function isInputElement(element) {
  return element.localName === "input";
}
function isOptionElement(element) {
  return element.localName === "option";
}
function isParentNode(node) {
  return !!IS_PARENT_NODE_TYPE[node.nodeType];
}
function longestIncreasingSubsequence(sequence) {
  const n = sequence.length;
  if (n === 0)
    return [];
  const smallestEnding = [];
  const indices = [];
  const prev = new Array(n);
  for (let i = 0;i < n; i++) {
    const val = sequence[i];
    if (val === undefined)
      continue;
    let left = 0;
    let right = smallestEnding.length;
    while (left < right) {
      const mid = Math.floor((left + right) / 2);
      if (smallestEnding[mid] < val)
        left = mid + 1;
      else
        right = mid;
    }
    prev[i] = left > 0 ? indices[left - 1] : -1;
    if (left === smallestEnding.length) {
      smallestEnding.push(val);
      indices.push(i);
    } else {
      smallestEnding[left] = val;
      indices[left] = i;
    }
  }
  const result = [];
  if (indices.length === 0)
    return result;
  let curr = indices[indices.length - 1];
  while (curr !== undefined && curr !== -1) {
    result.unshift(curr);
    curr = prev[curr];
  }
  return result;
}

//# debugId=744BF12CDB5DA17964756E2164756E21

const morphlexMorph = morph;
const morphlexMorphInner = morphInner;
function toMorphlexOptions(options) {
    if (!options) {
        return { preserveChanges: true };
    }
    return {
        preserveChanges: options.preserveChanges ?? true,
        beforeNodeVisited: options.beforeNodeVisited,
        afterNodeVisited: options.afterNodeVisited,
        beforeNodeAdded: options.beforeNodeAdded
            ? (node) => options.beforeNodeAdded(node.parentNode, node, null)
            : undefined,
        afterNodeAdded: options.afterNodeAdded,
        beforeNodeRemoved: options.beforeNodeRemoved,
        afterNodeRemoved: options.afterNodeRemoved,
    };
}
const morphlexEngine = {
    morph(target, content, options) {
        const morphlexOpts = toMorphlexOptions(options);
        morphlexMorph(target, content, morphlexOpts);
    },
    morphInner(target, content, options) {
        const morphlexOpts = toMorphlexOptions(options);
        let contentEl;
        if (typeof content === 'string') {
            const wrapper = document.createElement(target.tagName);
            wrapper.innerHTML = content;
            contentEl = wrapper;
        }
        else {
            contentEl = content;
        }
        morphlexMorphInner(target, contentEl, morphlexOpts);
    },
};
let currentEngine = morphlexEngine;
const morphAdapter = {
    morph(target, content, options) {
        currentEngine.morph(target, content, options);
    },
    morphInner(target, content, options) {
        currentEngine.morphInner(target, content, options);
    },
};

function isViewTransitionsSupported() {
    return (typeof document !== 'undefined' && typeof document.startViewTransition === 'function');
}
let config = {
    enabled: isViewTransitionsSupported(),
    defaultTimeout: 5000};
let transitionQueue = [];
let isProcessingQueue = false;
async function processQueue() {
    if (isProcessingQueue || transitionQueue.length === 0) {
        return;
    }
    isProcessingQueue = true;
    while (transitionQueue.length > 0) {
        const item = transitionQueue.shift();
        try {
            await executeTransition(item.callback, item.options);
            item.resolve();
        }
        catch (error) {
            item.reject(error);
        }
    }
    isProcessingQueue = false;
}
async function executeTransition(callback, options) {
    const { skipTransition = false, timeout = config.defaultTimeout } = options;
    if (!config.enabled || !isViewTransitionsSupported() || skipTransition) {
        await callback();
        return;
    }
    const transition = document.startViewTransition(async () => {
        await callback();
    });
    const timeoutPromise = new Promise((_, reject) => {
        setTimeout(() => reject(new Error('View transition timed out')), timeout);
    });
    try {
        await Promise.race([transition.finished, timeoutPromise]);
    }
    catch (error) {
    }
}
function withViewTransition(callback, options = {}) {
    return new Promise((resolve, reject) => {
        transitionQueue.push({
            callback,
            options,
            resolve,
            reject,
        });
        processQueue().catch(reject);
    });
}

function isHTMLElement(value) {
    return (value !== null &&
        typeof value === 'object' &&
        'nodeType' in value &&
        value.nodeType === 1 &&
        'tagName' in value &&
        typeof value.tagName === 'string' &&
        'classList' in value);
}

function isDevMode() {
    if (typeof window !== 'undefined' && window.__HYPERFIXI_PROD__) {
        return false;
    }
    if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production') {
        return false;
    }
    return true;
}
({
    enabled: isDevMode()});

function executeSwap(target, content, strategy, morphOptions) {
    const contentStr = content !== null && !isHTMLElement(content) ? content : '';
    const contentEl = isHTMLElement(content) ? content : null;
    switch (strategy) {
        case 'morph':
            if (content !== null) {
                try {
                    morphAdapter.morphInner(target, contentEl || contentStr, morphOptions);
                }
                catch (error) {
                    console.warn('[HyperFixi] Morph failed, falling back to innerHTML:', error);
                    if (contentEl) {
                        target.innerHTML = '';
                        target.appendChild(contentEl);
                    }
                    else {
                        target.innerHTML = contentStr;
                    }
                }
            }
            break;
        case 'morphOuter':
            if (content !== null) {
                try {
                    morphAdapter.morph(target, contentEl || contentStr, morphOptions);
                }
                catch (error) {
                    console.warn('[HyperFixi] Morph failed, falling back to outerHTML:', error);
                    if (contentEl) {
                        target.replaceWith(contentEl);
                    }
                    else {
                        target.outerHTML = contentStr;
                    }
                }
            }
            break;
        case 'innerHTML':
            if (contentEl) {
                target.innerHTML = '';
                target.appendChild(contentEl);
            }
            else {
                target.innerHTML = contentStr;
            }
            break;
        case 'outerHTML':
            if (contentEl) {
                target.replaceWith(contentEl);
            }
            else {
                target.outerHTML = contentStr;
            }
            break;
        case 'beforeBegin':
            if (contentEl) {
                target.parentElement?.insertBefore(contentEl, target);
            }
            else {
                target.insertAdjacentHTML('beforebegin', contentStr);
            }
            break;
        case 'afterBegin':
            if (contentEl) {
                target.insertBefore(contentEl, target.firstChild);
            }
            else {
                target.insertAdjacentHTML('afterbegin', contentStr);
            }
            break;
        case 'beforeEnd':
            if (contentEl) {
                target.appendChild(contentEl);
            }
            else {
                target.insertAdjacentHTML('beforeend', contentStr);
            }
            break;
        case 'afterEnd':
            if (contentEl) {
                target.parentElement?.insertBefore(contentEl, target.nextSibling);
            }
            else {
                target.insertAdjacentHTML('afterend', contentStr);
            }
            break;
        case 'delete':
            target.remove();
            break;
        case 'none':
            break;
        default:
            throw new Error(`[HyperFixi] swap: unknown strategy "${strategy}"`);
    }
}

function createCustomEvent(eventName, detail, options = {}) {
    return new CustomEvent(eventName, {
        detail: detail !== undefined ? detail : {},
        bubbles: options.bubbles !== undefined ? options.bubbles : true,
        cancelable: options.cancelable !== undefined ? options.cancelable : true,
        composed: options.composed !== undefined ? options.composed : false,
    });
}
function dispatchCustomEvent(target, eventName, detail = {}, options = {}) {
    const event = createCustomEvent(eventName, detail, options);
    target.dispatchEvent(event);
    return event;
}
function dispatchLokaScriptEvent(target, eventName, detail = {}, options = {}) {
    const lokascriptEvent = dispatchCustomEvent(target, `lokascript:${eventName}`, detail, options);
    const hyperfixiEvent = dispatchCustomEvent(target, `hyperfixi:${eventName}`, detail, options);
    return {
        lokascript: lokascriptEvent,
        hyperfixi: hyperfixiEvent,
    };
}

function createHistorySwap(config) {
    const { target, strategy = 'morph', useViewTransition = false, fetchOptions = {}, transformUrl, onBeforeFetch, onAfterSwap, onError, } = config;
    const resolveTarget = () => {
        if (typeof target === 'string') {
            const element = document.querySelector(target);
            return isHTMLElement(element) ? element : null;
        }
        return isHTMLElement(target) ? target : null;
    };
    const handlePopstate = async (event) => {
        const targetElement = resolveTarget();
        if (!targetElement) {
            console.warn(`HistorySwap: target "${target}" not found`);
            return;
        }
        let url = window.location.href;
        if (transformUrl) {
            url = transformUrl(url);
        }
        try {
            if (onBeforeFetch) {
                await onBeforeFetch(url);
            }
            targetElement.classList.add('hx-swapping');
            const response = await fetch(url, {
                method: 'GET',
                headers: {
                    Accept: 'text/html',
                    'HX-Request': 'true',
                    'HX-History-Restore-Request': 'true',
                },
                ...fetchOptions,
            });
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            const html = await response.text();
            const performSwap = () => {
                executeSwap(targetElement, html, strategy);
            };
            if (useViewTransition && isViewTransitionsSupported()) {
                await withViewTransition(performSwap);
            }
            else {
                performSwap();
            }
            targetElement.classList.remove('hx-swapping');
            if (onAfterSwap) {
                await onAfterSwap(url, html);
            }
            dispatchLokaScriptEvent(window, 'historyswap', { url, strategy, target });
        }
        catch (error) {
            targetElement.classList.remove('hx-swapping');
            if (onError) {
                onError(error, url);
            }
            else {
                console.error('HistorySwap fetch failed:', error);
            }
        }
    };
    window.addEventListener('popstate', handlePopstate);
    return {
        destroy: () => {
            window.removeEventListener('popstate', handlePopstate);
        },
        config,
    };
}
const HistorySwapBehavior = {
    name: 'HistorySwap',
    init(element, params = {}) {
        const config = {
            target: params.target || element,
            strategy: params.strategy || 'morph',
            useViewTransition: Boolean(params.useViewTransition),
        };
        return createHistorySwap(config);
    },
    destroy(instance) {
        instance.destroy();
    },
};
function registerHistorySwap(registry) {
    if (registry instanceof Map) {
        registry.set('HistorySwap', HistorySwapBehavior);
    }
    else if (registry && typeof registry.set === 'function') {
        registry.set('HistorySwap', HistorySwapBehavior);
    }
    else if (registry && typeof registry === 'object') {
        registry['HistorySwap'] = HistorySwapBehavior;
    }
}
const historySwapHyperscript = `
behavior HistorySwap(target, strategy)
  init
    if no target then set target to me end
    if no strategy then set strategy to 'morph' end
  end

  on popstate from window
    add .hx-swapping to target
    fetch location.href as html
    if strategy is 'morph' then
      swap target with it
    else if strategy is 'innerHTML' then
      swap innerHTML of target with it
    else
      swap target with it
    end
    remove .hx-swapping from target
  end
end
`;

function isExternalUrl(url) {
    try {
        const parsed = new URL(url, window.location.origin);
        return parsed.origin !== window.location.origin;
    }
    catch {
        return false;
    }
}

function shouldBoostLink(link) {
    if (isExternalUrl(link.href)) {
        return false;
    }
    if (link.target && link.target !== '_self') {
        return false;
    }
    if (link.hasAttribute('download')) {
        return false;
    }
    if (link.hasAttribute('data-no-boost') || link.hasAttribute('hx-boost-off')) {
        return false;
    }
    if (link.protocol === 'javascript:' || link.protocol === 'mailto:') {
        return false;
    }
    return true;
}
function shouldHandleClick(event) {
    if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
        return false;
    }
    if (event.button !== 0) {
        return false;
    }
    return true;
}
function createBoosted(config) {
    const { container, target, linkSelector = 'a[href]', formSelector = 'form', boostForms = false, strategy = 'morph', pushUrl = true, useViewTransition = false, fetchOptions = {}, onBeforeFetch, onAfterSwap, onError, } = config;
    const resolveTarget = () => {
        if (!target) {
            return document.body;
        }
        if (typeof target === 'string') {
            const element = document.querySelector(target);
            return isHTMLElement(element) ? element : null;
        }
        return isHTMLElement(target) ? target : null;
    };
    const boost = async (url, method = 'GET', body = null) => {
        const targetElement = resolveTarget();
        if (!targetElement) {
            console.warn(`Boosted: target "${target}" not found`);
            return;
        }
        try {
            if (onBeforeFetch) {
                const result = await onBeforeFetch(url, method);
                if (result === false) {
                    return;
                }
            }
            targetElement.classList.add('hx-swapping');
            container.classList.add('hx-boosting');
            const requestInit = {
                method,
                headers: {
                    Accept: 'text/html',
                    'HX-Request': 'true',
                    'HX-Boosted': 'true',
                },
                ...fetchOptions,
            };
            if (body && method !== 'GET') {
                requestInit.body = body;
            }
            const response = await fetch(url, requestInit);
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            const html = await response.text();
            const performSwap = () => {
                executeSwap(targetElement, html, strategy);
            };
            if (useViewTransition && isViewTransitionsSupported()) {
                await withViewTransition(performSwap);
            }
            else {
                performSwap();
            }
            targetElement.classList.remove('hx-swapping');
            container.classList.remove('hx-boosting');
            if (pushUrl && method === 'GET') {
                window.history.pushState(null, '', url);
            }
            if (onAfterSwap) {
                await onAfterSwap(url, html);
            }
            dispatchLokaScriptEvent(window, 'boosted', { url, method, strategy, target });
        }
        catch (error) {
            targetElement.classList.remove('hx-swapping');
            container.classList.remove('hx-boosting');
            if (onError) {
                onError(error, url);
            }
            else {
                console.error('Boosted fetch failed:', error);
                window.location.href = url;
            }
        }
    };
    const handleClick = async (event) => {
        const link = event.target.closest(linkSelector);
        if (!link) {
            return;
        }
        if (!shouldHandleClick(event)) {
            return;
        }
        if (!shouldBoostLink(link)) {
            return;
        }
        event.preventDefault();
        event.stopPropagation();
        await boost(link.href);
    };
    const handleSubmit = async (event) => {
        if (!boostForms) {
            return;
        }
        const form = event.target.closest(formSelector);
        if (!form) {
            return;
        }
        if (form.hasAttribute('data-no-boost') || form.hasAttribute('hx-boost-off')) {
            return;
        }
        if (form.target && form.target !== '_self') {
            return;
        }
        event.preventDefault();
        event.stopPropagation();
        const method = (form.method || 'GET').toUpperCase();
        const formData = new FormData(form);
        let url;
        let body = null;
        if (method === 'GET') {
            const params = new URLSearchParams();
            formData.forEach((value, key) => {
                params.append(key, String(value));
            });
            url = `${form.action || window.location.pathname}?${params.toString()}`;
        }
        else {
            url = form.action || window.location.pathname;
            body = formData;
        }
        await boost(url, method, body);
    };
    container.addEventListener('click', handleClick);
    if (boostForms) {
        container.addEventListener('submit', handleSubmit);
    }
    return {
        destroy: () => {
            container.removeEventListener('click', handleClick);
            container.removeEventListener('submit', handleSubmit);
        },
        config,
        boost,
    };
}
const BoostedBehavior = {
    name: 'Boosted',
    init(element, params = {}) {
        const config = {
            container: element,
            target: params.target,
            linkSelector: params.linkSelector || 'a[href]',
            formSelector: params.formSelector || 'form',
            boostForms: Boolean(params.boostForms),
            strategy: params.strategy || 'morph',
            pushUrl: params.pushUrl !== false,
            useViewTransition: Boolean(params.useViewTransition),
        };
        return createBoosted(config);
    },
    destroy(instance) {
        instance.destroy();
    },
};
function registerBoosted(registry) {
    if (registry instanceof Map) {
        registry.set('Boosted', BoostedBehavior);
    }
    else if (registry && typeof registry.set === 'function') {
        registry.set('Boosted', BoostedBehavior);
    }
    else if (registry && typeof registry === 'object') {
        registry['Boosted'] = BoostedBehavior;
    }
}
const boostedHyperscript = `
behavior Boosted(target, pushHistory)
  init
    if no target then set target to document.body end
    if no pushHistory then set pushHistory to true end
  end

  on click from <a[href]/> in me
    -- Skip if modifier keys pressed
    if event.metaKey or event.ctrlKey or event.shiftKey or event.altKey then exit end

    -- Skip external links
    set href to the @href of the target
    if href starts with 'http' and not (href starts with location.origin) then exit end

    -- Skip links with target attribute
    if the target has @target then exit end

    halt the event
    add .hx-swapping to target
    fetch href as html
    swap target with it
    remove .hx-swapping from target
    if pushHistory then push url href end
  end
end
`;

function registerAllBehaviors(registry) {
    const { registerHistorySwap } = require('./history-swap');
    const { registerBoosted } = require('./boosted');
    registerHistorySwap(registry);
    registerBoosted(registry);
}

exports.BoostedBehavior = BoostedBehavior;
exports.HistorySwapBehavior = HistorySwapBehavior;
exports.boostedHyperscript = boostedHyperscript;
exports.createBoosted = createBoosted;
exports.createHistorySwap = createHistorySwap;
exports.historySwapHyperscript = historySwapHyperscript;
exports.registerAllBehaviors = registerAllBehaviors;
exports.registerBoosted = registerBoosted;
exports.registerHistorySwap = registerHistorySwap;
//# sourceMappingURL=index.js.map
