X Tutup
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions modules/angular2/src/facade/collection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,27 @@ class ListWrapper {
if (end == null) return len;
return end < 0 ? max(len + end, 0) : min(end, len);
}


static maximum(List l, fn(item)) {
if (l.length == 0) {
return null;
}
var solution = null;
var maxValue = double.NEGATIVE_INFINITY;
for (var index = 0; index < l.length; index++) {
var candidate = l[index];
if (candidate == null) {
continue;
}
var candidateValue = fn(candidate);
if (candidateValue > maxValue) {
solution = candidate;
maxValue = candidateValue;
}
}
return solution;
}
}

bool isListLikeIterable(obj) => obj is Iterable;
Expand Down
22 changes: 21 additions & 1 deletion modules/angular2/src/facade/collection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {isJsObject, global, isPresent, isArray} from 'angular2/src/facade/lang';
import {isJsObject, global, isPresent, isBlank, isArray} from 'angular2/src/facade/lang';

export var List = global.Array;
export var Map = global.Map;
Expand Down Expand Up @@ -266,6 +266,26 @@ export class ListWrapper {
}
static toString<T>(l: List<T>): string { return l.toString(); }
static toJSON<T>(l: List<T>): string { return JSON.stringify(l); }

static maximum<T>(list: List<T>, predicate: (T) => number): T {
if (list.length == 0) {
return null;
}
var solution = null;
var maxValue = -Infinity;
for (var index = 0; index < list.length; index++) {
var candidate = list[index];
if (isBlank(candidate)) {
continue;
}
var candidateValue = predicate(candidate);
if (candidateValue > maxValue) {
solution = candidate;
maxValue = candidateValue;
}
}
return solution;
}
}

export function isListLikeIterable(obj: any): boolean {
Expand Down
34 changes: 21 additions & 13 deletions modules/angular2/src/router/path_recognizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,17 +216,17 @@ export class PathRecognizer {
var captured = [];

for (var i = 0; i < this._segments.length; i += 1) {
if (isBlank(nextSegment)) {
return null;
}
currentSegment = nextSegment;

var segment = this._segments[i];

currentSegment = nextSegment;
if (segment instanceof ContinuationSegment) {
break;
}

if (isBlank(currentSegment)) {
return null;
}

captured.push(currentSegment.path);

// the star segment consumes all of the remaining URL, including matrix params
Expand All @@ -251,18 +251,26 @@ export class PathRecognizer {

var urlPath = captured.join('/');

// If this is the root component, read query params. Otherwise, read matrix params.
var paramsSegment = beginningSegment instanceof RootUrl ? beginningSegment : currentSegment;
var auxiliary;
var instruction: ComponentInstruction;
if (isPresent(currentSegment)) {
// If this is the root component, read query params. Otherwise, read matrix params.
var paramsSegment = beginningSegment instanceof RootUrl ? beginningSegment : currentSegment;

var allParams = isPresent(paramsSegment.params) ?
StringMapWrapper.merge(paramsSegment.params, positionalParams) :
positionalParams;

var allParams = isPresent(paramsSegment.params) ?
StringMapWrapper.merge(paramsSegment.params, positionalParams) :
positionalParams;
var urlParams = serializeParams(paramsSegment.params);
var urlParams = serializeParams(paramsSegment.params);

var instruction = new ComponentInstruction(urlPath, urlParams, this, allParams);
instruction = new ComponentInstruction(urlPath, urlParams, this, allParams);

return new PathMatch(instruction, nextSegment, currentSegment.auxiliary);
auxiliary = currentSegment.auxiliary;
} else {
instruction = new ComponentInstruction(urlPath, [], this, positionalParams);
auxiliary = [];
}
return new PathMatch(instruction, nextSegment, auxiliary);
}


Expand Down
53 changes: 37 additions & 16 deletions modules/angular2/src/router/route_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
import {reflector} from 'angular2/src/reflection/reflection';
import {Injectable} from 'angular2/di';
import {normalizeRouteConfig} from './route_config_nomalizer';
import {parser, Url} from './url_parser';
import {parser, Url, pathSegmentsToUrl} from './url_parser';

var _resolveToNull = PromiseWrapper.resolve(null);

Expand Down Expand Up @@ -236,35 +236,56 @@ export class RouteRegistry {
componentCursor = response.componentType;
}

var instruction = null;
var instruction: Instruction = this._generateRedirects(componentCursor);


while (segments.length > 0) {
instruction = new Instruction(segments.pop(), instruction, {});
}

return instruction;
}

// if the child includes a redirect like : "/" -> "/something",
// we want to honor that redirection when creating the link
private _generateRedirects(componentCursor: Type): Instruction {
if (isBlank(componentCursor)) {
return null;
}
var componentRecognizer = this._rules.get(componentCursor);
if (isBlank(componentRecognizer)) {
return null;
}

for (let i = 0; i < componentRecognizer.redirects.length; i += 1) {
let redirect = componentRecognizer.redirects[i];

// we only handle redirecting from an empty segment
if (redirect.segments.length == 1 && redirect.segments[0] == '') {
var toSegments = pathSegmentsToUrl(redirect.toSegments);
var matches = componentRecognizer.recognize(toSegments);
var primaryInstruction =
ListWrapper.maximum(matches, (match: PathMatch) => match.instruction.specificity);

if (isPresent(primaryInstruction)) {
var child = this._generateRedirects(primaryInstruction.instruction.componentType);
return new Instruction(primaryInstruction.instruction, child, {});
}
return null;
}
}

return null;
}
}


/*
* Given a list of instructions, returns the most specific instruction
*/
function mostSpecific(instructions: List<PrimaryInstruction>): PrimaryInstruction {
if (instructions.length == 0) {
return null;
}
var mostSpecificSolution = instructions[0];
for (var solutionIndex = 1; solutionIndex < instructions.length; solutionIndex++) {
var solution: PrimaryInstruction = instructions[solutionIndex];
if (isBlank(solution)) {
continue;
}
if (solution.component.specificity > mostSpecificSolution.component.specificity) {
mostSpecificSolution = solution;
}
}
return mostSpecificSolution;
return ListWrapper.maximum(
instructions, (instruction: PrimaryInstruction) => instruction.component.specificity);
}

function assertTerminalComponent(component, path) {
Expand Down
8 changes: 8 additions & 0 deletions modules/angular2/src/router/url_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ export class RootUrl extends Url {
}
}

export function pathSegmentsToUrl(pathSegments: List<string>): Url {
var url = new Url(pathSegments[pathSegments.length - 1]);
for (var i = pathSegments.length - 2; i >= 0; i -= 1) {
url = new Url(pathSegments[i], url);
}
return url;
}

var SEGMENT_RE = RegExpWrapper.create('^[^\\/\\(\\)\\?;=&]+');
function matchUrlSegment(str: string): string {
var match = RegExpWrapper.firstMatch(SEGMENT_RE, str);
Expand Down
14 changes: 14 additions & 0 deletions modules/angular2/test/facade/collection_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,20 @@ export function main() {
it('should respect the startIndex parameter',
() => { expect(ListWrapper.indexOf(l, 1, 1)).toEqual(-1); });
});

describe('maximum', () => {
it('should return the maximal element',
() => { expect(ListWrapper.maximum([1, 2, 3, 4], x => x)).toEqual(4); });

it('should ignore null values',
() => { expect(ListWrapper.maximum([null, 2, 3, null], x => x)).toEqual(3); });

it('should use the provided function to determine maximum',
() => { expect(ListWrapper.maximum([1, 2, 3, 4], x => -x)).toEqual(1); });

it('should return null for an empty list',
() => { expect(ListWrapper.maximum([], x => x)).toEqual(null); });
});
});

