pjax-max 3.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 pjax-max contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,344 @@
1
+ # pjax-max
2
+
3
+ A lightweight (~2.3KB gzipped), zero-dependency PJAX library for page transitions. Navigate between pages without full reloads, with full control over transition animations.
4
+
5
+ Built on top of the browser's native History API, Fetch API, and EventTarget.
6
+
7
+ ---
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install pjax-max
13
+ ```
14
+
15
+ ```js
16
+ import { Core, Renderer, Transition } from 'pjax-max';
17
+ ```
18
+
19
+ Or via CDN:
20
+
21
+ ```html
22
+ <script src="https://unpkg.com/pjax-max"></script>
23
+ <script>
24
+ const { Core, Renderer, Transition } = PjaxMax;
25
+ </script>
26
+ ```
27
+
28
+ ## HTML Setup
29
+
30
+ pjax-max requires two data attributes in your markup:
31
+
32
+ ```html
33
+ <body>
34
+ <nav>
35
+ <a href="/">Home</a>
36
+ <a href="/about">About</a>
37
+ </nav>
38
+
39
+ <main data-router-wrapper>
40
+ <article data-router-view="home">
41
+ <!-- page content -->
42
+ </article>
43
+ </main>
44
+ </body>
45
+ ```
46
+
47
+ - `data-router-wrapper` — The container that holds views. Stays in the DOM across navigations.
48
+ - `data-router-view="slug"` — The swappable view element. The slug maps to your renderers and transitions.
49
+
50
+ ## Quick Start
51
+
52
+ ```js
53
+ import { Core, Renderer, Transition } from 'pjax-max';
54
+
55
+ // 1. Define a transition
56
+ class Fade extends Transition {
57
+ out({ from, done }) {
58
+ from.style.opacity = 0;
59
+ setTimeout(() => {
60
+ from.remove();
61
+ done();
62
+ }, 300);
63
+ }
64
+
65
+ in({ to, done }) {
66
+ to.style.opacity = 0;
67
+ requestAnimationFrame(() => {
68
+ to.style.transition = 'opacity 0.3s';
69
+ to.style.opacity = 1;
70
+ setTimeout(done, 300);
71
+ });
72
+ }
73
+ }
74
+
75
+ // 2. Initialize
76
+ const core = new Core({
77
+ transitions: {
78
+ default: Fade
79
+ }
80
+ });
81
+ ```
82
+
83
+ That's it. All internal links will now use PJAX navigation with your fade transition.
84
+
85
+ ## Core
86
+
87
+ ### Constructor Options
88
+
89
+ ```js
90
+ const core = new Core({
91
+ renderers: { ... }, // Map of slug -> Renderer class
92
+ transitions: { ... }, // Map of slug -> Transition class (or 'default')
93
+ onLeave: [ ... ], // Functions called at the start of every out transition
94
+ onEnter: [ ... ], // Functions called at the start of every in transition
95
+ });
96
+ ```
97
+
98
+ ### Properties
99
+
100
+ | Property | Type | Description |
101
+ |---|---|---|
102
+ | `core.transitioning` | `boolean` | `true` while a navigation is in progress |
103
+ | `core.namespace` | `string` | The current view's `data-router-view` slug |
104
+ | `core.cache` | `Map` | Page cache (URL -> properties) |
105
+
106
+ ### Methods
107
+
108
+ | Method | Description |
109
+ |---|---|
110
+ | `core.redirect(url, contextual?, trigger?)` | Navigate to a URL programmatically |
111
+ | `core.attach(links)` | Attach click listeners to links |
112
+ | `core.detach(links)` | Remove click listeners from links |
113
+ | `core.prefetch(links)` | Prefetch URLs or link elements into the cache |
114
+
115
+ ### Events
116
+
117
+ Listen to navigation lifecycle events using the native `addEventListener` API:
118
+
119
+ ```js
120
+ core.addEventListener('NAVIGATE_OUT', (e) => {
121
+ const { from, trigger, location } = e.detail;
122
+ });
123
+
124
+ core.addEventListener('NAVIGATE_IN', (e) => {
125
+ const { to, trigger, location } = e.detail;
126
+ });
127
+
128
+ core.addEventListener('NAVIGATE_END', (e) => {
129
+ const { from, to, trigger, location } = e.detail;
130
+ });
131
+ ```
132
+
133
+ ### Navigation Lifecycle Hooks
134
+
135
+ The `onLeave` and `onEnter` arrays let you run functions on every navigation without repeating logic in each transition. Common uses: closing menus, destroying scroll instances, resetting UI state.
136
+
137
+ ```js
138
+ const core = new Core({
139
+ transitions: { default: Fade },
140
+ onLeave: [
141
+ () => scroll?.destroy(),
142
+ () => megaNav.isOpen && megaNav.close(),
143
+ () => mobileMenu.isOpen && mobileMenu.close(),
144
+ ],
145
+ onEnter: [
146
+ () => console.log('Entering:', core.namespace),
147
+ ],
148
+ });
149
+ ```
150
+
151
+ `onLeave` runs before the out transition. `onEnter` runs after the new view is in the DOM but before the in transition plays. `core.namespace` is updated before `onEnter` fires, so it reflects the incoming page.
152
+
153
+ ## Renderers
154
+
155
+ Renderers control page-specific setup and teardown. Extend the base `Renderer` class and override lifecycle hooks:
156
+
157
+ ```js
158
+ class HomeRenderer extends Renderer {
159
+ onEnter() {
160
+ // Called when the view is added to the DOM
161
+ }
162
+ onEnterCompleted() {
163
+ // Called after the in transition completes
164
+ }
165
+ onLeave() {
166
+ // Called before the out transition starts
167
+ }
168
+ onLeaveCompleted() {
169
+ // Called after the view is removed
170
+ }
171
+ }
172
+ ```
173
+
174
+ Map renderers to view slugs:
175
+
176
+ ```js
177
+ const core = new Core({
178
+ renderers: {
179
+ home: HomeRenderer,
180
+ about: AboutRenderer,
181
+ },
182
+ transitions: { default: Fade }
183
+ });
184
+ ```
185
+
186
+ Renderers support dynamic imports for code splitting:
187
+
188
+ ```js
189
+ const core = new Core({
190
+ renderers: {
191
+ home: () => import('./renderers/home.js'),
192
+ }
193
+ });
194
+ ```
195
+
196
+ ## Transitions
197
+
198
+ Transitions control the animation between views. Extend the base `Transition` class and implement `in` and `out`:
199
+
200
+ ```js
201
+ class Fade extends Transition {
202
+ out({ from, trigger, done }) {
203
+ // Animate the old view out, then call done()
204
+ gsap.to(from, {
205
+ opacity: 0,
206
+ duration: 0.4,
207
+ onComplete: () => {
208
+ from.remove();
209
+ done();
210
+ }
211
+ });
212
+ }
213
+
214
+ in({ to, from, trigger, done }) {
215
+ // Animate the new view in, then call done()
216
+ gsap.fromTo(to,
217
+ { opacity: 0 },
218
+ { opacity: 1, duration: 0.4, onComplete: done }
219
+ );
220
+ }
221
+ }
222
+ ```
223
+
224
+ ### Default vs. Contextual Transitions
225
+
226
+ A `default` transition applies to all navigations:
227
+
228
+ ```js
229
+ const core = new Core({
230
+ transitions: {
231
+ default: Fade,
232
+ slideUp: SlideUp,
233
+ }
234
+ });
235
+ ```
236
+
237
+ Use contextual transitions on specific links with the `data-transition` attribute:
238
+
239
+ ```html
240
+ <a href="/gallery" data-transition="slideUp">Gallery</a>
241
+ ```
242
+
243
+ When this link is clicked, `SlideUp` is used instead of the default `Fade`. Contextual transitions only apply to click navigation, not browser back/forward.
244
+
245
+ ### Overlapping Transitions
246
+
247
+ The old view stays in the DOM until you explicitly remove it. This means you can run both views simultaneously for overlapping transitions:
248
+
249
+ ```js
250
+ class CrossFade extends Transition {
251
+ out({ from, done }) {
252
+ gsap.to(from, { opacity: 0, duration: 0.6, onComplete: done });
253
+ // Don't call from.remove() here — let the in transition overlap
254
+ }
255
+
256
+ in({ to, from, trigger, done }) {
257
+ from.remove(); // Remove old view when ready
258
+ gsap.fromTo(to,
259
+ { opacity: 0 },
260
+ { opacity: 1, duration: 0.6, onComplete: done }
261
+ );
262
+ }
263
+ }
264
+ ```
265
+
266
+ ## Scroll Restoration
267
+
268
+ By default, pjax-max lets the browser handle scroll position naturally. If you want to always start at the top of the page after navigation, enable `manualScrollRestoration`:
269
+
270
+ ```js
271
+ const core = new Core({
272
+ transitions: { default: Fade },
273
+ manualScrollRestoration: true
274
+ });
275
+ ```
276
+
277
+ When enabled, this sets `history.scrollRestoration = 'manual'` and scrolls to the top instantly after the old view is removed and the new view is added to the DOM — before the in transition plays.
278
+
279
+ Defaults to `false`.
280
+
281
+ ## Event Mask
282
+
283
+ During transitions, user interactions like scrolling, clicking, or swiping can cause visual glitches. pjax-max automatically creates an invisible full-screen overlay that blocks these gestures while a transition is in progress.
284
+
285
+ The mask is appended to the `<body>` on initialization with `pointer-events: none`. When a navigation starts it switches to `pointer-events: auto`, capturing and preventing `scroll`, `wheel`, and `touchmove` events. When the transition completes it switches back to `none`.
286
+
287
+ No setup required — this is fully automatic.
288
+
289
+ ## Prefetching
290
+
291
+ pjax-max can prefetch pages and store them in the cache for instant navigation.
292
+
293
+ ### Critical Prefetch
294
+
295
+ Fetched immediately on page load. Use for primary navigation links:
296
+
297
+ ```html
298
+ <a href="/about" data-prefetch="critical">About</a>
299
+ ```
300
+
301
+ ### Hover Prefetch
302
+
303
+ Fetched on `mouseenter`. The listener auto-removes after the first hover:
304
+
305
+ ```html
306
+ <a href="/blog" data-prefetch="hover">Blog</a>
307
+ ```
308
+
309
+ ### Programmatic Prefetch
310
+
311
+ For links inside menus or components that open dynamically:
312
+
313
+ ```js
314
+ // When a mega nav opens, prefetch its links
315
+ function openMegaNav() {
316
+ megaNav.open();
317
+ const links = megaNav.querySelectorAll('a');
318
+ core.prefetch(links);
319
+ }
320
+ ```
321
+
322
+ `core.prefetch()` accepts an array of link elements or URL strings. It skips URLs already in the cache and deduplicates in-flight requests.
323
+
324
+ ## Disabling PJAX on Links
325
+
326
+ Add `data-router-disabled` to opt out of PJAX navigation:
327
+
328
+ ```html
329
+ <a href="/external" data-router-disabled>Normal navigation</a>
330
+ ```
331
+
332
+ Links with a `target` attribute (e.g., `target="_blank"`) are also ignored automatically. Cmd/Ctrl+click opens links in a new tab as expected.
333
+
334
+ ## Browser Support
335
+
336
+ All modern browsers. No IE support.
337
+
338
+ - Chrome / Edge
339
+ - Firefox
340
+ - Safari
341
+
342
+ ## License
343
+
344
+ [MIT](LICENSE)
@@ -0,0 +1,2 @@
1
+ var p=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var m=(r,t)=>{for(var e in t)p(r,e,{get:t[e],enumerable:!0})},w=(r,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of u(t))!g.call(r,s)&&s!==e&&p(r,s,{get:()=>t[s],enumerable:!(i=f(t,s))||i.enumerable});return r};var v=r=>w(p({},"__esModule",{value:!0}),r);var y={};m(y,{Core:()=>h,Renderer:()=>a,Transition:()=>c,default:()=>T});module.exports=v(y);var a=class{constructor(t){this.wrap=document.querySelector("[data-router-wrapper]"),this.properties=t,this.Transition=t.transition?new t.transition.class(this.wrap,t.transition.name):null}setup(){var t,e;(t=this.onEnter)==null||t.call(this),(e=this.onEnterCompleted)==null||e.call(this)}add(){this.wrap.insertAdjacentHTML("beforeend",this.properties.view.outerHTML)}update(){document.title=this.properties.page.title}async show(t){var e,i,s;this.update(),(e=this.onEnter)==null||e.call(this),await((i=this.Transition)==null?void 0:i.show(t)),(s=this.onEnterCompleted)==null||s.call(this)}async hide(t){var e,i,s;(e=this.onLeave)==null||e.call(this),await((i=this.Transition)==null?void 0:i.hide(t)),(s=this.onLeaveCompleted)==null||s.call(this)}};var E=new DOMParser,l=class{constructor(t,e){this.renderers=t,this.transitions=e}getDOM(t){return typeof t=="string"?E.parseFromString(t,"text/html"):t}getView(t){return t.querySelector("[data-router-view]")}getSlug(t){return t.getAttribute("data-router-view")}async getRenderer(t){if(!this.renderers||!(t in this.renderers))return a;let e=this.renderers[t];return typeof e=="function"&&!a.isPrototypeOf(e)?(await e()).default:typeof(e==null?void 0:e.then)=="function"?(await e).default:e}getTransition(t){return this.transitions?t in this.transitions?{class:this.transitions[t],name:t}:"default"in this.transitions?{class:this.transitions.default,name:"default"}:null:null}getProperties(t){let e=this.getDOM(t),i=this.getView(e),s=this.getSlug(i),n=this.getRenderer(s),o=this.getTransition(s);return{page:e,view:i,slug:s,renderer:n,transition:o}}getLocation(t){let e=new URL(t);return{href:e.href,anchor:e.hash||null,origin:e.hostname,params:e.search?Object.fromEntries(e.searchParams):null,pathname:e.pathname,scheme:e.protocol.replace(":","")}}};var h=class extends EventTarget{constructor({renderers:t,transitions:e,onLeave:i,onEnter:s,manualScrollRestoration:n=!1}={}){super(),this.Helpers=new l(t,e),this.Transitions=e,this.Contextual=!1,this.location=this.Helpers.getLocation(window.location.href),this.properties=this.Helpers.getProperties(document.cloneNode(!0)),this.popping=!1,this.running=!1,this.trigger=null,this.controller=null,this.onLeave=i||[],this.onEnter=s||[],this.namespace=this.properties.slug,this.manualScrollRestoration=n,n&&(history.scrollRestoration="manual"),this.cache=new Map,this.cache.set(this.location.href,this.properties),this.prefetching=new Set,this.properties.renderer.then(o=>{this.From=new o(this.properties),this.From.setup()}),this._navigate=this.navigate.bind(this),this._prefetchOnHover=this._prefetchOnHover.bind(this),window.addEventListener("popstate",this.popState.bind(this)),this._eventMask=this._createEventMask(),this.links=document.querySelectorAll("a:not([target]):not([data-router-disabled])"),this.attach(this.links),this._initPrefetch()}get transitioning(){return this.running}_createEventMask(){let t=document.createElement("div");t.style.cssText="position:fixed;inset:0;z-index:2147483647;pointer-events:none;";let e=i=>{i.preventDefault(),i.stopPropagation()};return t.addEventListener("scroll",e),t.addEventListener("wheel",e,{passive:!1}),t.addEventListener("touchmove",e,{passive:!1}),document.body.appendChild(t),t}attach(t){for(let e of t)e.addEventListener("click",this._navigate)}detach(t){for(let e of t)e.removeEventListener("click",this._navigate)}prefetch(t){let e=[];for(let i of t){let s=typeof i=="string"?i:i.href;s&&e.push(s)}for(let i of e)this._prefetchURL(i)}_initPrefetch(){let t=document.querySelectorAll('[data-prefetch="critical"]');t.length&&this.prefetch(t);let e=document.querySelectorAll('[data-prefetch="hover"]');for(let i of e)i.addEventListener("mouseenter",this._prefetchOnHover,{once:!0})}_prefetchOnHover(t){let e=t.currentTarget.href;e&&this._prefetchURL(e)}async _prefetchURL(t){if(!(this.cache.has(t)||this.prefetching.has(t))){try{if(new URL(t).origin!==window.location.origin)return}catch{return}this.prefetching.add(t);try{let e=await fetch(t,{mode:"same-origin",method:"GET",headers:{"X-Requested-With":"PjaxMax"},credentials:"same-origin"});if(e.ok){let i=await e.text();this.cache.set(t,this.Helpers.getProperties(i))}}catch{}finally{this.prefetching.delete(t)}}}navigate(t){if(!(t.metaKey||t.ctrlKey)){t.preventDefault();let e=t.currentTarget.hasAttribute("data-transition")?t.currentTarget.dataset.transition:!1;this.redirect(t.currentTarget.href,e,t.currentTarget)}}redirect(t,e=!1,i="script"){if(this.trigger=i,!this.running&&t!==this.location.href){let s=this.Helpers.getLocation(t);this.Contextual=!1,e&&(this.Contextual=this.Transitions[e].prototype,this.Contextual.name=e),s.origin!==this.location.origin||s.scheme!==this.location.scheme||s.anchor&&s.pathname===this.location.pathname?window.location.href=t:(this.location=s,this.beforeFetch())}}popState(){this.trigger="popstate",this.Contextual=!1;let t=this.Helpers.getLocation(window.location.href);this.location.pathname!==t.pathname||!this.location.anchor&&!t.anchor?(this.popping=!0,this.location=t,this.beforeFetch()):this.location=t}pushState(){this.popping||window.history.pushState(this.location,"",this.location.href)}async fetch(){this.controller=new AbortController;let t=await fetch(this.location.href,{mode:"same-origin",method:"GET",headers:{"X-Requested-With":"PjaxMax"},credentials:"same-origin",signal:this.controller.signal});if(t.ok)return t.text();window.location.href=this.location.href}async beforeFetch(){var e;this.pushState(),this.running=!0,this._eventMask.style.pointerEvents="auto",(e=this.controller)==null||e.abort();for(let i of this.onLeave)i();this.dispatchEvent(new CustomEvent("NAVIGATE_OUT",{detail:{from:{page:this.From.properties.page,view:this.From.properties.view},trigger:this.trigger,location:this.location}}));let t={trigger:this.trigger,contextual:this.Contextual};if(this.cache.has(this.location.href))await this.From.hide(t),this.properties=this.cache.get(this.location.href);else{let[i]=await Promise.all([this.fetch(),this.From.hide(t)]);this.properties=this.Helpers.getProperties(i),this.cache.set(this.location.href,this.properties)}this.afterFetch()}async afterFetch(){let t=await this.properties.renderer;this.To=new t(this.properties),this.To.add(),this.manualScrollRestoration&&window.scrollTo(0,0),this.dispatchEvent(new CustomEvent("NAVIGATE_IN",{detail:{to:{page:this.To.properties.page,view:this.To.wrap.lastElementChild},trigger:this.trigger,location:this.location}})),this.namespace=this.To.properties.slug;for(let e of this.onEnter)e();await this.To.show({trigger:this.trigger,contextual:this.Contextual}),this.popping=!1,this.running=!1,this._eventMask.style.pointerEvents="none",this.detach(this.links),this.links=document.querySelectorAll("a:not([target]):not([data-router-disabled])"),this.attach(this.links),this._initPrefetch(),this.dispatchEvent(new CustomEvent("NAVIGATE_END",{detail:{to:{page:this.To.properties.page,view:this.To.wrap.lastElementChild},from:{page:this.From.properties.page,view:this.From.properties.view},trigger:this.trigger,location:this.location}})),this.From=this.To,this.trigger=null}};var c=class{constructor(t,e){this.wrap=t,this.name=e}show({trigger:t,contextual:e}){let i=this.wrap.lastElementChild,s=this.wrap.firstElementChild;return new Promise(n=>{var o,d;e?(i.setAttribute("data-transition-in",e.name),i.removeAttribute("data-transition-out"),(d=e.in)==null||d.call(e,{to:i,from:s,trigger:t,done:n})):(i.setAttribute("data-transition-in",this.name),i.removeAttribute("data-transition-out"),(o=this.in)==null||o.call(this,{to:i,from:s,trigger:t,done:n}))})}hide({trigger:t,contextual:e}){let i=this.wrap.firstElementChild;return new Promise(s=>{var n,o;e?(i.setAttribute("data-transition-out",e.name),i.removeAttribute("data-transition-in"),(o=e.out)==null||o.call(e,{from:i,trigger:t,done:s})):(i.setAttribute("data-transition-out",this.name),i.removeAttribute("data-transition-in"),(n=this.out)==null||n.call(this,{from:i,trigger:t,done:s}))})}};var T={Core:h,Renderer:a,Transition:c};0&&(module.exports={Core,Renderer,Transition});
2
+ //# sourceMappingURL=pjax-max.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pjax-max.js","../src/renderer.js","../src/helpers.js","../src/core.js","../src/transition.js"],"sourcesContent":["import Core from './core';\nimport Renderer from './renderer';\nimport Transition from './transition';\n\nexport { Core, Renderer, Transition };\nexport default { Core, Renderer, Transition };\n","export default class Renderer {\n\n constructor(properties) {\n this.wrap = document.querySelector('[data-router-wrapper]');\n this.properties = properties;\n this.Transition = properties.transition\n ? new properties.transition.class(this.wrap, properties.transition.name)\n : null;\n }\n\n setup() {\n this.onEnter?.();\n this.onEnterCompleted?.();\n }\n\n add() {\n this.wrap.insertAdjacentHTML('beforeend', this.properties.view.outerHTML);\n }\n\n update() {\n document.title = this.properties.page.title;\n }\n\n async show(data) {\n this.update();\n this.onEnter?.();\n await this.Transition?.show(data);\n this.onEnterCompleted?.();\n }\n\n async hide(data) {\n this.onLeave?.();\n await this.Transition?.hide(data);\n this.onLeaveCompleted?.();\n }\n}\n","import Renderer from './renderer';\n\nconst PARSER = new DOMParser();\n\nexport default class Helpers {\n\n constructor(renderers, transitions) {\n this.renderers = renderers;\n this.transitions = transitions;\n }\n\n getDOM(page) {\n return typeof page === 'string' ? PARSER.parseFromString(page, 'text/html') : page;\n }\n\n getView(page) {\n return page.querySelector('[data-router-view]');\n }\n\n getSlug(view) {\n return view.getAttribute('data-router-view');\n }\n\n async getRenderer(slug) {\n if (!this.renderers || !(slug in this.renderers)) {\n return Renderer;\n }\n\n const renderer = this.renderers[slug];\n\n // Dynamic import factory function\n if (typeof renderer === 'function' && !Renderer.isPrototypeOf(renderer)) {\n const mod = await renderer();\n return mod.default;\n }\n\n // Already a promise (e.g., top-level import())\n if (typeof renderer?.then === 'function') {\n const mod = await renderer;\n return mod.default;\n }\n\n return renderer;\n }\n\n getTransition(slug) {\n if (!this.transitions) {\n return null;\n }\n\n if (slug in this.transitions) {\n return { class: this.transitions[slug], name: slug };\n }\n\n if ('default' in this.transitions) {\n return { class: this.transitions['default'], name: 'default' };\n }\n\n return null;\n }\n\n getProperties(context) {\n const page = this.getDOM(context);\n const view = this.getView(page);\n const slug = this.getSlug(view);\n const renderer = this.getRenderer(slug);\n const transition = this.getTransition(slug);\n\n return {\n page,\n view,\n slug,\n renderer,\n transition\n };\n }\n\n getLocation(url) {\n const u = new URL(url);\n return {\n href: u.href,\n anchor: u.hash || null,\n origin: u.hostname,\n params: u.search ? Object.fromEntries(u.searchParams) : null,\n pathname: u.pathname,\n scheme: u.protocol.replace(':', '')\n };\n }\n}\n","import Helpers from './helpers';\n\nexport default class Core extends EventTarget {\n\n constructor({ renderers, transitions, onLeave, onEnter, manualScrollRestoration = false } = {}) {\n super();\n\n this.Helpers = new Helpers(renderers, transitions);\n this.Transitions = transitions;\n this.Contextual = false;\n\n this.location = this.Helpers.getLocation(window.location.href);\n this.properties = this.Helpers.getProperties(document.cloneNode(true));\n\n this.popping = false;\n this.running = false;\n this.trigger = null;\n this.controller = null;\n\n this.onLeave = onLeave || [];\n this.onEnter = onEnter || [];\n this.namespace = this.properties.slug;\n this.manualScrollRestoration = manualScrollRestoration;\n\n if (manualScrollRestoration) {\n history.scrollRestoration = 'manual';\n }\n\n this.cache = new Map();\n this.cache.set(this.location.href, this.properties);\n this.prefetching = new Set();\n\n this.properties.renderer.then(Renderer => {\n this.From = new Renderer(this.properties);\n this.From.setup();\n });\n\n this._navigate = this.navigate.bind(this);\n this._prefetchOnHover = this._prefetchOnHover.bind(this);\n window.addEventListener('popstate', this.popState.bind(this));\n\n this._eventMask = this._createEventMask();\n\n this.links = document.querySelectorAll('a:not([target]):not([data-router-disabled])');\n this.attach(this.links);\n\n this._initPrefetch();\n }\n\n get transitioning() {\n return this.running;\n }\n\n _createEventMask() {\n const el = document.createElement('div');\n el.style.cssText = 'position:fixed;inset:0;z-index:2147483647;pointer-events:none;';\n const prevent = (e) => { e.preventDefault(); e.stopPropagation(); };\n el.addEventListener('scroll', prevent);\n el.addEventListener('wheel', prevent, { passive: false });\n el.addEventListener('touchmove', prevent, { passive: false });\n document.body.appendChild(el);\n return el;\n }\n\n attach(links) {\n for (const link of links) {\n link.addEventListener('click', this._navigate);\n }\n }\n\n detach(links) {\n for (const link of links) {\n link.removeEventListener('click', this._navigate);\n }\n }\n\n /**\n * Prefetch URLs and store results in the cache.\n * Accepts an array of links (elements or URL strings).\n */\n prefetch(links) {\n const urls = [];\n\n for (const link of links) {\n const href = typeof link === 'string' ? link : link.href;\n if (href) urls.push(href);\n }\n\n for (const url of urls) {\n this._prefetchURL(url);\n }\n }\n\n _initPrefetch() {\n // Prefetch critical links immediately\n const critical = document.querySelectorAll('[data-prefetch=\"critical\"]');\n if (critical.length) this.prefetch(critical);\n\n // Attach hover listeners to hover-prefetch links\n const hover = document.querySelectorAll('[data-prefetch=\"hover\"]');\n for (const link of hover) {\n link.addEventListener('mouseenter', this._prefetchOnHover, { once: true });\n }\n }\n\n _prefetchOnHover(e) {\n const href = e.currentTarget.href;\n if (href) this._prefetchURL(href);\n }\n\n async _prefetchURL(url) {\n if (this.cache.has(url) || this.prefetching.has(url)) return;\n\n // Normalize against current origin\n try {\n const parsed = new URL(url);\n if (parsed.origin !== window.location.origin) return;\n } catch {\n return;\n }\n\n this.prefetching.add(url);\n\n try {\n const response = await fetch(url, {\n mode: 'same-origin',\n method: 'GET',\n headers: { 'X-Requested-With': 'PjaxMax' },\n credentials: 'same-origin'\n });\n\n if (response.ok) {\n const html = await response.text();\n this.cache.set(url, this.Helpers.getProperties(html));\n }\n } catch {\n // Prefetch is best-effort — silently ignore failures\n } finally {\n this.prefetching.delete(url);\n }\n }\n\n navigate(e) {\n if (!(e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n\n const contextual = e.currentTarget.hasAttribute('data-transition')\n ? e.currentTarget.dataset.transition\n : false;\n\n this.redirect(e.currentTarget.href, contextual, e.currentTarget);\n }\n }\n\n redirect(href, contextual = false, trigger = 'script') {\n this.trigger = trigger;\n\n if (!this.running && href !== this.location.href) {\n const location = this.Helpers.getLocation(href);\n\n this.Contextual = false;\n\n if (contextual) {\n this.Contextual = this.Transitions[contextual].prototype;\n this.Contextual.name = contextual;\n }\n\n if (location.origin !== this.location.origin || location.scheme !== this.location.scheme || location.anchor && location.pathname === this.location.pathname) {\n window.location.href = href;\n } else {\n this.location = location;\n this.beforeFetch();\n }\n }\n }\n\n popState() {\n this.trigger = 'popstate';\n this.Contextual = false;\n\n const location = this.Helpers.getLocation(window.location.href);\n\n if (this.location.pathname !== location.pathname || !this.location.anchor && !location.anchor) {\n this.popping = true;\n this.location = location;\n this.beforeFetch();\n } else {\n this.location = location;\n }\n }\n\n pushState() {\n if (!this.popping) {\n window.history.pushState(this.location, '', this.location.href);\n }\n }\n\n async fetch() {\n this.controller = new AbortController();\n\n const response = await fetch(this.location.href, {\n mode: 'same-origin',\n method: 'GET',\n headers: { 'X-Requested-With': 'PjaxMax' },\n credentials: 'same-origin',\n signal: this.controller.signal\n });\n\n if (response.ok) {\n return response.text();\n }\n\n window.location.href = this.location.href;\n }\n\n async beforeFetch() {\n this.pushState();\n this.running = true;\n this._eventMask.style.pointerEvents = 'auto';\n\n // Abort any in-flight fetch from a previous navigation\n this.controller?.abort();\n\n for (const fn of this.onLeave) fn();\n\n this.dispatchEvent(new CustomEvent('NAVIGATE_OUT', {\n detail: {\n from: {\n page: this.From.properties.page,\n view: this.From.properties.view\n },\n trigger: this.trigger,\n location: this.location\n }\n }));\n\n const data = {\n trigger: this.trigger,\n contextual: this.Contextual\n };\n\n if (this.cache.has(this.location.href)) {\n await this.From.hide(data);\n this.properties = this.cache.get(this.location.href);\n } else {\n const [html] = await Promise.all([\n this.fetch(),\n this.From.hide(data)\n ]);\n\n this.properties = this.Helpers.getProperties(html);\n this.cache.set(this.location.href, this.properties);\n }\n\n this.afterFetch();\n }\n\n async afterFetch() {\n const Renderer = await this.properties.renderer;\n\n this.To = new Renderer(this.properties);\n this.To.add();\n\n if (this.manualScrollRestoration) window.scrollTo(0, 0);\n\n this.dispatchEvent(new CustomEvent('NAVIGATE_IN', {\n detail: {\n to: {\n page: this.To.properties.page,\n view: this.To.wrap.lastElementChild\n },\n trigger: this.trigger,\n location: this.location\n }\n }));\n\n this.namespace = this.To.properties.slug;\n for (const fn of this.onEnter) fn();\n\n await this.To.show({\n trigger: this.trigger,\n contextual: this.Contextual\n });\n\n this.popping = false;\n this.running = false;\n this._eventMask.style.pointerEvents = 'none';\n\n this.detach(this.links);\n this.links = document.querySelectorAll('a:not([target]):not([data-router-disabled])');\n this.attach(this.links);\n this._initPrefetch();\n\n this.dispatchEvent(new CustomEvent('NAVIGATE_END', {\n detail: {\n to: {\n page: this.To.properties.page,\n view: this.To.wrap.lastElementChild\n },\n from: {\n page: this.From.properties.page,\n view: this.From.properties.view\n },\n trigger: this.trigger,\n location: this.location\n }\n }));\n\n this.From = this.To;\n this.trigger = null;\n }\n}\n","export default class Transition {\n\n constructor(wrap, name) {\n this.wrap = wrap;\n this.name = name;\n }\n\n show({ trigger, contextual }) {\n const to = this.wrap.lastElementChild;\n const from = this.wrap.firstElementChild;\n\n return new Promise(resolve => {\n if (!contextual) {\n to.setAttribute('data-transition-in', this.name);\n to.removeAttribute('data-transition-out');\n this.in?.({ to, from, trigger, done: resolve });\n } else {\n to.setAttribute('data-transition-in', contextual.name);\n to.removeAttribute('data-transition-out');\n contextual.in?.({ to, from, trigger, done: resolve });\n }\n });\n }\n\n hide({ trigger, contextual }) {\n const from = this.wrap.firstElementChild;\n\n return new Promise(resolve => {\n if (!contextual) {\n from.setAttribute('data-transition-out', this.name);\n from.removeAttribute('data-transition-in');\n this.out?.({ from, trigger, done: resolve });\n } else {\n from.setAttribute('data-transition-out', contextual.name);\n from.removeAttribute('data-transition-in');\n contextual.out?.({ from, trigger, done: resolve });\n }\n });\n }\n}\n"],"mappings":"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","names":["pjax_max_exports","__export","Core","Renderer","Transition","pjax_max_default","__toCommonJS","Renderer","properties","_a","_b","data","_c","PARSER","Helpers","renderers","transitions","page","view","slug","Renderer","renderer","context","transition","url","u","Core","renderers","transitions","onLeave","onEnter","manualScrollRestoration","Helpers","Renderer","el","prevent","e","links","link","urls","href","url","critical","hover","response","html","contextual","trigger","location","_a","fn","data","Transition","wrap","name","trigger","contextual","to","from","resolve","_a","_b","pjax_max_default","Core","Renderer","Transition"]}
@@ -0,0 +1,2 @@
1
+ var PjaxMax=(()=>{var p=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var m=(r,t)=>{for(var e in t)p(r,e,{get:t[e],enumerable:!0})},w=(r,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of u(t))!g.call(r,s)&&s!==e&&p(r,s,{get:()=>t[s],enumerable:!(i=f(t,s))||i.enumerable});return r};var v=r=>w(p({},"__esModule",{value:!0}),r);var y={};m(y,{Core:()=>h,Renderer:()=>a,Transition:()=>c,default:()=>T});var a=class{constructor(t){this.wrap=document.querySelector("[data-router-wrapper]"),this.properties=t,this.Transition=t.transition?new t.transition.class(this.wrap,t.transition.name):null}setup(){var t,e;(t=this.onEnter)==null||t.call(this),(e=this.onEnterCompleted)==null||e.call(this)}add(){this.wrap.insertAdjacentHTML("beforeend",this.properties.view.outerHTML)}update(){document.title=this.properties.page.title}async show(t){var e,i,s;this.update(),(e=this.onEnter)==null||e.call(this),await((i=this.Transition)==null?void 0:i.show(t)),(s=this.onEnterCompleted)==null||s.call(this)}async hide(t){var e,i,s;(e=this.onLeave)==null||e.call(this),await((i=this.Transition)==null?void 0:i.hide(t)),(s=this.onLeaveCompleted)==null||s.call(this)}};var E=new DOMParser,l=class{constructor(t,e){this.renderers=t,this.transitions=e}getDOM(t){return typeof t=="string"?E.parseFromString(t,"text/html"):t}getView(t){return t.querySelector("[data-router-view]")}getSlug(t){return t.getAttribute("data-router-view")}async getRenderer(t){if(!this.renderers||!(t in this.renderers))return a;let e=this.renderers[t];return typeof e=="function"&&!a.isPrototypeOf(e)?(await e()).default:typeof(e==null?void 0:e.then)=="function"?(await e).default:e}getTransition(t){return this.transitions?t in this.transitions?{class:this.transitions[t],name:t}:"default"in this.transitions?{class:this.transitions.default,name:"default"}:null:null}getProperties(t){let e=this.getDOM(t),i=this.getView(e),s=this.getSlug(i),n=this.getRenderer(s),o=this.getTransition(s);return{page:e,view:i,slug:s,renderer:n,transition:o}}getLocation(t){let e=new URL(t);return{href:e.href,anchor:e.hash||null,origin:e.hostname,params:e.search?Object.fromEntries(e.searchParams):null,pathname:e.pathname,scheme:e.protocol.replace(":","")}}};var h=class extends EventTarget{constructor({renderers:t,transitions:e,onLeave:i,onEnter:s,manualScrollRestoration:n=!1}={}){super(),this.Helpers=new l(t,e),this.Transitions=e,this.Contextual=!1,this.location=this.Helpers.getLocation(window.location.href),this.properties=this.Helpers.getProperties(document.cloneNode(!0)),this.popping=!1,this.running=!1,this.trigger=null,this.controller=null,this.onLeave=i||[],this.onEnter=s||[],this.namespace=this.properties.slug,this.manualScrollRestoration=n,n&&(history.scrollRestoration="manual"),this.cache=new Map,this.cache.set(this.location.href,this.properties),this.prefetching=new Set,this.properties.renderer.then(o=>{this.From=new o(this.properties),this.From.setup()}),this._navigate=this.navigate.bind(this),this._prefetchOnHover=this._prefetchOnHover.bind(this),window.addEventListener("popstate",this.popState.bind(this)),this._eventMask=this._createEventMask(),this.links=document.querySelectorAll("a:not([target]):not([data-router-disabled])"),this.attach(this.links),this._initPrefetch()}get transitioning(){return this.running}_createEventMask(){let t=document.createElement("div");t.style.cssText="position:fixed;inset:0;z-index:2147483647;pointer-events:none;";let e=i=>{i.preventDefault(),i.stopPropagation()};return t.addEventListener("scroll",e),t.addEventListener("wheel",e,{passive:!1}),t.addEventListener("touchmove",e,{passive:!1}),document.body.appendChild(t),t}attach(t){for(let e of t)e.addEventListener("click",this._navigate)}detach(t){for(let e of t)e.removeEventListener("click",this._navigate)}prefetch(t){let e=[];for(let i of t){let s=typeof i=="string"?i:i.href;s&&e.push(s)}for(let i of e)this._prefetchURL(i)}_initPrefetch(){let t=document.querySelectorAll('[data-prefetch="critical"]');t.length&&this.prefetch(t);let e=document.querySelectorAll('[data-prefetch="hover"]');for(let i of e)i.addEventListener("mouseenter",this._prefetchOnHover,{once:!0})}_prefetchOnHover(t){let e=t.currentTarget.href;e&&this._prefetchURL(e)}async _prefetchURL(t){if(!(this.cache.has(t)||this.prefetching.has(t))){try{if(new URL(t).origin!==window.location.origin)return}catch{return}this.prefetching.add(t);try{let e=await fetch(t,{mode:"same-origin",method:"GET",headers:{"X-Requested-With":"PjaxMax"},credentials:"same-origin"});if(e.ok){let i=await e.text();this.cache.set(t,this.Helpers.getProperties(i))}}catch{}finally{this.prefetching.delete(t)}}}navigate(t){if(!(t.metaKey||t.ctrlKey)){t.preventDefault();let e=t.currentTarget.hasAttribute("data-transition")?t.currentTarget.dataset.transition:!1;this.redirect(t.currentTarget.href,e,t.currentTarget)}}redirect(t,e=!1,i="script"){if(this.trigger=i,!this.running&&t!==this.location.href){let s=this.Helpers.getLocation(t);this.Contextual=!1,e&&(this.Contextual=this.Transitions[e].prototype,this.Contextual.name=e),s.origin!==this.location.origin||s.scheme!==this.location.scheme||s.anchor&&s.pathname===this.location.pathname?window.location.href=t:(this.location=s,this.beforeFetch())}}popState(){this.trigger="popstate",this.Contextual=!1;let t=this.Helpers.getLocation(window.location.href);this.location.pathname!==t.pathname||!this.location.anchor&&!t.anchor?(this.popping=!0,this.location=t,this.beforeFetch()):this.location=t}pushState(){this.popping||window.history.pushState(this.location,"",this.location.href)}async fetch(){this.controller=new AbortController;let t=await fetch(this.location.href,{mode:"same-origin",method:"GET",headers:{"X-Requested-With":"PjaxMax"},credentials:"same-origin",signal:this.controller.signal});if(t.ok)return t.text();window.location.href=this.location.href}async beforeFetch(){var e;this.pushState(),this.running=!0,this._eventMask.style.pointerEvents="auto",(e=this.controller)==null||e.abort();for(let i of this.onLeave)i();this.dispatchEvent(new CustomEvent("NAVIGATE_OUT",{detail:{from:{page:this.From.properties.page,view:this.From.properties.view},trigger:this.trigger,location:this.location}}));let t={trigger:this.trigger,contextual:this.Contextual};if(this.cache.has(this.location.href))await this.From.hide(t),this.properties=this.cache.get(this.location.href);else{let[i]=await Promise.all([this.fetch(),this.From.hide(t)]);this.properties=this.Helpers.getProperties(i),this.cache.set(this.location.href,this.properties)}this.afterFetch()}async afterFetch(){let t=await this.properties.renderer;this.To=new t(this.properties),this.To.add(),this.manualScrollRestoration&&window.scrollTo(0,0),this.dispatchEvent(new CustomEvent("NAVIGATE_IN",{detail:{to:{page:this.To.properties.page,view:this.To.wrap.lastElementChild},trigger:this.trigger,location:this.location}})),this.namespace=this.To.properties.slug;for(let e of this.onEnter)e();await this.To.show({trigger:this.trigger,contextual:this.Contextual}),this.popping=!1,this.running=!1,this._eventMask.style.pointerEvents="none",this.detach(this.links),this.links=document.querySelectorAll("a:not([target]):not([data-router-disabled])"),this.attach(this.links),this._initPrefetch(),this.dispatchEvent(new CustomEvent("NAVIGATE_END",{detail:{to:{page:this.To.properties.page,view:this.To.wrap.lastElementChild},from:{page:this.From.properties.page,view:this.From.properties.view},trigger:this.trigger,location:this.location}})),this.From=this.To,this.trigger=null}};var c=class{constructor(t,e){this.wrap=t,this.name=e}show({trigger:t,contextual:e}){let i=this.wrap.lastElementChild,s=this.wrap.firstElementChild;return new Promise(n=>{var o,d;e?(i.setAttribute("data-transition-in",e.name),i.removeAttribute("data-transition-out"),(d=e.in)==null||d.call(e,{to:i,from:s,trigger:t,done:n})):(i.setAttribute("data-transition-in",this.name),i.removeAttribute("data-transition-out"),(o=this.in)==null||o.call(this,{to:i,from:s,trigger:t,done:n}))})}hide({trigger:t,contextual:e}){let i=this.wrap.firstElementChild;return new Promise(s=>{var n,o;e?(i.setAttribute("data-transition-out",e.name),i.removeAttribute("data-transition-in"),(o=e.out)==null||o.call(e,{from:i,trigger:t,done:s})):(i.setAttribute("data-transition-out",this.name),i.removeAttribute("data-transition-in"),(n=this.out)==null||n.call(this,{from:i,trigger:t,done:s}))})}};var T={Core:h,Renderer:a,Transition:c};return v(y);})();
2
+ //# sourceMappingURL=pjax-max.global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pjax-max.js","../src/renderer.js","../src/helpers.js","../src/core.js","../src/transition.js"],"sourcesContent":["import Core from './core';\nimport Renderer from './renderer';\nimport Transition from './transition';\n\nexport { Core, Renderer, Transition };\nexport default { Core, Renderer, Transition };\n","export default class Renderer {\n\n constructor(properties) {\n this.wrap = document.querySelector('[data-router-wrapper]');\n this.properties = properties;\n this.Transition = properties.transition\n ? new properties.transition.class(this.wrap, properties.transition.name)\n : null;\n }\n\n setup() {\n this.onEnter?.();\n this.onEnterCompleted?.();\n }\n\n add() {\n this.wrap.insertAdjacentHTML('beforeend', this.properties.view.outerHTML);\n }\n\n update() {\n document.title = this.properties.page.title;\n }\n\n async show(data) {\n this.update();\n this.onEnter?.();\n await this.Transition?.show(data);\n this.onEnterCompleted?.();\n }\n\n async hide(data) {\n this.onLeave?.();\n await this.Transition?.hide(data);\n this.onLeaveCompleted?.();\n }\n}\n","import Renderer from './renderer';\n\nconst PARSER = new DOMParser();\n\nexport default class Helpers {\n\n constructor(renderers, transitions) {\n this.renderers = renderers;\n this.transitions = transitions;\n }\n\n getDOM(page) {\n return typeof page === 'string' ? PARSER.parseFromString(page, 'text/html') : page;\n }\n\n getView(page) {\n return page.querySelector('[data-router-view]');\n }\n\n getSlug(view) {\n return view.getAttribute('data-router-view');\n }\n\n async getRenderer(slug) {\n if (!this.renderers || !(slug in this.renderers)) {\n return Renderer;\n }\n\n const renderer = this.renderers[slug];\n\n // Dynamic import factory function\n if (typeof renderer === 'function' && !Renderer.isPrototypeOf(renderer)) {\n const mod = await renderer();\n return mod.default;\n }\n\n // Already a promise (e.g., top-level import())\n if (typeof renderer?.then === 'function') {\n const mod = await renderer;\n return mod.default;\n }\n\n return renderer;\n }\n\n getTransition(slug) {\n if (!this.transitions) {\n return null;\n }\n\n if (slug in this.transitions) {\n return { class: this.transitions[slug], name: slug };\n }\n\n if ('default' in this.transitions) {\n return { class: this.transitions['default'], name: 'default' };\n }\n\n return null;\n }\n\n getProperties(context) {\n const page = this.getDOM(context);\n const view = this.getView(page);\n const slug = this.getSlug(view);\n const renderer = this.getRenderer(slug);\n const transition = this.getTransition(slug);\n\n return {\n page,\n view,\n slug,\n renderer,\n transition\n };\n }\n\n getLocation(url) {\n const u = new URL(url);\n return {\n href: u.href,\n anchor: u.hash || null,\n origin: u.hostname,\n params: u.search ? Object.fromEntries(u.searchParams) : null,\n pathname: u.pathname,\n scheme: u.protocol.replace(':', '')\n };\n }\n}\n","import Helpers from './helpers';\n\nexport default class Core extends EventTarget {\n\n constructor({ renderers, transitions, onLeave, onEnter, manualScrollRestoration = false } = {}) {\n super();\n\n this.Helpers = new Helpers(renderers, transitions);\n this.Transitions = transitions;\n this.Contextual = false;\n\n this.location = this.Helpers.getLocation(window.location.href);\n this.properties = this.Helpers.getProperties(document.cloneNode(true));\n\n this.popping = false;\n this.running = false;\n this.trigger = null;\n this.controller = null;\n\n this.onLeave = onLeave || [];\n this.onEnter = onEnter || [];\n this.namespace = this.properties.slug;\n this.manualScrollRestoration = manualScrollRestoration;\n\n if (manualScrollRestoration) {\n history.scrollRestoration = 'manual';\n }\n\n this.cache = new Map();\n this.cache.set(this.location.href, this.properties);\n this.prefetching = new Set();\n\n this.properties.renderer.then(Renderer => {\n this.From = new Renderer(this.properties);\n this.From.setup();\n });\n\n this._navigate = this.navigate.bind(this);\n this._prefetchOnHover = this._prefetchOnHover.bind(this);\n window.addEventListener('popstate', this.popState.bind(this));\n\n this._eventMask = this._createEventMask();\n\n this.links = document.querySelectorAll('a:not([target]):not([data-router-disabled])');\n this.attach(this.links);\n\n this._initPrefetch();\n }\n\n get transitioning() {\n return this.running;\n }\n\n _createEventMask() {\n const el = document.createElement('div');\n el.style.cssText = 'position:fixed;inset:0;z-index:2147483647;pointer-events:none;';\n const prevent = (e) => { e.preventDefault(); e.stopPropagation(); };\n el.addEventListener('scroll', prevent);\n el.addEventListener('wheel', prevent, { passive: false });\n el.addEventListener('touchmove', prevent, { passive: false });\n document.body.appendChild(el);\n return el;\n }\n\n attach(links) {\n for (const link of links) {\n link.addEventListener('click', this._navigate);\n }\n }\n\n detach(links) {\n for (const link of links) {\n link.removeEventListener('click', this._navigate);\n }\n }\n\n /**\n * Prefetch URLs and store results in the cache.\n * Accepts an array of links (elements or URL strings).\n */\n prefetch(links) {\n const urls = [];\n\n for (const link of links) {\n const href = typeof link === 'string' ? link : link.href;\n if (href) urls.push(href);\n }\n\n for (const url of urls) {\n this._prefetchURL(url);\n }\n }\n\n _initPrefetch() {\n // Prefetch critical links immediately\n const critical = document.querySelectorAll('[data-prefetch=\"critical\"]');\n if (critical.length) this.prefetch(critical);\n\n // Attach hover listeners to hover-prefetch links\n const hover = document.querySelectorAll('[data-prefetch=\"hover\"]');\n for (const link of hover) {\n link.addEventListener('mouseenter', this._prefetchOnHover, { once: true });\n }\n }\n\n _prefetchOnHover(e) {\n const href = e.currentTarget.href;\n if (href) this._prefetchURL(href);\n }\n\n async _prefetchURL(url) {\n if (this.cache.has(url) || this.prefetching.has(url)) return;\n\n // Normalize against current origin\n try {\n const parsed = new URL(url);\n if (parsed.origin !== window.location.origin) return;\n } catch {\n return;\n }\n\n this.prefetching.add(url);\n\n try {\n const response = await fetch(url, {\n mode: 'same-origin',\n method: 'GET',\n headers: { 'X-Requested-With': 'PjaxMax' },\n credentials: 'same-origin'\n });\n\n if (response.ok) {\n const html = await response.text();\n this.cache.set(url, this.Helpers.getProperties(html));\n }\n } catch {\n // Prefetch is best-effort — silently ignore failures\n } finally {\n this.prefetching.delete(url);\n }\n }\n\n navigate(e) {\n if (!(e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n\n const contextual = e.currentTarget.hasAttribute('data-transition')\n ? e.currentTarget.dataset.transition\n : false;\n\n this.redirect(e.currentTarget.href, contextual, e.currentTarget);\n }\n }\n\n redirect(href, contextual = false, trigger = 'script') {\n this.trigger = trigger;\n\n if (!this.running && href !== this.location.href) {\n const location = this.Helpers.getLocation(href);\n\n this.Contextual = false;\n\n if (contextual) {\n this.Contextual = this.Transitions[contextual].prototype;\n this.Contextual.name = contextual;\n }\n\n if (location.origin !== this.location.origin || location.scheme !== this.location.scheme || location.anchor && location.pathname === this.location.pathname) {\n window.location.href = href;\n } else {\n this.location = location;\n this.beforeFetch();\n }\n }\n }\n\n popState() {\n this.trigger = 'popstate';\n this.Contextual = false;\n\n const location = this.Helpers.getLocation(window.location.href);\n\n if (this.location.pathname !== location.pathname || !this.location.anchor && !location.anchor) {\n this.popping = true;\n this.location = location;\n this.beforeFetch();\n } else {\n this.location = location;\n }\n }\n\n pushState() {\n if (!this.popping) {\n window.history.pushState(this.location, '', this.location.href);\n }\n }\n\n async fetch() {\n this.controller = new AbortController();\n\n const response = await fetch(this.location.href, {\n mode: 'same-origin',\n method: 'GET',\n headers: { 'X-Requested-With': 'PjaxMax' },\n credentials: 'same-origin',\n signal: this.controller.signal\n });\n\n if (response.ok) {\n return response.text();\n }\n\n window.location.href = this.location.href;\n }\n\n async beforeFetch() {\n this.pushState();\n this.running = true;\n this._eventMask.style.pointerEvents = 'auto';\n\n // Abort any in-flight fetch from a previous navigation\n this.controller?.abort();\n\n for (const fn of this.onLeave) fn();\n\n this.dispatchEvent(new CustomEvent('NAVIGATE_OUT', {\n detail: {\n from: {\n page: this.From.properties.page,\n view: this.From.properties.view\n },\n trigger: this.trigger,\n location: this.location\n }\n }));\n\n const data = {\n trigger: this.trigger,\n contextual: this.Contextual\n };\n\n if (this.cache.has(this.location.href)) {\n await this.From.hide(data);\n this.properties = this.cache.get(this.location.href);\n } else {\n const [html] = await Promise.all([\n this.fetch(),\n this.From.hide(data)\n ]);\n\n this.properties = this.Helpers.getProperties(html);\n this.cache.set(this.location.href, this.properties);\n }\n\n this.afterFetch();\n }\n\n async afterFetch() {\n const Renderer = await this.properties.renderer;\n\n this.To = new Renderer(this.properties);\n this.To.add();\n\n if (this.manualScrollRestoration) window.scrollTo(0, 0);\n\n this.dispatchEvent(new CustomEvent('NAVIGATE_IN', {\n detail: {\n to: {\n page: this.To.properties.page,\n view: this.To.wrap.lastElementChild\n },\n trigger: this.trigger,\n location: this.location\n }\n }));\n\n this.namespace = this.To.properties.slug;\n for (const fn of this.onEnter) fn();\n\n await this.To.show({\n trigger: this.trigger,\n contextual: this.Contextual\n });\n\n this.popping = false;\n this.running = false;\n this._eventMask.style.pointerEvents = 'none';\n\n this.detach(this.links);\n this.links = document.querySelectorAll('a:not([target]):not([data-router-disabled])');\n this.attach(this.links);\n this._initPrefetch();\n\n this.dispatchEvent(new CustomEvent('NAVIGATE_END', {\n detail: {\n to: {\n page: this.To.properties.page,\n view: this.To.wrap.lastElementChild\n },\n from: {\n page: this.From.properties.page,\n view: this.From.properties.view\n },\n trigger: this.trigger,\n location: this.location\n }\n }));\n\n this.From = this.To;\n this.trigger = null;\n }\n}\n","export default class Transition {\n\n constructor(wrap, name) {\n this.wrap = wrap;\n this.name = name;\n }\n\n show({ trigger, contextual }) {\n const to = this.wrap.lastElementChild;\n const from = this.wrap.firstElementChild;\n\n return new Promise(resolve => {\n if (!contextual) {\n to.setAttribute('data-transition-in', this.name);\n to.removeAttribute('data-transition-out');\n this.in?.({ to, from, trigger, done: resolve });\n } else {\n to.setAttribute('data-transition-in', contextual.name);\n to.removeAttribute('data-transition-out');\n contextual.in?.({ to, from, trigger, done: resolve });\n }\n });\n }\n\n hide({ trigger, contextual }) {\n const from = this.wrap.firstElementChild;\n\n return new Promise(resolve => {\n if (!contextual) {\n from.setAttribute('data-transition-out', this.name);\n from.removeAttribute('data-transition-in');\n this.out?.({ from, trigger, done: resolve });\n } else {\n from.setAttribute('data-transition-out', contextual.name);\n from.removeAttribute('data-transition-in');\n contextual.out?.({ from, trigger, done: resolve });\n }\n });\n }\n}\n"],"mappings":"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","names":["pjax_max_exports","__export","Core","Renderer","Transition","pjax_max_default","Renderer","properties","_a","_b","data","_c","PARSER","Helpers","renderers","transitions","page","view","slug","Renderer","renderer","context","transition","url","u","Core","renderers","transitions","onLeave","onEnter","manualScrollRestoration","Helpers","Renderer","el","prevent","e","links","link","urls","href","url","critical","hover","response","html","contextual","trigger","location","_a","fn","data","Transition","wrap","name","trigger","contextual","to","from","resolve","_a","_b","pjax_max_default","Core","Renderer","Transition"]}
@@ -0,0 +1,2 @@
1
+ var o=class{constructor(t){this.wrap=document.querySelector("[data-router-wrapper]"),this.properties=t,this.Transition=t.transition?new t.transition.class(this.wrap,t.transition.name):null}setup(){var t,e;(t=this.onEnter)==null||t.call(this),(e=this.onEnterCompleted)==null||e.call(this)}add(){this.wrap.insertAdjacentHTML("beforeend",this.properties.view.outerHTML)}update(){document.title=this.properties.page.title}async show(t){var e,i,s;this.update(),(e=this.onEnter)==null||e.call(this),await((i=this.Transition)==null?void 0:i.show(t)),(s=this.onEnterCompleted)==null||s.call(this)}async hide(t){var e,i,s;(e=this.onLeave)==null||e.call(this),await((i=this.Transition)==null?void 0:i.hide(t)),(s=this.onLeaveCompleted)==null||s.call(this)}};var d=new DOMParser,a=class{constructor(t,e){this.renderers=t,this.transitions=e}getDOM(t){return typeof t=="string"?d.parseFromString(t,"text/html"):t}getView(t){return t.querySelector("[data-router-view]")}getSlug(t){return t.getAttribute("data-router-view")}async getRenderer(t){if(!this.renderers||!(t in this.renderers))return o;let e=this.renderers[t];return typeof e=="function"&&!o.isPrototypeOf(e)?(await e()).default:typeof(e==null?void 0:e.then)=="function"?(await e).default:e}getTransition(t){return this.transitions?t in this.transitions?{class:this.transitions[t],name:t}:"default"in this.transitions?{class:this.transitions.default,name:"default"}:null:null}getProperties(t){let e=this.getDOM(t),i=this.getView(e),s=this.getSlug(i),r=this.getRenderer(s),n=this.getTransition(s);return{page:e,view:i,slug:s,renderer:r,transition:n}}getLocation(t){let e=new URL(t);return{href:e.href,anchor:e.hash||null,origin:e.hostname,params:e.search?Object.fromEntries(e.searchParams):null,pathname:e.pathname,scheme:e.protocol.replace(":","")}}};var h=class extends EventTarget{constructor({renderers:t,transitions:e,onLeave:i,onEnter:s,manualScrollRestoration:r=!1}={}){super(),this.Helpers=new a(t,e),this.Transitions=e,this.Contextual=!1,this.location=this.Helpers.getLocation(window.location.href),this.properties=this.Helpers.getProperties(document.cloneNode(!0)),this.popping=!1,this.running=!1,this.trigger=null,this.controller=null,this.onLeave=i||[],this.onEnter=s||[],this.namespace=this.properties.slug,this.manualScrollRestoration=r,r&&(history.scrollRestoration="manual"),this.cache=new Map,this.cache.set(this.location.href,this.properties),this.prefetching=new Set,this.properties.renderer.then(n=>{this.From=new n(this.properties),this.From.setup()}),this._navigate=this.navigate.bind(this),this._prefetchOnHover=this._prefetchOnHover.bind(this),window.addEventListener("popstate",this.popState.bind(this)),this._eventMask=this._createEventMask(),this.links=document.querySelectorAll("a:not([target]):not([data-router-disabled])"),this.attach(this.links),this._initPrefetch()}get transitioning(){return this.running}_createEventMask(){let t=document.createElement("div");t.style.cssText="position:fixed;inset:0;z-index:2147483647;pointer-events:none;";let e=i=>{i.preventDefault(),i.stopPropagation()};return t.addEventListener("scroll",e),t.addEventListener("wheel",e,{passive:!1}),t.addEventListener("touchmove",e,{passive:!1}),document.body.appendChild(t),t}attach(t){for(let e of t)e.addEventListener("click",this._navigate)}detach(t){for(let e of t)e.removeEventListener("click",this._navigate)}prefetch(t){let e=[];for(let i of t){let s=typeof i=="string"?i:i.href;s&&e.push(s)}for(let i of e)this._prefetchURL(i)}_initPrefetch(){let t=document.querySelectorAll('[data-prefetch="critical"]');t.length&&this.prefetch(t);let e=document.querySelectorAll('[data-prefetch="hover"]');for(let i of e)i.addEventListener("mouseenter",this._prefetchOnHover,{once:!0})}_prefetchOnHover(t){let e=t.currentTarget.href;e&&this._prefetchURL(e)}async _prefetchURL(t){if(!(this.cache.has(t)||this.prefetching.has(t))){try{if(new URL(t).origin!==window.location.origin)return}catch{return}this.prefetching.add(t);try{let e=await fetch(t,{mode:"same-origin",method:"GET",headers:{"X-Requested-With":"PjaxMax"},credentials:"same-origin"});if(e.ok){let i=await e.text();this.cache.set(t,this.Helpers.getProperties(i))}}catch{}finally{this.prefetching.delete(t)}}}navigate(t){if(!(t.metaKey||t.ctrlKey)){t.preventDefault();let e=t.currentTarget.hasAttribute("data-transition")?t.currentTarget.dataset.transition:!1;this.redirect(t.currentTarget.href,e,t.currentTarget)}}redirect(t,e=!1,i="script"){if(this.trigger=i,!this.running&&t!==this.location.href){let s=this.Helpers.getLocation(t);this.Contextual=!1,e&&(this.Contextual=this.Transitions[e].prototype,this.Contextual.name=e),s.origin!==this.location.origin||s.scheme!==this.location.scheme||s.anchor&&s.pathname===this.location.pathname?window.location.href=t:(this.location=s,this.beforeFetch())}}popState(){this.trigger="popstate",this.Contextual=!1;let t=this.Helpers.getLocation(window.location.href);this.location.pathname!==t.pathname||!this.location.anchor&&!t.anchor?(this.popping=!0,this.location=t,this.beforeFetch()):this.location=t}pushState(){this.popping||window.history.pushState(this.location,"",this.location.href)}async fetch(){this.controller=new AbortController;let t=await fetch(this.location.href,{mode:"same-origin",method:"GET",headers:{"X-Requested-With":"PjaxMax"},credentials:"same-origin",signal:this.controller.signal});if(t.ok)return t.text();window.location.href=this.location.href}async beforeFetch(){var e;this.pushState(),this.running=!0,this._eventMask.style.pointerEvents="auto",(e=this.controller)==null||e.abort();for(let i of this.onLeave)i();this.dispatchEvent(new CustomEvent("NAVIGATE_OUT",{detail:{from:{page:this.From.properties.page,view:this.From.properties.view},trigger:this.trigger,location:this.location}}));let t={trigger:this.trigger,contextual:this.Contextual};if(this.cache.has(this.location.href))await this.From.hide(t),this.properties=this.cache.get(this.location.href);else{let[i]=await Promise.all([this.fetch(),this.From.hide(t)]);this.properties=this.Helpers.getProperties(i),this.cache.set(this.location.href,this.properties)}this.afterFetch()}async afterFetch(){let t=await this.properties.renderer;this.To=new t(this.properties),this.To.add(),this.manualScrollRestoration&&window.scrollTo(0,0),this.dispatchEvent(new CustomEvent("NAVIGATE_IN",{detail:{to:{page:this.To.properties.page,view:this.To.wrap.lastElementChild},trigger:this.trigger,location:this.location}})),this.namespace=this.To.properties.slug;for(let e of this.onEnter)e();await this.To.show({trigger:this.trigger,contextual:this.Contextual}),this.popping=!1,this.running=!1,this._eventMask.style.pointerEvents="none",this.detach(this.links),this.links=document.querySelectorAll("a:not([target]):not([data-router-disabled])"),this.attach(this.links),this._initPrefetch(),this.dispatchEvent(new CustomEvent("NAVIGATE_END",{detail:{to:{page:this.To.properties.page,view:this.To.wrap.lastElementChild},from:{page:this.From.properties.page,view:this.From.properties.view},trigger:this.trigger,location:this.location}})),this.From=this.To,this.trigger=null}};var c=class{constructor(t,e){this.wrap=t,this.name=e}show({trigger:t,contextual:e}){let i=this.wrap.lastElementChild,s=this.wrap.firstElementChild;return new Promise(r=>{var n,p;e?(i.setAttribute("data-transition-in",e.name),i.removeAttribute("data-transition-out"),(p=e.in)==null||p.call(e,{to:i,from:s,trigger:t,done:r})):(i.setAttribute("data-transition-in",this.name),i.removeAttribute("data-transition-out"),(n=this.in)==null||n.call(this,{to:i,from:s,trigger:t,done:r}))})}hide({trigger:t,contextual:e}){let i=this.wrap.firstElementChild;return new Promise(s=>{var r,n;e?(i.setAttribute("data-transition-out",e.name),i.removeAttribute("data-transition-in"),(n=e.out)==null||n.call(e,{from:i,trigger:t,done:s})):(i.setAttribute("data-transition-out",this.name),i.removeAttribute("data-transition-in"),(r=this.out)==null||r.call(this,{from:i,trigger:t,done:s}))})}};var b={Core:h,Renderer:o,Transition:c};export{h as Core,o as Renderer,c as Transition,b as default};
2
+ //# sourceMappingURL=pjax-max.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/renderer.js","../src/helpers.js","../src/core.js","../src/transition.js","../src/pjax-max.js"],"sourcesContent":["export default class Renderer {\n\n constructor(properties) {\n this.wrap = document.querySelector('[data-router-wrapper]');\n this.properties = properties;\n this.Transition = properties.transition\n ? new properties.transition.class(this.wrap, properties.transition.name)\n : null;\n }\n\n setup() {\n this.onEnter?.();\n this.onEnterCompleted?.();\n }\n\n add() {\n this.wrap.insertAdjacentHTML('beforeend', this.properties.view.outerHTML);\n }\n\n update() {\n document.title = this.properties.page.title;\n }\n\n async show(data) {\n this.update();\n this.onEnter?.();\n await this.Transition?.show(data);\n this.onEnterCompleted?.();\n }\n\n async hide(data) {\n this.onLeave?.();\n await this.Transition?.hide(data);\n this.onLeaveCompleted?.();\n }\n}\n","import Renderer from './renderer';\n\nconst PARSER = new DOMParser();\n\nexport default class Helpers {\n\n constructor(renderers, transitions) {\n this.renderers = renderers;\n this.transitions = transitions;\n }\n\n getDOM(page) {\n return typeof page === 'string' ? PARSER.parseFromString(page, 'text/html') : page;\n }\n\n getView(page) {\n return page.querySelector('[data-router-view]');\n }\n\n getSlug(view) {\n return view.getAttribute('data-router-view');\n }\n\n async getRenderer(slug) {\n if (!this.renderers || !(slug in this.renderers)) {\n return Renderer;\n }\n\n const renderer = this.renderers[slug];\n\n // Dynamic import factory function\n if (typeof renderer === 'function' && !Renderer.isPrototypeOf(renderer)) {\n const mod = await renderer();\n return mod.default;\n }\n\n // Already a promise (e.g., top-level import())\n if (typeof renderer?.then === 'function') {\n const mod = await renderer;\n return mod.default;\n }\n\n return renderer;\n }\n\n getTransition(slug) {\n if (!this.transitions) {\n return null;\n }\n\n if (slug in this.transitions) {\n return { class: this.transitions[slug], name: slug };\n }\n\n if ('default' in this.transitions) {\n return { class: this.transitions['default'], name: 'default' };\n }\n\n return null;\n }\n\n getProperties(context) {\n const page = this.getDOM(context);\n const view = this.getView(page);\n const slug = this.getSlug(view);\n const renderer = this.getRenderer(slug);\n const transition = this.getTransition(slug);\n\n return {\n page,\n view,\n slug,\n renderer,\n transition\n };\n }\n\n getLocation(url) {\n const u = new URL(url);\n return {\n href: u.href,\n anchor: u.hash || null,\n origin: u.hostname,\n params: u.search ? Object.fromEntries(u.searchParams) : null,\n pathname: u.pathname,\n scheme: u.protocol.replace(':', '')\n };\n }\n}\n","import Helpers from './helpers';\n\nexport default class Core extends EventTarget {\n\n constructor({ renderers, transitions, onLeave, onEnter, manualScrollRestoration = false } = {}) {\n super();\n\n this.Helpers = new Helpers(renderers, transitions);\n this.Transitions = transitions;\n this.Contextual = false;\n\n this.location = this.Helpers.getLocation(window.location.href);\n this.properties = this.Helpers.getProperties(document.cloneNode(true));\n\n this.popping = false;\n this.running = false;\n this.trigger = null;\n this.controller = null;\n\n this.onLeave = onLeave || [];\n this.onEnter = onEnter || [];\n this.namespace = this.properties.slug;\n this.manualScrollRestoration = manualScrollRestoration;\n\n if (manualScrollRestoration) {\n history.scrollRestoration = 'manual';\n }\n\n this.cache = new Map();\n this.cache.set(this.location.href, this.properties);\n this.prefetching = new Set();\n\n this.properties.renderer.then(Renderer => {\n this.From = new Renderer(this.properties);\n this.From.setup();\n });\n\n this._navigate = this.navigate.bind(this);\n this._prefetchOnHover = this._prefetchOnHover.bind(this);\n window.addEventListener('popstate', this.popState.bind(this));\n\n this._eventMask = this._createEventMask();\n\n this.links = document.querySelectorAll('a:not([target]):not([data-router-disabled])');\n this.attach(this.links);\n\n this._initPrefetch();\n }\n\n get transitioning() {\n return this.running;\n }\n\n _createEventMask() {\n const el = document.createElement('div');\n el.style.cssText = 'position:fixed;inset:0;z-index:2147483647;pointer-events:none;';\n const prevent = (e) => { e.preventDefault(); e.stopPropagation(); };\n el.addEventListener('scroll', prevent);\n el.addEventListener('wheel', prevent, { passive: false });\n el.addEventListener('touchmove', prevent, { passive: false });\n document.body.appendChild(el);\n return el;\n }\n\n attach(links) {\n for (const link of links) {\n link.addEventListener('click', this._navigate);\n }\n }\n\n detach(links) {\n for (const link of links) {\n link.removeEventListener('click', this._navigate);\n }\n }\n\n /**\n * Prefetch URLs and store results in the cache.\n * Accepts an array of links (elements or URL strings).\n */\n prefetch(links) {\n const urls = [];\n\n for (const link of links) {\n const href = typeof link === 'string' ? link : link.href;\n if (href) urls.push(href);\n }\n\n for (const url of urls) {\n this._prefetchURL(url);\n }\n }\n\n _initPrefetch() {\n // Prefetch critical links immediately\n const critical = document.querySelectorAll('[data-prefetch=\"critical\"]');\n if (critical.length) this.prefetch(critical);\n\n // Attach hover listeners to hover-prefetch links\n const hover = document.querySelectorAll('[data-prefetch=\"hover\"]');\n for (const link of hover) {\n link.addEventListener('mouseenter', this._prefetchOnHover, { once: true });\n }\n }\n\n _prefetchOnHover(e) {\n const href = e.currentTarget.href;\n if (href) this._prefetchURL(href);\n }\n\n async _prefetchURL(url) {\n if (this.cache.has(url) || this.prefetching.has(url)) return;\n\n // Normalize against current origin\n try {\n const parsed = new URL(url);\n if (parsed.origin !== window.location.origin) return;\n } catch {\n return;\n }\n\n this.prefetching.add(url);\n\n try {\n const response = await fetch(url, {\n mode: 'same-origin',\n method: 'GET',\n headers: { 'X-Requested-With': 'PjaxMax' },\n credentials: 'same-origin'\n });\n\n if (response.ok) {\n const html = await response.text();\n this.cache.set(url, this.Helpers.getProperties(html));\n }\n } catch {\n // Prefetch is best-effort — silently ignore failures\n } finally {\n this.prefetching.delete(url);\n }\n }\n\n navigate(e) {\n if (!(e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n\n const contextual = e.currentTarget.hasAttribute('data-transition')\n ? e.currentTarget.dataset.transition\n : false;\n\n this.redirect(e.currentTarget.href, contextual, e.currentTarget);\n }\n }\n\n redirect(href, contextual = false, trigger = 'script') {\n this.trigger = trigger;\n\n if (!this.running && href !== this.location.href) {\n const location = this.Helpers.getLocation(href);\n\n this.Contextual = false;\n\n if (contextual) {\n this.Contextual = this.Transitions[contextual].prototype;\n this.Contextual.name = contextual;\n }\n\n if (location.origin !== this.location.origin || location.scheme !== this.location.scheme || location.anchor && location.pathname === this.location.pathname) {\n window.location.href = href;\n } else {\n this.location = location;\n this.beforeFetch();\n }\n }\n }\n\n popState() {\n this.trigger = 'popstate';\n this.Contextual = false;\n\n const location = this.Helpers.getLocation(window.location.href);\n\n if (this.location.pathname !== location.pathname || !this.location.anchor && !location.anchor) {\n this.popping = true;\n this.location = location;\n this.beforeFetch();\n } else {\n this.location = location;\n }\n }\n\n pushState() {\n if (!this.popping) {\n window.history.pushState(this.location, '', this.location.href);\n }\n }\n\n async fetch() {\n this.controller = new AbortController();\n\n const response = await fetch(this.location.href, {\n mode: 'same-origin',\n method: 'GET',\n headers: { 'X-Requested-With': 'PjaxMax' },\n credentials: 'same-origin',\n signal: this.controller.signal\n });\n\n if (response.ok) {\n return response.text();\n }\n\n window.location.href = this.location.href;\n }\n\n async beforeFetch() {\n this.pushState();\n this.running = true;\n this._eventMask.style.pointerEvents = 'auto';\n\n // Abort any in-flight fetch from a previous navigation\n this.controller?.abort();\n\n for (const fn of this.onLeave) fn();\n\n this.dispatchEvent(new CustomEvent('NAVIGATE_OUT', {\n detail: {\n from: {\n page: this.From.properties.page,\n view: this.From.properties.view\n },\n trigger: this.trigger,\n location: this.location\n }\n }));\n\n const data = {\n trigger: this.trigger,\n contextual: this.Contextual\n };\n\n if (this.cache.has(this.location.href)) {\n await this.From.hide(data);\n this.properties = this.cache.get(this.location.href);\n } else {\n const [html] = await Promise.all([\n this.fetch(),\n this.From.hide(data)\n ]);\n\n this.properties = this.Helpers.getProperties(html);\n this.cache.set(this.location.href, this.properties);\n }\n\n this.afterFetch();\n }\n\n async afterFetch() {\n const Renderer = await this.properties.renderer;\n\n this.To = new Renderer(this.properties);\n this.To.add();\n\n if (this.manualScrollRestoration) window.scrollTo(0, 0);\n\n this.dispatchEvent(new CustomEvent('NAVIGATE_IN', {\n detail: {\n to: {\n page: this.To.properties.page,\n view: this.To.wrap.lastElementChild\n },\n trigger: this.trigger,\n location: this.location\n }\n }));\n\n this.namespace = this.To.properties.slug;\n for (const fn of this.onEnter) fn();\n\n await this.To.show({\n trigger: this.trigger,\n contextual: this.Contextual\n });\n\n this.popping = false;\n this.running = false;\n this._eventMask.style.pointerEvents = 'none';\n\n this.detach(this.links);\n this.links = document.querySelectorAll('a:not([target]):not([data-router-disabled])');\n this.attach(this.links);\n this._initPrefetch();\n\n this.dispatchEvent(new CustomEvent('NAVIGATE_END', {\n detail: {\n to: {\n page: this.To.properties.page,\n view: this.To.wrap.lastElementChild\n },\n from: {\n page: this.From.properties.page,\n view: this.From.properties.view\n },\n trigger: this.trigger,\n location: this.location\n }\n }));\n\n this.From = this.To;\n this.trigger = null;\n }\n}\n","export default class Transition {\n\n constructor(wrap, name) {\n this.wrap = wrap;\n this.name = name;\n }\n\n show({ trigger, contextual }) {\n const to = this.wrap.lastElementChild;\n const from = this.wrap.firstElementChild;\n\n return new Promise(resolve => {\n if (!contextual) {\n to.setAttribute('data-transition-in', this.name);\n to.removeAttribute('data-transition-out');\n this.in?.({ to, from, trigger, done: resolve });\n } else {\n to.setAttribute('data-transition-in', contextual.name);\n to.removeAttribute('data-transition-out');\n contextual.in?.({ to, from, trigger, done: resolve });\n }\n });\n }\n\n hide({ trigger, contextual }) {\n const from = this.wrap.firstElementChild;\n\n return new Promise(resolve => {\n if (!contextual) {\n from.setAttribute('data-transition-out', this.name);\n from.removeAttribute('data-transition-in');\n this.out?.({ from, trigger, done: resolve });\n } else {\n from.setAttribute('data-transition-out', contextual.name);\n from.removeAttribute('data-transition-in');\n contextual.out?.({ from, trigger, done: resolve });\n }\n });\n }\n}\n","import Core from './core';\nimport Renderer from './renderer';\nimport Transition from './transition';\n\nexport { Core, Renderer, Transition };\nexport default { Core, Renderer, Transition };\n"],"mappings":"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","names":["Renderer","properties","_a","_b","data","_c","PARSER","Helpers","renderers","transitions","page","view","slug","Renderer","renderer","context","transition","url","u","Core","renderers","transitions","onLeave","onEnter","manualScrollRestoration","Helpers","Renderer","el","prevent","e","links","link","urls","href","url","critical","hover","response","html","contextual","trigger","location","_a","fn","data","Transition","wrap","name","trigger","contextual","to","from","resolve","_a","_b","pjax_max_default","Core","Renderer","Transition"]}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "pjax-max",
3
+ "version": "3.0.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "description": "Lightweight PJAX library for page transitions",
7
+ "source": "src/pjax-max.js",
8
+ "main": "./dist/pjax-max.cjs",
9
+ "module": "./dist/pjax-max.js",
10
+ "unpkg": "./dist/pjax-max.global.js",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/pjax-max.js",
14
+ "require": "./dist/pjax-max.cjs",
15
+ "default": "./dist/pjax-max.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "src",
20
+ "dist"
21
+ ],
22
+ "contributors": [
23
+ {
24
+ "name": "Anthony Du Pont",
25
+ "url": "https://twitter.com/Anthodpnt"
26
+ },
27
+ {
28
+ "name": "Thao Delefortrie",
29
+ "url": "https://twitter.com/ThaoD5"
30
+ },
31
+ {
32
+ "name": "Josh Kirk",
33
+ "email": "josh@zero.nyc",
34
+ "url": "https://twitter.com/joshgkirk"
35
+ },
36
+ {
37
+ "name": "Mike Wagz",
38
+ "email": "mike@selfaware.studio",
39
+ "url": "https://twitter.com/mike_wagz"
40
+ }
41
+ ],
42
+ "scripts": {
43
+ "clean": "rm -rf dist",
44
+ "build": "tsup",
45
+ "watch": "tsup --watch"
46
+ },
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+https://github.com/joshgkirk/pjax-max.git"
50
+ },
51
+ "keywords": [
52
+ "pjax",
53
+ "transitions",
54
+ "spa",
55
+ "navigation",
56
+ "ajax",
57
+ "javascript",
58
+ "library"
59
+ ],
60
+ "bugs": {
61
+ "url": "https://github.com/joshgkirk/pjax-max/issues"
62
+ },
63
+ "engines": {
64
+ "node": ">=18"
65
+ },
66
+ "devDependencies": {
67
+ "tsup": "^8.0.0",
68
+ "typescript": "^6.0.2"
69
+ }
70
+ }
package/src/core.js ADDED
@@ -0,0 +1,312 @@
1
+ import Helpers from './helpers';
2
+
3
+ export default class Core extends EventTarget {
4
+
5
+ constructor({ renderers, transitions, onLeave, onEnter, manualScrollRestoration = false } = {}) {
6
+ super();
7
+
8
+ this.Helpers = new Helpers(renderers, transitions);
9
+ this.Transitions = transitions;
10
+ this.Contextual = false;
11
+
12
+ this.location = this.Helpers.getLocation(window.location.href);
13
+ this.properties = this.Helpers.getProperties(document.cloneNode(true));
14
+
15
+ this.popping = false;
16
+ this.running = false;
17
+ this.trigger = null;
18
+ this.controller = null;
19
+
20
+ this.onLeave = onLeave || [];
21
+ this.onEnter = onEnter || [];
22
+ this.namespace = this.properties.slug;
23
+ this.manualScrollRestoration = manualScrollRestoration;
24
+
25
+ if (manualScrollRestoration) {
26
+ history.scrollRestoration = 'manual';
27
+ }
28
+
29
+ this.cache = new Map();
30
+ this.cache.set(this.location.href, this.properties);
31
+ this.prefetching = new Set();
32
+
33
+ this.properties.renderer.then(Renderer => {
34
+ this.From = new Renderer(this.properties);
35
+ this.From.setup();
36
+ });
37
+
38
+ this._navigate = this.navigate.bind(this);
39
+ this._prefetchOnHover = this._prefetchOnHover.bind(this);
40
+ window.addEventListener('popstate', this.popState.bind(this));
41
+
42
+ this._eventMask = this._createEventMask();
43
+
44
+ this.links = document.querySelectorAll('a:not([target]):not([data-router-disabled])');
45
+ this.attach(this.links);
46
+
47
+ this._initPrefetch();
48
+ }
49
+
50
+ get transitioning() {
51
+ return this.running;
52
+ }
53
+
54
+ _createEventMask() {
55
+ const el = document.createElement('div');
56
+ el.style.cssText = 'position:fixed;inset:0;z-index:2147483647;pointer-events:none;';
57
+ const prevent = (e) => { e.preventDefault(); e.stopPropagation(); };
58
+ el.addEventListener('scroll', prevent);
59
+ el.addEventListener('wheel', prevent, { passive: false });
60
+ el.addEventListener('touchmove', prevent, { passive: false });
61
+ document.body.appendChild(el);
62
+ return el;
63
+ }
64
+
65
+ attach(links) {
66
+ for (const link of links) {
67
+ link.addEventListener('click', this._navigate);
68
+ }
69
+ }
70
+
71
+ detach(links) {
72
+ for (const link of links) {
73
+ link.removeEventListener('click', this._navigate);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Prefetch URLs and store results in the cache.
79
+ * Accepts an array of links (elements or URL strings).
80
+ */
81
+ prefetch(links) {
82
+ const urls = [];
83
+
84
+ for (const link of links) {
85
+ const href = typeof link === 'string' ? link : link.href;
86
+ if (href) urls.push(href);
87
+ }
88
+
89
+ for (const url of urls) {
90
+ this._prefetchURL(url);
91
+ }
92
+ }
93
+
94
+ _initPrefetch() {
95
+ // Prefetch critical links immediately
96
+ const critical = document.querySelectorAll('[data-prefetch="critical"]');
97
+ if (critical.length) this.prefetch(critical);
98
+
99
+ // Attach hover listeners to hover-prefetch links
100
+ const hover = document.querySelectorAll('[data-prefetch="hover"]');
101
+ for (const link of hover) {
102
+ link.addEventListener('mouseenter', this._prefetchOnHover, { once: true });
103
+ }
104
+ }
105
+
106
+ _prefetchOnHover(e) {
107
+ const href = e.currentTarget.href;
108
+ if (href) this._prefetchURL(href);
109
+ }
110
+
111
+ async _prefetchURL(url) {
112
+ if (this.cache.has(url) || this.prefetching.has(url)) return;
113
+
114
+ // Normalize against current origin
115
+ try {
116
+ const parsed = new URL(url);
117
+ if (parsed.origin !== window.location.origin) return;
118
+ } catch {
119
+ return;
120
+ }
121
+
122
+ this.prefetching.add(url);
123
+
124
+ try {
125
+ const response = await fetch(url, {
126
+ mode: 'same-origin',
127
+ method: 'GET',
128
+ headers: { 'X-Requested-With': 'PjaxMax' },
129
+ credentials: 'same-origin'
130
+ });
131
+
132
+ if (response.ok) {
133
+ const html = await response.text();
134
+ this.cache.set(url, this.Helpers.getProperties(html));
135
+ }
136
+ } catch {
137
+ // Prefetch is best-effort — silently ignore failures
138
+ } finally {
139
+ this.prefetching.delete(url);
140
+ }
141
+ }
142
+
143
+ navigate(e) {
144
+ if (!(e.metaKey || e.ctrlKey)) {
145
+ e.preventDefault();
146
+
147
+ const contextual = e.currentTarget.hasAttribute('data-transition')
148
+ ? e.currentTarget.dataset.transition
149
+ : false;
150
+
151
+ this.redirect(e.currentTarget.href, contextual, e.currentTarget);
152
+ }
153
+ }
154
+
155
+ redirect(href, contextual = false, trigger = 'script') {
156
+ this.trigger = trigger;
157
+
158
+ if (!this.running && href !== this.location.href) {
159
+ const location = this.Helpers.getLocation(href);
160
+
161
+ this.Contextual = false;
162
+
163
+ if (contextual) {
164
+ this.Contextual = this.Transitions[contextual].prototype;
165
+ this.Contextual.name = contextual;
166
+ }
167
+
168
+ if (location.origin !== this.location.origin || location.scheme !== this.location.scheme || location.anchor && location.pathname === this.location.pathname) {
169
+ window.location.href = href;
170
+ } else {
171
+ this.location = location;
172
+ this.beforeFetch();
173
+ }
174
+ }
175
+ }
176
+
177
+ popState() {
178
+ this.trigger = 'popstate';
179
+ this.Contextual = false;
180
+
181
+ const location = this.Helpers.getLocation(window.location.href);
182
+
183
+ if (this.location.pathname !== location.pathname || !this.location.anchor && !location.anchor) {
184
+ this.popping = true;
185
+ this.location = location;
186
+ this.beforeFetch();
187
+ } else {
188
+ this.location = location;
189
+ }
190
+ }
191
+
192
+ pushState() {
193
+ if (!this.popping) {
194
+ window.history.pushState(this.location, '', this.location.href);
195
+ }
196
+ }
197
+
198
+ async fetch() {
199
+ this.controller = new AbortController();
200
+
201
+ const response = await fetch(this.location.href, {
202
+ mode: 'same-origin',
203
+ method: 'GET',
204
+ headers: { 'X-Requested-With': 'PjaxMax' },
205
+ credentials: 'same-origin',
206
+ signal: this.controller.signal
207
+ });
208
+
209
+ if (response.ok) {
210
+ return response.text();
211
+ }
212
+
213
+ window.location.href = this.location.href;
214
+ }
215
+
216
+ async beforeFetch() {
217
+ this.pushState();
218
+ this.running = true;
219
+ this._eventMask.style.pointerEvents = 'auto';
220
+
221
+ // Abort any in-flight fetch from a previous navigation
222
+ this.controller?.abort();
223
+
224
+ for (const fn of this.onLeave) fn();
225
+
226
+ this.dispatchEvent(new CustomEvent('NAVIGATE_OUT', {
227
+ detail: {
228
+ from: {
229
+ page: this.From.properties.page,
230
+ view: this.From.properties.view
231
+ },
232
+ trigger: this.trigger,
233
+ location: this.location
234
+ }
235
+ }));
236
+
237
+ const data = {
238
+ trigger: this.trigger,
239
+ contextual: this.Contextual
240
+ };
241
+
242
+ if (this.cache.has(this.location.href)) {
243
+ await this.From.hide(data);
244
+ this.properties = this.cache.get(this.location.href);
245
+ } else {
246
+ const [html] = await Promise.all([
247
+ this.fetch(),
248
+ this.From.hide(data)
249
+ ]);
250
+
251
+ this.properties = this.Helpers.getProperties(html);
252
+ this.cache.set(this.location.href, this.properties);
253
+ }
254
+
255
+ this.afterFetch();
256
+ }
257
+
258
+ async afterFetch() {
259
+ const Renderer = await this.properties.renderer;
260
+
261
+ this.To = new Renderer(this.properties);
262
+ this.To.add();
263
+
264
+ if (this.manualScrollRestoration) window.scrollTo(0, 0);
265
+
266
+ this.dispatchEvent(new CustomEvent('NAVIGATE_IN', {
267
+ detail: {
268
+ to: {
269
+ page: this.To.properties.page,
270
+ view: this.To.wrap.lastElementChild
271
+ },
272
+ trigger: this.trigger,
273
+ location: this.location
274
+ }
275
+ }));
276
+
277
+ this.namespace = this.To.properties.slug;
278
+ for (const fn of this.onEnter) fn();
279
+
280
+ await this.To.show({
281
+ trigger: this.trigger,
282
+ contextual: this.Contextual
283
+ });
284
+
285
+ this.popping = false;
286
+ this.running = false;
287
+ this._eventMask.style.pointerEvents = 'none';
288
+
289
+ this.detach(this.links);
290
+ this.links = document.querySelectorAll('a:not([target]):not([data-router-disabled])');
291
+ this.attach(this.links);
292
+ this._initPrefetch();
293
+
294
+ this.dispatchEvent(new CustomEvent('NAVIGATE_END', {
295
+ detail: {
296
+ to: {
297
+ page: this.To.properties.page,
298
+ view: this.To.wrap.lastElementChild
299
+ },
300
+ from: {
301
+ page: this.From.properties.page,
302
+ view: this.From.properties.view
303
+ },
304
+ trigger: this.trigger,
305
+ location: this.location
306
+ }
307
+ }));
308
+
309
+ this.From = this.To;
310
+ this.trigger = null;
311
+ }
312
+ }
package/src/helpers.js ADDED
@@ -0,0 +1,89 @@
1
+ import Renderer from './renderer';
2
+
3
+ const PARSER = new DOMParser();
4
+
5
+ export default class Helpers {
6
+
7
+ constructor(renderers, transitions) {
8
+ this.renderers = renderers;
9
+ this.transitions = transitions;
10
+ }
11
+
12
+ getDOM(page) {
13
+ return typeof page === 'string' ? PARSER.parseFromString(page, 'text/html') : page;
14
+ }
15
+
16
+ getView(page) {
17
+ return page.querySelector('[data-router-view]');
18
+ }
19
+
20
+ getSlug(view) {
21
+ return view.getAttribute('data-router-view');
22
+ }
23
+
24
+ async getRenderer(slug) {
25
+ if (!this.renderers || !(slug in this.renderers)) {
26
+ return Renderer;
27
+ }
28
+
29
+ const renderer = this.renderers[slug];
30
+
31
+ // Dynamic import factory function
32
+ if (typeof renderer === 'function' && !Renderer.isPrototypeOf(renderer)) {
33
+ const mod = await renderer();
34
+ return mod.default;
35
+ }
36
+
37
+ // Already a promise (e.g., top-level import())
38
+ if (typeof renderer?.then === 'function') {
39
+ const mod = await renderer;
40
+ return mod.default;
41
+ }
42
+
43
+ return renderer;
44
+ }
45
+
46
+ getTransition(slug) {
47
+ if (!this.transitions) {
48
+ return null;
49
+ }
50
+
51
+ if (slug in this.transitions) {
52
+ return { class: this.transitions[slug], name: slug };
53
+ }
54
+
55
+ if ('default' in this.transitions) {
56
+ return { class: this.transitions['default'], name: 'default' };
57
+ }
58
+
59
+ return null;
60
+ }
61
+
62
+ getProperties(context) {
63
+ const page = this.getDOM(context);
64
+ const view = this.getView(page);
65
+ const slug = this.getSlug(view);
66
+ const renderer = this.getRenderer(slug);
67
+ const transition = this.getTransition(slug);
68
+
69
+ return {
70
+ page,
71
+ view,
72
+ slug,
73
+ renderer,
74
+ transition
75
+ };
76
+ }
77
+
78
+ getLocation(url) {
79
+ const u = new URL(url);
80
+ return {
81
+ href: u.href,
82
+ anchor: u.hash || null,
83
+ origin: u.hostname,
84
+ params: u.search ? Object.fromEntries(u.searchParams) : null,
85
+ pathname: u.pathname,
86
+ scheme: u.protocol.replace(':', '')
87
+ };
88
+ }
89
+ }
@@ -0,0 +1,6 @@
1
+ import Core from './core';
2
+ import Renderer from './renderer';
3
+ import Transition from './transition';
4
+
5
+ export { Core, Renderer, Transition };
6
+ export default { Core, Renderer, Transition };
@@ -0,0 +1,36 @@
1
+ export default class Renderer {
2
+
3
+ constructor(properties) {
4
+ this.wrap = document.querySelector('[data-router-wrapper]');
5
+ this.properties = properties;
6
+ this.Transition = properties.transition
7
+ ? new properties.transition.class(this.wrap, properties.transition.name)
8
+ : null;
9
+ }
10
+
11
+ setup() {
12
+ this.onEnter?.();
13
+ this.onEnterCompleted?.();
14
+ }
15
+
16
+ add() {
17
+ this.wrap.insertAdjacentHTML('beforeend', this.properties.view.outerHTML);
18
+ }
19
+
20
+ update() {
21
+ document.title = this.properties.page.title;
22
+ }
23
+
24
+ async show(data) {
25
+ this.update();
26
+ this.onEnter?.();
27
+ await this.Transition?.show(data);
28
+ this.onEnterCompleted?.();
29
+ }
30
+
31
+ async hide(data) {
32
+ this.onLeave?.();
33
+ await this.Transition?.hide(data);
34
+ this.onLeaveCompleted?.();
35
+ }
36
+ }
@@ -0,0 +1,40 @@
1
+ export default class Transition {
2
+
3
+ constructor(wrap, name) {
4
+ this.wrap = wrap;
5
+ this.name = name;
6
+ }
7
+
8
+ show({ trigger, contextual }) {
9
+ const to = this.wrap.lastElementChild;
10
+ const from = this.wrap.firstElementChild;
11
+
12
+ return new Promise(resolve => {
13
+ if (!contextual) {
14
+ to.setAttribute('data-transition-in', this.name);
15
+ to.removeAttribute('data-transition-out');
16
+ this.in?.({ to, from, trigger, done: resolve });
17
+ } else {
18
+ to.setAttribute('data-transition-in', contextual.name);
19
+ to.removeAttribute('data-transition-out');
20
+ contextual.in?.({ to, from, trigger, done: resolve });
21
+ }
22
+ });
23
+ }
24
+
25
+ hide({ trigger, contextual }) {
26
+ const from = this.wrap.firstElementChild;
27
+
28
+ return new Promise(resolve => {
29
+ if (!contextual) {
30
+ from.setAttribute('data-transition-out', this.name);
31
+ from.removeAttribute('data-transition-in');
32
+ this.out?.({ from, trigger, done: resolve });
33
+ } else {
34
+ from.setAttribute('data-transition-out', contextual.name);
35
+ from.removeAttribute('data-transition-in');
36
+ contextual.out?.({ from, trigger, done: resolve });
37
+ }
38
+ });
39
+ }
40
+ }