@digicreon/mujs 1.2.0 → 1.4.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/README.md +47 -29
- package/dist/mu.min.js +2 -482
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ Inspired by [pjax](https://github.com/defunkt/jquery-pjax), [Turbo](https://turb
|
|
|
28
28
|
- [HTTP methods](#http-methods)
|
|
29
29
|
- [Triggers](#triggers)
|
|
30
30
|
- [Server-Sent Events (SSE)](#server-sent-events-sse)
|
|
31
|
-
- [
|
|
31
|
+
- [History & Scroll](#history--scroll)
|
|
32
32
|
- [Scroll restoration](#scroll-restoration)
|
|
33
33
|
- [Prefetch](#prefetch)
|
|
34
34
|
- [DOM morphing](#dom-morphing)
|
|
@@ -46,9 +46,14 @@ Inspired by [pjax](https://github.com/defunkt/jquery-pjax), [Turbo](https://turb
|
|
|
46
46
|
|
|
47
47
|
### Via `<script>` tag (recommended)
|
|
48
48
|
|
|
49
|
+
Place the scripts at the end of `<body>`, after all your HTML content. This ensures the DOM is ready when µJS initializes.
|
|
50
|
+
|
|
49
51
|
```html
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
+
<body>
|
|
53
|
+
<!-- your content -->
|
|
54
|
+
<script src="/path/to/mu.min.js"></script>
|
|
55
|
+
<script>mu.init();</script>
|
|
56
|
+
</body>
|
|
52
57
|
```
|
|
53
58
|
|
|
54
59
|
### Via CDN
|
|
@@ -59,6 +64,8 @@ Inspired by [pjax](https://github.com/defunkt/jquery-pjax), [Turbo](https://turb
|
|
|
59
64
|
|
|
60
65
|
<!-- jsDelivr -->
|
|
61
66
|
<script src="https://cdn.jsdelivr.net/npm/@digicreon/mujs/dist/mu.min.js"></script>
|
|
67
|
+
|
|
68
|
+
<script>mu.init();</script>
|
|
62
69
|
```
|
|
63
70
|
|
|
64
71
|
### Via npm
|
|
@@ -77,8 +84,6 @@ After calling `mu.init()`, all internal links (URLs starting with `/`) are autom
|
|
|
77
84
|
<html>
|
|
78
85
|
<head>
|
|
79
86
|
<title>My site</title>
|
|
80
|
-
<script src="/path/to/mu.min.js"></script>
|
|
81
|
-
<script>mu.init();</script>
|
|
82
87
|
</head>
|
|
83
88
|
<body>
|
|
84
89
|
<!-- These links are automatically handled by µJS -->
|
|
@@ -97,6 +102,9 @@ After calling `mu.init()`, all internal links (URLs starting with `/`) are autom
|
|
|
97
102
|
|
|
98
103
|
<!-- This link is NOT handled (explicitly disabled) -->
|
|
99
104
|
<a href="/file.pdf" mu-disabled>Download PDF</a>
|
|
105
|
+
|
|
106
|
+
<script src="/path/to/mu.min.js"></script>
|
|
107
|
+
<script>mu.init();</script>
|
|
100
108
|
</body>
|
|
101
109
|
</html>
|
|
102
110
|
```
|
|
@@ -180,7 +188,7 @@ The `mu-patch-mode` attribute accepts the same values as `mu-mode` (except `patc
|
|
|
180
188
|
By default, patch mode does not modify browser history. To add the URL to history:
|
|
181
189
|
|
|
182
190
|
```html
|
|
183
|
-
<a href="/products?cat=3" mu-mode="patch" mu-patch-
|
|
191
|
+
<a href="/products?cat=3" mu-mode="patch" mu-patch-history="true">Filter</a>
|
|
184
192
|
```
|
|
185
193
|
|
|
186
194
|
|
|
@@ -201,7 +209,7 @@ Data is serialized as a query string. Behaves like a link.
|
|
|
201
209
|
|
|
202
210
|
### POST forms
|
|
203
211
|
|
|
204
|
-
Data is sent as `FormData`.
|
|
212
|
+
Data is sent as `FormData`. History is disabled by default (POST responses should not be replayed via the browser back button).
|
|
205
213
|
|
|
206
214
|
```html
|
|
207
215
|
<form action="/comment/create" method="post">
|
|
@@ -292,8 +300,6 @@ Supported values: `get`, `post`, `put`, `patch`, `delete`, `sse`.
|
|
|
292
300
|
|
|
293
301
|
Non-GET requests send an `X-Mu-Method` header with the HTTP method, allowing the server to distinguish between standard and µJS-initiated requests.
|
|
294
302
|
|
|
295
|
-
> **Note:** `mu-post` is deprecated. Use `mu-method="post"` instead.
|
|
296
|
-
|
|
297
303
|
|
|
298
304
|
## Triggers
|
|
299
305
|
|
|
@@ -375,7 +381,7 @@ Use `mu-debounce` to delay the fetch until the user stops interacting:
|
|
|
375
381
|
mu-url="/search" mu-target="#results" mu-mode="update">
|
|
376
382
|
```
|
|
377
383
|
|
|
378
|
-
> **Note:** Triggers other than `click` and `submit` default to
|
|
384
|
+
> **Note:** Triggers other than `click` and `submit` default to no browser history entry and no scroll (`mu-history="false"`, `mu-scroll="false"`).
|
|
379
385
|
|
|
380
386
|
|
|
381
387
|
## Server-Sent Events (SSE)
|
|
@@ -407,19 +413,34 @@ data: <span mu-patch-target="#online-count">42</span>
|
|
|
407
413
|
- **Automatic cleanup**: SSE connections are closed when the element is removed from the DOM (e.g. when the page changes).
|
|
408
414
|
|
|
409
415
|
|
|
410
|
-
##
|
|
416
|
+
## History & Scroll
|
|
411
417
|
|
|
412
|
-
|
|
418
|
+
`mu-history` controls whether the URL is added to browser history. `mu-scroll` controls whether the page scrolls to top after rendering. Both attributes are independent.
|
|
413
419
|
|
|
414
420
|
```html
|
|
415
|
-
<!--
|
|
416
|
-
<a href="/panel" mu-
|
|
421
|
+
<!-- Skip history on a link -->
|
|
422
|
+
<a href="/panel" mu-history="false">Open panel</a>
|
|
417
423
|
|
|
418
|
-
<!--
|
|
419
|
-
<script>mu.init({
|
|
424
|
+
<!-- Skip history globally -->
|
|
425
|
+
<script>mu.init({ history: false });</script>
|
|
426
|
+
|
|
427
|
+
<!-- Scroll to top without adding history -->
|
|
428
|
+
<a href="/page" mu-history="false" mu-scroll="true">Link</a>
|
|
420
429
|
```
|
|
421
430
|
|
|
422
|
-
|
|
431
|
+
Defaults for `mu-history` and `mu-scroll` depend on the mode and context:
|
|
432
|
+
|
|
433
|
+
| Mode | Context | `mu-history` | `mu-scroll` |
|
|
434
|
+
|---|---|---|---|
|
|
435
|
+
| `replace`, `update` | Links (GET) | `true` | `true` |
|
|
436
|
+
| `replace`, `update` | Forms (GET) | `true` | `true` |
|
|
437
|
+
| `replace`, `update` | Forms (POST/PUT/PATCH/DELETE) | `false` | `true` |
|
|
438
|
+
| `replace`, `update` | Triggers (change, blur, focus, load) | `false` | `false` |
|
|
439
|
+
| `replace`, `update` | SSE | `false` | `false` |
|
|
440
|
+
| `append`, `prepend`, `before`, `after`, `remove`, `none` | Any | `false` | `false` |
|
|
441
|
+
| `patch` | Any | `false` | `false` |
|
|
442
|
+
|
|
443
|
+
Redirections always add the URL to browser history, regardless of the `mu-history` setting. In patch mode, use `mu-patch-history="true"` to add the URL to history.
|
|
423
444
|
|
|
424
445
|
|
|
425
446
|
## Scroll restoration
|
|
@@ -429,9 +450,9 @@ When the user navigates with the browser's back/forward buttons, µJS automatica
|
|
|
429
450
|
|
|
430
451
|
## Prefetch
|
|
431
452
|
|
|
432
|
-
When enabled (default), µJS fetches the target page when the user hovers over a link, before they click. This saves ~100-300ms of perceived loading time.
|
|
453
|
+
When enabled (default), µJS fetches the target page when the user hovers over a link, before they click. A 50ms delay filters accidental hover-throughs (mouse passing over a link without intent to click). This saves ~100-300ms of perceived loading time.
|
|
433
454
|
|
|
434
|
-
The prefetch cache stores one entry per URL and is consumed on click.
|
|
455
|
+
The prefetch cache stores one entry per URL and is consumed on click. Prefetch only applies to GET requests — elements with `mu-method="post"`, `put`, `patch`, `delete` or `sse` are never prefetched.
|
|
435
456
|
|
|
436
457
|
```html
|
|
437
458
|
<!-- Disable prefetch on a specific link -->
|
|
@@ -574,9 +595,8 @@ All attributes support both `mu-*` and `data-mu-*` syntax.
|
|
|
574
595
|
| `mu-url` | Override the URL to fetch (instead of `href` / `action`). |
|
|
575
596
|
| `mu-prefix` | URL prefix for the fetch request. |
|
|
576
597
|
| `mu-title` | Selector for the title node. Supports `selector/attribute` syntax. Empty string to disable. |
|
|
577
|
-
| `mu-
|
|
578
|
-
| `mu-
|
|
579
|
-
| `mu-scroll-to-top` | Force (`true`) or prevent (`false`) scrolling to top. |
|
|
598
|
+
| `mu-history` | Add URL to browser history (`true`/`false`). Default depends on mode and context. |
|
|
599
|
+
| `mu-scroll` | Force (`true`) or prevent (`false`) scrolling to top. Default depends on mode and context. |
|
|
580
600
|
| `mu-morph` | Disable morphing on this element (`false`). |
|
|
581
601
|
| `mu-transition` | Disable view transitions on this element (`false`). |
|
|
582
602
|
| `mu-prefetch` | Disable prefetch on hover for this link (`false`). |
|
|
@@ -584,13 +604,12 @@ All attributes support both `mu-*` and `data-mu-*` syntax.
|
|
|
584
604
|
| `mu-trigger` | Event trigger: `click`, `submit`, `change`, `blur`, `focus`, `load`. |
|
|
585
605
|
| `mu-debounce` | Debounce delay in milliseconds (e.g. `"500"`). |
|
|
586
606
|
| `mu-repeat` | Polling interval in milliseconds (e.g. `"5000"`). |
|
|
587
|
-
| `mu-post` | *(Deprecated)* Use `mu-method="post"` instead. |
|
|
588
607
|
| `mu-confirm` | Show a confirmation dialog before loading. |
|
|
589
608
|
| `mu-confirm-quit` | *(Forms)* Prompt before leaving if the form has been modified. |
|
|
590
609
|
| `mu-validate` | *(Forms)* Name of a JS validation function. Must return `true`/`false`. |
|
|
591
610
|
| `mu-patch-target` | *(Patch fragments)* CSS selector of the target node. |
|
|
592
611
|
| `mu-patch-mode` | *(Patch fragments)* Injection mode for this fragment. |
|
|
593
|
-
| `mu-patch-
|
|
612
|
+
| `mu-patch-history` | Set to `true` to add the URL to browser history in patch mode. Default: `false`. |
|
|
594
613
|
|
|
595
614
|
|
|
596
615
|
## Configuration reference
|
|
@@ -599,7 +618,7 @@ Pass an object to `mu.init()` to override defaults:
|
|
|
599
618
|
|
|
600
619
|
```javascript
|
|
601
620
|
mu.init({
|
|
602
|
-
|
|
621
|
+
history: false,
|
|
603
622
|
processForms: false,
|
|
604
623
|
morph: false,
|
|
605
624
|
progress: true
|
|
@@ -610,13 +629,12 @@ mu.init({
|
|
|
610
629
|
|---|---|---|---|
|
|
611
630
|
| `processLinks` | bool | `true` | Intercept `<a>` tags. |
|
|
612
631
|
| `processForms` | bool | `true` | Intercept `<form>` tags. |
|
|
613
|
-
| `
|
|
614
|
-
| `ghostRedirect` | bool | `false` | Ghost mode for HTTP redirections. |
|
|
632
|
+
| `history` | bool | `true` | Add URL to browser history. |
|
|
615
633
|
| `mode` | string | `"replace"` | Default injection mode. |
|
|
616
634
|
| `target` | string | `"body"` | Default target CSS selector. |
|
|
617
635
|
| `source` | string | `"body"` | Default source CSS selector. |
|
|
618
636
|
| `title` | string | `"title"` | Title selector (`"selector"` or `"selector/attribute"`). |
|
|
619
|
-
| `
|
|
637
|
+
| `scroll` | bool\|null | `null` | Scroll behavior. `null` = auto (depends on mode and context). |
|
|
620
638
|
| `urlPrefix` | string\|null | `null` | Prefix added to fetched URLs. |
|
|
621
639
|
| `progress` | bool | `true` | Show progress bar during fetch. |
|
|
622
640
|
| `prefetch` | bool | `true` | Prefetch pages on link hover. |
|
|
@@ -629,7 +647,7 @@ mu.init({
|
|
|
629
647
|
|
|
630
648
|
```javascript
|
|
631
649
|
// Load a page programmatically
|
|
632
|
-
mu.load("/page", {
|
|
650
|
+
mu.load("/page", { history: false, target: "#content" });
|
|
633
651
|
|
|
634
652
|
// Get the last URL loaded by µJS
|
|
635
653
|
mu.getLastUrl(); // "/about" or null
|
package/dist/mu.min.js
CHANGED
|
@@ -1,482 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
* µJS (muJS) - Lightweight AJAX navigation library
|
|
3
|
-
* https://mujs.org
|
|
4
|
-
* MIT License
|
|
5
|
-
*
|
|
6
|
-
* @file mu.js
|
|
7
|
-
* @version 1.2
|
|
8
|
-
* @license MIT
|
|
9
|
-
* @see https://mujs.org
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* ============================================================================
|
|
13
|
-
* TABLE OF CONTENTS
|
|
14
|
-
* ============================================================================
|
|
15
|
-
*
|
|
16
|
-
* 1. Overview
|
|
17
|
-
* 2. Installation
|
|
18
|
-
* 3. Basic usage
|
|
19
|
-
* 4. Modes
|
|
20
|
-
* 5. Patch mode
|
|
21
|
-
* 6. Attributes reference
|
|
22
|
-
* 7. Configuration reference
|
|
23
|
-
* 8. Events
|
|
24
|
-
* 9. Morphing (idiomorph)
|
|
25
|
-
* 10. View Transitions
|
|
26
|
-
* 11. Forms
|
|
27
|
-
* 12. Ghost mode
|
|
28
|
-
* 13. Prefetch
|
|
29
|
-
* 14. Progress bar
|
|
30
|
-
* 15. Triggers
|
|
31
|
-
* 16. Server-Sent Events (SSE)
|
|
32
|
-
* 17. Programmatic usage
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* ============================================================================
|
|
36
|
-
* 1. OVERVIEW
|
|
37
|
-
* ============================================================================
|
|
38
|
-
*
|
|
39
|
-
* µJS intercepts clicks on links and form submissions to load pages via AJAX
|
|
40
|
-
* instead of full browser navigation. The fetched content replaces part (or
|
|
41
|
-
* all) of the current page, making navigation faster and smoother.
|
|
42
|
-
*
|
|
43
|
-
* It is inspired by pjax, Turbo (Hotwire), and HTMX, but aims to be simpler
|
|
44
|
-
* and lighter, with zero dependencies.
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* ============================================================================
|
|
48
|
-
* 2. INSTALLATION
|
|
49
|
-
* ============================================================================
|
|
50
|
-
*
|
|
51
|
-
* Include the script in your HTML page:
|
|
52
|
-
*
|
|
53
|
-
* <script src="/path/to/mu.js"><\/script>
|
|
54
|
-
* <script>mu.init();</script>
|
|
55
|
-
*
|
|
56
|
-
* Optionally, load idiomorph before mu.js for DOM morphing support:
|
|
57
|
-
*
|
|
58
|
-
* <script src="/path/to/idiomorph.js"></script>
|
|
59
|
-
* <script src="/path/to/mu.js"></script>
|
|
60
|
-
* <script>mu.init();</script>
|
|
61
|
-
*
|
|
62
|
-
* µJS auto-detects idiomorph. If present, morphing is enabled by default.
|
|
63
|
-
*
|
|
64
|
-
* Or use as an ES module:
|
|
65
|
-
*
|
|
66
|
-
* import mu from './mu.js';
|
|
67
|
-
* mu.init();
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* ============================================================================
|
|
71
|
-
* 3. BASIC USAGE
|
|
72
|
-
* ============================================================================
|
|
73
|
-
*
|
|
74
|
-
* After calling mu.init(), all internal links (href starting with "/") are
|
|
75
|
-
* automatically intercepted. Clicking a link fetches the page via AJAX and
|
|
76
|
-
* replaces the current <body> with the fetched <body>.
|
|
77
|
-
*
|
|
78
|
-
* No modification of your HTML is needed for basic usage.
|
|
79
|
-
*
|
|
80
|
-
* Example — default behavior (full page replacement):
|
|
81
|
-
*
|
|
82
|
-
* <a href="/about">About</a>
|
|
83
|
-
*
|
|
84
|
-
* Example — replace only a fragment:
|
|
85
|
-
*
|
|
86
|
-
* <a href="/about" mu-target="#content" mu-source="#main">About</a>
|
|
87
|
-
*
|
|
88
|
-
* Example — disable µJS on a specific link:
|
|
89
|
-
*
|
|
90
|
-
* <a href="/file.pdf" mu-disabled>Download</a>
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* ============================================================================
|
|
94
|
-
* 4. MODES
|
|
95
|
-
* ============================================================================
|
|
96
|
-
*
|
|
97
|
-
* The "mu-mode" attribute controls how fetched content is injected into the
|
|
98
|
-
* current page. Default mode is "replace".
|
|
99
|
-
*
|
|
100
|
-
* replace Replace the target node with the source node (default).
|
|
101
|
-
* update Replace the inner content of the target with the source's
|
|
102
|
-
* inner content.
|
|
103
|
-
* prepend Insert the source node at the beginning of the target.
|
|
104
|
-
* append Insert the source node at the end of the target.
|
|
105
|
-
* before Insert the source node before the target.
|
|
106
|
-
* after Insert the source node after the target.
|
|
107
|
-
* remove Remove the target node (source content is ignored).
|
|
108
|
-
* none Do nothing to the DOM (events are still fired).
|
|
109
|
-
* patch Process multiple targeted fragments (see section 5).
|
|
110
|
-
*
|
|
111
|
-
* Example:
|
|
112
|
-
*
|
|
113
|
-
* <a href="/notifications" mu-mode="update" mu-target="#notifs">
|
|
114
|
-
* Refresh notifications
|
|
115
|
-
* </a>
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* ============================================================================
|
|
119
|
-
* 5. PATCH MODE
|
|
120
|
-
* ============================================================================
|
|
121
|
-
*
|
|
122
|
-
* In patch mode, the fetched HTML contains multiple fragments, each targeting
|
|
123
|
-
* a different part of the current page. Fragments are identified by the
|
|
124
|
-
* "mu-patch-target" attribute.
|
|
125
|
-
*
|
|
126
|
-
* Link triggering a patch:
|
|
127
|
-
*
|
|
128
|
-
* <a href="/api/comments/new" mu-mode="patch">Add comment</a>
|
|
129
|
-
*
|
|
130
|
-
* Server response (plain HTML):
|
|
131
|
-
*
|
|
132
|
-
* <!-- Replaces #comment-42 (default mode: replace) -->
|
|
133
|
-
* <div mu-patch-target="#comment-42" class="comment">
|
|
134
|
-
* Updated comment
|
|
135
|
-
* </div>
|
|
136
|
-
*
|
|
137
|
-
* <!-- Appends to #comments -->
|
|
138
|
-
* <div class="comment" mu-patch-target="#comments" mu-patch-mode="append">
|
|
139
|
-
* New comment
|
|
140
|
-
* </div>
|
|
141
|
-
*
|
|
142
|
-
* <!-- Updates the page title -->
|
|
143
|
-
* <title mu-patch-target="title">New title</title>
|
|
144
|
-
*
|
|
145
|
-
* <!-- Removes an element -->
|
|
146
|
-
* <div mu-patch-target="#old-banner" mu-patch-mode="remove"></div>
|
|
147
|
-
*
|
|
148
|
-
* <!-- Adds a stylesheet -->
|
|
149
|
-
* <link rel="stylesheet" href="/css/gallery.css"
|
|
150
|
-
* mu-patch-target="head" mu-patch-mode="append">
|
|
151
|
-
*
|
|
152
|
-
* Patch fragments are standard HTML elements. No special tags needed.
|
|
153
|
-
* The mu-patch-* attributes are kept on injected nodes for debugging.
|
|
154
|
-
*
|
|
155
|
-
* By default, patch mode does not modify browser history. To enable it:
|
|
156
|
-
*
|
|
157
|
-
* <a href="/products?cat=3" mu-mode="patch" mu-patch-ghost="false">
|
|
158
|
-
* Filter
|
|
159
|
-
* </a>
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* ============================================================================
|
|
163
|
-
* 6. ATTRIBUTES REFERENCE
|
|
164
|
-
* ============================================================================
|
|
165
|
-
*
|
|
166
|
-
* All attributes support both "mu-*" and "data-mu-*" syntax.
|
|
167
|
-
*
|
|
168
|
-
* mu-disabled Disable µJS on this element.
|
|
169
|
-
* mu-mode Injection mode (see section 4).
|
|
170
|
-
* mu-target CSS selector for the target node in the current page.
|
|
171
|
-
* mu-source CSS selector for the source node in the fetched page.
|
|
172
|
-
* mu-url Override the URL to fetch (instead of href/action).
|
|
173
|
-
* mu-prefix URL prefix for the fetch request.
|
|
174
|
-
* mu-title Selector for the title node. Supports
|
|
175
|
-
* "selector/attribute" syntax. Empty string to disable
|
|
176
|
-
* title update.
|
|
177
|
-
* mu-ghost "true" to skip browser history and scroll-to-top.
|
|
178
|
-
* mu-ghost-redirect "true" to skip history for redirected responses.
|
|
179
|
-
* mu-scroll-to-top "true"/"false" to force or prevent scrolling.
|
|
180
|
-
* mu-morph "false" to disable morphing on this element.
|
|
181
|
-
* mu-transition "false" to disable view transitions on this element.
|
|
182
|
-
* mu-prefetch "false" to disable prefetch on hover for this link.
|
|
183
|
-
* mu-method HTTP method: "get", "post", "put", "patch",
|
|
184
|
-
* "delete", or "sse". Default: "get" for links,
|
|
185
|
-
* form's method attribute for forms.
|
|
186
|
-
* mu-trigger Event trigger: "click", "submit", "change",
|
|
187
|
-
* "blur", "focus", "load". Default depends on
|
|
188
|
-
* element type (see section 15).
|
|
189
|
-
* mu-debounce Debounce delay in milliseconds (e.g. "500").
|
|
190
|
-
* mu-repeat Repeat interval in ms for polling (e.g. "5000").
|
|
191
|
-
* mu-post (Deprecated) Use mu-method="post" instead.
|
|
192
|
-
* mu-confirm Confirmation message shown before loading.
|
|
193
|
-
* mu-confirm-quit (Forms) Enable quit-page confirmation when the form
|
|
194
|
-
* has been modified.
|
|
195
|
-
* mu-validate Name of a JS function for form validation. Receives
|
|
196
|
-
* the form element. Must return true/false.
|
|
197
|
-
* mu-patch-target (Patch fragments) CSS selector of the target node.
|
|
198
|
-
* mu-patch-mode (Patch fragments) Injection mode for this fragment.
|
|
199
|
-
* mu-patch-ghost "false" to add the URL to browser history in patch
|
|
200
|
-
* mode (default: "true", no history).
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* ============================================================================
|
|
204
|
-
* 7. CONFIGURATION REFERENCE
|
|
205
|
-
* ============================================================================
|
|
206
|
-
*
|
|
207
|
-
* Pass an object to mu.init() to override defaults:
|
|
208
|
-
*
|
|
209
|
-
* mu.init({
|
|
210
|
-
* ghost: true,
|
|
211
|
-
* processForms: false,
|
|
212
|
-
* morph: false
|
|
213
|
-
* });
|
|
214
|
-
*
|
|
215
|
-
* processLinks (bool) Process <a> tags. Default: true.
|
|
216
|
-
* processForms (bool) Process <form> tags. Default: true.
|
|
217
|
-
* ghost (bool) Ghost mode for all links. Default: false.
|
|
218
|
-
* ghostRedirect (bool) Ghost mode for redirections. Default: false.
|
|
219
|
-
* mode (string) Default injection mode. Default: "replace".
|
|
220
|
-
* target (string) Default target selector. Default: "body".
|
|
221
|
-
* source (string) Default source selector. Default: "body".
|
|
222
|
-
* title (string) Title selector. Default: "title".
|
|
223
|
-
* scrollToTop (bool) Force scroll behavior. Default: null (auto).
|
|
224
|
-
* urlPrefix (string) Prefix added to fetched URLs. Default: null.
|
|
225
|
-
* progress (bool) Show progress bar. Default: true.
|
|
226
|
-
* prefetch (bool) Prefetch on hover. Default: true.
|
|
227
|
-
* morph (bool) Enable DOM morphing. Default: true.
|
|
228
|
-
* transition (bool) Enable View Transitions. Default: true.
|
|
229
|
-
* confirmQuitText (string) Quit-page confirmation message.
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* ============================================================================
|
|
233
|
-
* 8. EVENTS
|
|
234
|
-
* ============================================================================
|
|
235
|
-
*
|
|
236
|
-
* µJS dispatches CustomEvents on `document`. All events carry a `detail`
|
|
237
|
-
* object with `lastUrl` and `previousUrl`.
|
|
238
|
-
*
|
|
239
|
-
* mu:init Fired after initialization.
|
|
240
|
-
* detail: {url}
|
|
241
|
-
*
|
|
242
|
-
* mu:before-fetch Fired before fetching. Cancelable (preventDefault()
|
|
243
|
-
* aborts the load).
|
|
244
|
-
* detail: {url, fetchUrl, config, sourceElement}
|
|
245
|
-
*
|
|
246
|
-
* mu:before-render Fired after fetch, before DOM injection. Cancelable.
|
|
247
|
-
* detail.html can be modified to alter injected content.
|
|
248
|
-
* detail: {url, finalUrl, html, config}
|
|
249
|
-
*
|
|
250
|
-
* mu:after-render Fired after DOM injection.
|
|
251
|
-
* detail: {url, finalUrl, mode}
|
|
252
|
-
*
|
|
253
|
-
* mu:fetch-error Fired on fetch failure or HTTP error.
|
|
254
|
-
* detail: {url, fetchUrl, status, response, error}
|
|
255
|
-
*
|
|
256
|
-
* Example:
|
|
257
|
-
*
|
|
258
|
-
* document.addEventListener("mu:after-render", function(e) {
|
|
259
|
-
* console.log("Loaded: " + e.detail.url);
|
|
260
|
-
* myApp.initWidgets();
|
|
261
|
-
* });
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
* ============================================================================
|
|
265
|
-
* 9. MORPHING (IDIOMORPH)
|
|
266
|
-
* ============================================================================
|
|
267
|
-
*
|
|
268
|
-
* When a morph library is available, µJS uses it for "replace" and "update"
|
|
269
|
-
* modes to preserve DOM state (focus, scroll position, video playback, etc.).
|
|
270
|
-
*
|
|
271
|
-
* µJS auto-detects idiomorph (https://github.com/bigskysoftware/idiomorph):
|
|
272
|
-
*
|
|
273
|
-
* <script src="/path/to/idiomorph.js"></script>
|
|
274
|
-
* <script src="/path/to/mu.js"></script>
|
|
275
|
-
*
|
|
276
|
-
* To use a different morph library:
|
|
277
|
-
*
|
|
278
|
-
* mu.init();
|
|
279
|
-
* mu.setMorph(function(target, html, opts) {
|
|
280
|
-
* myMorphLib.morph(target, html, opts);
|
|
281
|
-
* });
|
|
282
|
-
*
|
|
283
|
-
* Morphing can be disabled globally (morph: false in config) or per-element
|
|
284
|
-
* (mu-morph="false").
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* ============================================================================
|
|
288
|
-
* 10. VIEW TRANSITIONS
|
|
289
|
-
* ============================================================================
|
|
290
|
-
*
|
|
291
|
-
* µJS uses the View Transitions API (document.startViewTransition) when
|
|
292
|
-
* supported by the browser. This provides smooth animated transitions between
|
|
293
|
-
* page states.
|
|
294
|
-
*
|
|
295
|
-
* Enabled by default. Falls back silently on unsupported browsers.
|
|
296
|
-
*
|
|
297
|
-
* Disable globally:
|
|
298
|
-
*
|
|
299
|
-
* mu.init({ transition: false });
|
|
300
|
-
*
|
|
301
|
-
* Disable per-element:
|
|
302
|
-
*
|
|
303
|
-
* <a href="/page" mu-transition="false">Link</a>
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* ============================================================================
|
|
307
|
-
* 11. FORMS
|
|
308
|
-
* ============================================================================
|
|
309
|
-
*
|
|
310
|
-
* µJS intercepts form submissions. HTML5 validation (reportValidity) is
|
|
311
|
-
* checked before any fetch.
|
|
312
|
-
*
|
|
313
|
-
* GET forms: data is serialized as query string, behaves like a link.
|
|
314
|
-
* POST forms: data is sent as FormData. Ghost mode is enabled by default
|
|
315
|
-
* (POST responses should not be replayed via browser back button).
|
|
316
|
-
*
|
|
317
|
-
* Example — POST form in patch mode:
|
|
318
|
-
*
|
|
319
|
-
* <form action="/comment/create" method="post" mu-mode="patch">
|
|
320
|
-
* <textarea name="body"></textarea>
|
|
321
|
-
* <button type="submit">Send</button>
|
|
322
|
-
* </form>
|
|
323
|
-
*
|
|
324
|
-
* Quit-page confirmation: add mu-confirm-quit to a form. If any input is
|
|
325
|
-
* modified, the user will be prompted before navigating away.
|
|
326
|
-
*
|
|
327
|
-
* <form action="/save" method="post" mu-confirm-quit>...</form>
|
|
328
|
-
*
|
|
329
|
-
* Custom validation via mu-validate attribute:
|
|
330
|
-
*
|
|
331
|
-
* <form action="/save" method="post" mu-validate="myValidator">...</form>
|
|
332
|
-
* <script>
|
|
333
|
-
* function myValidator(form) {
|
|
334
|
-
* return form.querySelector('#name').value.length > 0;
|
|
335
|
-
* }
|
|
336
|
-
* </script>
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
* ============================================================================
|
|
340
|
-
* 12. GHOST MODE
|
|
341
|
-
* ============================================================================
|
|
342
|
-
*
|
|
343
|
-
* Ghost mode prevents a navigation from being added to browser history and
|
|
344
|
-
* disables automatic scroll-to-top.
|
|
345
|
-
*
|
|
346
|
-
* <a href="/panel" mu-ghost>Open panel</a>
|
|
347
|
-
*
|
|
348
|
-
* Global ghost mode:
|
|
349
|
-
*
|
|
350
|
-
* mu.init({ ghost: true });
|
|
351
|
-
*
|
|
352
|
-
* In patch mode, ghost is enabled by default (mu-patch-ghost="true").
|
|
353
|
-
* Use mu-patch-ghost="false" to add the URL to history.
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
* ============================================================================
|
|
357
|
-
* 13. PREFETCH
|
|
358
|
-
* ============================================================================
|
|
359
|
-
*
|
|
360
|
-
* When enabled (default), µJS fetches the page on mouse hover, before the
|
|
361
|
-
* user clicks. This saves ~100-300ms of perceived loading time.
|
|
362
|
-
*
|
|
363
|
-
* The prefetch cache stores one entry per URL. It is consumed and cleared
|
|
364
|
-
* when the link is clicked.
|
|
365
|
-
*
|
|
366
|
-
* Disable globally:
|
|
367
|
-
*
|
|
368
|
-
* mu.init({ prefetch: false });
|
|
369
|
-
*
|
|
370
|
-
* Disable per-link:
|
|
371
|
-
*
|
|
372
|
-
* <a href="/heavy-page" mu-prefetch="false">Link</a>
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
* ============================================================================
|
|
376
|
-
* 14. PROGRESS BAR
|
|
377
|
-
* ============================================================================
|
|
378
|
-
*
|
|
379
|
-
* A thin progress bar is displayed at the top of the page during fetch.
|
|
380
|
-
* It is styled with inline CSS (no external stylesheet needed).
|
|
381
|
-
*
|
|
382
|
-
* The bar element has id="mu-progress" and can be styled via CSS:
|
|
383
|
-
*
|
|
384
|
-
* #mu-progress { background: red !important; height: 5px !important; }
|
|
385
|
-
*
|
|
386
|
-
* Disable globally:
|
|
387
|
-
*
|
|
388
|
-
* mu.init({ progress: false });
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
* ============================================================================
|
|
392
|
-
* 15. TRIGGERS
|
|
393
|
-
* ============================================================================
|
|
394
|
-
*
|
|
395
|
-
* µJS supports custom event triggers via the mu-trigger attribute. This
|
|
396
|
-
* allows any element with a mu-url to initiate a fetch on events other
|
|
397
|
-
* than click or submit.
|
|
398
|
-
*
|
|
399
|
-
* Default triggers (when mu-trigger is absent):
|
|
400
|
-
* <a> click
|
|
401
|
-
* <form> submit
|
|
402
|
-
* <input>, <textarea>, <select> change
|
|
403
|
-
* Any other element click
|
|
404
|
-
*
|
|
405
|
-
* Trigger mapping to browser events:
|
|
406
|
-
* click → click (handled by event delegation)
|
|
407
|
-
* submit → submit (handled by event delegation)
|
|
408
|
-
* change → input (browser event)
|
|
409
|
-
* blur → change + blur (deduplicated)
|
|
410
|
-
* focus → focus
|
|
411
|
-
* load → fires immediately when rendered
|
|
412
|
-
*
|
|
413
|
-
* Example — live search with debounce:
|
|
414
|
-
*
|
|
415
|
-
* <input type="text" name="q"
|
|
416
|
-
* mu-trigger="change" mu-debounce="500"
|
|
417
|
-
* mu-url="/search" mu-target="#results" mu-source="#results"
|
|
418
|
-
* mu-mode="update">
|
|
419
|
-
*
|
|
420
|
-
* Example — polling every 5 seconds:
|
|
421
|
-
*
|
|
422
|
-
* <div mu-trigger="load" mu-repeat="5000"
|
|
423
|
-
* mu-url="/notifications" mu-target="#notifs" mu-mode="update">
|
|
424
|
-
* </div>
|
|
425
|
-
*
|
|
426
|
-
* Example — action on focus:
|
|
427
|
-
*
|
|
428
|
-
* <input type="text" mu-trigger="focus"
|
|
429
|
-
* mu-url="/suggestions" mu-target="#suggestions"
|
|
430
|
-
* mu-mode="update">
|
|
431
|
-
*
|
|
432
|
-
* Triggers other than click/submit default to ghost mode (no history).
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
* ============================================================================
|
|
436
|
-
* 16. SERVER-SENT EVENTS (SSE)
|
|
437
|
-
* ============================================================================
|
|
438
|
-
*
|
|
439
|
-
* µJS supports Server-Sent Events via mu-method="sse". This opens an
|
|
440
|
-
* EventSource connection to the given URL. Each incoming message is
|
|
441
|
-
* treated as HTML and rendered according to the element's mode.
|
|
442
|
-
*
|
|
443
|
-
* Example — real-time chat with patch mode:
|
|
444
|
-
*
|
|
445
|
-
* <div mu-trigger="load" mu-url="/chat/stream"
|
|
446
|
-
* mu-mode="patch" mu-method="sse">
|
|
447
|
-
* </div>
|
|
448
|
-
*
|
|
449
|
-
* The server sends SSE messages where each data payload is HTML containing
|
|
450
|
-
* patch fragments (with mu-patch-target attributes).
|
|
451
|
-
*
|
|
452
|
-
* Limitations:
|
|
453
|
-
* - EventSource does not support custom HTTP headers. Use query
|
|
454
|
-
* parameters for authentication or identification.
|
|
455
|
-
* - Browser limit: ~6 SSE connections per domain in HTTP/1.1.
|
|
456
|
-
* Use HTTP/2 to avoid this limit.
|
|
457
|
-
* - SSE connections are automatically closed when the element is
|
|
458
|
-
* removed from the DOM (via _cleanupTriggers).
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
* ============================================================================
|
|
462
|
-
* 17. PROGRAMMATIC USAGE
|
|
463
|
-
* ============================================================================
|
|
464
|
-
*
|
|
465
|
-
* // Load a page
|
|
466
|
-
* mu.load("/page", { ghost: true, target: "#content" });
|
|
467
|
-
*
|
|
468
|
-
* // Get last loaded URL
|
|
469
|
-
* mu.getLastUrl();
|
|
470
|
-
*
|
|
471
|
-
* // Get previous URL
|
|
472
|
-
* mu.getPreviousUrl();
|
|
473
|
-
*
|
|
474
|
-
* // Enable/disable quit-page confirmation
|
|
475
|
-
* mu.setConfirmQuit(true);
|
|
476
|
-
* mu.setConfirmQuit(false);
|
|
477
|
-
*
|
|
478
|
-
* // Register a custom morph function
|
|
479
|
-
* mu.setMorph(myMorphFunction);
|
|
480
|
-
*
|
|
481
|
-
*/
|
|
482
|
-
var mu=mu||new function(){this._VERSION="1.2",this._defaults={processLinks:!0,processForms:!0,ghost:!1,ghostRedirect:!1,mode:"replace",target:"body",source:"body",title:"title",scrollToTop:null,urlPrefix:null,progress:!0,prefetch:!0,prefetchTtl:3e3,morph:!0,transition:!0,confirmQuitText:"Are you sure you want to leave this page?"},this._cfg={},this._lastUrl=null,this._previousUrl=null,this._abortCtrl=null,this._progressEl=null,this._prefetchCache=new Map,this._confirmQuit=!1,this._jsIncludes={},this._morph=null,this._initialized=!1,this.init=function(t){if(mu._cfg=Object.assign({},mu._defaults,t||{}),mu._cfg._titleAttr=null,mu._cfg.title){var e=mu._cfg.title.lastIndexOf("/");-1!==e&&(mu._cfg._titleAttr=mu._cfg.title.substring(e+1),mu._cfg.title=mu._cfg.title.substring(0,e))}mu._morph||void 0===window.Idiomorph||"function"!=typeof window.Idiomorph.morph||(mu._morph=function(t,e,r){window.Idiomorph.morph(t,e,r)});for(var r=document.querySelectorAll("script[src]"),u=0;u<r.length;u++)mu._jsIncludes[r[u].getAttribute("src")]=!0;mu._initialized||(document.addEventListener("click",mu._onClick),document.addEventListener("submit",mu._onSubmit),document.addEventListener("mouseover",mu._onMouseOver),document.addEventListener("mouseout",mu._onMouseOut),document.addEventListener("input",mu._onInput),window.addEventListener("popstate",mu._onPopState),window.addEventListener("beforeunload",mu._onBeforeUnload),mu._initialized=!0),window.history.replaceState({mu:!0,url:location.pathname+location.search},""),mu._initTriggers(document.body),mu._emit("mu:init",{url:location.pathname+location.search})},this.load=function(t,e){var r=Object.assign({},mu._cfg,e||{});mu._loadExec(t,r)},this.getLastUrl=function(){return mu._lastUrl},this.getPreviousUrl=function(){return mu._previousUrl},this.setConfirmQuit=function(t){mu._confirmQuit=!!t},this.setMorph=function(t){mu._morph=t},this._attr=function(t,e){return t.hasAttribute("mu-"+e)?t.getAttribute("mu-"+e):t.hasAttribute("data-mu-"+e)?t.getAttribute("data-mu-"+e):null},this._attrBool=function(t,e,r){var u=mu._attr(t,e);return null===u?r:""===u||"true"===u||"false"!==u&&r},this._shouldProcess=function(t){if("true"===mu._attr(t,"disabled")||""===mu._attr(t,"disabled"))return!1;if("false"===t.getAttribute("mu")||"false"===t.getAttribute("data-mu"))return!1;if(t.hasAttribute("target"))return!1;if("A"===t.tagName&&t.hasAttribute("onclick"))return!1;if("FORM"===t.tagName&&t.hasAttribute("onsubmit"))return!1;if("true"===t.getAttribute("mu")||"true"===t.getAttribute("data-mu"))return!0;var e=mu._attr(t,"url");if(null!==e)return!(!e.startsWith("/")||e.startsWith("//"));var r=t.getAttribute("href")||t.getAttribute("action")||"";return!(!r.startsWith("/")||r.startsWith("//"))},this._elemCfg=function(t){var e,r=Object.assign({},mu._cfg);if(null!==(e=mu._attr(t,"mode"))&&(r.mode=e),null!==(e=mu._attr(t,"target"))&&(r.target=e),null!==(e=mu._attr(t,"source"))&&(r.source=e),null!==(e=mu._attr(t,"title"))){r.title=e,r._titleAttr=null;var u=e.lastIndexOf("/");-1!==u&&(r._titleAttr=e.substring(u+1),r.title=e.substring(0,u))}return null!==(e=mu._attr(t,"url"))&&(r._url=e),null!==(e=mu._attr(t,"prefix"))&&(r.urlPrefix=e),r.ghost=mu._attrBool(t,"ghost",r.ghost),r.ghostRedirect=mu._attrBool(t,"ghost-redirect",r.ghostRedirect),r.scrollToTop=mu._attrBool(t,"scroll-to-top",r.scrollToTop),r.morph=mu._attrBool(t,"morph",r.morph),r.transition=mu._attrBool(t,"transition",r.transition),null!==(e=mu._attr(t,"method"))&&(r.method=e.toLowerCase()),!r.method&&mu._attrBool(t,"post",!1)&&(r.method="post"),r.confirm=mu._attr(t,"confirm"),r.patchGhost=mu._attrBool(t,"patch-ghost",!0),r},this._onClick=function(t){var e=t.target.closest("[mu-url], [data-mu-url], a");if(e&&mu._shouldProcess(e)&&"click"===mu._getTrigger(e)&&("A"!==e.tagName||mu._cfg.processLinks)&&!(t.ctrlKey||t.metaKey||t.shiftKey||t.altKey)){t.preventDefault();var r=mu._elemCfg(e),u=r._url||e.getAttribute("href");if(r._sourceElement=e,r.method||(r.method="get"),!r.confirm||window.confirm(r.confirm)){if(mu._confirmQuit){if(!window.confirm(r.confirmQuitText))return;mu.setConfirmQuit(!1)}"sse"!==r.method?mu._loadExec(u,r):mu._openSSE(u,e,r)}}},this._onSubmit=function(t){var e=t.target.closest("form");if(e&&mu._cfg.processForms&&mu._shouldProcess(e)&&"submit"===mu._getTrigger(e)&&e.reportValidity()){var r=mu._attr(e,"validate");if(!r||"function"!=typeof window[r]||window[r](e)){var u=mu._elemCfg(e),o=u.method||(e.getAttribute("method")||"get").toLowerCase();u.method=o;var i=u._url||e.getAttribute("action");if(u._sourceElement=e,"get"!==o)t.preventDefault(),u.postData=new FormData(e),e.hasAttribute("mu-ghost")||e.hasAttribute("data-mu-ghost")||(u.ghost=!0),null===u.scrollToTop&&"patch"!==u.mode&&(u.scrollToTop=!0);else{t.preventDefault();var n=new FormData(e);i=i+"?"+new URLSearchParams(n).toString()}mu.setConfirmQuit(!1),mu._loadExec(i,u)}}},this._onInput=function(t){t.target.closest("form[mu-confirm-quit], form[data-mu-confirm-quit]")&&mu.setConfirmQuit(!0)},this._onMouseOver=function(t){if(mu._cfg.prefetch){var e=t.target.closest("[mu-url], [data-mu-url], a");if(e&&mu._shouldProcess(e)&&!1!==mu._attrBool(e,"prefetch",!0)){var r=mu._attr(e,"url")||e.getAttribute("href");if(r&&!mu._prefetchCache.has(r)&&r!==location.pathname+location.search){mu._prefetchCache.set(r,null);var u=mu._cfg.urlPrefix?mu._cfg.urlPrefix+r:r;fetch(u,{headers:{"X-Requested-With":"mujs","X-Mu-Prefetch":"1"},priority:"low"}).then(function(t){return t.ok?t.text():null}).then(function(t){t?mu._prefetchCache.set(r,{html:t,ts:Date.now()}):mu._prefetchCache.delete(r)}).catch(function(){mu._prefetchCache.delete(r)})}}}},this._onMouseOut=function(t){if(mu._cfg.prefetch){var e=t.target.closest("[mu-url], [data-mu-url], a");if(e){var r=mu._attr(e,"url")||e.getAttribute("href");r&&mu._prefetchCache.delete(r)}}},this._onPopState=function(t){var e=t.state;if(e&&e.mu){var r=Object.assign({},mu._cfg);r.ghost=!0,r.scrollToTop=!1,r._restoreScroll=void 0!==e.scrollX?{x:e.scrollX,y:e.scrollY}:null,mu._loadExec(e.url,r)}else window.location.href=document.location},this._onBeforeUnload=function(t){mu._confirmQuit&&(t.preventDefault(),t.returnValue=mu._cfg.confirmQuitText)},this._getTrigger=function(t){var e=mu._attr(t,"trigger");if(e)return e;var r=t.tagName;return"FORM"===r?"submit":"INPUT"===r||"TEXTAREA"===r||"SELECT"===r?"change":"click"},this._debounce=function(t,e){var r=null;return function(){clearTimeout(r),r=setTimeout(t,e)}},this._triggerAction=function(t){if(mu._shouldProcess(t)){var e=mu._elemCfg(t),r=e._url||t.getAttribute("href")||t.getAttribute("action");if(r){if(e._sourceElement=t,e.method||(e.method="get"),t.hasAttribute("mu-ghost")||t.hasAttribute("data-mu-ghost")||(e.ghost=!0),"INPUT"===t.tagName||"TEXTAREA"===t.tagName||"SELECT"===t.tagName){var u=t.closest("form");if(u)if("get"===e.method){var o=new FormData(u),i=new URLSearchParams(o).toString();r=r.split("?")[0]+"?"+i}else e.postData=new FormData(u);else t.name&&"get"===e.method&&(r=r.split("?")[0]+"?"+encodeURIComponent(t.name)+"="+encodeURIComponent(t.value))}"sse"!==e.method?mu._loadExec(r,e):mu._openSSE(r,t,e)}}},this._initTriggers=function(t){for(var e=t.querySelectorAll("[mu-url], [data-mu-url], [mu-trigger], [data-mu-trigger]"),r=0;r<e.length;r++){var u=e[r];if(!u._mu_bound){var o=mu._getTrigger(u);if("click"!==o&&"submit"!==o)if(mu._attr(u,"url")||u.getAttribute("href")||u.getAttribute("action")){u._mu_bound=!0;var i=parseInt(mu._attr(u,"debounce"),10)||0,n=parseInt(mu._attr(u,"repeat"),10)||0,a=function(t){return function(){mu._triggerAction(t)}}(u);if(i>0&&(a=mu._debounce(a,i)),n>0&&(a=function(t,e,r){var u=!1;return function(){e(),u||(u=!0,t._mu_interval=setInterval(e,r))}}(u,a,n)),"change"===o)u.addEventListener("input",a);else if("blur"===o){var s=function(t){var e=0;return function(){var r=Date.now();r-e<50||(e=r,t())}}(a);u.addEventListener("change",s),u.addEventListener("blur",s)}else"focus"===o?u.addEventListener("focus",a):"load"===o&&a()}}}},this._cleanupTriggers=function(t){for(var e=t.querySelectorAll?t.querySelectorAll("*"):[],r=0;r<e.length;r++)e[r]._mu_interval&&(clearInterval(e[r]._mu_interval),e[r]._mu_interval=null),e[r]._mu_sse&&(e[r]._mu_sse.close(),e[r]._mu_sse=null);t._mu_interval&&(clearInterval(t._mu_interval),t._mu_interval=null),t._mu_sse&&(t._mu_sse.close(),t._mu_sse=null)},this._openSSE=function(t,e,r){e._mu_sse&&e._mu_sse.close();var u=r.urlPrefix?r.urlPrefix+t:t,o=new EventSource(u);e._mu_sse=o,o.onmessage=function(e){var u={url:t,html:e.data,config:r};mu._emit("mu:before-render",u)&&("patch"===r.mode?mu._renderPatch(u.html,r):mu._renderPage(u.html,r),mu._emit("mu:after-render",{url:t,finalUrl:t,mode:r.mode}))},o.onerror=function(){mu._emit("mu:fetch-error",{url:t,fetchUrl:u,error:new Error("SSE connection error")})}},this._loadExec=async function(t,e){var r=e.urlPrefix?e.urlPrefix+t:t;if(mu._saveScroll(),mu._emit("mu:before-fetch",{url:t,fetchUrl:r,config:e,sourceElement:e._sourceElement||null})){mu._abortCtrl&&mu._abortCtrl.abort(),mu._abortCtrl=new AbortController,mu._showProgress();try{var u=null,o=null,i=t,n=mu._prefetchCache.get(t);if(n&&n.html&&Date.now()-n.ts<mu._cfg.prefetchTtl)u=n.html,mu._prefetchCache.delete(t);else{var a={signal:mu._abortCtrl.signal,headers:{"X-Requested-With":"mujs","X-Mu-Mode":e.mode}},s=(e.method||"get").toUpperCase();if("GET"!==s&&(a.method=s,a.headers["X-Mu-Method"]=s,e.postData&&(a.body=e.postData)),!(o=await fetch(r,a)).ok)return void mu._emit("mu:fetch-error",{url:t,fetchUrl:r,status:o.status,response:o});o.redirected&&(i=new URL(o.url).pathname+new URL(o.url).search),u=await o.text()}var l,m={url:t,finalUrl:i,html:u,config:e};if(!mu._emit("mu:before-render",m))return;if(l="patch"===e.mode?function(){mu._renderPatch(m.html,e)}:function(){mu._renderPage(m.html,e)},e.transition&&document.startViewTransition?document.startViewTransition(l):l(),"patch"===e.mode)e.patchGhost||(mu._previousUrl=mu._lastUrl,mu._lastUrl=i,window.history.pushState({mu:!0,url:i},"",i));else e.ghost||o&&o.redirected&&e.ghostRedirect||e.postData?o&&o.redirected&&!e.ghostRedirect?(mu._previousUrl=mu._lastUrl,mu._lastUrl=i,window.history.pushState({mu:!0,url:i},"",i)):(mu._previousUrl=mu._lastUrl,mu._lastUrl=i):(mu._previousUrl=mu._lastUrl,mu._lastUrl=i,window.history.pushState({mu:!0,url:i},"",i));if("patch"!==e.mode)if(e._restoreScroll)window.scrollTo(e._restoreScroll.x,e._restoreScroll.y);else{var c=e.ghost||o&&o.redirected&&e.ghostRedirect;if(null!==e.scrollToTop?e.scrollToTop:!c){var h=t.indexOf("#");if(-1!==h){var d=document.getElementById(t.substring(h+1));d&&d.scrollIntoView({behavior:"smooth"})}else window.scrollTo(0,0)}}mu.setConfirmQuit(!1),mu._emit("mu:after-render",{url:t,finalUrl:i,mode:e.mode})}catch(e){if("AbortError"===e.name)return;mu._emit("mu:fetch-error",{url:t,fetchUrl:r,error:e})}finally{mu._hideProgress()}}},this._renderPage=function(t,e){var r=(new DOMParser).parseFromString(t,"text/html"),u=null;e.source&&(u=r.querySelector(e.source)),u||(u=r.body);var o=document.querySelector(e.target);if(o){mu._cleanupTriggers(o),mu._applyMode(e.mode,o,u,e),e.ghost||mu._updateTitle(r,e),mu._mergeHead(r);var i=document.querySelector(e.target)||document.body;mu._runScripts(i),mu._initTriggers(i)}else console.warn("[µJS] Target element '"+e.target+"' not found.")},this._renderPatch=function(t,e){for(var r=(new DOMParser).parseFromString(t,"text/html").querySelectorAll("[mu-patch-target], [data-mu-patch-target]"),u=[],o=0;o<r.length;o++){var i=r[o],n=i.getAttribute("mu-patch-target")||i.getAttribute("data-mu-patch-target"),a=i.getAttribute("mu-patch-mode")||i.getAttribute("data-mu-patch-mode")||"replace",s=document.querySelector(n);s?(mu._cleanupTriggers(s),mu._applyMode(a,s,i,e),"remove"!==a&&(mu._runScripts(i),u.push(n))):console.warn("[µJS] Patch target '"+n+"' not found.")}for(var l=0;l<u.length;l++){var m=document.querySelector(u[l]);m&&mu._initTriggers(m)}},this._applyMode=function(t,e,r,u){var o=u.morph&&mu._morph;switch(t){case"update":o?mu._morph(e,r.innerHTML,{morphStyle:"innerHTML"}):e.innerHTML=r.innerHTML;break;case"prepend":e.prepend(r);break;case"append":e.append(r);break;case"before":e.before(r);break;case"after":e.after(r);break;case"remove":e.remove();break;case"none":break;default:"BODY"===e.tagName&&"BODY"===r.tagName?o?mu._morph(e,r.innerHTML,{morphStyle:"innerHTML"}):e.innerHTML=r.innerHTML:o?mu._morph(e,r.outerHTML,{morphStyle:"outerHTML"}):e.replaceWith(r)}},this._updateTitle=function(t,e){if(e.title){var r=t.querySelector(e.title);if(r){var u=e._titleAttr?r.getAttribute(e._titleAttr):r.textContent;u&&(document.title=u)}}},this._mergeHead=function(t){for(var e="link[rel='stylesheet'], style, script",r=document.head.querySelectorAll(e),u=t.head.querySelectorAll(e),o=new Set,i=0;i<r.length;i++)o.add(mu._elKey(r[i]));for(i=0;i<u.length;i++)if(!o.has(mu._elKey(u[i])))if("SCRIPT"===u[i].tagName){for(var n=document.createElement("script"),a=0;a<u[i].attributes.length;a++)n.setAttribute(u[i].attributes[a].name,u[i].attributes[a].value);n.textContent=u[i].textContent,n.hasAttribute("src")&&(mu._jsIncludes[n.getAttribute("src")]=!0),document.head.appendChild(n)}else document.head.appendChild(u[i].cloneNode(!0))},this._elKey=function(t){return"LINK"===t.tagName?"link:"+t.getAttribute("href"):"STYLE"===t.tagName?"style:"+t.textContent.substring(0,100):"SCRIPT"===t.tagName?"script:"+(t.getAttribute("src")||t.textContent.substring(0,100)):t.outerHTML},this._runScripts=function(t){for(var e=t.querySelectorAll("script"),r=0;r<e.length;r++){var u=e[r];if("true"!==mu._attr(u,"disabled")&&""!==mu._attr(u,"disabled")){if(u.hasAttribute("src")){var o=u.getAttribute("src");if(mu._jsIncludes[o])continue;mu._jsIncludes[o]=!0}for(var i=document.createElement("script"),n=0;n<u.attributes.length;n++)i.setAttribute(u.attributes[n].name,u.attributes[n].value);i.textContent=u.textContent,u.parentNode.replaceChild(i,u)}}},this._showProgress=function(){if(mu._cfg.progress){if(!mu._progressEl){mu._progressEl=document.createElement("div"),mu._progressEl.id="mu-progress";var t=mu._progressEl.style;t.position="fixed",t.top="0",t.left="0",t.height="3px",t.background="#29d",t.zIndex="99999",t.transition="width .3s ease",t.width="0"}document.body.appendChild(mu._progressEl),mu._progressEl.offsetWidth,mu._progressEl.style.width="70%"}},this._hideProgress=function(){mu._progressEl&&(mu._progressEl.style.width="100%",setTimeout(function(){mu._progressEl&&(mu._progressEl.style.transition="none",mu._progressEl.style.width="0",mu._progressEl.offsetWidth,mu._progressEl.style.transition="width .3s ease",mu._progressEl.remove())},200))},this._saveScroll=function(){var t=window.history.state;t&&t.mu&&(t.scrollX=window.scrollX,t.scrollY=window.scrollY,window.history.replaceState(t,""))},this._emit=function(t,e){(e=e||{}).lastUrl=mu._lastUrl,e.previousUrl=mu._previousUrl;var r=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:e});return document.dispatchEvent(r)}};
|
|
1
|
+
/* µJS (muJS) - mujs.org */
|
|
2
|
+
var mu=mu||new function(){this._VERSION="1.4.0",this._defaults={processLinks:!0,processForms:!0,history:!0,mode:"replace",target:"body",source:"body",title:"title",scroll:null,urlPrefix:null,progress:!0,prefetch:!0,prefetchTtl:3e3,morph:!0,transition:!0,confirmQuitText:"Are you sure you want to leave this page?"},this._cfg={},this._lastUrl=null,this._previousUrl=null,this._abortCtrl=null,this._progressEl=null,this._prefetchCache=new Map,this._prefetchTimer=null,this._confirmQuit=!1,this._jsIncludes={},this._morph=null,this._initialized=!1,this.init=function(t){if(mu._cfg=Object.assign({},mu._defaults,t||{}),mu._cfg._titleAttr=null,mu._cfg.title){var e=mu._cfg.title.lastIndexOf("/");-1!==e&&(mu._cfg._titleAttr=mu._cfg.title.substring(e+1),mu._cfg.title=mu._cfg.title.substring(0,e))}mu._morph||void 0===window.Idiomorph||"function"!=typeof window.Idiomorph.morph||(mu._morph=function(t,e,r){window.Idiomorph.morph(t,e,r)});for(var r=document.querySelectorAll("script[src]"),u=0;u<r.length;u++)mu._jsIncludes[r[u].getAttribute("src")]=!0;mu._initialized||(document.addEventListener("click",mu._onClick),document.addEventListener("submit",mu._onSubmit),document.addEventListener("mouseover",mu._onMouseOver),document.addEventListener("mouseout",mu._onMouseOut),document.addEventListener("input",mu._onInput),window.addEventListener("popstate",mu._onPopState),window.addEventListener("beforeunload",mu._onBeforeUnload),mu._initialized=!0),window.history.replaceState({mu:!0,url:location.pathname+location.search},""),document.body&&mu._initTriggers(document.body),mu._emit("mu:init",{url:location.pathname+location.search})},this.load=function(t,e){var r=Object.assign({},mu._cfg,e||{});mu._loadExec(t,r)},this.getLastUrl=function(){return mu._lastUrl},this.getPreviousUrl=function(){return mu._previousUrl},this.setConfirmQuit=function(t){mu._confirmQuit=!!t},this.setMorph=function(t){mu._morph=t},this._attr=function(t,e){return t.hasAttribute("mu-"+e)?t.getAttribute("mu-"+e):t.hasAttribute("data-mu-"+e)?t.getAttribute("data-mu-"+e):null},this._attrBool=function(t,e,r){var u=mu._attr(t,e);return null===u?r:""===u||"true"===u||"false"!==u&&r},this._shouldProcess=function(t){if("true"===mu._attr(t,"disabled")||""===mu._attr(t,"disabled"))return!1;if("false"===t.getAttribute("mu")||"false"===t.getAttribute("data-mu"))return!1;if(t.hasAttribute("target"))return!1;if("A"===t.tagName&&t.hasAttribute("onclick"))return!1;if("FORM"===t.tagName&&t.hasAttribute("onsubmit"))return!1;if("true"===t.getAttribute("mu")||"true"===t.getAttribute("data-mu"))return!0;var e=mu._attr(t,"url");if(null!==e)return!(!e.startsWith("/")||e.startsWith("//"));var r=t.getAttribute("href")||t.getAttribute("action")||"";return!(!r.startsWith("/")||r.startsWith("//"))},this._elemCfg=function(t){var e,r=Object.assign({},mu._cfg);if(null!==(e=mu._attr(t,"mode"))&&(r.mode=e),null!==(e=mu._attr(t,"target"))&&(r.target=e),null!==(e=mu._attr(t,"source"))&&(r.source=e),null!==(e=mu._attr(t,"title"))){r.title=e,r._titleAttr=null;var u=e.lastIndexOf("/");-1!==u&&(r._titleAttr=e.substring(u+1),r.title=e.substring(0,u))}return null!==(e=mu._attr(t,"url"))&&(r._url=e),null!==(e=mu._attr(t,"prefix"))&&(r.urlPrefix=e),r.history=mu._attrBool(t,"history",r.history),r.scroll=mu._attrBool(t,"scroll",r.scroll),r.morph=mu._attrBool(t,"morph",r.morph),r.transition=mu._attrBool(t,"transition",r.transition),null!==(e=mu._attr(t,"method"))&&(r.method=e.toLowerCase()),r.confirm=mu._attr(t,"confirm"),r.patchHistory=mu._attrBool(t,"patch-history",!1),"replace"!==r.mode&&"update"!==r.mode&&"patch"!==r.mode&&(t.hasAttribute("mu-history")||t.hasAttribute("data-mu-history")||(r.history=!1),t.hasAttribute("mu-scroll")||t.hasAttribute("data-mu-scroll")||null!==r.scroll||(r.scroll=!1)),r},this._onClick=function(t){var e=t.target.closest("[mu-url], [data-mu-url], a");if(e&&mu._shouldProcess(e)&&"click"===mu._getTrigger(e)&&("A"!==e.tagName||mu._cfg.processLinks)&&!(t.ctrlKey||t.metaKey||t.shiftKey||t.altKey)){t.preventDefault();var r=mu._elemCfg(e),u=r._url||e.getAttribute("href");if(r._sourceElement=e,r.method||(r.method="get"),!r.confirm||window.confirm(r.confirm)){if(mu._confirmQuit){if(!window.confirm(r.confirmQuitText))return;mu.setConfirmQuit(!1)}"sse"!==r.method?mu._loadExec(u,r):mu._openSSE(u,e,r)}}},this._onSubmit=function(t){var e=t.target.closest("form");if(e&&mu._cfg.processForms&&mu._shouldProcess(e)&&"submit"===mu._getTrigger(e)&&e.reportValidity()){var r=mu._attr(e,"validate");if(!r||"function"!=typeof window[r]||window[r](e)){var u=mu._elemCfg(e),o=u.method||(e.getAttribute("method")||"get").toLowerCase();u.method=o;var i=u._url||e.getAttribute("action");if(u._sourceElement=e,"get"!==o)t.preventDefault(),u.postData=new FormData(e),e.hasAttribute("mu-history")||e.hasAttribute("data-mu-history")||(u.history=!1),null===u.scroll&&"patch"!==u.mode&&(u.scroll=!0);else{t.preventDefault();var n=new FormData(e);i=i+"?"+new URLSearchParams(n).toString()}mu.setConfirmQuit(!1),mu._loadExec(i,u)}}},this._onInput=function(t){t.target.closest("form[mu-confirm-quit], form[data-mu-confirm-quit]")&&mu.setConfirmQuit(!0)},this._onMouseOver=function(t){if(mu._cfg.prefetch){var e=t.target.closest("[mu-url], [data-mu-url], a");if(e&&mu._shouldProcess(e)&&"click"===mu._getTrigger(e)){var r=mu._attr(e,"method");if((!r||"get"===r.toLowerCase())&&!1!==mu._attrBool(e,"prefetch",!0)){var u=mu._attr(e,"url")||e.getAttribute("href");if(u){var o=mu._prefetchCache.get(u);o&&Date.now()-o.ts<mu._cfg.prefetchTtl||u!==location.pathname+location.search&&(clearTimeout(mu._prefetchTimer),mu._prefetchTimer=setTimeout(function(){mu._prefetchTimer=null;var t=mu._prefetchCache.get(u);if(!(t&&Date.now()-t.ts<mu._cfg.prefetchTtl)){var e=mu._cfg.urlPrefix?mu._cfg.urlPrefix+u:u,r=fetch(e,{headers:{"X-Requested-With":"mujs","X-Mu-Prefetch":"1"},priority:"low"}).then(function(t){return t.ok?t.text():null}).catch(function(){return null});mu._prefetchCache.set(u,{promise:r,ts:Date.now()})}},50))}}}}},this._onMouseOut=function(t){mu._prefetchTimer&&(clearTimeout(mu._prefetchTimer),mu._prefetchTimer=null)},this._onPopState=function(t){var e=t.state;if(e&&e.mu){var r=Object.assign({},mu._cfg);r.history=!1,r.scroll=!1,r._restoreScroll=void 0!==e.scrollX?{x:e.scrollX,y:e.scrollY}:null,mu._loadExec(e.url,r)}else window.location.href=document.location},this._onBeforeUnload=function(t){mu._confirmQuit&&(t.preventDefault(),t.returnValue=mu._cfg.confirmQuitText)},this._getTrigger=function(t){var e=mu._attr(t,"trigger");if(e)return e;var r=t.tagName;return"FORM"===r?"submit":"INPUT"===r||"TEXTAREA"===r||"SELECT"===r?"change":"click"},this._debounce=function(t,e){var r=null;return function(){clearTimeout(r),r=setTimeout(t,e)}},this._triggerAction=function(t){if(mu._shouldProcess(t)){var e=mu._elemCfg(t),r=e._url||t.getAttribute("href")||t.getAttribute("action");if(r){if(e._sourceElement=t,e._trigger=!0,e.transition=!1,e.method||(e.method="get"),t.hasAttribute("mu-history")||t.hasAttribute("data-mu-history")||(e.history=!1),t.hasAttribute("mu-scroll")||t.hasAttribute("data-mu-scroll")||null!==e.scroll||(e.scroll=!1),"INPUT"===t.tagName||"TEXTAREA"===t.tagName||"SELECT"===t.tagName){var u=t.closest("form");if(u)if("get"===e.method){var o=new FormData(u),i=new URLSearchParams(o).toString();r=r.split("?")[0]+"?"+i}else e.postData=new FormData(u);else t.name&&"get"===e.method&&(r=r.split("?")[0]+"?"+encodeURIComponent(t.name)+"="+encodeURIComponent(t.value))}"sse"!==e.method?mu._loadExec(r,e):mu._openSSE(r,t,e)}}},this._initTriggers=function(t){for(var e=t.querySelectorAll("[mu-url], [data-mu-url], [mu-trigger], [data-mu-trigger]"),r=0;r<e.length;r++){var u=e[r];if(!u._mu_bound){var o=mu._getTrigger(u);if("click"!==o&&"submit"!==o)if(mu._attr(u,"url")||u.getAttribute("href")||u.getAttribute("action")){u._mu_bound=!0;var i=parseInt(mu._attr(u,"debounce"),10)||0,n=parseInt(mu._attr(u,"repeat"),10)||0,a=function(t){return function(){mu._triggerAction(t)}}(u);if(i>0&&(a=mu._debounce(a,i)),n>0&&(a=function(t,e,r){var u=!1;return function(){e(),u||(u=!0,t._mu_interval=setInterval(e,r))}}(u,a,n)),"change"===o)u.addEventListener("input",a);else if("blur"===o){var s=function(t){var e=0;return function(){var r=Date.now();r-e<50||(e=r,t())}}(a);u.addEventListener("change",s),u.addEventListener("blur",s)}else"focus"===o?u.addEventListener("focus",a):"load"===o&&a()}}}},this._cleanupTriggers=function(t){for(var e=t.querySelectorAll?t.querySelectorAll("*"):[],r=0;r<e.length;r++)e[r]._mu_interval&&(clearInterval(e[r]._mu_interval),e[r]._mu_interval=null),e[r]._mu_sse&&(e[r]._mu_sse.close(),e[r]._mu_sse=null);t._mu_interval&&(clearInterval(t._mu_interval),t._mu_interval=null),t._mu_sse&&(t._mu_sse.close(),t._mu_sse=null)},this._openSSE=function(t,e,r){e._mu_sse&&e._mu_sse.close();var u=r.urlPrefix?r.urlPrefix+t:t,o=new EventSource(u);e._mu_sse=o,o.onmessage=function(e){var u={url:t,html:e.data,config:r};mu._emit("mu:before-render",u)&&("patch"===r.mode?mu._renderPatch(u.html,r):mu._renderPage(u.html,r),mu._emit("mu:after-render",{url:t,finalUrl:t,mode:r.mode}))},o.onerror=function(){mu._emit("mu:fetch-error",{url:t,fetchUrl:u,error:new Error("SSE connection error")})}},this._loadExec=async function(t,e){var r=e.urlPrefix?e.urlPrefix+t:t;if(e._trigger||mu._saveScroll(),mu._emit("mu:before-fetch",{url:t,fetchUrl:r,config:e,sourceElement:e._sourceElement||null})){var u;e._trigger?u=new AbortController:(mu._abortCtrl&&mu._abortCtrl.abort(),u=mu._abortCtrl=new AbortController),e._trigger||mu._showProgress();try{var o=null,i=null,n=t,a=mu._prefetchCache.get(t);if(a&&a.promise&&Date.now()-a.ts<mu._cfg.prefetchTtl&&(o=await a.promise,u.signal.aborted))return;if(!o){var s={signal:u.signal,headers:{"X-Requested-With":"mujs","X-Mu-Mode":e.mode}},l=(e.method||"get").toUpperCase();if("GET"!==l&&(s.method=l,s.headers["X-Mu-Method"]=l,e.postData&&(s.body=e.postData)),!(i=await fetch(r,s)).ok)return void mu._emit("mu:fetch-error",{url:t,fetchUrl:r,status:i.status,response:i});i.redirected&&(n=new URL(i.url).pathname+new URL(i.url).search),o=await i.text()}e._trigger||mu._prefetchCache.set(t,{promise:Promise.resolve(o),ts:Date.now()});var m,c={url:t,finalUrl:n,html:o,config:e};if(!mu._emit("mu:before-render",c))return;if("patch"===e.mode?e._addHistory=e.patchHistory:(e._addHistory=e.history,i&&i.redirected&&(e._addHistory=!0)),m="patch"===e.mode?function(){mu._renderPatch(c.html,e)}:function(){mu._renderPage(c.html,e)},e.transition&&document.startViewTransition?document.startViewTransition(m):m(),mu._previousUrl=mu._lastUrl,mu._lastUrl=n,e._addHistory&&window.history.pushState({mu:!0,url:n},"",n),"patch"!==e.mode)if(e._restoreScroll)window.scrollTo(e._restoreScroll.x,e._restoreScroll.y);else if(null===e.scroll||e.scroll){var h=t.indexOf("#");if(-1!==h){var _=document.getElementById(t.substring(h+1));_&&_.scrollIntoView({behavior:"smooth"})}else window.scrollTo(0,0)}mu.setConfirmQuit(!1),mu._emit("mu:after-render",{url:t,finalUrl:n,mode:e.mode})}catch(e){if("AbortError"===e.name)return;mu._emit("mu:fetch-error",{url:t,fetchUrl:r,error:e})}finally{e._trigger||mu._hideProgress()}}},this._renderPage=function(t,e){var r=(new DOMParser).parseFromString(t,"text/html"),u=null;e.source&&(u=r.querySelector(e.source)),u||(u=r.body);var o=document.querySelector(e.target);if(o){mu._cleanupTriggers(o),mu._applyMode(e.mode,o,u,e),e._addHistory&&mu._updateTitle(r,e),mu._mergeHead(r);var i=document.querySelector(e.target)||document.body;mu._runScripts(i),mu._initTriggers(i)}else console.warn("[µJS] Target element '"+e.target+"' not found.")},this._renderPatch=function(t,e){for(var r=(new DOMParser).parseFromString(t,"text/html").querySelectorAll("[mu-patch-target], [data-mu-patch-target]"),u=[],o=0;o<r.length;o++){var i=r[o],n=i.getAttribute("mu-patch-target")||i.getAttribute("data-mu-patch-target"),a=i.getAttribute("mu-patch-mode")||i.getAttribute("data-mu-patch-mode")||"replace",s=document.querySelector(n);s?(mu._cleanupTriggers(s),mu._applyMode(a,s,i,e),"remove"!==a&&(mu._runScripts(i),u.push(n))):console.warn("[µJS] Patch target '"+n+"' not found.")}for(var l=0;l<u.length;l++){var m=document.querySelector(u[l]);m&&mu._initTriggers(m)}},this._applyMode=function(t,e,r,u){var o=u.morph&&mu._morph;switch(t){case"update":o?mu._morph(e,r.innerHTML,{morphStyle:"innerHTML"}):e.innerHTML=r.innerHTML;break;case"prepend":e.prepend(r);break;case"append":e.append(r);break;case"before":e.before(r);break;case"after":e.after(r);break;case"remove":e.remove();break;case"none":break;default:"BODY"===e.tagName&&"BODY"===r.tagName?o?mu._morph(e,r.innerHTML,{morphStyle:"innerHTML"}):e.innerHTML=r.innerHTML:o?mu._morph(e,r.outerHTML,{morphStyle:"outerHTML"}):e.replaceWith(r)}},this._updateTitle=function(t,e){if(e.title){var r=t.querySelector(e.title);if(r){var u=e._titleAttr?r.getAttribute(e._titleAttr):r.textContent;u&&(document.title=u)}}},this._mergeHead=function(t){for(var e="link[rel='stylesheet'], style, script",r=document.head.querySelectorAll(e),u=t.head.querySelectorAll(e),o=new Set,i=0;i<r.length;i++)o.add(mu._elKey(r[i]));for(i=0;i<u.length;i++)if(!o.has(mu._elKey(u[i])))if("SCRIPT"===u[i].tagName){for(var n=document.createElement("script"),a=0;a<u[i].attributes.length;a++)n.setAttribute(u[i].attributes[a].name,u[i].attributes[a].value);n.textContent=u[i].textContent,n.hasAttribute("src")&&(mu._jsIncludes[n.getAttribute("src")]=!0),document.head.appendChild(n)}else document.head.appendChild(u[i].cloneNode(!0))},this._elKey=function(t){return"LINK"===t.tagName?"link:"+t.getAttribute("href"):"STYLE"===t.tagName?"style:"+t.textContent.substring(0,100):"SCRIPT"===t.tagName?"script:"+(t.getAttribute("src")||t.textContent.substring(0,100)):t.outerHTML},this._runScripts=function(t){for(var e=t.querySelectorAll("script"),r=0;r<e.length;r++){var u=e[r];if("true"!==mu._attr(u,"disabled")&&""!==mu._attr(u,"disabled")){if(u.hasAttribute("src")){var o=u.getAttribute("src");if(mu._jsIncludes[o])continue;mu._jsIncludes[o]=!0}for(var i=document.createElement("script"),n=0;n<u.attributes.length;n++)i.setAttribute(u.attributes[n].name,u.attributes[n].value);i.textContent=u.textContent,u.parentNode.replaceChild(i,u)}}},this._showProgress=function(){if(mu._cfg.progress){if(!mu._progressEl){mu._progressEl=document.createElement("div"),mu._progressEl.id="mu-progress";var t=mu._progressEl.style;t.position="fixed",t.top="0",t.left="0",t.height="3px",t.background="#29d",t.zIndex="99999",t.transition="width .3s ease",t.width="0"}document.body.appendChild(mu._progressEl),mu._progressEl.offsetWidth,mu._progressEl.style.width="70%"}},this._hideProgress=function(){mu._progressEl&&(mu._progressEl.style.width="100%",setTimeout(function(){mu._progressEl&&(mu._progressEl.style.transition="none",mu._progressEl.style.width="0",mu._progressEl.offsetWidth,mu._progressEl.style.transition="width .3s ease",mu._progressEl.remove())},200))},this._saveScroll=function(){var t=window.history.state;t&&t.mu&&(t.scrollX=window.scrollX,t.scrollY=window.scrollY,window.history.replaceState(t,""))},this._emit=function(t,e){(e=e||{}).lastUrl=mu._lastUrl,e.previousUrl=mu._previousUrl;var r=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:e});return document.dispatchEvent(r)}};
|