phlex-reactive 0.11.4 → 0.11.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -989,6 +989,23 @@ function anyOfAllsMatches(groups, fieldValue) {
989
989
  group.every((term) => dnfTermMatches(term, fieldValue)))
990
990
  }
991
991
 
992
+ // Every field a DNF payload's groups reference (issue #209) — drives the
993
+ // "leave the target alone when NO referenced field is owned" skip, the
994
+ // single-field-target skip generalized. Returns null for a malformed payload
995
+ // (no groups, or no term names a field) so the caller warn-skips instead of
996
+ // toggling on garbage (default-deny, like every other malformed-wire arm).
997
+ function dnfGroupFields(groups) {
998
+ if (!Array.isArray(groups) || groups.length === 0) return null
999
+ const fields = new Set()
1000
+ for (const group of groups) {
1001
+ if (!Array.isArray(group)) continue
1002
+ for (const term of group) {
1003
+ if (term && typeof term === "object" && typeof term.field === "string") fields.add(term.field)
1004
+ }
1005
+ }
1006
+ return fields.size > 0 ? [...fields] : null
1007
+ }
1008
+
992
1009
  // Route a parsed data-reactive-show payload to the right evaluator. The 0.10
993
1010
  // wire is { any: [ [term,…], … ] } (DNF — groups are ARRAYS). For a stale tab
994
1011
  // still serving pre-0.10 HTML (deploy overlap), fall back to the 0.9.5 compound
