@haxtheweb/haxcms-nodejs
Version:
HAXcms single and multisite nodejs server, api, and administration
1 lines • 11.1 kB
JavaScript
import e from"./resolver/resolver.js";import t from"./resolver/generateUrls.js";import r from"./triggers/setNavigationTriggers.js";import n from"./transitions/animate.js";import{ensureRoute as i,fireRouterEvent as o,loadBundle as a,log as s,logValue as h,toArray as l,isFunction as _,isString as c,isObject as u,getNotFoundError as d,notFoundResult as p}from"./utils.js";function isResultNotEmpty(e){return null!=e}function createLocation({pathname:e="",search:t="",hash:r="",chain:n=[],params:i={},redirectFrom:o,resolver:a},s){const h=n.map((e=>e.route));return{baseUrl:a&&a.baseUrl||"",pathname:e,search:t,hash:r,routes:h,route:s||h.length&&h[h.length-1]||null,params:i,redirectFrom:o,getUrl:(e={})=>getPathnameForRouter(Router.pathToRegexp.compile(getMatchedPath(h))(Object.assign({},i,e)),a)}}function createRedirect(e,t){const r=Object.assign({},e.params);return{redirect:{pathname:t,from:e.pathname,params:r}}}function runCallbackIfPossible(e,t,r){if(_(e))return e.apply(r,t)}function amend(e,t,r){return n=>n&&(n.cancel||n.redirect)?n:r?runCallbackIfPossible(r[e],t,r):void 0}function removeDomNodes(e){if(e&&e.length){const t=e[0].parentNode;for(let r=0;r<e.length;r++)t.removeChild(e[r])}}function getPathnameForRouter(e,t){const r=t.__effectiveBaseUrl;return r?t.constructor.__createUrl(e.replace(/^\//,""),r).pathname:e}function getMatchedPath(e){return e.map((e=>e.path)).reduce(((e,t)=>t.length?e.replace(/\/$/,"")+"/"+t.replace(/^\//,""):e),"")}export class Router extends e{constructor(t,r){const n=document.head.querySelector("base"),i=n&&n.getAttribute("href");super([],Object.assign({baseUrl:i&&e.__createUrl(i,document.URL).pathname.replace(/[^\/]*$/,"")},r)),this.resolveRoute=e=>this.__resolveRoute(e);const o=Router.NavigationTrigger;Router.setTriggers.apply(Router,Object.keys(o).map((e=>o[e]))),this.baseUrl,this.ready,this.ready=Promise.resolve(t),this.location,this.location=createLocation({resolver:this}),this.__lastStartedRenderId=0,this.__navigationEventHandler=this.__onNavigationEvent.bind(this),this.setOutlet(t),this.subscribe(),this.__createdByRouter=new WeakMap,this.__addedByRouter=new WeakMap}__resolveRoute(e){const t=e.route;let r=Promise.resolve();_(t.children)&&(r=r.then((()=>t.children(function copyContextWithoutNext(e){const t=Object.assign({},e);return delete t.next,t}(e)))).then((e=>{isResultNotEmpty(e)||_(t.children)||(e=t.children),function processNewChildren(e,t){if(!Array.isArray(e)&&!u(e))throw new Error(s(`Incorrect "children" value for the route ${t.path}: expected array or object, but got ${e}`));t.__children=[];const r=l(e);for(let e=0;e<r.length;e++)i(r[e]),t.__children.push(r[e])}(e,t)})));const n={redirect:t=>createRedirect(e,t),component:e=>{const t=document.createElement(e);return this.__createdByRouter.set(t,!0),t}};return r.then((()=>{if(this.__isLatestRender(e))return runCallbackIfPossible(t.action,[e,n],t)})).then((e=>isResultNotEmpty(e)&&(e instanceof HTMLElement||e.redirect||e===p)?e:c(t.redirect)?n.redirect(t.redirect):t.bundle?a(t.bundle).then((()=>{}),(()=>{throw new Error(s(`Bundle not found: ${t.bundle}. Check if the file name is correct`))})):void 0)).then((e=>isResultNotEmpty(e)?e:c(t.component)?n.component(t.component):void 0))}setOutlet(e){e&&this.__ensureOutlet(e),this.__outlet=e}getOutlet(){return this.__outlet}setRoutes(e,t=!1){return this.__previousContext=void 0,this.__urlForName=void 0,super.setRoutes(e),t||this.__onNavigationEvent(),this.ready}render(e,t){const r=++this.__lastStartedRenderId,n=Object.assign({search:"",hash:""},c(e)?{pathname:e}:e,{__renderId:r});return this.ready=this.resolve(n).then((e=>this.__fullyResolveChain(e))).then((e=>{if(this.__isLatestRender(e)){const n=this.__previousContext;if(e===n)return this.__updateBrowserHistory(n,!0),this.location;if(this.location=createLocation(e),t&&this.__updateBrowserHistory(e,1===r),o("location-changed",{router:this,location:this.location}),e.__skipAttach)return this.__copyUnchangedElements(e,n),this.__previousContext=e,this.location;this.__addAppearingContent(e,n);const i=this.__animateIfNeeded(e);return this.__runOnAfterEnterCallbacks(e),this.__runOnAfterLeaveCallbacks(e,n),i.then((()=>{if(this.__isLatestRender(e))return this.__removeDisappearingContent(),this.__previousContext=e,this.location}))}})).catch((e=>{if(r===this.__lastStartedRenderId)throw t&&this.__updateBrowserHistory(n),removeDomNodes(this.__outlet&&this.__outlet.children),this.location=createLocation(Object.assign(n,{resolver:this})),o("error",Object.assign({router:this,error:e},n)),e})),this.ready}__fullyResolveChain(e,t=e){return this.__findComponentContextAfterAllRedirects(t).then((r=>{const n=r!==t?r:e,i=getPathnameForRouter(getMatchedPath(r.chain),r.resolver)===r.pathname,findNextContextIfAny=(e,t=e.route,r)=>e.next(void 0,t,r).then((r=>null===r||r===p?i?e:null!==t.parent?findNextContextIfAny(e,t.parent,r):r:r));return findNextContextIfAny(r).then((e=>{if(null===e||e===p)throw d(n);return e&&e!==p&&e!==r?this.__fullyResolveChain(n,e):this.__amendWithOnBeforeCallbacks(r)}))}))}__findComponentContextAfterAllRedirects(e){const t=e.result;return t instanceof HTMLElement?(function renderElement(e,t){t.location=createLocation(e);const r=e.chain.map((e=>e.route)).indexOf(e.route);return e.chain[r].element=t,t}(e,t),Promise.resolve(e)):t.redirect?this.__redirect(t.redirect,e.__redirectCount,e.__renderId).then((e=>this.__findComponentContextAfterAllRedirects(e))):t instanceof Error?Promise.reject(t):Promise.reject(new Error(s(`Invalid route resolution result for path "${e.pathname}". Expected redirect object or HTML element, but got: "${h(t)}". Double check the action return value for the route.`)))}__amendWithOnBeforeCallbacks(e){return this.__runOnBeforeCallbacks(e).then((t=>t===this.__previousContext||t===e?t:this.__fullyResolveChain(t)))}__runOnBeforeCallbacks(e){const t=this.__previousContext||{},r=t.chain||[],n=e.chain;let i=Promise.resolve();const prevent=()=>({cancel:!0}),redirect=t=>createRedirect(e,t);if(e.__divergedChainIndex=0,e.__skipAttach=!1,r.length){for(let t=0;t<Math.min(r.length,n.length)&&(r[t].route===n[t].route&&(r[t].path===n[t].path||r[t].element===n[t].element)&&this.__isReusableElement(r[t].element,n[t].element));t=++e.__divergedChainIndex);if(e.__skipAttach=n.length===r.length&&e.__divergedChainIndex==n.length&&this.__isReusableElement(e.result,t.result),e.__skipAttach){for(let t=n.length-1;t>=0;t--)i=this.__runOnBeforeLeaveCallbacks(i,e,{prevent},r[t]);for(let t=0;t<n.length;t++)i=this.__runOnBeforeEnterCallbacks(i,e,{prevent,redirect},n[t]),r[t].element.location=createLocation(e,r[t].route)}else for(let t=r.length-1;t>=e.__divergedChainIndex;t--)i=this.__runOnBeforeLeaveCallbacks(i,e,{prevent},r[t])}if(!e.__skipAttach)for(let t=0;t<n.length;t++)t<e.__divergedChainIndex?t<r.length&&r[t].element&&(r[t].element.location=createLocation(e,r[t].route)):(i=this.__runOnBeforeEnterCallbacks(i,e,{prevent,redirect},n[t]),n[t].element&&(n[t].element.location=createLocation(e,n[t].route)));return i.then((t=>{if(t){if(t.cancel)return this.__previousContext.__renderId=e.__renderId,this.__previousContext;if(t.redirect)return this.__redirect(t.redirect,e.__redirectCount,e.__renderId)}return e}))}__runOnBeforeLeaveCallbacks(e,t,r,n){const i=createLocation(t);return e.then((e=>{if(this.__isLatestRender(t)){return amend("onBeforeLeave",[i,r,this],n.element)(e)}})).then((e=>{if(!(e||{}).redirect)return e}))}__runOnBeforeEnterCallbacks(e,t,r,n){const i=createLocation(t,n.route);return e.then((e=>{if(this.__isLatestRender(t)){return amend("onBeforeEnter",[i,r,this],n.element)(e)}}))}__isReusableElement(e,t){return!(!e||!t)&&(this.__createdByRouter.get(e)&&this.__createdByRouter.get(t)?e.localName===t.localName:e===t)}__isLatestRender(e){return e.__renderId===this.__lastStartedRenderId}__redirect(e,t,r){if(t>256)throw new Error(s(`Too many redirects when rendering ${e.from}`));return this.resolve({pathname:this.urlForPath(e.pathname,e.params),redirectFrom:e.from,__redirectCount:(t||0)+1,__renderId:r})}__ensureOutlet(e=this.__outlet){if(!(e instanceof Node))throw new TypeError(s(`Expected router outlet to be a valid DOM Node (but got ${e})`))}__updateBrowserHistory({pathname:e,search:t="",hash:r=""},n){if(window.location.pathname!==e||window.location.search!==t||window.location.hash!==r){const i=n?"replaceState":"pushState";window.history[i](null,document.title,e+t+r),window.dispatchEvent(new PopStateEvent("popstate",{state:"vaadin-router-ignore"}))}}__copyUnchangedElements(e,t){let r=this.__outlet;for(let n=0;n<e.__divergedChainIndex;n++){const i=t&&t.chain[n].element;if(i){if(i.parentNode!==r)break;e.chain[n].element=i,r=i}}return r}__addAppearingContent(e,t){this.__ensureOutlet(),this.__removeAppearingContent();const r=this.__copyUnchangedElements(e,t);this.__appearingContent=[],this.__disappearingContent=Array.from(r.children).filter((t=>this.__addedByRouter.get(t)&&t!==e.result));let n=r;for(let t=e.__divergedChainIndex;t<e.chain.length;t++){const i=e.chain[t].element;i&&(n.appendChild(i),this.__addedByRouter.set(i,!0),n===r&&this.__appearingContent.push(i),n=i)}}__removeDisappearingContent(){this.__disappearingContent&&removeDomNodes(this.__disappearingContent),this.__disappearingContent=null,this.__appearingContent=null}__removeAppearingContent(){this.__disappearingContent&&this.__appearingContent&&(removeDomNodes(this.__appearingContent),this.__disappearingContent=null,this.__appearingContent=null)}__runOnAfterLeaveCallbacks(e,t){if(t)for(let r=t.chain.length-1;r>=e.__divergedChainIndex&&this.__isLatestRender(e);r--){const n=t.chain[r].element;if(n)try{const r=createLocation(e);runCallbackIfPossible(n.onAfterLeave,[r,{},t.resolver],n)}finally{this.__disappearingContent.indexOf(n)>-1&&removeDomNodes(n.children)}}}__runOnAfterEnterCallbacks(e){for(let t=e.__divergedChainIndex;t<e.chain.length&&this.__isLatestRender(e);t++){const r=e.chain[t].element||{},n=createLocation(e,e.chain[t].route);runCallbackIfPossible(r.onAfterEnter,[n,{},e.resolver],r)}}__animateIfNeeded(e){const t=(this.__disappearingContent||[])[0],r=(this.__appearingContent||[])[0],i=[],o=e.chain;let a;for(let e=o.length;e>0;e--)if(o[e-1].route.animate){a=o[e-1].route.animate;break}if(t&&r&&a){const e=u(a)&&a.leave||"leaving",o=u(a)&&a.enter||"entering";i.push(n(t,e)),i.push(n(r,o))}return Promise.all(i).then((()=>e))}subscribe(){window.addEventListener("vaadin-router-go",this.__navigationEventHandler)}unsubscribe(){window.removeEventListener("vaadin-router-go",this.__navigationEventHandler)}__onNavigationEvent(e){const{pathname:t,search:r,hash:n}=e?e.detail:window.location;c(this.__normalizePathname(t))&&(e&&e.preventDefault&&e.preventDefault(),this.render({pathname:t,search:r,hash:n},!0))}static setTriggers(...e){r(e)}urlForName(e,r){return this.__urlForName||(this.__urlForName=t(this)),getPathnameForRouter(this.__urlForName(e,r),this)}urlForPath(e,t){return getPathnameForRouter(Router.pathToRegexp.compile(e)(t),this)}static go(e){const{pathname:t,search:r,hash:n}=c(e)?this.__createUrl(e,"http://a"):e;return o("go",{pathname:t,search:r,hash:n})}}