1 | import { StringMapWrapper } from '../src/facade/collection';
|
2 | import { BaseException } from '../src/facade/exceptions';
|
3 | import { RegExpWrapper, isBlank, isPresent } from '../src/facade/lang';
|
4 | export function convertUrlParamsToArray(urlParams) {
|
5 | var paramsArray = [];
|
6 | if (isBlank(urlParams)) {
|
7 | return [];
|
8 | }
|
9 | StringMapWrapper.forEach(urlParams, (value , key ) => {
|
10 | paramsArray.push((value === true) ? key : key + '=' + value);
|
11 | });
|
12 | return paramsArray;
|
13 | }
|
14 |
|
15 | export function serializeParams(urlParams, joiner = '&') {
|
16 | return convertUrlParamsToArray(urlParams).join(joiner);
|
17 | }
|
18 |
|
19 |
|
20 |
|
21 | export class Url {
|
22 | constructor(path, child = null, auxiliary = [], params = {}) {
|
23 | this.path = path;
|
24 | this.child = child;
|
25 | this.auxiliary = auxiliary;
|
26 | this.params = params;
|
27 | }
|
28 | toString() {
|
29 | return this.path + this._matrixParamsToString() + this._auxToString() + this._childString();
|
30 | }
|
31 | segmentToString() { return this.path + this._matrixParamsToString(); }
|
32 |
|
33 | _auxToString() {
|
34 | return this.auxiliary.length > 0 ?
|
35 | ('(' + this.auxiliary.map(sibling => sibling.toString()).join('//') + ')') :
|
36 | '';
|
37 | }
|
38 | _matrixParamsToString() {
|
39 | var paramString = serializeParams(this.params, ';');
|
40 | if (paramString.length > 0) {
|
41 | return ';' + paramString;
|
42 | }
|
43 | return '';
|
44 | }
|
45 |
|
46 | _childString() { return isPresent(this.child) ? ('/' + this.child.toString()) : ''; }
|
47 | }
|
48 | export class RootUrl extends Url {
|
49 | constructor(path, child = null, auxiliary = [], params = null) {
|
50 | super(path, child, auxiliary, params);
|
51 | }
|
52 | toString() {
|
53 | return this.path + this._auxToString() + this._childString() + this._queryParamsToString();
|
54 | }
|
55 | segmentToString() { return this.path + this._queryParamsToString(); }
|
56 | _queryParamsToString() {
|
57 | if (isBlank(this.params)) {
|
58 | return '';
|
59 | }
|
60 | return '?' + serializeParams(this.params);
|
61 | }
|
62 | }
|
63 | export function pathSegmentsToUrl(pathSegments) {
|
64 | var url = new Url(pathSegments[pathSegments.length - 1]);
|
65 | for (var i = pathSegments.length - 2; i >= 0; i -= 1) {
|
66 | url = new Url(pathSegments[i], url);
|
67 | }
|
68 | return url;
|
69 | }
|
70 | var SEGMENT_RE = RegExpWrapper.create('^[^\\/\\(\\)\\?;=&#]+');
|
71 | function matchUrlSegment(str) {
|
72 | var match = RegExpWrapper.firstMatch(SEGMENT_RE, str);
|
73 | return isPresent(match) ? match[0] : '';
|
74 | }
|
75 | var QUERY_PARAM_VALUE_RE = RegExpWrapper.create('^[^\\(\\)\\?;&#]+');
|
76 | function matchUrlQueryParamValue(str) {
|
77 | var match = RegExpWrapper.firstMatch(QUERY_PARAM_VALUE_RE, str);
|
78 | return isPresent(match) ? match[0] : '';
|
79 | }
|
80 | export class UrlParser {
|
81 | peekStartsWith(str) { return this._remaining.startsWith(str); }
|
82 | capture(str) {
|
83 | if (!this._remaining.startsWith(str)) {
|
84 | throw new BaseException(`Expected "${str}".`);
|
85 | }
|
86 | this._remaining = this._remaining.substring(str.length);
|
87 | }
|
88 | parse(url) {
|
89 | this._remaining = url;
|
90 | if (url == '' || url == '/') {
|
91 | return new Url('');
|
92 | }
|
93 | return this.parseRoot();
|
94 | }
|
95 |
|
96 | parseRoot() {
|
97 | if (this.peekStartsWith('/')) {
|
98 | this.capture('/');
|
99 | }
|
100 | var path = matchUrlSegment(this._remaining);
|
101 | this.capture(path);
|
102 | var aux = [];
|
103 | if (this.peekStartsWith('(')) {
|
104 | aux = this.parseAuxiliaryRoutes();
|
105 | }
|
106 | if (this.peekStartsWith(';')) {
|
107 |
|
108 | this.parseMatrixParams();
|
109 | }
|
110 | var child = null;
|
111 | if (this.peekStartsWith('/') && !this.peekStartsWith('//')) {
|
112 | this.capture('/');
|
113 | child = this.parseSegment();
|
114 | }
|
115 | var queryParams = null;
|
116 | if (this.peekStartsWith('?')) {
|
117 | queryParams = this.parseQueryParams();
|
118 | }
|
119 | return new RootUrl(path, child, aux, queryParams);
|
120 | }
|
121 |
|
122 | parseSegment() {
|
123 | if (this._remaining.length == 0) {
|
124 | return null;
|
125 | }
|
126 | if (this.peekStartsWith('/')) {
|
127 | this.capture('/');
|
128 | }
|
129 | var path = matchUrlSegment(this._remaining);
|
130 | this.capture(path);
|
131 | var matrixParams = null;
|
132 | if (this.peekStartsWith(';')) {
|
133 | matrixParams = this.parseMatrixParams();
|
134 | }
|
135 | var aux = [];
|
136 | if (this.peekStartsWith('(')) {
|
137 | aux = this.parseAuxiliaryRoutes();
|
138 | }
|
139 | var child = null;
|
140 | if (this.peekStartsWith('/') && !this.peekStartsWith('//')) {
|
141 | this.capture('/');
|
142 | child = this.parseSegment();
|
143 | }
|
144 | return new Url(path, child, aux, matrixParams);
|
145 | }
|
146 | parseQueryParams() {
|
147 | var params = {};
|
148 | this.capture('?');
|
149 | this.parseQueryParam(params);
|
150 | while (this._remaining.length > 0 && this.peekStartsWith('&')) {
|
151 | this.capture('&');
|
152 | this.parseQueryParam(params);
|
153 | }
|
154 | return params;
|
155 | }
|
156 | parseMatrixParams() {
|
157 | var params = {};
|
158 | while (this._remaining.length > 0 && this.peekStartsWith(';')) {
|
159 | this.capture(';');
|
160 | this.parseParam(params);
|
161 | }
|
162 | return params;
|
163 | }
|
164 | parseParam(params) {
|
165 | var key = matchUrlSegment(this._remaining);
|
166 | if (isBlank(key)) {
|
167 | return;
|
168 | }
|
169 | this.capture(key);
|
170 | var value = true;
|
171 | if (this.peekStartsWith('=')) {
|
172 | this.capture('=');
|
173 | var valueMatch = matchUrlSegment(this._remaining);
|
174 | if (isPresent(valueMatch)) {
|
175 | value = valueMatch;
|
176 | this.capture(value);
|
177 | }
|
178 | }
|
179 | params[key] = value;
|
180 | }
|
181 | parseQueryParam(params) {
|
182 | var key = matchUrlSegment(this._remaining);
|
183 | if (isBlank(key)) {
|
184 | return;
|
185 | }
|
186 | this.capture(key);
|
187 | var value = true;
|
188 | if (this.peekStartsWith('=')) {
|
189 | this.capture('=');
|
190 | var valueMatch = matchUrlQueryParamValue(this._remaining);
|
191 | if (isPresent(valueMatch)) {
|
192 | value = valueMatch;
|
193 | this.capture(value);
|
194 | }
|
195 | }
|
196 | params[key] = value;
|
197 | }
|
198 | parseAuxiliaryRoutes() {
|
199 | var routes = [];
|
200 | this.capture('(');
|
201 | while (!this.peekStartsWith(')') && this._remaining.length > 0) {
|
202 | routes.push(this.parseSegment());
|
203 | if (this.peekStartsWith('//')) {
|
204 | this.capture('//');
|
205 | }
|
206 | }
|
207 | this.capture(')');
|
208 | return routes;
|
209 | }
|
210 | }
|
211 | export var parser = new UrlParser();
|
212 |
|
\ | No newline at end of file |