@@ -1137,6 +1154,16 @@ export default class extends Controller {
1137
1154
  // missing-template warning latch.
1138
1155
  #boundSyncTags
1139
1156
  #tagsWarnedTemplate = false
1157
+ // Draft nested-attribute rows (issue #208): the strictly-monotonic index
1158
+ // counter (clock-seeded so it never collides with server-rendered 0..n
1159
+ // indexes) plus the once-only missing-list/template warning latch.
1160
+ #nestedIndex = 0
1161
+ #nestedWarned = false
1162
+ // JSON-mode nested rows (issue #208): the bound delegated input/change
1163
+ // handler and the bound morph re-seed, held so disconnect() removes exactly
1164
+ // them.
1165
+ #boundSyncNestedJson
1166
+ #boundSeedNestedJson
1140
1167
  // Connect-time compute seed (issue #199): the bound re-seed attached to
1141
1168
  // turbo:morph-element so an in-place morph re-runs the compute, held for teardown.
1142
1169
  #boundSeedCompute
@@ -1261,6 +1288,24 @@ export default class extends Controller {
1261
1288
  this.#syncTags()
1262
1289
  }
1263
1290
 
1291
+ // JSON-mode nested rows (issue #208) — ONLY when the root owns a list with
1292
+ // `as: :json`, so a form without one pays a single probe (the show/filter/
1293
+ // tags gate precedent). ONE delegated input + change listener re-serializes
1294
+ // the rows into the hidden field on every owned edit (nestedAdd/Remove call
1295
+ // the sync directly; this covers typing into a row's fields). The connect
1296
+ // seed writes the initial array (a plain replace re-connects), and
1297
+ // turbo:morph-element re-seeds after an in-place morph (which keeps the
1298
+ // element connected, fires no Stimulus lifecycle, and may have rewritten
1299
+ // the rows to server truth while the hidden field kept its pre-morph value).
1300
+ if (this.#nestedJsonEnabled()) {
1301
+ this.#boundSyncNestedJson = (event) => this.syncNestedJson(event)
1302
+ this.#boundSeedNestedJson = () => this.#syncAllNestedJson()
1303
+ this.element.addEventListener?.("input", this.#boundSyncNestedJson)
1304
+ this.element.addEventListener?.("change", this.#boundSyncNestedJson)
1305
+ this.element.addEventListener?.("turbo:morph-element", this.#boundSeedNestedJson)
1306
+ this.#syncAllNestedJson()
1307
+ }
1308
+
1264
1309
  // Connect-time compute seed (issue #199) — ONLY when the root carries a
1265
1310
  // reactive_compute binding that opts in (data-reactive-compute-seed). A
1266
1311
  // freshly-rendered compute root (a first paint, or a server validation-error
@@ -1313,6 +1358,7 @@ export default class extends Controller {
1313
1358
  this.#teardownShowSync()
1314
1359
  this.#teardownFilterSync()
1315
1360
  this.#teardownTagsSync()
1361
+ this.#teardownNestedJsonSync()
1316
1362
  this.#teardownComputeSeed()
1317
1363
  if (this.#boundProbeLazyDefer) {
1318
1364
  this.element.removeEventListener?.("turbo:morph-element", this.#boundProbeLazyDefer)
@@ -1793,6 +1839,174 @@ export default class extends Controller {
1793
1839
  this.#tagsWrite(field, next)
1794
1840
  }
1795
1841
 
1842
+ // Draft nested-attribute rows (issue #208) — the "new parent + child rows"
1843
+ // window. The rows are FORM state (the reactive_tags posture): no token, no
1844
+ // POST, ever — the surrounding REAL form submit carries Rails'
1845
+ // accepts_nested_attributes_for names and the server reconciles parent +
1846
+ // rows in ONE create. Add clones the association's server-owned
1847
+ // <template data-reactive-nested-template="assoc"> row, swaps every NEW_ROW
1848
+ // in the clone's name/id/for for a fresh unique index (each row posts as its
1849
+ // own `…_attributes[<index>][field]` group), appends it to the owned
1850
+ // [data-reactive-nested-list="assoc"] container, and focuses the new row's
1851
+ // first field. Several collections can share one root — everything is keyed
1852
+ // by the association name the trigger carries.
1853
+ nestedAdd(event) {
1854
+ event?.preventDefault?.()
1855
+ const trigger = event?.currentTarget ?? event?.target
1856
+ const assoc = trigger?.getAttribute?.("data-reactive-association-param")
1857
+ if (!assoc) return
1858
+ if (typeof this.element?.querySelectorAll !== "function") return
1859
+
1860
+ const owns = this.#ownershipFilter()
1861
+ const list = [...this.element.querySelectorAll(`[data-reactive-nested-list="${assoc}"]`)].find(owns)
1862
+ const template = [...this.element.querySelectorAll(`[data-reactive-nested-template="${assoc}"]`)].find(owns)
1863
+ const proto = template?.content?.firstElementChild
1864
+ if (!list || !proto) {
1865
+ this.#warnNestedOnce(assoc)
1866
+ return
1867
+ }
1868
+
1869
+ const row = proto.cloneNode(true)
1870
+ this.#renumberNestedRow(row, this.#nextNestedIndex())
1871
+ list.appendChild(row)
1872
+ const first = [...(row.querySelectorAll?.("input, select, textarea") ?? [])][0]
1873
+ first?.focus?.()
1874
+
1875
+ // JSON mode (issue #208): a freshly-added empty row must land in the hidden
1876
+ // field immediately, so the serialized array reflects the DOM even before
1877
+ // the first keystroke. A no-op when this list isn't `as: :json`.
1878
+ if (list.getAttribute?.("data-reactive-nested-json") === assoc) this.#syncNestedJson(assoc)
1879
+ }
1880
+
1881
+ // Remove the trigger's closest row wrapper. A DRAFT row (no [_destroy]
1882
+ // input) leaves the DOM — it was never persisted, so removing its fields IS
1883
+ // the removal. A PERSISTED row (an edit form rendered a hidden [_destroy]
1884
+ // input via nested_field_name) is marked "1" and hidden instead — Rails
1885
+ // destroys it on save. The mark dispatches a real bubbling `input` (the
1886
+ // set-value + dispatch contract, issue #183) so dirty tracking/compute see it.
1887
+ nestedRemove(event) {
1888
+ event?.preventDefault?.()
1889
+ const trigger = event?.currentTarget ?? event?.target
1890
+ const row = trigger?.closest?.("[data-reactive-nested-row]")
1891
+ if (!row) return
1892
+ // The closest() walk must not escape this root — a root can itself sit
1893
+ // inside ANOTHER collection's row (the issue #15 closest-form posture).
1894
+ if (row.closest?.('[data-controller~="reactive"]') !== this.element) return
1895
+
1896
+ const destroy = [...(row.querySelectorAll?.('input[name$="[_destroy]"]') ?? [])][0]
1897
+ if (destroy) {
1898
+ destroy.value = "1"
1899
+ if (typeof destroy.dispatchEvent === "function") {
1900
+ destroy.dispatchEvent(new Event("input", { bubbles: true }))
1901
+ }
1902
+ row.hidden = true
1903
+ } else {
1904
+ row.parentNode?.removeChild?.(row)
1905
+ }
1906
+
1907
+ // JSON mode (issue #208): the removed (or _destroy-hidden) row must leave
1908
+ // the serialized array too — an absent row IS the removal. Re-sync every
1909
+ // owned JSON-mode list; a form without one iterates an empty set and exits.
1910
+ this.#syncAllNestedJson()
1911
+ }
1912
+
1913
+ // JSON-mode nested rows (issue #208) — the delegated input/change handler.
1914
+ // An app whose controller parses a serialized JSON param instead of Rails'
1915
+ // accepts_nested_attributes_for opts a list into `as: :json`; the client
1916
+ // then mirrors that list's rows into ONE hidden field as a JSON array on
1917
+ // every owned edit. Public so Stimulus can bind it; a no-op unless the
1918
+ // edited field belongs to a JSON-mode list this root owns.
1919
+ syncNestedJson(event) {
1920
+ const target = event?.target
1921
+ if (!target || !this.#ownsField(target)) return
1922
+ // Re-serialize every JSON-mode list (an edit could touch any of them; the
1923
+ // per-list owned-row scan is cheap and keeps this handler association-free).
1924
+ this.#syncAllNestedJson()
1925
+ }
1926
+
1927
+ // Serialize every owned JSON-mode list into its hidden field. The connect
1928
+ // seed and the input/remove re-syncs funnel through here so one place owns
1929
+ // the "DOM rows → JSON field" projection.
1930
+ #syncAllNestedJson() {
1931
+ if (typeof this.element?.querySelectorAll !== "function") return
1932
+ const owns = this.#ownershipFilter()
1933
+ for (const list of [...this.element.querySelectorAll("[data-reactive-nested-json]")].filter(owns)) {
1934
+ this.#syncNestedJson(list.getAttribute("data-reactive-nested-json"))
1935
+ }
1936
+ }
1937
+
1938
+ // Project ONE JSON-mode list's surviving rows into its hidden field. Each
1939
+ // row becomes an object keyed by the trailing bracket segment of its inputs'
1940
+ // names (…[title] → "title"); a hidden/_destroy-marked row is skipped (an
1941
+ // absent row IS the removal — JSON carries no destroy marker). The write
1942
+ // uses the set-value + dispatch contract (issue #183) so dirty tracking,
1943
+ // reactive_show, and compute see the change — but only when the value
1944
+ // actually changed, so a connect seed on an already-correct field is silent.
1945
+ #syncNestedJson(assoc) {
1946
+ const owns = this.#ownershipFilter()
1947
+ const list = [...this.element.querySelectorAll(`[data-reactive-nested-list="${assoc}"]`)].find(owns)
1948
+ if (!list) return
1949
+ const field = this.#nestedJsonField(list)
1950
+ if (!field) return
1951
+
1952
+ const rows = []
1953
+ for (const row of [...(list.querySelectorAll?.("[data-reactive-nested-row]") ?? [])]) {
1954
+ if (!owns(row) || row.hidden) continue
1955
+ rows.push(this.#nestedRowObject(row))
1956
+ }
1957
+
1958
+ const next = JSON.stringify(rows)
1959
+ if (field.value === next) return
1960
+ field.value = next
1961
+ if (typeof field.dispatchEvent === "function") {
1962
+ field.dispatchEvent(new Event("input", { bubbles: true }))
1963
+ }
1964
+ }
1965
+
1966
+ // The hidden field a JSON-mode list mirrors into — resolved fresh (a morph
1967
+ // replaces nodes, never cache it) and OWNED by this root (#15). null when
1968
+ // the selector resolves nothing, so the caller no-ops (a half-built binding
1969
+ // must never break the page).
1970
+ #nestedJsonField(list) {
1971
+ const selector = list.getAttribute?.("data-reactive-nested-json-field")
1972
+ if (!selector) return null
1973
+ const owns = this.#ownershipFilter()
1974
+ return [...this.element.querySelectorAll(selector)].find(owns) ?? null
1975
+ }
1976
+
1977
+ // One row → { key: value } over its named form controls. The JSON key is the
1978
+ // trailing bracket segment of each control's name (order[todos_attributes]
1979
+ // [3][title] → "title"; a bare `title` → "title"), the "infer from input
1980
+ // names" contract. The [_destroy] control is dropped (JSON has no destroy
1981
+ // marker). Later inputs with the same key win (last-wins, the DOM order).
1982
+ #nestedRowObject(row) {
1983
+ const obj = {}
1984
+ for (const el of [...(row.querySelectorAll?.("input, select, textarea") ?? [])]) {
1985
+ const key = this.#nestedJsonKey(el.getAttribute?.("name"))
1986
+ if (key === null || key === "_destroy") continue
1987
+ obj[key] = this.#nestedFieldValue(el)
1988
+ }
1989
+ return obj
1990
+ }
1991
+
1992
+ // The trailing bracket segment of a field name (the inferred JSON key), or
1993
+ // the bare name when it carries no brackets. null for a nameless control
1994
+ // (a bare button, an unnamed helper input) — skipped by the caller.
1995
+ #nestedJsonKey(name) {
1996
+ if (!name) return null
1997
+ const match = name.match(/\[([^\][]+)\]$/)
1998
+ return match ? match[1] : name
1999
+ }
2000
+
2001
+ // A form control's submitted value: an unchecked checkbox contributes "" (it
2002
+ // wouldn't post at all), a checked one its value (default "on"); everything
2003
+ // else its .value. Keeps the JSON shape close to what a real form submit
2004
+ // would carry for the same control.
2005
+ #nestedFieldValue(el) {
2006
+ if (el.type === "checkbox") return el.checked ? (el.value || "on") : ""
2007
+ return el.value ?? ""
2008
+ }
2009
+
1796
2010
  // Parse a JSON string list from a root data attr; [] on absence/parse error so
1797
2011
  // a malformed binding degrades to "no fields" rather than throwing on input.
1798
2012
  #parseComputeList(attr) {
@@ -2810,7 +3024,7 @@ export default class extends Controller {
2810
3024
 
2811
3025
  // The cross-root pass (issue #164) shares the same owned-field memo, so a
2812
3026
  // field driving both an owned binding and an outside target reads once.
2813
- this.#syncShowTargets(owns, values, scope)
3027
+ this.#syncShowTargets(fieldValue)
2814
3028
  }
2815
3029
 
2816
3030
  // Toggle `hidden` (and, when the binding declares data-reactive-show-disable,
@@ -2839,12 +3053,20 @@ export default class extends Controller {
2839
3053
  // unrendered tab pane is normal); a malformed predicate warn-skips its one
2840
3054
  // target while siblings still apply. With no map declared this is one
2841
3055
  // getAttribute and out.
2842
- #syncShowTargets(owns, values, scope) {
3056
+ //
3057
+ // A "#id" KEY (issue #209) is a TARGET-KEYED entry instead: its value is the
3058
+ // same DNF payload data-reactive-show holds, folded with per-term owned-field
3059
+ // reads — the multi-field cross-root case. The "#" prefix routes unambiguously
3060
+ // (a field name may never start with "#"; the Ruby helper raises).
3061
+ #syncShowTargets(fieldValue) {
2843
3062
  const map = this.#parseShowTargets()
2844
3063
  for (const [name, targets] of Object.entries(map)) {
3064
+ if (name.startsWith("#")) {
3065
+ this.#applyConditionsTarget(name, targets, fieldValue)
3066
+ continue
3067
+ }
2845
3068
  if (!targets || typeof targets !== "object" || Array.isArray(targets)) continue
2846
- if (!values.has(name)) values.set(name, this.#showFieldValue(name, owns, scope))
2847
- const value = values.get(name)
3069
+ const value = fieldValue(name)
2848
3070
  if (value === null) continue // no owned field with that name — leave them be
2849
3071
  // Every target's terms share this one field, so a constant resolver folds
2850
3072
  // the group (issue #180): a target's value is a DNF GROUP (terms ANDed).
@@ -2874,6 +3096,28 @@ export default class extends Controller {
2874
3096
  }
2875
3097
  }
2876
3098
 
3099
+ // Apply ONE target-keyed conditions entry (issue #209): "#id" → the DNF
3100
+ // payload { any: [[term,…],…] }, folded by the SAME anyOfAllsMatches as an
3101
+ // in-root reactive_show — each term reads its OWN owned field, a missing
3102
+ // owned field reads as blank (fail-closed, the shared-fixture contract). A
3103
+ // target whose referenced fields are ALL unowned is left alone — the
3104
+ // single-field skip generalized (this root has nothing to evaluate with). A
3105
+ // malformed payload warn-skips its one target while siblings still apply;
3106
+ // the selector guard is the same id-only allowlist as every cross-root arm.
3107
+ #applyConditionsTarget(selector, payload, fieldValue) {
3108
+ if (!guardShowTargetSelector(selector)) return
3109
+ const groups = payload && typeof payload === "object" && !Array.isArray(payload) ? payload.any : null
3110
+ const fields = dnfGroupFields(groups)
3111
+ if (fields === null) {
3112
+ console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${selector} — skipped`)
3113
+ return
3114
+ }
3115
+ if (fields.every((name) => fieldValue(name) === null)) return // no owned field — leave it be
3116
+ const match = anyOfAllsMatches(groups, fieldValue)
3117
+ if (match === null) return // unreachable after dnfGroupFields, kept fail-closed
3118
+ for (const node of document.querySelectorAll(selector)) node.hidden = !match
3119
+ }
3120
+
2877
3121
  // The declared cross-root show-target map (issue #164): a JSON object of
2878
3122
  // { field: { "#id": predicate } } from data-reactive-show-targets (emitted
2879
3123
  // by reactive_show_targets on the root). Absent degrades to {}; malformed
@@ -3033,6 +3277,15 @@ export default class extends Controller {
3033
3277
  return !!this.element.getAttribute?.("data-reactive-tags-field")
3034
3278
  }
3035
3279
 
3280
+ // Whether this root owns at least one JSON-mode nested list (issue #208) —
3281
+ // the connect() gate. A cheap descendant probe: a form without one wires no
3282
+ // input/change listeners. Ownership is re-checked per sync, so a stray match
3283
+ // in a nested reactive root here is harmless (it just arms the listeners).
3284
+ #nestedJsonEnabled() {
3285
+ if (typeof this.element?.querySelector !== "function") return false
3286
+ return !!this.element.querySelector("[data-reactive-nested-json]")
3287
+ }
3288
+
3036
3289
  // The hidden input storing the comma-joined value, resolved fresh per use
3037
3290
  // (a morph replaces nodes — never cache it) and OWNED by this root (issue
3038
3291
  // #15). null when the selector resolves nothing — every caller then no-ops:
@@ -3180,6 +3433,39 @@ export default class extends Controller {
3180
3433
  if (this.#filterEnabled()) this.#syncFilter()
3181
3434
  }
3182
3435
 
3436
+ // A fresh index per nested-row add (issue #208) — strictly monotonic and
3437
+ // clock-seeded, so it can never collide with server-rendered integer indexes
3438
+ // (0..n) NOR with a rapid same-millisecond double add.
3439
+ #nextNestedIndex() {
3440
+ this.#nestedIndex = Math.max(this.#nestedIndex + 1, Date.now())
3441
+ return this.#nestedIndex
3442
+ }
3443
+
3444
+ // Swap every NEW_ROW in the clone's name/id/for for the fresh index, so the
3445
+ // row posts as its own `…_attributes[<index>][field]` group and labels keep
3446
+ // pointing at their (renumbered) inputs.
3447
+ #renumberNestedRow(row, index) {
3448
+ const nodes = [row, ...(row.querySelectorAll?.("*") ?? [])]
3449
+ for (const el of nodes) {
3450
+ for (const attr of ["name", "id", "for"]) {
3451
+ const value = el.getAttribute?.(attr)
3452
+ if (value && value.includes("NEW_ROW")) el.setAttribute?.(attr, value.replaceAll("NEW_ROW", String(index)))
3453
+ }
3454
+ }
3455
+ }
3456
+
3457
+ // A half-built nested-rows binding should be loud, but never per-click.
3458
+ #warnNestedOnce(assoc) {
3459
+ if (this.#nestedWarned) return
3460
+ console.warn(
3461
+ `[phlex-reactive] nested rows: no owned [data-reactive-nested-list="${assoc}"] container + ` +
3462
+ `<template data-reactive-nested-template="${assoc}"> pair found in this root — the add ` +
3463
+ "trigger did nothing. Render both inside the same reactive root (reactive_nested_list / " +
3464
+ "reactive_nested_template)."
3465
+ )
3466
+ this.#nestedWarned = true
3467
+ }
3468
+
3183
3469
  // Remove the tags morph listener on disconnect, so a stray morph event after
3184
3470
  // the element leaves the DOM never re-projects against a detached root.
3185
3471
  #teardownTagsSync() {
@@ -3188,6 +3474,21 @@ export default class extends Controller {
3188
3474
  this.#boundSyncTags = undefined
3189
3475
  }
3190
3476
 
3477
+ // Remove the JSON-mode nested-rows listeners on disconnect (issue #208), so a
3478
+ // stray input/change/morph after the element leaves the DOM never re-syncs
3479
+ // against a detached root.
3480
+ #teardownNestedJsonSync() {
3481
+ if (this.#boundSyncNestedJson) {
3482
+ this.element.removeEventListener?.("input", this.#boundSyncNestedJson)
3483
+ this.element.removeEventListener?.("change", this.#boundSyncNestedJson)
3484
+ this.#boundSyncNestedJson = undefined
3485
+ }
3486
+ if (this.#boundSeedNestedJson) {
3487
+ this.element.removeEventListener?.("turbo:morph-element", this.#boundSeedNestedJson)
3488
+ this.#boundSeedNestedJson = undefined
3489
+ }
3490
+ }
3491
+
3191
3492
  // Remove the compute seed morph listener on disconnect (issue #199), so a
3192
3493
  // stray turbo:morph-element after the element leaves the DOM never re-seeds
3193
3494
  // against a detached root.
@@ -1,4 +1,4 @@
1
- import{Controller as Xj}from"@hotwired/stimulus";import{confirmResolver as b}from"phlex/reactive/confirm";import{computeReducer as Zj}from"phlex/reactive/compute";import{confirmPredicate as $j}from"phlex/reactive/confirm_predicate";function Jj(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:visit"])return;j["reactive:visit"]=function(){let X=this.getAttribute("data-url");if(X)window.Turbo.visit(X,{action:"advance"})}}function Qj(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:token"])return;j["reactive:token"]=function(){let X=this.getAttribute("data-reactive-token-value"),Z=this.getAttribute("target");if(!X||!Z)return;let $=document.getElementById(Z);if($)$.setAttribute("data-reactive-token-value",X)}}function Gj(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:js"])return;j["reactive:js"]=function(){let X=e(this.getAttribute("data-reactive-ops"));if(!X.length)return;let Z=this.getAttribute("target"),$=Z?document.getElementById(Z):null;if(Z&&!$)return;jj(X,(J)=>wj(J,$))}}var V=new Map;function p(j,X){let Z=!V.has(j);if(V.set(j,X),Z)l()}function O(j){if(V.delete(j))o()}function fj(){V.clear(),T=!1}function pj(j){return V.get(j)?.via}var T=!1;function zj(){let j=window.Turbo?.StreamActions;if(!j||j["reactive:defer"])return;if(j["reactive:defer"]=function(){let X=this.getAttribute("target");if(!X)return;if(this.getAttribute("data-reactive-defer-via")==="stream"){qj(X,this);return}let Z=this.getAttribute("data-reactive-defer-token");if(!Z)return;P(X,Z)},!T&&typeof document<"u"&&document.addEventListener)T=!0,document.addEventListener("turbo:before-stream-render",Kj)}function Kj(j){let Z=j.target?.getAttribute?.("target");if(!Z)return;let $=Z.startsWith("reactive-defer-src-")?Z.slice(19):Z;if(V.get($)?.via==="stream")O($)}function P(j,X){let Z=document.getElementById(j);if(!Z){console.warn(`[phlex-reactive] reactive:defer target #${j} is not on the page — skipped`);return}m(j),g(Z);let $={via:"fetch",abort:new AbortController,timedOut:!1};p(j,$),Yj(j,$,X)}function qj(j,X){let Z=document.getElementById(j);if(!Z){console.warn(`[phlex-reactive] reactive:defer target #${j} is not on the page — skipped`);return}let $=X.getAttribute("data-reactive-defer-src");if(!$)return;if(!globalThis.customElements?.get?.("pgbus-stream-source")){let Q=X.getAttribute("data-reactive-defer-token");if(Q){P(j,Q);return}console.error("[phlex-reactive] reactive:defer via=stream but <pgbus-stream-source> is not registered "+"and no fallback token was provided — is the pgbus client loaded on this page?");return}m(j),g(Z);let J=document.createElement("pgbus-stream-source");J.id=u(j),J.setAttribute("src",$),J.setAttribute("since-id",X.getAttribute("data-reactive-defer-since-id")??"0"),J.setAttribute("hidden",""),document.body.appendChild(J),p(j,{via:"stream"})}function u(j){return`reactive-defer-src-${j}`}async function Yj(j,X,Z){let $=setTimeout(()=>{X.timedOut=!0,X.abort.abort()},Wj()),J;try{J=await fetch(Uj(),{method:"POST",headers:{Accept:"text/vnd.turbo-stream.html","Content-Type":"application/json","X-CSRF-Token":Hj()},body:JSON.stringify({token:Z}),credentials:"same-origin",signal:X.abort.signal})}catch(G){if(clearTimeout($),V.get(j)!==X)return;console.error("[phlex-reactive] deferred render failed",G),F(j,Z);return}if(V.get(j)!==X){clearTimeout($);return}if(J.status===204){clearTimeout($),h(j);return}if(!J.ok){clearTimeout($),console.error(`[phlex-reactive] deferred render failed: HTTP ${J.status}`),F(j,Z,J.status);return}let Q;try{Q=await J.text()}catch(G){if(clearTimeout($),V.get(j)!==X)return;console.error("[phlex-reactive] deferred render failed reading the body",G),F(j,Z);return}if(clearTimeout($),V.get(j)!==X)return;h(j),window.Turbo.renderStreamMessage(Q)}function m(j){let X=V.get(j);if(!X)return;if(O(j),X.via==="fetch")X.abort.abort();else document.getElementById(u(j))?.remove?.()}function g(j){j.setAttribute("data-reactive-defer-pending","true"),j.setAttribute("aria-busy","true")}function c(j){j.removeAttribute("data-reactive-defer-pending"),j.removeAttribute("aria-busy")}function h(j){O(j);let X=document.getElementById(j);if(!X)return;c(X),X.removeAttribute("data-reactive-error")}function F(j,X,Z){O(j);let $=document.getElementById(j);if(!$)return;c($),$.setAttribute("data-reactive-error","defer");let J=()=>{let Q=document.getElementById(j);if(!Q){console.warn("[phlex-reactive] defer retry() ignored — the target left the DOM");return}Q.removeAttribute("data-reactive-error"),P(j,X)};$.dispatchEvent(new CustomEvent("reactive:error",{bubbles:!0,composed:!0,detail:{kind:"defer",target:j,status:Z,retry:J}}))}function Uj(){return document.querySelector('meta[name="phlex-reactive-defer-path"]')?.content||"/reactive/defer"}function Hj(){return document.querySelector('meta[name="csrf-token"]')?.content??""}function Wj(){let j=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,X=Number(j);return Number.isFinite(X)&&X>0?X:30000}var I=!1;function _j(){if(I)return;if(typeof document>"u"||!document.addEventListener)return;I=!0,document.addEventListener("turbo:before-stream-render",Lj)}function Lj(j){let X=j.detail,Z=X?.render;if(typeof Z!=="function"||Z.__reactiveDismissWrapped){if(typeof requestAnimationFrame==="function")requestAnimationFrame(C);else setTimeout(C,0);return}let $=async(J)=>{await Z(J),C()};$.__reactiveDismissWrapped=!0,X.render=$}function C(){let j=document.querySelectorAll("[data-reactive-dismiss-after]");for(let X of j){if(X.hasAttribute("data-reactive-dismiss-scheduled"))continue;let Z=Number(X.getAttribute("data-reactive-dismiss-after"));if(!Number.isFinite(Z)||Z<=0)continue;X.setAttribute("data-reactive-dismiss-scheduled",""),setTimeout(()=>X.remove(),Z)}}function uj(){I=!1}var k=!1;function Nj(){if(k)return;if(typeof window>"u"||typeof document>"u")return;if(typeof window.addEventListener!=="function")return;k=!0;let j=()=>{let X=document.documentElement;if(typeof X?.toggleAttribute!=="function")return;X.toggleAttribute("data-reactive-offline",globalThis.navigator?.onLine===!1)};j(),window.addEventListener("online",j),window.addEventListener("offline",j)}function mj(){k=!1}var S="phlex-reactive:latency",x=!1;function Vj(j){if(typeof sessionStorage>"u")return;sessionStorage.setItem(S,String(j))}function Mj(){if(typeof sessionStorage>"u")return;sessionStorage.removeItem(S),x=!1}function Aj(){if(typeof window>"u"||typeof document>"u")return;if(document.querySelector?.('meta[name="phlex-reactive-env"]')?.content!=="development")return;window.PhlexReactive={enableLatencySim:Vj,disableLatencySim:Mj}}function gj(){x=!1}var d="data-reactive-active",M=0;function l(){if(M++,M===1)s("reactive:busy")}function o(){if(M===0)return;if(M--,M===0)s("reactive:idle")}function cj(){return M}function dj(){M=0,(typeof document<"u"?document.documentElement:null)?.removeAttribute?.(d)}function s(j){if(typeof document>"u")return;let X=document.documentElement;if(typeof X?.toggleAttribute==="function")X.toggleAttribute(d,M>0);if(typeof document.dispatchEvent==="function"&&typeof CustomEvent==="function")document.dispatchEvent(new CustomEvent(j,{detail:{count:M}}))}function y(){Jj(),Qj(),Gj(),zj(),_j(),Nj(),Aj()}function Bj(j){return j.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}if(typeof window<"u")if(window.Turbo)y();else document.addEventListener("turbo:load",y,{once:!0});var E=!1;function xj(){if(E)return;if(typeof document>"u")return;let j=document.querySelectorAll('[data-controller~="reactive"]');if(!j||j.length===0)return;console.warn("[phlex-reactive] found "+j.length+' element(s) with data-controller="reactive" '+"but the reactive controller never connected. It is loaded but not registered — "+'add `application.register("reactive", ReactiveController)` (importmap) or import it into app/javascript/controllers/ for lazyLoadControllersFrom apps. See the README.')}function lj(){E=!1}function oj(){E=!0}if(typeof window<"u"&&typeof document<"u"){let j=()=>setTimeout(xj,0);if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",j,{once:!0});else j()}var Oj=new Set(["href","src","srcdoc","action","formaction","xlink:href","style"]);function Pj(j){let X=String(j).toLowerCase();return X.startsWith("on")||Oj.has(X)}function Ej(j,X,Z){let[$,J,Q]=X;j.classList.add($,J),Z(),requestAnimationFrame(()=>{j.classList.remove(J),j.classList.add(Q)});let G=!1,K=()=>{if(G)return;G=!0,j.classList.remove($,Q)};j.addEventListener("animationend",K,{once:!0}),setTimeout(K,350)}var v=Object.freeze({show:(j,X)=>R(j,!1,X),hide:(j,X)=>R(j,!0,X),toggle:(j,X)=>R(j,!j.hidden,X),add_class:(j,X)=>j.classList.add(...X.classes??[]),remove_class:(j,X)=>j.classList.remove(...X.classes??[]),toggle_class:(j,X)=>(X.classes??[]).forEach((Z)=>j.classList.toggle(Z)),set_attr:(j,X)=>{if(D(X.name))j.setAttribute(X.name,X.value??"")},remove_attr:(j,X)=>{if(D(X.name))j.removeAttribute(X.name)},toggle_attr:(j,X)=>{if(!D(X.name))return;if(j.hasAttribute(X.name))j.removeAttribute(X.name);else j.setAttribute(X.name,"")},focus:(j)=>j.focus?.(),focus_first:(j)=>Sj(j)?.focus?.(),text:(j,X)=>{let Z=String(X.value??"");if(j.textContent!==Z)j.textContent=Z},dispatch:(j,X)=>{j.dispatchEvent(new CustomEvent(X.name,{bubbles:!0,composed:!0,detail:X.detail??{}}))}});function R(j,X,Z){if(Z?.transition)Ej(j,Z.transition,()=>j.hidden=X);else j.hidden=X}function D(j){if(!Pj(j))return!0;return console.warn(`[phlex-reactive] refused client attr op on ${JSON.stringify(j)} — skipped`),!1}var i=/^#[A-Za-z_][\w-]*$/;function Fj(j){if(typeof j==="string"&&i.test(j))return!0;return console.warn(`[phlex-reactive] refused cross-root mirror target ${JSON.stringify(j)} — skipped`),!1}function Cj(j,X){let Z=j.getAttribute("data-reactive-show-equals");if(Z!==null)return X===Z;let $=j.getAttribute("data-reactive-show-not");if($!==null)return X!==$;let J=j.getAttribute("data-reactive-show-in");if(J!==null){try{let Q=JSON.parse(J);if(Array.isArray(Q))return Q.includes(X)}catch{}return console.warn(`[phlex-reactive] malformed reactive_show in: list ${JSON.stringify(J)} — skipped`),null}for(let Q of n){let G=j.getAttribute(`data-reactive-show-${Q}`);if(G!==null)return a(Q,G,X)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}var n=["gte","gt","lte","lt"];function a(j,X,Z){let $=Number(X);if(Number.isNaN($))return console.warn(`[phlex-reactive] reactive_show ${j}: needs a numeric literal, got ${JSON.stringify(X)} — skipped`),null;let J=Z==null?"":String(Z).trim(),Q=J===""?NaN:Number(J);if(Number.isNaN(Q))return!1;switch(j){case"gte":return Q>=$;case"gt":return Q>$;case"lte":return Q<=$;case"lt":return Q<$;default:return null}}function r(j,X){if(!j||typeof j!=="object")return null;if(typeof j.equals==="string")return X===j.equals;if(typeof j.not==="string")return X!==j.not;if(Array.isArray(j.in))return j.in.includes(X);for(let Z of n)if(Z in j)return a(Z,j[Z],X);return null}var f="[data-reactive-show-field], [data-reactive-show]";function Rj(j){try{let X=JSON.parse(j);if(X&&typeof X==="object"&&!Array.isArray(X))return X}catch{}return console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(j)} — skipped`),null}function w(j,X){if(!j||typeof j!=="object"||typeof j.field!=="string")return!1;let Z=X(j.field)??"";return r(j,Z)===!0}function t(j,X){if(!Array.isArray(j)||j.length===0)return null;return j.some((Z)=>Array.isArray(Z)&&Z.length>0&&Z.every(($)=>w($,X)))}function Dj(j,X){if(!j||typeof j!=="object")return null;let Z=j.any;if(Array.isArray(Z)&&(Z.length===0||Array.isArray(Z[0])))return t(Z,X);return Tj(j,X)}function Tj(j,X){let Z=Array.isArray(j.all)?"all":Array.isArray(j.any)?"any":null;if(!Z)return null;let $=j[Z];if($.length===0)return null;let J=$.map((Q)=>w(Q,X));return Z==="all"?J.every(Boolean):J.some(Boolean)}function Ij(j){if(typeof j==="string"&&i.test(j))return!0;return console.warn(`[phlex-reactive] refused cross-root show target ${JSON.stringify(j)} — skipped`),!1}var kj='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function Sj(j){return j.querySelectorAll?.(kj)?.[0]??null}function e(j){if(Array.isArray(j))return j;if(typeof j!=="string")return[];try{let X=JSON.parse(j);return Array.isArray(X)?X:[]}catch{return[]}}function jj(j,X){for(let Z of j){if(!Array.isArray(Z))continue;let[$,J={}]=Z;if(!Object.hasOwn(v,$)){console.warn(`[phlex-reactive] unknown client op ${JSON.stringify($)} — skipped`);continue}for(let Q of X(J))v[$](Q,J)}}function wj(j,X){let Z=j.to;if(X){if(Z==="@root")return[X];if(typeof Z!=="string"||Z==="")return[];if(j.global)return[...document.querySelectorAll(Z)];return[...X.querySelectorAll(Z)]}if(typeof Z!=="string"||Z===""||Z==="@root")return[];return[...document.querySelectorAll(Z)]}class sj extends Xj{static values={token:String};#b;#V=new Map;#q=new Map;#Kj;#F;#h;#C=0;#Y=new Map;#R=new WeakMap;#M=new Map;#y=new WeakSet;#U;#H;#W;#j;#$;#_;#v=!1;#L;#A;connect(){if(E=!0,this.element.id==="")console.warn("[phlex-reactive] a reactive root has no id; its next-action token can't self-match "+"and may fall back to the first token in the response → a silent HTTP 403 on the NEXT action. "+"Put id: on the SAME element as reactive_attrs — use div(**reactive_root) (emits id + token together), "+"or div(id:, **reactive_attrs). The id: must NOT be on a child. See the README.");if(this.element.getAttribute?.("data-reactive-defer-token"))this.#f(),this.#A=()=>this.#f(),this.element.addEventListener?.("turbo:morph-element",this.#A);if(this.#qj()){if(this.#U=()=>this.#T(),this.element.addEventListener?.("turbo:morph-element",this.#U),this.#T(),this.element.getAttribute?.("data-reactive-warn-unsaved")==="true")this.#Tj()}if(this.#kj())this.#j=()=>this.#a(),this.element.addEventListener?.("input",this.#j),this.element.addEventListener?.("change",this.#j),this.element.addEventListener?.("turbo:morph-element",this.#j),this.#a();if(this.#P())this.#$=(j)=>{if(j?.type==="input"&&!this.#yj(j))return;this.#N()},this.element.addEventListener?.("input",this.#$),this.element.addEventListener?.("turbo:morph-element",this.#$),this.#N();if(this.#E())this.#_=()=>this.#S(),this.element.addEventListener?.("turbo:morph-element",this.#_),this.#S();if(this.#hj())this.#L=()=>this.recompute(),this.element.addEventListener?.("turbo:morph-element",this.#L),this.recompute()}#qj(){if((this.element.getAttribute?.("data-action")??"").includes("reactive#trackDirty"))return!0;let j=this.element.querySelectorAll?.('[data-action*="reactive#trackDirty"]')??[];for(let X of j)if(this.#X(X))return!0;return!1}disconnect(){if(this.#Mj(),this.#Bj(),this.#Ij(),this.#bj(),this.#vj(),this.#mj(),this.#gj(),this.#A)this.element.removeEventListener?.("turbo:morph-element",this.#A)}#f(){let j=this.element;if(!j?.id)return;let X=j.getAttribute?.("data-reactive-defer-token");if(!X)return;if(j.getAttribute?.("data-reactive-defer-pending")!=="true")return;P(j.id,X)}dispatch(j){let{action:X,params:Z,debounce:$,throttle:J,confirm:Q,confirmWhen:G,outside:K,window:z,optimistic:Y}=j.params;if(!X)return;let H=j.params.busy??this.#tj(j.params.loading);if(K&&this.element.contains(j.target))return;let L=j.currentTarget??j.target;if(!z&&!this.#lj(Y,L))j.preventDefault();let W=this.#s(Q,G);if(!W)return this.#g(L,X,Z,$,J,Y,H);Promise.resolve().then(()=>b(W)).catch(()=>!1).then((_)=>{if(_)this.#g(L,X,Z,$,J,Y,H)})}runOps(j){let{ops:X,confirm:Z,confirmWhen:$,outside:J,window:Q}=j.params;if(J&&this.element.contains(j.target))return;if(!Q)j.preventDefault();let G=this.#s(Z,$);if(!G)return this.#$j(this.#Zj(X));Promise.resolve().then(()=>b(G)).catch(()=>!1).then((K)=>{if(K)this.#$j(this.#Zj(X))})}trackDirty(){this.#T()}recompute(j){if(j&&this.#y.has(j))return;let X=this.#Uj(),Z=X.map(([q])=>q),$=this.element.getAttribute?.("data-reactive-scope")||null,J=(q)=>$&&!q.includes("[")?`${$}[${q}]`:q,Q=this.#Z(),G=new Map,K=(q)=>{if(G.has(q))return G.get(q);let U=null;for(let A of this.element.querySelectorAll(`[name="${J(q)}"]`))if(Q(A)){U=A;break}return G.set(q,U),U};for(let q of Z)this.#u(q,K(q)?.value??"");let z=this.element.getAttribute("data-reactive-compute-reducer-param"),Y=z?Zj(z):null;if(!Y){this.#m({},K);return}let H=this.#Yj("data-reactive-compute-outputs-param"),L={};for(let[q,U]of X){let A=K(q);if(U==="string")L[q]=A?.value??"";else{let N=Number(A?.value);L[q]=Number.isFinite(N)?N:0}}let W=Y(L,{changed:this.#Hj(j,Z,$)})||{},_=[];for(let q of H){if(!(q in W))continue;let U=K(q);if(!U)continue;if(String(W[q])===U.value)continue;U.value=W[q],_.push(U)}for(let q of Object.keys(W)){let U=W[q];if(U===void 0||U===null)continue;this.#u(q,U)}this.#m(W,K);for(let q of _){let U=new Event("input",{bubbles:!0});this.#y.add(U),q.dispatchEvent(U)}}listnavNext(j){this.#p(j,1)}listnavPrev(j){this.#p(j,-1)}listnavPick(j){let X=this.#B(j),Z=X.findIndex(($)=>$.hasAttribute("data-reactive-highlighted"));if(Z<0)return;j.preventDefault(),X[Z].click()}listnavClose(j){for(let X of this.#B(j))X.removeAttribute("data-reactive-highlighted")}#p(j,X){let Z=this.#B(j);if(!Z.length)return;j.preventDefault();let $=Z.findIndex((G)=>G.hasAttribute("data-reactive-highlighted")),J=$<0?X>0?0:Z.length-1:($+X+Z.length)%Z.length;for(let G of Z)G.removeAttribute("data-reactive-highlighted");let Q=Z[J];Q.setAttribute("data-reactive-highlighted","true"),Q.scrollIntoView?.({block:"nearest"})}#B(j){let Z=(j?.currentTarget??j?.target??this.element).getAttribute?.("data-reactive-listnav-option-param")??this.element.getAttribute("data-reactive-listnav-option-param");if(!Z)return[];let $=this.#Z();return Array.from(this.element.querySelectorAll(Z)).filter((J)=>!J.hidden&&$(J))}tagsAdd(j){if(!this.#E())return;if(j?.defaultPrevented)return;if(this.#B(j).some(($)=>$.hasAttribute?.("data-reactive-highlighted")))return;j?.preventDefault?.();let X=j?.currentTarget??j?.target;if(!X)return;if(!this.#e(String(X.value??"").split(",")))return;if(X.value="",this.#P())this.#N()}tagsPick(j){if(!this.#E())return;j?.preventDefault?.();let Z=(j?.currentTarget??j?.target)?.getAttribute?.("data-reactive-tag-param");if(!Z)return;if(!this.#e([Z]))return;let $=this.#fj();if(!$)return;$.value="",this.#N(),$.focus?.()}tagsRemove(j){if(!this.#E())return;j?.preventDefault?.();let Z=(j?.currentTarget??j?.target)?.getAttribute?.("data-reactive-tag-param");if(!Z)return;let $=this.#I();if(!$)return;let J=this.#k($),Q=J.filter((G)=>G.toLowerCase()!==Z.toLowerCase());if(Q.length===J.length)return;this.#jj($,Q)}#Yj(j){let X=this.element.getAttribute(j);if(!X)return[];try{let Z=JSON.parse(X);return Array.isArray(Z)?Z:[]}catch{return[]}}#Uj(){let j=this.element.getAttribute("data-reactive-compute-inputs-param");if(!j)return[];try{let X=JSON.parse(j);if(Array.isArray(X))return X.map((Z)=>[Z,"number"]);if(X&&typeof X==="object")return Object.entries(X);return[]}catch{return[]}}#Hj(j,X,Z){let $=j?.target;if(!$?.name||typeof $.closest!=="function")return null;let J=this.#Wj($.name,Z);if(!X.includes(J))return null;return this.#X($)?J:null}#Wj(j,X){if(!X)return j;let Z=`${X}[`;return j.startsWith(Z)&&j.endsWith("]")?j.slice(Z.length,-1):j}#u(j,X){let Z=String(X);for(let $ of this.#_j(j)){if($.textContent===Z)continue;$.textContent=Z}}#_j(j){let X=this.element.querySelectorAll(`[data-reactive-text="${j}"]`);return Array.from(X).filter((Z)=>this.#X(Z))}#m(j,X){let Z=this.#Lj();for(let[$,J]of Object.entries(Z)){let Q=$ in j?j[$]:X($)?.value;if(Q===void 0||Q===null)continue;let G=String(Q);for(let K of Array.isArray(J)?J:[J]){if(!Fj(K))continue;for(let z of document.querySelectorAll(K)){if(z.textContent===G)continue;z.textContent=G}}}}#Lj(){let j=this.element.getAttribute("data-reactive-compute-mirror-param");if(!j)return{};try{let X=JSON.parse(j);return X&&typeof X==="object"&&!Array.isArray(X)?X:{}}catch{return{}}}#g(j,X,Z,$,J,Q,G){if(this.#O("reactive:before-dispatch",{action:X,params:this.#Xj(Z),element:this.element},{cancelable:!0}).defaultPrevented)return;let z=Number($)||0;if(z>0)return this.#Vj(j,z,X,Z,Q,G);let Y=Number(J)||0;if(Y>0)return this.#Aj(j,Y,X,Z,Q,G);return this.#x(X,Z,Q,j,G)}#x(j,X,Z,$,J){let Q=this.#oj(Z,$),G=this.#sj(j,$,J),K=this.#c()?this.#Nj(Z,$):null;return this.queue=(this.queue??Promise.resolve()).then(()=>this.#Cj(j,X,Q,G,K)),this.queue}#Nj(j,X){if(!j?.hide)return null;let Z=this.#Gj(j,X);if(!Z.length)return null;return()=>{let $=Z.filter((J)=>J.isConnected&&!J.hidden);if(!$.length)return;console.warn("[phlex-reactive] optimistic: { hide: true } was undone by the reply's re-render — "+"the element is visible again. For an instant delete, return reply.remove so the server removes it; otherwise the hide only flashes.",$)}}#Vj(j,X,Z,$,J,Q){this.#D(j);let G=()=>{this.#D(j),this.#x(Z,$,J,j,Q)},K=setTimeout(G,X);j?.addEventListener?.("blur",G,{once:!0}),this.#V.set(j,{timer:K,flush:G})}#D(j){let X=this.#V.get(j);if(!X)return;clearTimeout(X.timer),j?.removeEventListener?.("blur",X.flush),this.#V.delete(j)}#Mj(){for(let j of[...this.#V.keys()])this.#D(j)}#Aj(j,X,Z,$,J,Q){let G=this.#q.get(j)??new Map;if(G.has(Z))return;let K=setTimeout(()=>{if(G.delete(Z),G.size===0)this.#q.delete(j)},X);return G.set(Z,K),this.#q.set(j,G),this.#x(Z,$,J,j,Q)}#Bj(){for(let j of this.#q.values())for(let X of j.values())clearTimeout(X);this.#q.clear()}#O(j,X,{cancelable:Z=!1}={}){let $=new CustomEvent(j,{bubbles:!0,composed:!0,cancelable:Z,detail:X});return(this.element.isConnected?this.element:document).dispatchEvent($),$}#J(j,X,Z,$){let J=()=>{if(!this.element.isConnected){console.warn("[phlex-reactive] retry() ignored — the reactive root left the DOM");return}return this.#x(j,X)};this.#O("reactive:error",{action:j,params:Z,...$,retry:J})}#Q(j){if(this.element?.isConnected===!1)return;this.element?.setAttribute?.("data-reactive-error",j)}#xj(){this.element?.removeAttribute?.("data-reactive-error")}#Oj(){let j=document.querySelector("[data-reactive-error-flash]");if(!j?.content)return;let X=j.getAttribute("data-reactive-error-flash")||"flash",Z=document.getElementById(X);if(!Z)return;Z.appendChild(j.content.cloneNode(!0))}#Pj(){if(typeof sessionStorage>"u")return Promise.resolve();let j=Number(sessionStorage.getItem(S));if(!Number.isFinite(j)||j<=0)return Promise.resolve();if(!x)x=!0,console.warn(`[phlex-reactive] latency simulator ACTIVE — every action is delayed by ${j}ms. Call PhlexReactive.disableLatencySim() (or clear sessionStorage) to turn it off.`);return new Promise((X)=>setTimeout(X,j))}#c(){return this.element?.getAttribute?.("data-reactive-debug")==="true"}#d(){return typeof performance<"u"&&typeof performance.now==="function"?performance.now():Date.now()}#Ej(j){if(!j)return[];let X=[],Z=/<turbo-stream\b([^>]*)>/g,$;while(($=Z.exec(j))!==null){let J=$[1],Q=J.match(/\baction="([^"]*)"/)?.[1]??"?",G=J.match(/\btarget="([^"]*)"/)?.[1];X.push(G?`${Q} → #${G}`:Q)}return X}#Fj(j){let{action:X,status:Z,ms:$}=j,Q=`reactive ${this.element?.id?`#${this.element.id} `:""}${X} → ${Z??"—"} (${Math.round($)}ms)`;if(console.groupCollapsed(Q),console.log(`params: [${j.paramNames.join(", ")}] + collected: [${j.fieldNames.join(", ")}]`),console.log(`encoding: ${j.encoding}`),j.streams.length)console.log(`streams: ${j.streams.join(" ")}`);console.log(`token: ${j.tokenRefreshed?"refreshed ✓":"unchanged"}`),console.groupEnd()}async#Cj(j,X,Z,$,J){let{fields:Q,files:G}=this.#i(),K=this.#Xj(X),z={...Q,...K},Y=this.#G,H=G.length>0,L=H?this.#cj(Y,j,z,G):JSON.stringify({token:Y,act:j,params:z}),W=this.#c()?{action:j,paramNames:Object.keys(K),fieldNames:Object.keys(Q),encoding:H?"multipart":"json",status:null,streams:[],tokenRefreshed:!1,started:this.#d()}:null;await this.#Pj();try{if(navigator.onLine===!1){this.#z(Z),this.#Q("offline"),this.#J(j,X,z,{kind:"offline"});return}let _;try{let N={Accept:"text/vnd.turbo-stream.html","X-CSRF-Token":this.#XX()};if(!H)N["Content-Type"]="application/json";let B=this.#ZX();if(B)N["X-Pgbus-Connection"]=B;_=await fetch(this.#ej(),{method:"POST",headers:N,body:L,credentials:"same-origin",signal:AbortSignal.timeout(this.#jX())})}catch(N){if(console.error("[phlex-reactive] action error",N),this.#z(Z),N?.name==="TimeoutError"||N?.name==="AbortError"){this.#Q("timeout"),this.#J(j,X,z,{kind:"timeout"});return}this.#Oj(),this.#Q("network"),this.#J(j,X,z,{kind:"network"});return}if(W)W.status=_.status;if(_.redirected){console.error("[phlex-reactive] action was redirected (auth/CSRF?) — no update applied"),this.#z(Z),this.#Q("redirected"),this.#J(j,X,z,{kind:"redirected",status:_.status});return}if(!_.ok){let N=await _.text();if(console.error(`[phlex-reactive] action failed: HTTP ${_.status}`,N),this.#z(Z),(_.headers.get("Content-Type")||"").includes("turbo-stream")){let B=this.#o(N);if(this.#G=B??this.#G,W)this.#l(W,N,B);window.Turbo.renderStreamMessage(N)}this.#Q("http"),this.#J(j,X,z,{kind:"http",status:_.status,body:N});return}let q=_.headers.get("Content-Type")||"";if(!q.includes("turbo-stream")){console.error(`[phlex-reactive] expected a turbo-stream, got "${q}" — no update applied`),this.#z(Z),this.#Q("content-type"),this.#J(j,X,z,{kind:"content-type",status:_.status});return}let U=await _.text(),A=this.#o(U);if(this.#G=A??this.#G,W)this.#l(W,U,A);if(window.Turbo.renderStreamMessage(U),J)queueMicrotask(J);this.#xj(),this.#O("reactive:applied",{action:j,params:z,html:U})}catch(_){console.error("[phlex-reactive] action error",_),this.#z(Z),this.#O("reactive:error",{action:j,params:z,kind:"apply"})}finally{if($?.(),W)this.#Fj({...W,ms:this.#d()-W.started})}}#l(j,X,Z){j.streams=this.#Ej(X),j.tokenRefreshed=Z!=null}get#G(){return this.#b??this.tokenValue}set#G(j){this.#b=j}#o(j){let X=this.element.id;if(!X)return j.match(/data-reactive-token-value="([^"]+)"/)?.[1];let{token:Z,self:$}=this.#Rj(X),J=j.match(Z);if(J)return J[1];let Q=j.match($);if(Q)return Q[1].match(/data-reactive-token-value="([^"]+)"/)?.[1];return}#Rj(j){let X=this.#h;if(X&&X.id===j)return X;let Z=Bj(j);return this.#h={id:j,token:new RegExp(`<turbo-stream\\b[^>]*\\baction="reactive:token"[^>]*\\btarget="${Z}"[^>]*\\bdata-reactive-token-value="([^"]+)"`),self:new RegExp(`<turbo-stream\\b[^>]*\\baction="(?:replace|update)"[^>]*\\btarget="${Z}"[^>]*>([\\s\\S]*?)</turbo-stream>`)}}#X(j){return j.closest('[data-controller~="reactive"]')===this.element}#Z(){if(this.element.querySelectorAll('[data-controller~="reactive"]').length===0)return()=>!0;return(X)=>this.#X(X)}#s(j,X){if(j)return j;if(!X)return null;let Z=X;if(typeof X==="string")try{Z=JSON.parse(X)}catch{return console.warn(`[phlex-reactive] malformed conditional confirm payload ${JSON.stringify(X)} — skipped`),null}if(!Z||typeof Z!=="object")return null;let{fields:$}=this.#i(),J=(G)=>$[G],Q;if(typeof Z.predicate==="string"){let G=$j(Z.predicate);if(!G)return console.warn(`[phlex-reactive] confirm predicate "${Z.predicate}" is not registered — proceeding without a dialog (register it with setConfirmPredicate)`),null;Q=!!G($)}else Q=t(Z.groups?.any,J)===!0;return Q?Z.message:null}#i(){let j={},X=[],Z=this.#Z();return this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach(($)=>{if(!Z($))return;if($.type==="file")for(let J of $.files??[])X.push({name:$.name,file:J,multiple:$.multiple});else if($.type==="checkbox")j[$.name]=$.checked;else if($.type==="radio"){if($.checked)j[$.name]=$.value}else j[$.name]=$.value}),this.element.querySelectorAll("[name]:is(lexxy-editor, trix-editor, [contenteditable=''], [contenteditable=true], [contenteditable=plaintext-only])").forEach(($)=>{if(!Z($))return;let J=$.getAttribute("name");if(!J)return;let Q=j[J];if(Q==null||Q==="")j[J]=$.value??$.textContent??$.innerHTML??""}),{fields:j,files:X}}#T(){if(typeof this.element?.querySelectorAll!=="function")return;let j=0;if(this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((X)=>{if(!this.#X(X))return;if(X.type==="file")return;if(this.#Dj(X))X.setAttribute("data-reactive-dirty","true"),j++;else X.removeAttribute("data-reactive-dirty")}),j>0)this.element.setAttribute("data-reactive-dirty",String(j));else this.element.removeAttribute("data-reactive-dirty")}#Dj(j){if(j.type==="checkbox"||j.type==="radio")return j.checked!==j.defaultChecked;if(j.tag==="select"||j.options)return Array.from(j.options??[]).some((X)=>X.selected!==X.defaultSelected);return j.value!==j.defaultValue}#n(){let j=this.element.getAttribute?.("data-reactive-dirty"),X=Number(j);return Number.isFinite(X)&&X>0?X:0}#Tj(){if(typeof window>"u"||typeof window.addEventListener!=="function")return;this.#H=(j)=>{if(this.#n()===0)return;return j.preventDefault(),j.returnValue="You have unsaved changes.",j.returnValue},this.#W=(j)=>{if(this.#n()===0)return;if(!(typeof window.confirm==="function"?window.confirm("You have unsaved changes. Leave anyway?"):!0))j.preventDefault?.()},window.addEventListener("beforeunload",this.#H),window.addEventListener("turbo:before-visit",this.#W)}#Ij(){if(this.#U)this.element.removeEventListener?.("turbo:morph-element",this.#U),this.#U=void 0;if(typeof window<"u"&&typeof window.removeEventListener==="function"){if(this.#H)window.removeEventListener("beforeunload",this.#H);if(this.#W)window.removeEventListener("turbo:before-visit",this.#W)}this.#H=void 0,this.#W=void 0}#kj(){if(this.element.getAttribute?.("data-reactive-show-targets"))return!0;let j=this.element.querySelectorAll?.(f)??[];for(let X of j)if(this.#X(X))return!0;return!1}#a(){if(typeof this.element?.querySelectorAll!=="function")return;let j=this.#Z(),X=this.element.getAttribute?.("data-reactive-scope")||null,Z=new Map,$=(J)=>{if(!Z.has(J))Z.set(J,this.#t(J,j,X));return Z.get(J)};for(let J of this.element.querySelectorAll(f)){if(!j(J))continue;let Q=J.getAttribute("data-reactive-show");if(Q!==null){let Y=Dj(Rj(Q),$);if(Y!==null)this.#r(J,Y,j,X);continue}let G=J.getAttribute("data-reactive-show-field");if(!G)continue;let K=$(G);if(K===null)continue;let z=Cj(J,K);if(z===null)continue;this.#r(J,z,j,X)}this.#Sj(j,Z,X)}#r(j,X,Z,$){if(j.hidden=!X,j.getAttribute("data-reactive-show-disable")!=="true")return;if(typeof j.querySelectorAll!=="function")return;for(let J of j.querySelectorAll("input[name], select[name], textarea[name]"))if(Z(J))J.disabled=!X;if(j.name&&Z(j))j.disabled=!X}#Sj(j,X,Z){let $=this.#wj();for(let[J,Q]of Object.entries($)){if(!Q||typeof Q!=="object"||Array.isArray(Q))continue;if(!X.has(J))X.set(J,this.#t(J,j,Z));let G=X.get(J);if(G===null)continue;let K=()=>G;for(let[z,Y]of Object.entries(Q)){if(!Ij(z))continue;let H;if(Array.isArray(Y)){if(Y.length===0){console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${z} — skipped`);continue}H=Y.every((L)=>w(L,K))}else{let L=r(Y,G);if(L===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${z} — skipped`);continue}H=L}for(let L of document.querySelectorAll(z))L.hidden=!H}}}#wj(){let j=this.element.getAttribute?.("data-reactive-show-targets");if(!j)return{};try{let X=JSON.parse(j);if(X&&typeof X==="object"&&!Array.isArray(X))return X}catch{}return console.warn("[phlex-reactive] malformed data-reactive-show-targets — ignored. "+"Did two reactive_show_targets calls collide on one root? Declare every field in ONE call: reactive_show_targets(mode: { ... }, kind: { ... })"),{}}#t(j,X,Z){let $=Z&&!j.includes("[")?`${Z}[${j}]`:j,J=!1,Q=null;for(let G of this.element.querySelectorAll(`[name="${$}"]`)){if(!X(G))continue;if(G.type==="checkbox")return G.checked?"true":"false";if(G.type==="radio"){if(G.checked)return G.value??"";J=!0;continue}Q??=G}if(Q)return Q.value??"";return J?"":null}#bj(){if(!this.#j)return;this.element.removeEventListener?.("input",this.#j),this.element.removeEventListener?.("change",this.#j),this.element.removeEventListener?.("turbo:morph-element",this.#j),this.#j=void 0}#P(){return!!(this.element.getAttribute?.("data-reactive-filter-input")&&this.element.getAttribute?.("data-reactive-filter-option"))}#hj(){return this.element.getAttribute?.("data-reactive-compute-seed")==="true"}#yj(j){let X=this.element.getAttribute("data-reactive-filter-input");return!!X&&typeof j.target?.matches==="function"&&j.target.matches(X)}#N(){if(typeof this.element?.querySelectorAll!=="function")return;let j=this.element.getAttribute("data-reactive-filter-input"),X=this.element.getAttribute("data-reactive-filter-option");if(!j||!X)return;let Z=this.#Z(),$=[...this.element.querySelectorAll(j)].find(Z);if(!$)return;let J=($.value??"").trim().toLowerCase(),Q=0;for(let z of this.element.querySelectorAll(X)){if(!Z(z))continue;let Y=(z.getAttribute("data-reactive-filter-text")??z.textContent??"").toLowerCase(),H=z.hasAttribute?.("data-reactive-tags-selected")||J!==""&&!Y.includes(J);if(z.hidden=H,H)z.removeAttribute("data-reactive-highlighted");else Q++}let G=this.element.getAttribute("data-reactive-filter-group");if(G)for(let z of this.element.querySelectorAll(G)){if(!Z(z))continue;let Y=[...z.querySelectorAll(X)].filter(Z);if(Y.length===0)continue;z.hidden=Y.every((H)=>H.hidden)}let K=this.element.getAttribute("data-reactive-filter-empty");if(K){for(let z of this.element.querySelectorAll(K))if(Z(z))z.hidden=Q>0}}#vj(){if(!this.#$)return;this.element.removeEventListener?.("input",this.#$),this.element.removeEventListener?.("turbo:morph-element",this.#$),this.#$=void 0}#E(){return!!this.element.getAttribute?.("data-reactive-tags-field")}#I(){if(typeof this.element?.querySelectorAll!=="function")return null;let j=this.element.getAttribute("data-reactive-tags-field");if(!j)return null;let X=this.#Z();return[...this.element.querySelectorAll(j)].find(X)??null}#k(j){let X=new Set,Z=[];for(let $ of String(j.value??"").split(",")){let J=$.trim();if(J===""||X.has(J.toLowerCase()))continue;X.add(J.toLowerCase()),Z.push(J)}return Z}#e(j){let X=this.#I();if(!X)return!1;let Z=this.#k(X),$=new Set(Z.map((Q)=>Q.toLowerCase())),J=!1;for(let Q of j){let G=String(Q??"").trim();if(G===""||$.has(G.toLowerCase()))continue;$.add(G.toLowerCase()),Z.push(G),J=!0}if(J)this.#jj(X,Z);return J}#jj(j,X){if(j.value=X.join(","),typeof j.dispatchEvent==="function")j.dispatchEvent(new Event("input",{bubbles:!0}));this.#S()}#fj(){if(typeof this.element?.querySelectorAll!=="function")return null;let j=this.element.getAttribute("data-reactive-filter-input");if(!j)return null;let X=this.#Z();return[...this.element.querySelectorAll(j)].find(X)??null}#S(){let j=this.#I();if(!j)return;let X=this.#k(j),Z=this.#Z();this.#pj(X,Z),this.#uj(X,Z)}#pj(j,X){let Z=[...this.element.querySelectorAll("[data-reactive-tags-list]")].find(X);if(!Z)return;let J=[...this.element.querySelectorAll("[data-reactive-tags-template]")].find(X)?.content?.firstElementChild;if(!J){if(!this.#v)console.warn("[phlex-reactive] reactive_tags: no chip <template data-reactive-tags-template> found in this root — "+"chips will not render (the hidden field still updates). Add a template with a [data-reactive-tag-text] node and a reactive_tags_remove button."),this.#v=!0;return}while(Z.firstChild)Z.removeChild(Z.firstChild);for(let Q of j){let G=J.cloneNode(!0);G.setAttribute?.("data-reactive-tag",Q);let K=G.matches?.("[data-reactive-tag-text]")?G:(G.querySelectorAll?.("[data-reactive-tag-text]")??[])[0];if(K)K.textContent=Q;let z=[...G.querySelectorAll?.('[data-action*="reactive#tagsRemove"]')??[]];if(G.matches?.('[data-action*="reactive#tagsRemove"]'))z.push(G);for(let Y of z)Y.setAttribute?.("data-reactive-tag-param",Q);Z.appendChild(G)}}#uj(j,X){let Z=new Set(j.map(($)=>$.toLowerCase()));for(let $ of this.element.querySelectorAll("[role=option]")){if(!X($))continue;let J=$.getAttribute?.("data-reactive-tag-param");if(!J)continue;if(Z.has(J.toLowerCase()))$.setAttribute("data-reactive-tags-selected","true"),$.hidden=!0,$.removeAttribute?.("data-reactive-highlighted");else if($.hasAttribute?.("data-reactive-tags-selected")){if($.removeAttribute("data-reactive-tags-selected"),!this.#P())$.hidden=!1}}if(this.#P())this.#N()}#mj(){if(!this.#_)return;this.element.removeEventListener?.("turbo:morph-element",this.#_),this.#_=void 0}#gj(){if(!this.#L)return;this.element.removeEventListener?.("turbo:morph-element",this.#L),this.#L=void 0}#cj(j,X,Z,$){let J=new FormData;J.append("token",j),J.append("act",X);for(let[G,K]of Object.entries(Z))this.#w(J,`params[${G}]`,K);let Q=this.#dj($);for(let{name:G,file:K,multiple:z}of $){let H=z||Q.has(G)?`params[${G}][]`:`params[${G}]`;J.append(H,K,K.name)}return J}#w(j,X,Z){if(Z==null)j.append(X,"");else if(Array.isArray(Z))Z.forEach(($,J)=>this.#w(j,`${X}[${J}]`,$));else if(typeof Z==="object")for(let[$,J]of Object.entries(Z))this.#w(j,`${X}[${$}]`,J);else j.append(X,String(Z))}#dj(j){let X=new Map;for(let{name:Z}of j)X.set(Z,(X.get(Z)??0)+1);return new Set([...X].filter(([,Z])=>Z>1).map(([Z])=>Z))}#Xj(j){if(!j)return{};try{return typeof j==="string"?JSON.parse(j):j}catch{return{}}}#Zj(j){return e(j)}#$j(j){jj(j,(X)=>this.#Jj(X))}#Jj(j){let X=j.to;if(X==="@root")return[this.element];if(typeof X!=="string"||X==="")return[];if(j.global)return[...document.querySelectorAll(X)];return[...this.element.querySelectorAll(X)].filter((Z)=>this.#X(Z))}#lj(j,X){if(j?.checked!=="keep")return!1;let Z=X?.type;return Z==="checkbox"||Z==="radio"}#oj(j,X){if(!j)return null;let Z=this.#Qj(j,X,!0);return Z.length?Z:null}#z(j){if(!j)return;if(!this.element.isConnected)return;for(let X of j)X()}#sj(j,X,Z){this.#nj(j,X);let $=Z?this.#Qj(Z,X,!1):[];l();let J=!1;return()=>{if(J)return;J=!0,this.#aj(j,X),o();for(let Q of $)Q()}}#Qj(j,X,Z){let $=[];for(let J of this.#Gj(j,X)){if(j.add_class){let Q=j.add_class.filter((G)=>!J.classList.contains(G));if(J.classList.add(...Q),Q.length)$.push(()=>J.classList.remove(...Q))}if(j.remove_class){let Q=j.remove_class.filter((G)=>J.classList.contains(G));if(J.classList.remove(...Q),Q.length)$.push(()=>J.classList.add(...Q))}if(j.toggle_class)j.toggle_class.forEach((Q)=>J.classList.toggle(Q)),$.push(()=>j.toggle_class.forEach((Q)=>J.classList.toggle(Q)));if(j.hide)J.hidden=!0,$.push(()=>J.hidden=!1);if(j.show)J.hidden=!1,$.push(()=>J.hidden=!0)}if(X&&(j.disable||j.text!=null))$.push(this.#ij(j,X));if(Z&&j.checked==="keep"&&X&&"checked"in X){let J=X.checked;$.push(()=>X.checked=!J)}return $}#Gj(j,X){if(j.to==null)return X?[X]:[];return this.#Jj({to:j.to})}#ij(j,X){let Z=this.#M.get(X);if(Z)Z.count++;else this.#M.set(X,{count:1,disabled:X.disabled,html:X.innerHTML,hadText:j.text!=null,swappedTo:j.text});if(j.disable)X.disabled=!0;if(j.text!=null)X.innerHTML=j.text;return()=>this.#rj(X,j)}#nj(j,X){if(this.#K(X,j,1),this.#K(this.element,j,1),this.#Y.set(j,(this.#Y.get(j)??0)+1),this.#C++===0)this.element.setAttribute("aria-busy","true");for(let Z of this.#zj(j))this.#K(Z,j,1)}#aj(j,X){this.#K(X,j,-1),this.#K(this.element,j,-1);let Z=(this.#Y.get(j)??1)-1;if(Z<=0)this.#Y.delete(j);else this.#Y.set(j,Z);if(--this.#C<=0)this.#C=0,this.element.removeAttribute("aria-busy");for(let $ of this.#zj(j))this.#K($,j,-1)}#K(j,X,Z){if(!j||typeof j.getAttribute!=="function")return;let $=this.#R.get(j)??new Map,J=($.get(X)??0)+Z;if(J<=0)$.delete(X);else $.set(X,J);if($.size===0){this.#R.delete(j),j.removeAttribute("data-reactive-busy");return}this.#R.set(j,$),j.setAttribute("data-reactive-busy",[...$.keys()].join(" "))}#zj(j){return[...this.element.querySelectorAll?.("[data-reactive-busy-on]")??[]].filter((Z)=>Z.getAttribute("data-reactive-busy-on")===j&&this.#X(Z))}#rj(j,X){let Z=this.#M.get(j);if(!Z)return;if(--Z.count>0)return;if(this.#M.delete(j),!j.isConnected)return;if(X.disable)j.disabled=Z.disabled;if(Z.hadText&&j.innerHTML===Z.swappedTo)j.innerHTML=Z.html}#tj(j){if(!j||typeof j!=="object")return null;let{class:X,...Z}=j;return X==null?j:{...Z,add_class:X}}#ej(){return this.#Kj??=document.querySelector('meta[name="phlex-reactive-action-path"]')?.content||"/reactive/actions"}#jX(){if(this.#F!=null)return this.#F;let j=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,X=Number(j);return this.#F=Number.isFinite(X)&&X>0?X:30000}#XX(){return document.querySelector('meta[name="csrf-token"]')?.content??""}#ZX(){return document.querySelector("pgbus-stream-source[connection-id]")?.getAttribute("connection-id")||document.querySelector('meta[name="pgbus-connection-id"]')?.content||null}}export{fj as resetReactiveDefers,dj as resetReactiveActivity,Jj as registerReactiveVisit,Qj as registerReactiveToken,Nj as registerReactiveOffline,Gj as registerReactiveJs,_j as registerReactiveDismiss,zj as registerReactiveDefer,y as registerReactiveActions,cj as reactiveActivityCount,pj as pendingDeferVia,o as exitReactiveActivity,Bj as escapeRegExp,l as enterReactiveActivity,Vj as enableLatencySim,Mj as disableLatencySim,sj as default,xj as checkReactiveRegistration,lj as __resetReactiveRegistrationForTest,mj as __resetReactiveOfflineForTest,gj as __resetReactiveLatencyForTest,uj as __resetReactiveDismissForTest,oj as __markReactiveConnectedForTest,S as LATENCY_KEY,d as ACTIVE_ATTR};
1
+ import{Controller as $X}from"@hotwired/stimulus";import{confirmResolver as h}from"phlex/reactive/confirm";import{computeReducer as QX}from"phlex/reactive/compute";import{confirmPredicate as jX}from"phlex/reactive/confirm_predicate";function GX(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:visit"])return;X["reactive:visit"]=function(){let Z=this.getAttribute("data-url");if(Z)window.Turbo.visit(Z,{action:"advance"})}}function zX(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:token"])return;X["reactive:token"]=function(){let Z=this.getAttribute("data-reactive-token-value"),$=this.getAttribute("target");if(!Z||!$)return;let Q=document.getElementById($);if(Q)Q.setAttribute("data-reactive-token-value",Z)}}function KX(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:js"])return;X["reactive:js"]=function(){let Z=XX(this.getAttribute("data-reactive-ops"));if(!Z.length)return;let $=this.getAttribute("target"),Q=$?document.getElementById($):null;if($&&!Q)return;ZX(Z,(j)=>bX(j,Q))}}var M=new Map;function u(X,Z){let $=!M.has(X);if(M.set(X,Z),$)i()}function x(X){if(M.delete(X))s()}function pX(){M.clear(),T=!1}function mX(X){return M.get(X)?.via}var T=!1;function qX(){let X=window.Turbo?.StreamActions;if(!X||X["reactive:defer"])return;if(X["reactive:defer"]=function(){let Z=this.getAttribute("target");if(!Z)return;if(this.getAttribute("data-reactive-defer-via")==="stream"){UX(Z,this);return}let $=this.getAttribute("data-reactive-defer-token");if(!$)return;P(Z,$)},!T&&typeof document<"u"&&document.addEventListener)T=!0,document.addEventListener("turbo:before-stream-render",YX)}function YX(X){let $=X.target?.getAttribute?.("target");if(!$)return;let Q=$.startsWith("reactive-defer-src-")?$.slice(19):$;if(M.get(Q)?.via==="stream")x(Q)}function P(X,Z){let $=document.getElementById(X);if(!$){console.warn(`[phlex-reactive] reactive:defer target #${X} is not on the page — skipped`);return}c(X),d($);let Q={via:"fetch",abort:new AbortController,timedOut:!1};u(X,Q),HX(X,Q,Z)}function UX(X,Z){let $=document.getElementById(X);if(!$){console.warn(`[phlex-reactive] reactive:defer target #${X} is not on the page — skipped`);return}let Q=Z.getAttribute("data-reactive-defer-src");if(!Q)return;if(!globalThis.customElements?.get?.("pgbus-stream-source")){let G=Z.getAttribute("data-reactive-defer-token");if(G){P(X,G);return}console.error("[phlex-reactive] reactive:defer via=stream but <pgbus-stream-source> is not registered "+"and no fallback token was provided — is the pgbus client loaded on this page?");return}c(X),d($);let j=document.createElement("pgbus-stream-source");j.id=g(X),j.setAttribute("src",Q),j.setAttribute("since-id",Z.getAttribute("data-reactive-defer-since-id")??"0"),j.setAttribute("hidden",""),document.body.appendChild(j),u(X,{via:"stream"})}function g(X){return`reactive-defer-src-${X}`}async function HX(X,Z,$){let Q=setTimeout(()=>{Z.timedOut=!0,Z.abort.abort()},JX()),j;try{j=await fetch(WX(),{method:"POST",headers:{Accept:"text/vnd.turbo-stream.html","Content-Type":"application/json","X-CSRF-Token":_X()},body:JSON.stringify({token:$}),credentials:"same-origin",signal:Z.abort.signal})}catch(z){if(clearTimeout(Q),M.get(X)!==Z)return;console.error("[phlex-reactive] deferred render failed",z),C(X,$);return}if(M.get(X)!==Z){clearTimeout(Q);return}if(j.status===204){clearTimeout(Q),v(X);return}if(!j.ok){clearTimeout(Q),console.error(`[phlex-reactive] deferred render failed: HTTP ${j.status}`),C(X,$,j.status);return}let G;try{G=await j.text()}catch(z){if(clearTimeout(Q),M.get(X)!==Z)return;console.error("[phlex-reactive] deferred render failed reading the body",z),C(X,$);return}if(clearTimeout(Q),M.get(X)!==Z)return;v(X),window.Turbo.renderStreamMessage(G)}function c(X){let Z=M.get(X);if(!Z)return;if(x(X),Z.via==="fetch")Z.abort.abort();else document.getElementById(g(X))?.remove?.()}function d(X){X.setAttribute("data-reactive-defer-pending","true"),X.setAttribute("aria-busy","true")}function l(X){X.removeAttribute("data-reactive-defer-pending"),X.removeAttribute("aria-busy")}function v(X){x(X);let Z=document.getElementById(X);if(!Z)return;l(Z),Z.removeAttribute("data-reactive-error")}function C(X,Z,$){x(X);let Q=document.getElementById(X);if(!Q)return;l(Q),Q.setAttribute("data-reactive-error","defer");let j=()=>{let G=document.getElementById(X);if(!G){console.warn("[phlex-reactive] defer retry() ignored — the target left the DOM");return}G.removeAttribute("data-reactive-error"),P(X,Z)};Q.dispatchEvent(new CustomEvent("reactive:error",{bubbles:!0,composed:!0,detail:{kind:"defer",target:X,status:$,retry:j}}))}function WX(){return document.querySelector('meta[name="phlex-reactive-defer-path"]')?.content||"/reactive/defer"}function _X(){return document.querySelector('meta[name="csrf-token"]')?.content??""}function JX(){let X=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,Z=Number(X);return Number.isFinite(Z)&&Z>0?Z:30000}var I=!1;function LX(){if(I)return;if(typeof document>"u"||!document.addEventListener)return;I=!0,document.addEventListener("turbo:before-stream-render",MX)}function MX(X){let Z=X.detail,$=Z?.render;if(typeof $!=="function"||$.__reactiveDismissWrapped){if(typeof requestAnimationFrame==="function")requestAnimationFrame(F);else setTimeout(F,0);return}let Q=async(j)=>{await $(j),F()};Q.__reactiveDismissWrapped=!0,Z.render=Q}function F(){let X=document.querySelectorAll("[data-reactive-dismiss-after]");for(let Z of X){if(Z.hasAttribute("data-reactive-dismiss-scheduled"))continue;let $=Number(Z.getAttribute("data-reactive-dismiss-after"));if(!Number.isFinite($)||$<=0)continue;Z.setAttribute("data-reactive-dismiss-scheduled",""),setTimeout(()=>Z.remove(),$)}}function uX(){I=!1}var k=!1;function AX(){if(k)return;if(typeof window>"u"||typeof document>"u")return;if(typeof window.addEventListener!=="function")return;k=!0;let X=()=>{let Z=document.documentElement;if(typeof Z?.toggleAttribute!=="function")return;Z.toggleAttribute("data-reactive-offline",globalThis.navigator?.onLine===!1)};X(),window.addEventListener("online",X),window.addEventListener("offline",X)}function gX(){k=!1}var w="phlex-reactive:latency",N=!1;function BX(X){if(typeof sessionStorage>"u")return;sessionStorage.setItem(w,String(X))}function VX(){if(typeof sessionStorage>"u")return;sessionStorage.removeItem(w),N=!1}function OX(){if(typeof window>"u"||typeof document>"u")return;if(document.querySelector?.('meta[name="phlex-reactive-env"]')?.content!=="development")return;window.PhlexReactive={enableLatencySim:BX,disableLatencySim:VX}}function cX(){N=!1}var o="data-reactive-active",B=0;function i(){if(B++,B===1)n("reactive:busy")}function s(){if(B===0)return;if(B--,B===0)n("reactive:idle")}function dX(){return B}function lX(){B=0,(typeof document<"u"?document.documentElement:null)?.removeAttribute?.(o)}function n(X){if(typeof document>"u")return;let Z=document.documentElement;if(typeof Z?.toggleAttribute==="function")Z.toggleAttribute(o,B>0);if(typeof document.dispatchEvent==="function"&&typeof CustomEvent==="function")document.dispatchEvent(new CustomEvent(X,{detail:{count:B}}))}function y(){GX(),zX(),KX(),qX(),LX(),AX(),OX()}function NX(X){return X.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}if(typeof window<"u")if(window.Turbo)y();else document.addEventListener("turbo:load",y,{once:!0});var E=!1;function xX(){if(E)return;if(typeof document>"u")return;let X=document.querySelectorAll('[data-controller~="reactive"]');if(!X||X.length===0)return;console.warn("[phlex-reactive] found "+X.length+' element(s) with data-controller="reactive" '+"but the reactive controller never connected. It is loaded but not registered — "+'add `application.register("reactive", ReactiveController)` (importmap) or import it into app/javascript/controllers/ for lazyLoadControllersFrom apps. See the README.')}function oX(){E=!1}function iX(){E=!0}if(typeof window<"u"&&typeof document<"u"){let X=()=>setTimeout(xX,0);if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",X,{once:!0});else X()}var PX=new Set(["href","src","srcdoc","action","formaction","xlink:href","style"]);function EX(X){let Z=String(X).toLowerCase();return Z.startsWith("on")||PX.has(Z)}function CX(X,Z,$){let[Q,j,G]=Z;X.classList.add(Q,j),$(),requestAnimationFrame(()=>{X.classList.remove(j),X.classList.add(G)});let z=!1,K=()=>{if(z)return;z=!0,X.classList.remove(Q,G)};X.addEventListener("animationend",K,{once:!0}),setTimeout(K,350)}var f=Object.freeze({show:(X,Z)=>R(X,!1,Z),hide:(X,Z)=>R(X,!0,Z),toggle:(X,Z)=>R(X,!X.hidden,Z),add_class:(X,Z)=>X.classList.add(...Z.classes??[]),remove_class:(X,Z)=>X.classList.remove(...Z.classes??[]),toggle_class:(X,Z)=>(Z.classes??[]).forEach(($)=>X.classList.toggle($)),set_attr:(X,Z)=>{if(D(Z.name))X.setAttribute(Z.name,Z.value??"")},remove_attr:(X,Z)=>{if(D(Z.name))X.removeAttribute(Z.name)},toggle_attr:(X,Z)=>{if(!D(Z.name))return;if(X.hasAttribute(Z.name))X.removeAttribute(Z.name);else X.setAttribute(Z.name,"")},focus:(X)=>X.focus?.(),focus_first:(X)=>wX(X)?.focus?.(),text:(X,Z)=>{let $=String(Z.value??"");if(X.textContent!==$)X.textContent=$},dispatch:(X,Z)=>{X.dispatchEvent(new CustomEvent(Z.name,{bubbles:!0,composed:!0,detail:Z.detail??{}}))}});function R(X,Z,$){if($?.transition)CX(X,$.transition,()=>X.hidden=Z);else X.hidden=Z}function D(X){if(!EX(X))return!0;return console.warn(`[phlex-reactive] refused client attr op on ${JSON.stringify(X)} — skipped`),!1}var a=/^#[A-Za-z_][\w-]*$/;function FX(X){if(typeof X==="string"&&a.test(X))return!0;return console.warn(`[phlex-reactive] refused cross-root mirror target ${JSON.stringify(X)} — skipped`),!1}function RX(X,Z){let $=X.getAttribute("data-reactive-show-equals");if($!==null)return Z===$;let Q=X.getAttribute("data-reactive-show-not");if(Q!==null)return Z!==Q;let j=X.getAttribute("data-reactive-show-in");if(j!==null){try{let G=JSON.parse(j);if(Array.isArray(G))return G.includes(Z)}catch{}return console.warn(`[phlex-reactive] malformed reactive_show in: list ${JSON.stringify(j)} — skipped`),null}for(let G of r){let z=X.getAttribute(`data-reactive-show-${G}`);if(z!==null)return t(G,z,Z)}return console.warn("[phlex-reactive] a reactive_show binding declares no predicate — skipped"),null}var r=["gte","gt","lte","lt"];function t(X,Z,$){let Q=Number(Z);if(Number.isNaN(Q))return console.warn(`[phlex-reactive] reactive_show ${X}: needs a numeric literal, got ${JSON.stringify(Z)} — skipped`),null;let j=$==null?"":String($).trim(),G=j===""?NaN:Number(j);if(Number.isNaN(G))return!1;switch(X){case"gte":return G>=Q;case"gt":return G>Q;case"lte":return G<=Q;case"lt":return G<Q;default:return null}}function e(X,Z){if(!X||typeof X!=="object")return null;if(typeof X.equals==="string")return Z===X.equals;if(typeof X.not==="string")return Z!==X.not;if(Array.isArray(X.in))return X.in.includes(Z);for(let $ of r)if($ in X)return t($,X[$],Z);return null}var p="[data-reactive-show-field], [data-reactive-show]";function DX(X){try{let Z=JSON.parse(X);if(Z&&typeof Z==="object"&&!Array.isArray(Z))return Z}catch{}return console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(X)} — skipped`),null}function b(X,Z){if(!X||typeof X!=="object"||typeof X.field!=="string")return!1;let $=Z(X.field)??"";return e(X,$)===!0}function S(X,Z){if(!Array.isArray(X)||X.length===0)return null;return X.some(($)=>Array.isArray($)&&$.length>0&&$.every((Q)=>b(Q,Z)))}function TX(X){if(!Array.isArray(X)||X.length===0)return null;let Z=new Set;for(let $ of X){if(!Array.isArray($))continue;for(let Q of $)if(Q&&typeof Q==="object"&&typeof Q.field==="string")Z.add(Q.field)}return Z.size>0?[...Z]:null}function IX(X,Z){if(!X||typeof X!=="object")return null;let $=X.any;if(Array.isArray($)&&($.length===0||Array.isArray($[0])))return S($,Z);return kX(X,Z)}function kX(X,Z){let $=Array.isArray(X.all)?"all":Array.isArray(X.any)?"any":null;if(!$)return null;let Q=X[$];if(Q.length===0)return null;let j=Q.map((G)=>b(G,Z));return $==="all"?j.every(Boolean):j.some(Boolean)}function m(X){if(typeof X==="string"&&a.test(X))return!0;return console.warn(`[phlex-reactive] refused cross-root show target ${JSON.stringify(X)} — skipped`),!1}var SX='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function wX(X){return X.querySelectorAll?.(SX)?.[0]??null}function XX(X){if(Array.isArray(X))return X;if(typeof X!=="string")return[];try{let Z=JSON.parse(X);return Array.isArray(Z)?Z:[]}catch{return[]}}function ZX(X,Z){for(let $ of X){if(!Array.isArray($))continue;let[Q,j={}]=$;if(!Object.hasOwn(f,Q)){console.warn(`[phlex-reactive] unknown client op ${JSON.stringify(Q)} — skipped`);continue}for(let G of Z(j))f[Q](G,j)}}function bX(X,Z){let $=X.to;if(Z){if($==="@root")return[Z];if(typeof $!=="string"||$==="")return[];if(X.global)return[...document.querySelectorAll($)];return[...Z.querySelectorAll($)]}if(typeof $!=="string"||$===""||$==="@root")return[];return[...document.querySelectorAll($)]}class sX extends $X{static values={token:String};#f;#V=new Map;#U=new Map;#_X;#D;#p;#T=0;#H=new Map;#I=new WeakMap;#O=new Map;#m=new WeakSet;#W;#_;#J;#Z;#Q;#L;#u=!1;#k=0;#g=!1;#j;#M;#A;#N;connect(){if(E=!0,this.element.id==="")console.warn("[phlex-reactive] a reactive root has no id; its next-action token can't self-match "+"and may fall back to the first token in the response → a silent HTTP 403 on the NEXT action. "+"Put id: on the SAME element as reactive_attrs — use div(**reactive_root) (emits id + token together), "+"or div(id:, **reactive_attrs). The id: must NOT be on a child. See the README.");if(this.element.getAttribute?.("data-reactive-defer-token"))this.#c(),this.#N=()=>this.#c(),this.element.addEventListener?.("turbo:morph-element",this.#N);if(this.#JX()){if(this.#W=()=>this.#w(),this.element.addEventListener?.("turbo:morph-element",this.#W),this.#w(),this.element.getAttribute?.("data-reactive-warn-unsaved")==="true")this.#fX()}if(this.#mX())this.#Z=()=>this.#$X(),this.element.addEventListener?.("input",this.#Z),this.element.addEventListener?.("change",this.#Z),this.element.addEventListener?.("turbo:morph-element",this.#Z),this.#$X();if(this.#F())this.#Q=(X)=>{if(X?.type==="input"&&!this.#iX(X))return;this.#B()},this.element.addEventListener?.("input",this.#Q),this.element.addEventListener?.("turbo:morph-element",this.#Q),this.#B();if(this.#R())this.#L=()=>this.#v(),this.element.addEventListener?.("turbo:morph-element",this.#L),this.#v();if(this.#nX())this.#j=(X)=>this.syncNestedJson(X),this.#M=()=>this.#P(),this.element.addEventListener?.("input",this.#j),this.element.addEventListener?.("change",this.#j),this.element.addEventListener?.("turbo:morph-element",this.#M),this.#P();if(this.#oX())this.#A=()=>this.recompute(),this.element.addEventListener?.("turbo:morph-element",this.#A),this.recompute()}#JX(){if((this.element.getAttribute?.("data-action")??"").includes("reactive#trackDirty"))return!0;let X=this.element.querySelectorAll?.('[data-action*="reactive#trackDirty"]')??[];for(let Z of X)if(this.#$(Z))return!0;return!1}disconnect(){if(this.#RX(),this.#TX(),this.#pX(),this.#lX(),this.#sX(),this.#$Z(),this.#QZ(),this.#jZ(),this.#N)this.element.removeEventListener?.("turbo:morph-element",this.#N)}#c(){let X=this.element;if(!X?.id)return;let Z=X.getAttribute?.("data-reactive-defer-token");if(!Z)return;if(X.getAttribute?.("data-reactive-defer-pending")!=="true")return;P(X.id,Z)}dispatch(X){let{action:Z,params:$,debounce:Q,throttle:j,confirm:G,confirmWhen:z,outside:K,window:q,optimistic:Y}=X.params;if(!Z)return;let L=X.params.busy??this.#JZ(X.params.loading);if(K&&this.element.contains(X.target))return;let A=X.currentTarget??X.target;if(!q&&!this.#KZ(Y,A))X.preventDefault();let W=this.#e(G,z);if(!W)return this.#s(A,Z,$,Q,j,Y,L);Promise.resolve().then(()=>h(W)).catch(()=>!1).then((_)=>{if(_)this.#s(A,Z,$,Q,j,Y,L)})}runOps(X){let{ops:Z,confirm:$,confirmWhen:Q,outside:j,window:G}=X.params;if(j&&this.element.contains(X.target))return;if(!G)X.preventDefault();let z=this.#e($,Q);if(!z)return this.#qX(this.#KX(Z));Promise.resolve().then(()=>h(z)).catch(()=>!1).then((K)=>{if(K)this.#qX(this.#KX(Z))})}trackDirty(){this.#w()}recompute(X){if(X&&this.#m.has(X))return;let Z=this.#OX(),$=Z.map(([U])=>U),Q=this.element.getAttribute?.("data-reactive-scope")||null,j=(U)=>Q&&!U.includes("[")?`${Q}[${U}]`:U,G=this.#X(),z=new Map,K=(U)=>{if(z.has(U))return z.get(U);let H=null;for(let V of this.element.querySelectorAll(`[name="${j(U)}"]`))if(G(V)){H=V;break}return z.set(U,H),H};for(let U of $)this.#o(U,K(U)?.value??"");let q=this.element.getAttribute("data-reactive-compute-reducer-param"),Y=q?QX(q):null;if(!Y){this.#i({},K);return}let L=this.#VX("data-reactive-compute-outputs-param"),A={};for(let[U,H]of Z){let V=K(U);if(H==="string")A[U]=V?.value??"";else{let J=Number(V?.value);A[U]=Number.isFinite(J)?J:0}}let W=Y(A,{changed:this.#NX(X,$,Q)})||{},_=[];for(let U of L){if(!(U in W))continue;let H=K(U);if(!H)continue;if(String(W[U])===H.value)continue;H.value=W[U],_.push(H)}for(let U of Object.keys(W)){let H=W[U];if(H===void 0||H===null)continue;this.#o(U,H)}this.#i(W,K);for(let U of _){let H=new Event("input",{bubbles:!0});this.#m.add(H),U.dispatchEvent(H)}}listnavNext(X){this.#d(X,1)}listnavPrev(X){this.#d(X,-1)}listnavPick(X){let Z=this.#x(X),$=Z.findIndex((Q)=>Q.hasAttribute("data-reactive-highlighted"));if($<0)return;X.preventDefault(),Z[$].click()}listnavClose(X){for(let Z of this.#x(X))Z.removeAttribute("data-reactive-highlighted")}#d(X,Z){let $=this.#x(X);if(!$.length)return;X.preventDefault();let Q=$.findIndex((z)=>z.hasAttribute("data-reactive-highlighted")),j=Q<0?Z>0?0:$.length-1:(Q+Z+$.length)%$.length;for(let z of $)z.removeAttribute("data-reactive-highlighted");let G=$[j];G.setAttribute("data-reactive-highlighted","true"),G.scrollIntoView?.({block:"nearest"})}#x(X){let $=(X?.currentTarget??X?.target??this.element).getAttribute?.("data-reactive-listnav-option-param")??this.element.getAttribute("data-reactive-listnav-option-param");if(!$)return[];let Q=this.#X();return Array.from(this.element.querySelectorAll($)).filter((j)=>!j.hidden&&Q(j))}tagsAdd(X){if(!this.#R())return;if(X?.defaultPrevented)return;if(this.#x(X).some((Q)=>Q.hasAttribute?.("data-reactive-highlighted")))return;X?.preventDefault?.();let Z=X?.currentTarget??X?.target;if(!Z)return;if(!this.#jX(String(Z.value??"").split(",")))return;if(Z.value="",this.#F())this.#B()}tagsPick(X){if(!this.#R())return;X?.preventDefault?.();let $=(X?.currentTarget??X?.target)?.getAttribute?.("data-reactive-tag-param");if(!$)return;if(!this.#jX([$]))return;let Q=this.#aX();if(!Q)return;Q.value="",this.#B(),Q.focus?.()}tagsRemove(X){if(!this.#R())return;X?.preventDefault?.();let $=(X?.currentTarget??X?.target)?.getAttribute?.("data-reactive-tag-param");if(!$)return;let Q=this.#b();if(!Q)return;let j=this.#h(Q),G=j.filter((z)=>z.toLowerCase()!==$.toLowerCase());if(G.length===j.length)return;this.#GX(Q,G)}nestedAdd(X){X?.preventDefault?.();let $=(X?.currentTarget??X?.target)?.getAttribute?.("data-reactive-association-param");if(!$)return;if(typeof this.element?.querySelectorAll!=="function")return;let Q=this.#X(),j=[...this.element.querySelectorAll(`[data-reactive-nested-list="${$}"]`)].find(Q),z=[...this.element.querySelectorAll(`[data-reactive-nested-template="${$}"]`)].find(Q)?.content?.firstElementChild;if(!j||!z){this.#ZZ($);return}let K=z.cloneNode(!0);if(this.#XZ(K,this.#eX()),j.appendChild(K),[...K.querySelectorAll?.("input, select, textarea")??[]][0]?.focus?.(),j.getAttribute?.("data-reactive-nested-json")===$)this.#l($)}nestedRemove(X){X?.preventDefault?.();let $=(X?.currentTarget??X?.target)?.closest?.("[data-reactive-nested-row]");if(!$)return;if($.closest?.('[data-controller~="reactive"]')!==this.element)return;let Q=[...$.querySelectorAll?.('input[name$="[_destroy]"]')??[]][0];if(Q){if(Q.value="1",typeof Q.dispatchEvent==="function")Q.dispatchEvent(new Event("input",{bubbles:!0}));$.hidden=!0}else $.parentNode?.removeChild?.($);this.#P()}syncNestedJson(X){let Z=X?.target;if(!Z||!this.#$(Z))return;this.#P()}#P(){if(typeof this.element?.querySelectorAll!=="function")return;let X=this.#X();for(let Z of[...this.element.querySelectorAll("[data-reactive-nested-json]")].filter(X))this.#l(Z.getAttribute("data-reactive-nested-json"))}#l(X){let Z=this.#X(),$=[...this.element.querySelectorAll(`[data-reactive-nested-list="${X}"]`)].find(Z);if(!$)return;let Q=this.#LX($);if(!Q)return;let j=[];for(let z of[...$.querySelectorAll?.("[data-reactive-nested-row]")??[]]){if(!Z(z)||z.hidden)continue;j.push(this.#MX(z))}let G=JSON.stringify(j);if(Q.value===G)return;if(Q.value=G,typeof Q.dispatchEvent==="function")Q.dispatchEvent(new Event("input",{bubbles:!0}))}#LX(X){let Z=X.getAttribute?.("data-reactive-nested-json-field");if(!Z)return null;let $=this.#X();return[...this.element.querySelectorAll(Z)].find($)??null}#MX(X){let Z={};for(let $ of[...X.querySelectorAll?.("input, select, textarea")??[]]){let Q=this.#AX($.getAttribute?.("name"));if(Q===null||Q==="_destroy")continue;Z[Q]=this.#BX($)}return Z}#AX(X){if(!X)return null;let Z=X.match(/\[([^\][]+)\]$/);return Z?Z[1]:X}#BX(X){if(X.type==="checkbox")return X.checked?X.value||"on":"";return X.value??""}#VX(X){let Z=this.element.getAttribute(X);if(!Z)return[];try{let $=JSON.parse(Z);return Array.isArray($)?$:[]}catch{return[]}}#OX(){let X=this.element.getAttribute("data-reactive-compute-inputs-param");if(!X)return[];try{let Z=JSON.parse(X);if(Array.isArray(Z))return Z.map(($)=>[$,"number"]);if(Z&&typeof Z==="object")return Object.entries(Z);return[]}catch{return[]}}#NX(X,Z,$){let Q=X?.target;if(!Q?.name||typeof Q.closest!=="function")return null;let j=this.#xX(Q.name,$);if(!Z.includes(j))return null;return this.#$(Q)?j:null}#xX(X,Z){if(!Z)return X;let $=`${Z}[`;return X.startsWith($)&&X.endsWith("]")?X.slice($.length,-1):X}#o(X,Z){let $=String(Z);for(let Q of this.#PX(X)){if(Q.textContent===$)continue;Q.textContent=$}}#PX(X){let Z=this.element.querySelectorAll(`[data-reactive-text="${X}"]`);return Array.from(Z).filter(($)=>this.#$($))}#i(X,Z){let $=this.#EX();for(let[Q,j]of Object.entries($)){let G=Q in X?X[Q]:Z(Q)?.value;if(G===void 0||G===null)continue;let z=String(G);for(let K of Array.isArray(j)?j:[j]){if(!FX(K))continue;for(let q of document.querySelectorAll(K)){if(q.textContent===z)continue;q.textContent=z}}}}#EX(){let X=this.element.getAttribute("data-reactive-compute-mirror-param");if(!X)return{};try{let Z=JSON.parse(X);return Z&&typeof Z==="object"&&!Array.isArray(Z)?Z:{}}catch{return{}}}#s(X,Z,$,Q,j,G,z){if(this.#C("reactive:before-dispatch",{action:Z,params:this.#zX($),element:this.element},{cancelable:!0}).defaultPrevented)return;let q=Number(Q)||0;if(q>0)return this.#FX(X,q,Z,$,G,z);let Y=Number(j)||0;if(Y>0)return this.#DX(X,Y,Z,$,G,z);return this.#E(Z,$,G,X,z)}#E(X,Z,$,Q,j){let G=this.#qZ($,Q),z=this.#YZ(X,Q,j),K=this.#n()?this.#CX($,Q):null;return this.queue=(this.queue??Promise.resolve()).then(()=>this.#hX(X,Z,G,z,K)),this.queue}#CX(X,Z){if(!X?.hide)return null;let $=this.#HX(X,Z);if(!$.length)return null;return()=>{let Q=$.filter((j)=>j.isConnected&&!j.hidden);if(!Q.length)return;console.warn("[phlex-reactive] optimistic: { hide: true } was undone by the reply's re-render — "+"the element is visible again. For an instant delete, return reply.remove so the server removes it; otherwise the hide only flashes.",Q)}}#FX(X,Z,$,Q,j,G){this.#S(X);let z=()=>{this.#S(X),this.#E($,Q,j,X,G)},K=setTimeout(z,Z);X?.addEventListener?.("blur",z,{once:!0}),this.#V.set(X,{timer:K,flush:z})}#S(X){let Z=this.#V.get(X);if(!Z)return;clearTimeout(Z.timer),X?.removeEventListener?.("blur",Z.flush),this.#V.delete(X)}#RX(){for(let X of[...this.#V.keys()])this.#S(X)}#DX(X,Z,$,Q,j,G){let z=this.#U.get(X)??new Map;if(z.has($))return;let K=setTimeout(()=>{if(z.delete($),z.size===0)this.#U.delete(X)},Z);return z.set($,K),this.#U.set(X,z),this.#E($,Q,j,X,G)}#TX(){for(let X of this.#U.values())for(let Z of X.values())clearTimeout(Z);this.#U.clear()}#C(X,Z,{cancelable:$=!1}={}){let Q=new CustomEvent(X,{bubbles:!0,composed:!0,cancelable:$,detail:Z});return(this.element.isConnected?this.element:document).dispatchEvent(Q),Q}#G(X,Z,$,Q){let j=()=>{if(!this.element.isConnected){console.warn("[phlex-reactive] retry() ignored — the reactive root left the DOM");return}return this.#E(X,Z)};this.#C("reactive:error",{action:X,params:$,...Q,retry:j})}#z(X){if(this.element?.isConnected===!1)return;this.element?.setAttribute?.("data-reactive-error",X)}#IX(){this.element?.removeAttribute?.("data-reactive-error")}#kX(){let X=document.querySelector("[data-reactive-error-flash]");if(!X?.content)return;let Z=X.getAttribute("data-reactive-error-flash")||"flash",$=document.getElementById(Z);if(!$)return;$.appendChild(X.content.cloneNode(!0))}#SX(){if(typeof sessionStorage>"u")return Promise.resolve();let X=Number(sessionStorage.getItem(w));if(!Number.isFinite(X)||X<=0)return Promise.resolve();if(!N)N=!0,console.warn(`[phlex-reactive] latency simulator ACTIVE — every action is delayed by ${X}ms. Call PhlexReactive.disableLatencySim() (or clear sessionStorage) to turn it off.`);return new Promise((Z)=>setTimeout(Z,X))}#n(){return this.element?.getAttribute?.("data-reactive-debug")==="true"}#a(){return typeof performance<"u"&&typeof performance.now==="function"?performance.now():Date.now()}#wX(X){if(!X)return[];let Z=[],$=/<turbo-stream\b([^>]*)>/g,Q;while((Q=$.exec(X))!==null){let j=Q[1],G=j.match(/\baction="([^"]*)"/)?.[1]??"?",z=j.match(/\btarget="([^"]*)"/)?.[1];Z.push(z?`${G} → #${z}`:G)}return Z}#bX(X){let{action:Z,status:$,ms:Q}=X,G=`reactive ${this.element?.id?`#${this.element.id} `:""}${Z} → ${$??"—"} (${Math.round(Q)}ms)`;if(console.groupCollapsed(G),console.log(`params: [${X.paramNames.join(", ")}] + collected: [${X.fieldNames.join(", ")}]`),console.log(`encoding: ${X.encoding}`),X.streams.length)console.log(`streams: ${X.streams.join(" ")}`);console.log(`token: ${X.tokenRefreshed?"refreshed ✓":"unchanged"}`),console.groupEnd()}async#hX(X,Z,$,Q,j){let{fields:G,files:z}=this.#XX(),K=this.#zX(Z),q={...G,...K},Y=this.#K,L=z.length>0,A=L?this.#GZ(Y,X,q,z):JSON.stringify({token:Y,act:X,params:q}),W=this.#n()?{action:X,paramNames:Object.keys(K),fieldNames:Object.keys(G),encoding:L?"multipart":"json",status:null,streams:[],tokenRefreshed:!1,started:this.#a()}:null;await this.#SX();try{if(navigator.onLine===!1){this.#q($),this.#z("offline"),this.#G(X,Z,q,{kind:"offline"});return}let _;try{let J={Accept:"text/vnd.turbo-stream.html","X-CSRF-Token":this.#AZ()};if(!L)J["Content-Type"]="application/json";let O=this.#BZ();if(O)J["X-Pgbus-Connection"]=O;_=await fetch(this.#LZ(),{method:"POST",headers:J,body:A,credentials:"same-origin",signal:AbortSignal.timeout(this.#MZ())})}catch(J){if(console.error("[phlex-reactive] action error",J),this.#q($),J?.name==="TimeoutError"||J?.name==="AbortError"){this.#z("timeout"),this.#G(X,Z,q,{kind:"timeout"});return}this.#kX(),this.#z("network"),this.#G(X,Z,q,{kind:"network"});return}if(W)W.status=_.status;if(_.redirected){console.error("[phlex-reactive] action was redirected (auth/CSRF?) — no update applied"),this.#q($),this.#z("redirected"),this.#G(X,Z,q,{kind:"redirected",status:_.status});return}if(!_.ok){let J=await _.text();if(console.error(`[phlex-reactive] action failed: HTTP ${_.status}`,J),this.#q($),(_.headers.get("Content-Type")||"").includes("turbo-stream")){let O=this.#t(J);if(this.#K=O??this.#K,W)this.#r(W,J,O);window.Turbo.renderStreamMessage(J)}this.#z("http"),this.#G(X,Z,q,{kind:"http",status:_.status,body:J});return}let U=_.headers.get("Content-Type")||"";if(!U.includes("turbo-stream")){console.error(`[phlex-reactive] expected a turbo-stream, got "${U}" — no update applied`),this.#q($),this.#z("content-type"),this.#G(X,Z,q,{kind:"content-type",status:_.status});return}let H=await _.text(),V=this.#t(H);if(this.#K=V??this.#K,W)this.#r(W,H,V);if(window.Turbo.renderStreamMessage(H),j)queueMicrotask(j);this.#IX(),this.#C("reactive:applied",{action:X,params:q,html:H})}catch(_){console.error("[phlex-reactive] action error",_),this.#q($),this.#C("reactive:error",{action:X,params:q,kind:"apply"})}finally{if(Q?.(),W)this.#bX({...W,ms:this.#a()-W.started})}}#r(X,Z,$){X.streams=this.#wX(Z),X.tokenRefreshed=$!=null}get#K(){return this.#f??this.tokenValue}set#K(X){this.#f=X}#t(X){let Z=this.element.id;if(!Z)return X.match(/data-reactive-token-value="([^"]+)"/)?.[1];let{token:$,self:Q}=this.#vX(Z),j=X.match($);if(j)return j[1];let G=X.match(Q);if(G)return G[1].match(/data-reactive-token-value="([^"]+)"/)?.[1];return}#vX(X){let Z=this.#p;if(Z&&Z.id===X)return Z;let $=NX(X);return this.#p={id:X,token:new RegExp(`<turbo-stream\\b[^>]*\\baction="reactive:token"[^>]*\\btarget="${$}"[^>]*\\bdata-reactive-token-value="([^"]+)"`),self:new RegExp(`<turbo-stream\\b[^>]*\\baction="(?:replace|update)"[^>]*\\btarget="${$}"[^>]*>([\\s\\S]*?)</turbo-stream>`)}}#$(X){return X.closest('[data-controller~="reactive"]')===this.element}#X(){if(this.element.querySelectorAll('[data-controller~="reactive"]').length===0)return()=>!0;return(Z)=>this.#$(Z)}#e(X,Z){if(X)return X;if(!Z)return null;let $=Z;if(typeof Z==="string")try{$=JSON.parse(Z)}catch{return console.warn(`[phlex-reactive] malformed conditional confirm payload ${JSON.stringify(Z)} — skipped`),null}if(!$||typeof $!=="object")return null;let{fields:Q}=this.#XX(),j=(z)=>Q[z],G;if(typeof $.predicate==="string"){let z=jX($.predicate);if(!z)return console.warn(`[phlex-reactive] confirm predicate "${$.predicate}" is not registered — proceeding without a dialog (register it with setConfirmPredicate)`),null;G=!!z(Q)}else G=S($.groups?.any,j)===!0;return G?$.message:null}#XX(){let X={},Z=[],$=this.#X();return this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((Q)=>{if(!$(Q))return;if(Q.type==="file")for(let j of Q.files??[])Z.push({name:Q.name,file:j,multiple:Q.multiple});else if(Q.type==="checkbox")X[Q.name]=Q.checked;else if(Q.type==="radio"){if(Q.checked)X[Q.name]=Q.value}else X[Q.name]=Q.value}),this.element.querySelectorAll("[name]:is(lexxy-editor, trix-editor, [contenteditable=''], [contenteditable=true], [contenteditable=plaintext-only])").forEach((Q)=>{if(!$(Q))return;let j=Q.getAttribute("name");if(!j)return;let G=X[j];if(G==null||G==="")X[j]=Q.value??Q.textContent??Q.innerHTML??""}),{fields:X,files:Z}}#w(){if(typeof this.element?.querySelectorAll!=="function")return;let X=0;if(this.element.querySelectorAll("input[name], select[name], textarea[name]").forEach((Z)=>{if(!this.#$(Z))return;if(Z.type==="file")return;if(this.#yX(Z))Z.setAttribute("data-reactive-dirty","true"),X++;else Z.removeAttribute("data-reactive-dirty")}),X>0)this.element.setAttribute("data-reactive-dirty",String(X));else this.element.removeAttribute("data-reactive-dirty")}#yX(X){if(X.type==="checkbox"||X.type==="radio")return X.checked!==X.defaultChecked;if(X.tag==="select"||X.options)return Array.from(X.options??[]).some((Z)=>Z.selected!==Z.defaultSelected);return X.value!==X.defaultValue}#ZX(){let X=this.element.getAttribute?.("data-reactive-dirty"),Z=Number(X);return Number.isFinite(Z)&&Z>0?Z:0}#fX(){if(typeof window>"u"||typeof window.addEventListener!=="function")return;this.#_=(X)=>{if(this.#ZX()===0)return;return X.preventDefault(),X.returnValue="You have unsaved changes.",X.returnValue},this.#J=(X)=>{if(this.#ZX()===0)return;if(!(typeof window.confirm==="function"?window.confirm("You have unsaved changes. Leave anyway?"):!0))X.preventDefault?.()},window.addEventListener("beforeunload",this.#_),window.addEventListener("turbo:before-visit",this.#J)}#pX(){if(this.#W)this.element.removeEventListener?.("turbo:morph-element",this.#W),this.#W=void 0;if(typeof window<"u"&&typeof window.removeEventListener==="function"){if(this.#_)window.removeEventListener("beforeunload",this.#_);if(this.#J)window.removeEventListener("turbo:before-visit",this.#J)}this.#_=void 0,this.#J=void 0}#mX(){if(this.element.getAttribute?.("data-reactive-show-targets"))return!0;let X=this.element.querySelectorAll?.(p)??[];for(let Z of X)if(this.#$(Z))return!0;return!1}#$X(){if(typeof this.element?.querySelectorAll!=="function")return;let X=this.#X(),Z=this.element.getAttribute?.("data-reactive-scope")||null,$=new Map,Q=(j)=>{if(!$.has(j))$.set(j,this.#dX(j,X,Z));return $.get(j)};for(let j of this.element.querySelectorAll(p)){if(!X(j))continue;let G=j.getAttribute("data-reactive-show");if(G!==null){let Y=IX(DX(G),Q);if(Y!==null)this.#QX(j,Y,X,Z);continue}let z=j.getAttribute("data-reactive-show-field");if(!z)continue;let K=Q(z);if(K===null)continue;let q=RX(j,K);if(q===null)continue;this.#QX(j,q,X,Z)}this.#uX(Q)}#QX(X,Z,$,Q){if(X.hidden=!Z,X.getAttribute("data-reactive-show-disable")!=="true")return;if(typeof X.querySelectorAll!=="function")return;for(let j of X.querySelectorAll("input[name], select[name], textarea[name]"))if($(j))j.disabled=!Z;if(X.name&&$(X))X.disabled=!Z}#uX(X){let Z=this.#cX();for(let[$,Q]of Object.entries(Z)){if($.startsWith("#")){this.#gX($,Q,X);continue}if(!Q||typeof Q!=="object"||Array.isArray(Q))continue;let j=X($);if(j===null)continue;let G=()=>j;for(let[z,K]of Object.entries(Q)){if(!m(z))continue;let q;if(Array.isArray(K)){if(K.length===0){console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${z} — skipped`);continue}q=K.every((Y)=>b(Y,G))}else{let Y=e(K,j);if(Y===null){console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${z} — skipped`);continue}q=Y}for(let Y of document.querySelectorAll(z))Y.hidden=!q}}}#gX(X,Z,$){if(!m(X))return;let Q=Z&&typeof Z==="object"&&!Array.isArray(Z)?Z.any:null,j=TX(Q);if(j===null){console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${X} — skipped`);return}if(j.every((z)=>$(z)===null))return;let G=S(Q,$);if(G===null)return;for(let z of document.querySelectorAll(X))z.hidden=!G}#cX(){let X=this.element.getAttribute?.("data-reactive-show-targets");if(!X)return{};try{let Z=JSON.parse(X);if(Z&&typeof Z==="object"&&!Array.isArray(Z))return Z}catch{}return console.warn("[phlex-reactive] malformed data-reactive-show-targets — ignored. "+"Did two reactive_show_targets calls collide on one root? Declare every field in ONE call: reactive_show_targets(mode: { ... }, kind: { ... })"),{}}#dX(X,Z,$){let Q=$&&!X.includes("[")?`${$}[${X}]`:X,j=!1,G=null;for(let z of this.element.querySelectorAll(`[name="${Q}"]`)){if(!Z(z))continue;if(z.type==="checkbox")return z.checked?"true":"false";if(z.type==="radio"){if(z.checked)return z.value??"";j=!0;continue}G??=z}if(G)return G.value??"";return j?"":null}#lX(){if(!this.#Z)return;this.element.removeEventListener?.("input",this.#Z),this.element.removeEventListener?.("change",this.#Z),this.element.removeEventListener?.("turbo:morph-element",this.#Z),this.#Z=void 0}#F(){return!!(this.element.getAttribute?.("data-reactive-filter-input")&&this.element.getAttribute?.("data-reactive-filter-option"))}#oX(){return this.element.getAttribute?.("data-reactive-compute-seed")==="true"}#iX(X){let Z=this.element.getAttribute("data-reactive-filter-input");return!!Z&&typeof X.target?.matches==="function"&&X.target.matches(Z)}#B(){if(typeof this.element?.querySelectorAll!=="function")return;let X=this.element.getAttribute("data-reactive-filter-input"),Z=this.element.getAttribute("data-reactive-filter-option");if(!X||!Z)return;let $=this.#X(),Q=[...this.element.querySelectorAll(X)].find($);if(!Q)return;let j=(Q.value??"").trim().toLowerCase(),G=0;for(let q of this.element.querySelectorAll(Z)){if(!$(q))continue;let Y=(q.getAttribute("data-reactive-filter-text")??q.textContent??"").toLowerCase(),L=q.hasAttribute?.("data-reactive-tags-selected")||j!==""&&!Y.includes(j);if(q.hidden=L,L)q.removeAttribute("data-reactive-highlighted");else G++}let z=this.element.getAttribute("data-reactive-filter-group");if(z)for(let q of this.element.querySelectorAll(z)){if(!$(q))continue;let Y=[...q.querySelectorAll(Z)].filter($);if(Y.length===0)continue;q.hidden=Y.every((L)=>L.hidden)}let K=this.element.getAttribute("data-reactive-filter-empty");if(K){for(let q of this.element.querySelectorAll(K))if($(q))q.hidden=G>0}}#sX(){if(!this.#Q)return;this.element.removeEventListener?.("input",this.#Q),this.element.removeEventListener?.("turbo:morph-element",this.#Q),this.#Q=void 0}#R(){return!!this.element.getAttribute?.("data-reactive-tags-field")}#nX(){if(typeof this.element?.querySelector!=="function")return!1;return!!this.element.querySelector("[data-reactive-nested-json]")}#b(){if(typeof this.element?.querySelectorAll!=="function")return null;let X=this.element.getAttribute("data-reactive-tags-field");if(!X)return null;let Z=this.#X();return[...this.element.querySelectorAll(X)].find(Z)??null}#h(X){let Z=new Set,$=[];for(let Q of String(X.value??"").split(",")){let j=Q.trim();if(j===""||Z.has(j.toLowerCase()))continue;Z.add(j.toLowerCase()),$.push(j)}return $}#jX(X){let Z=this.#b();if(!Z)return!1;let $=this.#h(Z),Q=new Set($.map((G)=>G.toLowerCase())),j=!1;for(let G of X){let z=String(G??"").trim();if(z===""||Q.has(z.toLowerCase()))continue;Q.add(z.toLowerCase()),$.push(z),j=!0}if(j)this.#GX(Z,$);return j}#GX(X,Z){if(X.value=Z.join(","),typeof X.dispatchEvent==="function")X.dispatchEvent(new Event("input",{bubbles:!0}));this.#v()}#aX(){if(typeof this.element?.querySelectorAll!=="function")return null;let X=this.element.getAttribute("data-reactive-filter-input");if(!X)return null;let Z=this.#X();return[...this.element.querySelectorAll(X)].find(Z)??null}#v(){let X=this.#b();if(!X)return;let Z=this.#h(X),$=this.#X();this.#rX(Z,$),this.#tX(Z,$)}#rX(X,Z){let $=[...this.element.querySelectorAll("[data-reactive-tags-list]")].find(Z);if(!$)return;let j=[...this.element.querySelectorAll("[data-reactive-tags-template]")].find(Z)?.content?.firstElementChild;if(!j){if(!this.#u)console.warn("[phlex-reactive] reactive_tags: no chip <template data-reactive-tags-template> found in this root — "+"chips will not render (the hidden field still updates). Add a template with a [data-reactive-tag-text] node and a reactive_tags_remove button."),this.#u=!0;return}while($.firstChild)$.removeChild($.firstChild);for(let G of X){let z=j.cloneNode(!0);z.setAttribute?.("data-reactive-tag",G);let K=z.matches?.("[data-reactive-tag-text]")?z:(z.querySelectorAll?.("[data-reactive-tag-text]")??[])[0];if(K)K.textContent=G;let q=[...z.querySelectorAll?.('[data-action*="reactive#tagsRemove"]')??[]];if(z.matches?.('[data-action*="reactive#tagsRemove"]'))q.push(z);for(let Y of q)Y.setAttribute?.("data-reactive-tag-param",G);$.appendChild(z)}}#tX(X,Z){let $=new Set(X.map((Q)=>Q.toLowerCase()));for(let Q of this.element.querySelectorAll("[role=option]")){if(!Z(Q))continue;let j=Q.getAttribute?.("data-reactive-tag-param");if(!j)continue;if($.has(j.toLowerCase()))Q.setAttribute("data-reactive-tags-selected","true"),Q.hidden=!0,Q.removeAttribute?.("data-reactive-highlighted");else if(Q.hasAttribute?.("data-reactive-tags-selected")){if(Q.removeAttribute("data-reactive-tags-selected"),!this.#F())Q.hidden=!1}}if(this.#F())this.#B()}#eX(){return this.#k=Math.max(this.#k+1,Date.now()),this.#k}#XZ(X,Z){let $=[X,...X.querySelectorAll?.("*")??[]];for(let Q of $)for(let j of["name","id","for"]){let G=Q.getAttribute?.(j);if(G&&G.includes("NEW_ROW"))Q.setAttribute?.(j,G.replaceAll("NEW_ROW",String(Z)))}}#ZZ(X){if(this.#g)return;console.warn(`[phlex-reactive] nested rows: no owned [data-reactive-nested-list="${X}"] container + <template data-reactive-nested-template="${X}"> pair found in this root — the add `+"trigger did nothing. Render both inside the same reactive root (reactive_nested_list / reactive_nested_template)."),this.#g=!0}#$Z(){if(!this.#L)return;this.element.removeEventListener?.("turbo:morph-element",this.#L),this.#L=void 0}#QZ(){if(this.#j)this.element.removeEventListener?.("input",this.#j),this.element.removeEventListener?.("change",this.#j),this.#j=void 0;if(this.#M)this.element.removeEventListener?.("turbo:morph-element",this.#M),this.#M=void 0}#jZ(){if(!this.#A)return;this.element.removeEventListener?.("turbo:morph-element",this.#A),this.#A=void 0}#GZ(X,Z,$,Q){let j=new FormData;j.append("token",X),j.append("act",Z);for(let[z,K]of Object.entries($))this.#y(j,`params[${z}]`,K);let G=this.#zZ(Q);for(let{name:z,file:K,multiple:q}of Q){let L=q||G.has(z)?`params[${z}][]`:`params[${z}]`;j.append(L,K,K.name)}return j}#y(X,Z,$){if($==null)X.append(Z,"");else if(Array.isArray($))$.forEach((Q,j)=>this.#y(X,`${Z}[${j}]`,Q));else if(typeof $==="object")for(let[Q,j]of Object.entries($))this.#y(X,`${Z}[${Q}]`,j);else X.append(Z,String($))}#zZ(X){let Z=new Map;for(let{name:$}of X)Z.set($,(Z.get($)??0)+1);return new Set([...Z].filter(([,$])=>$>1).map(([$])=>$))}#zX(X){if(!X)return{};try{return typeof X==="string"?JSON.parse(X):X}catch{return{}}}#KX(X){return XX(X)}#qX(X){ZX(X,(Z)=>this.#YX(Z))}#YX(X){let Z=X.to;if(Z==="@root")return[this.element];if(typeof Z!=="string"||Z==="")return[];if(X.global)return[...document.querySelectorAll(Z)];return[...this.element.querySelectorAll(Z)].filter(($)=>this.#$($))}#KZ(X,Z){if(X?.checked!=="keep")return!1;let $=Z?.type;return $==="checkbox"||$==="radio"}#qZ(X,Z){if(!X)return null;let $=this.#UX(X,Z,!0);return $.length?$:null}#q(X){if(!X)return;if(!this.element.isConnected)return;for(let Z of X)Z()}#YZ(X,Z,$){this.#HZ(X,Z);let Q=$?this.#UX($,Z,!1):[];i();let j=!1;return()=>{if(j)return;j=!0,this.#WZ(X,Z),s();for(let G of Q)G()}}#UX(X,Z,$){let Q=[];for(let j of this.#HX(X,Z)){if(X.add_class){let G=X.add_class.filter((z)=>!j.classList.contains(z));if(j.classList.add(...G),G.length)Q.push(()=>j.classList.remove(...G))}if(X.remove_class){let G=X.remove_class.filter((z)=>j.classList.contains(z));if(j.classList.remove(...G),G.length)Q.push(()=>j.classList.add(...G))}if(X.toggle_class)X.toggle_class.forEach((G)=>j.classList.toggle(G)),Q.push(()=>X.toggle_class.forEach((G)=>j.classList.toggle(G)));if(X.hide)j.hidden=!0,Q.push(()=>j.hidden=!1);if(X.show)j.hidden=!1,Q.push(()=>j.hidden=!0)}if(Z&&(X.disable||X.text!=null))Q.push(this.#UZ(X,Z));if($&&X.checked==="keep"&&Z&&"checked"in Z){let j=Z.checked;Q.push(()=>Z.checked=!j)}return Q}#HX(X,Z){if(X.to==null)return Z?[Z]:[];return this.#YX({to:X.to})}#UZ(X,Z){let $=this.#O.get(Z);if($)$.count++;else this.#O.set(Z,{count:1,disabled:Z.disabled,html:Z.innerHTML,hadText:X.text!=null,swappedTo:X.text});if(X.disable)Z.disabled=!0;if(X.text!=null)Z.innerHTML=X.text;return()=>this.#_Z(Z,X)}#HZ(X,Z){if(this.#Y(Z,X,1),this.#Y(this.element,X,1),this.#H.set(X,(this.#H.get(X)??0)+1),this.#T++===0)this.element.setAttribute("aria-busy","true");for(let $ of this.#WX(X))this.#Y($,X,1)}#WZ(X,Z){this.#Y(Z,X,-1),this.#Y(this.element,X,-1);let $=(this.#H.get(X)??1)-1;if($<=0)this.#H.delete(X);else this.#H.set(X,$);if(--this.#T<=0)this.#T=0,this.element.removeAttribute("aria-busy");for(let Q of this.#WX(X))this.#Y(Q,X,-1)}#Y(X,Z,$){if(!X||typeof X.getAttribute!=="function")return;let Q=this.#I.get(X)??new Map,j=(Q.get(Z)??0)+$;if(j<=0)Q.delete(Z);else Q.set(Z,j);if(Q.size===0){this.#I.delete(X),X.removeAttribute("data-reactive-busy");return}this.#I.set(X,Q),X.setAttribute("data-reactive-busy",[...Q.keys()].join(" "))}#WX(X){return[...this.element.querySelectorAll?.("[data-reactive-busy-on]")??[]].filter(($)=>$.getAttribute("data-reactive-busy-on")===X&&this.#$($))}#_Z(X,Z){let $=this.#O.get(X);if(!$)return;if(--$.count>0)return;if(this.#O.delete(X),!X.isConnected)return;if(Z.disable)X.disabled=$.disabled;if($.hadText&&X.innerHTML===$.swappedTo)X.innerHTML=$.html}#JZ(X){if(!X||typeof X!=="object")return null;let{class:Z,...$}=X;return Z==null?X:{...$,add_class:Z}}#LZ(){return this.#_X??=document.querySelector('meta[name="phlex-reactive-action-path"]')?.content||"/reactive/actions"}#MZ(){if(this.#D!=null)return this.#D;let X=document.querySelector('meta[name="phlex-reactive-timeout"]')?.content,Z=Number(X);return this.#D=Number.isFinite(Z)&&Z>0?Z:30000}#AZ(){return document.querySelector('meta[name="csrf-token"]')?.content??""}#BZ(){return document.querySelector("pgbus-stream-source[connection-id]")?.getAttribute("connection-id")||document.querySelector('meta[name="pgbus-connection-id"]')?.content||null}}export{pX as resetReactiveDefers,lX as resetReactiveActivity,GX as registerReactiveVisit,zX as registerReactiveToken,AX as registerReactiveOffline,KX as registerReactiveJs,LX as registerReactiveDismiss,qX as registerReactiveDefer,y as registerReactiveActions,dX as reactiveActivityCount,mX as pendingDeferVia,s as exitReactiveActivity,NX as escapeRegExp,i as enterReactiveActivity,BX as enableLatencySim,VX as disableLatencySim,sX as default,xX as checkReactiveRegistration,oX as __resetReactiveRegistrationForTest,gX as __resetReactiveOfflineForTest,cX as __resetReactiveLatencyForTest,uX as __resetReactiveDismissForTest,iX as __markReactiveConnectedForTest,w as LATENCY_KEY,o as ACTIVE_ATTR};
2
2
 
3
- //# debugId=5113E7379078B03464756E2164756E21
3
+ //# debugId=01D39E17DBA3F0CE64756E2164756E21
4
4
  //# sourceMappingURL=reactive_controller.min.js.map