var DOCUMENT_FRAGMENT_NODE = 11;

function morphAttrs(fromNode, toNode) {
  var toNodeAttrs = toNode.attributes;
  var attr;
  var attrName;
  var attrNamespaceURI;
  var attrValue;
  var fromValue;
  if (toNode.nodeType === DOCUMENT_FRAGMENT_NODE || fromNode.nodeType === DOCUMENT_FRAGMENT_NODE) {
    return;
  }
  for (var i = toNodeAttrs.length - 1; i >= 0; i--) {
    attr = toNodeAttrs[i];
    attrName = attr.name;
    attrNamespaceURI = attr.namespaceURI;
    attrValue = attr.value;
    if (attrNamespaceURI) {
      attrName = attr.localName || attrName;
      fromValue = fromNode.getAttributeNS(attrNamespaceURI, attrName);
      if (fromValue !== attrValue) {
        if (attr.prefix === "xmlns") {
          attrName = attr.name;
        }
        fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue);
      }
    } else {
      fromValue = fromNode.getAttribute(attrName);
      if (fromValue !== attrValue) {
        fromNode.setAttribute(attrName, attrValue);
      }
    }
  }
  var fromNodeAttrs = fromNode.attributes;
  for (var d = fromNodeAttrs.length - 1; d >= 0; d--) {
    attr = fromNodeAttrs[d];
    attrName = attr.name;
    attrNamespaceURI = attr.namespaceURI;
    if (attrNamespaceURI) {
      attrName = attr.localName || attrName;
      if (!toNode.hasAttributeNS(attrNamespaceURI, attrName)) {
        fromNode.removeAttributeNS(attrNamespaceURI, attrName);
      }
    } else {
      if (!toNode.hasAttribute(attrName)) {
        fromNode.removeAttribute(attrName);
      }
    }
  }
}

var range;

var NS_XHTML = "http://www.w3.org/1999/xhtml";

var doc = typeof document === "undefined" ? undefined : document;

var HAS_TEMPLATE_SUPPORT = !!doc && "content" in doc.createElement("template");

var HAS_RANGE_SUPPORT = !!doc && doc.createRange && "createContextualFragment" in doc.createRange();

function createFragmentFromTemplate(str) {
  var template = doc.createElement("template");
  template.innerHTML = str;
  return template.content.childNodes[0];
}

function createFragmentFromRange(str) {
  if (!range) {
    range = doc.createRange();
    range.selectNode(doc.body);
  }
  var fragment = range.createContextualFragment(str);
  return fragment.childNodes[0];
}

function createFragmentFromWrap(str) {
  var fragment = doc.createElement("body");
  fragment.innerHTML = str;
  return fragment.childNodes[0];
}

function toElement(str) {
  str = str.trim();
  if (HAS_TEMPLATE_SUPPORT) {
    return createFragmentFromTemplate(str);
  } else if (HAS_RANGE_SUPPORT) {
    return createFragmentFromRange(str);
  }
  return createFragmentFromWrap(str);
}

function compareNodeNames(fromEl, toEl) {
  var fromNodeName = fromEl.nodeName;
  var toNodeName = toEl.nodeName;
  var fromCodeStart, toCodeStart;
  if (fromNodeName === toNodeName) {
    return true;
  }
  fromCodeStart = fromNodeName.charCodeAt(0);
  toCodeStart = toNodeName.charCodeAt(0);
  if (fromCodeStart <= 90 && toCodeStart >= 97) {
    return fromNodeName === toNodeName.toUpperCase();
  } else if (toCodeStart <= 90 && fromCodeStart >= 97) {
    return toNodeName === fromNodeName.toUpperCase();
  } else {
    return false;
  }
}

function createElementNS(name, namespaceURI) {
  return !namespaceURI || namespaceURI === NS_XHTML ? doc.createElement(name) : doc.createElementNS(namespaceURI, name);
}

function moveChildren(fromEl, toEl) {
  var curChild = fromEl.firstChild;
  while (curChild) {
    var nextChild = curChild.nextSibling;
    toEl.appendChild(curChild);
    curChild = nextChild;
  }
  return toEl;
}

function syncBooleanAttrProp(fromEl, toEl, name) {
  if (fromEl[name] !== toEl[name]) {
    fromEl[name] = toEl[name];
    if (fromEl[name]) {
      fromEl.setAttribute(name, "");
    } else {
      fromEl.removeAttribute(name);
    }
  }
}

var specialElHandlers = {
  OPTION: function(fromEl, toEl) {
    var parentNode = fromEl.parentNode;
    if (parentNode) {
      var parentName = parentNode.nodeName.toUpperCase();
      if (parentName === "OPTGROUP") {
        parentNode = parentNode.parentNode;
        parentName = parentNode && parentNode.nodeName.toUpperCase();
      }
      if (parentName === "SELECT" && !parentNode.hasAttribute("multiple")) {
        if (fromEl.hasAttribute("selected") && !toEl.selected) {
          fromEl.setAttribute("selected", "selected");
          fromEl.removeAttribute("selected");
        }
        parentNode.selectedIndex = -1;
      }
    }
    syncBooleanAttrProp(fromEl, toEl, "selected");
  },
  INPUT: function(fromEl, toEl) {
    syncBooleanAttrProp(fromEl, toEl, "checked");
    syncBooleanAttrProp(fromEl, toEl, "disabled");
    if (fromEl.value !== toEl.value) {
      fromEl.value = toEl.value;
    }
    if (!toEl.hasAttribute("value")) {
      fromEl.removeAttribute("value");
    }
  },
  TEXTAREA: function(fromEl, toEl) {
    var newValue = toEl.value;
    if (fromEl.value !== newValue) {
      fromEl.value = newValue;
    }
    var firstChild = fromEl.firstChild;
    if (firstChild) {
      var oldValue = firstChild.nodeValue;
      if (oldValue == newValue || !newValue && oldValue == fromEl.placeholder) {
        return;
      }
      firstChild.nodeValue = newValue;
    }
  },
  SELECT: function(fromEl, toEl) {
    if (!toEl.hasAttribute("multiple")) {
      var selectedIndex = -1;
      var i = 0;
      var curChild = fromEl.firstChild;
      var optgroup;
      var nodeName;
      while (curChild) {
        nodeName = curChild.nodeName && curChild.nodeName.toUpperCase();
        if (nodeName === "OPTGROUP") {
          optgroup = curChild;
          curChild = optgroup.firstChild;
        } else {
          if (nodeName === "OPTION") {
            if (curChild.hasAttribute("selected")) {
              selectedIndex = i;
              break;
            }
            i++;
          }
          curChild = curChild.nextSibling;
          if (!curChild && optgroup) {
            curChild = optgroup.nextSibling;
            optgroup = null;
          }
        }
      }
      fromEl.selectedIndex = selectedIndex;
    }
  }
};

