@zensen/router
Version:
A declarative router for native web components
1 lines • 4.33 kB
JavaScript
export const EVENT_ROUTE_CHANGE="routechange";export const EVENT_ROUTE_CANCEL="routecancel";export const EVENT_ROUTE_SHOULD_CHANGE="routeshouldchange";const OPERATION={POP:"popState",PUSH:"pushState",REPLACE:"replaceState"};let __initialized=!1,__currentHref="";function __regexparam(e,t){var n,i,o,a,r=[],c="",l=e.split("/");for(l[0]||l.shift();o=l.shift();)"*"===(n=o[0])?(r.push("wild"),c+="/(.*)"):":"===n?(i=o.indexOf("?",1),a=o.indexOf(".",1),r.push(o.substring(1,~i?i:~a?a:o.length)),c+=~i&&!~a?"(?:/([^/]+?))?":"/([^/]+?)",~a&&(c+=(~i?"?":"")+"\\"+o.substring(a))):c+="/"+o;return{keys:r,pattern:new RegExp("^"+c+(t?"(?:$|/)":"/?$"),"i")}}function __objToQuerystring(e){const t=e&&new URLSearchParams(e);return t?t.toString():""}function __isRouteDifferent(e,t){const n=__objToQuerystring(t);return e!==window.location.pathname||n!==window.location.search.substring(1)}function __validateInitialized(){if(!__initialized)throw new Error("Router is not initialized")}function __buildParams(e,t,n){const i=e.exec(n);return t.reduce((e,t,n)=>({...e,[t]:i&&i[n+1]||""}),{})}function __resolveRoute(e,t,n,i,o){const a=new RegExp(e),r=n.replace(a,""),c={routeTail:0!==r.indexOf("/")?`/${r}`:"/",navItem:i,params:__buildParams(e,t,n),query:getQuery(),data:o};if(i.redirect){return navigate(i.redirect(c)),""}return i.resolver(c)}function __updateCurrentHref(){__currentHref=[window.location.pathname,window.location.search].filter(Boolean).join("")}function __changeRoute(e,t,n){const{pathname:i}=new URL(e),o=__objToQuerystring(t),a=[i,o].filter(Boolean).join("?");if(window.dispatchEvent(new CustomEvent("routeshouldchange",{cancelable:!0,detail:{full:a,pathname:i,operation:n,query:t}}))){const e=[i,o].filter(Boolean).join("?");n&&n!==OPERATION.POP&&(window.history[n]({},"",e),__updateCurrentHref()),window.dispatchEvent(new CustomEvent("routechange",{detail:{full:a,pathname:i,operation:n,query:t}}))}else n&&n===OPERATION.POP&&window.history.pushState({},"",__currentHref),window.dispatchEvent(new CustomEvent("routecancel",{cancelable:!0,detail:{full:a,pathname:i,operation:n,query:t}}))}function getPathname(){return window.location.pathname}function getHash(){return window.location.hash}function getQuery(){const e=new URLSearchParams(window.location.search);return Object.fromEntries(e)}function getSegments(){return window.location.pathname.split("/").filter(Boolean)}function navigate(e,t={}){if(__validateInitialized(),!__isRouteDifferent(e,t))return;const{origin:n,hash:i}=window.location;__changeRoute(`${n}${e.split("?")[0]}${i}`,t,OPERATION.PUSH)}function redirect(e,t={}){if(__validateInitialized(),!__isRouteDifferent(e,t))return;const{origin:n,hash:i}=window.location;__changeRoute(`${n}${e.split("?")[0]}${i}`,t,OPERATION.REPLACE)}function matchSwitch(e,t,n={}){__validateInitialized();let i={};const o=t.split("?")[0],a=e.find(e=>{const t=void 0!==e.exact&&e.exact;return i=__regexparam(e.path,!t),i.pattern.test(o)}),{pattern:r,keys:c}=i;return a?__resolveRoute(r,c,o,a,n):""}export function handleAnchorClick(e){if(e.metaKey||e.ctrlKey||e.shiftKey||e.defaultPrevented)return;const t=e.composedPath().find(e=>"A"===e.tagName);if(!t||t.target||t.hasAttribute("download")||"external"===t.getAttribute("rel"))return;if(!t.href||-1!==t.href.indexOf("mailto:"))return;const n=window.location.origin||window.location.protocol+"//"+window.location.host;if(0===t.href.indexOf(n)&&(e.preventDefault(),t.href!==window.location.href)){const e=new URLSearchParams(t.search),n=Object.fromEntries(e);__changeRoute(t.href,n,OPERATION.PUSH)}}export function handlePopState(){__changeRoute(window.location.href,null,OPERATION.POP)}function isInitialized(){return __initialized}function initialize(){if(__initialized)throw new Error("Router is already initialized");window.addEventListener("click",handleAnchorClick),window.addEventListener("popstate",handlePopState),__initialized=!0,__changeRoute(window.location.href,null,null)}function shutdown(){__validateInitialized(),window.removeEventListener("click",handleAnchorClick),window.removeEventListener("popstate",handlePopState),__initialized=!1}export default{isInitialized:isInitialized,initialize:initialize,shutdown:shutdown,getPathname:getPathname,getHash:getHash,getQuery:getQuery,getSegments:getSegments,navigate:navigate,redirect:redirect,matchSwitch:matchSwitch};