1- import { isInjectable , extend , isDefined , isString , isArray , filter , map , prop , curry } from "../common/common" ;
1+ import { isInjectable , extend , isDefined , isString , isArray , filter , map , pick , prop , propEq , curry , applyPairs } from "../common/common" ;
2+ import { IRawParams } from "../params/interface" ;
23import { runtime } from "../common/angular1" ;
34import matcherConfig from "../url/urlMatcherConfig" ;
45import paramTypes from "./paramTypes" ;
56import Type from "./type" ;
67
8+ let hasOwn = Object . prototype . hasOwnProperty ;
9+ let isShorthand = cfg => [ "value" , "type" , "squash" , "array" , "dynamic" ] . filter ( hasOwn . bind ( cfg || { } ) ) . length === 0 ;
10+
11+ enum DefType {
12+ PATH , SEARCH , CONFIG
13+ }
14+
715export default class Param {
816 id : string ;
917 type : Type ;
10- location : string ;
18+ location : DefType ;
1119 array : boolean ;
1220 squash : ( boolean | string ) ;
1321 replace : any ;
1422 isOptional : boolean ;
1523 dynamic : boolean ;
1624 config : any ;
1725
18- constructor ( id , type , config , location ) {
26+ constructor ( id : string , type : Type , config : any , location : DefType ) {
1927 config = unwrapShorthand ( config ) ;
2028 type = getType ( config , type , location ) ;
2129 var arrayMode = getArrayMode ( ) ;
22- type = arrayMode ? type . $asArray ( arrayMode , location === "search" ) : type ;
30+ type = arrayMode ? type . $asArray ( arrayMode , location === DefType . SEARCH ) : type ;
2331 var isOptional = config . value !== undefined ;
2432 var dynamic = config . dynamic === true ;
2533 var squash = getSquashPolicy ( config , isOptional ) ;
2634 var replace = getReplace ( config , arrayMode , isOptional , squash ) ;
2735
2836 function unwrapShorthand ( config ) {
29- var configKeys = [ "value" , "type" , "squash" , "array" , "dynamic" ] . filter ( function ( key ) {
30- return ( config || { } ) . hasOwnProperty ( key ) ;
37+ config = isShorthand ( config ) && { value : config } || config ;
38+
39+ return extend ( config , {
40+ $$fn : isInjectable ( config . value ) ? config . value : ( ) => config . value
3141 } ) ;
32- var isShorthand = configKeys . length === 0 ;
33- if ( isShorthand ) config = { value : config } ;
34- config . $$fn = isInjectable ( config . value ) ? config . value : function ( ) {
35- return config . value ;
36- } ;
37- return config ;
3842 }
3943
4044 function getType ( config , urlType , location ) {
4145 if ( config . type && urlType && urlType . name !== 'string' ) throw new Error ( `Param '${ id } ' has two type configurations.` ) ;
4246 if ( config . type && urlType && urlType . name === 'string' && paramTypes . type ( config . type ) ) return paramTypes . type ( config . type ) ;
4347 if ( urlType ) return urlType ;
44- if ( ! config . type ) return ( location === "config" ? paramTypes . type ( "any" ) : paramTypes . type ( "string" ) ) ;
48+ if ( ! config . type ) return ( location === DefType . CONFIG ? paramTypes . type ( "any" ) : paramTypes . type ( "string" ) ) ;
4549 return config . type instanceof Type ? config . type : paramTypes . type ( config . type ) ;
4650 }
4751
4852 // array config: param name (param[]) overrides default settings. explicit config overrides param name.
4953 function getArrayMode ( ) {
50- var arrayDefaults = { array : ( location === "search" ? "auto" : false ) } ;
51- var arrayParamNomenclature = id . match ( / \[ \] $ / ) ? { array : true } : { } ;
54+ var arrayDefaults = { array : ( location === DefType . SEARCH ? "auto" : false ) } ;
55+ var arrayParamNomenclature = id . match ( / \[ \] $ / ) ? { array : true } : { } ;
5256 return extend ( arrayDefaults , arrayParamNomenclature , config ) . array ;
5357 }
5458
@@ -60,7 +64,7 @@ export default class Param {
6064 if ( ! isOptional || squash === false ) return false ;
6165 if ( ! isDefined ( squash ) || squash == null ) return matcherConfig . defaultSquashPolicy ( ) ;
6266 if ( squash === true || isString ( squash ) ) return squash ;
63- throw new Error ( " Invalid squash policy: '" + squash + " '. Valid policies: false, true, or arbitrary string" ) ;
67+ throw new Error ( ` Invalid squash policy: '${ squash } '. Valid policies: false, true, or arbitrary string` ) ;
6468 }
6569
6670 function getReplace ( config , arrayMode , isOptional , squash ) {
@@ -69,23 +73,23 @@ export default class Param {
6973 { from : null , to : ( isOptional || arrayMode ? undefined : "" ) }
7074 ] ;
7175 replace = isArray ( config . replace ) ? config . replace : [ ] ;
72- if ( isString ( squash ) ) replace . push ( { from : squash , to : undefined } ) ;
76+ if ( isString ( squash ) ) replace . push ( { from : squash , to : undefined } ) ;
7377 configuredKeys = map ( replace , prop ( "from" ) ) ;
7478 return filter ( defaultPolicy , item => configuredKeys . indexOf ( item . from ) === - 1 ) . concat ( replace ) ;
7579 }
7680
7781 extend ( this , { id, type, location, squash, replace, isOptional, dynamic, config, array : arrayMode } ) ;
7882 }
7983
80- isDefaultValue ( value : any ) {
84+ isDefaultValue ( value : any ) : boolean {
8185 return this . isOptional && this . type . equals ( this . value ( ) , value ) ;
8286 }
8387
8488 /**
8589 * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
8690 * default value, which may be the result of an injectable function.
8791 */
88- value ( value ?: any ) {
92+ value ( value ?: any ) : any {
8993 /**
9094 * [Internal] Get the default value of a parameter, which may be an injectable function.
9195 */
@@ -97,19 +101,63 @@ export default class Param {
97101 return defaultValue ;
98102 } ;
99103
100- const hasReplaceVal = curry ( ( val , obj ) => obj . from === val ) ;
101-
102104 const $replace = ( value ) => {
103- var replacement : any = map ( filter ( this . replace , hasReplaceVal ( value ) ) , prop ( "to" ) ) ;
105+ var replacement : any = map ( filter ( this . replace , propEq ( 'from' , value ) ) , prop ( "to" ) ) ;
104106 return replacement . length ? replacement [ 0 ] : value ;
105107 } ;
106108
107109 value = $replace ( value ) ;
108110 return ! isDefined ( value ) ? $$getDefaultValue ( ) : this . type . $normalize ( value ) ;
109111 }
110112
113+ isSearch ( ) : boolean {
114+ return this . location === DefType . SEARCH ;
115+ }
116+
117+ validates ( value : any ) : boolean {
118+ // There was no parameter value, but the param is optional
119+ if ( ( ! isDefined ( value ) || value === null ) && this . isOptional ) return true ;
120+
121+ // The value was not of the correct Type, and could not be decoded to the correct Type
122+ const normalized = this . type . $normalize ( value ) ;
123+ if ( ! this . type . is ( normalized ) ) return false ;
124+
125+ // The value was of the correct type, but when encoded, did not match the Type's regexp
126+ const encoded = this . type . encode ( normalized ) ;
127+ if ( isString ( encoded ) && ! this . type . pattern . exec ( < string > encoded ) ) return false ;
128+
129+ return true ;
130+ }
131+
111132 toString ( ) {
112133 return `{Param:${ this . id } ${ this . type } squash: '${ this . squash } ' optional: ${ this . isOptional } }` ;
113134 }
114135
115- }
136+ static fromConfig ( id : string , type : Type , config : any ) : Param {
137+ return new Param ( id , type , config , DefType . CONFIG ) ;
138+ }
139+
140+ static fromPath ( id : string , type : Type , config : any ) : Param {
141+ return new Param ( id , type , config , DefType . PATH ) ;
142+ }
143+
144+ static fromSearch ( id : string , type : Type , config : any ) : Param {
145+ return new Param ( id , type , config , DefType . SEARCH ) ;
146+ }
147+
148+ static values ( params : Param [ ] , values ) : IRawParams {
149+ values = values || { } ;
150+ return < IRawParams > params . map ( param => [ param . id , param . value ( values [ param . id ] ) ] ) . reduce ( applyPairs , { } ) ;
151+ }
152+
153+ static equals ( params : Param [ ] , values1 , values2 ) : boolean {
154+ values1 = values1 || { } ;
155+ values2 = values2 || { } ;
156+ return params . map ( param => param . type . equals ( values1 [ param . id ] , values2 [ param . id ] ) ) . indexOf ( false ) === - 1 ;
157+ }
158+
159+ static validates ( params : Param [ ] , values ) : boolean {
160+ values = values || { } ;
161+ return params . map ( param => param . validates ( values [ param . id ] ) ) . indexOf ( false ) === - 1 ;
162+ }
163+ }
0 commit comments