var ELEMENT_NODE = 1;

var DOCUMENT_FRAGMENT_NODE$1 = 11;

var TEXT_NODE = 3;

var COMMENT_NODE = 8;

function noop() {}

function defaultGetNodeKey(node) {
  if (node) {
    return node.getAttribute && node.getAttribute("id") || node.id;
  }
}

function morphdomFactory(morphAttrs) {
  return function morphdom(fromNode, toNode, options) {
    if (!options) {
      options = {};
    }
    if (typeof toNode === "string") {
      if (fromNode.nodeName === "#document" || fromNode.nodeName === "HTML" || fromNode.nodeName === "BODY") {
        var toNodeHtml = toNode;
        toNode = doc.createElement("html");
        toNode.innerHTML = toNodeHtml;
      } else {
        toNode = toElement(toNode);
      }
    }
    var getNodeKey = options.getNodeKey || defaultGetNodeKey;
    var onBeforeNodeAdded = options.onBeforeNodeAdded || noop;
    var onNodeAdded = options.onNodeAdded || noop;
    var onBeforeElUpdated = options.onBeforeElUpdated || noop;
    var onElUpdated = options.onElUpdated || noop;
    var onBeforeNodeDiscarded = options.onBeforeNodeDiscarded || noop;
    var onNodeDiscarded = options.onNodeDiscarded || noop;
    var onBeforeElChildrenUpdated = options.onBeforeElChildrenUpdated || noop;
    var childrenOnly = options.childrenOnly === true;
    var fromNodesLookup = Object.create(null);
    var keyedRemovalList = [];
    function addKeyedRemoval(key) {
      keyedRemovalList.push(key);
    }
    function walkDiscardedChildNodes(node, skipKeyedNodes) {
      if (node.nodeType === ELEMENT_NODE) {
        var curChild = node.firstChild;
        while (curChild) {
          var key = undefined;
          if (skipKeyedNodes && (key = getNodeKey(curChild))) {
            addKeyedRemoval(key);
          } else {
            onNodeDiscarded(curChild);
            if (curChild.firstChild) {
              walkDiscardedChildNodes(curChild, skipKeyedNodes);
            }
          }
          curChild = curChild.nextSibling;
        }
      }
    }
    function removeNode(node, parentNode, skipKeyedNodes) {
      if (onBeforeNodeDiscarded(node) === false) {
        return;
      }
      if (parentNode) {
        parentNode.removeChild(node);
      }
      onNodeDiscarded(node);
      walkDiscardedChildNodes(node, skipKeyedNodes);
    }
    function indexTree(node) {
      if (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE$1) {
        var curChild = node.firstChild;
        while (curChild) {
          var key = getNodeKey(curChild);
          if (key) {
            fromNodesLookup[key] = curChild;
          }
          indexTree(curChild);
          curChild = curChild.nextSibling;
        }
      }
    }
    indexTree(fromNode);
    function handleNodeAdded(el) {
      onNodeAdded(el);
      var curChild = el.firstChild;
      while (curChild) {
        var nextSibling = curChild.nextSibling;
        var key = getNodeKey(curChild);
        if (key) {
          var unmatchedFromEl = fromNodesLookup[key];
          if (unmatchedFromEl && compareNodeNames(curChild, unmatchedFromEl)) {
            curChild.parentNode.replaceChild(unmatchedFromEl, curChild);
            morphEl(unmatchedFromEl, curChild);
          } else {
            handleNodeAdded(curChild);
          }
        } else {
          handleNodeAdded(curChild);
        }
        curChild = nextSibling;
      }
    }
    function cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey) {
      while (curFromNodeChild) {
        var fromNextSibling = curFromNodeChild.nextSibling;
        if (curFromNodeKey = getNodeKey(curFromNodeChild)) {
          addKeyedRemoval(curFromNodeKey);
        } else {
          removeNode(curFromNodeChild, fromEl, true);
        }
        curFromNodeChild = fromNextSibling;
      }
    }
    function morphEl(fromEl, toEl, childrenOnly) {
      var toElKey = getNodeKey(toEl);
      if (toElKey) {
        delete fromNodesLookup[toElKey];
      }
      if (!childrenOnly) {
        if (onBeforeElUpdated(fromEl, toEl) === false) {
          return;
        }
        morphAttrs(fromEl, toEl);
        onElUpdated(fromEl);
        if (onBeforeElChildrenUpdated(fromEl, toEl) === false) {
          return;
        }
      }
      if (fromEl.nodeName !== "TEXTAREA") {
        morphChildren(fromEl, toEl);
      } else {
        specialElHandlers.TEXTAREA(fromEl, toEl);
      }
    }
    function morphChildren(fromEl, toEl) {
      var curToNodeChild = toEl.firstChild;
      var curFromNodeChild = fromEl.firstChild;
      var curToNodeKey;
      var curFromNodeKey;
      var fromNextSibling;
      var toNextSibling;
      var matchingFromEl;
      outer: while (curToNodeChild) {
        toNextSibling = curToNodeChild.nextSibling;
        curToNodeKey = getNodeKey(curToNodeChild);
        while (curFromNodeChild) {
          fromNextSibling = curFromNodeChild.nextSibling;
          if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
            curToNodeChild = toNextSibling;
            curFromNodeChild = fromNextSibling;
            continue outer;
          }
          curFromNodeKey = getNodeKey(curFromNodeChild);
          var curFromNodeType = curFromNodeChild.nodeType;
          var isCompatible = undefined;
          if (curFromNodeType === curToNodeChild.nodeType) {
            if (curFromNodeType === ELEMENT_NODE) {
              if (curToNodeKey) {
                if (curToNodeKey !== curFromNodeKey) {
                  if (matchingFromEl = fromNodesLookup[curToNodeKey]) {
                    if (fromNextSibling === matchingFromEl) {
                      isCompatible = false;
                    } else {
                      fromEl.insertBefore(matchingFromEl, curFromNodeChild);
                      if (curFromNodeKey) {
                        addKeyedRemoval(curFromNodeKey);
                      } else {
                        removeNode(curFromNodeChild, fromEl, true);
                      }
                      curFromNodeChild = matchingFromEl;
                    }
                  } else {
                    isCompatible = false;
                  }
                }
              } else if (curFromNodeKey) {
                isCompatible = false;
              }
              isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
              if (isCompatible) {
                morphEl(curFromNodeChild, curToNodeChild);
              }
            } else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
              isCompatible = true;
              if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
                curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
              }
            }
          }
          if (isCompatible) {
            curToNodeChild = toNextSibling;
            curFromNodeChild = fromNextSibling;
            continue outer;
          }
          if (curFromNodeKey) {
            addKeyedRemoval(curFromNodeKey);
          } else {
            removeNode(curFromNodeChild, fromEl, true);
          }
          curFromNodeChild = fromNextSibling;
        }
        if (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
          fromEl.appendChild(matchingFromEl);
          morphEl(matchingFromEl, curToNodeChild);
        } else {
          var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
          if (onBeforeNodeAddedResult !== false) {
            if (onBeforeNodeAddedResult) {
              curToNodeChild = onBeforeNodeAddedResult;
            }
            if (curToNodeChild.actualize) {
              curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
            }
            fromEl.appendChild(curToNodeChild);
            handleNodeAdded(curToNodeChild);
          }
        }
        curToNodeChild = toNextSibling;
        curFromNodeChild = fromNextSibling;
      }
      cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey);
      var specialElHandler = specialElHandlers[fromEl.nodeName];
      if (specialElHandler) {
        specialElHandler(fromEl, toEl);
      }
    }
    var morphedNode = fromNode;
    var morphedNodeType = morphedNode.nodeType;
    var toNodeType = toNode.nodeType;
    if (!childrenOnly) {
      if (morphedNodeType === ELEMENT_NODE) {
        if (toNodeType === ELEMENT_NODE) {
          if (!compareNodeNames(fromNode, toNode)) {
            onNodeDiscarded(fromNode);
            morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI));
          }
        } else {
          morphedNode = toNode;
        }
      } else if (morphedNodeType === TEXT_NODE || morphedNodeType === COMMENT_NODE) {
        if (toNodeType === morphedNodeType) {
          if (morphedNode.nodeValue !== toNode.nodeValue) {
            morphedNode.nodeValue = toNode.nodeValue;
          }
          return morphedNode;
        } else {
          morphedNode = toNode;
        }
      }
    }
    if (morphedNode === toNode) {
      onNodeDiscarded(fromNode);
    } else {
      if (toNode.isSameNode && toNode.isSameNode(morphedNode)) {
        return;
      }
      morphEl(morphedNode, toNode, childrenOnly);
      if (keyedRemovalList) {
        for (var i = 0, len = keyedRemovalList.length; i < len; i++) {
          var elToRemove = fromNodesLookup[keyedRemovalList[i]];
          if (elToRemove) {
            removeNode(elToRemove, elToRemove.parentNode, false);
          }
        }
      }
    }
    if (!childrenOnly && morphedNode !== fromNode && fromNode.parentNode) {
      if (morphedNode.actualize) {
        morphedNode = morphedNode.actualize(fromNode.ownerDocument || doc);
      }
      fromNode.parentNode.replaceChild(morphedNode, fromNode);
    }
    return morphedNode;
  };
}

