lightweight-router 1.0.5 → 1.0.7

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.
@@ -1 +1 @@
1
- (()=>{let c={},n=async()=>{var e=document.querySelector("router"),t=globalThis.location.pathname;let o=e.querySelector(`route[path="${t}"]`),n=(o||((o=document.createElement("route")).setAttribute("path",globalThis.location.pathname),e.appendChild(o)),document.body.classList.add("loading"),c[globalThis.location.href]);n||(n=await d(globalThis.location.href),c[globalThis.location.href]=n);var r,a=(new DOMParser).parseFromString(n,"text/html"),i=a.querySelector("title"),i=(i&&(document.title=i.textContent),o.innerHTML=a.body.innerHTML,Array.from(o.querySelectorAll("script")));for(r of i){var l=document.createElement("script");r.src?l.src=r.src:l.textContent=r.textContent,r.parentNode.replaceChild(l,r)}e.querySelectorAll("route").forEach(e=>e.style.display="none"),o.style.display="contents",document.body.classList.remove("loading"),window.scrollTo(0,0),s&&s(t)},r=async e=>{c[e.href]||(c[e.href]=await d(e.href))},a=(e,t)=>{e.forEach(e=>{e.isIntersecting&&(e=e.target,c[e.href]||(r(e),t.unobserve(e)))})},i=e=>{var t=e.target.closest("A");t&&t.href&&l(t.href)&&t.origin===location.origin&&(e.preventDefault(),globalThis.history.pushState(null,null,t.href),globalThis.dispatchEvent(new Event("popstate")))};function l(e){if(e&&!e.startsWith("#")&&!e.startsWith("javascript:")){if(e.startsWith("/"))return 1;try{var t=new URL(e,window.location.origin),o=new URL(window.location.href);return o.hostname.replace(/^www\./,"")===t.hostname.replace(/^www\./,"")?o.pathname!==t.pathname||!t.hash:void 0}catch{}}}let s,d=async e=>{e=await t(e);return e.ok?e.text():"Couldn't fetch the route - HTTP error! status: "+e.status},t=async e=>{if(!h){var t=await fetch(e,{method:"POST",body:"onlyRoute"});if(t.ok)return t}return fetch(e)},h=!1,e=(e={})=>{var t,e=e.onRouteChange,e=(e&&(e=e,s=e),document.createElement("style")),e=(e.textContent=".loading{animation:pulse 1s infinite alternate}@keyframes pulse{from{opacity:.8}to{opacity:.3}}route{content-visibility:auto}",document.head.appendChild(e),document.querySelector("router")),o=globalThis.location.pathname,o=(e||(e=document.createElement("router"),(t=document.createElement("route")).setAttribute("path",o),t.innerHTML=document.body.innerHTML,e.appendChild(t),document.body.innerHTML="",document.body.appendChild(e),h=!0),globalThis.addEventListener("popstate",n),document.addEventListener("click",i),document.body.addEventListener("mouseover",e=>{"A"===e.target.tagName&&"onHover"===e.target.getAttribute("prefetch")&&(async e=>{e=e.target;!c[e.href]&&l(e.href)&&await r(e)})(e)}),new IntersectionObserver(a,{root:null,threshold:.5}));(t=>{let o=navigator.connection&&navigator.connection.saveData;document.querySelectorAll("a").forEach(e=>{"onHover"===e.getAttribute("prefetch")||o||l(e.href)||t.observe(e)})})(o)};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>e()):e()})();
1
+ (()=>{let l={},n=async()=>{document.body.classList.add("loading");var e=globalThis.location.pathname,t=document.querySelector("router");let o=t.querySelector(`route[path="${e}"]`);if(o||((o=document.createElement("route")).setAttribute("path",e),t.appendChild(o)),!o.innerHTML){let e=l[globalThis.location.href];e||(e=await d(globalThis.location.href),l[globalThis.location.href]=e);var n,r=(new DOMParser).parseFromString(e,"text/html"),a=r.querySelector("title"),a=(a&&(document.title=a.textContent),o.innerHTML=r.body.innerHTML,Array.from(o.querySelectorAll("script")));for(n of a){var i=document.createElement("script");n.src?i.src=n.src:i.textContent=n.textContent,n.parentNode.replaceChild(i,n)}}t.querySelectorAll("route").forEach(e=>e.style.display="none"),o.style.display="contents",document.body.classList.remove("loading"),window.scrollTo(0,0),s&&s(e)},r=async e=>{l[e.href]||(l[e.href]=await d(e.href))},a=(e,t)=>{e.forEach(e=>{e.isIntersecting&&(e=e.target,l[e.href]||(r(e),t.unobserve(e)))})},i=e=>{var t=e.target.closest("A");t&&t.href&&c(t.href)&&t.origin===location.origin&&(e.preventDefault(),globalThis.history.pushState(null,null,t.href),globalThis.dispatchEvent(new Event("popstate")))};function c(e){if(e&&!e.startsWith("#")&&!e.startsWith("javascript:")){if(e.startsWith("/"))return 1;try{var t=new URL(e,window.location.origin),o=new URL(window.location.href);return o.hostname.replace(/^www\./,"")===t.hostname.replace(/^www\./,"")?o.pathname!==t.pathname||!t.hash:void 0}catch{}}}let s,d=async e=>{e=await t(e);return e.ok?e.text():"Couldn't fetch the route - HTTP error! status: "+e.status},t=async e=>{if(!h){var t=await fetch(e,{method:"POST",body:"onlyRoute"});if(t.ok)return t}return fetch(e)},h=!1,e=(e={})=>{var t,e=e.onRouteChange,e=(e&&(e=e,s=e),document.createElement("style")),e=(e.textContent=".loading{animation:pulse 1s infinite alternate}@keyframes pulse{from{opacity:.6}to{opacity:.1}}route{content-visibility:auto}",document.head.appendChild(e),document.querySelector("router")),o=globalThis.location.pathname,o=(e||(e=document.createElement("router"),(t=document.createElement("route")).setAttribute("path",o),t.innerHTML=document.body.innerHTML,e.appendChild(t),document.body.innerHTML="",document.body.appendChild(e),h=!0),globalThis.addEventListener("popstate",n),document.addEventListener("click",i),document.body.addEventListener("mouseover",e=>{"A"===e.target.tagName&&"onHover"===e.target.getAttribute("prefetch")&&(async e=>{e=e.target;!l[e.href]&&c(e.href)&&await r(e)})(e)}),new IntersectionObserver(a,{root:null,threshold:.5}));(t=>{let o=navigator.connection&&navigator.connection.saveData;document.querySelectorAll("a").forEach(e=>{"onHover"===e.getAttribute("prefetch")||o||c(e.href)||t.observe(e)})})(o)};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>e()):e()})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightweight-router",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "main": "src/router.js",
5
5
  "scripts": {
6
6
  "dev": "npx http-server src -o",
package/src/router.js CHANGED
@@ -1,50 +1,55 @@
1
1
  let linkData = {};
2
2
  const handlePopState = async () => {
3
- const router = document.querySelector("router");
3
+ document.body.classList.add("loading");
4
4
  const currentPath = globalThis.location.pathname;
5
+ const router = document.querySelector("router");
5
6
 
6
7
  let currentRoute = router.querySelector(`route[path="${currentPath}"]`);
7
8
 
9
+ // If the route doesn't exist in DOM, create and append it
8
10
  if (!currentRoute) {
9
11
  currentRoute = document.createElement("route");
10
- currentRoute.setAttribute("path", globalThis.location.pathname);
12
+ currentRoute.setAttribute("path", currentPath);
11
13
  router.appendChild(currentRoute);
12
14
  }
13
15
 
14
- document.body.classList.add("loading");
16
+ // Only fetch and render content if the route is empty
17
+ if (!currentRoute.innerHTML) {
18
+ let content = linkData[globalThis.location.href];
15
19
 
16
- let content = linkData[globalThis.location.href];
17
- if (!content) {
18
- content = await fetchContent(globalThis.location.href);
19
- linkData[globalThis.location.href] = content;
20
- }
20
+ // Fetch content if it's not already cached
21
+ if (!content) {
22
+ content = await fetchContent(globalThis.location.href);
23
+ linkData[globalThis.location.href] = content;
24
+ }
21
25
 
22
- const parser = new DOMParser();
23
- const doc = parser.parseFromString(content, "text/html");
26
+ const parser = new DOMParser();
27
+ const doc = parser.parseFromString(content, "text/html");
24
28
 
25
- // Update the page title with the new content's title
26
- const newTitle = doc.querySelector("title");
27
- if (newTitle) document.title = newTitle.textContent;
29
+ // Update the page title with the new content's title
30
+ const newTitle = doc.querySelector("title");
31
+ if (newTitle) document.title = newTitle.textContent;
28
32
 
29
- currentRoute.innerHTML = doc.body.innerHTML;
33
+ currentRoute.innerHTML = doc.body.innerHTML;
30
34
 
31
- // Execute scripts from the fetched content
32
- const scripts = Array.from(currentRoute.querySelectorAll("script"));
33
- for (const oldScript of scripts) {
34
- const newScript = document.createElement("script");
35
- if (oldScript.src) {
36
- newScript.src = oldScript.src;
37
- } else {
38
- newScript.textContent = oldScript.textContent;
35
+ // Execute scripts from the fetched content
36
+ const scripts = Array.from(currentRoute.querySelectorAll("script"));
37
+ for (const oldScript of scripts) {
38
+ const newScript = document.createElement("script");
39
+ if (oldScript.src) {
40
+ newScript.src = oldScript.src;
41
+ } else {
42
+ newScript.textContent = oldScript.textContent;
43
+ }
44
+ oldScript.parentNode.replaceChild(newScript, oldScript);
39
45
  }
40
- oldScript.parentNode.replaceChild(newScript, oldScript);
41
46
  }
42
47
 
48
+ // Display only the current route
43
49
  router.querySelectorAll("route").forEach(route => (route.style.display = "none"));
44
50
  currentRoute.style.display = "contents";
45
51
 
46
52
  document.body.classList.remove("loading");
47
- // Reset scroll position to the top
48
53
  window.scrollTo(0, 0);
49
54
 
50
55
  // Call the route change handler if it's set
@@ -152,8 +157,8 @@ const startRouter = (options = {}) => {
152
157
  animation: pulse 1s infinite alternate;
153
158
  }
154
159
  @keyframes pulse {
155
- from { opacity: 0.8; }
156
- to { opacity: 0.3; }
160
+ from { opacity: 0.6; }
161
+ to { opacity: 0.1; }
157
162
  }
158
163
  route {
159
164
  content-visibility: auto;
@@ -189,3 +194,11 @@ const startRouter = (options = {}) => {
189
194
  };
190
195
 
191
196
  export { startRouter };
197
+
198
+ // TODO: create ultra minified version or deploy
199
+ // TODO: write proper automated tests
200
+ // - add support for prefetching on hover
201
+ // - add support for prefetching on click
202
+ // - add support for prefetching on scroll
203
+ // - add support for prefetching on focus
204
+ // - add support for prefetching on touch