describe('StringMapWrapper', () => {
Expand Down
48 changes: 47 additions & 1 deletion modules/angular2/test/router/route_registry_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {Type} from 'angular2/src/facade/lang';

import {RouteRegistry} from 'angular2/src/router/route_registry';
import {RouteConfig, Route, AuxRoute, AsyncRoute} from 'angular2/src/router/route_config_decorator';
import {
RouteConfig,
Route,
Redirect,
AuxRoute,
AsyncRoute
} from 'angular2/src/router/route_config_decorator';
import {stringifyInstruction} from 'angular2/src/router/instruction';
import {IS_DART} from '../platform';

Expand Down Expand Up @@ -45,6 +51,24 @@ export function main() {
.toEqual('second');
});

it('should generate URLs that account for redirects', () => {
registry.config(
RootHostCmp,
new Route({path: '/first/...', component: DummyParentRedirectCmp, as: 'firstCmp'}));

expect(stringifyInstruction(registry.generate(['firstCmp'], RootHostCmp)))
.toEqual('first/second');
});

it('should generate URLs in a hierarchy of redirects', () => {
registry.config(
RootHostCmp,
new Route({path: '/first/...', component: DummyMultipleRedirectCmp, as: 'firstCmp'}));

expect(stringifyInstruction(registry.generate(['firstCmp'], RootHostCmp)))
.toEqual('first/second/third');
});

it('should generate URLs with params', () => {
registry.config(
RootHostCmp,
Expand Down Expand Up @@ -255,6 +279,28 @@ class DummyAsyncCmp {
class DummyCmpA {}
class DummyCmpB {}

@RouteConfig([
new Redirect({path: '/', redirectTo: '/third'}),
new Route({path: '/third', component: DummyCmpB, as: 'thirdCmp'})
])
class DummyRedirectCmp {
}


@RouteConfig([
new Redirect({path: '/', redirectTo: '/second'}),
new Route({path: '/second/...', component: DummyRedirectCmp, as: 'secondCmp'})
])
class DummyMultipleRedirectCmp {
}

@RouteConfig([
new Redirect({path: '/', redirectTo: '/second'}),
new Route({path: '/second', component: DummyCmpB, as: 'secondCmp'})
])
class DummyParentRedirectCmp {
}

@RouteConfig([new Route({path: '/second', component: DummyCmpB, as: 'secondCmp'})])
class DummyParentCmp {
}
Expand Down
X Tutup