var morphdom = morphdomFactory(morphAttrs);

var name = "cable_ready";

var version = "5.0.1";

var description = "CableReady helps you create great real-time user experiences by making it simple to trigger client-side DOM changes from server-side Ruby.";

var keywords = [ "ruby", "rails", "websockets", "actioncable", "cable", "ssr", "stimulus_reflex", "client-side", "dom" ];

var homepage = "https://cableready.stimulusreflex.com";

var bugs = "https://github.com/stimulusreflex/cable_ready/issues";

var repository = "https://github.com/stimulusreflex/cable_ready";

var license = "MIT";

var author = "Nathan Hopkins <natehop@gmail.com>";

var contributors = [ "Andrew Mason <andrewmcodes@protonmail.com>", "Julian Rubisch <julian@julianrubisch.at>", "Marco Roth <marco.roth@intergga.ch>", "Nathan Hopkins <natehop@gmail.com>" ];

var main = "./dist/cable_ready.js";

var module = "./dist/cable_ready.js";

var browser = "./dist/cable_ready.js";

var unpkg = "./dist/cable_ready.umd.js";

var umd = "./dist/cable_ready.umd.js";

var files = [ "dist/*", "javascript/*" ];

var scripts = {
  lint: "yarn run format --check",
  format: "yarn run prettier-standard ./javascript/**/*.js rollup.config.mjs",
  build: "yarn rollup -c",
  watch: "yarn rollup -wc",
  test: "web-test-runner javascript/test/**/*.test.js",
  "docs:dev": "vitepress dev docs",
  "docs:build": "vitepress build docs && cp ./docs/_redirects ./docs/.vitepress/dist",
  "docs:preview": "vitepress preview docs"
};

var dependencies = {
  morphdom: "2.6.1"
};

var devDependencies = {
  "@open-wc/testing": "^3.1.7",
  "@rollup/plugin-json": "^6.0.0",
  "@rollup/plugin-node-resolve": "^15.0.1",
  "@rollup/plugin-terser": "^0.4.0",
  "@web/dev-server-esbuild": "^0.3.3",
  "@web/dev-server-rollup": "^0.3.21",
  "@web/test-runner": "^0.15.1",
  "prettier-standard": "^16.4.1",
  rollup: "^3.19.1",
  sinon: "^15.0.2",
  vite: "^4.1.4",
  vitepress: "^1.0.0-beta.1",
  "vitepress-plugin-search": "^1.0.4-alpha.19"
};

var packageInfo = {
  name: name,
  version: version,
  description: description,
  keywords: keywords,
  homepage: homepage,
  bugs: bugs,
  repository: repository,
  license: license,
  author: author,
  contributors: contributors,
  main: main,
  module: module,
  browser: browser,
  import: "./dist/cable_ready.js",
  unpkg: unpkg,
  umd: umd,
  files: files,
  scripts: scripts,
  dependencies: dependencies,
  devDependencies: devDependencies
};

const inputTags = {
  INPUT: true,
  TEXTAREA: true,
  SELECT: true
};

const mutableTags = {
  INPUT: true,
  TEXTAREA: true,
  OPTION: true
};

const textInputTypes = {
  "datetime-local": true,
  "select-multiple": true,
  "select-one": true,
  color: true,
  date: true,
  datetime: true,
  email: true,
  month: true,
  number: true,
  password: true,
  range: true,
  search: true,
  tel: true,
  text: true,
  textarea: true,
  time: true,
  url: true,
  week: true
};

let activeElement;

var ActiveElement = {
  get element() {
    return activeElement;
  },
  set(element) {
    activeElement = element;
  }
};

const isTextInput = element => inputTags[element.tagName] && textInputTypes[element.type];

