1 | import { OpaqueToken } from '@angular/core';
|
2 | import { isArray, isBlank, isPresent } from '../util/util';
|
3 |
|
4 |
|
5 |
|
6 | var UrlSerializer = (function () {
|
7 | |
8 |
|
9 |
|
10 |
|
11 | function UrlSerializer(_app, config) {
|
12 | this._app = _app;
|
13 | if (config && isArray(config.links)) {
|
14 | this.links = normalizeLinks(config.links);
|
15 | }
|
16 | else {
|
17 | this.links = [];
|
18 | }
|
19 | }
|
20 | |
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | UrlSerializer.prototype.parse = function (browserUrl) {
|
27 | if (browserUrl.charAt(0) === '/') {
|
28 | browserUrl = browserUrl.substr(1);
|
29 | }
|
30 |
|
31 | browserUrl = browserUrl.split('?')[0].split('#')[0];
|
32 | return convertUrlToSegments(this._app, browserUrl, this.links);
|
33 | };
|
34 | |
35 |
|
36 |
|
37 |
|
38 |
|
39 | UrlSerializer.prototype.createSegmentFromName = function (navContainer, nameOrComponent) {
|
40 | var configLink = this.getLinkFromName(nameOrComponent);
|
41 | if (configLink) {
|
42 | return this._createSegment(this._app, navContainer, configLink, null);
|
43 | }
|
44 | return null;
|
45 | };
|
46 | |
47 |
|
48 |
|
49 |
|
50 | UrlSerializer.prototype.getLinkFromName = function (nameOrComponent) {
|
51 | return this.links.find(function (link) {
|
52 | return (link.component === nameOrComponent) ||
|
53 | (link.name === nameOrComponent);
|
54 | });
|
55 | };
|
56 | |
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 | UrlSerializer.prototype.serialize = function (segments) {
|
63 | if (!segments || !segments.length) {
|
64 | return '/';
|
65 | }
|
66 | var sections = segments.map(function (segment) {
|
67 | if (segment.type === 'tabs') {
|
68 | if (segment.requiresExplicitNavPrefix) {
|
69 | return "/" + segment.type + "/" + segment.navId + "/" + segment.secondaryId + "/" + segment.id;
|
70 | }
|
71 | return "/" + segment.secondaryId + "/" + segment.id;
|
72 | }
|
73 |
|
74 | if (segment.requiresExplicitNavPrefix) {
|
75 | return "/" + segment.type + "/" + segment.navId + "/" + segment.id;
|
76 | }
|
77 | return "/" + segment.id;
|
78 | });
|
79 | return sections.join('');
|
80 | };
|
81 | |
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | UrlSerializer.prototype.serializeComponent = function (navContainer, component, data) {
|
89 | if (component) {
|
90 | var link = findLinkByComponentData(this.links, component, data);
|
91 | if (link) {
|
92 | return this._createSegment(this._app, navContainer, link, data);
|
93 | }
|
94 | }
|
95 | return null;
|
96 | };
|
97 | |
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 | UrlSerializer.prototype._createSegment = function (app, navContainer, configLink, data) {
|
106 | var urlParts = configLink.segmentParts;
|
107 | if (isPresent(data)) {
|
108 |
|
109 | urlParts = urlParts.slice();
|
110 |
|
111 | var keys = Object.keys(data);
|
112 | var keysLength = keys.length;
|
113 | if (keysLength) {
|
114 | for (var i = 0; i < urlParts.length; i++) {
|
115 | if (urlParts[i].charAt(0) === ':') {
|
116 | for (var j = 0; j < keysLength; j++) {
|
117 | if (urlParts[i] === ":" + keys[j]) {
|
118 |
|
119 | urlParts[i] = encodeURIComponent(data[keys[j]]);
|
120 | break;
|
121 | }
|
122 | }
|
123 | }
|
124 | }
|
125 | }
|
126 | }
|
127 | var requiresExplicitPrefix = true;
|
128 | if (navContainer.parent) {
|
129 | requiresExplicitPrefix = navContainer.parent && navContainer.parent.getAllChildNavs().length > 1;
|
130 | }
|
131 | else {
|
132 |
|
133 | requiresExplicitPrefix = app.getRootNavById(navContainer.id) && app.getRootNavs().length > 1;
|
134 | }
|
135 | return {
|
136 | id: urlParts.join('/'),
|
137 | name: configLink.name,
|
138 | component: configLink.component,
|
139 | loadChildren: configLink.loadChildren,
|
140 | data: data,
|
141 | defaultHistory: configLink.defaultHistory,
|
142 | navId: navContainer.name || navContainer.id,
|
143 | type: navContainer.getType(),
|
144 | secondaryId: navContainer.getSecondaryIdentifier(),
|
145 | requiresExplicitNavPrefix: requiresExplicitPrefix
|
146 | };
|
147 | };
|
148 | return UrlSerializer;
|
149 | }());
|
150 | export { UrlSerializer };
|
151 | function UrlSerializer_tsickle_Closure_declarations() {
|
152 |
|
153 | UrlSerializer.prototype.links;
|
154 |
|
155 | UrlSerializer.prototype._app;
|
156 | }
|
157 |
|
158 |
|
159 |
|
160 |
|
161 | export function formatUrlPart(name) {
|
162 | name = name.replace(URL_REPLACE_REG, '-');
|
163 | name = name.charAt(0).toLowerCase() + name.substring(1).replace(/[A-Z]/g, function (match) {
|
164 | return '-' + match.toLowerCase();
|
165 | });
|
166 | while (name.indexOf('--') > -1) {
|
167 | name = name.replace('--', '-');
|
168 | }
|
169 | if (name.charAt(0) === '-') {
|
170 | name = name.substring(1);
|
171 | }
|
172 | if (name.substring(name.length - 1) === '-') {
|
173 | name = name.substring(0, name.length - 1);
|
174 | }
|
175 | return encodeURIComponent(name);
|
176 | }
|
177 | export var isPartMatch = function (urlPart, configLinkPart) {
|
178 | if (isPresent(urlPart) && isPresent(configLinkPart)) {
|
179 | if (configLinkPart.charAt(0) === ':') {
|
180 | return true;
|
181 | }
|
182 | return (urlPart === configLinkPart);
|
183 | }
|
184 | return false;
|
185 | };
|
186 | export var createMatchedData = function (matchedUrlParts, link) {
|
187 | var data = null;
|
188 | for (var i = 0; i < link.segmentPartsLen; i++) {
|
189 | if (link.segmentParts[i].charAt(0) === ':') {
|
190 | data = data || {};
|
191 | data[link.segmentParts[i].substring(1)] = decodeURIComponent(matchedUrlParts[i]);
|
192 | }
|
193 | }
|
194 | return data;
|
195 | };
|
196 | export var findLinkByComponentData = function (links, component, instanceData) {
|
197 | var foundLink = null;
|
198 | var foundLinkDataMatches = -1;
|
199 | for (var i = 0; i < links.length; i++) {
|
200 | var link = links[i];
|
201 | if (link.component === component) {
|
202 |
|
203 |
|
204 | var dataMatches = 0;
|
205 | if (instanceData) {
|
206 | var instanceDataKeys = Object.keys(instanceData);
|
207 |
|
208 | for (var j = 0; j < instanceDataKeys.length; j++) {
|
209 | if (isPresent(link.dataKeys[instanceDataKeys[j]])) {
|
210 | dataMatches++;
|
211 | }
|
212 | }
|
213 | }
|
214 | else if (link.dataLen) {
|
215 |
|
216 | continue;
|
217 | }
|
218 | if (dataMatches >= foundLinkDataMatches) {
|
219 | foundLink = link;
|
220 | foundLinkDataMatches = dataMatches;
|
221 | }
|
222 | }
|
223 | }
|
224 | return foundLink;
|
225 | };
|
226 | export var normalizeLinks = function (links) {
|
227 | for (var i = 0, ilen = links.length; i < ilen; i++) {
|
228 | var link = links[i];
|
229 | if (isBlank(link.segment)) {
|
230 | link.segment = link.name;
|
231 | }
|
232 | link.dataKeys = {};
|
233 | link.segmentParts = link.segment.split('/');
|
234 | link.segmentPartsLen = link.segmentParts.length;
|
235 |
|
236 | link.staticLen = link.dataLen = 0;
|
237 | var stillCountingStatic = true;
|
238 | for (var j = 0; j < link.segmentPartsLen; j++) {
|
239 | if (link.segmentParts[j].charAt(0) === ':') {
|
240 | link.dataLen++;
|
241 | stillCountingStatic = false;
|
242 | link.dataKeys[link.segmentParts[j].substring(1)] = true;
|
243 | }
|
244 | else if (stillCountingStatic) {
|
245 | link.staticLen++;
|
246 | }
|
247 | }
|
248 | }
|
249 |
|
250 |
|
251 | return links.sort(sortConfigLinks);
|
252 | };
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 | function sortConfigLinks(a, b) {
|
259 |
|
260 | if (a.segmentPartsLen > b.segmentPartsLen) {
|
261 | return -1;
|
262 | }
|
263 | if (a.segmentPartsLen < b.segmentPartsLen) {
|
264 | return 1;
|
265 | }
|
266 |
|
267 | if (a.staticLen > b.staticLen) {
|
268 | return -1;
|
269 | }
|
270 | if (a.staticLen < b.staticLen) {
|
271 | return 1;
|
272 | }
|
273 |
|
274 | if (a.dataLen < b.dataLen) {
|
275 | return -1;
|
276 | }
|
277 | if (a.dataLen > b.dataLen) {
|
278 | return 1;
|
279 | }
|
280 | return 0;
|
281 | }
|
282 | var URL_REPLACE_REG = /\s+|\?|\!|\$|\,|\.|\+|\"|\'|\*|\^|\||\/|\\|\[|\]|#|%|`|>|<|;|:|@|&|=/g;
|
283 |
|
284 |
|
285 |
|
286 | export var DeepLinkConfigToken = new OpaqueToken('USERLINKS');
|
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 | export function setupUrlSerializer(app, userDeepLinkConfig) {
|
293 | return new UrlSerializer(app, userDeepLinkConfig);
|
294 | }
|
295 |
|
296 |
|
297 |
|
298 |
|
299 | export function navGroupStringtoObjects(navGroupStrings) {
|
300 |
|
301 | return navGroupStrings.map(function (navGroupString) {
|
302 | var sections = navGroupString.split('/');
|
303 | if (sections[0] === 'nav') {
|
304 | return {
|
305 | type: 'nav',
|
306 | navId: sections[1],
|
307 | niceId: sections[1],
|
308 | secondaryId: null,
|
309 | segmentPieces: sections.splice(2)
|
310 | };
|
311 | }
|
312 | else if (sections[0] === 'tabs') {
|
313 | return {
|
314 | type: 'tabs',
|
315 | navId: sections[1],
|
316 | niceId: sections[1],
|
317 | secondaryId: sections[2],
|
318 | segmentPieces: sections.splice(3)
|
319 | };
|
320 | }
|
321 | return {
|
322 | type: null,
|
323 | navId: null,
|
324 | niceId: null,
|
325 | secondaryId: null,
|
326 | segmentPieces: sections
|
327 | };
|
328 | });
|
329 | }
|
330 |
|
331 |
|
332 |
|
333 |
|
334 | export function urlToNavGroupStrings(url) {
|
335 | var tokens = url.split('/');
|
336 | var keywordIndexes = [];
|
337 | for (var i = 0; i < tokens.length; i++) {
|
338 | if (i !== 0 && (tokens[i] === 'nav' || tokens[i] === 'tabs')) {
|
339 | keywordIndexes.push(i);
|
340 | }
|
341 | }
|
342 |
|
343 | keywordIndexes.push(tokens.length);
|
344 | var groupings = [];
|
345 | var activeKeywordIndex = 0;
|
346 | var tmpArray = [];
|
347 | for (var i = 0; i < tokens.length; i++) {
|
348 | if (i >= keywordIndexes[activeKeywordIndex]) {
|
349 | groupings.push(tmpArray.join('/'));
|
350 | tmpArray = [];
|
351 | activeKeywordIndex++;
|
352 | }
|
353 | tmpArray.push(tokens[i]);
|
354 | }
|
355 |
|
356 | groupings.push(tmpArray.join('/'));
|
357 | return groupings;
|
358 | }
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 | export function convertUrlToSegments(app, url, navLinks) {
|
366 | var pairs = convertUrlToDehydratedSegments(url, navLinks);
|
367 | return hydrateSegmentsWithNav(app, pairs);
|
368 | }
|
369 |
|
370 |
|
371 |
|
372 |
|
373 |
|
374 | export function convertUrlToDehydratedSegments(url, navLinks) {
|
375 | var navGroupStrings = urlToNavGroupStrings(url);
|
376 | var navGroups = navGroupStringtoObjects(navGroupStrings);
|
377 | return getSegmentsFromNavGroups(navGroups, navLinks);
|
378 | }
|
379 |
|
380 |
|
381 |
|
382 |
|
383 |
|
384 | export function hydrateSegmentsWithNav(app, dehydratedSegmentPairs) {
|
385 | var segments = [];
|
386 | for (var i = 0; i < dehydratedSegmentPairs.length; i++) {
|
387 | var navs = getNavFromNavGroup(dehydratedSegmentPairs[i].navGroup, app);
|
388 |
|
389 | for (var _i = 0, _a = dehydratedSegmentPairs[i].segments; _i < _a.length; _i++) {
|
390 | var dehydratedSegment = _a[_i];
|
391 | if (navs.length === 1) {
|
392 | segments.push(hydrateSegment(dehydratedSegment, navs[0]));
|
393 | navs = navs[0].getActiveChildNavs();
|
394 | }
|
395 | else if (navs.length > 1) {
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 | segments.push(hydrateSegment(dehydratedSegment, navs[navs.length - 1]));
|
403 | navs = navs[navs.length - 1].getActiveChildNavs();
|
404 | }
|
405 | else {
|
406 | break;
|
407 | }
|
408 | }
|
409 | }
|
410 | return segments;
|
411 | }
|
412 |
|
413 |
|
414 |
|
415 |
|
416 |
|
417 | export function getNavFromNavGroup(navGroup, app) {
|
418 | if (navGroup.navId) {
|
419 | var rootNav = app.getNavByIdOrName(navGroup.navId);
|
420 | if (rootNav) {
|
421 | return [rootNav];
|
422 | }
|
423 | return [];
|
424 | }
|
425 |
|
426 |
|
427 | return app.getRootNavs();
|
428 | }
|
429 |
|
430 |
|
431 |
|
432 |
|
433 |
|
434 | export function getSegmentsFromNavGroups(navGroups, navLinks) {
|
435 | var pairs = [];
|
436 | var usedNavLinks = new Set();
|
437 | for (var _i = 0, navGroups_1 = navGroups; _i < navGroups_1.length; _i++) {
|
438 | var navGroup = navGroups_1[_i];
|
439 | var segments = [];
|
440 | var segmentPieces = navGroup.segmentPieces.concat([]);
|
441 | for (var i = segmentPieces.length; i >= 0; i--) {
|
442 | var created = false;
|
443 | for (var j = 0; j < i; j++) {
|
444 | var startIndex = i - j - 1;
|
445 | var endIndex = i;
|
446 | var subsetOfUrl = segmentPieces.slice(startIndex, endIndex);
|
447 | for (var _a = 0, navLinks_1 = navLinks; _a < navLinks_1.length; _a++) {
|
448 | var navLink = navLinks_1[_a];
|
449 | if (!usedNavLinks.has(navLink.name)) {
|
450 | var segment = getSegmentsFromUrlPieces(subsetOfUrl, navLink);
|
451 | if (segment) {
|
452 | i = startIndex + 1;
|
453 | usedNavLinks.add(navLink.name);
|
454 | created = true;
|
455 |
|
456 | segments.push(segment);
|
457 |
|
458 | for (var k = startIndex; k < endIndex; k++) {
|
459 | segmentPieces[k] = null;
|
460 | }
|
461 | break;
|
462 | }
|
463 | }
|
464 | }
|
465 | if (created) {
|
466 | break;
|
467 | }
|
468 | }
|
469 | if (!created && segmentPieces[i - 1]) {
|
470 |
|
471 | segments.push({
|
472 | id: null,
|
473 | name: null,
|
474 | secondaryId: segmentPieces[i - 1],
|
475 | component: null,
|
476 | loadChildren: null,
|
477 | data: null,
|
478 | defaultHistory: null
|
479 | });
|
480 | }
|
481 | }
|
482 |
|
483 |
|
484 | var orderedSegments = segments.reverse();
|
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 | for (var i = 0; i < orderedSegments.length; i++) {
|
493 | if (orderedSegments[i].secondaryId && !orderedSegments[i].id && ((i + 1) <= orderedSegments.length - 1)) {
|
494 | orderedSegments[i + 1].secondaryId = orderedSegments[i].secondaryId;
|
495 | orderedSegments[i] = null;
|
496 | }
|
497 | }
|
498 | var cleanedSegments = segments.filter(function (segment) { return !!segment; });
|
499 |
|
500 | if (navGroup.secondaryId && segments.length) {
|
501 | cleanedSegments[0].secondaryId = navGroup.secondaryId;
|
502 | }
|
503 | pairs.push({
|
504 | navGroup: navGroup,
|
505 | segments: cleanedSegments
|
506 | });
|
507 | }
|
508 | return pairs;
|
509 | }
|
510 |
|
511 |
|
512 |
|
513 |
|
514 |
|
515 | export function getSegmentsFromUrlPieces(urlSections, navLink) {
|
516 | if (navLink.segmentPartsLen !== urlSections.length) {
|
517 | return null;
|
518 | }
|
519 | for (var i = 0; i < urlSections.length; i++) {
|
520 | if (!isPartMatch(urlSections[i], navLink.segmentParts[i])) {
|
521 |
|
522 | return null;
|
523 | }
|
524 | }
|
525 | return {
|
526 | id: urlSections.join('/'),
|
527 | name: navLink.name,
|
528 | component: navLink.component,
|
529 | loadChildren: navLink.loadChildren,
|
530 | data: createMatchedData(urlSections, navLink),
|
531 | defaultHistory: navLink.defaultHistory
|
532 | };
|
533 | }
|
534 |
|
535 |
|
536 |
|
537 |
|
538 |
|
539 | export function hydrateSegment(segment, nav) {
|
540 | var hydratedSegment = (Object.assign({}, segment));
|
541 | hydratedSegment.type = nav.getType();
|
542 | hydratedSegment.navId = nav.name || nav.id;
|
543 |
|
544 | hydratedSegment.secondaryId = segment.secondaryId;
|
545 | return hydratedSegment;
|
546 | }
|
547 |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 | export function getNonHydratedSegmentIfLinkAndUrlMatch(urlChunks, navLink) {
|
553 | var allSegmentsMatch = true;
|
554 | for (var i = 0; i < urlChunks.length; i++) {
|
555 | if (!isPartMatch(urlChunks[i], navLink.segmentParts[i])) {
|
556 | allSegmentsMatch = false;
|
557 | break;
|
558 | }
|
559 | }
|
560 | if (allSegmentsMatch) {
|
561 | return {
|
562 | id: navLink.segmentParts.join('/'),
|
563 | name: navLink.name,
|
564 | component: navLink.component,
|
565 | loadChildren: navLink.loadChildren,
|
566 | data: createMatchedData(urlChunks, navLink),
|
567 | defaultHistory: navLink.defaultHistory
|
568 | };
|
569 | }
|
570 | return null;
|
571 | }
|
572 |
|
\ | No newline at end of file |