@livenetworks/ashlar 2.1.13 → 2.2.0
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/components/COMPONENTS.md +80 -80
- package/components/ln-api-connector/ln-api-connector.schema.json +105 -105
- package/components/ln-chart/ln-chart-dev.scss +0 -4
- package/components/ln-core/README.md +21 -21
- package/components/ln-core/ln-core.schema.json +120 -120
- package/components/ln-core/query.js +113 -113
- package/components/ln-data-coordinator/README.md +2 -0
- package/components/ln-data-coordinator/ln-data-coordinator.js +1 -1
- package/components/ln-data-coordinator/ln-data-coordinator.schema.json +196 -196
- package/components/ln-data-coordinator/src/ln-data-coordinator.js +457 -432
- package/components/ln-data-store/ln-data-store.schema.json +92 -92
- package/components/ln-date/ln-date.js +1 -1
- package/components/ln-date/src/ln-date.js +1 -1
- package/components/ln-debug/ln-debug.js +1 -1
- package/components/ln-debug/ln-debug.schema.json +28 -28
- package/components/ln-debug/src/generated-attributes.js +304 -302
- package/components/ln-dropdown/ln-dropdown.schema.json +90 -90
- package/components/ln-external-links/README.md +19 -19
- package/components/ln-external-links/ln-external-links.js +1 -1
- package/components/ln-external-links/ln-external-links.schema.json +9 -0
- package/components/ln-external-links/ln-external-links.scss +12 -0
- package/components/ln-external-links/src/ln-external-links.js +3 -2
- package/components/ln-filter/README.md +2 -1
- package/components/ln-filter/ln-filter.js +1 -1
- package/components/ln-filter/ln-filter.schema.json +157 -169
- package/components/ln-filter/src/ln-filter.js +1 -3
- package/components/ln-form/ln-form.schema.json +57 -57
- package/components/ln-key/ln-key.js +1 -1
- package/components/ln-key/ln-key.schema.json +8 -0
- package/components/ln-key/src/ln-key.js +8 -5
- package/components/ln-obfuscator/ln-obfuscator.js +1 -1
- package/components/ln-obfuscator/ln-obfuscator.schema.json +6 -0
- package/components/ln-obfuscator/src/ln-obfuscator.js +2 -2
- package/components/ln-options/ln-options.schema.json +47 -47
- package/components/ln-popover/ln-popover.schema.json +82 -82
- package/components/ln-progress/ln-progress.schema.json +0 -13
- package/components/ln-search/ln-search.schema.json +111 -111
- package/components/ln-sort/README.md +1 -0
- package/components/ln-sort/ln-sort.js +1 -1
- package/components/ln-sort/ln-sort.schema.json +100 -112
- package/components/ln-sort/src/sort-model.js +1 -6
- package/components/ln-sortable/ln-sortable.schema.json +38 -38
- package/components/ln-sortable/ln-sortable.scss +25 -25
- package/components/ln-table/README.md +8 -1
- package/components/ln-table/ln-table.js +1 -1
- package/components/ln-table/ln-table.schema.json +195 -195
- package/components/ln-table/src/ln-table.js +43 -7
- package/components/ln-table-coordinator/ln-table-coordinator.js +1 -1
- package/components/ln-table-coordinator/ln-table-coordinator.schema.json +18 -0
- package/components/ln-table-coordinator/src/ln-table-coordinator.js +104 -2
- package/components/ln-toast/README.md +1 -1
- package/components/ln-toast/ln-toast.js +1 -1
- package/components/ln-toast/ln-toast.schema.json +6 -0
- package/components/ln-toast/src/ln-toast.js +1 -1
- package/components/ln-validate/README.md +6 -6
- package/components/ln-validate/ln-validate-dev.scss +0 -5
- package/components/ln-validate/ln-validate.js +1 -1
- package/components/ln-validate/ln-validate.schema.json +0 -6
- package/components/ln-validate/src/ln-validate.js +48 -29
- package/ln-ashlar.html-data.json +1824 -1814
- package/package.json +95 -95
- package/theme/README.md +7 -6
- package/theme/base/_global.scss +2 -2
- package/theme/base/_reset.scss +11 -0
- package/theme/components/_app-shell.scss +0 -8
- package/theme/components/_form.scss +6 -6
- package/theme/components/_ln-table.scss +6 -3
- package/theme/components/_scrollbar.scss +5 -4
- package/theme/components/_sortable.scss +19 -19
- package/theme/components/_stat-card.scss +1 -0
- package/theme/components/_table.scss +10 -33
- package/theme/config/_density.scss +236 -104
- package/theme/config/_palette.scss +71 -49
- package/theme/config/_theme.scss +27 -31
- package/theme/config/_tokens.scss +28 -26
- package/theme/config/mixins/_app-shell.scss +17 -21
- package/theme/config/mixins/_btn.scss +4 -2
- package/theme/config/mixins/_card.scss +87 -67
- package/theme/config/mixins/_colors.scss +2 -1
- package/theme/config/mixins/_display.scss +12 -0
- package/theme/config/mixins/_footer.scss +3 -1
- package/theme/config/mixins/_form.scss +48 -16
- package/theme/config/mixins/_layout.scss +9 -0
- package/theme/config/mixins/_ln-table.scss +22 -15
- package/theme/config/mixins/_modal.scss +3 -0
- package/theme/config/mixins/_nav.scss +27 -4
- package/theme/config/mixins/_sidebar.scss +2 -1
- package/theme/config/mixins/_stat-card.scss +33 -2
- package/theme/config/mixins/_status-badge.scss +1 -0
- package/theme/config/mixins/_table.scss +81 -39
- package/theme/config/mixins/_tabs.scss +31 -0
- package/theme/config/mixins/_toast.scss +0 -1
- package/theme/designs/relaxed/_card.scss +12 -0
- package/theme/designs/relaxed/_layout.scss +24 -0
- package/theme/ln-ashlar-core.scss +1 -0
- package/theme/ln-ashlar-dev.scss +0 -1
- package/theme/ln-ashlar-relaxed.scss +67 -0
- package/theme/tailwind/components.css +561 -24
- package/theme/utilities/_utilities.scss +1 -11
- package/web-types.json +2351 -2337
- package/components/ln-progress/ln-progress-dev.scss +0 -8
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$comment": "This file is partly generated. The $comment, component, generator, and each attribute's direction, sources, type, values, fallback, and description keys are owned by the generator and synchronized from JS source code. All other keys are preserved and safe to hand-author. Regenerate with `npm run sync:ln-schemas`.",
|
|
3
|
-
"component": "ln-dropdown",
|
|
4
|
-
"generator": "scripts/sync-ln-schemas.mjs",
|
|
5
|
-
"attributes": {
|
|
6
|
-
"data-ln-debug": {
|
|
7
|
-
"direction": null,
|
|
8
|
-
"sources": [
|
|
9
|
-
"ln-dropdown-dev.scss"
|
|
10
|
-
]
|
|
11
|
-
},
|
|
12
|
-
"data-ln-dropdown": {
|
|
13
|
-
"direction": null,
|
|
14
|
-
"sources": [
|
|
15
|
-
"ln-dropdown-dev.scss",
|
|
16
|
-
"ln-dropdown.scss",
|
|
17
|
-
"src/ln-dropdown.js"
|
|
18
|
-
],
|
|
19
|
-
"type": "marker",
|
|
20
|
-
"description": "Initializes the dropdown container"
|
|
21
|
-
},
|
|
22
|
-
"data-ln-dropdown-menu": {
|
|
23
|
-
"direction": "runtime",
|
|
24
|
-
"sources": [
|
|
25
|
-
"ln-dropdown.scss",
|
|
26
|
-
"src/ln-dropdown.js"
|
|
27
|
-
],
|
|
28
|
-
"type": "marker",
|
|
29
|
-
"description": "Dropdown menu element containing items"
|
|
30
|
-
},
|
|
31
|
-
"data-ln-dropdown-placement": {
|
|
32
|
-
"direction": "both",
|
|
33
|
-
"sources": [
|
|
34
|
-
"src/ln-dropdown.js"
|
|
35
|
-
],
|
|
36
|
-
"type": "enum",
|
|
37
|
-
"values": [
|
|
38
|
-
"top",
|
|
39
|
-
"top-start",
|
|
40
|
-
"top-end",
|
|
41
|
-
"bottom",
|
|
42
|
-
"bottom-start",
|
|
43
|
-
"bottom-end",
|
|
44
|
-
"left",
|
|
45
|
-
"left-start",
|
|
46
|
-
"left-end",
|
|
47
|
-
"right",
|
|
48
|
-
"right-start",
|
|
49
|
-
"right-end"
|
|
50
|
-
],
|
|
51
|
-
"description": "Active calculated placement applied at runtime"
|
|
52
|
-
},
|
|
53
|
-
"data-ln-dropdown-position": {
|
|
54
|
-
"direction": null,
|
|
55
|
-
"sources": [
|
|
56
|
-
"src/ln-dropdown.js"
|
|
57
|
-
],
|
|
58
|
-
"type": "enum",
|
|
59
|
-
"values": [
|
|
60
|
-
"top",
|
|
61
|
-
"top-start",
|
|
62
|
-
"top-end",
|
|
63
|
-
"bottom",
|
|
64
|
-
"bottom-start",
|
|
65
|
-
"bottom-end",
|
|
66
|
-
"left",
|
|
67
|
-
"left-start",
|
|
68
|
-
"left-end",
|
|
69
|
-
"right",
|
|
70
|
-
"right-start",
|
|
71
|
-
"right-end"
|
|
72
|
-
],
|
|
73
|
-
"description": "Preferred positioning anchor"
|
|
74
|
-
},
|
|
75
|
-
"data-ln-toggle": {
|
|
76
|
-
"direction": "both",
|
|
77
|
-
"sources": [
|
|
78
|
-
"ln-dropdown-dev.scss",
|
|
79
|
-
"src/ln-dropdown.js"
|
|
80
|
-
]
|
|
81
|
-
},
|
|
82
|
-
"data-ln-toggle-for": {
|
|
83
|
-
"direction": "author",
|
|
84
|
-
"sources": [
|
|
85
|
-
"ln-dropdown-dev.scss",
|
|
86
|
-
"src/ln-dropdown.js"
|
|
87
|
-
]
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$comment": "This file is partly generated. The $comment, component, generator, and each attribute's direction, sources, type, values, fallback, and description keys are owned by the generator and synchronized from JS source code. All other keys are preserved and safe to hand-author. Regenerate with `npm run sync:ln-schemas`.",
|
|
3
|
+
"component": "ln-dropdown",
|
|
4
|
+
"generator": "scripts/sync-ln-schemas.mjs",
|
|
5
|
+
"attributes": {
|
|
6
|
+
"data-ln-debug": {
|
|
7
|
+
"direction": null,
|
|
8
|
+
"sources": [
|
|
9
|
+
"ln-dropdown-dev.scss"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"data-ln-dropdown": {
|
|
13
|
+
"direction": null,
|
|
14
|
+
"sources": [
|
|
15
|
+
"ln-dropdown-dev.scss",
|
|
16
|
+
"ln-dropdown.scss",
|
|
17
|
+
"src/ln-dropdown.js"
|
|
18
|
+
],
|
|
19
|
+
"type": "marker",
|
|
20
|
+
"description": "Initializes the dropdown container"
|
|
21
|
+
},
|
|
22
|
+
"data-ln-dropdown-menu": {
|
|
23
|
+
"direction": "runtime",
|
|
24
|
+
"sources": [
|
|
25
|
+
"ln-dropdown.scss",
|
|
26
|
+
"src/ln-dropdown.js"
|
|
27
|
+
],
|
|
28
|
+
"type": "marker",
|
|
29
|
+
"description": "Dropdown menu element containing items"
|
|
30
|
+
},
|
|
31
|
+
"data-ln-dropdown-placement": {
|
|
32
|
+
"direction": "both",
|
|
33
|
+
"sources": [
|
|
34
|
+
"src/ln-dropdown.js"
|
|
35
|
+
],
|
|
36
|
+
"type": "enum",
|
|
37
|
+
"values": [
|
|
38
|
+
"top",
|
|
39
|
+
"top-start",
|
|
40
|
+
"top-end",
|
|
41
|
+
"bottom",
|
|
42
|
+
"bottom-start",
|
|
43
|
+
"bottom-end",
|
|
44
|
+
"left",
|
|
45
|
+
"left-start",
|
|
46
|
+
"left-end",
|
|
47
|
+
"right",
|
|
48
|
+
"right-start",
|
|
49
|
+
"right-end"
|
|
50
|
+
],
|
|
51
|
+
"description": "Active calculated placement applied at runtime"
|
|
52
|
+
},
|
|
53
|
+
"data-ln-dropdown-position": {
|
|
54
|
+
"direction": null,
|
|
55
|
+
"sources": [
|
|
56
|
+
"src/ln-dropdown.js"
|
|
57
|
+
],
|
|
58
|
+
"type": "enum",
|
|
59
|
+
"values": [
|
|
60
|
+
"top",
|
|
61
|
+
"top-start",
|
|
62
|
+
"top-end",
|
|
63
|
+
"bottom",
|
|
64
|
+
"bottom-start",
|
|
65
|
+
"bottom-end",
|
|
66
|
+
"left",
|
|
67
|
+
"left-start",
|
|
68
|
+
"left-end",
|
|
69
|
+
"right",
|
|
70
|
+
"right-start",
|
|
71
|
+
"right-end"
|
|
72
|
+
],
|
|
73
|
+
"description": "Preferred positioning anchor"
|
|
74
|
+
},
|
|
75
|
+
"data-ln-toggle": {
|
|
76
|
+
"direction": "both",
|
|
77
|
+
"sources": [
|
|
78
|
+
"ln-dropdown-dev.scss",
|
|
79
|
+
"src/ln-dropdown.js"
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
"data-ln-toggle-for": {
|
|
83
|
+
"direction": "author",
|
|
84
|
+
"sources": [
|
|
85
|
+
"ln-dropdown-dev.scss",
|
|
86
|
+
"src/ln-dropdown.js"
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Runs globally across `document.body` watching all `<a>` and `<area>` elements. When an element's `href` host
|
|
4
4
|
> differs from `window.location.host`, it sets `target="_blank"`, merges `rel="noopener noreferrer"`,
|
|
5
|
-
> appends a `<span
|
|
5
|
+
> appends a `<span data-ln-external-links-hint>(opens in new tab)</span>`, and marks it with `data-ln-external-link="processed"` to prevent redundant mutations.
|
|
6
6
|
|
|
7
7
|
## Markup anatomy
|
|
8
8
|
|
|
@@ -20,7 +20,7 @@ story:
|
|
|
20
20
|
rel="noopener noreferrer"
|
|
21
21
|
data-ln-external-link="processed">
|
|
22
22
|
Read more
|
|
23
|
-
<span
|
|
23
|
+
<span data-ln-external-links-hint>(opens in new tab)</span>
|
|
24
24
|
</a>
|
|
25
25
|
|
|
26
26
|
<!-- Internal links — no change at all -->
|
|
@@ -34,7 +34,7 @@ Four edits happen on each external link, in this order:
|
|
|
34
34
|
|
|
35
35
|
1. `target="_blank"` — opens in a new tab.
|
|
36
36
|
2. `rel="noopener noreferrer"` — security + privacy.
|
|
37
|
-
3. A `<span
|
|
37
|
+
3. A `<span data-ln-external-links-hint>(opens in new tab)</span>` is appended
|
|
38
38
|
inside the anchor as the last child. Sighted users see no
|
|
39
39
|
change; assistive tech announces "Read more, opens in new tab"
|
|
40
40
|
when the link receives focus. WCAG 2.4.4 ("Link Purpose")
|
|
@@ -51,15 +51,15 @@ Four edits happen on each external link, in this order:
|
|
|
51
51
|
| Has this link been processed? | `data-ln-external-link="processed"` on the `<a>` / `<area>` | ln-external-links (writes once, reads on every pass) |
|
|
52
52
|
| Should it open in a new tab? | `target="_blank"` on the link | ln-external-links (writes) |
|
|
53
53
|
| Security / privacy posture | `rel="noopener noreferrer"` on the link | ln-external-links (writes — merges with prior `rel`) |
|
|
54
|
-
| Screen-reader hint | `<span
|
|
54
|
+
| Screen-reader hint | `<span data-ln-external-links-hint>` appended inside the link | ln-external-links (creates and appends) |
|
|
55
55
|
| Click event delivery | `document.body` click delegate | ln-external-links (one listener for the whole page) |
|
|
56
56
|
| Decoration of new DOM | `MutationObserver` on `document.body`, `childList: true, subtree: true, attributes: true, attributeFilter: ['href']` | ln-external-links |
|
|
57
57
|
|
|
58
58
|
## States & visual feedback
|
|
59
59
|
|
|
60
60
|
There is no visual state. Decoration is invisible to sighted users
|
|
61
|
-
(target/rel are non-rendering attributes, the
|
|
62
|
-
by the
|
|
61
|
+
(target/rel are non-rendering attributes, the hint span is hidden
|
|
62
|
+
by the rule shipped in `ln-ashlar-core.css`). The "before/after" is purely the four mutations
|
|
63
63
|
listed above.
|
|
64
64
|
|
|
65
65
|
| Trigger | What JS does | What the user sees |
|
|
@@ -85,6 +85,7 @@ that doubles as an opt-out hatch.
|
|
|
85
85
|
| Attribute | On | Direction | Description |
|
|
86
86
|
|---|---|---|---|
|
|
87
87
|
| `data-ln-external-link="processed"` | `<a>` / `<area>` | written by component, read on each pass | Idempotency marker. Set after a link is decorated. The processing function early-returns on links that already carry it. **Pre-setting it manually** in your markup makes the component skip the link entirely — the unofficial opt-out path. |
|
|
88
|
+
| `data-ln-external-links-hint` | `<span>` | written | Marks the visually hidden "(opens in new tab)" hint span appended inside a processed link. |
|
|
88
89
|
| `target` | `<a>` / `<area>` | written | Set to `_blank` on every external link. **Overwrites** any pre-existing `target` value. |
|
|
89
90
|
| `rel` | `<a>` / `<area>` | written | Merged: `noopener` and `noreferrer` are added if not already present. Pre-existing tokens (`me`, `author`, `license`, `nofollow`, `ugc`, `sponsored`) survive on the link. |
|
|
90
91
|
|
|
@@ -99,7 +100,7 @@ itself.
|
|
|
99
100
|
|
|
100
101
|
| Event | Bubbles | Cancelable | `detail` | Dispatched when | Common consumer |
|
|
101
102
|
|---|---|---|---|---|---|
|
|
102
|
-
| `ln-external-links:processed` | yes | no | `{ link: HTMLAnchorElement, href: string }` | A link finishes decoration (after `target`, `rel`,
|
|
103
|
+
| `ln-external-links:processed` | yes | no | `{ link: HTMLAnchorElement, href: string }` | A link finishes decoration (after `target`, `rel`, hint span, and the marker attribute are all written) | Auditing / debug logging; verifying decoration coverage |
|
|
103
104
|
| `ln-external-links:clicked` | yes | no | `{ link, href: string, text: string }` | The user clicks anywhere inside a processed external link. `text` is `link.textContent || link.title || ''` — the visible label, falling back to the title attribute, falling back to empty string | Analytics / outbound-link tracking |
|
|
104
105
|
|
|
105
106
|
The `:clicked` event is a **notification, not a hook**. The component does not call `preventDefault()` and does not honor a consumer's `preventDefault()` on the CustomEvent. To intercept navigation, attach a click listener on the link or a parent in the **capture phase**, and `preventDefault()` the platform `click` event. The demo page shows a working interstitial.
|
|
@@ -222,7 +223,7 @@ feature.
|
|
|
222
223
|
The component **merges** `noopener` and `noreferrer` into any pre-existing
|
|
223
224
|
`rel` token list. Authoring `rel="me"` on a personal-website link in your
|
|
224
225
|
bio is enough — no pre-marking required. After decoration, the link
|
|
225
|
-
carries `rel="me noopener noreferrer"` and the auto
|
|
226
|
+
carries `rel="me noopener noreferrer"` and the auto hint span:
|
|
226
227
|
|
|
227
228
|
```html
|
|
228
229
|
<!-- Before (your authored HTML) -->
|
|
@@ -234,13 +235,13 @@ carries `rel="me noopener noreferrer"` and the auto sr-only hint span:
|
|
|
234
235
|
target="_blank"
|
|
235
236
|
data-ln-external-link="processed">
|
|
236
237
|
Live Networks
|
|
237
|
-
<span
|
|
238
|
+
<span data-ln-external-links-hint>(opens in new tab)</span>
|
|
238
239
|
</a>
|
|
239
240
|
```
|
|
240
241
|
|
|
241
242
|
## Common mistakes
|
|
242
243
|
|
|
243
|
-
### Mistake 1 — Reading `link.textContent` for analytics and getting the
|
|
244
|
+
### Mistake 1 — Reading `link.textContent` for analytics and getting the hint string
|
|
244
245
|
|
|
245
246
|
```js
|
|
246
247
|
document.addEventListener('ln-external-links:clicked', function (e) {
|
|
@@ -252,7 +253,7 @@ document.addEventListener('ln-external-links:clicked', function (e) {
|
|
|
252
253
|
`detail.text` is `link.textContent || link.title || ''`, evaluated at
|
|
253
254
|
click time *after* the hint span has been appended.
|
|
254
255
|
`link.textContent` returns the concatenated text of all descendants,
|
|
255
|
-
including the
|
|
256
|
+
including the hint span. If your analytics dashboard collects
|
|
256
257
|
`event_label`, you'll see the hint suffix on every entry.
|
|
257
258
|
|
|
258
259
|
Two workarounds:
|
|
@@ -265,7 +266,7 @@ const cleanLabel = label.replace(/\s*\(opens in new tab\)\s*$/, '');
|
|
|
265
266
|
// The hint is always the LAST child of the link.
|
|
266
267
|
const link = e.detail.link;
|
|
267
268
|
const labelOnly = Array.prototype.filter.call(link.childNodes, function (n) {
|
|
268
|
-
return !(n.nodeType === 1 && n.
|
|
269
|
+
return !(n.nodeType === 1 && n.hasAttribute && n.hasAttribute('data-ln-external-links-hint'));
|
|
269
270
|
}).map(function (n) { return n.textContent; }).join('').trim();
|
|
270
271
|
```
|
|
271
272
|
|
|
@@ -323,10 +324,9 @@ This self-contained script executes immediately and sets up the necessary hooks
|
|
|
323
324
|
- **`@mixin ln-icon`** is unused here — there's no icon decoration on
|
|
324
325
|
external links by default. If you want a "↗" indicator, add it in
|
|
325
326
|
project SCSS via `a[data-ln-external-link="processed"]::after`.
|
|
326
|
-
-
|
|
327
|
-
visually-hidden
|
|
328
|
-
|
|
329
|
-
`.sr-only` definition or the hint will be visible.
|
|
327
|
+
- **`[data-ln-external-links-hint]`** (`components/ln-external-links/ln-external-links.scss`) — the
|
|
328
|
+
visually-hidden rule for the appended hint span. It ships in
|
|
329
|
+
`ln-ashlar-core.css`; no `.sr-only` utility is needed.
|
|
330
330
|
- **[`ln-modal`](../ln-modal/README.md)** — the demo page composes
|
|
331
331
|
`ln-modal` with this component to show a "leaving the site"
|
|
332
332
|
interstitial. The composition is project code, not library code;
|
|
@@ -359,7 +359,7 @@ Both the click delegate and the observer are wrapped in `guardBody` (`ln-core`):
|
|
|
359
359
|
2. `!_isExternalLink(link)` → return.
|
|
360
360
|
3. `target = '_blank'` (clobbers any pre-existing value).
|
|
361
361
|
4. `rel` tokens merged: `noopener` + `noreferrer` added, existing tokens (`me`, `nofollow`, etc.) survive.
|
|
362
|
-
5. `<span
|
|
362
|
+
5. `<span data-ln-external-links-hint>(opens in new tab)</span>` appended as last child.
|
|
363
363
|
6. `data-ln-external-link="processed"` written (the marker for future passes).
|
|
364
364
|
7. `ln-external-links:processed` dispatched (bubbles, not cancelable).
|
|
365
365
|
|
|
@@ -371,11 +371,11 @@ Watches `document.body` with `childList: true, subtree: true, attributes: true,
|
|
|
371
371
|
|
|
372
372
|
### Click delegation
|
|
373
373
|
|
|
374
|
-
One delegate on `document.body`, resolving `e.target.closest('a, area')`. No `preventDefault()` — the browser's default action and all modifier-key semantics (Ctrl/Cmd/middle/Shift-click) pass through untouched. Firing of `:clicked` is gated on the `processed` marker, **not** a fresh `_isExternalLink` re-check — a link pre-marked `processed` in markup (the opt-out hatch) fires `:clicked` even if it's actually internal. `detail.text` reads `link.textContent`, which includes the appended
|
|
374
|
+
One delegate on `document.body`, resolving `e.target.closest('a, area')`. No `preventDefault()` — the browser's default action and all modifier-key semantics (Ctrl/Cmd/middle/Shift-click) pass through untouched. Firing of `:clicked` is gated on the `processed` marker, **not** a fresh `_isExternalLink` re-check — a link pre-marked `processed` in markup (the opt-out hatch) fires `:clicked` even if it's actually internal. `detail.text` reads `link.textContent`, which includes the appended hint span text.
|
|
375
375
|
|
|
376
376
|
### Accessibility
|
|
377
377
|
|
|
378
|
-
The
|
|
378
|
+
The hint has no i18n hook — the string is hard-coded English. Projects needing localization must patch the compiled bundle or pre-mark every link to skip the component.
|
|
379
379
|
|
|
380
380
|
### Cross-component coordination
|
|
381
381
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){"use strict";function T(){if(typeof window>"u")return!1;if(window.lnDebug===!0||window.lnCore&&window.lnCore._debugSink)return!0;if(typeof document<"u"){if(document.documentElement&&document.documentElement.hasAttribute("data-ln-debug"))return!0;if(document.body)return document.body.hasAttribute("data-ln-debug")||document.body.querySelector("[data-ln-debug]")!==null}return!1}function y(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 o=null;for(let l=1;l<t.length;l++)if(t[l]&&t[l].nodeType===1){o=t[l];break}if(!y(o))return}e.apply(console,t)}}function p(e,t,r){const o=r||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,o),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:o}))}function w(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){w(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}function M(e){const t=e.querySelector('input[name="_method"]');return((t&&t.value!==""?t.value:e.method)||"").toUpperCase()}function B(e,t){const r=!!(t&&t.typed),o=t&&t.exclude,l={},c=e.elements,i={};if(r)for(let s=0;s<c.length;s++){const n=c[s];n.name&&n.type==="checkbox"&&!n.disabled&&(i[n.name]=(i[n.name]||0)+1)}for(let s=0;s<c.length;s++){const n=c[s];if(!(!n.name||n.disabled||n.type==="file"||n.type==="submit"||n.type==="button")&&!(o&&n.matches&&n.matches(o)))if(n.type==="checkbox")r&&i[n.name]===1?l[n.name]=n.checked:(l[n.name]||(l[n.name]=[]),n.checked&&l[n.name].push(n.value));else if(n.type==="radio")n.checked&&(l[n.name]=n.value);else if(n.type==="select-multiple"){l[n.name]=[];for(let a=0;a<n.options.length;a++)n.options[a].selected&&l[n.name].push(n.options[a].value)}else if(r&&n.type==="hidden")l[n.name]=n.value;else if(r&&(n.type==="number"||n.type==="range")){const a=Number(n.value);l[n.name]=n.value===""||isNaN(a)?null:a}else l[n.name]=n.value}return l}function j(e){if(typeof e!="string")return!!e;const t=e.trim().toLowerCase();return t!=="false"&&t!=="0"&&t!==""&&t!=="off"&&t!=="no"}function W(e,t){const r=e.elements,o=[],l={};for(let c=0;c<r.length;c++){const i=r[c];i.name&&i.type==="checkbox"&&(l[i.name]=(l[i.name]||0)+1)}for(let c=0;c<r.length;c++){const i=r[c];if(i.type==="file"||i.type==="submit"||i.type==="button")continue;const s=i.getAttribute("data-ln-fill-as")||i.name;if(!s||!(s in t))continue;const n=t[s];if(i.type==="checkbox"){if(Array.isArray(n))i.checked=n.indexOf(i.value)!==-1;else if(l[i.name]>1){const a=String(n).split(",").map(function(u){return u.trim()});i.checked=a.indexOf(i.value)!==-1}else i.checked=j(n);o.push(i)}else if(i.type==="radio")i.checked=i.value===String(n),o.push(i);else if(i.type==="select-multiple"){if(Array.isArray(n))for(let a=0;a<i.options.length;a++)i.options[a].selected=n.indexOf(i.options[a].value)!==-1;o.push(i)}else i.value=n,i.tagName==="SELECT"&&n!=null&&i.setAttribute("data-ln-value",n),o.push(i)}return o}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.populateForm=W,window.lnCore.serializeForm=B,window.lnCore.resolveFormMethod=M);const A={};function U(e,t){A[e]=t}function V(e){return A[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=U,window.lnCore.getDataMapper=V);function H(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,w(function(){new MutationObserver(function(){p(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const x={};function J(e,t){if(!e||typeof t!="object")return;const r=e.toLowerCase().split("-")[0];x[r]=t}function K(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return x[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=J,window.lnCore.getLocaleFallback=K,window.lnCore.ensureLocaleObserver=H);const b={};function P(e,t){b[e]||(b[e]=document.querySelector('[data-ln-template="'+e+'"]'));const r=b[e];return r?r.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function _(e,t){if(!e||!t)return e;const r=e.querySelectorAll("[data-ln-field]");for(let i=0;i<r.length;i++){const s=r[i],n=s.getAttribute("data-ln-field");t[n]!=null&&(s.textContent=t[n])}const o=e.querySelectorAll("[data-ln-attr]");for(let i=0;i<o.length;i++){const s=o[i],n=s.getAttribute("data-ln-attr").split(",");for(let a=0;a<n.length;a++){const u=n[a].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();t[f]!=null&&s.setAttribute(d,t[f])}}const l=e.querySelectorAll("[data-ln-show]");for(let i=0;i<l.length;i++){const s=l[i],n=s.getAttribute("data-ln-show");n in t&&s.classList.toggle("hidden",!t[n])}const c=e.querySelectorAll("[data-ln-class]");for(let i=0;i<c.length;i++){const s=c[i],n=s.getAttribute("data-ln-class").split(",");for(let a=0;a<n.length;a++){const u=n[a].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();f in t&&s.classList.toggle(d,!!t[f])}}return e}function G(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 r=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let o=0;o<r.length;o++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",r[o],t??null),r[o].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)_(e.target,e.detail);else{const t=e.target.querySelectorAll("[data-ln-field]");for(let r=0;r<t.length;r++)t[r].textContent=""}})));function k(e,t){if(!e||!t)return e;const r=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;r.nextNode();){const c=r.currentNode;c.textContent.indexOf("{{")!==-1&&(c.textContent=c.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(i,s){return t[s]!==void 0?t[s]:""}))}const o=function(c,i){return t[i]!==void 0?t[i]:""},l=Array.from(e.querySelectorAll("*"));e.nodeType===1&&l.push(e);for(let c=0;c<l.length;c++){const i=l[c],s=i.attributes;for(let n=0;n<s.length;n++){const a=s[n];a.value.indexOf("{{")!==-1&&i.setAttribute(a.name,a.value.replace(/\{\{\s*(\w+)\s*\}\}/g,o))}}return e}function Q(e,t,r,o,l,c){const i={};for(let n=0;n<e.children.length;n++){const a=e.children[n],u=a.getAttribute("data-ln-render-key");u&&(i[u]=a)}const s=document.createDocumentFragment();for(let n=0;n<t.length;n++){const a=t[n],u=String(o(a));let d=i[u];if(d)l(d,a,n);else{const f=P(r,c);if(!f||(k(f,a),d=f.firstElementChild,!d))continue;d.setAttribute("data-ln-render-key",u),l(d,a,n)}s.appendChild(d)}e.textContent="",e.appendChild(s)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=k,window.lnCore.fill=_,window.lnCore.lnFill=G,window.lnCore.renderList=Q);function g(e,t,r){const o=e.getAttribute(t);return o===null?r:o}function S(e,t,r){const o=parseInt(e.getAttribute(t),10);return isNaN(o)?r:o}function v(e,t,r=!1){const o=e.getAttribute(t);if(o===null)return!!r;const l=o.trim().toLowerCase();return!(l==="false"||l==="0")}function E(e,t){return(e.getAttribute(t)||"").split(",").map(r=>r.trim()).filter(Boolean)}const m=new Map;function L(e,t){const r=(e?e.join("|"):"")+"::"+t;if(m.has(r))return m.get(r);const o=new Set(e||[]),l=function(c,i){const s=c.getAttribute(i);return s!==null&&o.has(s)?s:t};return m.set(r,l),l}function q(e,t,r){const o=parseFloat(e.getAttribute(t));return isNaN(o)?r:o}function D(e,t,r){const o=e.getAttribute(t);if(!o)return r;try{return JSON.parse(o)}catch{return r}}function z(e,t,r,o,l){if(!e||t===null)return!0;const c=o||"ln-component",i=e.type;if(i==="trigger"||i==="marker"||i==="string"||i==="list"||!i||t===""&&e.fallback!==void 0)return!0;const s=n=>{l?console.warn(n,l):console.warn(n)};if(i==="boolean"){const n=t.trim().toLowerCase();return n!==""&&n!=="true"&&n!=="false"&&n!=="1"&&n!=="0"?(s(`[${c}] Invalid value "${t}" for boolean attribute "${r}". Allowed: "true", "false", or presence-only.`),!1):!0}if(i==="enum"){const n=e.values||[];return n.includes(t)?!0:(s(`[${c}] Invalid value "${t}" for attribute "${r}". Allowed: ${n.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(i==="integer"){if(!/^-?\d+$/.test(t))return s(`[${c}] Invalid integer "${t}" for attribute "${r}". Fallback: ${e.fallback}.`),!1;const n=parseInt(t,10);return e.min!==void 0&&n<e.min?(s(`[${c}] Value ${n} for attribute "${r}" is less than min (${e.min}).`),!1):e.max!==void 0&&n>e.max?(s(`[${c}] Value ${n} for attribute "${r}" is greater than max (${e.max}).`),!1):!0}if(i==="float")return isNaN(Number(t))?(s(`[${c}] Invalid float "${t}" for attribute "${r}". Fallback: ${e.fallback}.`),!1):!0;if(i==="json")try{return JSON.parse(t),!0}catch(n){return s(`[${c}] Invalid JSON for attribute "${r}": ${n.message}. Fallback: ${e.fallback}.`),!1}return!0}function X(e,t,r){for(const o in r){const[l,c,i]=r[o];Object.defineProperty(e,o,{get:function(){return l(t,c,i)},enumerable:!0,configurable:!0})}return e}function Y(e){const t={};for(const r in e){const o=e[r];if(!o||!o.prop)continue;let l=o.read;if(!l&&o.type)switch(o.type){case"enum":l=L(o.values,o.fallback);break;case"integer":l=S;break;case"float":l=q;break;case"boolean":l=v;break;case"list":l=E;break;case"json":l=D;break;case"string":default:l=g;break}l||(l=g),t[o.prop]=[l,r,o.fallback]}return t}function Z(e){if(e.onAttrChange&&!e.declared)return null;const t=new Set;if(e.effects)for(const r in e.effects)t.add(r);if(e.onAttrChange&&e.declared)for(const r of e.declared)t.add(r);return t}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.defineAttrs=X,window.lnCore.attrSpec=Y,window.lnCore.attrStr=g,window.lnCore.attrInt=S,window.lnCore.attrBool=v,window.lnCore.attrList=E,window.lnCore.attrEnum=L,window.lnCore.attrFloat=q,window.lnCore.attrJson=D);function h(e,t){if(!e[t.attribute]){window.lnCore._persistSink&&e.hasAttribute&&e.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(e,t.selector);try{e[t.attribute]=new t.ComponentFn(e)}catch(r){console.error("["+t.attribute+"] init failed",e,r)}}}function ee(e,t,r,o){if(e.nodeType!==1)return;const c=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",i={selector:t,attribute:r,ComponentFn:o};if(e.matches&&e.matches(c)&&h(e,i),e.querySelectorAll){const s=e.querySelectorAll(c);for(let n=0;n<s.length;n++)h(s[n],i)}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function O(){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 te(e){const t=O(),r=e.observed||[];for(let o=0;o<r.length;o++){const l=r[o];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 o in e.attributes){t.byDeclaredAttr.has(o)||t.byDeclaredAttr.set(o,[]);const l=t.byDeclaredAttr.get(o);l.some(c=>c.componentTag===e.componentTag)||l.push({spec:e.attributes[o],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const o=Z(e);if(o===null)t.reactiveWildcard.push(e);else for(const l of o)t.byReactive.has(l)||t.byReactive.set(l,[]),t.byReactive.get(l).push(e)}e.persist&&t.persist.push(e)}function F(e,t,r,o){for(let l=0;l<e.length;l++){const c=e[l];if(!t[c.attribute])continue;const i=c.effects&&c.effects[r];i?i(t,r,o):c.onAttrChange&&(!c.declared||c.declared.has(r))&&c.onAttrChange(t,r,o)}}function ne(e){const t=e.attributeName,r=t.indexOf("data-ln-")===0,o=O(),l=o.byAttr.get(t),c=T()&&o.byDeclaredAttr&&o.byDeclaredAttr.has(t);if(!r&&!l&&!c)return;const i=e.target;if(e.oldValue!==i.getAttribute(t)){if(c&&y(i)){const s=o.byDeclaredAttr.get(t),n=i.getAttribute(t);for(let a=0;a<s.length;a++)z(s[a].spec,n,t,s[a].componentTag,i)}if(window.lnCore._debugSink&&(r||l)&&window.lnCore._debugSink("attr",t,i,{oldValue:e.oldValue,newValue:i.getAttribute(t)}),r){const s=o.byReactive.get(t);s&&F(s,i,t,e.oldValue),o.reactiveWildcard.length&&F(o.reactiveWildcard,i,t,e.oldValue)}if(l)for(let s=0;s<l.length;s++){const n=l[s];if(n.handler){n.handler(i,t,e.oldValue);continue}n.onAttributeChange&&i[n.attribute]?n.onAttributeChange(i,t):(ee(i,n.selector,n.attribute,n.ComponentFn),n.onInit&&n.onInit(i))}}}function re(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,w(function(){new MutationObserver(function(t){for(let r=0;r<t.length;r++)try{ne(t[r])}catch(o){console.error("[ln-core] mutation handler failed",t[r].target,o)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function I(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function $(){return window.lnCore=window.lnCore||{},window.lnCore._subtreeRegistry=window.lnCore._subtreeRegistry||[]}function R(e,t){if(!document.contains(e))if(t.attribute){const r=e[t.attribute];if(r&&typeof r.destroy=="function")try{r.destroy()}catch(o){console.error("["+t.attribute+"] destroy failed",e,o)}delete e[t.attribute]}else t.onDestroy&&t.onDestroy(e)}function oe(e){const t=I();if(!t.length)return;const r=e.target;if(r){const l=r.nodeType===1?r:r.parentElement;if(l&&(l.isContentEditable||l.closest&&l.closest('[contenteditable]:not([contenteditable="false"])')))return}const o=$();if(e.target&&o.length)for(let l=0;l<o.length;l++){const c=o[l],i=c.query,s=e.target.nodeType===1?e.target.matches&&e.target.matches(i)?e.target:e.target.closest?e.target.closest(i):null:e.target.parentElement&&e.target.parentElement.closest?e.target.parentElement.closest(i):null;s&&c.onSubtreeChange(s,e)}for(let l=0;l<e.addedNodes.length;l++){const c=e.addedNodes[l];if(c.nodeType!==1)continue;const i=!!(c.firstElementChild||c.children&&c.children.length);for(let s=0;s<t.length;s++){const n=t[s],a=n.query;if(a&&(c.matches&&c.matches(a)&&(n.ComponentFn&&h(c,n),n.onInit&&n.onInit(c)),i&&c.querySelectorAll)){const u=c.querySelectorAll(a);for(let d=0;d<u.length;d++)n.ComponentFn&&h(u[d],n),n.onInit&&n.onInit(u[d])}}}for(let l=0;l<e.removedNodes.length;l++){const c=e.removedNodes[l];if(c.nodeType!==1||document.contains(c))continue;const i=!!(c.firstElementChild||c.children&&c.children.length);for(let s=0;s<t.length;s++){const n=t[s],a=n.query;if(a&&(c.matches&&c.matches(a)&&R(c,n),i&&c.querySelectorAll)){const u=c.querySelectorAll(a);for(let d=0;d<u.length;d++)R(u[d],n)}}}}function ie(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,w(function(){const e=new MutationObserver(function(t){for(let r=0;r<t.length;r++){const o=t[r];if(o.type==="childList")try{oe(o)}catch(l){console.error("[ln-core] lifecycle handler failed",o.target,l)}}});window.lnCore._lifecycleObserver=e,e.observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function le(e,t){te({observed:e,handler:t}),re()}function se(e){I().push(e),e.onSubtreeChange&&e.query&&$().push(e),ie()}function N(e){return(e||"").replace(/^#/,"")}function C(e){const t=e===void 0?location.hash:e,r={},o=N(t);if(!o)return r;const l=o.split("&");for(let c=0;c<l.length;c++){const i=l[c];if(!i)continue;const s=i.indexOf(":"),n=s>-1?i.slice(0,s):i,a=s>-1?i.slice(s+1):"";if(n)try{r[n]=decodeURIComponent(a)}catch{r[n]=a}}return r}function ce(e){if(!e)return null;const t=C();return e in t?t[e]:null}function ae(e,t){if(!e)return;const r=C();t==null?delete r[e]:r[e]=String(t);const l=Object.keys(r).map(function(c){const i=r[c];return i===""?c:c+":"+encodeURIComponent(i)}).join("&");N(location.hash)!==l&&(location.hash=l)}function ue(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function de(e,t){if(!e||!e.hasAttribute("data-ln-hash"))return null;const r=e.getAttribute("data-ln-hash");if(r&&r.trim()!=="")return r.trim();const o=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return o?t?o+"-"+t:o:t||null}function fe(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function we(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 he(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function pe(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const r=e.slice(0,t),o=e.slice(t+1),l=o?o.split(",").map(function(c){try{return decodeURIComponent(c)}catch{return c}}).filter(Boolean):[];return{key:r,values:l}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=C,window.lnCore.hashGet=ce,window.lnCore.hashSet=ae,window.lnCore.hashLinkClick=ue,window.lnCore.resolveHashNamespace=de,window.lnCore.hashSortEncode=fe,window.lnCore.hashSortDecode=we,window.lnCore.hashFilterEncode=he,window.lnCore.hashFilterDecode=pe);function be(e){return(e||"").replace(/^\?/,"")}function ge(e){const t=e===void 0?typeof window<"u"?window.location.search:"":e,r={},o=be(t);if(!o)return r;const l=new URLSearchParams(o);for(const[c,i]of l.entries())r[c]=i;return r}function me(e){if(!e||typeof window>"u")return null;const t=new URLSearchParams(window.location.search);return t.has(e)?t.get(e):null}function Ce(e,t){if(!e||typeof window>"u")return;const r=new URL(window.location.href);if(t==null||t===""?r.searchParams.delete(e):r.searchParams.set(e,String(t)),r.search===window.location.search)return;const o=r.pathname+r.search+r.hash;window.history.replaceState(window.history.state,"",o)}function ye(e,t){if(!e||!e.hasAttribute("data-ln-query"))return null;const r=e.getAttribute("data-ln-query");if(r&&r.trim()!=="")return r.trim();const o=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return o?t?o+"_"+t:o:t||null}function Ae(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function xe(e,t){if(e&&typeof e=="string"){if(e.endsWith(".asc"))return{fieldOrColumn:e.slice(0,-4),direction:"asc"};if(e.endsWith(".desc"))return{fieldOrColumn:e.slice(0,-5),direction:"desc"}}if(t&&typeof t=="object"){const r=t.sort_field||t.sort_by;if(r){const o=t.sort_dir&&String(t.sort_dir).toLowerCase()==="desc"?"desc":"asc";return{fieldOrColumn:r,direction:o}}}return null}function _e(e){return!Array.isArray(e)||e.length===0?null:e.map(encodeURIComponent).join(",")}function ke(e){return!e||typeof e!="string"?[]:e.split(",").map(function(t){try{return decodeURIComponent(t.trim())}catch{return t.trim()}}).filter(Boolean)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.queryParse=ge,window.lnCore.queryGet=me,window.lnCore.querySet=Ce,window.lnCore.resolveQueryParam=ye,window.lnCore.querySortEncode=Ae,window.lnCore.querySortDecode=xe,window.lnCore.queryFilterEncode=_e,window.lnCore.queryFilterDecode=ke);function Se(){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=Se),(function(){const e="lnExternalLinks";if(window[e]!==void 0)return;function t(s){return s.hostname&&s.hostname!==window.location.hostname}function r(s){if(s.getAttribute("data-ln-external-link")==="processed"||!t(s))return;s.target="_blank";const n=(s.rel||"").split(/\s+/).filter(Boolean);n.includes("noopener")||n.push("noopener"),n.includes("noreferrer")||n.push("noreferrer"),s.rel=n.join(" ");const a=document.createElement("span");a.className="sr-only",a.textContent="(opens in new tab)",s.appendChild(a),s.setAttribute("data-ln-external-link","processed"),p(s,"ln-external-links:processed",{link:s,href:s.href})}function o(s){s=s||document.body;for(const n of s.querySelectorAll("a, area"))r(n)}function l(){w(function(){document.body.addEventListener("click",function(s){const n=s.target.closest("a, area");n&&n.getAttribute("data-ln-external-link")==="processed"&&p(n,"ln-external-links:clicked",{link:n,href:n.href,text:n.textContent||n.title||""})})},"ln-external-links")}function c(){w(function(){se({onInit:function(s){if(s.matches&&(s.matches("a")||s.matches("area"))&&r(s),s.querySelectorAll)for(const n of s.querySelectorAll("a, area"))r(n)}}),le(["href"],function(s){s.matches&&(s.matches("a")||s.matches("area"))&&r(s)})},"ln-external-links")}function i(){l(),c(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){o()}):o()}window[e]={process:o},i()})()})();
|
|
1
|
+
(function(){"use strict";function N(){if(typeof window>"u")return!1;if(window.lnDebug===!0||window.lnCore&&window.lnCore._debugSink)return!0;if(typeof document<"u"){if(document.documentElement&&document.documentElement.hasAttribute("data-ln-debug"))return!0;if(document.body)return document.body.hasAttribute("data-ln-debug")||document.body.querySelector("[data-ln-debug]")!==null}return!1}function y(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 o=null;for(let l=1;l<t.length;l++)if(t[l]&&t[l].nodeType===1){o=t[l];break}if(!y(o))return}e.apply(console,t)}}function p(e,t,r){const o=r||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,o),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:o}))}function w(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){w(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}function M(e){const t=e.querySelector('input[name="_method"]');return((t&&t.value!==""?t.value:e.method)||"").toUpperCase()}function B(e,t){const r=!!(t&&t.typed),o=t&&t.exclude,l={},c=e.elements,i={};if(r)for(let s=0;s<c.length;s++){const n=c[s];n.name&&n.type==="checkbox"&&!n.disabled&&(i[n.name]=(i[n.name]||0)+1)}for(let s=0;s<c.length;s++){const n=c[s];if(!(!n.name||n.disabled||n.type==="file"||n.type==="submit"||n.type==="button")&&!(o&&n.matches&&n.matches(o)))if(n.type==="checkbox")r&&i[n.name]===1?l[n.name]=n.checked:(l[n.name]||(l[n.name]=[]),n.checked&&l[n.name].push(n.value));else if(n.type==="radio")n.checked&&(l[n.name]=n.value);else if(n.type==="select-multiple"){l[n.name]=[];for(let a=0;a<n.options.length;a++)n.options[a].selected&&l[n.name].push(n.options[a].value)}else if(r&&n.type==="hidden")l[n.name]=n.value;else if(r&&(n.type==="number"||n.type==="range")){const a=Number(n.value);l[n.name]=n.value===""||isNaN(a)?null:a}else l[n.name]=n.value}return l}function j(e){if(typeof e!="string")return!!e;const t=e.trim().toLowerCase();return t!=="false"&&t!=="0"&&t!==""&&t!=="off"&&t!=="no"}function W(e,t){const r=e.elements,o=[],l={};for(let c=0;c<r.length;c++){const i=r[c];i.name&&i.type==="checkbox"&&(l[i.name]=(l[i.name]||0)+1)}for(let c=0;c<r.length;c++){const i=r[c];if(i.type==="file"||i.type==="submit"||i.type==="button")continue;const s=i.getAttribute("data-ln-fill-as")||i.name;if(!s||!(s in t))continue;const n=t[s];if(i.type==="checkbox"){if(Array.isArray(n))i.checked=n.indexOf(i.value)!==-1;else if(l[i.name]>1){const a=String(n).split(",").map(function(u){return u.trim()});i.checked=a.indexOf(i.value)!==-1}else i.checked=j(n);o.push(i)}else if(i.type==="radio")i.checked=i.value===String(n),o.push(i);else if(i.type==="select-multiple"){if(Array.isArray(n))for(let a=0;a<i.options.length;a++)i.options[a].selected=n.indexOf(i.options[a].value)!==-1;o.push(i)}else i.value=n,i.tagName==="SELECT"&&n!=null&&i.setAttribute("data-ln-value",n),o.push(i)}return o}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.populateForm=W,window.lnCore.serializeForm=B,window.lnCore.resolveFormMethod=M);const A={};function U(e,t){A[e]=t}function V(e){return A[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=U,window.lnCore.getDataMapper=V);function H(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,w(function(){new MutationObserver(function(){p(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const x={};function J(e,t){if(!e||typeof t!="object")return;const r=e.toLowerCase().split("-")[0];x[r]=t}function K(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return x[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=J,window.lnCore.getLocaleFallback=K,window.lnCore.ensureLocaleObserver=H);const b={};function P(e,t){b[e]||(b[e]=document.querySelector('[data-ln-template="'+e+'"]'));const r=b[e];return r?r.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function _(e,t){if(!e||!t)return e;const r=e.querySelectorAll("[data-ln-field]");for(let i=0;i<r.length;i++){const s=r[i],n=s.getAttribute("data-ln-field");t[n]!=null&&(s.textContent=t[n])}const o=e.querySelectorAll("[data-ln-attr]");for(let i=0;i<o.length;i++){const s=o[i],n=s.getAttribute("data-ln-attr").split(",");for(let a=0;a<n.length;a++){const u=n[a].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();t[f]!=null&&s.setAttribute(d,t[f])}}const l=e.querySelectorAll("[data-ln-show]");for(let i=0;i<l.length;i++){const s=l[i],n=s.getAttribute("data-ln-show");n in t&&s.classList.toggle("hidden",!t[n])}const c=e.querySelectorAll("[data-ln-class]");for(let i=0;i<c.length;i++){const s=c[i],n=s.getAttribute("data-ln-class").split(",");for(let a=0;a<n.length;a++){const u=n[a].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();f in t&&s.classList.toggle(d,!!t[f])}}return e}function G(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 r=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let o=0;o<r.length;o++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",r[o],t??null),r[o].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)_(e.target,e.detail);else{const t=e.target.querySelectorAll("[data-ln-field]");for(let r=0;r<t.length;r++)t[r].textContent=""}})));function k(e,t){if(!e||!t)return e;const r=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;r.nextNode();){const c=r.currentNode;c.textContent.indexOf("{{")!==-1&&(c.textContent=c.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(i,s){return t[s]!==void 0?t[s]:""}))}const o=function(c,i){return t[i]!==void 0?t[i]:""},l=Array.from(e.querySelectorAll("*"));e.nodeType===1&&l.push(e);for(let c=0;c<l.length;c++){const i=l[c],s=i.attributes;for(let n=0;n<s.length;n++){const a=s[n];a.value.indexOf("{{")!==-1&&i.setAttribute(a.name,a.value.replace(/\{\{\s*(\w+)\s*\}\}/g,o))}}return e}function Q(e,t,r,o,l,c){const i={};for(let n=0;n<e.children.length;n++){const a=e.children[n],u=a.getAttribute("data-ln-render-key");u&&(i[u]=a)}const s=document.createDocumentFragment();for(let n=0;n<t.length;n++){const a=t[n],u=String(o(a));let d=i[u];if(d)l(d,a,n);else{const f=P(r,c);if(!f||(k(f,a),d=f.firstElementChild,!d))continue;d.setAttribute("data-ln-render-key",u),l(d,a,n)}s.appendChild(d)}e.textContent="",e.appendChild(s)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=k,window.lnCore.fill=_,window.lnCore.lnFill=G,window.lnCore.renderList=Q);function g(e,t,r){const o=e.getAttribute(t);return o===null?r:o}function S(e,t,r){const o=parseInt(e.getAttribute(t),10);return isNaN(o)?r:o}function v(e,t,r=!1){const o=e.getAttribute(t);if(o===null)return!!r;const l=o.trim().toLowerCase();return!(l==="false"||l==="0")}function E(e,t){return(e.getAttribute(t)||"").split(",").map(r=>r.trim()).filter(Boolean)}const m=new Map;function L(e,t){const r=(e?e.join("|"):"")+"::"+t;if(m.has(r))return m.get(r);const o=new Set(e||[]),l=function(c,i){const s=c.getAttribute(i);return s!==null&&o.has(s)?s:t};return m.set(r,l),l}function q(e,t,r){const o=parseFloat(e.getAttribute(t));return isNaN(o)?r:o}function D(e,t,r){const o=e.getAttribute(t);if(!o)return r;try{return JSON.parse(o)}catch{return r}}function z(e,t,r,o,l){if(!e||t===null)return!0;const c=o||"ln-component",i=e.type;if(i==="trigger"||i==="marker"||i==="string"||i==="list"||!i||t===""&&e.fallback!==void 0)return!0;const s=n=>{l?console.warn(n,l):console.warn(n)};if(i==="boolean"){const n=t.trim().toLowerCase();return n!==""&&n!=="true"&&n!=="false"&&n!=="1"&&n!=="0"?(s(`[${c}] Invalid value "${t}" for boolean attribute "${r}". Allowed: "true", "false", or presence-only.`),!1):!0}if(i==="enum"){const n=e.values||[];return n.includes(t)?!0:(s(`[${c}] Invalid value "${t}" for attribute "${r}". Allowed: ${n.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(i==="integer"){if(!/^-?\d+$/.test(t))return s(`[${c}] Invalid integer "${t}" for attribute "${r}". Fallback: ${e.fallback}.`),!1;const n=parseInt(t,10);return e.min!==void 0&&n<e.min?(s(`[${c}] Value ${n} for attribute "${r}" is less than min (${e.min}).`),!1):e.max!==void 0&&n>e.max?(s(`[${c}] Value ${n} for attribute "${r}" is greater than max (${e.max}).`),!1):!0}if(i==="float")return isNaN(Number(t))?(s(`[${c}] Invalid float "${t}" for attribute "${r}". Fallback: ${e.fallback}.`),!1):!0;if(i==="json")try{return JSON.parse(t),!0}catch(n){return s(`[${c}] Invalid JSON for attribute "${r}": ${n.message}. Fallback: ${e.fallback}.`),!1}return!0}function X(e,t,r){for(const o in r){const[l,c,i]=r[o];Object.defineProperty(e,o,{get:function(){return l(t,c,i)},enumerable:!0,configurable:!0})}return e}function Y(e){const t={};for(const r in e){const o=e[r];if(!o||!o.prop)continue;let l=o.read;if(!l&&o.type)switch(o.type){case"enum":l=L(o.values,o.fallback);break;case"integer":l=S;break;case"float":l=q;break;case"boolean":l=v;break;case"list":l=E;break;case"json":l=D;break;case"string":default:l=g;break}l||(l=g),t[o.prop]=[l,r,o.fallback]}return t}function Z(e){if(e.onAttrChange&&!e.declared)return null;const t=new Set;if(e.effects)for(const r in e.effects)t.add(r);if(e.onAttrChange&&e.declared)for(const r of e.declared)t.add(r);return t}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.defineAttrs=X,window.lnCore.attrSpec=Y,window.lnCore.attrStr=g,window.lnCore.attrInt=S,window.lnCore.attrBool=v,window.lnCore.attrList=E,window.lnCore.attrEnum=L,window.lnCore.attrFloat=q,window.lnCore.attrJson=D);function h(e,t){if(!e[t.attribute]){window.lnCore._persistSink&&e.hasAttribute&&e.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(e,t.selector);try{e[t.attribute]=new t.ComponentFn(e)}catch(r){console.error("["+t.attribute+"] init failed",e,r)}}}function ee(e,t,r,o){if(e.nodeType!==1)return;const c=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",i={selector:t,attribute:r,ComponentFn:o};if(e.matches&&e.matches(c)&&h(e,i),e.querySelectorAll){const s=e.querySelectorAll(c);for(let n=0;n<s.length;n++)h(s[n],i)}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function O(){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 te(e){const t=O(),r=e.observed||[];for(let o=0;o<r.length;o++){const l=r[o];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 o in e.attributes){t.byDeclaredAttr.has(o)||t.byDeclaredAttr.set(o,[]);const l=t.byDeclaredAttr.get(o);l.some(c=>c.componentTag===e.componentTag)||l.push({spec:e.attributes[o],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const o=Z(e);if(o===null)t.reactiveWildcard.push(e);else for(const l of o)t.byReactive.has(l)||t.byReactive.set(l,[]),t.byReactive.get(l).push(e)}e.persist&&t.persist.push(e)}function F(e,t,r,o){for(let l=0;l<e.length;l++){const c=e[l];if(!t[c.attribute])continue;const i=c.effects&&c.effects[r];i?i(t,r,o):c.onAttrChange&&(!c.declared||c.declared.has(r))&&c.onAttrChange(t,r,o)}}function ne(e){const t=e.attributeName,r=t.indexOf("data-ln-")===0,o=O(),l=o.byAttr.get(t),c=N()&&o.byDeclaredAttr&&o.byDeclaredAttr.has(t);if(!r&&!l&&!c)return;const i=e.target;if(e.oldValue!==i.getAttribute(t)){if(c&&y(i)){const s=o.byDeclaredAttr.get(t),n=i.getAttribute(t);for(let a=0;a<s.length;a++)z(s[a].spec,n,t,s[a].componentTag,i)}if(window.lnCore._debugSink&&(r||l)&&window.lnCore._debugSink("attr",t,i,{oldValue:e.oldValue,newValue:i.getAttribute(t)}),r){const s=o.byReactive.get(t);s&&F(s,i,t,e.oldValue),o.reactiveWildcard.length&&F(o.reactiveWildcard,i,t,e.oldValue)}if(l)for(let s=0;s<l.length;s++){const n=l[s];if(n.handler){n.handler(i,t,e.oldValue);continue}n.onAttributeChange&&i[n.attribute]?n.onAttributeChange(i,t):(ee(i,n.selector,n.attribute,n.ComponentFn),n.onInit&&n.onInit(i))}}}function re(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,w(function(){new MutationObserver(function(t){for(let r=0;r<t.length;r++)try{ne(t[r])}catch(o){console.error("[ln-core] mutation handler failed",t[r].target,o)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function I(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function $(){return window.lnCore=window.lnCore||{},window.lnCore._subtreeRegistry=window.lnCore._subtreeRegistry||[]}function R(e,t){if(!document.contains(e))if(t.attribute){const r=e[t.attribute];if(r&&typeof r.destroy=="function")try{r.destroy()}catch(o){console.error("["+t.attribute+"] destroy failed",e,o)}delete e[t.attribute]}else t.onDestroy&&t.onDestroy(e)}function oe(e){const t=I();if(!t.length)return;const r=e.target;if(r){const l=r.nodeType===1?r:r.parentElement;if(l&&(l.isContentEditable||l.closest&&l.closest('[contenteditable]:not([contenteditable="false"])')))return}const o=$();if(e.target&&o.length)for(let l=0;l<o.length;l++){const c=o[l],i=c.query,s=e.target.nodeType===1?e.target.matches&&e.target.matches(i)?e.target:e.target.closest?e.target.closest(i):null:e.target.parentElement&&e.target.parentElement.closest?e.target.parentElement.closest(i):null;s&&c.onSubtreeChange(s,e)}for(let l=0;l<e.addedNodes.length;l++){const c=e.addedNodes[l];if(c.nodeType!==1)continue;const i=!!(c.firstElementChild||c.children&&c.children.length);for(let s=0;s<t.length;s++){const n=t[s],a=n.query;if(a&&(c.matches&&c.matches(a)&&(n.ComponentFn&&h(c,n),n.onInit&&n.onInit(c)),i&&c.querySelectorAll)){const u=c.querySelectorAll(a);for(let d=0;d<u.length;d++)n.ComponentFn&&h(u[d],n),n.onInit&&n.onInit(u[d])}}}for(let l=0;l<e.removedNodes.length;l++){const c=e.removedNodes[l];if(c.nodeType!==1||document.contains(c))continue;const i=!!(c.firstElementChild||c.children&&c.children.length);for(let s=0;s<t.length;s++){const n=t[s],a=n.query;if(a&&(c.matches&&c.matches(a)&&R(c,n),i&&c.querySelectorAll)){const u=c.querySelectorAll(a);for(let d=0;d<u.length;d++)R(u[d],n)}}}}function ie(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,w(function(){const e=new MutationObserver(function(t){for(let r=0;r<t.length;r++){const o=t[r];if(o.type==="childList")try{oe(o)}catch(l){console.error("[ln-core] lifecycle handler failed",o.target,l)}}});window.lnCore._lifecycleObserver=e,e.observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function le(e,t){te({observed:e,handler:t}),re()}function se(e){I().push(e),e.onSubtreeChange&&e.query&&$().push(e),ie()}function T(e){return(e||"").replace(/^#/,"")}function C(e){const t=e===void 0?location.hash:e,r={},o=T(t);if(!o)return r;const l=o.split("&");for(let c=0;c<l.length;c++){const i=l[c];if(!i)continue;const s=i.indexOf(":"),n=s>-1?i.slice(0,s):i,a=s>-1?i.slice(s+1):"";if(n)try{r[n]=decodeURIComponent(a)}catch{r[n]=a}}return r}function ce(e){if(!e)return null;const t=C();return e in t?t[e]:null}function ae(e,t){if(!e)return;const r=C();t==null?delete r[e]:r[e]=String(t);const l=Object.keys(r).map(function(c){const i=r[c];return i===""?c:c+":"+encodeURIComponent(i)}).join("&");T(location.hash)!==l&&(location.hash=l)}function ue(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function de(e,t){if(!e||!e.hasAttribute("data-ln-hash"))return null;const r=e.getAttribute("data-ln-hash");if(r&&r.trim()!=="")return r.trim();const o=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return o?t?o+"-"+t:o:t||null}function fe(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function we(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 he(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function pe(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const r=e.slice(0,t),o=e.slice(t+1),l=o?o.split(",").map(function(c){try{return decodeURIComponent(c)}catch{return c}}).filter(Boolean):[];return{key:r,values:l}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=C,window.lnCore.hashGet=ce,window.lnCore.hashSet=ae,window.lnCore.hashLinkClick=ue,window.lnCore.resolveHashNamespace=de,window.lnCore.hashSortEncode=fe,window.lnCore.hashSortDecode=we,window.lnCore.hashFilterEncode=he,window.lnCore.hashFilterDecode=pe);function be(e){return(e||"").replace(/^\?/,"")}function ge(e){const t=e===void 0?typeof window<"u"?window.location.search:"":e,r={},o=be(t);if(!o)return r;const l=new URLSearchParams(o);for(const[c,i]of l.entries())r[c]=i;return r}function me(e){if(!e||typeof window>"u")return null;const t=new URLSearchParams(window.location.search);return t.has(e)?t.get(e):null}function Ce(e,t){if(!e||typeof window>"u")return;const r=new URL(window.location.href);if(t==null||t===""?r.searchParams.delete(e):r.searchParams.set(e,String(t)),r.search===window.location.search)return;const o=r.pathname+r.search+r.hash;window.history.replaceState(window.history.state,"",o)}function ye(e,t){if(!e||!e.hasAttribute("data-ln-query"))return null;const r=e.getAttribute("data-ln-query");if(r&&r.trim()!=="")return r.trim();const o=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return o?t?o+"_"+t:o:t||null}function Ae(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function xe(e,t){if(e&&typeof e=="string"){if(e.endsWith(".asc"))return{fieldOrColumn:e.slice(0,-4),direction:"asc"};if(e.endsWith(".desc"))return{fieldOrColumn:e.slice(0,-5),direction:"desc"}}if(t&&typeof t=="object"){const r=t.sort_field||t.sort_by;if(r){const o=t.sort_dir&&String(t.sort_dir).toLowerCase()==="desc"?"desc":"asc";return{fieldOrColumn:r,direction:o}}}return null}function _e(e){return!Array.isArray(e)||e.length===0?null:e.map(encodeURIComponent).join(",")}function ke(e){return!e||typeof e!="string"?[]:e.split(",").map(function(t){try{return decodeURIComponent(t.trim())}catch{return t.trim()}}).filter(Boolean)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.queryParse=ge,window.lnCore.queryGet=me,window.lnCore.querySet=Ce,window.lnCore.resolveQueryParam=ye,window.lnCore.querySortEncode=Ae,window.lnCore.querySortDecode=xe,window.lnCore.queryFilterEncode=_e,window.lnCore.queryFilterDecode=ke);function Se(){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=Se),(function(){const e="lnExternalLinks";if(window[e]!==void 0)return;function t(s){return s.hostname&&s.hostname!==window.location.hostname}function r(s){if(s.getAttribute("data-ln-external-link")==="processed"||!t(s))return;s.target="_blank";const n=(s.rel||"").split(/\s+/).filter(Boolean);n.includes("noopener")||n.push("noopener"),n.includes("noreferrer")||n.push("noreferrer"),s.rel=n.join(" ");const a=document.createElement("span");a.setAttribute("data-ln-external-links-hint",""),a.textContent="(opens in new tab)",s.appendChild(a),s.setAttribute("data-ln-external-link","processed"),p(s,"ln-external-links:processed",{link:s,href:s.href})}function o(s){s=s||document.body;for(const n of s.querySelectorAll("a, area"))r(n)}function l(){w(function(){document.body.addEventListener("click",function(s){const n=s.target.closest("a, area");n&&n.getAttribute("data-ln-external-link")==="processed"&&p(n,"ln-external-links:clicked",{link:n,href:n.href,text:n.textContent||n.title||""})})},"ln-external-links")}function c(){w(function(){se({onInit:function(s){if(s.matches&&(s.matches("a")||s.matches("area"))&&r(s),s.querySelectorAll)for(const n of s.querySelectorAll("a, area"))r(n)}}),le(["href"],function(s){s.matches&&(s.matches("a")||s.matches("area"))&&r(s)})},"ln-external-links")}function i(){l(),c(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",function(){o()}):o()}window[e]={process:o},i()})()})();
|
|
@@ -13,6 +13,15 @@
|
|
|
13
13
|
"processed"
|
|
14
14
|
],
|
|
15
15
|
"description": "Marker indicating link was identified and processed as external"
|
|
16
|
+
},
|
|
17
|
+
"data-ln-external-links-hint": {
|
|
18
|
+
"direction": "runtime",
|
|
19
|
+
"sources": [
|
|
20
|
+
"ln-external-links.scss",
|
|
21
|
+
"src/ln-external-links.js"
|
|
22
|
+
],
|
|
23
|
+
"type": "marker",
|
|
24
|
+
"description": "Marks the visually hidden "
|
|
16
25
|
}
|
|
17
26
|
}
|
|
18
27
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// ==========================================================================
|
|
2
|
+
// ln-external-links — Functional Hiding Contract
|
|
3
|
+
// ==========================================================================
|
|
4
|
+
|
|
5
|
+
// The injected "(opens in new tab)" hint is a screen-reader-only span. This file
|
|
6
|
+
// ships in the CORE bundle and the theme `.sr-only` utility does not, so the
|
|
7
|
+
// hiding cannot be delegated to the theme.
|
|
8
|
+
@use '../../theme/config/mixins/display' as *;
|
|
9
|
+
|
|
10
|
+
[data-ln-external-links-hint] {
|
|
11
|
+
@include sr-only;
|
|
12
|
+
}
|
|
@@ -7,7 +7,8 @@ import { dispatch, guardBody, observeAttributes, observeLifecycle } from '../../
|
|
|
7
7
|
|
|
8
8
|
// ─── Attribute Contract (SSOT) ──────────────────────────
|
|
9
9
|
const ATTRIBUTES = {
|
|
10
|
-
'data-ln-external-link': { type: 'enum', values: ['processed'], description: 'Marker indicating link was identified and processed as external' }
|
|
10
|
+
'data-ln-external-link': { type: 'enum', values: ['processed'], description: 'Marker indicating link was identified and processed as external' },
|
|
11
|
+
'data-ln-external-links-hint': { type: 'marker', description: 'Marks the visually hidden "(opens in new tab)" hint span appended inside a processed link' }
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
function _isExternalLink(link) {
|
|
@@ -25,7 +26,7 @@ import { dispatch, guardBody, observeAttributes, observeLifecycle } from '../../
|
|
|
25
26
|
link.rel = existing.join(' ');
|
|
26
27
|
|
|
27
28
|
const hint = document.createElement('span');
|
|
28
|
-
hint.
|
|
29
|
+
hint.setAttribute('data-ln-external-links-hint', '');
|
|
29
30
|
hint.textContent = '(opens in new tab)';
|
|
30
31
|
link.appendChild(hint);
|
|
31
32
|
|
|
@@ -78,6 +78,7 @@ Filters plain `<table>` rows by Column Index `2` (Department) and saves state to
|
|
|
78
78
|
| `data-ln-query` | Container root | Opt-in. Synchronizes active filter values to URL query parameter (e.g. `?category=design,dev` or `?status=active`). Value is parameter name (if omitted, falls back to `data-ln-filter-col` or `data-ln-filter-key`). Interoperable with browser back/forward history (`popstate`). |
|
|
79
79
|
| `data-ln-filter-values` | Container root | *State*. Comma-joined, percent-encoded list of currently active filter values. Written by the component, read by `ln-persist` for restore/save. |
|
|
80
80
|
| `data-ln-filter-hide` | Children of target | *State*. Automatically toggled on non-matching elements (`display: none !important`). |
|
|
81
|
+
| `data-ln-table-empty` | Table rows | Rows carrying this attribute are skipped when filtering plain-table rows. ln-table writes it on its empty and loading placeholder row; author it on a hand-written placeholder row in a plain table. |
|
|
81
82
|
|
|
82
83
|
|
|
83
84
|
---
|
|
@@ -102,7 +103,7 @@ The canonical composition for table per-column filters.
|
|
|
102
103
|
```html
|
|
103
104
|
<th data-ln-table-filter-col="department">
|
|
104
105
|
Department
|
|
105
|
-
<button
|
|
106
|
+
<button type="button"
|
|
106
107
|
data-ln-table-col-filter
|
|
107
108
|
data-ln-popover-for="filter-my-table-dept"
|
|
108
109
|
aria-label="Filter department">
|