const assignFocus = selector => {
  const element = selector && selector.nodeType === Node.ELEMENT_NODE ? selector : document.querySelector(selector);
  const focusElement = element || ActiveElement.element;
  if (focusElement && focusElement.focus) focusElement.focus();
};

const dispatch = (element, name, detail = {}) => {
  const init = {
    bubbles: true,
    cancelable: true,
    detail: detail
  };
  const event = new CustomEvent(name, init);
  element.dispatchEvent(event);
  if (window.jQuery) window.jQuery(element).trigger(name, detail);
};

const xpathToElement = xpath => document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;

const xpathToElementArray = (xpath, reverse = false) => {
  const snapshotList = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  const snapshots = [];
  for (let i = 0; i < snapshotList.snapshotLength; i++) {
    snapshots.push(snapshotList.snapshotItem(i));
  }
  return reverse ? snapshots.reverse() : snapshots;
};

const getClassNames = names => Array.from(names).flat();

const processElements = (operation, callback) => {
  Array.from(operation.selectAll ? operation.element : [ operation.element ]).forEach(callback);
};

const kebabize = createCompounder((function(result, word, index) {
  return result + (index ? "-" : "") + word.toLowerCase();
}));

function createCompounder(callback) {
  return function(str) {
    return words(str).reduce(callback, "");
  };
}

const words = str => {
  str = str == null ? "" : str;
  return str.match(/([A-Z]{2,}|[0-9]+|[A-Z]?[a-z]+|[A-Z])/g) || [];
};

const operate = (operation, callback) => {
  if (!operation.cancel) {
    operation.delay ? setTimeout(callback, operation.delay) : callback();
    return true;
  }
  return false;
};

const before = (target, operation) => dispatch(target, `cable-ready:before-${kebabize(operation.operation)}`, operation);

const after = (target, operation) => dispatch(target, `cable-ready:after-${kebabize(operation.operation)}`, operation);

function debounce(fn, delay = 250) {
  let timer;
  return (...args) => {
    const callback = () => fn.apply(this, args);
    if (timer) clearTimeout(timer);
    timer = setTimeout(callback, delay);
  };
}

function handleErrors(response) {
  if (!response.ok) throw Error(response.statusText);
  return response;
}

function safeScalar(val) {
  if (val !== undefined && ![ "string", "number", "boolean" ].includes(typeof val)) console.warn(`Operation expects a string, number or boolean, but got ${val} (${typeof val})`);
  return val != null ? val : "";
}

function safeString(str) {
  if (str !== undefined && typeof str !== "string") console.warn(`Operation expects a string, but got ${str} (${typeof str})`);
  return str != null ? String(str) : "";
}

function safeArray(arr) {
  if (arr !== undefined && !Array.isArray(arr)) console.warn(`Operation expects an array, but got ${arr} (${typeof arr})`);
  return arr != null ? Array.from(arr) : [];
}

function safeObject(obj) {
  if (obj !== undefined && typeof obj !== "object") console.warn(`Operation expects an object, but got ${obj} (${typeof obj})`);
  return obj != null ? Object(obj) : {};
}

function safeStringOrArray(elem) {
  if (elem !== undefined && !Array.isArray(elem) && typeof elem !== "string") console.warn(`Operation expects an Array or a String, but got ${elem} (${typeof elem})`);
  return elem == null ? "" : Array.isArray(elem) ? Array.from(elem) : String(elem);
}

function fragmentToString(fragment) {
  return (new XMLSerializer).serializeToString(fragment);
}

async function graciouslyFetch(url, additionalHeaders) {
  try {
    const response = await fetch(url, {
      headers: {
        "X-REQUESTED-WITH": "XmlHttpRequest",
        ...additionalHeaders
      }
    });
    if (response == undefined) return;
    handleErrors(response);
    return response;
  } catch (e) {
    console.error(`Could not fetch ${url}`);
  }
}

class BoundedQueue {
  constructor(maxSize) {
    this.maxSize = maxSize;
    this.queue = [];
  }
  push(item) {
    if (this.isFull()) {
      this.shift();
    }
    this.queue.push(item);
  }
  shift() {
    return this.queue.shift();
  }
  isFull() {
    return this.queue.length === this.maxSize;
  }
}

Object.freeze({
  __proto__: null,
  BoundedQueue: BoundedQueue,
  after: after,
  assignFocus: assignFocus,
  before: before,
  debounce: debounce,
  dispatch: dispatch,
  fragmentToString: fragmentToString,
  getClassNames: getClassNames,
  graciouslyFetch: graciouslyFetch,
  handleErrors: handleErrors,
  isTextInput: isTextInput,
  kebabize: kebabize,
  operate: operate,
  processElements: processElements,
  safeArray: safeArray,
  safeObject: safeObject,
  safeScalar: safeScalar,
  safeString: safeString,
  safeStringOrArray: safeStringOrArray,
  xpathToElement: xpathToElement,
  xpathToElementArray: xpathToElementArray
});

const shouldMorph = operation => (fromEl, toEl) => !shouldMorphCallbacks.map((callback => typeof callback === "function" ? callback(operation, fromEl, toEl) : true)).includes(false);

const didMorph = operation => el => {
  didMorphCallbacks.forEach((callback => {
    if (typeof callback === "function") callback(operation, el);
  }));
};

const verifyNotMutable = (detail, fromEl, toEl) => {
  if (!mutableTags[fromEl.tagName] && fromEl.isEqualNode(toEl)) return false;
  return true;
};

const verifyNotContentEditable = (detail, fromEl, toEl) => {
  if (fromEl === ActiveElement.element && fromEl.isContentEditable) return false;
  return true;
};

const verifyNotPermanent = (detail, fromEl, toEl) => {
  const {permanentAttributeName: permanentAttributeName} = detail;
  if (!permanentAttributeName) return true;
  const permanent = fromEl.closest(`[${permanentAttributeName}]`);
  if (!permanent && fromEl === ActiveElement.element && isTextInput(fromEl)) {
    const ignore = {
      value: true
    };
    Array.from(toEl.attributes).forEach((attribute => {
      if (!ignore[attribute.name]) fromEl.setAttribute(attribute.name, attribute.value);
    }));
    return false;
  }
  return !permanent;
};

const shouldMorphCallbacks = [ verifyNotMutable, verifyNotPermanent, verifyNotContentEditable ];

const didMorphCallbacks = [];

