import { resetScrollAfterNextBatch } from '../Rendering/Renderer' ; import { EventDelegator } from '../Rendering/EventDelegator' ; let hasEnabledNavigationInterception = false ; let hasRegisteredNavigationEventListeners = false ; let notifyLocationChangedCallback = null ; export const internalFunctions = { listenForNavigationEvents , enableNavigationInterception , navigateTo , getBaseURI : ( ) => document . baseURI , getLocationHref : ( ) => location . href , } ; function listenForNavigationEvents ( callback ) { notifyLocationChangedCallback = callback ; if ( hasRegisteredNavigationEventListeners ) { return ; } hasRegisteredNavigationEventListeners = true ; window . addEventListener ( 'popstate' , ( ) => notifyLocationChanged ( false ) ) ; } function enableNavigationInterception ( ) { hasEnabledNavigationInterception = true ; } export function attachToEventDelegator ( eventDelegator ) { eventDelegator . notifyAfterClick ( event => { if ( ! hasEnabledNavigationInterception ) { return ; } if ( event . button !== 0 || eventHasSpecialKey ( event ) ) { return ; } if ( event . defaultPrevented ) { return ; } const anchorTarget = findClosestAncestor ( event . target as Element | null , 'A' ) as HTMLAnchorElement | null ; const hrefAttributeName = 'href' ; if ( anchorTarget && anchorTarget . hasAttribute ( hrefAttributeName ) ) { const targetAttributeValue = anchorTarget . getAttribute ( 'target' ) ; const opensInSameFrame = ! targetAttributeValue || targetAttributeValue === '_self' ; if ( ! opensInSameFrame ) { return ; } const href = anchorTarget . getAttribute ( hrefAttributeName ) ! ; const absoluteHref = toAbsoluteUri ( href ) ; if ( isWithinBaseUriSpace ( absoluteHref ) ) { event . preventDefault ( ) ; performInternalNavigation ( absoluteHref , true ) ; } } } ) ; } export function navigateTo ( uri , forceLoad ) { const absoluteUri = toAbsoluteUri ( uri ) ; if ( ! forceLoad && isWithinBaseUriSpace ( absoluteUri ) ) { performInternalNavigation ( absoluteUri , false ) ; } else if ( forceLoad && location . href === uri ) { const temporaryUri = uri + '?' ; history . replaceState ( null , '' , temporaryUri ) ; location . replace ( uri ) ; } else { location . href = uri ; } } function performInternalNavigation ( absoluteInternalHref , interceptedLink ) { resetScrollAfterNextBatch ( ) ; history . pushState ( null , '' , absoluteInternalHref ) ; notifyLocationChanged ( interceptedLink ) ; } async function notifyLocationChanged ( interceptedLink ) { if ( notifyLocationChangedCallback ) { await notifyLocationChangedCallback ( location . href , interceptedLink ) ; } } let testAnchor ; function toAbsoluteUri ( relativeUri ) { testAnchor = testAnchor || document . createElement ( 'a' ) ; testAnchor . href = relativeUri ; return testAnchor . href ; } function findClosestAncestor ( element , tagName ) { return ! element ? null : element . tagName === tagName ? element : findClosestAncestor ( element . parentElement , tagName ) ; } function isWithinBaseUriSpace ( href ) { const baseUriWithTrailingSlash = toBaseUriWithTrailingSlash ( document . baseURI ! ) ; return href . startsWith ( baseUriWithTrailingSlash ) ; } function toBaseUriWithTrailingSlash ( baseUri ) { return baseUri . substr ( 0 , baseUri . lastIndexOf ( '/' ) + 1 ) ; } function eventHasSpecialKey ( event ) { return event . ctrlKey || event . shiftKey || event . altKey || event . metaKey ; }