diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index e2dc060..fc2ac7b 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -28,17 +28,27 @@ header .muted {
padding: 10px;
}
-.nm-list {
+#nm-list {
}
-.nm-list section {
+#nm-list section {
margin-bottom: 5px;
display: grid;
- grid-template-columns: .05fr 1fr 1fr .5fr;
+ grid-template-columns: .05fr 1fr 1fr .3fr;
align-items: center;
- padding: 0 10px;
- background-color: #eee;
+ padding-left: 10px;
+ background-color: #3D9970;
+ color: white;
+}
+
+#nm-list section.popped {
+ background-color: #b5443a;
+ color: #63f0fd;
+}
+
+#nm-list section div.button {
+ height: 100%;
}
img {
@@ -59,7 +69,7 @@ h3.nm-info {
.badge {
font-size: 12px;
font-weight: bold;
- border: 1px solid black;
+ border: 1px solid white;
vertical-align: middle;
padding: 1px 6px;
border-radius: 10px;
@@ -70,13 +80,31 @@ small.badge {
font-size: 10px;
}
+.popped .badge {
+ border-color: #63f0fd;
+}
+
button.action {
width: 100%;
height: 100%;
+ border: 0;
+ display: block;
+ background-color: #005ba4;
+ color: #daffbe;
+ font-size: 18px;
+ text-transform: uppercase;
+}
+
+button.reset {
+ background-color: tomato;
+}
+
+button.action:hover {
+ cursor: pointer;
}
section .meta {
padding-left: 10px;
- padding-top: 4px;
- padding-bottom: 4px;
+ padding-top: 10px;
+ padding-bottom: 10px;
}
diff --git a/app/controllers/instance_controller.rb b/app/controllers/instance_controller.rb
index ae7cb55..9a39c6c 100644
--- a/app/controllers/instance_controller.rb
+++ b/app/controllers/instance_controller.rb
@@ -11,7 +11,17 @@ class InstanceController < ApplicationController
end
def show
- @instance = Instance.find_by(public_id: show_instance_params)
+ @instance = Instance.includes(:pops).find_by(public_id: show_instance_params)
+ end
+
+ def pop
+ instance_id, nm = pop_instance_params
+ parent_instance = Instance.find_by(public_id: instance_id)
+ pop = Pop.new(instance_id: parent_instance.id, name: nm)
+ if pop.save
+ @instance = Instance.includes(:pops).find_by(public_id: instance_id)
+ render partial: "list", locals: { instance: @instance }
+ end
end
private
@@ -23,4 +33,8 @@ class InstanceController < ApplicationController
def show_instance_params
params.expect(:public_id)
end
+
+ def pop_instance_params
+ params.expect(:instance, :nm)
+ end
end
diff --git a/app/javascript/application.js b/app/javascript/application.js
index 0d7b494..de6bf57 100644
--- a/app/javascript/application.js
+++ b/app/javascript/application.js
@@ -1,3 +1,8 @@
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
-import "@hotwired/turbo-rails"
-import "controllers"
+import "htmx.org"
+import htmx from "htmx.org"
+htmx.logger = function(elt, event, data) {
+ if(console) {
+ console.log(event, elt, data);
+ }
+}
diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js
deleted file mode 100644
index 1213e85..0000000
--- a/app/javascript/controllers/application.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Application } from "@hotwired/stimulus"
-
-const application = Application.start()
-
-// Configure Stimulus development experience
-application.debug = false
-window.Stimulus = application
-
-export { application }
diff --git a/app/javascript/controllers/hello_controller.js b/app/javascript/controllers/hello_controller.js
deleted file mode 100644
index 5975c07..0000000
--- a/app/javascript/controllers/hello_controller.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import { Controller } from "@hotwired/stimulus"
-
-export default class extends Controller {
- connect() {
- this.element.textContent = "Hello World!"
- }
-}
diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js
deleted file mode 100644
index 1156bf8..0000000
--- a/app/javascript/controllers/index.js
+++ /dev/null
@@ -1,4 +0,0 @@
-// Import and register all your controllers from the importmap via controllers/**/*_controller
-import { application } from "controllers/application"
-import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
-eagerLoadControllersFrom("controllers", application)
diff --git a/app/models/instance.rb b/app/models/instance.rb
index f77247f..c8eaed7 100644
--- a/app/models/instance.rb
+++ b/app/models/instance.rb
@@ -1,3 +1,5 @@
class Instance < ApplicationRecord
+ has_many :pops
+
validates :zone, inclusion: { in: %w[anemos pagos pyros hydatos] }
end
diff --git a/app/models/pop.rb b/app/models/pop.rb
new file mode 100644
index 0000000..716c670
--- /dev/null
+++ b/app/models/pop.rb
@@ -0,0 +1,3 @@
+class Pop < ApplicationRecord
+ belongs_to :instance
+end
diff --git a/app/views/instance/_list.html.erb b/app/views/instance/_list.html.erb
new file mode 100644
index 0000000..aedb3c9
--- /dev/null
+++ b/app/views/instance/_list.html.erb
@@ -0,0 +1,48 @@
+
+ <% APP_DATA[instance.zone.to_sym][:nms].each do |nm| %>
+ <% is_popped = instance.pops.any? { |pop| pop.name == nm[:name].parameterize } %>
+
+
+

" alt="<%= nm[:element] %>" width="30" />
+
+
+
+
+ <% if is_popped %>
+
+ <% else %>
+
+ <% end %>
+
+
+
+ <% end %>
+
diff --git a/app/views/instance/show.html.erb b/app/views/instance/show.html.erb
index 8250cf9..7b9d2bb 100644
--- a/app/views/instance/show.html.erb
+++ b/app/views/instance/show.html.erb
@@ -4,37 +4,4 @@
<%= render partial: "zone_img", locals: { zone: @instance.zone, alt: @instance.zone, title: @instance.zone.upcase_first } %>
-
- <% APP_DATA[@instance.zone.to_sym][:nms].each do |nm| %>
-
-
-

" alt="<%= nm[:element] %>" width="30" />
-
-
-
-
-
-
-
- <% end %>
-
+<%= render partial: "list", locals: { instance: @instance } %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index bd8792a..aabbd02 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -21,7 +21,7 @@
<%= javascript_importmap_tags %>
-
+
<%= yield %>
diff --git a/config/importmap.rb b/config/importmap.rb
index 909dfc5..d331e97 100644
--- a/config/importmap.rb
+++ b/config/importmap.rb
@@ -1,7 +1,4 @@
# Pin npm packages by running ./bin/importmap
pin "application"
-pin "@hotwired/turbo-rails", to: "turbo.min.js"
-pin "@hotwired/stimulus", to: "stimulus.min.js"
-pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
-pin_all_from "app/javascript/controllers", under: "controllers"
+pin "htmx.org" # @2.0.1
diff --git a/config/routes.rb b/config/routes.rb
index 8def87a..e9487da 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,6 +3,7 @@ Rails.application.routes.draw do
post "/new", to: "instance#create", as: :new_instance
get "/:public_id", to: "instance#show", as: :show_instance
+ post "/pop", to: "instance#pop", as: :pop_in_instance
get "up" => "rails/health#show", as: :rails_health_check
diff --git a/test/fixtures/pops.yml b/test/fixtures/pops.yml
new file mode 100644
index 0000000..d7a3329
--- /dev/null
+++ b/test/fixtures/pops.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+# This model initially had no columns defined. If you add columns to the
+# model remove the "{}" from the fixture names and add the columns immediately
+# below each fixture, per the syntax in the comments below
+#
+one: {}
+# column: value
+#
+two: {}
+# column: value
diff --git a/test/models/pop_test.rb b/test/models/pop_test.rb
new file mode 100644
index 0000000..608906e
--- /dev/null
+++ b/test/models/pop_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class PopTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/vendor/javascript/htmx.org.js b/vendor/javascript/htmx.org.js
new file mode 100644
index 0000000..3975876
--- /dev/null
+++ b/vendor/javascript/htmx.org.js
@@ -0,0 +1,1264 @@
+// htmx.org@2.0.1 downloaded from https://ga.jspm.io/npm:htmx.org@2.0.1/dist/htmx.esm.js
+
+var htmx=function(){const htmx={
+/** @type {typeof onLoadHelper} */
+onLoad:null,
+/** @type {typeof processNode} */
+process:null,
+/** @type {typeof addEventListenerImpl} */
+on:null,
+/** @type {typeof removeEventListenerImpl} */
+off:null,
+/** @type {typeof triggerEvent} */
+trigger:null,
+/** @type {typeof ajaxHelper} */
+ajax:null,
+/** @type {typeof find} */
+find:null,
+/** @type {typeof findAll} */
+findAll:null,
+/** @type {typeof closest} */
+closest:null,
+/**
+ * Returns the input values that would resolve for a given element via the htmx value resolution mechanism
+ *
+ * @see https://htmx.org/api/#values
+ *
+ * @param {Element} elt the element to resolve values on
+ * @param {HttpVerb} type the request type (e.g. **get** or **post**) non-GET's will include the enclosing form of the element. Defaults to **post**
+ * @returns {Object}
+ */
+values:function(e,t){const n=getInputValues(e,t||"post");return n.values},
+/** @type {typeof removeElement} */
+remove:null,
+/** @type {typeof addClassToElement} */
+addClass:null,
+/** @type {typeof removeClassFromElement} */
+removeClass:null,
+/** @type {typeof toggleClassOnElement} */
+toggleClass:null,
+/** @type {typeof takeClassForElement} */
+takeClass:null,
+/** @type {typeof swap} */
+swap:null,
+/** @type {typeof defineExtension} */
+defineExtension:null,
+/** @type {typeof removeExtension} */
+removeExtension:null,
+/** @type {typeof logAll} */
+logAll:null,
+/** @type {typeof logNone} */
+logNone:null,logger:null,config:{
+/**
+ * Whether to use history.
+ * @type boolean
+ * @default true
+ */
+historyEnabled:true,
+/**
+ * The number of pages to keep in **localStorage** for history support.
+ * @type number
+ * @default 10
+ */
+historyCacheSize:10,
+/**
+ * @type boolean
+ * @default false
+ */
+refreshOnHistoryMiss:false,
+/**
+ * The default swap style to use if **[hx-swap](https://htmx.org/attributes/hx-swap)** is omitted.
+ * @type HtmxSwapStyle
+ * @default 'innerHTML'
+ */
+defaultSwapStyle:"innerHTML",
+/**
+ * The default delay between receiving a response from the server and doing the swap.
+ * @type number
+ * @default 0
+ */
+defaultSwapDelay:0,
+/**
+ * The default delay between completing the content swap and settling attributes.
+ * @type number
+ * @default 20
+ */
+defaultSettleDelay:20,
+/**
+ * If true, htmx will inject a small amount of CSS into the page to make indicators invisible unless the **htmx-indicator** class is present.
+ * @type boolean
+ * @default true
+ */
+includeIndicatorStyles:true,
+/**
+ * The class to place on indicators when a request is in flight.
+ * @type string
+ * @default 'htmx-indicator'
+ */
+indicatorClass:"htmx-indicator",
+/**
+ * The class to place on triggering elements when a request is in flight.
+ * @type string
+ * @default 'htmx-request'
+ */
+requestClass:"htmx-request",
+/**
+ * The class to temporarily place on elements that htmx has added to the DOM.
+ * @type string
+ * @default 'htmx-added'
+ */
+addedClass:"htmx-added",
+/**
+ * The class to place on target elements when htmx is in the settling phase.
+ * @type string
+ * @default 'htmx-settling'
+ */
+settlingClass:"htmx-settling",
+/**
+ * The class to place on target elements when htmx is in the swapping phase.
+ * @type string
+ * @default 'htmx-swapping'
+ */
+swappingClass:"htmx-swapping",
+/**
+ * Allows the use of eval-like functionality in htmx, to enable **hx-vars**, trigger conditions & script tag evaluation. Can be set to **false** for CSP compatibility.
+ * @type boolean
+ * @default true
+ */
+allowEval:true,
+/**
+ * If set to false, disables the interpretation of script tags.
+ * @type boolean
+ * @default true
+ */
+allowScriptTags:true,
+/**
+ * If set, the nonce will be added to inline scripts.
+ * @type string
+ * @default ''
+ */
+inlineScriptNonce:"",
+/**
+ * If set, the nonce will be added to inline styles.
+ * @type string
+ * @default ''
+ */
+inlineStyleNonce:"",
+/**
+ * The attributes to settle during the settling phase.
+ * @type string[]
+ * @default ['class', 'style', 'width', 'height']
+ */
+attributesToSettle:["class","style","width","height"],
+/**
+ * Allow cross-site Access-Control requests using credentials such as cookies, authorization headers or TLS client certificates.
+ * @type boolean
+ * @default false
+ */
+withCredentials:false,
+/**
+ * @type number
+ * @default 0
+ */
+timeout:0,
+/**
+ * The default implementation of **getWebSocketReconnectDelay** for reconnecting after unexpected connection loss by the event code **Abnormal Closure**, **Service Restart** or **Try Again Later**.
+ * @type {'full-jitter' | ((retryCount:number) => number)}
+ * @default "full-jitter"
+ */
+wsReconnectDelay:"full-jitter",
+/**
+ * The type of binary data being received over the WebSocket connection
+ * @type BinaryType
+ * @default 'blob'
+ */
+wsBinaryType:"blob",
+/**
+ * @type string
+ * @default '[hx-disable], [data-hx-disable]'
+ */
+disableSelector:"[hx-disable], [data-hx-disable]",
+/**
+ * @type {'auto' | 'instant' | 'smooth'}
+ * @default 'smooth'
+ */
+scrollBehavior:"instant",
+/**
+ * If the focused element should be scrolled into view.
+ * @type boolean
+ * @default false
+ */
+defaultFocusScroll:false,
+/**
+ * If set to true htmx will include a cache-busting parameter in GET requests to avoid caching partial responses by the browser
+ * @type boolean
+ * @default false
+ */
+getCacheBusterParam:false,
+/**
+ * If set to true, htmx will use the View Transition API when swapping in new content.
+ * @type boolean
+ * @default false
+ */
+globalViewTransitions:false,
+/**
+ * htmx will format requests with these methods by encoding their parameters in the URL, not the request body
+ * @type {(HttpVerb)[]}
+ * @default ['get', 'delete']
+ */
+methodsThatUseUrlParams:["get","delete"],
+/**
+ * If set to true, disables htmx-based requests to non-origin hosts.
+ * @type boolean
+ * @default false
+ */
+selfRequestsOnly:true,
+/**
+ * If set to true htmx will not update the title of the document when a title tag is found in new content
+ * @type boolean
+ * @default false
+ */
+ignoreTitle:false,
+/**
+ * Whether the target of a boosted element is scrolled into the viewport.
+ * @type boolean
+ * @default true
+ */
+scrollIntoViewOnBoost:true,
+/**
+ * The cache to store evaluated trigger specifications into.
+ * You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
+ * @type {Object|null}
+ * @default null
+ */
+triggerSpecsCache:null,
+/** @type boolean */
+disableInheritance:false,
+/** @type HtmxResponseHandlingConfig[] */
+responseHandling:[{code:"204",swap:false},{code:"[23]..",swap:true},{code:"[45]..",swap:false,error:true}],
+/**
+ * Whether to process OOB swaps on elements that are nested within the main response element.
+ * @type boolean
+ * @default true
+ */
+allowNestedOobSwaps:true},
+/** @type {typeof parseInterval} */
+parseInterval:null,
+/** @type {typeof internalEval} */
+_:null,version:"2.0.1"};htmx.onLoad=onLoadHelper;htmx.process=processNode;htmx.on=addEventListenerImpl;htmx.off=removeEventListenerImpl;htmx.trigger=triggerEvent;htmx.ajax=ajaxHelper;htmx.find=find;htmx.findAll=findAll;htmx.closest=closest;htmx.remove=removeElement;htmx.addClass=addClassToElement;htmx.removeClass=removeClassFromElement;htmx.toggleClass=toggleClassOnElement;htmx.takeClass=takeClassForElement;htmx.swap=swap;htmx.defineExtension=defineExtension;htmx.removeExtension=removeExtension;htmx.logAll=logAll;htmx.logNone=logNone;htmx.parseInterval=parseInterval;htmx._=internalEval;const internalAPI={addTriggerHandler:addTriggerHandler,bodyContains:bodyContains,canAccessLocalStorage:canAccessLocalStorage,findThisElement:findThisElement,filterValues:filterValues,swap:swap,hasAttribute:hasAttribute,getAttributeValue:getAttributeValue,getClosestAttributeValue:getClosestAttributeValue,getClosestMatch:getClosestMatch,getExpressionVars:getExpressionVars,getHeaders:getHeaders,getInputValues:getInputValues,getInternalData:getInternalData,getSwapSpecification:getSwapSpecification,getTriggerSpecs:getTriggerSpecs,getTarget:getTarget,makeFragment:makeFragment,mergeObjects:mergeObjects,makeSettleInfo:makeSettleInfo,oobSwap:oobSwap,querySelectorExt:querySelectorExt,settleImmediately:settleImmediately,shouldCancel:shouldCancel,triggerEvent:triggerEvent,triggerErrorEvent:triggerErrorEvent,withExtensions:withExtensions};const VERBS=["get","post","put","delete","patch"];const VERB_SELECTOR=VERBS.map((function(e){return"[hx-"+e+"], [data-hx-"+e+"]"})).join(", ");const HEAD_TAG_REGEX=makeTagRegEx("head");
+/**
+ * @param {string} tag
+ * @param {boolean} global
+ * @returns {RegExp}
+ */function makeTagRegEx(e,t=false){return new RegExp(`<${e}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${e}>`,t?"gim":"im")}
+/**
+ * Parses an interval string consistent with the way htmx does. Useful for plugins that have timing-related attributes.
+ *
+ * Caution: Accepts an int followed by either **s** or **ms**. All other values use **parseFloat**
+ *
+ * @see https://htmx.org/api/#parseInterval
+ *
+ * @param {string} str timing string
+ * @returns {number|undefined}
+ */function parseInterval(e){if(e==void 0)return;let t=NaN;t=e.slice(-2)=="ms"?parseFloat(e.slice(0,-2)):e.slice(-1)=="s"?parseFloat(e.slice(0,-1))*1e3:e.slice(-1)=="m"?parseFloat(e.slice(0,-1))*1e3*60:parseFloat(e);return isNaN(t)?void 0:t}
+/**
+ * @param {Node} elt
+ * @param {string} name
+ * @returns {(string | null)}
+ */function getRawAttribute(e,t){return e instanceof Element&&e.getAttribute(t)}
+/**
+ * @param {Element} elt
+ * @param {string} qualifiedName
+ * @returns {boolean}
+ */function hasAttribute(e,t){return!!e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}
+/**
+ *
+ * @param {Node} elt
+ * @param {string} qualifiedName
+ * @returns {(string | null)}
+ */function getAttributeValue(e,t){return getRawAttribute(e,t)||getRawAttribute(e,"data-"+t)}
+/**
+ * @param {Node} elt
+ * @returns {Node | null}
+ */function parentElt(e){const t=e.parentElement;return!t&&e.parentNode instanceof ShadowRoot?e.parentNode:t}
+/**
+ * @returns {Document}
+ */function getDocument(){return document}
+/**
+ * @param {Node} elt
+ * @param {boolean} global
+ * @returns {Node|Document}
+ */function getRootNode(e,t){return e.getRootNode?e.getRootNode({composed:t}):getDocument()}
+/**
+ * @param {Node} elt
+ * @param {(e:Node) => boolean} condition
+ * @returns {Node | null}
+ */function getClosestMatch(e,t){while(e&&!t(e))e=parentElt(e);return e||null}
+/**
+ * @param {Element} initialElement
+ * @param {Element} ancestor
+ * @param {string} attributeName
+ * @returns {string|null}
+ */function getAttributeValueWithDisinheritance(e,t,n){const r=getAttributeValue(t,n);const o=getAttributeValue(t,"hx-disinherit");var s=getAttributeValue(t,"hx-inherit");if(e!==t){if(htmx.config.disableInheritance)return s&&(s==="*"||s.split(" ").indexOf(n)>=0)?r:null;if(o&&(o==="*"||o.split(" ").indexOf(n)>=0))return"unset"}return r}
+/**
+ * @param {Element} elt
+ * @param {string} attributeName
+ * @returns {string | null}
+ */function getClosestAttributeValue(e,t){let n=null;getClosestMatch(e,(function(r){return!!(n=getAttributeValueWithDisinheritance(e,asElement(r),t))}));if(n!=="unset")return n}
+/**
+ * @param {Node} elt
+ * @param {string} selector
+ * @returns {boolean}
+ */function matches(e,t){const n=e instanceof Element&&(e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector);return!!n&&n.call(e,t)}
+/**
+ * @param {string} str
+ * @returns {string}
+ */function getStartTag(e){const t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i;const n=t.exec(e);return n?n[1].toLowerCase():""}
+/**
+ * @param {string} resp
+ * @returns {Document}
+ */function parseHTML(e){const t=new DOMParser;return t.parseFromString(e,"text/html")}
+/**
+ * @param {DocumentFragment} fragment
+ * @param {Node} elt
+ */function takeChildrenFor(e,t){while(t.childNodes.length>0)e.append(t.childNodes[0])}
+/**
+ * @param {HTMLScriptElement} script
+ * @returns {HTMLScriptElement}
+ */function duplicateScript(e){const t=getDocument().createElement("script");forEach(e.attributes,(function(e){t.setAttribute(e.name,e.value)}));t.textContent=e.textContent;t.async=false;htmx.config.inlineScriptNonce&&(t.nonce=htmx.config.inlineScriptNonce);return t}
+/**
+ * @param {HTMLScriptElement} script
+ * @returns {boolean}
+ */function isJavaScriptScriptNode(e){return e.matches("script")&&(e.type==="text/javascript"||e.type==="module"||e.type==="")}
+/**
+ * we have to make new copies of script tags that we are going to insert because
+ * SOME browsers (not saying who, but it involves an element and an animal) don't
+ * execute scripts created in tags when they are inserted into the DOM
+ * and all the others do lmao
+ * @param {DocumentFragment} fragment
+ */function normalizeScriptTags(e){Array.from(e.querySelectorAll("script")).forEach((/** @param {HTMLScriptElement} script */e=>{if(isJavaScriptScriptNode(e)){const t=duplicateScript(e);const n=e.parentNode;try{n.insertBefore(t,e)}catch(e){logError(e)}finally{e.remove()}}}))}
+/**
+ * @typedef {DocumentFragment & {title?: string}} DocumentFragmentWithTitle
+ * @description a document fragment representing the response HTML, including
+ * a `title` property for any title information found
+ */
+/**
+ * @param {string} response HTML
+ * @returns {DocumentFragmentWithTitle}
+ */function makeFragment(e){const t=e.replace(HEAD_TAG_REGEX,"");const n=getStartTag(t);
+/** @type DocumentFragmentWithTitle */let r;if(n==="html"){r=/** @type DocumentFragmentWithTitle */new DocumentFragment;const t=parseHTML(e);takeChildrenFor(r,t.body);r.title=t.title}else if(n==="body"){r=/** @type DocumentFragmentWithTitle */new DocumentFragment;const e=parseHTML(t);takeChildrenFor(r,e.body);r.title=e.title}else{const e=parseHTML(''+t+"");r=/** @type DocumentFragmentWithTitle */e.querySelector("template").content;r.title=e.title;var o=r.querySelector("title");if(o&&o.parentNode===r){o.remove();r.title=o.innerText}}r&&(htmx.config.allowScriptTags?normalizeScriptTags(r):r.querySelectorAll("script").forEach((e=>e.remove())));return r}
+/**
+ * @param {Function} func
+ */function maybeCall(e){e&&e()}
+/**
+ * @param {any} o
+ * @param {string} type
+ * @returns
+ */function isType(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}
+/**
+ * @param {*} o
+ * @returns {o is Function}
+ */function isFunction(e){return typeof e==="function"}
+/**
+ * @param {*} o
+ * @returns {o is Object}
+ */function isRawObject(e){return isType(e,"Object")}
+/**
+ * @typedef {Object} OnHandler
+ * @property {(keyof HTMLElementEventMap)|string} event
+ * @property {EventListener} listener
+ */
+/**
+ * @typedef {Object} ListenerInfo
+ * @property {string} trigger
+ * @property {EventListener} listener
+ * @property {EventTarget} on
+ */
+/**
+ * @typedef {Object} HtmxNodeInternalData
+ * Element data
+ * @property {number} [initHash]
+ * @property {boolean} [boosted]
+ * @property {OnHandler[]} [onHandlers]
+ * @property {number} [timeout]
+ * @property {ListenerInfo[]} [listenerInfos]
+ * @property {boolean} [cancelled]
+ * @property {boolean} [triggeredOnce]
+ * @property {number} [delayed]
+ * @property {number|null} [throttle]
+ * @property {string} [lastValue]
+ * @property {boolean} [loaded]
+ * @property {string} [path]
+ * @property {string} [verb]
+ * @property {boolean} [polling]
+ * @property {HTMLButtonElement|HTMLInputElement|null} [lastButtonClicked]
+ * @property {number} [requestCount]
+ * @property {XMLHttpRequest} [xhr]
+ * @property {(() => void)[]} [queuedRequests]
+ * @property {boolean} [abortable]
+ *
+ * Event data
+ * @property {HtmxTriggerSpecification} [triggerSpec]
+ * @property {EventTarget[]} [handledFor]
+ */
+/**
+ * getInternalData retrieves "private" data stored by htmx within an element
+ * @param {EventTarget|Event} elt
+ * @returns {HtmxNodeInternalData}
+ */function getInternalData(e){const t="htmx-internal-data";let n=e[t];n||(n=e[t]={});return n}
+/**
+ * toArray converts an ArrayLike object into a real array.
+ * @template T
+ * @param {ArrayLike} arr
+ * @returns {T[]}
+ */function toArray(e){const t=[];if(e)for(let n=0;n} arr
+ * @param {(T) => void} func
+ */function forEach(e,t){if(e)for(let n=0;n=0}
+/**
+ * @param {Node} elt
+ * @returns {boolean}
+ */function bodyContains(e){const t=e.getRootNode&&e.getRootNode();return t&&t instanceof window.ShadowRoot?getDocument().body.contains(t.host):getDocument().body.contains(e)}
+/**
+ * @param {string} trigger
+ * @returns {string[]}
+ */function splitOnWhitespace(e){return e.trim().split(/\s+/)}
+/**
+ * mergeObjects takes all the keys from
+ * obj2 and duplicates them into obj1
+ * @template T1
+ * @template T2
+ * @param {T1} obj1
+ * @param {T2} obj2
+ * @returns {T1 & T2}
+ */function mergeObjects(e,t){for(const n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}
+/**
+ * @param {string} jString
+ * @returns {any|null}
+ */function parseJSON(e){try{return JSON.parse(e)}catch(e){logError(e);return null}}
+/**
+ * @returns {boolean}
+ */function canAccessLocalStorage(){const e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}
+/**
+ * @param {string} path
+ * @returns {string}
+ */function normalizePath(e){try{const t=new URL(e);t&&(e=t.pathname+t.search);/^\/$/.test(e)||(e=e.replace(/\/+$/,""));return e}catch(t){return e}}
+/**
+ * @param {string} str
+ * @returns {any}
+ */function internalEval(str){return maybeEval(getDocument().body,(function(){return eval(str)}))}
+/**
+ * Adds a callback for the **htmx:load** event. This can be used to process new content, for example initializing the content with a javascript library
+ *
+ * @see https://htmx.org/api/#onLoad
+ *
+ * @param {(elt: Node) => void} callback the callback to call on newly loaded content
+ * @returns {EventListener}
+ */function onLoadHelper(e){const t=htmx.on("htmx:load",(/** @param {CustomEvent} evt */function(t){e(t.detail.elt)}));return t}function logAll(){htmx.logger=function(e,t,n){console&&console.log(t,e,n)}}function logNone(){htmx.logger=null}
+/**
+ * Finds an element matching the selector
+ *
+ * @see https://htmx.org/api/#find
+ *
+ * @param {ParentNode|string} eltOrSelector the root element to find the matching element in, inclusive | the selector to match
+ * @param {string} [selector] the selector to match
+ * @returns {Element|null}
+ */function find(e,t){return typeof e!=="string"?e.querySelector(t):find(getDocument(),e)}
+/**
+ * Finds all elements matching the selector
+ *
+ * @see https://htmx.org/api/#findAll
+ *
+ * @param {ParentNode|string} eltOrSelector the root element to find the matching elements in, inclusive | the selector to match
+ * @param {string} [selector] the selector to match
+ * @returns {NodeListOf}
+ */function findAll(e,t){return typeof e!=="string"?e.querySelectorAll(t):findAll(getDocument(),e)}
+/**
+ * @returns Window
+ */function getWindow(){return window}
+/**
+ * Removes an element from the DOM
+ *
+ * @see https://htmx.org/api/#remove
+ *
+ * @param {Node} elt
+ * @param {number} [delay]
+ */function removeElement(e,t){e=resolveTarget(e);t?getWindow().setTimeout((function(){removeElement(e);e=null}),t):parentElt(e).removeChild(e)}
+/**
+ * @param {any} elt
+ * @return {Element|null}
+ */function asElement(e){return e instanceof Element?e:null}
+/**
+ * @param {any} elt
+ * @return {HTMLElement|null}
+ */function asHtmlElement(e){return e instanceof HTMLElement?e:null}
+/**
+ * @param {any} value
+ * @return {string|null}
+ */function asString(e){return typeof e==="string"?e:null}
+/**
+ * @param {EventTarget} elt
+ * @return {ParentNode|null}
+ */function asParentNode(e){return e instanceof Element||e instanceof Document||e instanceof DocumentFragment?e:null}
+/**
+ * This method adds a class to the given element.
+ *
+ * @see https://htmx.org/api/#addClass
+ *
+ * @param {Element|string} elt the element to add the class to
+ * @param {string} clazz the class to add
+ * @param {number} [delay] the delay (in milliseconds) before class is added
+ */function addClassToElement(e,t,n){e=asElement(resolveTarget(e));e&&(n?getWindow().setTimeout((function(){addClassToElement(e,t);e=null}),n):e.classList&&e.classList.add(t))}
+/**
+ * Removes a class from the given element
+ *
+ * @see https://htmx.org/api/#removeClass
+ *
+ * @param {Node|string} node element to remove the class from
+ * @param {string} clazz the class to remove
+ * @param {number} [delay] the delay (in milliseconds before class is removed)
+ */function removeClassFromElement(e,t,n){let r=asElement(resolveTarget(e));if(r)if(n)getWindow().setTimeout((function(){removeClassFromElement(r,t);r=null}),n);else if(r.classList){r.classList.remove(t);r.classList.length===0&&r.removeAttribute("class")}}
+/**
+ * Toggles the given class on an element
+ *
+ * @see https://htmx.org/api/#toggleClass
+ *
+ * @param {Element|string} elt the element to toggle the class on
+ * @param {string} clazz the class to toggle
+ */function toggleClassOnElement(e,t){e=resolveTarget(e);e.classList.toggle(t)}
+/**
+ * Takes the given class from its siblings, so that among its siblings, only the given element will have the class.
+ *
+ * @see https://htmx.org/api/#takeClass
+ *
+ * @param {Node|string} elt the element that will take the class
+ * @param {string} clazz the class to take
+ */function takeClassForElement(e,t){e=resolveTarget(e);forEach(e.parentElement.children,(function(e){removeClassFromElement(e,t)}));addClassToElement(asElement(e),t)}
+/**
+ * Finds the closest matching element in the given elements parentage, inclusive of the element
+ *
+ * @see https://htmx.org/api/#closest
+ *
+ * @param {Element|string} elt the element to find the selector from
+ * @param {string} selector the selector to find
+ * @returns {Element|null}
+ */function closest(e,t){e=asElement(resolveTarget(e));if(e&&e.closest)return e.closest(t);do{if(e==null||matches(e,t))return e}while(e=e&&asElement(parentElt(e)));return null}
+/**
+ * @param {string} str
+ * @param {string} prefix
+ * @returns {boolean}
+ */function startsWith(e,t){return e.substring(0,t.length)===t}
+/**
+ * @param {string} str
+ * @param {string} suffix
+ * @returns {boolean}
+ */function endsWith(e,t){return e.substring(e.length-t.length)===t}
+/**
+ * @param {string} selector
+ * @returns {string}
+ */function normalizeSelector(e){const t=e.trim();return startsWith(t,"<")&&endsWith(t,"/>")?t.substring(1,t.length-2):t}
+/**
+ * @param {Node|Element|Document|string} elt
+ * @param {string} selector
+ * @param {boolean=} global
+ * @returns {(Node|Window)[]}
+ */function querySelectorAllExt(e,t,n){e=resolveTarget(e);return t.indexOf("closest ")===0?[closest(asElement(e),normalizeSelector(t.substr(8)))]:t.indexOf("find ")===0?[find(asParentNode(e),normalizeSelector(t.substr(5)))]:t==="next"?[asElement(e).nextElementSibling]:t.indexOf("next ")===0?[scanForwardQuery(e,normalizeSelector(t.substr(5)),!!n)]:t==="previous"?[asElement(e).previousElementSibling]:t.indexOf("previous ")===0?[scanBackwardsQuery(e,normalizeSelector(t.substr(9)),!!n)]:t==="document"?[document]:t==="window"?[window]:t==="body"?[document.body]:t==="root"?[getRootNode(e,!!n)]:t.indexOf("global ")===0?querySelectorAllExt(e,t.slice(7),true):toArray(asParentNode(getRootNode(e,!!n)).querySelectorAll(normalizeSelector(t)))}
+/**
+ * @param {Node} start
+ * @param {string} match
+ * @param {boolean} global
+ * @returns {Element}
+ */var scanForwardQuery=function(e,t,n){const r=asParentNode(getRootNode(e,n)).querySelectorAll(t);for(let t=0;t=0;t--){const n=r[t];if(n.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING)return n}};
+/**
+ * @param {Node|string} eltOrSelector
+ * @param {string=} selector
+ * @returns {Node|Window}
+ */function querySelectorExt(e,t){return typeof e!=="string"?querySelectorAllExt(e,t)[0]:querySelectorAllExt(getDocument().body,e)[0]}
+/**
+ * @template {EventTarget} T
+ * @param {T|string} eltOrSelector
+ * @param {T} [context]
+ * @returns {Element|T|null}
+ */function resolveTarget(e,t){return typeof e==="string"?find(asParentNode(t)||document,e):e}
+/**
+ * @typedef {keyof HTMLElementEventMap|string} AnyEventName
+ */
+/**
+ * @typedef {Object} EventArgs
+ * @property {EventTarget} target
+ * @property {AnyEventName} event
+ * @property {EventListener} listener
+ */
+/**
+ * @param {EventTarget|AnyEventName} arg1
+ * @param {AnyEventName|EventListener} arg2
+ * @param {EventListener} [arg3]
+ * @returns {EventArgs}
+ */function processEventArgs(e,t,n){return isFunction(t)?{target:getDocument().body,event:asString(e),listener:t}:{target:resolveTarget(e),event:asString(t),listener:n}}
+/**
+ * Adds an event listener to an element
+ *
+ * @see https://htmx.org/api/#on
+ *
+ * @param {EventTarget|string} arg1 the element to add the listener to | the event name to add the listener for
+ * @param {string|EventListener} arg2 the event name to add the listener for | the listener to add
+ * @param {EventListener} [arg3] the listener to add
+ * @returns {EventListener}
+ */function addEventListenerImpl(e,t,n){ready((function(){const r=processEventArgs(e,t,n);r.target.addEventListener(r.event,r.listener)}));const r=isFunction(t);return r?t:n}
+/**
+ * Removes an event listener from an element
+ *
+ * @see https://htmx.org/api/#off
+ *
+ * @param {EventTarget|string} arg1 the element to remove the listener from | the event name to remove the listener from
+ * @param {string|EventListener} arg2 the event name to remove the listener from | the listener to remove
+ * @param {EventListener} [arg3] the listener to remove
+ * @returns {EventListener}
+ */function removeEventListenerImpl(e,t,n){ready((function(){const r=processEventArgs(e,t,n);r.target.removeEventListener(r.event,r.listener)}));return isFunction(t)?t:n}const DUMMY_ELT=getDocument().createElement("output");
+/**
+ * @param {Element} elt
+ * @param {string} attrName
+ * @returns {(Node|Window)[]}
+ */function findAttributeTargets(e,t){const n=getClosestAttributeValue(e,t);if(n){if(n==="this")return[findThisElement(e,t)];{const r=querySelectorAllExt(e,n);if(r.length===0){logError('The selector "'+n+'" on '+t+" returned no matches!");return[DUMMY_ELT]}return r}}}
+/**
+ * @param {Element} elt
+ * @param {string} attribute
+ * @returns {Element|null}
+ */function findThisElement(e,t){return asElement(getClosestMatch(e,(function(e){return getAttributeValue(asElement(e),t)!=null})))}
+/**
+ * @param {Element} elt
+ * @returns {Node|Window|null}
+ */function getTarget(e){const t=getClosestAttributeValue(e,"hx-target");if(t)return t==="this"?findThisElement(e,"hx-target"):querySelectorExt(e,t);{const t=getInternalData(e);return t.boosted?getDocument().body:e}}
+/**
+ * @param {string} name
+ * @returns {boolean}
+ */function shouldSettleAttribute(e){const t=htmx.config.attributesToSettle;for(let n=0;n0){o=e.substr(0,e.indexOf(":"));r=e.substr(e.indexOf(":")+1,e.length)}else o=e;const s=getDocument().querySelectorAll(r);if(s){forEach(s,(function(e){let r;const s=t.cloneNode(true);r=getDocument().createDocumentFragment();r.appendChild(s);isInlineSwap(o,e)||(r=asParentNode(s));const a={shouldSwap:true,target:e,fragment:r};if(triggerEvent(e,"htmx:oobBeforeSwap",a)){e=a.target;a.shouldSwap&&swapWithStyle(o,e,e,r,n);forEach(n.elts,(function(e){triggerEvent(e,"htmx:oobAfterSwap",a)}))}}));t.parentNode.removeChild(t)}else{t.parentNode.removeChild(t);triggerErrorEvent(getDocument().body,"htmx:oobErrorNoTarget",{content:t})}return e}
+/**
+ * @param {DocumentFragment} fragment
+ */function handlePreservedElements(e){forEach(findAll(e,"[hx-preserve], [data-hx-preserve]"),(function(e){const t=getAttributeValue(e,"id");const n=getDocument().getElementById(t);n!=null&&e.parentNode.replaceChild(n,e)}))}
+/**
+ * @param {Node} parentNode
+ * @param {ParentNode} fragment
+ * @param {HtmxSettleInfo} settleInfo
+ */function handleAttributes(e,t,n){forEach(t.querySelectorAll("[id]"),(function(t){const r=getRawAttribute(t,"id");if(r&&r.length>0){const o=r.replace("'","\\'");const s=t.tagName.replace(":","\\:");const a=asParentNode(e);const i=a&&a.querySelector(s+"[id='"+o+"']");if(i&&i!==a){const e=t.cloneNode();cloneAttributes(t,i);n.tasks.push((function(){cloneAttributes(t,e)}))}}}))}
+/**
+ * @param {Node} child
+ * @returns {HtmxSettleTask}
+ */function makeAjaxLoadTask(e){return function(){removeClassFromElement(e,htmx.config.addedClass);processNode(asElement(e));processFocus(asParentNode(e));triggerEvent(e,"htmx:load")}}
+/**
+ * @param {ParentNode} child
+ */function processFocus(e){const t="[autofocus]";const n=asHtmlElement(matches(e,t)?e:e.querySelector(t));n!=null&&n.focus()}
+/**
+ * @param {Node} parentNode
+ * @param {Node} insertBefore
+ * @param {ParentNode} fragment
+ * @param {HtmxSettleInfo} settleInfo
+ */function insertNodesBefore(e,t,n,r){handleAttributes(e,n,r);while(n.childNodes.length>0){const o=n.firstChild;addClassToElement(asElement(o),htmx.config.addedClass);e.insertBefore(o,t);o.nodeType!==Node.TEXT_NODE&&o.nodeType!==Node.COMMENT_NODE&&r.tasks.push(makeAjaxLoadTask(o))}}
+/**
+ * based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0,
+ * derived from Java's string hashcode implementation
+ * @param {string} string
+ * @param {number} hash
+ * @returns {number}
+ */function stringHash(e,t){let n=0;while(n0?getWindow().setTimeout(doSettle,n.settleDelay):doSettle()}
+/**
+ * @param {XMLHttpRequest} xhr
+ * @param {string} header
+ * @param {EventTarget} elt
+ */function handleTriggerHeader(e,t,n){const r=e.getResponseHeader(t);if(r.indexOf("{")===0){const e=parseJSON(r);for(const t in e)if(e.hasOwnProperty(t)){let r=e[t];isRawObject(r)||(r={value:r});triggerEvent(n,t,r)}}else{const e=r.split(",");for(let t=0;t0){const a=t[0];if(a==="]"){r--;if(r===0){s===null&&(o+="true");t.shift();o+=")})";try{const t=maybeEval(e,(function(){return Function(o)()}),(function(){return true}));t.source=o;return t}catch(e){triggerErrorEvent(getDocument().body,"htmx:syntax:error",{error:e,source:o});return null}}}else a==="["&&r++;isPossibleRelativeReference(a,s,n)?o+="(("+n+"."+a+") ? ("+n+"."+a+") : (window."+a+"))":o+=a;s=t.shift()}}}
+/**
+ * @param {string[]} tokens
+ * @param {RegExp} match
+ * @returns {string}
+ */function consumeUntil(e,t){let n="";while(e.length>0&&!t.test(e[0]))n+=e.shift();return n}
+/**
+ * @param {string[]} tokens
+ * @returns {string}
+ */function consumeCSSSelector(e){let t;if(e.length>0&&COMBINED_SELECTOR_START.test(e[0])){e.shift();t=consumeUntil(e,COMBINED_SELECTOR_END).trim();e.shift()}else t=consumeUntil(e,WHITESPACE_OR_COMMA);return t}const INPUT_SELECTOR="input, textarea, select";
+/**
+ * @param {Element} elt
+ * @param {string} explicitTrigger
+ * @param {Object} cache for trigger specs
+ * @returns {HtmxTriggerSpecification[]}
+ */function parseAndCacheTrigger(e,t,n){
+/** @type HtmxTriggerSpecification[] */
+const r=[];const o=tokenizeString(t);do{consumeUntil(o,NOT_WHITESPACE);const t=o.length;const n=consumeUntil(o,/[,\[\s]/);if(n!=="")if(n==="every"){
+/** @type HtmxTriggerSpecification */
+const t={trigger:"every"};consumeUntil(o,NOT_WHITESPACE);t.pollInterval=parseInterval(consumeUntil(o,/[,\[\s]/));consumeUntil(o,NOT_WHITESPACE);var s=maybeGenerateConditional(e,o,"event");s&&(t.eventFilter=s);r.push(t)}else{
+/** @type HtmxTriggerSpecification */
+const t={trigger:n};s=maybeGenerateConditional(e,o,"event");s&&(t.eventFilter=s);while(o.length>0&&o[0]!==","){consumeUntil(o,NOT_WHITESPACE);const n=o.shift();if(n==="changed")t.changed=true;else if(n==="once")t.once=true;else if(n==="consume")t.consume=true;else if(n==="delay"&&o[0]===":"){o.shift();t.delay=parseInterval(consumeUntil(o,WHITESPACE_OR_COMMA))}else if(n==="from"&&o[0]===":"){o.shift();if(COMBINED_SELECTOR_START.test(o[0]))var a=consumeCSSSelector(o);else{a=consumeUntil(o,WHITESPACE_OR_COMMA);if(a==="closest"||a==="find"||a==="next"||a==="previous"){o.shift();const e=consumeCSSSelector(o);e.length>0&&(a+=" "+e)}}t.from=a}else if(n==="target"&&o[0]===":"){o.shift();t.target=consumeCSSSelector(o)}else if(n==="throttle"&&o[0]===":"){o.shift();t.throttle=parseInterval(consumeUntil(o,WHITESPACE_OR_COMMA))}else if(n==="queue"&&o[0]===":"){o.shift();t.queue=consumeUntil(o,WHITESPACE_OR_COMMA)}else if(n==="root"&&o[0]===":"){o.shift();t[n]=consumeCSSSelector(o)}else if(n==="threshold"&&o[0]===":"){o.shift();t[n]=consumeUntil(o,WHITESPACE_OR_COMMA)}else triggerErrorEvent(e,"htmx:syntax:error",{token:o.shift()})}r.push(t)}o.length===t&&triggerErrorEvent(e,"htmx:syntax:error",{token:o.shift()});consumeUntil(o,NOT_WHITESPACE)}while(o[0]===","&&o.shift());n&&(n[t]=r);return r}
+/**
+ * @param {Element} elt
+ * @returns {HtmxTriggerSpecification[]}
+ */function getTriggerSpecs(e){const t=getAttributeValue(e,"hx-trigger");let n=[];if(t){const r=htmx.config.triggerSpecsCache;n=r&&r[t]||parseAndCacheTrigger(e,t,r)}return n.length>0?n:matches(e,"form")?[{trigger:"submit"}]:matches(e,'input[type="button"], input[type="submit"]')?[{trigger:"click"}]:matches(e,INPUT_SELECTOR)?[{trigger:"change"}]:[{trigger:"click"}]}
+/**
+ * @param {Element} elt
+ */function cancelPolling(e){getInternalData(e).cancelled=true}
+/**
+ * @param {Element} elt
+ * @param {TriggerHandler} handler
+ * @param {HtmxTriggerSpecification} spec
+ */function processPolling(e,t,n){const r=getInternalData(e);r.timeout=getWindow().setTimeout((function(){if(bodyContains(e)&&r.cancelled!==true){maybeFilterEvent(n,e,makeEvent("hx:poll:trigger",{triggerSpec:n,target:e}))||t(e);processPolling(e,t,n)}}),n.pollInterval)}
+/**
+ * @param {HTMLAnchorElement} elt
+ * @returns {boolean}
+ */function isLocalLink(e){return location.hostname===e.hostname&&getRawAttribute(e,"href")&&getRawAttribute(e,"href").indexOf("#")!==0}
+/**
+ * @param {Element} elt
+ */function eltIsDisabled(e){return closest(e,htmx.config.disableSelector)}
+/**
+ * @param {Element} elt
+ * @param {HtmxNodeInternalData} nodeData
+ * @param {HtmxTriggerSpecification[]} triggerSpecs
+ */function boostElement(e,t,n){if(e instanceof HTMLAnchorElement&&isLocalLink(e)&&(e.target===""||e.target==="_self")||e.tagName==="FORM"){t.boosted=true;let r,o;if(e.tagName==="A"){r="get";o=getRawAttribute(e,"href")}else{const t=getRawAttribute(e,"method");r=t?t.toLowerCase():"get";r==="get";o=getRawAttribute(e,"action")}n.forEach((function(n){addEventListener(e,(function(e,t){const n=asElement(e);eltIsDisabled(n)?cleanUpElement(n):issueAjaxRequest(r,o,n,t)}),t,n,true)}))}}
+/**
+ * @param {Event} evt
+ * @param {Node} node
+ * @returns {boolean}
+ */function shouldCancel(e,t){const n=asElement(t);if(!n)return false;if(e.type==="submit"||e.type==="click"){if(n.tagName==="FORM")return true;if(matches(n,'input[type="submit"], button')&&closest(n,"form")!==null)return true;if(n instanceof HTMLAnchorElement&&n.href&&(n.getAttribute("href")==="#"||n.getAttribute("href").indexOf("#")!==0))return true}return false}
+/**
+ * @param {Node} elt
+ * @param {Event|MouseEvent|KeyboardEvent|TouchEvent} evt
+ * @returns {boolean}
+ */function ignoreBoostedAnchorCtrlClick(e,t){return getInternalData(e).boosted&&e instanceof HTMLAnchorElement&&t.type==="click"&&(t.ctrlKey||t.metaKey)}
+/**
+ * @param {HtmxTriggerSpecification} triggerSpec
+ * @param {Node} elt
+ * @param {Event} evt
+ * @returns {boolean}
+ */function maybeFilterEvent(e,t,n){const r=e.eventFilter;if(r)try{return r.call(t,n)!==true}catch(e){const t=r.source;triggerErrorEvent(getDocument().body,"htmx:eventFilter:error",{error:e,source:t});return true}return false}
+/**
+ * @param {Node} elt
+ * @param {TriggerHandler} handler
+ * @param {HtmxNodeInternalData} nodeData
+ * @param {HtmxTriggerSpecification} triggerSpec
+ * @param {boolean} [explicitCancel]
+ */function addEventListener(e,t,n,r,o){const s=getInternalData(e);
+/** @type {(Node|Window)[]} */let a;a=r.from?querySelectorAllExt(e,r.from):[e];r.changed&&a.forEach((function(e){const t=getInternalData(e);t.lastValue=e.value}));forEach(a,(function(a){
+/** @type EventListener */
+const eventListener=function(n){if(!bodyContains(e)){a.removeEventListener(r.trigger,eventListener);return}if(ignoreBoostedAnchorCtrlClick(e,n))return;(o||shouldCancel(n,e))&&n.preventDefault();if(maybeFilterEvent(r,e,n))return;const i=getInternalData(n);i.triggerSpec=r;i.handledFor==null&&(i.handledFor=[]);if(i.handledFor.indexOf(e)<0){i.handledFor.push(e);r.consume&&n.stopPropagation();if(r.target&&n.target&&!matches(asElement(n.target),r.target))return;if(r.once){if(s.triggeredOnce)return;s.triggeredOnce=true}if(r.changed){const e=getInternalData(a);const t=a.value;if(e.lastValue===t)return;e.lastValue=t}s.delayed&&clearTimeout(s.delayed);if(s.throttle)return;if(r.throttle>0){if(!s.throttle){t(e,n);s.throttle=getWindow().setTimeout((function(){s.throttle=null}),r.throttle)}}else if(r.delay>0)s.delayed=getWindow().setTimeout((function(){t(e,n)}),r.delay);else{triggerEvent(e,"htmx:trigger");t(e,n)}}};n.listenerInfos==null&&(n.listenerInfos=[]);n.listenerInfos.push({trigger:r.trigger,listener:eventListener,on:a});a.addEventListener(r.trigger,eventListener)}))}let windowIsScrolling=false;let scrollHandler=null;function initScrollHandler(){if(!scrollHandler){scrollHandler=function(){windowIsScrolling=true};window.addEventListener("scroll",scrollHandler);setInterval((function(){if(windowIsScrolling){windowIsScrolling=false;forEach(getDocument().querySelectorAll("[hx-trigger*='revealed'],[data-hx-trigger*='revealed']"),(function(e){maybeReveal(e)}))}}),200)}}
+/**
+ * @param {Element} elt
+ */function maybeReveal(e){if(!hasAttribute(e,"data-hx-revealed")&&isScrolledIntoView(e)){e.setAttribute("data-hx-revealed","true");const t=getInternalData(e);t.initHash?triggerEvent(e,"revealed"):e.addEventListener("htmx:afterProcessNode",(function(){triggerEvent(e,"revealed")}),{once:true})}}
+/**
+ * @param {Element} elt
+ * @param {TriggerHandler} handler
+ * @param {HtmxNodeInternalData} nodeData
+ * @param {number} delay
+ */function loadImmediately(e,t,n,r){const load=function(){if(!n.loaded){n.loaded=true;t(e)}};r>0?getWindow().setTimeout(load,r):load()}
+/**
+ * @param {Element} elt
+ * @param {HtmxNodeInternalData} nodeData
+ * @param {HtmxTriggerSpecification[]} triggerSpecs
+ * @returns {boolean}
+ */function processVerbs(e,t,n){let r=false;forEach(VERBS,(function(o){if(hasAttribute(e,"hx-"+o)){const s=getAttributeValue(e,"hx-"+o);r=true;t.path=s;t.verb=o;n.forEach((function(n){addTriggerHandler(e,n,t,(function(e,t){const n=asElement(e);closest(n,htmx.config.disableSelector)?cleanUpElement(n):issueAjaxRequest(o,s,n,t)}))}))}}));return r}
+/**
+ * @callback TriggerHandler
+ * @param {Node} elt
+ * @param {Event} [evt]
+ */
+/**
+ * @param {Node} elt
+ * @param {HtmxTriggerSpecification} triggerSpec
+ * @param {HtmxNodeInternalData} nodeData
+ * @param {TriggerHandler} handler
+ */function addTriggerHandler(e,t,n,r){if(t.trigger==="revealed"){initScrollHandler();addEventListener(e,r,n,t);maybeReveal(asElement(e))}else if(t.trigger==="intersect"){const o={};t.root&&(o.root=querySelectorExt(e,t.root));t.threshold&&(o.threshold=parseFloat(t.threshold));const s=new IntersectionObserver((function(t){for(let n=0;n0){n.polling=true;processPolling(asElement(e),r,t)}else addEventListener(e,r,n,t)}
+/**
+ * @param {Node} node
+ * @returns {boolean}
+ */function shouldProcessHxOn(e){const t=asElement(e);if(!t)return false;const n=t.attributes;for(let e=0;e|[]}
+ */function findElementsToProcess(e){if(e.querySelectorAll){const n=", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]";const r=[];for(const e in extensions){const n=extensions[e];if(n.getSelectors){var t=n.getSelectors();t&&r.push(t)}}const o=e.querySelectorAll(VERB_SELECTOR+n+", form, [type='submit'], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger]"+r.flat().map((e=>", "+e)).join(""));return o}return[]}
+/**
+ * Handle submit buttons/inputs that have the form attribute set
+ * see https://developer.mozilla.org/docs/Web/HTML/Element/button
+ * @param {Event} evt
+ */function maybeSetLastButtonClicked(e){const t=/** @type {HTMLButtonElement|HTMLInputElement} */closest(asElement(e.target),"button, input[type='submit']");const n=getRelatedFormData(e);n&&(n.lastButtonClicked=t)}
+/**
+ * @param {Event} evt
+ */function maybeUnsetLastButtonClicked(e){const t=getRelatedFormData(e);t&&(t.lastButtonClicked=null)}
+/**
+ * @param {Event} evt
+ * @returns {HtmxNodeInternalData|undefined}
+ */function getRelatedFormData(e){const t=closest(asElement(e.target),"button, input[type='submit']");if(!t)return;const n=resolveTarget("#"+getRawAttribute(t,"form"),t.getRootNode())||closest(t,"form");return n?getInternalData(n):void 0}
+/**
+ * @param {EventTarget} elt
+ */function initButtonTracking(e){e.addEventListener("click",maybeSetLastButtonClicked);e.addEventListener("focusin",maybeSetLastButtonClicked);e.addEventListener("focusout",maybeUnsetLastButtonClicked)}
+/**
+ * @param {Element} elt
+ * @param {string} eventName
+ * @param {string} code
+ */function addHxOnEventHandler(e,t,n){const r=getInternalData(e);Array.isArray(r.onHandlers)||(r.onHandlers=[]);let o;
+/** @type EventListener */const listener=function(t){maybeEval(e,(function(){if(!eltIsDisabled(e)){o||(o=new Function("event",n));o.call(e,t)}}))};e.addEventListener(t,listener);r.onHandlers.push({event:t,listener:listener})}
+/**
+ * @param {Element} elt
+ */function processHxOnWildcard(e){deInitOnHandlers(e);for(let t=0;t void} toDo
+ * @returns void
+ */function withExtensions(e,t){forEach(getExtensions(e),(function(e){try{t(e)}catch(e){logError(e)}}))}function logError(e){console.error?console.error(e):console.log&&console.log("ERROR: ",e)}
+/**
+ * Triggers a given event on an element
+ *
+ * @see https://htmx.org/api/#trigger
+ *
+ * @param {EventTarget|string} elt the element to trigger the event on
+ * @param {string} eventName the name of the event to trigger
+ * @param {any=} detail details for the event
+ * @returns {boolean}
+ */function triggerEvent(e,t,n){e=resolveTarget(e);n==null&&(n={});n.elt=e;const r=makeEvent(t,n);htmx.logger&&!ignoreEventForLogging(t)&&htmx.logger(e,t,n);if(n.error){logError(n.error);triggerEvent(e,"htmx:error",{errorInfo:n})}let o=e.dispatchEvent(r);const s=kebabEventName(t);if(o&&s!==t){const t=makeEvent(s,r.detail);o=o&&e.dispatchEvent(t)}withExtensions(asElement(e),(function(e){o=o&&e.onEvent(t,r)!==false&&!r.defaultPrevented}));return o}let currentPathForHistory=location.pathname+location.search;
+/**
+ * @returns {Element}
+ */function getHistoryElement(){const e=getDocument().querySelector("[hx-history-elt],[data-hx-history-elt]");return e||getDocument().body}
+/**
+ * @param {string} url
+ * @param {Element} rootElt
+ */function saveToHistoryCache(e,t){if(!canAccessLocalStorage())return;const n=cleanInnerHtmlForHistory(t);const r=getDocument().title;const o=window.scrollY;if(htmx.config.historyCacheSize<=0){localStorage.removeItem("htmx-history-cache");return}e=normalizePath(e);const s=parseJSON(localStorage.getItem("htmx-history-cache"))||[];for(let t=0;thtmx.config.historyCacheSize)s.shift();while(s.length>0)try{localStorage.setItem("htmx-history-cache",JSON.stringify(s));break}catch(e){triggerErrorEvent(getDocument().body,"htmx:historyCacheError",{cause:e,cache:s});s.shift()}}
+/**
+ * @typedef {Object} HtmxHistoryItem
+ * @property {string} url
+ * @property {string} content
+ * @property {string} title
+ * @property {number} scroll
+ */
+/**
+ * @param {string} url
+ * @returns {HtmxHistoryItem|null}
+ */function getCachedHistory(e){if(!canAccessLocalStorage())return null;e=normalizePath(e);const t=parseJSON(localStorage.getItem("htmx-history-cache"))||[];for(let n=0;n=200&&this.status<400){triggerEvent(getDocument().body,"htmx:historyCacheMissLoad",n);const t=makeFragment(this.response);
+/** @type ParentNode */const r=t.querySelector("[hx-history-elt],[data-hx-history-elt]")||t;const o=getHistoryElement();const s=makeSettleInfo(o);handleTitle(t.title);swapInnerHTML(o,r,s);settleImmediately(s.tasks);currentPathForHistory=e;triggerEvent(getDocument().body,"htmx:historyRestore",{path:e,cacheMiss:true,serverResponse:this.response})}else triggerErrorEvent(getDocument().body,"htmx:historyCacheMissLoadError",n)};t.send()}
+/**
+ * @param {string} [path]
+ */function restoreHistory(e){saveCurrentPageToHistory();e=e||location.pathname+location.search;const t=getCachedHistory(e);if(t){const n=makeFragment(t.content);const r=getHistoryElement();const o=makeSettleInfo(r);handleTitle(n.title);swapInnerHTML(r,n,o);settleImmediately(o.tasks);getWindow().setTimeout((function(){window.scrollTo(0,t.scroll)}),0);currentPathForHistory=e;triggerEvent(getDocument().body,"htmx:historyRestore",{path:e,item:t})}else htmx.config.refreshOnHistoryMiss?window.location.reload(true):loadHistoryFromServer(e)}
+/**
+ * @param {Element} elt
+ * @returns {Element[]}
+ */function addRequestIndicatorClasses(e){let t=/** @type Element[] */findAttributeTargets(e,"hx-indicator");t==null&&(t=[e]);forEach(t,(function(e){const t=getInternalData(e);t.requestCount=(t.requestCount||0)+1;e.classList.add.call(e.classList,htmx.config.requestClass)}));return t}
+/**
+ * @param {Element} elt
+ * @returns {Element[]}
+ */function disableElements(e){let t=/** @type Element[] */findAttributeTargets(e,"hx-disabled-elt");t==null&&(t=[]);forEach(t,(function(e){const t=getInternalData(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")}));return t}
+/**
+ * @param {Element[]} indicators
+ * @param {Element[]} disabled
+ */function removeRequestIndicators(e,t){forEach(e,(function(e){const t=getInternalData(e);t.requestCount=(t.requestCount||0)-1;t.requestCount===0&&e.classList.remove.call(e.classList,htmx.config.requestClass)}));forEach(t,(function(e){const t=getInternalData(e);t.requestCount=(t.requestCount||0)-1;t.requestCount===0&&e.removeAttribute("disabled")}))}
+/**
+ * @param {Element[]} processed
+ * @param {Element} elt
+ * @returns {boolean}
+ */function haveSeenNode(e,t){for(let n=0;nt.indexOf(e)<0)):r.filter((e=>e!==t));n.delete(e);forEach(r,(t=>n.append(e,t)))}}
+/**
+ * @param {Element[]} processed
+ * @param {FormData} formData
+ * @param {HtmxElementValidationError[]} errors
+ * @param {Element|HTMLInputElement|HTMLSelectElement|HTMLFormElement} elt
+ * @param {boolean} validate
+ */function processInputValue(e,t,n,r,o){if(r!=null&&!haveSeenNode(e,r)){e.push(r);if(shouldInclude(r)){const e=getRawAttribute(r,"name");let s=r.value;r instanceof HTMLSelectElement&&r.multiple&&(s=toArray(r.querySelectorAll("option:checked")).map((function(e){/** @type HTMLOptionElement */return e.value})));r instanceof HTMLInputElement&&r.files&&(s=toArray(r.files));addValueToFormData(e,s,t);o&&validateElement(r,n)}if(r instanceof HTMLFormElement){forEach(r.elements,(function(r){e.indexOf(r)>=0?removeValueFromFormData(r.name,r.value,t):e.push(r);o&&validateElement(r,n)}));new FormData(r).forEach((function(e,n){e instanceof File&&e.name===""||addValueToFormData(n,e,t)}))}}}
+/**
+ *
+ * @param {Element} elt
+ * @param {HtmxElementValidationError[]} errors
+ */function validateElement(e,t){const n=/** @type {HTMLElement & ElementInternals} */e;if(n.willValidate){triggerEvent(n,"htmx:validation:validate");if(!n.checkValidity()){t.push({elt:n,message:n.validationMessage,validity:n.validity});triggerEvent(n,"htmx:validation:failed",{message:n.validationMessage,validity:n.validity})}}}
+/**
+ * Override values in the one FormData with those from another.
+ * @param {FormData} receiver the formdata that will be mutated
+ * @param {FormData} donor the formdata that will provide the overriding values
+ * @returns {FormData} the {@linkcode receiver}
+ */function overrideFormData(e,t){for(const n of t.keys()){e.delete(n);t.getAll(n).forEach((function(t){e.append(n,t)}))}return e}
+/**
+ * @param {Element|HTMLFormElement} elt
+ * @param {HttpVerb} verb
+ * @returns {{errors: HtmxElementValidationError[], formData: FormData, values: Object}}
+ */function getInputValues(e,t){
+/** @type Element[] */
+const n=[];const r=new FormData;const o=new FormData;
+/** @type HtmxElementValidationError[] */const s=[];const a=getInternalData(e);a.lastButtonClicked&&!bodyContains(a.lastButtonClicked)&&(a.lastButtonClicked=null);let i=e instanceof HTMLFormElement&&e.noValidate!==true||getAttributeValue(e,"hx-validate")==="true";a.lastButtonClicked&&(i=i&&a.lastButtonClicked.formNoValidate!==true);t!=="get"&&processInputValue(n,o,s,closest(e,"form"),i);processInputValue(n,r,s,e,i);if(a.lastButtonClicked||e.tagName==="BUTTON"||e.tagName==="INPUT"&&getRawAttribute(e,"type")==="submit"){const t=a.lastButtonClicked||/** @type HTMLInputElement|HTMLButtonElement */e;const n=getRawAttribute(t,"name");addValueToFormData(n,t.value,o)}const l=findAttributeTargets(e,"hx-include");forEach(l,(function(e){processInputValue(n,r,s,asElement(e),i);matches(e,"form")||forEach(asParentNode(e).querySelectorAll(INPUT_SELECTOR),(function(e){processInputValue(n,r,s,e,i)}))}));overrideFormData(r,o);return{errors:s,formData:r,values:formDataProxy(r)}}
+/**
+ * @param {string} returnStr
+ * @param {string} name
+ * @param {any} realValue
+ * @returns {string}
+ */function appendParam(e,t,n){e!==""&&(e+="&");String(n)==="[object Object]"&&(n=JSON.stringify(n));const r=encodeURIComponent(n);e+=encodeURIComponent(t)+"="+r;return e}
+/**
+ * @param {FormData|Object} values
+ * @returns string
+ */function urlEncode(e){e=formDataFromObject(e);let t="";e.forEach((function(e,n){t=appendParam(t,n,e)}));return t}
+/**
+ * @param {Element} elt
+ * @param {Element} target
+ * @param {string} prompt
+ * @returns {HtmxHeaderSpecification}
+ */function getHeaders(e,t,n){
+/** @type HtmxHeaderSpecification */
+const r={"HX-Request":"true","HX-Trigger":getRawAttribute(e,"id"),"HX-Trigger-Name":getRawAttribute(e,"name"),"HX-Target":getAttributeValue(t,"id"),"HX-Current-URL":getDocument().location.href};getValuesForElement(e,"hx-headers",false,r);n!==void 0&&(r["HX-Prompt"]=n);getInternalData(e).boosted&&(r["HX-Boosted"]="true");return r}
+/**
+ * filterValues takes an object containing form input values
+ * and returns a new object that only contains keys that are
+ * specified by the closest "hx-params" attribute
+ * @param {FormData} inputValues
+ * @param {Element} elt
+ * @returns {FormData}
+ */function filterValues(e,t){const n=getClosestAttributeValue(t,"hx-params");if(n){if(n==="none")return new FormData;if(n==="*")return e;if(n.indexOf("not ")===0){forEach(n.substr(4).split(","),(function(t){t=t.trim();e.delete(t)}));return e}{const t=new FormData;forEach(n.split(","),(function(n){n=n.trim();e.has(n)&&e.getAll(n).forEach((function(e){t.append(n,e)}))}));return t}}return e}
+/**
+ * @param {Element} elt
+ * @return {boolean}
+ */function isAnchorLink(e){return!!getRawAttribute(e,"href")&&getRawAttribute(e,"href").indexOf("#")>=0}
+/**
+ * @param {Element} elt
+ * @param {HtmxSwapStyle} [swapInfoOverride]
+ * @returns {HtmxSwapSpecification}
+ */function getSwapSpecification(e,t){const n=t||getClosestAttributeValue(e,"hx-swap");
+/** @type HtmxSwapSpecification */const r={swapStyle:getInternalData(e).boosted?"innerHTML":htmx.config.defaultSwapStyle,swapDelay:htmx.config.defaultSwapDelay,settleDelay:htmx.config.defaultSettleDelay};htmx.config.scrollIntoViewOnBoost&&getInternalData(e).boosted&&!isAnchorLink(e)&&(r.show="top");if(n){const e=splitOnWhitespace(n);if(e.length>0)for(let t=0;t0?o.join(":"):null;r.scroll=t;r.scrollTarget=s}else if(n.indexOf("show:")===0){const e=n.substr(5);o=e.split(":");const t=o.pop();s=o.length>0?o.join(":"):null;r.show=t;r.showTarget=s}else if(n.indexOf("focus-scroll:")===0){const e=n.substr(13);r.focusScroll=e=="true"}else t==0?r.swapStyle=n:logError("Unknown modifier in hx-swap: "+n)}}return r}
+/**
+ * @param {Element} elt
+ * @return {boolean}
+ */function usesFormData(e){return getClosestAttributeValue(e,"hx-encoding")==="multipart/form-data"||matches(e,"form")&&getRawAttribute(e,"enctype")==="multipart/form-data"}
+/**
+ * @param {XMLHttpRequest} xhr
+ * @param {Element} elt
+ * @param {FormData} filteredParameters
+ * @returns {*|string|null}
+ */function encodeParamsForBody(e,t,n){let r=null;withExtensions(t,(function(o){r==null&&(r=o.encodeParameters(e,n,t))}));return r!=null?r:usesFormData(t)?overrideFormData(new FormData,formDataFromObject(n)):urlEncode(n)}
+/**
+ *
+ * @param {Element} target
+ * @returns {HtmxSettleInfo}
+ */function makeSettleInfo(e){return{tasks:[],elts:[e]}}
+/**
+ * @param {Element[]} content
+ * @param {HtmxSwapSpecification} swapSpec
+ */function updateScrollState(e,t){const n=e[0];const r=e[e.length-1];if(t.scroll){var o=null;t.scrollTarget&&(o=asElement(querySelectorExt(n,t.scrollTarget)));if(t.scroll==="top"&&(n||o)){o=o||n;o.scrollTop=0}if(t.scroll==="bottom"&&(r||o)){o=o||r;o.scrollTop=o.scrollHeight}}if(t.show){o=null;if(t.showTarget){let e=t.showTarget;t.showTarget==="window"&&(e="body");o=asElement(querySelectorExt(n,e))}if(t.show==="top"&&(n||o)){o=o||n;o.scrollIntoView({block:"start",behavior:htmx.config.scrollBehavior})}if(t.show==="bottom"&&(r||o)){o=o||r;o.scrollIntoView({block:"end",behavior:htmx.config.scrollBehavior})}}}
+/**
+ * @param {Element} elt
+ * @param {string} attr
+ * @param {boolean=} evalAsDefault
+ * @param {Object=} values
+ * @returns {Object}
+ */function getValuesForElement(e,t,n,r){r==null&&(r={});if(e==null)return r;const o=getAttributeValue(e,t);if(o){let t=o.trim();let s=n;if(t==="unset")return null;if(t.indexOf("javascript:")===0){t=t.substr(11);s=true}else if(t.indexOf("js:")===0){t=t.substr(3);s=true}t.indexOf("{")!==0&&(t="{"+t+"}");let a;a=s?maybeEval(e,(function(){return Function("return ("+t+")")()}),{}):parseJSON(t);for(const e in a)a.hasOwnProperty(e)&&r[e]==null&&(r[e]=a[e])}return getValuesForElement(asElement(parentElt(e)),t,n,r)}
+/**
+ * @param {EventTarget|string} elt
+ * @param {() => any} toEval
+ * @param {any=} defaultVal
+ * @returns {any}
+ */function maybeEval(e,t,n){if(htmx.config.allowEval)return t();triggerErrorEvent(e,"htmx:evalDisallowedError");return n}
+/**
+ * @param {Element} elt
+ * @param {*?} expressionVars
+ * @returns
+ */function getHXVarsForElement(e,t){return getValuesForElement(e,"hx-vars",true,t)}
+/**
+ * @param {Element} elt
+ * @param {*?} expressionVars
+ * @returns
+ */function getHXValsForElement(e,t){return getValuesForElement(e,"hx-vals",false,t)}
+/**
+ * @param {Element} elt
+ * @returns {FormData}
+ */function getExpressionVars(e){return mergeObjects(getHXVarsForElement(e),getHXValsForElement(e))}
+/**
+ * @param {XMLHttpRequest} xhr
+ * @param {string} header
+ * @param {string|null} headerValue
+ */function safelySetHeaderValue(e,t,n){if(n!==null)try{e.setRequestHeader(t,n)}catch(r){e.setRequestHeader(t,encodeURIComponent(n));e.setRequestHeader(t+"-URI-AutoEncoded","true")}}
+/**
+ * @param {XMLHttpRequest} xhr
+ * @return {string}
+ */function getPathFromResponse(e){if(e.responseURL&&typeof URL!=="undefined")try{const t=new URL(e.responseURL);return t.pathname+t.search}catch(t){triggerErrorEvent(getDocument().body,"htmx:badResponseUrl",{url:e.responseURL})}}
+/**
+ * @param {XMLHttpRequest} xhr
+ * @param {RegExp} regexp
+ * @return {boolean}
+ */function hasHeader(e,t){return t.test(e.getAllResponseHeaders())}
+/**
+ * Issues an htmx-style AJAX request
+ *
+ * @see https://htmx.org/api/#ajax
+ *
+ * @param {HttpVerb} verb
+ * @param {string} path the URL path to make the AJAX
+ * @param {Element|string|HtmxAjaxHelperContext} context the element to target (defaults to the **body**) | a selector for the target | a context object that contains any of the following
+ * @return {Promise} Promise that resolves immediately if no request is sent, or when the request is complete
+ */function ajaxHelper(e,t,n){e=/** @type HttpVerb */e.toLowerCase();return n?n instanceof Element||typeof n==="string"?issueAjaxRequest(e,t,null,null,{targetOverride:resolveTarget(n),returnPromise:true}):issueAjaxRequest(e,t,resolveTarget(n.source),n.event,{handler:n.handler,headers:n.headers,values:n.values,targetOverride:resolveTarget(n.target),swapOverride:n.swap,select:n.select,returnPromise:true}):issueAjaxRequest(e,t,null,null,{returnPromise:true})}
+/**
+ * @param {Element} elt
+ * @return {Element[]}
+ */function hierarchyForElt(e){const t=[];while(e){t.push(e);e=e.parentElement}return t}
+/**
+ * @param {Element} elt
+ * @param {string} path
+ * @param {HtmxRequestConfig} requestConfig
+ * @return {boolean}
+ */function verifyPath(e,t,n){let r;let o;if(typeof URL==="function"){o=new URL(t,document.location.href);const e=document.location.origin;r=e===o.origin}else{o=t;r=startsWith(t,document.location.origin)}return!(htmx.config.selfRequestsOnly&&!r)&&triggerEvent(e,"htmx:validateUrl",mergeObjects({url:o,sameHost:r},n))}
+/**
+ * @param {Object|FormData} obj
+ * @return {FormData}
+ */function formDataFromObject(e){if(e instanceof FormData)return e;const t=new FormData;for(const n in e)e.hasOwnProperty(n)&&(typeof e[n].forEach==="function"?e[n].forEach((function(e){t.append(n,e)})):typeof e[n]==="object"?t.append(n,JSON.stringify(e[n])):t.append(n,e[n]));return t}
+/**
+ * @param {FormData} formData
+ * @param {string} name
+ * @param {Array} array
+ * @returns {Array}
+ */function formDataArrayProxy(e,t,n){return new Proxy(n,{get:function(n,r){return typeof r==="number"?n[r]:r==="length"?n.length:r==="push"?function(r){n.push(r);e.append(t,r)}:typeof n[r]==="function"?function(){n[r].apply(n,arguments);e.delete(t);n.forEach((function(n){e.append(t,n)}))}:n[r]&&n[r].length===1?n[r][0]:n[r]},set:function(n,r,o){n[r]=o;e.delete(t);n.forEach((function(n){e.append(t,n)}));return true}})}
+/**
+ * @param {FormData} formData
+ * @returns {Object}
+ */function formDataProxy(e){return new Proxy(e,{get:function(t,n){if(typeof n==="symbol")return Reflect.get(t,n);if(n==="toJSON")return()=>Object.fromEntries(e);if(n in t)return typeof t[n]==="function"?function(){return e[n].apply(e,arguments)}:t[n];const r=e.getAll(n);return r.length===0?void 0:r.length===1?r[0]:formDataArrayProxy(t,n,r)},set:function(e,t,n){if(typeof t!=="string")return false;e.delete(t);typeof n.forEach==="function"?n.forEach((function(n){e.append(t,n)})):e.append(t,n);return true},deleteProperty:function(e,t){typeof t==="string"&&e.delete(t);return true},ownKeys:function(e){return Reflect.ownKeys(Object.fromEntries(e))},getOwnPropertyDescriptor:function(e,t){return Reflect.getOwnPropertyDescriptor(Object.fromEntries(e),t)}})}
+/**
+ * @param {HttpVerb} verb
+ * @param {string} path
+ * @param {Element} elt
+ * @param {Event} event
+ * @param {HtmxAjaxEtc} [etc]
+ * @param {boolean} [confirmed]
+ * @return {Promise}
+ */function issueAjaxRequest(e,t,n,r,o,s){let a=null;let i=null;o=o!=null?o:{};if(o.returnPromise&&typeof Promise!=="undefined")var l=new Promise((function(e,t){a=e;i=t}));n==null&&(n=getDocument().body);const c=o.handler||handleAjaxResponse;const u=o.select||null;if(!bodyContains(n)){maybeCall(a);return l}const f=o.targetOverride||asElement(getTarget(n));if(f==null||f==DUMMY_ELT){triggerErrorEvent(n,"htmx:targetError",{target:getAttributeValue(n,"hx-target")});maybeCall(i);return l}let g=getInternalData(n);const d=g.lastButtonClicked;if(d){const n=getRawAttribute(d,"formaction");n!=null&&(t=n);const r=getRawAttribute(d,"formmethod");r!=null&&r.toLowerCase()!=="dialog"&&(e=/** @type HttpVerb */r)}const h=getClosestAttributeValue(n,"hx-confirm");if(s===void 0){const issueRequest=function(s){return issueAjaxRequest(e,t,n,r,o,!!s)};const s={target:f,elt:n,path:t,verb:e,triggeringEvent:r,etc:o,issueRequest:issueRequest,question:h};if(triggerEvent(n,"htmx:confirm",s)===false){maybeCall(a);return l}}let m=n;let p=getClosestAttributeValue(n,"hx-sync");let E=null;let x=false;if(p){const e=p.split(":");const t=e[0].trim();m=t==="this"?findThisElement(n,"hx-sync"):asElement(querySelectorExt(n,t));p=(e[1]||"drop").trim();g=getInternalData(m);if(p==="drop"&&g.xhr&&g.abortable!==true){maybeCall(a);return l}if(p==="abort"){if(g.xhr){maybeCall(a);return l}x=true}else if(p==="replace")triggerEvent(m,"htmx:abort");else if(p.indexOf("queue")===0){const e=p.split(" ");E=(e[1]||"last").trim()}}if(g.xhr){if(!g.abortable){if(E==null){if(r){const e=getInternalData(r);e&&e.triggerSpec&&e.triggerSpec.queue&&(E=e.triggerSpec.queue)}E==null&&(E="last")}g.queuedRequests==null&&(g.queuedRequests=[]);if(E==="first"&&g.queuedRequests.length===0)g.queuedRequests.push((function(){issueAjaxRequest(e,t,n,r,o)}));else if(E==="all")g.queuedRequests.push((function(){issueAjaxRequest(e,t,n,r,o)}));else if(E==="last"){g.queuedRequests=[];g.queuedRequests.push((function(){issueAjaxRequest(e,t,n,r,o)}))}maybeCall(a);return l}triggerEvent(m,"htmx:abort")}const b=new XMLHttpRequest;g.xhr=b;g.abortable=x;const endRequestLock=function(){g.xhr=null;g.abortable=false;if(g.queuedRequests!=null&&g.queuedRequests.length>0){const e=g.queuedRequests.shift();e()}};const y=getClosestAttributeValue(n,"hx-prompt");if(y){var v=prompt(y);if(v===null||!triggerEvent(n,"htmx:prompt",{prompt:v,target:f})){maybeCall(a);endRequestLock();return l}}if(h&&!s&&!confirm(h)){maybeCall(a);endRequestLock();return l}let S=getHeaders(n,f,v);e==="get"||usesFormData(n)||(S["Content-Type"]="application/x-www-form-urlencoded");o.headers&&(S=mergeObjects(S,o.headers));const w=getInputValues(n,e);let C=w.errors;const T=w.formData;o.values&&overrideFormData(T,formDataFromObject(o.values));const A=formDataFromObject(getExpressionVars(n));const H=overrideFormData(T,A);let R=filterValues(H,n);htmx.config.getCacheBusterParam&&e==="get"&&R.set("org.htmx.cache-buster",getRawAttribute(f,"id")||"true");t!=null&&t!==""||(t=getDocument().location.href)
+/**
+ * @type {Object}
+ * @property {boolean} [credentials]
+ * @property {number} [timeout]
+ * @property {boolean} [noHeaders]
+ */;const O=getValuesForElement(n,"hx-request");const I=getInternalData(n).boosted;let D=htmx.config.methodsThatUseUrlParams.indexOf(e)>=0;
+/** @type HtmxRequestConfig */const N={boosted:I,useUrlParams:D,formData:R,parameters:formDataProxy(R),unfilteredFormData:H,unfilteredParameters:formDataProxy(H),headers:S,target:f,verb:e,errors:C,withCredentials:o.credentials||O.credentials||htmx.config.withCredentials,timeout:o.timeout||O.timeout||htmx.config.timeout,path:t,triggeringEvent:r};if(!triggerEvent(n,"htmx:configRequest",N)){maybeCall(a);endRequestLock();return l}t=N.path;e=N.verb;S=N.headers;R=formDataFromObject(N.parameters);C=N.errors;D=N.useUrlParams;if(C&&C.length>0){triggerEvent(n,"htmx:validation:halted",N);maybeCall(a);endRequestLock();return l}const L=t.split("#");const q=L[0];const P=L[1];let F=t;if(D){F=q;const e=!R.keys().next().done;if(e){F.indexOf("?")<0?F+="?":F+="&";F+=urlEncode(R);P&&(F+="#"+P)}}if(!verifyPath(n,F,N)){triggerErrorEvent(n,"htmx:invalidPath",N);maybeCall(i);return l}b.open(e.toUpperCase(),F,true);b.overrideMimeType("text/html");b.withCredentials=N.withCredentials;b.timeout=N.timeout;if(O.noHeaders);else for(const e in S)if(S.hasOwnProperty(e)){const t=S[e];safelySetHeaderValue(b,e,t)}
+/** @type {HtmxResponseInfo} */const k={xhr:b,target:f,requestConfig:N,etc:o,boosted:I,select:u,pathInfo:{requestPath:t,finalRequestPath:F,responsePath:null,anchor:P}};b.onload=function(){try{const e=hierarchyForElt(n);k.pathInfo.responsePath=getPathFromResponse(b);c(n,k);removeRequestIndicators(M,V);triggerEvent(n,"htmx:afterRequest",k);triggerEvent(n,"htmx:afterOnLoad",k);if(!bodyContains(n)){let t=null;while(e.length>0&&t==null){const n=e.shift();bodyContains(n)&&(t=n)}if(t){triggerEvent(t,"htmx:afterRequest",k);triggerEvent(t,"htmx:afterOnLoad",k)}}maybeCall(a);endRequestLock()}catch(e){triggerErrorEvent(n,"htmx:onLoadError",mergeObjects({error:e},k));throw e}};b.onerror=function(){removeRequestIndicators(M,V);triggerErrorEvent(n,"htmx:afterRequest",k);triggerErrorEvent(n,"htmx:sendError",k);maybeCall(i);endRequestLock()};b.onabort=function(){removeRequestIndicators(M,V);triggerErrorEvent(n,"htmx:afterRequest",k);triggerErrorEvent(n,"htmx:sendAbort",k);maybeCall(i);endRequestLock()};b.ontimeout=function(){removeRequestIndicators(M,V);triggerErrorEvent(n,"htmx:afterRequest",k);triggerErrorEvent(n,"htmx:timeout",k);maybeCall(i);endRequestLock()};if(!triggerEvent(n,"htmx:beforeRequest",k)){maybeCall(a);endRequestLock();return l}var M=addRequestIndicatorClasses(n);var V=disableElements(n);forEach(["loadstart","loadend","progress","abort"],(function(e){forEach([b,b.upload],(function(t){t.addEventListener(e,(function(t){triggerEvent(n,"htmx:xhr:"+e,{lengthComputable:t.lengthComputable,loaded:t.loaded,total:t.total})}))}))}));triggerEvent(n,"htmx:beforeSend",k);const B=D?null:encodeParamsForBody(b,n,R);b.send(B);return l}
+/**
+ * @typedef {Object} HtmxHistoryUpdate
+ * @property {string|null} [type]
+ * @property {string|null} [path]
+ */
+/**
+ * @param {Element} elt
+ * @param {HtmxResponseInfo} responseInfo
+ * @return {HtmxHistoryUpdate}
+ */function determineHistoryUpdates(e,t){const n=t.xhr;let r=null;let o=null;if(hasHeader(n,/HX-Push:/i)){r=n.getResponseHeader("HX-Push");o="push"}else if(hasHeader(n,/HX-Push-Url:/i)){r=n.getResponseHeader("HX-Push-Url");o="push"}else if(hasHeader(n,/HX-Replace-Url:/i)){r=n.getResponseHeader("HX-Replace-Url");o="replace"}if(r)return r==="false"?{}:{type:o,path:r};const s=t.pathInfo.finalRequestPath;const a=t.pathInfo.responsePath;const i=getClosestAttributeValue(e,"hx-push-url");const l=getClosestAttributeValue(e,"hx-replace-url");const c=getInternalData(e).boosted;let u=null;let f=null;if(i){u="push";f=i}else if(l){u="replace";f=l}else if(c){u="push";f=a||s}if(f){if(f==="false")return{};f==="true"&&(f=a||s);t.pathInfo.anchor&&f.indexOf("#")===-1&&(f=f+"#"+t.pathInfo.anchor);return{type:u,path:f}}return{}}
+/**
+ * @param {HtmxResponseHandlingConfig} responseHandlingConfig
+ * @param {number} status
+ * @return {boolean}
+ */function codeMatches(e,t){var n=new RegExp(e.code);return n.test(t.toString(10))}
+/**
+ * @param {XMLHttpRequest} xhr
+ * @return {HtmxResponseHandlingConfig}
+ */function resolveResponseHandling(e){for(var t=0;t0?getWindow().setTimeout(doSwap,E.swapDelay):doSwap()}f&&triggerErrorEvent(e,"htmx:responseError",mergeObjects({error:"Response Status Error Code "+n.status+" from "+t.pathInfo.requestPath},t))}}
+/** @type {Object} */const extensions={};
+/**
+ * extensionBase defines the default functions for all extensions.
+ * @returns {HtmxExtension}
+ */function extensionBase(){return{init:function(e){return null},getSelectors:function(){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,n){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,n,r){return false},encodeParameters:function(e,t,n){return null}}}
+/**
+ * defineExtension initializes the extension and adds it to the htmx registry
+ *
+ * @see https://htmx.org/api/#defineExtension
+ *
+ * @param {string} name the extension name
+ * @param {HtmxExtension} extension the extension definition
+ */function defineExtension(e,t){t.init&&t.init(internalAPI);extensions[e]=mergeObjects(extensionBase(),t)}
+/**
+ * removeExtension removes an extension from the htmx registry
+ *
+ * @see https://htmx.org/api/#removeExtension
+ *
+ * @param {string} name
+ */function removeExtension(e){delete extensions[e]}
+/**
+ * getExtensions searches up the DOM tree to return all extensions that can be applied to a given element
+ *
+ * @param {Element} elt
+ * @param {HtmxExtension[]=} extensionsToReturn
+ * @param {string[]=} extensionsToIgnore
+ * @returns {HtmxExtension[]}
+ */function getExtensions(e,t,n){t==void 0&&(t=[]);if(e==void 0)return t;n==void 0&&(n=[]);const r=getAttributeValue(e,"hx-ext");r&&forEach(r.split(","),(function(e){e=e.replace(/ /g,"");if(e.slice(0,7)!="ignore:"){if(n.indexOf(e)<0){const n=extensions[e];n&&t.indexOf(n)<0&&t.push(n)}}else n.push(e.slice(7))}));return getExtensions(asElement(parentElt(e)),t,n)}var isReady=false;getDocument().addEventListener("DOMContentLoaded",(function(){isReady=true}));function ready(e){isReady||getDocument().readyState==="complete"?e():getDocument().addEventListener("DOMContentLoaded",e)}function insertIndicatorStyles(){if(htmx.config.includeIndicatorStyles!==false){const e=htmx.config.inlineStyleNonce?` nonce="${htmx.config.inlineStyleNonce}"`:"";getDocument().head.insertAdjacentHTML("beforeend","")}}function getMetaConfig(){
+/** @type HTMLMetaElement */
+const e=getDocument().querySelector('meta[name="htmx-config"]');return e?parseJSON(e.content):null}function mergeMetaConfig(){const e=getMetaConfig();e&&(htmx.config=mergeObjects(htmx.config,e))}ready((function(){mergeMetaConfig();insertIndicatorStyles();let e=getDocument().body;processNode(e);const t=getDocument().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",(function(e){const t=e.target;const n=getInternalData(t);n&&n.xhr&&n.xhr.abort()}));
+/** @type {(ev: PopStateEvent) => any} */const n=window.onpopstate?window.onpopstate.bind(window):null;
+/** @type {(ev: PopStateEvent) => any} */window.onpopstate=function(e){if(e.state&&e.state.htmx){restoreHistory();forEach(t,(function(e){triggerEvent(e,"htmx:restored",{document:getDocument(),triggerEvent:triggerEvent})}))}else n&&n(e)};getWindow().setTimeout((function(){triggerEvent(e,"htmx:load",{});e=null}),0)}));return htmx}();export{htmx as default};
+