Object.freeze({
  __proto__: null,
  didMorph: didMorph,
  didMorphCallbacks: didMorphCallbacks,
  shouldMorph: shouldMorph,
  shouldMorphCallbacks: shouldMorphCallbacks,
  verifyNotContentEditable: verifyNotContentEditable,
  verifyNotMutable: verifyNotMutable,
  verifyNotPermanent: verifyNotPermanent
});

var Operations = {
  append: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {html: html, focusSelector: focusSelector} = operation;
        element.insertAdjacentHTML("beforeend", safeScalar(html));
        assignFocus(focusSelector);
      }));
      after(element, operation);
    }));
  },
  graft: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {parent: parent, focusSelector: focusSelector} = operation;
        const parentElement = document.querySelector(parent);
        if (parentElement) {
          parentElement.appendChild(element);
          assignFocus(focusSelector);
        }
      }));
      after(element, operation);
    }));
  },
  innerHtml: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {html: html, focusSelector: focusSelector} = operation;
        element.innerHTML = safeScalar(html);
        assignFocus(focusSelector);
      }));
      after(element, operation);
    }));
  },
  insertAdjacentHtml: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {html: html, position: position, focusSelector: focusSelector} = operation;
        element.insertAdjacentHTML(position || "beforeend", safeScalar(html));
        assignFocus(focusSelector);
      }));
      after(element, operation);
    }));
  },
  insertAdjacentText: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {text: text, position: position, focusSelector: focusSelector} = operation;
        element.insertAdjacentText(position || "beforeend", safeScalar(text));
        assignFocus(focusSelector);
      }));
      after(element, operation);
    }));
  },
  outerHtml: operation => {
    processElements(operation, (element => {
      const parent = element.parentElement;
      const idx = parent && Array.from(parent.children).indexOf(element);
      before(element, operation);
      operate(operation, (() => {
        const {html: html, focusSelector: focusSelector} = operation;
        element.outerHTML = safeScalar(html);
        assignFocus(focusSelector);
      }));
      after(parent ? parent.children[idx] : document.documentElement, operation);
    }));
  },
  prepend: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {html: html, focusSelector: focusSelector} = operation;
        element.insertAdjacentHTML("afterbegin", safeScalar(html));
        assignFocus(focusSelector);
      }));
      after(element, operation);
    }));
  },
  remove: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {focusSelector: focusSelector} = operation;
        element.remove();
        assignFocus(focusSelector);
      }));
      after(document, operation);
    }));
  },
  replace: operation => {
    processElements(operation, (element => {
      const parent = element.parentElement;
      const idx = parent && Array.from(parent.children).indexOf(element);
      before(element, operation);
      operate(operation, (() => {
        const {html: html, focusSelector: focusSelector} = operation;
        element.outerHTML = safeScalar(html);
        assignFocus(focusSelector);
      }));
      after(parent ? parent.children[idx] : document.documentElement, operation);
    }));
  },
  textContent: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {text: text, focusSelector: focusSelector} = operation;
        element.textContent = safeScalar(text);
        assignFocus(focusSelector);
      }));
      after(element, operation);
    }));
  },
  addCssClass: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {name: name} = operation;
        element.classList.add(...getClassNames([ safeStringOrArray(name) ]));
      }));
      after(element, operation);
    }));
  },
  removeAttribute: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {name: name} = operation;
        element.removeAttribute(safeString(name));
      }));
      after(element, operation);
    }));
  },
  removeCssClass: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {name: name} = operation;
        element.classList.remove(...getClassNames([ safeStringOrArray(name) ]));
        if (element.classList.length === 0) element.removeAttribute("class");
      }));
      after(element, operation);
    }));
  },
  setAttribute: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {name: name, value: value} = operation;
        element.setAttribute(safeString(name), safeScalar(value));
      }));
      after(element, operation);
    }));
  },
  setDatasetProperty: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {name: name, value: value} = operation;
        element.dataset[safeString(name)] = safeScalar(value);
      }));
      after(element, operation);
    }));
  },
  setProperty: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {name: name, value: value} = operation;
        if (name in element) element[safeString(name)] = safeScalar(value);
      }));
      after(element, operation);
    }));
  },
  setStyle: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {name: name, value: value} = operation;
        element.style[safeString(name)] = safeScalar(value);
      }));
      after(element, operation);
    }));
  },
  setStyles: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {styles: styles} = operation;
        for (let [name, value] of Object.entries(styles)) element.style[safeString(name)] = safeScalar(value);
      }));
      after(element, operation);
    }));
  },
  setValue: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {value: value} = operation;
        element.value = safeScalar(value);
      }));
      after(element, operation);
    }));
  },
  dispatchEvent: operation => {
    processElements(operation, (element => {
      before(element, operation);
      operate(operation, (() => {
        const {name: name, detail: detail} = operation;
        dispatch(element, safeString(name), safeObject(detail));
      }));
      after(element, operation);
    }));
  },
  setMeta: operation => {
    before(document, operation);
    operate(operation, (() => {
      const {name: name, content: content} = operation;
      let meta = document.head.querySelector(`meta[name='${name}']`);
      if (!meta) {
        meta = document.createElement("meta");
        meta.name = safeString(name);
        document.head.appendChild(meta);
      }
      meta.content = safeScalar(content);
    }));
    after(document, operation);
  },
  setTitle: operation => {
    before(document, operation);
    operate(operation, (() => {
      const {title: title} = operation;
      document.title = safeScalar(title);
    }));
    after(document, operation);
  },
  clearStorage: operation => {
    before(document, operation);
    operate(operation, (() => {
      const {type: type} = operation;
      const storage = type === "session" ? sessionStorage : localStorage;
      storage.clear();
    }));
    after(document, operation);
  },
  go: operation => {
    before(window, operation);
    operate(operation, (() => {
      const {delta: delta} = operation;
      history.go(delta);
    }));
    after(window, operation);
  },
  pushState: operation => {
    before(window, operation);
    operate(operation, (() => {
      const {state: state, title: title, url: url} = operation;
      history.pushState(safeObject(state), safeString(title), safeString(url));
    }));
    after(window, operation);
  },
  redirectTo: operation => {
    before(window, operation);
    operate(operation, (() => {
      let {url: url, action: action, turbo: turbo} = operation;
      action = action || "advance";
      url = safeString(url);
      if (turbo === undefined) turbo = true;
      if (turbo) {
        if (window.Turbo) window.Turbo.visit(url, {
          action: action
        });
        if (window.Turbolinks) window.Turbolinks.visit(url, {
          action: action
        });
        if (!window.Turbo && !window.Turbolinks) window.location.href = url;
      } else {
        window.location.href = url;
      }
    }));
    after(window, operation);
  },
  reload: operation => {
    before(window, operation);
    operate(operation, (() => {
      window.location.reload();
    }));
    after(window, operation);
  },
  removeStorageItem: operation => {
    before(document, operation);
    operate(operation, (() => {
      const {key: key, type: type} = operation;
      const storage = type === "session" ? sessionStorage : localStorage;
      storage.removeItem(safeString(key));
    }));
    after(document, operation);
  },
  replaceState: operation => {
    before(window, operation);
    operate(operation, (() => {
      const {state: state, title: title, url: url} = operation;
      history.replaceState(safeObject(state), safeString(title), safeString(url));
    }));
    after(window, operation);
  },
  scrollIntoView: operation => {
    const {element: element} = operation;
    before(element, operation);
    operate(operation, (() => {
      element.scrollIntoView(operation);
    }));
    after(element, operation);
  },
  setCookie: operation => {
    before(document, operation);
    operate(operation, (() => {
      const {cookie: cookie} = operation;
      document.cookie = safeScalar(cookie);
    }));
    after(document, operation);
  },
  setFocus: operation => {
    const {element: element} = operation;
    before(element, operation);
    operate(operation, (() => {
      assignFocus(element);
    }));
    after(element, operation);
  },
  setStorageItem: operation => {
    before(document, operation);
    operate(operation, (() => {
      const {key: key, value: value, type: type} = operation;
      const storage = type === "session" ? sessionStorage : localStorage;
      storage.setItem(safeString(key), safeScalar(value));
    }));
    after(document, operation);
  },
  consoleLog: operation => {
    before(document, operation);
    operate(operation, (() => {
      const {message: message, level: level} = operation;
      level && [ "warn", "info", "error" ].includes(level) ? console[level](message) : console.log(message);
    }));
    after(document, operation);
  },
  consoleTable: operation => {
    before(document, operation);
    operate(operation, (() => {
      const {data: data, columns: columns} = operation;
      console.table(data, safeArray(columns));
    }));
    after(document, operation);
  },
  notification: operation => {
    before(document, operation);
    operate(operation, (() => {
      const {title: title, options: options} = operation;
      Notification.requestPermission().then((result => {
        operation.permission = result;
        if (result === "granted") new Notification(safeString(title), safeObject(options));
      }));
    }));
    after(document, operation);
  },
  morph: operation => {
    processElements(operation, (element => {
      const {html: html} = operation;
      const template = document.createElement("template");
      template.innerHTML = String(safeScalar(html)).trim();
      operation.content = template.content;
      const parent = element.parentElement;
      const idx = parent && Array.from(parent.children).indexOf(element);
      before(element, operation);
      operate(operation, (() => {
        const {childrenOnly: childrenOnly, focusSelector: focusSelector} = operation;
        morphdom(element, childrenOnly ? template.content : template.innerHTML, {
          childrenOnly: !!childrenOnly,
          onBeforeElUpdated: shouldMorph(operation),
          onElUpdated: didMorph(operation)
        });
        assignFocus(focusSelector);
      }));
      after(parent ? parent.children[idx] : document.documentElement, operation);
    }));
  }
};

