@digicreon/mujs 1.0.0 → 1.2.1

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 CHANGED
@@ -9,9 +9,12 @@ No build step required. No dependencies. No framework. Just a single `<script>`
9
9
  Inspired by [pjax](https://github.com/defunkt/jquery-pjax), [Turbo](https://turbo.hotwired.dev/) and [HTMX](https://htmx.org/), µJS aims to be simpler and lighter while covering the most common use cases.
10
10
 
11
11
  - 🚀 **Fast** — Prefetch on hover, no full page reload, progress bar
12
- - 🪶 **Lightweight** — Single file, ~3 KB gzipped, zero dependencies
12
+ - 🪶 **Lightweight** — Single file, ~10 KB gzipped, zero dependencies
13
13
  - 🔌 **Drop-in** — Works with any backend (PHP, Python, Ruby, Go…), no server-side changes needed
14
14
  - 🧩 **Patch mode** — Update multiple page fragments in a single request
15
+ - 🎯 **Triggers** — Any element, any event: live search, polling, focus actions
16
+ - 🔄 **HTTP verbs** — GET, POST, PUT, PATCH, DELETE on links, buttons, and forms
17
+ - 📡 **SSE** — Real-time updates via Server-Sent Events
15
18
  - ✨ **Modern** — View Transitions, DOM morphing (via idiomorph), `fetch` API, event delegation
16
19
 
17
20
 
@@ -22,6 +25,9 @@ Inspired by [pjax](https://github.com/defunkt/jquery-pjax), [Turbo](https://turb
22
25
  - [Modes](#modes)
23
26
  - [Patch mode](#patch-mode)
24
27
  - [Forms](#forms)
28
+ - [HTTP methods](#http-methods)
29
+ - [Triggers](#triggers)
30
+ - [Server-Sent Events (SSE)](#server-sent-events-sse)
25
31
  - [Ghost mode](#ghost-mode)
26
32
  - [Scroll restoration](#scroll-restoration)
27
33
  - [Prefetch](#prefetch)
@@ -40,25 +46,32 @@ Inspired by [pjax](https://github.com/defunkt/jquery-pjax), [Turbo](https://turb
40
46
 
41
47
  ### Via `<script>` tag (recommended)
42
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
+
43
51
  ```html
44
- <script src="/path/to/mu.min.js"></script>
45
- <script>mu.init();</script>
52
+ <body>
53
+ <!-- your content -->
54
+ <script src="/path/to/mu.min.js"></script>
55
+ <script>mu.init();</script>
56
+ </body>
46
57
  ```
47
58
 
48
59
  ### Via CDN
49
60
 
50
61
  ```html
51
62
  <!-- unpkg -->
52
- <script src="https://unpkg.com/mujs/dist/mu.min.js"></script>
63
+ <script src="https://unpkg.com/@digicreon/mujs/dist/mu.min.js"></script>
53
64
 
54
65
  <!-- jsDelivr -->
55
- <script src="https://cdn.jsdelivr.net/npm/mujs/dist/mu.min.js"></script>
66
+ <script src="https://cdn.jsdelivr.net/npm/@digicreon/mujs/dist/mu.min.js"></script>
67
+
68
+ <script>mu.init();</script>
56
69
  ```
57
70
 
58
71
  ### Via npm
59
72
 
60
73
  ```bash
61
- npm install mujs
74
+ npm install @digicreon/mujs
62
75
  ```
63
76
 
64
77
 
@@ -71,8 +84,6 @@ After calling `mu.init()`, all internal links (URLs starting with `/`) are autom
71
84
  <html>
72
85
  <head>
73
86
  <title>My site</title>
74
- <script src="/path/to/mu.min.js"></script>
75
- <script>mu.init();</script>
76
87
  </head>
77
88
  <body>
78
89
  <!-- These links are automatically handled by µJS -->
@@ -91,6 +102,9 @@ After calling `mu.init()`, all internal links (URLs starting with `/`) are autom
91
102
 
92
103
  <!-- This link is NOT handled (explicitly disabled) -->
93
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>
94
108
  </body>
95
109
  </html>
96
110
  ```
@@ -204,6 +218,23 @@ Data is sent as `FormData`. Ghost mode is enabled by default (POST responses sho
204
218
  </form>
205
219
  ```
206
220
 
221
+ ### PUT / PATCH / DELETE forms
222
+
223
+ Use `mu-method` to override the HTTP method. The form data is sent as `FormData`, like POST.
224
+
225
+ ```html
226
+ <!-- PUT form -->
227
+ <form action="/api/user/1" mu-method="put">
228
+ <input type="text" name="name">
229
+ <button type="submit">Update</button>
230
+ </form>
231
+
232
+ <!-- DELETE form (no data needed) -->
233
+ <form action="/api/user/1" mu-method="delete">
234
+ <button type="submit">Delete</button>
235
+ </form>
236
+ ```
237
+
207
238
  ### POST form with patch response
208
239
 
209
240
  ```html
@@ -251,6 +282,139 @@ Add `mu-confirm-quit` to a form. If any input is modified, the user is prompted
251
282
  ```
252
283
 
253
284
 
285
+ ## HTTP methods
286
+
287
+ By default, links use GET and forms use their `method` attribute. The `mu-method` attribute overrides the HTTP method for any element.
288
+
289
+ Supported values: `get`, `post`, `put`, `patch`, `delete`, `sse`.
290
+
291
+ ```html
292
+ <!-- DELETE button -->
293
+ <button mu-url="/api/item/42" mu-method="delete" mu-mode="remove" mu-target="#item-42">
294
+ Delete
295
+ </button>
296
+
297
+ <!-- PUT link -->
298
+ <a href="/api/publish/5" mu-method="put" mu-mode="none">Publish</a>
299
+ ```
300
+
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.
302
+
303
+ > **Note:** `mu-post` is deprecated. Use `mu-method="post"` instead.
304
+
305
+
306
+ ## Triggers
307
+
308
+ µJS supports custom event triggers via the `mu-trigger` attribute. This allows any element with a `mu-url` to initiate a fetch on events other than click or submit.
309
+
310
+ ### Default triggers
311
+
312
+ When `mu-trigger` is absent, the trigger depends on the element type:
313
+
314
+ | Element | Default trigger |
315
+ |---|---|
316
+ | `<a>` | `click` |
317
+ | `<form>` | `submit` |
318
+ | `<input>`, `<textarea>`, `<select>` | `change` |
319
+ | Any other element | `click` |
320
+
321
+ ### Available triggers
322
+
323
+ | Trigger | Browser event(s) | Typical elements |
324
+ |---|---|---|
325
+ | `click` | `click` | Any element (default for `<a>`, `<button>`, `<div>`...) |
326
+ | `submit` | `submit` | `<form>` |
327
+ | `change` | `input` | `<input>`, `<textarea>`, `<select>` |
328
+ | `blur` | `change` + `blur` (deduplicated) | `<input>`, `<textarea>`, `<select>` |
329
+ | `focus` | `focus` | `<input>`, `<textarea>`, `<select>` |
330
+ | `load` | *(fires immediately when rendered)* | Any element |
331
+
332
+ ### Examples
333
+
334
+ **Live search with debounce:**
335
+
336
+ ```html
337
+ <input type="text" name="q"
338
+ mu-trigger="change" mu-debounce="500"
339
+ mu-url="/search" mu-target="#results" mu-source="#results"
340
+ mu-mode="update">
341
+ ```
342
+
343
+ **Action on focus (e.g. load suggestions):**
344
+
345
+ ```html
346
+ <input type="text" mu-trigger="focus"
347
+ mu-url="/suggestions" mu-target="#suggestions" mu-mode="update">
348
+ ```
349
+
350
+ **Action on blur (save on field exit):**
351
+
352
+ ```html
353
+ <input type="text" name="title" mu-trigger="blur"
354
+ mu-url="/api/save" mu-method="put" mu-target="#status" mu-mode="update">
355
+ ```
356
+
357
+ **Load content immediately:**
358
+
359
+ ```html
360
+ <div mu-trigger="load"
361
+ mu-url="/sidebar" mu-target="#sidebar" mu-mode="update">
362
+ </div>
363
+ ```
364
+
365
+ ### Polling
366
+
367
+ Combine `mu-trigger="load"` with `mu-repeat` to poll a URL at regular intervals:
368
+
369
+ ```html
370
+ <div mu-trigger="load" mu-repeat="5000"
371
+ mu-url="/notifications" mu-target="#notifs" mu-mode="update">
372
+ </div>
373
+ ```
374
+
375
+ The first fetch fires immediately, then every 5 seconds. Polling intervals are automatically cleaned up when the element is removed from the DOM.
376
+
377
+ ### Debounce
378
+
379
+ Use `mu-debounce` to delay the fetch until the user stops interacting:
380
+
381
+ ```html
382
+ <input type="text" name="q" mu-debounce="300"
383
+ mu-url="/search" mu-target="#results" mu-mode="update">
384
+ ```
385
+
386
+ > **Note:** Triggers other than `click` and `submit` default to ghost mode (no browser history entry).
387
+
388
+
389
+ ## Server-Sent Events (SSE)
390
+
391
+ µJS supports real-time updates via Server-Sent Events. Set `mu-method="sse"` to open an `EventSource` connection instead of a one-shot fetch.
392
+
393
+ ```html
394
+ <div mu-trigger="load" mu-url="/chat/stream"
395
+ mu-mode="patch" mu-method="sse">
396
+ </div>
397
+ ```
398
+
399
+ Each incoming SSE message is treated as HTML and rendered according to the element's `mu-mode`. In patch mode, the server sends HTML fragments with `mu-patch-target` attributes, just like a regular patch response.
400
+
401
+ ### Server-side example
402
+
403
+ ```
404
+ event: message
405
+ data: <div mu-patch-target="#messages" mu-patch-mode="append"><p>New message!</p></div>
406
+
407
+ event: message
408
+ data: <span mu-patch-target="#online-count">42</span>
409
+ ```
410
+
411
+ ### Limitations
412
+
413
+ - **No custom headers**: `EventSource` does not support custom HTTP headers. Use query parameters for authentication (e.g. `mu-url="/stream?token=abc"`).
414
+ - **Connection limit**: Browsers allow ~6 SSE connections per domain in HTTP/1.1. Use HTTP/2 to avoid this limit.
415
+ - **Automatic cleanup**: SSE connections are closed when the element is removed from the DOM (e.g. when the page changes).
416
+
417
+
254
418
  ## Ghost mode
255
419
 
256
420
  Ghost mode prevents a navigation from being added to browser history and disables automatic scroll-to-top.
@@ -424,7 +588,11 @@ All attributes support both `mu-*` and `data-mu-*` syntax.
424
588
  | `mu-morph` | Disable morphing on this element (`false`). |
425
589
  | `mu-transition` | Disable view transitions on this element (`false`). |
426
590
  | `mu-prefetch` | Disable prefetch on hover for this link (`false`). |
427
- | `mu-post` | Force POST method on a link. |
591
+ | `mu-method` | HTTP method: `get`, `post`, `put`, `patch`, `delete`, or `sse`. |
592
+ | `mu-trigger` | Event trigger: `click`, `submit`, `change`, `blur`, `focus`, `load`. |
593
+ | `mu-debounce` | Debounce delay in milliseconds (e.g. `"500"`). |
594
+ | `mu-repeat` | Polling interval in milliseconds (e.g. `"5000"`). |
595
+ | `mu-post` | *(Deprecated)* Use `mu-method="post"` instead. |
428
596
  | `mu-confirm` | Show a confirmation dialog before loading. |
429
597
  | `mu-confirm-quit` | *(Forms)* Prompt before leaving if the form has been modified. |
430
598
  | `mu-validate` | *(Forms)* Name of a JS validation function. Must return `true`/`false`. |
package/dist/mu.min.js CHANGED
@@ -1,2 +1,2 @@
1
- /* µJS (muJS) v1.0 - mujs.org */
2
- var mu=new function(){this._VERSION="1.0",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,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.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)}),document.addEventListener("click",mu._onClick),document.addEventListener("submit",mu._onSubmit),document.addEventListener("mouseover",mu._onMouseOver),document.addEventListener("input",mu._onInput),window.addEventListener("popstate",mu._onPopState),window.addEventListener("beforeunload",mu._onBeforeUnload),window.history.replaceState({mu:!0,url:location.pathname+location.search},""),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 o=mu._attr(t,e);return null===o?r:""===o||"true"===o||"false"!==o&&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=t.getAttribute("href")||t.getAttribute("action")||"";return!(!e.startsWith("/")||e.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 o=e.lastIndexOf("/");-1!==o&&(r._titleAttr=e.substring(o+1),r.title=e.substring(0,o))}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),r.post=mu._attrBool(t,"post",!1),r.confirm=mu._attr(t,"confirm"),r.patchGhost=mu._attrBool(t,"patch-ghost",!0),r},this._onClick=function(t){var e=t.target.closest("a");if(e&&mu._cfg.processLinks&&mu._shouldProcess(e)&&!(t.ctrlKey||t.metaKey||t.shiftKey||t.altKey)){t.preventDefault();var r=mu._elemCfg(e),o=r._url||e.getAttribute("href");if(r._sourceElement=e,!r.confirm||window.confirm(r.confirm)){if(mu._confirmQuit){if(!window.confirm(r.confirmQuitText))return;mu.setConfirmQuit(!1)}mu._loadExec(o,r)}}},this._onSubmit=function(t){var e=t.target.closest("form");if(e&&mu._cfg.processForms&&mu._shouldProcess(e)&&e.reportValidity()){var r=mu._attr(e,"validate");if(!r||"function"!=typeof window[r]||window[r](e)){var o=(e.getAttribute("method")||"get").toLowerCase(),u=mu._elemCfg(e),i=u._url||e.getAttribute("action");if(u._sourceElement=e,"post"===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("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)){mu._prefetchCache.set(r,null);var o=mu._cfg.urlPrefix?mu._cfg.urlPrefix+r:r;fetch(o,{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,t):mu._prefetchCache.delete(r)}).catch(function(){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._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 o=null,u=null,i=t;if(mu._prefetchCache.has(t)&&null!==mu._prefetchCache.get(t))o=mu._prefetchCache.get(t),mu._prefetchCache.delete(t);else{var n={signal:mu._abortCtrl.signal,headers:{"X-Requested-With":"mujs","X-Mu-Mode":e.mode}};if(e.postData?(n.method="POST",n.body=e.postData):e.post&&(n.method="POST"),!(u=await fetch(r,n)).ok)return void mu._emit("mu:fetch-error",{url:t,fetchUrl:r,status:u.status,response:u});u.redirected&&(i=new URL(u.url).pathname+new URL(u.url).search),o=await u.text()}var s,a={url:t,finalUrl:i,html:o,config:e};if(!mu._emit("mu:before-render",a))return;if(s="patch"===e.mode?function(){mu._renderPatch(a.html,e)}:function(){mu._renderPage(a.html,e)},e.transition&&document.startViewTransition?document.startViewTransition(s):s(),"patch"===e.mode)e.patchGhost||(mu._previousUrl=mu._lastUrl,mu._lastUrl=i,window.history.pushState({mu:!0,url:i},"",i));else e.ghost||u&&u.redirected&&e.ghostRedirect||e.postData?u&&u.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 l=e.ghost||u&&u.redirected&&e.ghostRedirect;if(null!==e.scrollToTop?e.scrollToTop:!l){var m=t.indexOf("#");if(-1!==m){var c=document.getElementById(t.substring(m+1));c&&c.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"),o=null;e.source&&(o=r.querySelector(e.source)),o||(o=r.body);var u=document.querySelector(e.target);if(u){mu._applyMode(e.mode,u,o,e),mu._updateTitle(r,e),mu._mergeHead(r);var i=document.querySelector(e.target)||document.body;mu._runScripts(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]"),o=0;o<r.length;o++){var u=r[o],i=u.getAttribute("mu-patch-target")||u.getAttribute("data-mu-patch-target"),n=u.getAttribute("mu-patch-mode")||u.getAttribute("data-mu-patch-mode")||"replace",s=document.querySelector(i);s?(mu._applyMode(n,s,u,e),"remove"!==n&&mu._runScripts(u)):console.warn("[µJS] Patch target '"+i+"' not found.")}},this._applyMode=function(t,e,r,o){var u=o.morph&&mu._morph;switch(t){case"update":u?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?u?mu._morph(e,r.innerHTML,{morphStyle:"innerHTML"}):e.innerHTML=r.innerHTML:u?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 o=e._titleAttr?r.getAttribute(e._titleAttr):r.textContent;o&&(document.title=o)}}},this._mergeHead=function(t){for(var e="link[rel='stylesheet'], style, meta[name], meta[property]",r=document.head.querySelectorAll(e),o=t.head.querySelectorAll(e),u=new Map,i=0;i<r.length;i++)u.set(mu._elKey(r[i]),r[i]);var n=new Set;for(i=0;i<o.length;i++){var s=mu._elKey(o[i]);n.add(s),u.has(s)||document.head.appendChild(o[i].cloneNode(!0))}u.forEach(function(t,e){n.has(e)||t.remove()})},this._elKey=function(t){return"LINK"===t.tagName?"link:"+t.getAttribute("href"):"STYLE"===t.tagName?"style:"+t.textContent.substring(0,100):"META"===t.tagName?"meta:"+(t.getAttribute("name")||t.getAttribute("property")):t.outerHTML},this._runScripts=function(t){for(var e=t.querySelectorAll("script"),r=0;r<e.length;r++){var o=e[r];if("true"!==mu._attr(o,"disabled")&&""!==mu._attr(o,"disabled")){if(o.hasAttribute("src")){var u=o.getAttribute("src");if(mu._jsIncludes[u])continue;mu._jsIncludes[u]=!0}for(var i=document.createElement("script"),n=0;n<o.attributes.length;n++)i.setAttribute(o.attributes[n].name,o.attributes[n].value);i.textContent=o.textContent,o.parentNode.replaceChild(i,o)}}},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.2.1",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},""),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.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)&&"click"===mu._getTrigger(e)&&!1!==mu._attrBool(e,"prefetch",!0)){var r=mu._attr(e,"url")||e.getAttribute("href");if(r){var u=mu._prefetchCache.get(r);if(!(u&&Date.now()-u.ts<mu._cfg.prefetchTtl)&&r!==location.pathname+location.search){var o=mu._cfg.urlPrefix?mu._cfg.urlPrefix+r:r,i=fetch(o,{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(r,{promise:i,ts:Date.now()})}}}}},this._onMouseOut=function(t){},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._trigger=!0,e.transition=!1,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,s=function(t){return function(){mu._triggerAction(t)}}(u);if(i>0&&(s=mu._debounce(s,i)),n>0&&(s=function(t,e,r){var u=!1;return function(){e(),u||(u=!0,t._mu_interval=setInterval(e,r))}}(u,s,n)),"change"===o)u.addEventListener("input",s);else if("blur"===o){var a=function(t){var e=0;return function(){var r=Date.now();r-e<50||(e=r,t())}}(s);u.addEventListener("change",a),u.addEventListener("blur",a)}else"focus"===o?u.addEventListener("focus",s):"load"===o&&s()}}}},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,s=mu._prefetchCache.get(t);if(s&&s.promise&&Date.now()-s.ts<mu._cfg.prefetchTtl&&(o=await s.promise,u.signal.aborted))return;if(!o){var a={signal:u.signal,headers:{"X-Requested-With":"mujs","X-Mu-Mode":e.mode}},l=(e.method||"get").toUpperCase();if("GET"!==l&&(a.method=l,a.headers["X-Mu-Method"]=l,e.postData&&(a.body=e.postData)),!(i=await fetch(r,a)).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(m="patch"===e.mode?function(){mu._renderPatch(c.html,e)}:function(){mu._renderPage(c.html,e)},e.transition&&document.startViewTransition?document.startViewTransition(m):m(),"patch"===e.mode)e.patchGhost||(mu._previousUrl=mu._lastUrl,mu._lastUrl=n,window.history.pushState({mu:!0,url:n},"",n));else e.ghost||i&&i.redirected&&e.ghostRedirect||e.postData?i&&i.redirected&&!e.ghostRedirect?(mu._previousUrl=mu._lastUrl,mu._lastUrl=n,window.history.pushState({mu:!0,url:n},"",n)):(mu._previousUrl=mu._lastUrl,mu._lastUrl=n):(mu._previousUrl=mu._lastUrl,mu._lastUrl=n,window.history.pushState({mu:!0,url:n},"",n));if("patch"!==e.mode)if(e._restoreScroll)window.scrollTo(e._restoreScroll.x,e._restoreScroll.y);else{var h=e.ghost||i&&i.redirected&&e.ghostRedirect;if(null!==e.scrollToTop?e.scrollToTop:!h){var _=t.indexOf("#");if(-1!==_){var d=document.getElementById(t.substring(_+1));d&&d.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.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"),s=i.getAttribute("mu-patch-mode")||i.getAttribute("data-mu-patch-mode")||"replace",a=document.querySelector(n);a?(mu._cleanupTriggers(a),mu._applyMode(s,a,i,e),"remove"!==s&&(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"),s=0;s<u[i].attributes.length;s++)n.setAttribute(u[i].attributes[s].name,u[i].attributes[s].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)}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digicreon/mujs",
3
- "version": "1.0.0",
3
+ "version": "1.2.1",
4
4
  "description": "Lightweight AJAX navigation library",
5
5
  "main": "dist/mu.min.js",
6
6
  "files": [