@livenetworks/ashlar 2.0.7 → 2.0.9
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.
- package/LICENSE +641 -641
- package/components/COMPONENTS.md +1116 -1116
- package/components/index.js +81 -81
- package/components/ln-accordion/ln-accordion-dev.scss +8 -8
- package/components/ln-accordion/ln-accordion.js +1 -1
- package/components/ln-accordion/ln-accordion.schema.json +30 -30
- package/components/ln-ajax/ln-ajax-dev.scss +8 -8
- package/components/ln-ajax/ln-ajax.js +1 -1
- package/components/ln-ajax/ln-ajax.schema.json +30 -30
- package/components/ln-api-connector/ln-api-connector.js +1 -1
- package/components/ln-api-connector/ln-api-connector.schema.json +9 -0
- package/components/ln-api-connector/src/ln-api-connector.js +2 -0
- package/components/ln-api-queue/ln-api-queue.js +1 -1
- package/components/ln-api-queue/ln-api-queue.schema.json +34 -34
- package/components/ln-api-queue/src/ln-api-queue.js +3 -13
- package/components/ln-autoresize/ln-autoresize-dev.scss +8 -8
- package/components/ln-autoresize/ln-autoresize.js +1 -1
- package/components/ln-autoresize/ln-autoresize.schema.json +22 -22
- package/components/ln-autosave/ln-autosave-dev.scss +13 -13
- package/components/ln-autosave/ln-autosave.js +1 -1
- package/components/ln-autosave/ln-autosave.schema.json +48 -48
- package/components/ln-autosave/src/autosave-model.js +34 -34
- package/components/ln-chart/ln-chart.js +1 -1
- package/components/ln-chart/ln-chart.schema.json +146 -146
- package/components/ln-chart/src/chart-model.js +154 -154
- package/components/ln-circular-progress/ln-circular-progress.js +1 -1
- package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -42
- package/components/ln-confirm/ln-confirm-dev.scss +24 -24
- package/components/ln-confirm/ln-confirm.js +1 -1
- package/components/ln-confirm/ln-confirm.schema.json +66 -66
- package/components/ln-confirm/src/ln-confirm.js +178 -178
- package/components/ln-core/attrs.js +13 -0
- package/components/ln-core/crypto.js +286 -265
- package/components/ln-core/dom.js +115 -115
- package/components/ln-core/form.js +146 -136
- package/components/ln-core/helpers.js +33 -33
- package/components/ln-core/http.js +48 -48
- package/components/ln-core/index.js +1 -1
- package/components/ln-core/lifecycle.js +5 -0
- package/components/ln-core/ln-core.schema.json +3 -2
- package/components/ln-core/locale.js +55 -55
- package/components/ln-core/matching.js +78 -78
- package/components/ln-core/number.js +125 -125
- package/components/ln-core/template.js +206 -206
- package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
- package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -50
- package/components/ln-couchdb-connector/src/ln-couchdb-connector.js +1 -0
- package/components/ln-data-coordinator/README.md +9 -38
- package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -8
- package/components/ln-data-coordinator/ln-data-coordinator.js +1 -1
- package/components/ln-data-coordinator/ln-data-coordinator.schema.json +16 -16
- package/components/ln-data-coordinator/src/data-read-policy.js +0 -7
- package/components/ln-data-coordinator/src/ln-data-coordinator.js +432 -1308
- package/components/ln-data-store/ln-data-store-dev.scss +13 -13
- package/components/ln-data-store/ln-data-store.js +1 -1
- package/components/ln-data-store/ln-data-store.schema.json +84 -84
- package/components/ln-data-store/src/data-store-model.js +184 -184
- package/components/ln-data-store/src/ln-data-store.js +1 -1
- package/components/ln-date/ln-date.js +1 -1
- package/components/ln-date/ln-date.schema.json +92 -92
- package/components/ln-date/src/date-model.js +186 -186
- package/components/ln-debug/ln-debug.js +1 -1
- package/components/ln-debug/ln-debug.schema.json +28 -28
- package/components/ln-debug/src/console-sink.js +19 -19
- package/components/ln-debug/src/gate.js +95 -95
- package/components/ln-debug/src/generated-attributes.js +4 -2
- package/components/ln-dropdown/ln-dropdown-dev.scss +13 -13
- package/components/ln-dropdown/ln-dropdown.js +1 -1
- package/components/ln-dropdown/ln-dropdown.schema.json +90 -90
- package/components/ln-editor/ln-editor.js +1 -1
- package/components/ln-editor/ln-editor.schema.json +58 -58
- package/components/ln-external-links/ln-external-links.js +1 -1
- package/components/ln-external-links/ln-external-links.schema.json +18 -18
- package/components/ln-fill/ln-fill.js +1 -1
- package/components/ln-fill/ln-fill.schema.json +23 -23
- package/components/ln-filter/ln-filter.js +1 -1
- package/components/ln-filter/ln-filter.schema.json +153 -153
- package/components/ln-filter/src/filter-model.js +185 -185
- package/components/ln-form/ln-form-dev.scss +8 -8
- package/components/ln-form/ln-form.js +1 -1
- package/components/ln-form/ln-form.schema.json +6 -0
- package/components/ln-form/src/ln-form.js +21 -0
- package/components/ln-http/ln-http.js +1 -1
- package/components/ln-http/ln-http.schema.json +6 -6
- package/components/ln-icon/ln-icon.schema.json +6 -6
- package/components/ln-include/ln-include.js +1 -1
- package/components/ln-include/ln-include.schema.json +23 -23
- package/components/ln-key/ln-key-dev.scss +18 -18
- package/components/ln-key/ln-key.js +1 -1
- package/components/ln-key/ln-key.schema.json +56 -56
- package/components/ln-key/src/ln-key.js +10 -10
- package/components/ln-link/ln-link-dev.scss +8 -8
- package/components/ln-link/ln-link.js +1 -1
- package/components/ln-link/ln-link.schema.json +22 -22
- package/components/ln-list/README.md +152 -152
- package/components/ln-list/ln-list-dev.scss +8 -8
- package/components/ln-list/ln-list.js +1 -1
- package/components/ln-list/ln-list.schema.json +166 -166
- package/components/ln-modal/ln-modal.js +1 -1
- package/components/ln-modal/ln-modal.schema.json +65 -65
- package/components/ln-nav/ln-nav.js +1 -1
- package/components/ln-nav/ln-nav.schema.json +25 -25
- package/components/ln-number/ln-number.js +1 -1
- package/components/ln-number/ln-number.schema.json +63 -63
- package/components/ln-number/src/ln-number.js +26 -0
- package/components/ln-number/src/number-model.js +31 -31
- package/components/ln-obfuscator/README.md +60 -60
- package/components/ln-obfuscator/ln-obfuscator.js +1 -1
- package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -49
- package/components/ln-obfuscator/src/ln-obfuscator.js +159 -159
- package/components/ln-obfuscator/src/obfuscator-model.js +250 -250
- package/components/ln-options/ln-options.js +1 -1
- package/components/ln-options/ln-options.schema.json +6 -0
- package/components/ln-options/src/ln-options.js +4 -4
- package/components/ln-persist/README.md +151 -151
- package/components/ln-persist/ln-persist.js +1 -1
- package/components/ln-persist/ln-persist.schema.json +28 -28
- package/components/ln-persist/src/ln-persist.js +83 -83
- package/components/ln-picklist/README.md +230 -230
- package/components/ln-picklist/ln-picklist-dev.scss +22 -22
- package/components/ln-picklist/ln-picklist.js +1 -1
- package/components/ln-picklist/ln-picklist.schema.json +49 -49
- package/components/ln-picklist/src/ln-picklist.js +228 -228
- package/components/ln-popover/ln-popover-dev.scss +13 -13
- package/components/ln-popover/ln-popover.js +1 -1
- package/components/ln-popover/ln-popover.schema.json +82 -82
- package/components/ln-progress/ln-progress-dev.scss +8 -8
- package/components/ln-progress/ln-progress.js +1 -1
- package/components/ln-progress/ln-progress.schema.json +40 -40
- package/components/ln-router/ln-router.js +1 -1
- package/components/ln-router/ln-router.schema.json +76 -76
- package/components/ln-scroll/README.md +45 -45
- package/components/ln-scroll/ln-scroll.js +1 -1
- package/components/ln-scroll/ln-scroll.schema.json +85 -85
- package/components/ln-scroll/src/ln-scroll.js +150 -150
- package/components/ln-search/README.md +188 -188
- package/components/ln-search/ln-search.js +1 -1
- package/components/ln-search/ln-search.schema.json +103 -103
- package/components/ln-slug/ln-slug.js +1 -1
- package/components/ln-slug/ln-slug.schema.json +23 -23
- package/components/ln-slug/src/ln-slug.js +25 -1
- package/components/ln-sort/ln-sort.js +1 -1
- package/components/ln-sort/ln-sort.schema.json +104 -104
- package/components/ln-sort/src/ln-sort.js +321 -321
- package/components/ln-sort/src/sort-model.js +117 -117
- package/components/ln-sortable/ln-sortable-dev.scss +13 -13
- package/components/ln-sortable/ln-sortable.js +1 -1
- package/components/ln-sortable/ln-sortable.schema.json +36 -36
- package/components/ln-stat/ln-stat-dev.scss +8 -8
- package/components/ln-stat/ln-stat.js +1 -1
- package/components/ln-stat/ln-stat.schema.json +32 -32
- package/components/ln-stat/src/stat-model.js +28 -28
- package/components/ln-table/ln-table.js +1 -1
- package/components/ln-table/ln-table.schema.json +211 -211
- package/components/ln-table/src/table-model.js +98 -98
- package/components/ln-table-coordinator/ln-table-coordinator.js +1 -1
- package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -93
- package/components/ln-tabs/ln-tabs.js +1 -1
- package/components/ln-tabs/ln-tabs.schema.json +75 -75
- package/components/ln-tabs/src/ln-tabs.js +208 -208
- package/components/ln-tabs/src/tabs-model.js +82 -82
- package/components/ln-time/ln-time-dev.scss +13 -13
- package/components/ln-time/ln-time.js +1 -1
- package/components/ln-time/ln-time.schema.json +38 -38
- package/components/ln-toast/ln-toast.js +1 -1
- package/components/ln-toast/ln-toast.schema.json +65 -65
- package/components/ln-toggle/ln-toggle.js +1 -1
- package/components/ln-toggle/ln-toggle.schema.json +52 -52
- package/components/ln-tooltip/ln-tooltip.js +1 -1
- package/components/ln-tooltip/ln-tooltip.schema.json +86 -86
- package/components/ln-translations/ln-translations.js +1 -1
- package/components/ln-translations/ln-translations.schema.json +120 -120
- package/components/ln-ui-coordinator/README.md +62 -62
- package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -1
- package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +28 -28
- package/components/ln-ui-coordinator/src/ln-ui-coordinator.js +140 -140
- package/components/ln-upload/ln-upload.js +1 -1
- package/components/ln-upload/ln-upload.schema.json +193 -193
- package/components/ln-upload/src/upload-model.js +78 -78
- package/components/ln-validate/ln-validate.js +1 -1
- package/components/ln-validate/ln-validate.schema.json +50 -50
- package/components/ln-validate/src/validate-model.js +64 -64
- package/components/ln-websocket-connector/README.md +166 -166
- package/components/ln-websocket-connector/ln-websocket-connector.js +1 -1
- package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -29
- package/components/ln-websocket-connector/src/ln-websocket-connector.js +283 -279
- package/ln-ashlar.html-data.json +15 -8
- package/package.json +85 -84
- package/theme/brand.css +73 -73
- package/theme/components/_confirm.scss +6 -6
- package/theme/components/_picklist.scss +3 -3
- package/theme/config/_palette.scss +274 -274
- package/theme/config/_theme.scss +292 -292
- package/theme/config/_tokens.scss +285 -285
- package/theme/config/mixins/_index.scss +62 -62
- package/theme/config/mixins/_picklist.scss +66 -66
- package/theme/ln-ashlar-dev.scss +66 -66
- package/theme/ln-ashlar-landing.scss +56 -56
- package/theme/ln-ashlar-theme.scss +63 -63
- package/theme/presentation/_bento.scss +151 -151
- package/theme/presentation/_cta.scss +111 -111
- package/theme/presentation/_effects.scss +216 -216
- package/theme/presentation/_features.scss +108 -108
- package/theme/presentation/_footer.scss +129 -129
- package/theme/presentation/_hero.scss +158 -158
- package/theme/presentation/_layout.scss +170 -170
- package/theme/presentation/_navbar.scss +140 -140
- package/theme/presentation/_pricing.scss +262 -262
- package/theme/presentation/_stats.scss +87 -87
- package/theme/presentation/_testimonials.scss +124 -124
- package/theme/presentation/_tokens.scss +39 -39
- package/theme/presentation/_typography.scss +163 -163
- package/web-types.json +26 -12
|
@@ -1,188 +1,188 @@
|
|
|
1
|
-
# ln-search
|
|
2
|
-
|
|
3
|
-
> Applied via a two-host bridge: an `<input type="search" data-ln-search-for="targetId">` controls an element
|
|
4
|
-
> (`data-ln-search id="targetId"`). Typing fires native `input` events that write `target.setAttribute('data-ln-search', input.value)`.
|
|
5
|
-
> A `MutationObserver` on the target observes attribute changes, matches items against search tokens, and adds `data-ln-search-hide`
|
|
6
|
-
> and `.hidden` to non-matching elements. Clicking `[data-ln-search-clear]` sets `data-ln-search=""` and clears the input.
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## ✅ Canonical Markup — Copy This (REQUIRED)
|
|
11
|
-
|
|
12
|
-
**Hard rule — non-negotiable.** Every search or filter text input MUST use the full `<search>` chrome below: the leading magnifier icon **and** the clear ("x") button.
|
|
13
|
-
|
|
14
|
-
```html
|
|
15
|
-
<search aria-label="Search records">
|
|
16
|
-
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-search"></use></svg>
|
|
17
|
-
<input type="search" placeholder="Search …" data-ln-search-for="<targetId>">
|
|
18
|
-
<button type="button" data-ln-search-clear aria-label="Clear search">
|
|
19
|
-
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-x"></use></svg>
|
|
20
|
-
</button>
|
|
21
|
-
</search>
|
|
22
|
-
|
|
23
|
-
<ul id="<targetId>" data-ln-search="">
|
|
24
|
-
<li>Item Alpha</li>
|
|
25
|
-
<li>Item Beta</li>
|
|
26
|
-
<li data-ln-search-exclude>Pinned Item (Always Visible)</li>
|
|
27
|
-
</ul>
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
For deep targeting (e.g. table rows or checkbox lists) add `data-ln-search-items="<selector>"` on the **target element**:
|
|
31
|
-
|
|
32
|
-
```html
|
|
33
|
-
<table id="<targetId>" data-ln-search="" data-ln-search-items="tbody tr">
|
|
34
|
-
...
|
|
35
|
-
</table>
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## 📋 SSR Table Search — Without `ln-table` (REQUIRED shape)
|
|
41
|
-
|
|
42
|
-
When a table's rows are already printed in `<tbody>` by the server and the
|
|
43
|
-
**only** interaction is text search — no sort, no column filters, no
|
|
44
|
-
data-driven mode — `ln-search` binds directly to the `<table>`. Do **not**
|
|
45
|
-
add `data-ln-table`.
|
|
46
|
-
|
|
47
|
-
```html
|
|
48
|
-
<header class="ln-table__toolbar">
|
|
49
|
-
<search aria-label="Product search">
|
|
50
|
-
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-search"></use></svg>
|
|
51
|
-
<input type="search" data-ln-search-for="demo-table" aria-label="Search products">
|
|
52
|
-
<button type="button" data-ln-search-clear aria-label="Clear search">
|
|
53
|
-
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-x"></use></svg>
|
|
54
|
-
</button>
|
|
55
|
-
</search>
|
|
56
|
-
</header>
|
|
57
|
-
|
|
58
|
-
<!-- data-ln-persist: the term survives a page reload. Drop it when the
|
|
59
|
-
search should start empty every visit. -->
|
|
60
|
-
<table data-ln-search="" data-ln-search-items="tbody tr" data-ln-persist id="demo-table">
|
|
61
|
-
...
|
|
62
|
-
</table>
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Three rules govern this shape:
|
|
66
|
-
|
|
67
|
-
1. `data-ln-search` goes on the `<table>` itself, not a wrapper `<div>` —
|
|
68
|
-
`data-ln-table` is absent.
|
|
69
|
-
2. `data-ln-search-items="tbody tr"` is required. Without it, `_apply()`
|
|
70
|
-
falls back to `dom.children` — the `<thead>` and `<tbody>`, not rows. It
|
|
71
|
-
must be `tbody tr`, not bare `tr` — otherwise the header row's own `<tr>`
|
|
72
|
-
gets `data-ln-search-hide` when nothing matches.
|
|
73
|
-
3. The toolbar is a `<header class="ln-table__toolbar">`, a sibling of the
|
|
74
|
-
`<table>` — never a `<tr><th colspan="N">` row inside `<thead>`. Only that
|
|
75
|
-
wrapper gets the compact toolbar layout from
|
|
76
|
-
`theme/components/_ln-table.scss`.
|
|
77
|
-
|
|
78
|
-
See `demo/admin/src/pages/table.html` for the live reference.
|
|
79
|
-
|
|
80
|
-
**This is a different case from `ln-table`.** The moment the table also
|
|
81
|
-
needs column filters (`data-ln-table-filter-col`) or data-driven rows
|
|
82
|
-
(`data-ln-table-source`), `data-ln-table` goes on the wrapper and `ln-table`
|
|
83
|
-
owns search instead — see [`ln-table`](../ln-table/README.md#search-integration-ln-search).
|
|
84
|
-
`ln-table` listens for `ln-search:change` on itself and calls
|
|
85
|
-
`preventDefault()` unconditionally; since `dispatchCancelable` bubbles, the
|
|
86
|
-
generic `ln-search` DOM-filter path (including `data-ln-search-items`) never
|
|
87
|
-
runs while `data-ln-table` is present. The two modes are not meant to be
|
|
88
|
-
combined on the same target — pick one per table.
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## 🧭 Philosophy & Architecture
|
|
93
|
-
|
|
94
|
-
1. **Two-Host Separation (Attribute Bridge):**
|
|
95
|
-
- **Control (`data-ln-search-for="targetId"`):** Forwards user input and writes the raw value to `target.setAttribute('data-ln-search', rawValue)`.
|
|
96
|
-
- **State Host (`data-ln-search="term"`):** Uses `MutationObserver` (`_syncAttribute`) to detect attribute changes, syncs all matching controls, and dispatches `ln-search:change`.
|
|
97
|
-
2. **Tokenized AND Matching:** By default, splits search queries on whitespace and tests tokens with substring checks (`indexOf`). An item matches only if every token is present in its text.
|
|
98
|
-
3. **Exemptions & Subtree Exclusion (`data-ln-search-exclude`):**
|
|
99
|
-
- **On item root:** The item is exempt from filtering — always visible, never given `data-ln-search-hide`.
|
|
100
|
-
- **On descendant:** The subtree text is excluded from search matching.
|
|
101
|
-
4. **Cancelable Change Event:** Consumers (`ln-table`, `ln-list`, `ln-data-store`) call `e.preventDefault()` on `ln-search:change` to handle their own records and skip default DOM show/hide.
|
|
102
|
-
5. **Boot Seeding:** Pre-filled or deep-linked `data-ln-search` attributes seed safely using `queueBoot`.
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## 🛠️ Declarative API Contract
|
|
107
|
-
|
|
108
|
-
### HTML Attributes
|
|
109
|
-
|
|
110
|
-
| Attribute | Element | Description |
|
|
111
|
-
| :--- | :--- | :--- |
|
|
112
|
-
| `data-ln-search-for="targetId"` | `<input>` / wrapper | Pointer to the target element ID. |
|
|
113
|
-
| `data-ln-search="term"` | Target element | Search term state. Single source of truth. |
|
|
114
|
-
| `data-ln-search-items="selector"` | Target element | Deep CSS selector (e.g. `tbody tr`) to query items instead of direct children. |
|
|
115
|
-
| `data-ln-search-fields="a,b"` | Target element | Comma-separated list of field keys forwarded in event detail. |
|
|
116
|
-
| `data-ln-search-exclude` | Item root or descendant | Excludes item from filtering (on root) or excludes subtree text (on descendant). |
|
|
117
|
-
| `data-ln-search-clear` | `<button>` | Identifies a clear button (in search control or target empty state). Clears input and resets target state. |
|
|
118
|
-
| `data-ln-search-clear-for="targetId"` | `<button>` | Remote clear button targeting a specific element ID anywhere on the page. |
|
|
119
|
-
| `data-ln-search-hide="true"` | Items in target | State attribute automatically set on non-matching elements (`display: none !important`). |
|
|
120
|
-
| `data-ln-hash` | Target / Control | Opt-in. Synchronizes search query to URL hash fragment (e.g. `#users-search:john`). Value is custom namespace; if empty defaults to `[targetId]-search`. |
|
|
121
|
-
| `data-ln-persist` | Target element | **Recommended.** Persists the search term to `localStorage` and restores it before boot; omit it only when the search must start empty on every visit. Goes on `[data-ln-search]`, never on the input. The target needs an `id`, or give the attribute an explicit value (`data-ln-persist="key"`). No effect while `data-ln-hash` is active on the target — hash wins, never double-written. Stored key format: `ln:{id}:data-ln-search` (global by default); add `data-ln-persist-scope="page"` to opt into page-scoping, which stores `ln:{id}:{pathname}:data-ln-search`. |
|
|
122
|
-
| `data-ln-persist-scope` | Target element | Opt-in, independent attribute. `"page"` scopes the persisted key to the current URL pathname. |
|
|
123
|
-
|
|
124
|
-
### JavaScript API
|
|
125
|
-
|
|
126
|
-
| Instance | Property / Method | Description |
|
|
127
|
-
| :--- | :--- | :--- |
|
|
128
|
-
| `el.lnSearchControl` (Control) | `targetId`, `input`, `destroy()` | Control instance managing input. |
|
|
129
|
-
| `el.lnSearch` (State Host) | `term`, `nsKey`, `hashEnabled`, `_apply()`, `destroy()` | State instance owning term, URL hash sync, and DOM filter logic. |
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
## ⚡ DOM Events
|
|
135
|
-
|
|
136
|
-
### `ln-search:change`
|
|
137
|
-
Dispatched on the **target** element whenever the search term updates.
|
|
138
|
-
- **Cancelable**: Yes (`e.preventDefault()` disables default DOM show/hide).
|
|
139
|
-
- **Payload (`detail`)**:
|
|
140
|
-
```js
|
|
141
|
-
{
|
|
142
|
-
term: string, // Trimmed and lowercased string
|
|
143
|
-
tokens: string[], // Array of whitespace-separated tokens
|
|
144
|
-
targetId: string, // ID of the target
|
|
145
|
-
fields: string[]|null // Forwarded field names
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## 🌐 Backend Search Contract (Whitespace & Wildcard Handling)
|
|
152
|
-
|
|
153
|
-
To keep remote search consistent with client-side DOM and table filtering, backend APIs (e.g. `api.php`, SQL endpoints) must mirror the tokenized search contract:
|
|
154
|
-
|
|
155
|
-
1. **Whitespace Tokenization (`AND` Logic):** Split the query string on whitespace (`\s+`). Every token must be present in the record.
|
|
156
|
-
2. **Wildcard `*` Conversion:** Convert asterisks (`*`) into SQL `%` or Regex `.*`.
|
|
157
|
-
3. **Multi-Field Matching (`OR` per token):** Each token can match anywhere across the allowed searchable columns/fields.
|
|
158
|
-
|
|
159
|
-
```php
|
|
160
|
-
// Reference PHP / SQLite PDO implementation
|
|
161
|
-
$search = isset($_GET['search']) ? trim($_GET['search']) : (isset($_GET['q']) ? trim($_GET['q']) : '');
|
|
162
|
-
if ($search !== '') {
|
|
163
|
-
$tokens = preg_split('/\s+/', $search, -1, PREG_SPLIT_NO_EMPTY);
|
|
164
|
-
$searchFields = ['title', 'department', 'owner', 'tags'];
|
|
165
|
-
|
|
166
|
-
foreach ($tokens as $token) {
|
|
167
|
-
$tokenPattern = '%' . str_replace('*', '%', $token) . '%';
|
|
168
|
-
$tokenPattern = preg_replace('/%+/', '%', $tokenPattern);
|
|
169
|
-
|
|
170
|
-
$fieldClauses = [];
|
|
171
|
-
foreach ($searchFields as $field) {
|
|
172
|
-
$fieldClauses[] = "$field LIKE ?";
|
|
173
|
-
$params[] = $tokenPattern;
|
|
174
|
-
}
|
|
175
|
-
$where[] = '(' . implode(' OR ', $fieldClauses) . ')';
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## ⚠️ Common Pitfalls
|
|
183
|
-
|
|
184
|
-
- **Using `data-ln-search="tableId"` on inputs:** `data-ln-search` is the target state attribute. Inputs must use `data-ln-search-for="tableId"`.
|
|
185
|
-
- **Programmatic Value Mutations:** Assigning `input.value = "text"` programmatically does not trigger native `input` events. Either dispatch a native `input` event on the control or set the attribute directly on the target:
|
|
186
|
-
```js
|
|
187
|
-
document.getElementById('my-table').setAttribute('data-ln-search', 'query');
|
|
188
|
-
```
|
|
1
|
+
# ln-search
|
|
2
|
+
|
|
3
|
+
> Applied via a two-host bridge: an `<input type="search" data-ln-search-for="targetId">` controls an element
|
|
4
|
+
> (`data-ln-search id="targetId"`). Typing fires native `input` events that write `target.setAttribute('data-ln-search', input.value)`.
|
|
5
|
+
> A `MutationObserver` on the target observes attribute changes, matches items against search tokens, and adds `data-ln-search-hide`
|
|
6
|
+
> and `.hidden` to non-matching elements. Clicking `[data-ln-search-clear]` sets `data-ln-search=""` and clears the input.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ✅ Canonical Markup — Copy This (REQUIRED)
|
|
11
|
+
|
|
12
|
+
**Hard rule — non-negotiable.** Every search or filter text input MUST use the full `<search>` chrome below: the leading magnifier icon **and** the clear ("x") button.
|
|
13
|
+
|
|
14
|
+
```html
|
|
15
|
+
<search aria-label="Search records">
|
|
16
|
+
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-search"></use></svg>
|
|
17
|
+
<input type="search" placeholder="Search …" data-ln-search-for="<targetId>">
|
|
18
|
+
<button type="button" data-ln-search-clear aria-label="Clear search">
|
|
19
|
+
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-x"></use></svg>
|
|
20
|
+
</button>
|
|
21
|
+
</search>
|
|
22
|
+
|
|
23
|
+
<ul id="<targetId>" data-ln-search="">
|
|
24
|
+
<li>Item Alpha</li>
|
|
25
|
+
<li>Item Beta</li>
|
|
26
|
+
<li data-ln-search-exclude>Pinned Item (Always Visible)</li>
|
|
27
|
+
</ul>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
For deep targeting (e.g. table rows or checkbox lists) add `data-ln-search-items="<selector>"` on the **target element**:
|
|
31
|
+
|
|
32
|
+
```html
|
|
33
|
+
<table id="<targetId>" data-ln-search="" data-ln-search-items="tbody tr">
|
|
34
|
+
...
|
|
35
|
+
</table>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 📋 SSR Table Search — Without `ln-table` (REQUIRED shape)
|
|
41
|
+
|
|
42
|
+
When a table's rows are already printed in `<tbody>` by the server and the
|
|
43
|
+
**only** interaction is text search — no sort, no column filters, no
|
|
44
|
+
data-driven mode — `ln-search` binds directly to the `<table>`. Do **not**
|
|
45
|
+
add `data-ln-table`.
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<header class="ln-table__toolbar">
|
|
49
|
+
<search aria-label="Product search">
|
|
50
|
+
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-search"></use></svg>
|
|
51
|
+
<input type="search" data-ln-search-for="demo-table" aria-label="Search products">
|
|
52
|
+
<button type="button" data-ln-search-clear aria-label="Clear search">
|
|
53
|
+
<svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-x"></use></svg>
|
|
54
|
+
</button>
|
|
55
|
+
</search>
|
|
56
|
+
</header>
|
|
57
|
+
|
|
58
|
+
<!-- data-ln-persist: the term survives a page reload. Drop it when the
|
|
59
|
+
search should start empty every visit. -->
|
|
60
|
+
<table data-ln-search="" data-ln-search-items="tbody tr" data-ln-persist id="demo-table">
|
|
61
|
+
...
|
|
62
|
+
</table>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Three rules govern this shape:
|
|
66
|
+
|
|
67
|
+
1. `data-ln-search` goes on the `<table>` itself, not a wrapper `<div>` —
|
|
68
|
+
`data-ln-table` is absent.
|
|
69
|
+
2. `data-ln-search-items="tbody tr"` is required. Without it, `_apply()`
|
|
70
|
+
falls back to `dom.children` — the `<thead>` and `<tbody>`, not rows. It
|
|
71
|
+
must be `tbody tr`, not bare `tr` — otherwise the header row's own `<tr>`
|
|
72
|
+
gets `data-ln-search-hide` when nothing matches.
|
|
73
|
+
3. The toolbar is a `<header class="ln-table__toolbar">`, a sibling of the
|
|
74
|
+
`<table>` — never a `<tr><th colspan="N">` row inside `<thead>`. Only that
|
|
75
|
+
wrapper gets the compact toolbar layout from
|
|
76
|
+
`theme/components/_ln-table.scss`.
|
|
77
|
+
|
|
78
|
+
See `demo/admin/src/pages/table.html` for the live reference.
|
|
79
|
+
|
|
80
|
+
**This is a different case from `ln-table`.** The moment the table also
|
|
81
|
+
needs column filters (`data-ln-table-filter-col`) or data-driven rows
|
|
82
|
+
(`data-ln-table-source`), `data-ln-table` goes on the wrapper and `ln-table`
|
|
83
|
+
owns search instead — see [`ln-table`](../ln-table/README.md#search-integration-ln-search).
|
|
84
|
+
`ln-table` listens for `ln-search:change` on itself and calls
|
|
85
|
+
`preventDefault()` unconditionally; since `dispatchCancelable` bubbles, the
|
|
86
|
+
generic `ln-search` DOM-filter path (including `data-ln-search-items`) never
|
|
87
|
+
runs while `data-ln-table` is present. The two modes are not meant to be
|
|
88
|
+
combined on the same target — pick one per table.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 🧭 Philosophy & Architecture
|
|
93
|
+
|
|
94
|
+
1. **Two-Host Separation (Attribute Bridge):**
|
|
95
|
+
- **Control (`data-ln-search-for="targetId"`):** Forwards user input and writes the raw value to `target.setAttribute('data-ln-search', rawValue)`.
|
|
96
|
+
- **State Host (`data-ln-search="term"`):** Uses `MutationObserver` (`_syncAttribute`) to detect attribute changes, syncs all matching controls, and dispatches `ln-search:change`.
|
|
97
|
+
2. **Tokenized AND Matching:** By default, splits search queries on whitespace and tests tokens with substring checks (`indexOf`). An item matches only if every token is present in its text.
|
|
98
|
+
3. **Exemptions & Subtree Exclusion (`data-ln-search-exclude`):**
|
|
99
|
+
- **On item root:** The item is exempt from filtering — always visible, never given `data-ln-search-hide`.
|
|
100
|
+
- **On descendant:** The subtree text is excluded from search matching.
|
|
101
|
+
4. **Cancelable Change Event:** Consumers (`ln-table`, `ln-list`, `ln-data-store`) call `e.preventDefault()` on `ln-search:change` to handle their own records and skip default DOM show/hide.
|
|
102
|
+
5. **Boot Seeding:** Pre-filled or deep-linked `data-ln-search` attributes seed safely using `queueBoot`.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 🛠️ Declarative API Contract
|
|
107
|
+
|
|
108
|
+
### HTML Attributes
|
|
109
|
+
|
|
110
|
+
| Attribute | Element | Description |
|
|
111
|
+
| :--- | :--- | :--- |
|
|
112
|
+
| `data-ln-search-for="targetId"` | `<input>` / wrapper | Pointer to the target element ID. |
|
|
113
|
+
| `data-ln-search="term"` | Target element | Search term state. Single source of truth. |
|
|
114
|
+
| `data-ln-search-items="selector"` | Target element | Deep CSS selector (e.g. `tbody tr`) to query items instead of direct children. |
|
|
115
|
+
| `data-ln-search-fields="a,b"` | Target element | Comma-separated list of field keys forwarded in event detail. |
|
|
116
|
+
| `data-ln-search-exclude` | Item root or descendant | Excludes item from filtering (on root) or excludes subtree text (on descendant). |
|
|
117
|
+
| `data-ln-search-clear` | `<button>` | Identifies a clear button (in search control or target empty state). Clears input and resets target state. |
|
|
118
|
+
| `data-ln-search-clear-for="targetId"` | `<button>` | Remote clear button targeting a specific element ID anywhere on the page. |
|
|
119
|
+
| `data-ln-search-hide="true"` | Items in target | State attribute automatically set on non-matching elements (`display: none !important`). |
|
|
120
|
+
| `data-ln-hash` | Target / Control | Opt-in. Synchronizes search query to URL hash fragment (e.g. `#users-search:john`). Value is custom namespace; if empty defaults to `[targetId]-search`. |
|
|
121
|
+
| `data-ln-persist` | Target element | **Recommended.** Persists the search term to `localStorage` and restores it before boot; omit it only when the search must start empty on every visit. Goes on `[data-ln-search]`, never on the input. The target needs an `id`, or give the attribute an explicit value (`data-ln-persist="key"`). No effect while `data-ln-hash` is active on the target — hash wins, never double-written. Stored key format: `ln:{id}:data-ln-search` (global by default); add `data-ln-persist-scope="page"` to opt into page-scoping, which stores `ln:{id}:{pathname}:data-ln-search`. |
|
|
122
|
+
| `data-ln-persist-scope` | Target element | Opt-in, independent attribute. `"page"` scopes the persisted key to the current URL pathname. |
|
|
123
|
+
|
|
124
|
+
### JavaScript API
|
|
125
|
+
|
|
126
|
+
| Instance | Property / Method | Description |
|
|
127
|
+
| :--- | :--- | :--- |
|
|
128
|
+
| `el.lnSearchControl` (Control) | `targetId`, `input`, `destroy()` | Control instance managing input. |
|
|
129
|
+
| `el.lnSearch` (State Host) | `term`, `nsKey`, `hashEnabled`, `_apply()`, `destroy()` | State instance owning term, URL hash sync, and DOM filter logic. |
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## ⚡ DOM Events
|
|
135
|
+
|
|
136
|
+
### `ln-search:change`
|
|
137
|
+
Dispatched on the **target** element whenever the search term updates.
|
|
138
|
+
- **Cancelable**: Yes (`e.preventDefault()` disables default DOM show/hide).
|
|
139
|
+
- **Payload (`detail`)**:
|
|
140
|
+
```js
|
|
141
|
+
{
|
|
142
|
+
term: string, // Trimmed and lowercased string
|
|
143
|
+
tokens: string[], // Array of whitespace-separated tokens
|
|
144
|
+
targetId: string, // ID of the target
|
|
145
|
+
fields: string[]|null // Forwarded field names
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 🌐 Backend Search Contract (Whitespace & Wildcard Handling)
|
|
152
|
+
|
|
153
|
+
To keep remote search consistent with client-side DOM and table filtering, backend APIs (e.g. `api.php`, SQL endpoints) must mirror the tokenized search contract:
|
|
154
|
+
|
|
155
|
+
1. **Whitespace Tokenization (`AND` Logic):** Split the query string on whitespace (`\s+`). Every token must be present in the record.
|
|
156
|
+
2. **Wildcard `*` Conversion:** Convert asterisks (`*`) into SQL `%` or Regex `.*`.
|
|
157
|
+
3. **Multi-Field Matching (`OR` per token):** Each token can match anywhere across the allowed searchable columns/fields.
|
|
158
|
+
|
|
159
|
+
```php
|
|
160
|
+
// Reference PHP / SQLite PDO implementation
|
|
161
|
+
$search = isset($_GET['search']) ? trim($_GET['search']) : (isset($_GET['q']) ? trim($_GET['q']) : '');
|
|
162
|
+
if ($search !== '') {
|
|
163
|
+
$tokens = preg_split('/\s+/', $search, -1, PREG_SPLIT_NO_EMPTY);
|
|
164
|
+
$searchFields = ['title', 'department', 'owner', 'tags'];
|
|
165
|
+
|
|
166
|
+
foreach ($tokens as $token) {
|
|
167
|
+
$tokenPattern = '%' . str_replace('*', '%', $token) . '%';
|
|
168
|
+
$tokenPattern = preg_replace('/%+/', '%', $tokenPattern);
|
|
169
|
+
|
|
170
|
+
$fieldClauses = [];
|
|
171
|
+
foreach ($searchFields as $field) {
|
|
172
|
+
$fieldClauses[] = "$field LIKE ?";
|
|
173
|
+
$params[] = $tokenPattern;
|
|
174
|
+
}
|
|
175
|
+
$where[] = '(' . implode(' OR ', $fieldClauses) . ')';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## ⚠️ Common Pitfalls
|
|
183
|
+
|
|
184
|
+
- **Using `data-ln-search="tableId"` on inputs:** `data-ln-search` is the target state attribute. Inputs must use `data-ln-search-for="tableId"`.
|
|
185
|
+
- **Programmatic Value Mutations:** Assigning `input.value = "text"` programmatically does not trigger native `input` events. Either dispatch a native `input` event on the control or set the attribute directly on the target:
|
|
186
|
+
```js
|
|
187
|
+
document.getElementById('my-table').setAttribute('data-ln-search', 'query');
|
|
188
|
+
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){"use strict";function j(){return typeof window>"u"?!1:window.lnDebug===!0||window.lnCore&&window.lnCore._debugSink?!0:typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug")||document.body.querySelector("[data-ln-debug]")!==null:!1}function I(e){if(typeof window>"u")return!1;if(window.lnDebug===!0)return!0;if(window.lnCore&&window.lnCore._debugIsContained)return e?window.lnCore._debugIsContained(e):window.lnCore._debugSink!==null;if(e&&e.closest){const t=e.closest("[data-ln-debug]");return!(!t||typeof document<"u"&&t===document.documentElement)}return typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug"):!1}if(typeof window<"u"&&(window.lnCore=window.lnCore||{},!window.lnCore._warnBound)){window.lnCore._warnBound=!0;const e=console.warn;console.warn=function(...t){if(typeof t[0]=="string"&&(t[0].startsWith("[ln-")||t[0].startsWith("[lnCore"))){let r=null;for(let o=1;o<t.length;o++)if(t[o]&&t[o].nodeType===1){r=t[o];break}if(!I(r))return}e.apply(console,t)}}function Z(e,t,n){const r=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,r),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:r}))}function ee(e,t,n){const r=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,r);const o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:r});return e.dispatchEvent(o),o}function k(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){k(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}const W={};function te(e,t){W[e]=t}function ne(e){return W[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=te,window.lnCore.getDataMapper=ne);function re(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,k(function(){new MutationObserver(function(){Z(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const V={};function oe(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];V[n]=t}function ie(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return V[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=oe,window.lnCore.getLocaleFallback=ie,window.lnCore.ensureLocaleObserver=re);const O={};function le(e,t){O[e]||(O[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=O[e];return n?n.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function K(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let l=0;l<n.length;l++){const d=n[l],a=d.getAttribute("data-ln-field");t[a]!=null&&(d.textContent=t[a])}const r=e.querySelectorAll("[data-ln-attr]");for(let l=0;l<r.length;l++){const d=r[l],a=d.getAttribute("data-ln-attr").split(",");for(let f=0;f<a.length;f++){const p=a[f].trim().split(":");if(p.length!==2)continue;const h=p[0].trim(),b=p[1].trim();t[b]!=null&&d.setAttribute(h,t[b])}}const o=e.querySelectorAll("[data-ln-show]");for(let l=0;l<o.length;l++){const d=o[l],a=d.getAttribute("data-ln-show");a in t&&d.classList.toggle("hidden",!t[a])}const i=e.querySelectorAll("[data-ln-class]");for(let l=0;l<i.length;l++){const d=i[l],a=d.getAttribute("data-ln-class").split(",");for(let f=0;f<a.length;f++){const p=a[f].trim().split(":");if(p.length!==2)continue;const h=p[0].trim(),b=p[1].trim();b in t&&d.classList.toggle(h,!!t[b])}}return e}function se(e,t){e.matches&&e.matches("[data-ln-form], [data-ln-fillable]")&&(window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",e,t??null),e.dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0})));const n=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let r=0;r<n.length;r++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",n[r],t??null),n[r].dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0}));return e}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._fillBound||(window.lnCore._fillBound=!0,document.addEventListener("ln-fill",function(e){if(!(!e.target.matches||!e.target.matches("[data-ln-fillable]")))if(e.detail)K(e.target,e.detail);else{const t=e.target.querySelectorAll("[data-ln-field]");for(let n=0;n<t.length;n++)t[n].textContent=""}})));function U(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const i=n.currentNode;i.textContent.indexOf("{{")!==-1&&(i.textContent=i.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(l,d){return t[d]!==void 0?t[d]:""}))}const r=function(i,l){return t[l]!==void 0?t[l]:""},o=Array.from(e.querySelectorAll("*"));e.nodeType===1&&o.push(e);for(let i=0;i<o.length;i++){const l=o[i],d=l.attributes;for(let a=0;a<d.length;a++){const f=d[a];f.value.indexOf("{{")!==-1&&l.setAttribute(f.name,f.value.replace(/\{\{\s*(\w+)\s*\}\}/g,r))}}return e}function ce(e,t,n,r,o,i){const l={};for(let a=0;a<e.children.length;a++){const f=e.children[a],p=f.getAttribute("data-ln-render-key");p&&(l[p]=f)}const d=document.createDocumentFragment();for(let a=0;a<t.length;a++){const f=t[a],p=String(r(f));let h=l[p];if(h)o(h,f,a);else{const b=le(n,i);if(!b||(U(b,f),h=b.firstElementChild,!h))continue;h.setAttribute("data-ln-render-key",p),o(h,f,a)}d.appendChild(h)}e.textContent="",e.appendChild(d)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=U,window.lnCore.fill=K,window.lnCore.lnFill=se,window.lnCore.renderList=ce);function L(e,t,n){const r=e.getAttribute(t);return r===null?n:r}function ae(e,t,n){const r=parseInt(e.getAttribute(t),10);return isNaN(r)?n:r}function ue(e,t,n=!1){const r=e.getAttribute(t);if(r===null)return!!n;const o=r.trim().toLowerCase();return!(o==="false"||o==="0")}function de(e,t){return(e.getAttribute(t)||"").split(",").map(n=>n.trim()).filter(Boolean)}const R=new Map;function fe(e,t){const n=(e?e.join("|"):"")+"::"+t;if(R.has(n))return R.get(n);const r=new Set(e||[]),o=function(i,l){const d=i.getAttribute(l);return d!==null&&r.has(d)?d:t};return R.set(n,o),o}function he(e,t,n){const r=parseFloat(e.getAttribute(t));return isNaN(r)?n:r}function we(e,t,n){const r=e.getAttribute(t);if(!r)return n;try{return JSON.parse(r)}catch{return n}}function Q(e,t,n,r,o){if(!e||t===null)return!0;const i=r||"ln-component",l=e.type;if(l==="trigger"||l==="marker"||l==="string"||l==="list"||!l||t===""&&e.fallback!==void 0)return!0;const d=a=>{o?console.warn(a,o):console.warn(a)};if(l==="boolean"){const a=t.trim().toLowerCase();return a!==""&&a!=="true"&&a!=="false"&&a!=="1"&&a!=="0"?(d(`[${i}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(l==="enum"){const a=e.values||[];return a.includes(t)?!0:(d(`[${i}] Invalid value "${t}" for attribute "${n}". Allowed: ${a.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(l==="integer"){if(!/^-?\d+$/.test(t))return d(`[${i}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const a=parseInt(t,10);return e.min!==void 0&&a<e.min?(d(`[${i}] Value ${a} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&a>e.max?(d(`[${i}] Value ${a} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(l==="float")return isNaN(Number(t))?(d(`[${i}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(l==="json")try{return JSON.parse(t),!0}catch(a){return d(`[${i}] Invalid JSON for attribute "${n}": ${a.message}. Fallback: ${e.fallback}.`),!1}return!0}function pe(e,t,n){for(const r in n){const[o,i,l]=n[r];Object.defineProperty(e,r,{get:function(){return o(t,i,l)},enumerable:!0,configurable:!0})}return e}function ge(e){const t={};for(const n in e){const r=e[n];if(!r||!r.prop)continue;let o=r.read;if(!o&&r.type)switch(r.type){case"enum":o=fe(r.values,r.fallback);break;case"integer":o=ae;break;case"float":o=he;break;case"boolean":o=ue;break;case"list":o=de;break;case"json":o=we;break;case"string":default:o=L;break}o||(o=L),t[r.prop]=[o,n,r.fallback]}return t}function be(e){const t={};for(const n in e){const r=e[n];r&&r.effect&&(t[n]=r.effect)}return Object.keys(t).length?t:null}function me(e){if(e.onAttrChange&&!e.declared)return null;const t=new Set;if(e.effects)for(const n in e.effects)t.add(n);if(e.onAttrChange&&e.declared)for(const n of e.declared)t.add(n);return t}function D(e,t,n,r){if(e.nodeType!==1)return;const i=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",l=Array.from(e.querySelectorAll(i));e.matches&&e.matches(i)&&l.push(e);for(const d of l)if(!d[n]){window.lnCore._persistSink&&d.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(d,t);try{d[n]=new r(d)}catch(a){console.error("["+n+"] init failed",d,a)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function Ce(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function q(e){typeof window<"u"?(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[],window.lnCore._bootHolds>0?window.lnCore._bootQueue.push(e):setTimeout(e,0)):e()}function P(){window.lnCore=window.lnCore||{};const e=window.lnCore._attrRegistry=window.lnCore._attrRegistry||{byAttr:new Map,reactive:[],persist:[]};return e.byReactive=e.byReactive||new Map,e.reactiveWildcard=e.reactiveWildcard||[],e}function ye(e){const t=P(),n=e.observed||[];for(let r=0;r<n.length;r++){const o=n[r];t.byAttr.has(o)||t.byAttr.set(o,[]),t.byAttr.get(o).push(e)}if(t.byDeclaredAttr=t.byDeclaredAttr||new Map,e.attributes)for(const r in e.attributes){t.byDeclaredAttr.has(r)||t.byDeclaredAttr.set(r,[]);const o=t.byDeclaredAttr.get(r);o.some(i=>i.componentTag===e.componentTag)||o.push({spec:e.attributes[r],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const r=me(e);if(r===null)t.reactiveWildcard.push(e);else for(const o of r)t.byReactive.has(o)||t.byReactive.set(o,[]),t.byReactive.get(o).push(e)}e.persist&&t.persist.push(e)}function J(e,t,n,r){for(let o=0;o<e.length;o++){const i=e[o];if(!t[i.attribute])continue;const l=i.effects&&i.effects[n];l?l(t,n,r):i.onAttrChange&&(!i.declared||i.declared.has(n))&&i.onAttrChange(t,n,r)}}function Ae(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const r=P(),o=r.byAttr.get(n);if(j()&&r.byDeclaredAttr&&r.byDeclaredAttr.has(n)&&I(t)){const i=r.byDeclaredAttr.get(n),l=t.getAttribute(n);for(let d=0;d<i.length;d++)Q(i[d].spec,l,n,i[d].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||o)&&window.lnCore._debugSink("attr",n,t,{oldValue:e.oldValue,newValue:t.getAttribute(n)}),n.indexOf("data-ln-")===0){const i=r.byReactive.get(n);i&&J(i,t,n,e.oldValue),r.reactiveWildcard.length&&J(r.reactiveWildcard,t,n,e.oldValue)}if(o)for(let i=0;i<o.length;i++){const l=o[i];if(l.handler){l.handler(t,n,e.oldValue);continue}l.onAttributeChange&&t[l.attribute]?l.onAttributeChange(t,n):(D(t,l.selector,l.attribute,l.ComponentFn),l.onInit&&l.onInit(t))}}function _e(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,k(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{Ae(t[n])}catch(r){console.error("[ln-core] mutation handler failed",t[n].target,r)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function z(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function Se(e){const t=z();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const r=t[n];if(r.onSubtreeChange){const o=r.query,i=e.target.nodeType===1?e.target.matches(o)?e.target:e.target.closest(o):e.target.parentElement?e.target.parentElement.closest(o):null;i&&r.onSubtreeChange(i,e)}}for(let n=0;n<e.addedNodes.length;n++){const r=e.addedNodes[n];if(r.nodeType===1)for(let o=0;o<t.length;o++){const i=t[o];D(r,i.selector,i.attribute,i.ComponentFn),i.onInit&&i.onInit(r)}}for(let n=0;n<e.removedNodes.length;n++){const r=e.removedNodes[n];if(r.nodeType===1)for(let o=0;o<t.length;o++){const i=t[o],l=i.query,d=Array.from(r.querySelectorAll(l));r.matches&&r.matches(l)&&d.push(r);for(let a=0;a<d.length;a++){const f=d[a];if(!document.contains(f)){const p=f[i.attribute];if(p&&typeof p.destroy=="function")try{p.destroy()}catch(h){console.error("["+i.attribute+"] destroy failed",f,h)}delete f[i.attribute]}}}}}}function ve(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,k(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const r=t[n];if(r.type==="childList")try{Se(r)}catch(o){console.error("[ln-core] lifecycle handler failed",r.target,o)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function G(e,t,n,r,o={}){const i=o.extraAttributes||[],l=o.onAttributeChange||null,d=o.onSubtreeChange||null,a=o.onInit||null,f=o.onAttrChange||null,p=o.effects||null,h=o.attributes||null,b=h?be(h):p,x=h?new Set(Object.keys(h)):null,F=o.persist||null;function _(u){const c=u||document.body;if(D(c,e,t,n),h&&j())for(const s in h){const w=h[s],m=Array.from(c.querySelectorAll("["+s+"]"));c.matches&&c.matches("["+s+"]")&&m.push(c);for(let A=0;A<m.length;A++){const g=m[A];I(g)&&Q(w,g.getAttribute(s),s,r,g)}}a&&a(c)}const S=[];if(e.indexOf("[")!==-1){const u=/\[([\w-]+)/g;let c;for(;(c=u.exec(e))!==null;)S.push(c[1])}else S.push(e);ye({selector:e,attribute:t,componentTag:r,attributes:h,ComponentFn:n,onInit:a,observed:S.concat(i),onAttributeChange:l,onAttrChange:f,effects:b,declared:x,persist:F}),_e();const M=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";z().push({selector:e,attribute:t,ComponentFn:n,onInit:a,onSubtreeChange:d,query:M}),ve(),window[t]=_;function E(){Ce()>0?q(function(){_(document.body)}):_(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",E):E(),_}function X(e){return(e||"").replace(/^#/,"")}function $(e){const t=e===void 0?location.hash:e,n={},r=X(t);if(!r)return n;const o=r.split("&");for(let i=0;i<o.length;i++){const l=o[i];if(!l)continue;const d=l.indexOf(":"),a=d>-1?l.slice(0,d):l,f=d>-1?l.slice(d+1):"";if(a)try{n[a]=decodeURIComponent(f)}catch{n[a]=f}}return n}function B(e){if(!e)return null;const t=$();return e in t?t[e]:null}function Y(e,t){if(!e)return;const n=$();t==null?delete n[e]:n[e]=String(t);const o=Object.keys(n).map(function(i){const l=n[i];return l===""?i:i+":"+encodeURIComponent(l)}).join("&");X(location.hash)!==o&&(location.hash=o)}function Ee(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function N(e,t){if(!e||!e.hasAttribute("data-ln-hash"))return null;const n=e.getAttribute("data-ln-hash");if(n&&n.trim()!=="")return n.trim();const r=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return r?t?r+"-"+t:r:t||null}function Te(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function ke(e){return!e||typeof e!="string"?null:e.endsWith(".asc")?{fieldOrColumn:e.slice(0,-4),direction:"asc"}:e.endsWith(".desc")?{fieldOrColumn:e.slice(0,-5),direction:"desc"}:null}function xe(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function Ie(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const n=e.slice(0,t),r=e.slice(t+1),o=r?r.split(",").map(function(i){try{return decodeURIComponent(i)}catch{return i}}).filter(Boolean):[];return{key:n,values:o}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=$,window.lnCore.hashGet=B,window.lnCore.hashSet=Y,window.lnCore.hashLinkClick=Ee,window.lnCore.resolveHashNamespace=N,window.lnCore.hashSortEncode=Te,window.lnCore.hashSortDecode=ke,window.lnCore.hashFilterEncode=xe,window.lnCore.hashFilterDecode=Ie);function H(e){return String(e||"").trim().toLowerCase()}function Oe(e){const t=H(e);return t?t.split(/\s+/).filter(Boolean):[]}function Le(e){if(e==null)return null;const t=String(e).split(",").map(n=>n.trim()).filter(Boolean);return t.length?t:null}function Re(e,t){if(!t||t.length===0)return!0;if(!e)return!1;const n=String(e).toLowerCase();for(let r=0;r<t.length;r++)if(n.indexOf(t[r])===-1)return!1;return!0}function De(e){return!e||e.length===0?"":e.join(" ").replace(/\s+/g," ").trim().toLowerCase()}(function(){const e="data-ln-search",t="lnSearch",n="data-ln-search-for",r="lnSearchControl",o="data-ln-search-items",i="data-ln-search-fields",l="data-ln-search-exclude",d="data-ln-search-hide",a="data-ln-hash";if(window[t]!==void 0)return;const f={"data-ln-search":{type:"string",effect:E,description:"Active search query term on target element or container"},"data-ln-hash":{type:"string",effect:E,description:"URL hash routing key for search state persistence"},"data-ln-search-for":{prop:"targetId",type:"string",read:L,fallback:null,description:"Target table or list element ID that this input controls"},"data-ln-search-fields":{type:"list",description:"Comma-separated field names to include in client search"},"data-ln-search-items":{type:"string",description:"CSS selector matching searchable child items"},"data-ln-search-exclude":{type:"string",description:"CSS selector matching child items to exclude from search"},"data-ln-search-hide":{type:"enum",values:["collapse","none"],fallback:"collapse",description:"CSS hiding strategy for non-matching rows"},"data-ln-search-clear-for":{type:"trigger",description:"Click trigger to clear search input for target"}},p=ge(f);function h(u){const c=N(u,"search");if(c)return c;if(u.id){const s=document.querySelector("["+n+'="'+u.id+'"]');if(s){const w=N(s,"search");if(w)return w}}return null}function b(u){return u.matches("input, textarea")?u:u.querySelector("input, textarea")}function x(u,c){const s=u.childNodes;for(let w=0;w<s.length;w++){const m=s[w];if(m.nodeType===3){c.push(m.nodeValue);continue}m.nodeType===1&&(m.hasAttribute(l)||x(m,c))}}function F(u){if(u._lnSearchText!==void 0)return u._lnSearchText;const c=[];x(u,c);const s=De(c);return u._lnSearchText=s,s}function _(u,c){if(!u.id)return;const s=document.querySelectorAll("["+n+'="'+u.id+'"]');for(const w of s){const m=b(w);m&&m.value!==c&&(m.value=c)}}function S(u){this.dom=u,this.term=u.getAttribute(e)||"",this._destroyed=!1;const c=this;return this.nsKey=h(u),this.hashEnabled=!!this.nsKey,this._onHashChange=function(){if(c._destroyed||!c.hashEnabled)return;const s=B(c.nsKey),w=c.dom.getAttribute(e)||"";s!==null&&s!==w?c.dom.setAttribute(e,s):s===null&&w!==""&&c.dom.setAttribute(e,"")},this.hashEnabled&&window.addEventListener("hashchange",this._onHashChange),q(function(){if(!c._destroyed){if(c.hashEnabled){const s=B(c.nsKey);if(s!==null&&s!==c.term){c.term=s,c.dom.setAttribute(e,s),_(c.dom,s),c._apply();return}}H(c.term)&&(_(c.dom,c.term),c._apply())}}),this}S.prototype._apply=function(){const u=this.dom,c=H(this.term),s=Oe(c);this.hashEnabled&&Y(this.nsKey,this.term?this.term:null);const w=Le(u.getAttribute(i));if(ee(u,"ln-search:change",{term:c,tokens:s,targetId:u.id,fields:w}).defaultPrevented)return;const A=u.getAttribute(o),g=A?u.querySelectorAll(A):u.children;for(let C=0;C<g.length;C++){const y=g[C];if(y.removeAttribute(d),y.hasAttribute(l)||s.length===0)continue;const T=F(y);Re(T,s)||y.setAttribute(d,"true")}},S.prototype.destroy=function(){this.dom[t]&&(this._destroyed=!0,this.hashEnabled&&this._onHashChange&&window.removeEventListener("hashchange",this._onHashChange),delete this.dom[t])};function v(u){if(this.dom=u,pe(this,u,p),this.input=b(u),this._attachHandler(),this.input&&this.input.value.trim()){const c=this;q(function(){const s=document.getElementById(c.targetId);s&&((s.getAttribute(e)||"").trim()||c._write(c.input.value))})}return this}v.prototype._write=function(u){const c=document.getElementById(this.targetId);c&&c.getAttribute(e)!==u&&c.setAttribute(e,u)},v.prototype._attachHandler=function(){if(!this.input)return;const u=this;this._onInput=function(){u._write(u.input.value)},this.input.addEventListener("input",this._onInput)},v.prototype.destroy=function(){this.dom[r]&&(this.input&&this._onInput&&this.input.removeEventListener("input",this._onInput),delete this.dom[r])};function M(u){const c=u.getAttribute("data-ln-search-clear-for");if(c){const g=document.getElementById(c),C=document.querySelector("["+n+'="'+c+'"]'),y=C?b(C):null;return{target:g,input:y}}const s=u.closest("["+e+"]");if(s){const g=s.id?document.querySelector("["+n+'="'+s.id+'"]'):null,C=g?b(g):null;return{target:s,input:C}}const w=u.closest("[data-ln-table-source], [data-ln-list-source]");if(w){const g=w.getAttribute("data-ln-table-source")||w.getAttribute("data-ln-list-source"),C=g?document.getElementById(g):null;if(C&&C.hasAttribute(e)){const y=document.querySelector("["+n+'="'+g+'"]'),T=y?b(y):null;return{target:C,input:T}}}const m=u.closest("["+n+"]");if(m){const g=m.getAttribute(n),C=g?document.getElementById(g):null,y=b(m);return{target:C,input:y}}const A=u.parentElement;if(A){const g=A.querySelector("["+n+"]");if(g){const C=g.getAttribute(n),y=C?document.getElementById(C):null,T=b(g);return{target:y,input:T}}}return{target:null,input:null}}document.addEventListener("click",function(u){const c=u.target.closest("[data-ln-search-clear], [data-ln-search-clear-for]");if(!c)return;const s=M(c);!s.target&&!s.input||(u.preventDefault(),s.input&&(s.input.value="",s.input.focus()),s.target&&s.target.setAttribute(e,""))});function E(u,c){const s=u[t];if(!s||s._destroyed)return;if(c===a){s._onHashChange&&window.removeEventListener("hashchange",s._onHashChange),s.nsKey=h(u),s.hashEnabled=!!s.nsKey,s.hashEnabled&&window.addEventListener("hashchange",s._onHashChange);return}const w=u.getAttribute(e)||"";w!==s.term&&(s.term=w,_(u,w),s._apply())}G(e,t,S,"ln-search",{attributes:f,onSubtreeChange:function(u,c){const s=c.target;s&&s._lnSearchText!==void 0&&delete s._lnSearchText,s&&s.parentElement&&s.parentElement._lnSearchText!==void 0&&delete s.parentElement._lnSearchText},persist:{attr:e,hashActive:function(u){return!!h(u)}}}),G(n,r,v,"ln-search-control")})()})();
|
|
1
|
+
(function(){"use strict";function W(){return typeof window>"u"?!1:window.lnDebug===!0||window.lnCore&&window.lnCore._debugSink?!0:typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug")||document.body.querySelector("[data-ln-debug]")!==null:!1}function O(e){if(typeof window>"u")return!1;if(window.lnDebug===!0)return!0;if(window.lnCore&&window.lnCore._debugIsContained)return e?window.lnCore._debugIsContained(e):window.lnCore._debugSink!==null;if(e&&e.closest){const t=e.closest("[data-ln-debug]");return!(!t||typeof document<"u"&&t===document.documentElement)}return typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug"):!1}if(typeof window<"u"&&(window.lnCore=window.lnCore||{},!window.lnCore._warnBound)){window.lnCore._warnBound=!0;const e=console.warn;console.warn=function(...t){if(typeof t[0]=="string"&&(t[0].startsWith("[ln-")||t[0].startsWith("[lnCore"))){let r=null;for(let l=1;l<t.length;l++)if(t[l]&&t[l].nodeType===1){r=t[l];break}if(!O(r))return}e.apply(console,t)}}function se(e,t,n){const r=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,r),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:r}))}function ce(e,t,n){const r=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,r);const l=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:r});return e.dispatchEvent(l),l}function E(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){E(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}function ae(e){const t=e.querySelector('input[name="_method"]');return((t&&t.value!==""?t.value:e.method)||"").toUpperCase()}function ue(e,t){const n=!!(t&&t.typed),r=t&&t.exclude,l={},s=e.elements,i={};if(n)for(let c=0;c<s.length;c++){const o=s[c];o.name&&o.type==="checkbox"&&!o.disabled&&(i[o.name]=(i[o.name]||0)+1)}for(let c=0;c<s.length;c++){const o=s[c];if(!(!o.name||o.disabled||o.type==="file"||o.type==="submit"||o.type==="button")&&!(r&&o.matches&&o.matches(r)))if(o.type==="checkbox")n&&i[o.name]===1?l[o.name]=o.checked:(l[o.name]||(l[o.name]=[]),o.checked&&l[o.name].push(o.value));else if(o.type==="radio")o.checked&&(l[o.name]=o.value);else if(o.type==="select-multiple"){l[o.name]=[];for(let f=0;f<o.options.length;f++)o.options[f].selected&&l[o.name].push(o.options[f].value)}else if(n&&o.type==="hidden")l[o.name]=o.value;else if(n&&(o.type==="number"||o.type==="range")){const f=Number(o.value);l[o.name]=o.value===""||isNaN(f)?null:f}else l[o.name]=o.value}return l}function de(e){if(typeof e!="string")return!!e;const t=e.trim().toLowerCase();return t!=="false"&&t!=="0"&&t!==""&&t!=="off"&&t!=="no"}function fe(e,t){const n=e.elements,r=[],l={};for(let s=0;s<n.length;s++){const i=n[s];i.name&&i.type==="checkbox"&&(l[i.name]=(l[i.name]||0)+1)}for(let s=0;s<n.length;s++){const i=n[s];if(i.type==="file"||i.type==="submit"||i.type==="button")continue;const c=i.getAttribute("data-ln-fill-as")||i.name;if(!c||!(c in t))continue;const o=t[c];if(i.type==="checkbox"){if(Array.isArray(o))i.checked=o.indexOf(i.value)!==-1;else if(l[i.name]>1){const f=String(o).split(",").map(function(w){return w.trim()});i.checked=f.indexOf(i.value)!==-1}else i.checked=de(o);r.push(i)}else if(i.type==="radio")i.checked=i.value===String(o),r.push(i);else if(i.type==="select-multiple"){if(Array.isArray(o))for(let f=0;f<i.options.length;f++)i.options[f].selected=o.indexOf(i.options[f].value)!==-1;r.push(i)}else i.value=o,i.tagName==="SELECT"&&o!=null&&i.setAttribute("data-ln-value",o),r.push(i)}return r}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.populateForm=fe,window.lnCore.serializeForm=ue,window.lnCore.resolveFormMethod=ae);const U={};function he(e,t){U[e]=t}function we(e){return U[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=he,window.lnCore.getDataMapper=we);function pe(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,E(function(){new MutationObserver(function(){se(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const V={};function ge(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];V[n]=t}function be(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return V[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=ge,window.lnCore.getLocaleFallback=be,window.lnCore.ensureLocaleObserver=pe);const L={};function me(e,t){L[e]||(L[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=L[e];return n?n.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function K(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let i=0;i<n.length;i++){const c=n[i],o=c.getAttribute("data-ln-field");t[o]!=null&&(c.textContent=t[o])}const r=e.querySelectorAll("[data-ln-attr]");for(let i=0;i<r.length;i++){const c=r[i],o=c.getAttribute("data-ln-attr").split(",");for(let f=0;f<o.length;f++){const w=o[f].trim().split(":");if(w.length!==2)continue;const h=w[0].trim(),b=w[1].trim();t[b]!=null&&c.setAttribute(h,t[b])}}const l=e.querySelectorAll("[data-ln-show]");for(let i=0;i<l.length;i++){const c=l[i],o=c.getAttribute("data-ln-show");o in t&&c.classList.toggle("hidden",!t[o])}const s=e.querySelectorAll("[data-ln-class]");for(let i=0;i<s.length;i++){const c=s[i],o=c.getAttribute("data-ln-class").split(",");for(let f=0;f<o.length;f++){const w=o[f].trim().split(":");if(w.length!==2)continue;const h=w[0].trim(),b=w[1].trim();b in t&&c.classList.toggle(h,!!t[b])}}return e}function ye(e,t){e.matches&&e.matches("[data-ln-form], [data-ln-fillable]")&&(window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",e,t??null),e.dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0})));const n=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let r=0;r<n.length;r++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",n[r],t??null),n[r].dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0}));return e}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._fillBound||(window.lnCore._fillBound=!0,document.addEventListener("ln-fill",function(e){if(!(!e.target.matches||!e.target.matches("[data-ln-fillable]")))if(e.detail)K(e.target,e.detail);else{const t=e.target.querySelectorAll("[data-ln-field]");for(let n=0;n<t.length;n++)t[n].textContent=""}})));function Q(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const s=n.currentNode;s.textContent.indexOf("{{")!==-1&&(s.textContent=s.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(i,c){return t[c]!==void 0?t[c]:""}))}const r=function(s,i){return t[i]!==void 0?t[i]:""},l=Array.from(e.querySelectorAll("*"));e.nodeType===1&&l.push(e);for(let s=0;s<l.length;s++){const i=l[s],c=i.attributes;for(let o=0;o<c.length;o++){const f=c[o];f.value.indexOf("{{")!==-1&&i.setAttribute(f.name,f.value.replace(/\{\{\s*(\w+)\s*\}\}/g,r))}}return e}function Ce(e,t,n,r,l,s){const i={};for(let o=0;o<e.children.length;o++){const f=e.children[o],w=f.getAttribute("data-ln-render-key");w&&(i[w]=f)}const c=document.createDocumentFragment();for(let o=0;o<t.length;o++){const f=t[o],w=String(r(f));let h=i[w];if(h)l(h,f,o);else{const b=me(n,s);if(!b||(Q(b,f),h=b.firstElementChild,!h))continue;h.setAttribute("data-ln-render-key",w),l(h,f,o)}c.appendChild(h)}e.textContent="",e.appendChild(c)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=Q,window.lnCore.fill=K,window.lnCore.lnFill=ye,window.lnCore.renderList=Ce);function T(e,t,n){const r=e.getAttribute(t);return r===null?n:r}function z(e,t,n){const r=parseInt(e.getAttribute(t),10);return isNaN(r)?n:r}function J(e,t,n=!1){const r=e.getAttribute(t);if(r===null)return!!n;const l=r.trim().toLowerCase();return!(l==="false"||l==="0")}function P(e,t){return(e.getAttribute(t)||"").split(",").map(n=>n.trim()).filter(Boolean)}const D=new Map;function G(e,t){const n=(e?e.join("|"):"")+"::"+t;if(D.has(n))return D.get(n);const r=new Set(e||[]),l=function(s,i){const c=s.getAttribute(i);return c!==null&&r.has(c)?c:t};return D.set(n,l),l}function X(e,t,n){const r=parseFloat(e.getAttribute(t));return isNaN(r)?n:r}function Y(e,t,n){const r=e.getAttribute(t);if(!r)return n;try{return JSON.parse(r)}catch{return n}}function Z(e,t,n,r,l){if(!e||t===null)return!0;const s=r||"ln-component",i=e.type;if(i==="trigger"||i==="marker"||i==="string"||i==="list"||!i||t===""&&e.fallback!==void 0)return!0;const c=o=>{l?console.warn(o,l):console.warn(o)};if(i==="boolean"){const o=t.trim().toLowerCase();return o!==""&&o!=="true"&&o!=="false"&&o!=="1"&&o!=="0"?(c(`[${s}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(i==="enum"){const o=e.values||[];return o.includes(t)?!0:(c(`[${s}] Invalid value "${t}" for attribute "${n}". Allowed: ${o.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(i==="integer"){if(!/^-?\d+$/.test(t))return c(`[${s}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const o=parseInt(t,10);return e.min!==void 0&&o<e.min?(c(`[${s}] Value ${o} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&o>e.max?(c(`[${s}] Value ${o} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(i==="float")return isNaN(Number(t))?(c(`[${s}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(i==="json")try{return JSON.parse(t),!0}catch(o){return c(`[${s}] Invalid JSON for attribute "${n}": ${o.message}. Fallback: ${e.fallback}.`),!1}return!0}function ee(e,t,n){for(const r in n){const[l,s,i]=n[r];Object.defineProperty(e,r,{get:function(){return l(t,s,i)},enumerable:!0,configurable:!0})}return e}function te(e){const t={};for(const n in e){const r=e[n];if(!r||!r.prop)continue;let l=r.read;if(!l&&r.type)switch(r.type){case"enum":l=G(r.values,r.fallback);break;case"integer":l=z;break;case"float":l=X;break;case"boolean":l=J;break;case"list":l=P;break;case"json":l=Y;break;case"string":default:l=T;break}l||(l=T),t[r.prop]=[l,n,r.fallback]}return t}function Ae(e){const t={};for(const n in e){const r=e[n];r&&r.effect&&(t[n]=r.effect)}return Object.keys(t).length?t:null}function _e(e){if(e.onAttrChange&&!e.declared)return null;const t=new Set;if(e.effects)for(const n in e.effects)t.add(n);if(e.onAttrChange&&e.declared)for(const n of e.declared)t.add(n);return t}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.defineAttrs=ee,window.lnCore.attrSpec=te,window.lnCore.attrStr=T,window.lnCore.attrInt=z,window.lnCore.attrBool=J,window.lnCore.attrList=P,window.lnCore.attrEnum=G,window.lnCore.attrFloat=X,window.lnCore.attrJson=Y);function R(e,t,n,r){if(e.nodeType!==1)return;const s=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",i=Array.from(e.querySelectorAll(s));e.matches&&e.matches(s)&&i.push(e);for(const c of i)if(!c[n]){window.lnCore._persistSink&&c.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(c,t);try{c[n]=new r(c)}catch(o){console.error("["+n+"] init failed",c,o)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function ve(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function q(e){typeof window<"u"?(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[],window.lnCore._bootHolds>0?window.lnCore._bootQueue.push(e):setTimeout(e,0)):e()}function ne(){window.lnCore=window.lnCore||{};const e=window.lnCore._attrRegistry=window.lnCore._attrRegistry||{byAttr:new Map,reactive:[],persist:[]};return e.byReactive=e.byReactive||new Map,e.reactiveWildcard=e.reactiveWildcard||[],e}function Se(e){const t=ne(),n=e.observed||[];for(let r=0;r<n.length;r++){const l=n[r];t.byAttr.has(l)||t.byAttr.set(l,[]),t.byAttr.get(l).push(e)}if(t.byDeclaredAttr=t.byDeclaredAttr||new Map,e.attributes)for(const r in e.attributes){t.byDeclaredAttr.has(r)||t.byDeclaredAttr.set(r,[]);const l=t.byDeclaredAttr.get(r);l.some(s=>s.componentTag===e.componentTag)||l.push({spec:e.attributes[r],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const r=_e(e);if(r===null)t.reactiveWildcard.push(e);else for(const l of r)t.byReactive.has(l)||t.byReactive.set(l,[]),t.byReactive.get(l).push(e)}e.persist&&t.persist.push(e)}function re(e,t,n,r){for(let l=0;l<e.length;l++){const s=e[l];if(!t[s.attribute])continue;const i=s.effects&&s.effects[n];i?i(t,n,r):s.onAttrChange&&(!s.declared||s.declared.has(n))&&s.onAttrChange(t,n,r)}}function xe(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const r=ne(),l=r.byAttr.get(n);if(W()&&r.byDeclaredAttr&&r.byDeclaredAttr.has(n)&&O(t)){const s=r.byDeclaredAttr.get(n),i=t.getAttribute(n);for(let c=0;c<s.length;c++)Z(s[c].spec,i,n,s[c].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||l)&&window.lnCore._debugSink("attr",n,t,{oldValue:e.oldValue,newValue:t.getAttribute(n)}),n.indexOf("data-ln-")===0){const s=r.byReactive.get(n);s&&re(s,t,n,e.oldValue),r.reactiveWildcard.length&&re(r.reactiveWildcard,t,n,e.oldValue)}if(l)for(let s=0;s<l.length;s++){const i=l[s];if(i.handler){i.handler(t,n,e.oldValue);continue}i.onAttributeChange&&t[i.attribute]?i.onAttributeChange(t,n):(R(t,i.selector,i.attribute,i.ComponentFn),i.onInit&&i.onInit(t))}}function ke(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,E(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{xe(t[n])}catch(r){console.error("[ln-core] mutation handler failed",t[n].target,r)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function oe(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function Ee(e){const t=oe();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const r=t[n];if(r.onSubtreeChange){const l=r.query,s=e.target.nodeType===1?e.target.matches(l)?e.target:e.target.closest(l):e.target.parentElement?e.target.parentElement.closest(l):null;s&&r.onSubtreeChange(s,e)}}for(let n=0;n<e.addedNodes.length;n++){const r=e.addedNodes[n];if(r.nodeType===1)for(let l=0;l<t.length;l++){const s=t[l];R(r,s.selector,s.attribute,s.ComponentFn),s.onInit&&s.onInit(r)}}for(let n=0;n<e.removedNodes.length;n++){const r=e.removedNodes[n];if(r.nodeType===1)for(let l=0;l<t.length;l++){const s=t[l],i=s.query,c=Array.from(r.querySelectorAll(i));r.matches&&r.matches(i)&&c.push(r);for(let o=0;o<c.length;o++){const f=c[o];if(!document.contains(f)){const w=f[s.attribute];if(w&&typeof w.destroy=="function")try{w.destroy()}catch(h){console.error("["+s.attribute+"] destroy failed",f,h)}delete f[s.attribute]}}}}}}function Te(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,E(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const r=t[n];if(r.type==="childList")try{Ee(r)}catch(l){console.error("[ln-core] lifecycle handler failed",r.target,l)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function B(e,t,n,r,l={}){const s=l.extraAttributes||[],i=l.onAttributeChange||null,c=l.onSubtreeChange||null,o=l.onInit||null,f=l.onAttrChange||null,w=l.effects||null,h=l.attributes||null,b=h?Ae(h):w,I=h?new Set(Object.keys(h)):null,H=l.persist||null;function _(d){const u=d||document.body;if(R(u,e,t,n),h&&W())for(const a in h){const p=h[a],m=Array.from(u.querySelectorAll("["+a+"]"));u.matches&&u.matches("["+a+"]")&&m.push(u);for(let A=0;A<m.length;A++){const g=m[A];O(g)&&Z(p,g.getAttribute(a),a,r,g)}}o&&o(u)}const v=[];if(e.indexOf("[")!==-1){const d=/\[([\w-]+)/g;let u;for(;(u=d.exec(e))!==null;)v.push(u[1])}else v.push(e);Se({selector:e,attribute:t,componentTag:r,attributes:h,ComponentFn:n,onInit:o,observed:v.concat(s),onAttributeChange:i,onAttrChange:f,effects:b,declared:I,persist:H}),ke();const j=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";oe().push({selector:e,attribute:t,ComponentFn:n,onInit:o,onSubtreeChange:c,query:j}),Te(),window[t]=_,typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerComponent=B);function x(){ve()>0?q(function(){_(document.body)}):_(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",x):x(),_}function ie(e){return(e||"").replace(/^#/,"")}function $(e){const t=e===void 0?location.hash:e,n={},r=ie(t);if(!r)return n;const l=r.split("&");for(let s=0;s<l.length;s++){const i=l[s];if(!i)continue;const c=i.indexOf(":"),o=c>-1?i.slice(0,c):i,f=c>-1?i.slice(c+1):"";if(o)try{n[o]=decodeURIComponent(f)}catch{n[o]=f}}return n}function F(e){if(!e)return null;const t=$();return e in t?t[e]:null}function le(e,t){if(!e)return;const n=$();t==null?delete n[e]:n[e]=String(t);const l=Object.keys(n).map(function(s){const i=n[s];return i===""?s:s+":"+encodeURIComponent(i)}).join("&");ie(location.hash)!==l&&(location.hash=l)}function Ie(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function N(e,t){if(!e||!e.hasAttribute("data-ln-hash"))return null;const n=e.getAttribute("data-ln-hash");if(n&&n.trim()!=="")return n.trim();const r=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return r?t?r+"-"+t:r:t||null}function Oe(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function Le(e){return!e||typeof e!="string"?null:e.endsWith(".asc")?{fieldOrColumn:e.slice(0,-4),direction:"asc"}:e.endsWith(".desc")?{fieldOrColumn:e.slice(0,-5),direction:"desc"}:null}function De(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function Re(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const n=e.slice(0,t),r=e.slice(t+1),l=r?r.split(",").map(function(s){try{return decodeURIComponent(s)}catch{return s}}).filter(Boolean):[];return{key:n,values:l}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=$,window.lnCore.hashGet=F,window.lnCore.hashSet=le,window.lnCore.hashLinkClick=Ie,window.lnCore.resolveHashNamespace=N,window.lnCore.hashSortEncode=Oe,window.lnCore.hashSortDecode=Le,window.lnCore.hashFilterEncode=De,window.lnCore.hashFilterDecode=Re);function qe(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.uuid=qe);function M(e){return String(e||"").trim().toLowerCase()}function Be(e){const t=M(e);return t?t.split(/\s+/).filter(Boolean):[]}function $e(e){if(e==null)return null;const t=String(e).split(",").map(n=>n.trim()).filter(Boolean);return t.length?t:null}function Fe(e,t){if(!t||t.length===0)return!0;if(!e)return!1;const n=String(e).toLowerCase();for(let r=0;r<t.length;r++)if(n.indexOf(t[r])===-1)return!1;return!0}function Ne(e){return!e||e.length===0?"":e.join(" ").replace(/\s+/g," ").trim().toLowerCase()}(function(){const e="data-ln-search",t="lnSearch",n="data-ln-search-for",r="lnSearchControl",l="data-ln-search-items",s="data-ln-search-fields",i="data-ln-search-exclude",c="data-ln-search-hide",o="data-ln-hash";if(window[t]!==void 0)return;const f={"data-ln-search":{type:"string",effect:x,description:"Active search query term on target element or container"},"data-ln-hash":{type:"string",effect:x,description:"URL hash routing key for search state persistence"},"data-ln-search-for":{prop:"targetId",type:"string",read:T,fallback:null,description:"Target table or list element ID that this input controls"},"data-ln-search-fields":{type:"list",description:"Comma-separated field names to include in client search"},"data-ln-search-items":{type:"string",description:"CSS selector matching searchable child items"},"data-ln-search-exclude":{type:"string",description:"CSS selector matching child items to exclude from search"},"data-ln-search-hide":{type:"enum",values:["collapse","none"],fallback:"collapse",description:"CSS hiding strategy for non-matching rows"},"data-ln-search-clear-for":{type:"trigger",description:"Click trigger to clear search input for target"}},w=te(f);function h(d){const u=N(d,"search");if(u)return u;if(d.id){const a=document.querySelector("["+n+'="'+d.id+'"]');if(a){const p=N(a,"search");if(p)return p}}return null}function b(d){return d.matches("input, textarea")?d:d.querySelector("input, textarea")}function I(d,u){const a=d.childNodes;for(let p=0;p<a.length;p++){const m=a[p];if(m.nodeType===3){u.push(m.nodeValue);continue}m.nodeType===1&&(m.hasAttribute(i)||I(m,u))}}function H(d){if(d._lnSearchText!==void 0)return d._lnSearchText;const u=[];I(d,u);const a=Ne(u);return d._lnSearchText=a,a}function _(d,u){if(!d.id)return;const a=document.querySelectorAll("["+n+'="'+d.id+'"]');for(const p of a){const m=b(p);m&&m.value!==u&&(m.value=u)}}function v(d){this.dom=d,this.term=d.getAttribute(e)||"",this._destroyed=!1;const u=this;return this.nsKey=h(d),this.hashEnabled=!!this.nsKey,this._onHashChange=function(){if(u._destroyed||!u.hashEnabled)return;const a=F(u.nsKey),p=u.dom.getAttribute(e)||"";a!==null&&a!==p?u.dom.setAttribute(e,a):a===null&&p!==""&&u.dom.setAttribute(e,"")},this.hashEnabled&&window.addEventListener("hashchange",this._onHashChange),q(function(){if(!u._destroyed){if(u.hashEnabled){const a=F(u.nsKey);if(a!==null&&a!==u.term){u.term=a,u.dom.setAttribute(e,a),_(u.dom,a),u._apply();return}}M(u.term)&&(_(u.dom,u.term),u._apply())}}),this}v.prototype._apply=function(){const d=this.dom,u=M(this.term),a=Be(u);this.hashEnabled&&le(this.nsKey,this.term?this.term:null);const p=$e(d.getAttribute(s));if(ce(d,"ln-search:change",{term:u,tokens:a,targetId:d.id,fields:p}).defaultPrevented)return;const A=d.getAttribute(l),g=A?d.querySelectorAll(A):d.children;for(let y=0;y<g.length;y++){const C=g[y];if(C.removeAttribute(c),C.hasAttribute(i)||a.length===0)continue;const k=H(C);Fe(k,a)||C.setAttribute(c,"true")}},v.prototype.destroy=function(){this.dom[t]&&(this._destroyed=!0,this.hashEnabled&&this._onHashChange&&window.removeEventListener("hashchange",this._onHashChange),delete this.dom[t])};function S(d){if(this.dom=d,ee(this,d,w),this.input=b(d),this._attachHandler(),this.input&&this.input.value.trim()){const u=this;q(function(){const a=document.getElementById(u.targetId);a&&((a.getAttribute(e)||"").trim()||u._write(u.input.value))})}return this}S.prototype._write=function(d){const u=document.getElementById(this.targetId);u&&u.getAttribute(e)!==d&&u.setAttribute(e,d)},S.prototype._attachHandler=function(){if(!this.input)return;const d=this;this._onInput=function(){d._write(d.input.value)},this.input.addEventListener("input",this._onInput)},S.prototype.destroy=function(){this.dom[r]&&(this.input&&this._onInput&&this.input.removeEventListener("input",this._onInput),delete this.dom[r])};function j(d){const u=d.getAttribute("data-ln-search-clear-for");if(u){const g=document.getElementById(u),y=document.querySelector("["+n+'="'+u+'"]'),C=y?b(y):null;return{target:g,input:C}}const a=d.closest("["+e+"]");if(a){const g=a.id?document.querySelector("["+n+'="'+a.id+'"]'):null,y=g?b(g):null;return{target:a,input:y}}const p=d.closest("[data-ln-table-source], [data-ln-list-source]");if(p){const g=p.getAttribute("data-ln-table-source")||p.getAttribute("data-ln-list-source"),y=g?document.getElementById(g):null;if(y&&y.hasAttribute(e)){const C=document.querySelector("["+n+'="'+g+'"]'),k=C?b(C):null;return{target:y,input:k}}}const m=d.closest("["+n+"]");if(m){const g=m.getAttribute(n),y=g?document.getElementById(g):null,C=b(m);return{target:y,input:C}}const A=d.parentElement;if(A){const g=A.querySelector("["+n+"]");if(g){const y=g.getAttribute(n),C=y?document.getElementById(y):null,k=b(g);return{target:C,input:k}}}return{target:null,input:null}}document.addEventListener("click",function(d){const u=d.target.closest("[data-ln-search-clear], [data-ln-search-clear-for]");if(!u)return;const a=j(u);!a.target&&!a.input||(d.preventDefault(),a.input&&(a.input.value="",a.input.focus()),a.target&&a.target.setAttribute(e,""))});function x(d,u){const a=d[t];if(!a||a._destroyed)return;if(u===o){a._onHashChange&&window.removeEventListener("hashchange",a._onHashChange),a.nsKey=h(d),a.hashEnabled=!!a.nsKey,a.hashEnabled&&window.addEventListener("hashchange",a._onHashChange);return}const p=d.getAttribute(e)||"";p!==a.term&&(a.term=p,_(d,p),a._apply())}B(e,t,v,"ln-search",{attributes:f,onSubtreeChange:function(d,u){const a=u.target;a&&a._lnSearchText!==void 0&&delete a._lnSearchText,a&&a.parentElement&&a.parentElement._lnSearchText!==void 0&&delete a.parentElement._lnSearchText},persist:{attr:e,hashActive:function(d){return!!h(d)}}}),B(n,r,S,"ln-search-control")})()})();
|