let operations = Operations;

const add = newOperations => {
  operations = {
    ...operations,
    ...newOperations
  };
};

const addOperations = operations => {
  add(operations);
};

const addOperation = (name, operation) => {
  const operations = {};
  operations[name] = operation;
  add(operations);
};

var OperationStore = {
  get all() {
    return operations;
  }
};

let missingElement = "warn";

var MissingElement$1 = {
  get behavior() {
    return missingElement;
  },
  set(value) {
    if ([ "warn", "ignore", "event", "exception" ].includes(value)) missingElement = value; else console.warn("Invalid 'onMissingElement' option. Defaulting to 'warn'.");
  }
};

const perform = (operations, options = {
  onMissingElement: MissingElement$1.behavior
}) => {
  const batches = {};
  operations.forEach((operation => {
    if (!!operation.batch) batches[operation.batch] = batches[operation.batch] ? ++batches[operation.batch] : 1;
  }));
  operations.forEach((operation => {
    const name = operation.operation;
    try {
      if (operation.selector) {
        if (operation.xpath) {
          operation.element = operation.selectAll ? xpathToElementArray(operation.selector) : xpathToElement(operation.selector);
        } else {
          operation.element = operation.selectAll ? document.querySelectorAll(operation.selector) : document.querySelector(operation.selector);
        }
      } else {
        operation.element = document;
      }
      if (operation.element || options.onMissingElement !== "ignore") {
        ActiveElement.set(document.activeElement);
        const cableReadyOperation = OperationStore.all[name];
        if (cableReadyOperation) {
          cableReadyOperation(operation);
          if (!!operation.batch && --batches[operation.batch] === 0) dispatch(document, "cable-ready:batch-complete", {
            batch: operation.batch
          });
        } else {
          console.error(`CableReady couldn't find the "${name}" operation. Make sure you use the camelized form when calling an operation method.`);
        }
      }
    } catch (e) {
      if (operation.element) {
        console.error(`CableReady detected an error in ${name || "operation"}: ${e.message}. If you need to support older browsers make sure you've included the corresponding polyfills. https://docs.stimulusreflex.com/setup#polyfills-for-ie11.`);
        console.error(e);
      } else {
        const warning = `CableReady ${name || ""} operation failed due to missing DOM element for selector: '${operation.selector}'`;
        switch (options.onMissingElement) {
         case "ignore":
          break;

         case "event":
          dispatch(document, "cable-ready:missing-element", {
            warning: warning,
            operation: operation
          });
          break;

         case "exception":
          throw warning;

         default:
          console.warn(warning);
        }
      }
    }
  }));
};

const performAsync = (operations, options = {
  onMissingElement: MissingElement$1.behavior
}) => new Promise(((resolve, reject) => {
  try {
    resolve(perform(operations, options));
  } catch (err) {
    reject(err);
  }
}));

class SubscribingElement extends HTMLElement {
  static get tagName() {
    throw new Error("Implement the tagName() getter in the inheriting class");
  }
  static define() {
    if (!customElements.get(this.tagName)) {
      customElements.define(this.tagName, this);
    }
  }
  disconnectedCallback() {
    if (this.channel) this.channel.unsubscribe();
  }
  createSubscription(consumer, channel, receivedCallback) {
    this.channel = consumer.subscriptions.create({
      channel: channel,
      identifier: this.identifier
    }, {
      received: receivedCallback
    });
  }
  get preview() {
    return document.documentElement.hasAttribute("data-turbolinks-preview") || document.documentElement.hasAttribute("data-turbo-preview");
  }
  get identifier() {
    return this.getAttribute("identifier");
  }
}

let consumer;

