stx-router 0.2.531 → 0.2.533
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/dist/client.js +61 -10
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -188,7 +188,13 @@ function routerSource() {
|
|
|
188
188
|
releaseScrollHold();
|
|
189
189
|
var reached=function(){return Math.abs((window.pageYOffset||window.scrollY||0)-y)<2};
|
|
190
190
|
if(typeof ResizeObserver!=='function')return;
|
|
191
|
-
var h={stop:releaseScrollHold,ro:null,timer:0,onScroll:null,anchor:null,win:window,root:document.documentElement};
|
|
191
|
+
var h={stop:releaseScrollHold,ro:null,timer:0,onScroll:null,anchor:null,win:window,root:document.documentElement,seen:{}};
|
|
192
|
+
// Every place the page was at while the hold kept it: where the hold put
|
|
193
|
+
// it, and where the page's own height clamped it as it grew and shrank.
|
|
194
|
+
// Their scroll events arrive a frame later, when the height they were
|
|
195
|
+
// clamped to may be gone, so they are recognised by place, not by height.
|
|
196
|
+
var note=function(){h.seen[Math.round(window.pageYOffset||window.scrollY||0)]=true};
|
|
197
|
+
note();
|
|
192
198
|
// Scroll anchoring off while held: content filling in above the place
|
|
193
199
|
// would otherwise carry the window down with it (Chrome keeps what is
|
|
194
200
|
// on screen in view), and Back landed thousands of pixels past it.
|
|
@@ -198,8 +204,11 @@ function routerSource() {
|
|
|
198
204
|
// Held for the whole window even once reached: a page that is tall
|
|
199
205
|
// enough when it is put back is often still drawing, above the place
|
|
200
206
|
// as much as below it.
|
|
201
|
-
if(scrollHold!==h
|
|
207
|
+
if(scrollHold!==h)return;
|
|
208
|
+
note();
|
|
209
|
+
if(reached())return;
|
|
202
210
|
window.scrollTo({left:x,top:y,behavior:'instant'});
|
|
211
|
+
note();
|
|
203
212
|
});
|
|
204
213
|
h.ro.observe(document.body||document.documentElement);
|
|
205
214
|
h.timer=setTimeout(releaseScrollHold,4000);
|
|
@@ -210,7 +219,7 @@ function routerSource() {
|
|
|
210
219
|
h.onScroll=function(){
|
|
211
220
|
var at=window.pageYOffset||window.scrollY||0;
|
|
212
221
|
var end=Math.max(0,(document.documentElement.scrollHeight||0)-(window.innerHeight||0));
|
|
213
|
-
if(Math.abs(at-y)<2||(at<y&&at>=end-1))return;
|
|
222
|
+
if(Math.abs(at-y)<2||(at<y&&at>=end-1)||h.seen[Math.round(at)])return;
|
|
214
223
|
releaseScrollHold();
|
|
215
224
|
};
|
|
216
225
|
window.addEventListener('scroll',h.onScroll,{passive:true});
|
|
@@ -799,6 +808,35 @@ function routerSource() {
|
|
|
799
808
|
var scopeId=root.getAttribute('data-stx-scope');
|
|
800
809
|
return !!scopeId&&code.indexOf(scopeId)!==-1;
|
|
801
810
|
}
|
|
811
|
+
// A script inside an :if (or :else) template runs when the branch is
|
|
812
|
+
// drawn: on a whole page the browser runs it as the runtime clones the
|
|
813
|
+
// template into the page, which is how a component used under :if gets its
|
|
814
|
+
// scope. A fragment's scripts are all turned into inert placeholders, and
|
|
815
|
+
// one inside template.content is out of the router's reach, so it never ran
|
|
816
|
+
// at all: the component's markup appeared with no setup behind it (an
|
|
817
|
+
// <OuraRecovery :if> on a page reached by a link stayed empty). Such a
|
|
818
|
+
// placeholder is made a live script again, so the branch runs it exactly as
|
|
819
|
+
// a whole page would. Loop templates keep theirs inert: bindFor runs those per
|
|
820
|
+
// row. Returns the slots it handed over, for runFragScripts to leave alone.
|
|
821
|
+
function armBranchScripts(root,slots){
|
|
822
|
+
if(!root||!root.querySelectorAll)return slots;
|
|
823
|
+
root.querySelectorAll('template').forEach(function(template){
|
|
824
|
+
if(hasLoopAttr(template)||!template.content)return;
|
|
825
|
+
template.content.querySelectorAll('script[type="application/stx-pending"][data-stx-route-script]').forEach(function(script){
|
|
826
|
+
if(script.hasAttribute('data-stx-scoped')&&loopOwnsScopedScript(script,script.textContent||''))return;
|
|
827
|
+
slots[script.getAttribute('data-stx-route-script')]=true;
|
|
828
|
+
// A new element, not the placeholder untyped: a script parsed by
|
|
829
|
+
// innerHTML is marked as already run, and so is every clone of it.
|
|
830
|
+
var live=script.ownerDocument.createElement('script');
|
|
831
|
+
var names=script.getAttributeNames?script.getAttributeNames():Array.prototype.map.call(script.attributes,function(a){return a.name});
|
|
832
|
+
names.forEach(function(name){if(name!=='type')live.setAttribute(name,script.getAttribute(name))});
|
|
833
|
+
live.textContent=script.textContent;
|
|
834
|
+
script.parentNode.replaceChild(live,script);
|
|
835
|
+
});
|
|
836
|
+
armBranchScripts(template.content,slots);
|
|
837
|
+
});
|
|
838
|
+
return slots;
|
|
839
|
+
}
|
|
802
840
|
// A component scope script names the root it binds in data-stx-owner (#1958).
|
|
803
841
|
// On navigation it runs only for a root that just ARRIVED: one swapped in
|
|
804
842
|
// with the new content, which is not bound yet. A layout component that
|
|
@@ -1236,6 +1274,7 @@ function routerSource() {
|
|
|
1236
1274
|
isNavigating=false;navAbort=null;
|
|
1237
1275
|
document.body.classList.remove(o.loadingClass);finishProgress();
|
|
1238
1276
|
settleDirection();
|
|
1277
|
+
if(openingUrl&&location.pathname+location.search!==openingUrl)keepOpeningPage();
|
|
1239
1278
|
}
|
|
1240
1279
|
function stale(){return id!==navSeq}
|
|
1241
1280
|
// A failed fetch keeps the app: the destination shows a way to try again
|
|
@@ -1714,6 +1753,7 @@ else {
|
|
|
1714
1753
|
return '<scr'+'ipt type="application/stx-pending" data-stx-route-script="'+slot+'"'
|
|
1715
1754
|
+(scoped?' data-stx-scoped':'')
|
|
1716
1755
|
+(runDecl?' data-stx-run="'+runDecl.toLowerCase()+'"':'')
|
|
1756
|
+
+(ownerDecl?' data-stx-owner="'+ownerDecl+'"':'')
|
|
1717
1757
|
+'>'+code+'<\\/scr'+'ipt>';
|
|
1718
1758
|
}
|
|
1719
1759
|
return '';
|
|
@@ -1774,6 +1814,7 @@ else {
|
|
|
1774
1814
|
if(scr)scr.cattrs=pendingContainerAttrs;
|
|
1775
1815
|
applyContainerAttrs(host,pendingContainerAttrs);
|
|
1776
1816
|
currentContent.innerHTML=cleanFrag;
|
|
1817
|
+
var branchSlots=armBranchScripts(currentContent,{});
|
|
1777
1818
|
// Remove old page scripts
|
|
1778
1819
|
qsa('script[data-stx-page]').forEach(function(s){s.remove()});
|
|
1779
1820
|
if(pushState!==false)writeHistory(pushState,url+(hash||''));
|
|
@@ -1800,6 +1841,10 @@ else {
|
|
|
1800
1841
|
fragScripts.forEach(function(entry){
|
|
1801
1842
|
var code=entry.text;
|
|
1802
1843
|
log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);
|
|
1844
|
+
if(branchSlots&&branchSlots[entry.slot]){
|
|
1845
|
+
log('[router] leaving a branch script to its :if');
|
|
1846
|
+
return;
|
|
1847
|
+
}
|
|
1803
1848
|
var placeholder=qs('script[data-stx-route-script="'+entry.slot+'"]');
|
|
1804
1849
|
var nestedScopedTemplate=false;
|
|
1805
1850
|
var scopedLoopSetup=false;
|
|
@@ -3584,14 +3629,22 @@ else {
|
|
|
3584
3629
|
// The page the app was opened on came as a whole document, so it is the
|
|
3585
3630
|
// one page the cache never holds: Back to it went to the server and left
|
|
3586
3631
|
// the page being read on screen under the new address until it answered
|
|
3587
|
-
// (a second or more on a distant server). Once the
|
|
3588
|
-
// fragment is fetched, the same request a link
|
|
3589
|
-
// is a swap like every other.
|
|
3632
|
+
// (a second or more on a distant server). Once the router has taken the
|
|
3633
|
+
// person somewhere else its fragment is fetched, the same request a link
|
|
3634
|
+
// to it would make, so Back is a swap like every other.
|
|
3635
|
+
//
|
|
3636
|
+
// Not before: the fragment is answered no-store, and a document that has
|
|
3637
|
+
// fetched a no-store response is kept out of the back/forward cache. Done
|
|
3638
|
+
// on load, it cost the opening page its instant restore when the person
|
|
3639
|
+
// left the app and came back (a full-document Back). A router navigation
|
|
3640
|
+
// has already fetched one, so after it nothing more is lost.
|
|
3641
|
+
var openingUrl=location.pathname+location.search;
|
|
3590
3642
|
function keepOpeningPage(){
|
|
3591
|
-
|
|
3643
|
+
var url=openingUrl;
|
|
3644
|
+
openingUrl='';
|
|
3645
|
+
if(!url||!o.prefetch||o.keepOpening===false||!o.cache||!shouldUseFragmentResponse())return;
|
|
3592
3646
|
var c=navigator.connection;
|
|
3593
3647
|
if(c&&(c.saveData||/2g/.test(c.effectiveType||'')))return;
|
|
3594
|
-
var url=location.pathname+location.search;
|
|
3595
3648
|
var key=cacheKey(url);
|
|
3596
3649
|
// Late by design, so it can find the page gone (a test's document torn
|
|
3597
3650
|
// down, a full navigation begun): never let that throw from a timer.
|
|
@@ -3887,8 +3940,6 @@ else {
|
|
|
3887
3940
|
restoreKeptPages();
|
|
3888
3941
|
prefetchEager();
|
|
3889
3942
|
observeLinks();
|
|
3890
|
-
// The runtime's first stx:load has usually gone by when this runs.
|
|
3891
|
-
if(o.prefetch&&o.keepOpening!==false)keepOpeningPage();
|
|
3892
3943
|
}
|
|
3893
3944
|
|
|
3894
3945
|
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stx-router",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.533",
|
|
5
5
|
"description": "File-based router for STX — .stx template discovery, nested layouts, typed route params, middleware, and client-side SPA navigation.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
7
7
|
"license": "MIT",
|