import { ProuterSubscriptors , ProuterSubscriptionType , ProuterSubscriptorCallback , ProuterNavigationEvent , ProuterBrowserRouter , ProuterBrowserOptions , ProuterParsedHandler , ProuterRequestCallback , ProuterGroup , ProuterRouter , ProuterProcessPathCallback , ProuterResponse , ProuterProcessPathOptions , ProuterNextMiddleware } from './entity' ; import { routerHelper } from './helper' ; export function browserRouter ( options = { } ) { const handlers = [ ] ; let listening = false ; let previousPath = routerHelper . getPath ( ) ; const subscriptors = { navigation : [ ] } ; const onPopState = ( ) => { const newPath = routerHelper . getPath ( ) ; if ( ! options . processHashChange && newPath === previousPath ) { return ; } br . processCurrentPath ( ) ; triggerOnNavigation ( { oldPath : previousPath , newPath } ) ; previousPath = newPath ; } ; const triggerOnNavigation = ( navigationEvt ) => { subscriptors . navigation . forEach ( ( subscriptor ) => { subscriptor ( navigationEvt ) ; } ) ; } ; const br = { listen ( ) { if ( listening ) { throw new Error ( 'Already listening.' ) ; } br . processCurrentPath ( ) ; addEventListener ( 'popstate' , onPopState ) ; listening = true ; } , stop ( ) { removeEventListener ( 'popstate' , onPopState ) ; } , use ( path , callback ) { if ( typeof callback === 'function' ) { const pathExp = routerHelper . stringToRegexp ( path ) ; handlers . push ( { path , pathExp , callback } ) ; } else { for ( const handler of callback . handlers ) { const itPath = path + handler . path ; const pathExp = routerHelper . stringToRegexp ( itPath ) ; handlers . push ( { path : itPath , pathExp , callback : handler . callback } ) ; } } return this ; } , processPath ( path , processPathCallback ? ) { const requestProcessors = routerHelper . obtainRequestProcessors ( path , handlers ) ; if ( requestProcessors . length === 0 ) { return ; } const listeningSnapshop = listening ; let wasProcessPathCallbackCalled ; let index = 0 ; const response = { end ( ) { if ( processPathCallback && ! wasProcessPathCallbackCalled ) { wasProcessPathCallbackCalled = true ; const opts = { preventNavigation : response . preventNavigation } ; processPathCallback ( opts ) ; } } } ; /** Call the middlewares for the given path. */ const next = ( ) => { if ( index === requestProcessors . length ) { response . end ( ) ; return ; } const reqProc = requestProcessors [ index ] ; reqProc . request . listening = listeningSnapshop ; index ++ ; reqProc . callback ( reqProc . request , response , next ) ; } ; next ( ) ; } , getPath : routerHelper . getPath , push ( newPath ) { br . processPath ( newPath , ( opts ) => { if ( ! opts || ! opts . preventNavigation ) { const oldPath = br . getPath ( ) ; history . pushState ( undefined , '' , newPath ) ; triggerOnNavigation ( { oldPath , newPath } ) ; } } ) ; } , processCurrentPath ( ) { const path = br . getPath ( ) ; br . processPath ( path ) ; } , on ( type , callback ) { subscriptors [ type ] . push ( callback ) ; } , off ( type , callback ) { subscriptors [ type ] = subscriptors [ type ] . filter ( ( cb ) => { return cb !== callback ; } ) ; } } ; return br ; }