const BACKOFF = [ 25, 50, 75, 100, 200, 250, 500, 800, 1e3, 2e3 ];

const wait = ms => new Promise((resolve => setTimeout(resolve, ms)));

const getConsumerWithRetry = async (retry = 0) => {
  if (consumer) return consumer;
  if (retry >= BACKOFF.length) {
    throw new Error("Couldn't obtain a Action Cable consumer within 5s");
  }
  await wait(BACKOFF[retry]);
  return await getConsumerWithRetry(retry + 1);
};

var CableConsumer = {
  setConsumer(value) {
    consumer = value;
  },
  get consumer() {
    return consumer;
  },
  async getConsumer() {
    return await getConsumerWithRetry();
  }
};

class StreamFromElement extends SubscribingElement {
  static get tagName() {
    return "cable-ready-stream-from";
  }
  async connectedCallback() {
    if (this.preview) return;
    const consumer = await CableConsumer.getConsumer();
    if (consumer) {
      this.createSubscription(consumer, "CableReady::Stream", this.performOperations.bind(this));
    } else {
      console.error("The `cable_ready_stream_from` helper cannot connect. You must initialize CableReady with an Action Cable consumer.");
    }
  }
  performOperations(data) {
    if (data.cableReady) perform(data.operations, {
      onMissingElement: this.onMissingElement
    });
  }
  get onMissingElement() {
    const value = this.getAttribute("missing") || MissingElement$1.behavior;
    if ([ "warn", "ignore", "event" ].includes(value)) return value; else {
      console.warn("Invalid 'missing' attribute. Defaulting to 'warn'.");
      return "warn";
    }
  }
}

let debugging = false;

var Debug = {
  get enabled() {
    return debugging;
  },
  get disabled() {
    return !debugging;
  },
  get value() {
    return debugging;
  },
  set(value) {
    debugging = !!value;
  },
  set debug(value) {
    debugging = !!value;
  }
};

const request = (data, blocks) => {
  if (Debug.disabled) return;
  const message = `↑ Updatable request affecting ${blocks.length} element(s): `;
  console.log(message, {
    elements: blocks.map((b => b.element)),
    identifiers: blocks.map((b => b.element.getAttribute("identifier"))),
    data: data
  });
  return message;
};

const cancel = (timestamp, reason) => {
  if (Debug.disabled) return;
  const duration = new Date - timestamp;
  const message = `❌ Updatable request canceled after ${duration}ms: ${reason}`;
  console.log(message);
  return message;
};

const response = (timestamp, element, urls) => {
  if (Debug.disabled) return;
  const duration = new Date - timestamp;
  const message = `↓ Updatable response: All URLs fetched in ${duration}ms`;
  console.log(message, {
    element: element,
    urls: urls
  });
  return message;
};

const morphStart = (timestamp, element) => {
  if (Debug.disabled) return;
  const duration = new Date - timestamp;
  const message = `↻ Updatable morph: starting after ${duration}ms`;
  console.log(message, {
    element: element
  });
  return message;
};

const morphEnd = (timestamp, element) => {
  if (Debug.disabled) return;
  const duration = new Date - timestamp;
  const message = `↺ Updatable morph: completed after ${duration}ms`;
  console.log(message, {
    element: element
  });
  return message;
};

var Log = {
  request: request,
  cancel: cancel,
  response: response,
  morphStart: morphStart,
  morphEnd: morphEnd
};

const template = `\n<style>\n  :host {\n    display: block;\n  }\n</style>\n<slot></slot>\n`;

class UpdatesForElement extends SubscribingElement {
  static get tagName() {
    return "cable-ready-updates-for";
  }
  constructor() {
    super();
    const shadowRoot = this.attachShadow({
      mode: "open"
    });
    shadowRoot.innerHTML = template;
    this.triggerElementLog = new BoundedQueue(10);
    this.targetElementLog = new BoundedQueue(10);
  }
  async connectedCallback() {
    if (this.preview) return;
    this.update = debounce(this.update.bind(this), this.debounce);
    const consumer = await CableConsumer.getConsumer();
    if (consumer) {
      this.createSubscription(consumer, "CableReady::Stream", this.update);
    } else {
      console.error("The `cable_ready_updates_for` helper cannot connect. You must initialize CableReady with an Action Cable consumer.");
    }
  }
  async update(data) {
    this.lastUpdateTimestamp = new Date;
    const blocks = Array.from(document.querySelectorAll(this.query), (element => new Block(element))).filter((block => block.shouldUpdate(data)));
    this.triggerElementLog.push(`${(new Date).toLocaleString()}: ${Log.request(data, blocks)}`);
    if (blocks.length === 0) {
      this.triggerElementLog.push(`${(new Date).toLocaleString()}: ${Log.cancel(this.lastUpdateTimestamp, "All elements filtered out")}`);
      return;
    }
    if (blocks[0].element !== this) {
      this.triggerElementLog.push(`${(new Date).toLocaleString()}: ${Log.cancel(this.lastUpdateTimestamp, "Update already requested")}`);
      return;
    }
    ActiveElement.set(document.activeElement);
    this.html = {};
    const uniqueUrls = [ ...new Set(blocks.map((block => block.url))) ];
    await Promise.all(uniqueUrls.map((async url => {
      if (!this.html.hasOwnProperty(url)) {
        const response = await graciouslyFetch(url, {
          "X-Cable-Ready": "update"
        });
        this.html[url] = await response.text();
      }
    })));
    this.triggerElementLog.push(`${(new Date).toLocaleString()}: ${Log.response(this.lastUpdateTimestamp, this, uniqueUrls)}`);
    this.index = {};
    blocks.forEach((block => {
      this.index.hasOwnProperty(block.url) ? this.index[block.url]++ : this.index[block.url] = 0;
      block.process(data, this.html, this.index, this.lastUpdateTimestamp);
    }));
  }
  get query() {
    return `${this.tagName}[identifier="${this.identifier}"]`;
  }
  get identifier() {
    return this.getAttribute("identifier");
  }
  get debounce() {
    return this.hasAttribute("debounce") ? parseInt(this.getAttribute("debounce")) : 20;
  }
}

