import { routerActions } from "connected-react-router" ; import * as assignDeep from "deep-extend" ; import { RootRouter , routerData , viewToPath } from "modules" ; import { ModuleNames } from "modules/names" ; import { RouterParser } from "react-coat" ; import { matchPath } from "react-router" ; import { Dispatch } from "redux" ; type ViewToPath = typeof viewToPath ; type PathToView = { [ pathname ] : [ string , string ] ; } ; const pathToView = ( ( maps ) => { const urls = { } ; for ( const mName in maps ) { if ( maps . hasOwnProperty ( mName ) ) { const item = maps [ mName ] ; if ( typeof item === "string" ) { urls [ item ] = [ mName , "Main" ] ; } else { for ( const vName in item ) { if ( item . hasOwnProperty ( vName ) ) { urls [ item [ vName ] ] = [ mName , vName ] ; } } } } } return urls ; } ) ( viewToPath as any ) ; function parsePathname ( pathname ) { const views = { } ; const pathData = { } ; Object . keys ( pathToView ) . forEach ( url => { const match = matchPath ( pathname , url ) ; if ( match ) { const [ moduleName , viewName ] = pathToView [ url ] ; if ( ! views [ moduleName ] ) { views [ moduleName ] = { [ viewName ] : 1 } ; } else { views [ moduleName ] [ viewName ] = 1 ; } if ( match . params ) { pathData [ moduleName ] = match . params ; } } } ) ; return { views , pathData } ; } export function toPath < N extends ModuleNames , V extends keyof ViewToPath [ N ] , P extends RootRouter [ "pathData" ] [ N ] > ( moduleName , viewName , params ? ) { const pathExp = viewToPath [ moduleName as "app" ] [ viewName as "Main" ] || "" ; let pathname = pathExp ; if ( params ) { pathname = pathExp . replace ( /:\w+/g , flag => { const key = flag . substr ( 1 ) ; if ( params [ key ] ) { return params [ key ] ; } else { return "" ; } } ) ; } return pathname ; } export function toUrl ( pathname , searchData ? , hashData ? ) { let url = pathname ; if ( searchData ) { const str = serialize ( excludeDefData ( searchData , routerData . wholeSearchData ) ) ; if ( str ) { url += "?" + str . replace ( "?" , "" ) ; } } if ( hashData ) { const str = serialize ( excludeDefData ( hashData , routerData . wholeHashData ) ) ; if ( str ) { url += "#" + str . replace ( "#" , "" ) ; } } return url ; } function serialize ( data ) { const flatArr = [ ] ; for ( const mName in data ) { if ( data . hasOwnProperty ( mName ) ) { const mRoute = data [ mName ] ; if ( mRoute ) { for ( const mKey in mRoute ) { if ( mRoute . hasOwnProperty ( mKey ) ) { if ( mRoute [ mKey ] !== undefined ) { flatArr . push ( `${ mName }-${ mKey }=${ escape ( JSON . stringify ( mRoute [ mKey ] ) ) }` ) ; } } } } } } if ( flatArr . length ) { return flatArr . sort ( ) . join ( "&" ) ; } else { return "" ; } } function parseRoute ( pre , cur ) { const [ key , val ] = cur . split ( "=" ) ; if ( key ) { const arr = key . split ( "-" ) ; const moduleName = arr . shift ( ) ; const moduleKey = arr . join ( "-" ) ; if ( moduleName && moduleKey ) { if ( ! pre [ moduleName ] ) { pre [ moduleName ] = { } ; } pre [ moduleName ] [ moduleKey ] = unserializeQuery ( val ) ; } } return pre ; } const ISO_DATE_FORMAT = /^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(\.\d+)?(Z|[+-][01]\d:[0-5]\d)$/ ; function unserializeQuery ( query ) { if ( ! query ) { return "" ; } let args ; try { args = JSON . parse ( unescape ( query ) , ( prop , value ) => { if ( typeof value === "string" && ISO_DATE_FORMAT . test ( value ) ) { return new Date ( value ) ; } return value ; } ) ; } catch ( e ) { args = "" ; } return args ; } const excludeDefData = ( data , def ) => { const result = { } ; for ( const key in data ) { if ( data . hasOwnProperty ( key ) ) { if ( typeof data [ key ] === "object" && def [ key ] && ! Array . isArray ( def [ key ] ) ) { result [ key ] = excludeDefData ( data [ key ] , def [ key ] ) ; } else if ( data [ key ] !== def [ key ] ) { result [ key ] = data [ key ] ; } } } if ( Object . keys ( result ) . length === 0 ) { return undefined ; } return result ; } ; function mergeDefData < T extends { [ moduleName ] : any ; } > ( views , data , def ) { const newData = { ... data } ; Object . keys ( views ) . forEach ( moduleName => { if ( ! newData [ moduleName ] ) { newData [ moduleName ] = { } ; } } ) ; Object . keys ( newData ) . forEach ( moduleName => { if ( def [ moduleName ] ) { newData [ moduleName ] = assignDeep ( { } , def [ moduleName ] , data [ moduleName ] ) ; } } ) ; return newData ; } export const routerParser = ( nextRouter , prevRouter ) => { const nRouter = { ... nextRouter } ; const changed = { pathname : false , search : false , hash : false } ; if ( prevRouter && nextRouter . location . pathname !== prevRouter . location . pathname ) { const { views , pathData } = parsePathname ( nextRouter . location . pathname ) ; nRouter . views = views ; nRouter . pathData = pathData ; changed . pathname = true ; } if ( prevRouter && nextRouter . location . search !== prevRouter . location . search ) { nRouter . searchData = nextRouter . location . search . split ( /[&?]/ ) . reduce ( parseRoute , { } ) ; changed . search = true ; } if ( prevRouter && nextRouter . location . hash !== prevRouter . location . hash ) { nRouter . hashData = nextRouter . location . hash . split ( /[&#]/ ) . reduce ( parseRoute , { } ) ; changed . hash = true ; } if ( changed . pathname || changed . search ) { nRouter . wholeSearchData = mergeDefData ( nRouter . views , nRouter . searchData , routerData . wholeSearchData ) ; } if ( changed . pathname || changed . hash ) { nRouter . wholeHashData = mergeDefData ( nRouter . views , nRouter . hashData , routerData . wholeHashData ) ; } return nRouter ; } ; export function advanceRouter ( url ) { let [ pathname , search , hash ] = url . split ( /[?#]/ ) ; if ( url . indexOf ( "?" ) === - 1 ) { hash = search ; search = "" ; } pathname = pathname . replace ( /^.+:\/\/[^/]+/ , "" ) ; search = search ? "?" + search : "" ; hash = hash ? "#" + hash : "" ; const redirects = [ { path : /^\/$/ , replace : "/photos" , } , { path : /\/$/ , replace : "" , } , ] ; for ( const rule of redirects ) { if ( rule . path . test ( pathname ) ) { return pathname . replace ( rule . path , rule . replace ) ; } } const { views , pathData } = parsePathname ( pathname ) ; if ( Object . keys ( views ) . length < 2 ) { return `${ InitEnv . clientPublicPath }404.html` ; } const searchData = search . split ( /[&?]/ ) . reduce ( parseRoute , { } ) ; const hashData = hash . split ( /[&#]/ ) . reduce ( parseRoute , { } ) ; return { location : { pathname , search , hash , state : null } , action : "POP" , views , pathData , searchData , hashData , wholeSearchData : mergeDefData ( views , searchData , routerData . wholeSearchData ) , wholeHashData : mergeDefData ( views , hashData , routerData . wholeHashData ) , } ; } export function linkTo ( e , dispatch ) { e . preventDefault ( ) ; const href = e . currentTarget . getAttribute ( "href" ) as string ; if ( href && href !== "#" ) { dispatch ( routerActions . push ( href ) ) ; } }