2015-05-29 14:58:41 -07:00
import {
RegExp ,
RegExpWrapper ,
StringWrapper ,
2015-06-30 13:18:51 -07:00
isBlank ,
2015-05-29 14:58:41 -07:00
isPresent ,
2015-06-30 13:18:51 -07:00
isType ,
isStringMap ,
2015-07-17 13:36:53 -07:00
Type
2015-08-20 14:28:25 -07:00
} from 'angular2/src/core/facade/lang' ;
2015-09-10 15:25:36 -07:00
import { BaseException , WrappedException } from 'angular2/src/core/facade/exceptions' ;
2015-10-02 16:47:54 -07:00
import { Map , MapWrapper , ListWrapper , StringMapWrapper } from 'angular2/src/core/facade/collection' ;
2015-04-17 09:59:56 -07:00
2015-07-17 13:36:53 -07:00
import { PathRecognizer , PathMatch } from './path_recognizer' ;
import { Route , AsyncRoute , AuxRoute , Redirect , RouteDefinition } from './route_config_impl' ;
2015-06-30 13:18:51 -07:00
import { AsyncRouteHandler } from './async_route_handler' ;
import { SyncRouteHandler } from './sync_route_handler' ;
2015-07-17 13:36:53 -07:00
import { Url } from './url_parser' ;
import { ComponentInstruction } from './instruction' ;
2015-04-17 09:59:56 -07:00
2015-05-15 02:05:57 -07:00
/ * *
* ` RouteRecognizer ` is responsible for recognizing routes for a single component .
2015-05-29 14:58:41 -07:00
* It is consumed by ` RouteRegistry ` , which knows how to recognize an entire hierarchy of
* components .
2015-05-15 02:05:57 -07:00
* /
2015-04-17 09:59:56 -07:00
export class RouteRecognizer {
2015-09-29 11:11:06 -07:00
names = new Map < string , PathRecognizer > ( ) ;
2015-04-17 09:59:56 -07:00
2015-09-29 11:11:06 -07:00
auxRoutes = new Map < string , PathRecognizer > ( ) ;
2015-07-17 13:36:53 -07:00
// TODO: optimize this into a trie
2015-08-28 11:29:19 -07:00
matchers : PathRecognizer [ ] = [ ] ;
2015-07-17 13:36:53 -07:00
// TODO: optimize this into a trie
2015-08-28 11:29:19 -07:00
redirects : Redirector [ ] = [ ] ;
2015-07-21 01:26:43 -07:00
2015-07-13 16:12:48 -07:00
config ( config : RouteDefinition ) : boolean {
var handler ;
2015-07-17 13:36:53 -07:00
2015-09-14 16:01:09 -07:00
if ( isPresent ( config . as ) && config . as [ 0 ] . toUpperCase ( ) != config . as [ 0 ] ) {
var suggestedAlias = config . as [ 0 ] . toUpperCase ( ) + config . as . substring ( 1 ) ;
throw new BaseException (
` Route ' ${ config . path } ' with alias ' ${ config . as } ' does not begin with an uppercase letter. Route aliases should be CamelCase like ' ${ suggestedAlias } '. ` ) ;
}
2015-07-17 13:36:53 -07:00
if ( config instanceof AuxRoute ) {
2015-08-10 20:29:40 -04:00
handler = new SyncRouteHandler ( config . component , config . data ) ;
2015-09-21 12:05:34 -07:00
let path =
StringWrapper . startsWith ( config . path , '/' ) ? config . path . substring ( 1 ) : config . path ;
2015-07-17 13:36:53 -07:00
var recognizer = new PathRecognizer ( config . path , handler ) ;
this . auxRoutes . set ( path , recognizer ) ;
return recognizer . terminal ;
}
2015-07-13 16:12:48 -07:00
if ( config instanceof Redirect ) {
2015-07-17 13:36:53 -07:00
this . redirects . push ( new Redirector ( config . path , config . redirectTo ) ) ;
2015-07-13 16:12:48 -07:00
return true ;
2015-07-17 13:36:53 -07:00
}
if ( config instanceof Route ) {
2015-08-10 20:29:40 -04:00
handler = new SyncRouteHandler ( config . component , config . data ) ;
2015-07-13 16:12:48 -07:00
} else if ( config instanceof AsyncRoute ) {
2015-08-10 20:29:40 -04:00
handler = new AsyncRouteHandler ( config . loader , config . data ) ;
2015-06-17 11:57:38 -07:00
}
2015-07-17 13:36:53 -07:00
var recognizer = new PathRecognizer ( config . path , handler ) ;
this . matchers . forEach ( ( matcher ) = > {
if ( recognizer . hash == matcher . hash ) {
2015-05-29 14:58:41 -07:00
throw new BaseException (
2015-07-13 16:12:48 -07:00
` Configuration ' ${ config . path } ' conflicts with existing route ' ${ matcher . path } ' ` ) ;
2015-05-15 02:05:57 -07:00
}
} ) ;
2015-07-17 13:36:53 -07:00
this . matchers . push ( recognizer ) ;
2015-07-13 16:12:48 -07:00
if ( isPresent ( config . as ) ) {
this . names . set ( config . as , recognizer ) ;
2015-04-17 09:59:56 -07:00
}
2015-06-17 11:57:38 -07:00
return recognizer . terminal ;
2015-04-17 09:59:56 -07:00
}
2015-05-15 02:05:57 -07:00
/ * *
* Given a URL , returns a list of ` RouteMatch ` es , which are partial recognitions for some route .
*
* /
2015-08-28 11:29:19 -07:00
recognize ( urlParse : Url ) : PathMatch [ ] {
2015-06-17 11:17:21 -07:00
var solutions = [ ] ;
2015-05-15 02:05:57 -07:00
2015-07-17 13:36:53 -07:00
urlParse = this . _redirect ( urlParse ) ;
2015-04-17 09:59:56 -07:00
2015-07-17 13:36:53 -07:00
this . matchers . forEach ( ( pathRecognizer : PathRecognizer ) = > {
var pathMatch = pathRecognizer . recognize ( urlParse ) ;
2015-07-21 01:26:43 -07:00
2015-07-17 13:36:53 -07:00
if ( isPresent ( pathMatch ) ) {
solutions . push ( pathMatch ) ;
2015-04-17 09:59:56 -07:00
}
} ) ;
return solutions ;
}
2015-10-09 17:21:25 -07:00
/** @internal */
2015-07-17 13:36:53 -07:00
_redirect ( urlParse : Url ) : Url {
for ( var i = 0 ; i < this . redirects . length ; i += 1 ) {
let redirector = this . redirects [ i ] ;
var redirectedUrl = redirector . redirect ( urlParse ) ;
if ( isPresent ( redirectedUrl ) ) {
return redirectedUrl ;
}
}
return urlParse ;
}
recognizeAuxiliary ( urlParse : Url ) : PathMatch {
var pathRecognizer = this . auxRoutes . get ( urlParse . path ) ;
if ( isBlank ( pathRecognizer ) ) {
return null ;
}
return pathRecognizer . recognize ( urlParse ) ;
}
2015-06-17 21:42:56 -07:00
hasRoute ( name : string ) : boolean { return this . names . has ( name ) ; }
2015-04-17 09:59:56 -07:00
2015-07-17 13:36:53 -07:00
generate ( name : string , params : any ) : ComponentInstruction {
2015-06-30 13:18:51 -07:00
var pathRecognizer : PathRecognizer = this . names . get ( name ) ;
if ( isBlank ( pathRecognizer ) ) {
return null ;
}
2015-07-17 13:36:53 -07:00
return pathRecognizer . generate ( params ) ;
2015-04-17 09:59:56 -07:00
}
}
2015-05-15 02:05:57 -07:00
2015-07-17 13:36:53 -07:00
export class Redirector {
2015-08-28 11:29:19 -07:00
segments : string [ ] = [ ] ;
toSegments : string [ ] = [ ] ;
2015-07-21 01:26:43 -07:00
2015-07-17 13:36:53 -07:00
constructor ( path : string , redirectTo : string ) {
2015-09-21 12:05:34 -07:00
if ( StringWrapper . startsWith ( path , '/' ) ) {
2015-07-17 13:36:53 -07:00
path = path . substring ( 1 ) ;
}
this . segments = path . split ( '/' ) ;
2015-09-21 12:05:34 -07:00
if ( StringWrapper . startsWith ( redirectTo , '/' ) ) {
2015-07-17 13:36:53 -07:00
redirectTo = redirectTo . substring ( 1 ) ;
2015-07-21 01:26:43 -07:00
}
2015-07-17 13:36:53 -07:00
this . toSegments = redirectTo . split ( '/' ) ;
2015-07-21 01:26:43 -07:00
}
2015-06-29 10:37:55 +02:00
2015-07-17 13:36:53 -07:00
/ * *
* Returns ` null ` or a ` ParsedUrl ` representing the new path to match
* /
redirect ( urlParse : Url ) : Url {
for ( var i = 0 ; i < this . segments . length ; i += 1 ) {
if ( isBlank ( urlParse ) ) {
return null ;
}
let segment = this . segments [ i ] ;
if ( segment != urlParse . path ) {
return null ;
}
urlParse = urlParse . child ;
2015-06-30 13:18:51 -07:00
}
2015-07-17 13:36:53 -07:00
for ( var i = this . toSegments . length - 1 ; i >= 0 ; i -= 1 ) {
let segment = this . toSegments [ i ] ;
urlParse = new Url ( segment , urlParse ) ;
2015-06-30 13:18:51 -07:00
}
2015-07-17 13:36:53 -07:00
return urlParse ;
2015-05-15 02:05:57 -07:00
}
}