class Block {
  constructor(element) {
    this.element = element;
  }
  async process(data, html, fragmentsIndex, startTimestamp) {
    const blockIndex = fragmentsIndex[this.url];
    const template = document.createElement("template");
    this.element.setAttribute("updating", "updating");
    template.innerHTML = String(html[this.url]).trim();
    await this.resolveTurboFrames(template.content);
    const fragments = template.content.querySelectorAll(this.query);
    if (fragments.length <= blockIndex) {
      console.warn(`Update aborted due to insufficient number of elements. The offending url is ${this.url}, the offending element is:`, this.element);
      return;
    }
    const operation = {
      element: this.element,
      html: fragments[blockIndex],
      permanentAttributeName: "data-ignore-updates"
    };
    dispatch(this.element, "cable-ready:before-update", operation);
    this.element.targetElementLog.push(`${(new Date).toLocaleString()}: ${Log.morphStart(startTimestamp, this.element)}`);
    morphdom(this.element, fragments[blockIndex], {
      childrenOnly: true,
      onBeforeElUpdated: shouldMorph(operation),
      onElUpdated: _ => {
        this.element.removeAttribute("updating");
        dispatch(this.element, "cable-ready:after-update", operation);
        assignFocus(operation.focusSelector);
      }
    });
    this.element.targetElementLog.push(`${(new Date).toLocaleString()}: ${Log.morphEnd(startTimestamp, this.element)}`);
  }
  async resolveTurboFrames(documentFragment) {
    const reloadingTurboFrames = [ ...documentFragment.querySelectorAll('turbo-frame[src]:not([loading="lazy"])') ];
    return Promise.all(reloadingTurboFrames.map((frame => new Promise((async resolve => {
      const frameResponse = await graciouslyFetch(frame.getAttribute("src"), {
        "Turbo-Frame": frame.id,
        "X-Cable-Ready": "update"
      });
      const frameTemplate = document.createElement("template");
      frameTemplate.innerHTML = await frameResponse.text();
      await this.resolveTurboFrames(frameTemplate.content);
      const selector = `turbo-frame#${frame.id}`;
      const frameContent = frameTemplate.content.querySelector(selector);
      const content = frameContent ? frameContent.innerHTML.trim() : "";
      documentFragment.querySelector(selector).innerHTML = content;
      resolve();
    })))));
  }
  shouldUpdate(data) {
    return !this.ignoresInnerUpdates && this.hasChangesSelectedForUpdate(data);
  }
  hasChangesSelectedForUpdate(data) {
    const only = this.element.getAttribute("only");
    return !(only && data.changed && !only.split(" ").some((attribute => data.changed.includes(attribute))));
  }
  get ignoresInnerUpdates() {
    return this.element.hasAttribute("ignore-inner-updates") && this.element.hasAttribute("performing-inner-update");
  }
  get url() {
    return this.element.hasAttribute("url") ? this.element.getAttribute("url") : location.href;
  }
  get identifier() {
    return this.element.identifier;
  }
  get query() {
    return this.element.query;
  }
}

const registerInnerUpdates = () => {
  document.addEventListener("stimulus-reflex:before", (event => {
    recursiveMarkUpdatesForElements(event.detail.element);
  }));
  document.addEventListener("stimulus-reflex:after", (event => {
    setTimeout((() => {
      recursiveUnmarkUpdatesForElements(event.detail.element);
    }));
  }));
  document.addEventListener("turbo:submit-start", (event => {
    recursiveMarkUpdatesForElements(event.target);
  }));
  document.addEventListener("turbo:submit-end", (event => {
    setTimeout((() => {
      recursiveUnmarkUpdatesForElements(event.target);
    }));
  }));
  document.addEventListener("turbo-boost:command:start", (event => {
    recursiveMarkUpdatesForElements(event.target);
  }));
  document.addEventListener("turbo-boost:command:finish", (event => {
    setTimeout((() => {
      recursiveUnmarkUpdatesForElements(event.target);
    }));
  }));
  document.addEventListener("turbo-boost:command:error", (event => {
    setTimeout((() => {
      recursiveUnmarkUpdatesForElements(event.target);
    }));
  }));
};

const recursiveMarkUpdatesForElements = leaf => {
  const closestUpdatesFor = leaf && leaf.parentElement && leaf.parentElement.closest("cable-ready-updates-for");
  if (closestUpdatesFor) {
    closestUpdatesFor.setAttribute("performing-inner-update", "");
    recursiveMarkUpdatesForElements(closestUpdatesFor);
  }
};

const recursiveUnmarkUpdatesForElements = leaf => {
  const closestUpdatesFor = leaf && leaf.parentElement && leaf.parentElement.closest("cable-ready-updates-for");
  if (closestUpdatesFor) {
    closestUpdatesFor.removeAttribute("performing-inner-update");
    recursiveUnmarkUpdatesForElements(closestUpdatesFor);
  }
};

const defineElements$1 = () => {
  registerInnerUpdates();
  StreamFromElement.define();
  UpdatesForElement.define();
};

const initialize = (initializeOptions = {}) => {
  const {consumer: consumer, onMissingElement: onMissingElement, debug: debug} = initializeOptions;
  Debug.set(!!debug);
  if (consumer) {
    CableConsumer.setConsumer(consumer);
  } else {
    console.error("CableReady requires a reference to your Action Cable `consumer` for its helpers to function.\nEnsure that you have imported the `CableReady` package as well as `consumer` from your `channels` folder, then call `CableReady.initialize({ consumer })`.");
  }
  if (onMissingElement) {
    MissingElement.set(onMissingElement);
  }
  defineElements$1();
};

const global = {
  perform: perform,
  performAsync: performAsync,
  shouldMorphCallbacks: shouldMorphCallbacks,
  didMorphCallbacks: didMorphCallbacks,
  initialize: initialize,
  addOperation: addOperation,
  addOperations: addOperations,
  version: packageInfo.version,
  cable: CableConsumer,
  get DOMOperations() {
    console.warn("DEPRECATED: Please use `CableReady.operations` instead of `CableReady.DOMOperations`");
    return OperationStore.all;
  },
  get operations() {
    return OperationStore.all;
  },
  get consumer() {
    return CableConsumer.consumer;
  }
};

window.CableReady = global;

class CableReadyElement extends HTMLElement {
  static define() {
    if (!customElements.get("cable-ready")) {
      customElements.define("cable-ready", this);
    }
  }
  connectedCallback() {
    setTimeout((() => {
      try {
        const operations = JSON.parse(this.scriptElement.textContent);
        global.perform(operations);
      } catch (error) {
        console.error(error);
      } finally {
        try {
          this.remove();
        } catch {}
      }
    }));
  }
  get scriptElement() {
    if (this.firstElementChild instanceof HTMLScriptElement && this.firstElementChild.getAttribute("type") === "application/json") {
      return this.firstElementChild;
    }
    throw new Error('First child element in a `<cable-ready>` tag must be `<script type="application/json">`.');
  }
}

const defineElements = () => {
  CableReadyElement.define();
};

defineElements();

export { CableReadyElement };