refactor(router): rename candidate into snapshot

This commit is contained in:
vsavkin 2016-06-01 17:55:21 -07:00
parent c5cca8e098
commit 243612e36d
7 changed files with 89 additions and 62 deletions

View File

@ -1,18 +1,18 @@
import { RouterStateCandidate, ActivatedRouteCandidate, RouterState, ActivatedRoute } from './router_state'; import { RouterStateSnapshot, ActivatedRouteSnapshot, RouterState, ActivatedRoute } from './router_state';
import { TreeNode } from './utils/tree'; import { TreeNode } from './utils/tree';
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export function createRouterState(curr: RouterStateCandidate, prevState: RouterState): RouterState { export function createRouterState(curr: RouterStateSnapshot, prevState: RouterState): RouterState {
const root = createNode(curr._root, prevState ? prevState._root : null); const root = createNode(curr._root, prevState ? prevState._root : null);
const queryParams = prevState ? prevState.queryParams : new BehaviorSubject(curr.queryParams); const queryParams = prevState ? prevState.queryParams : new BehaviorSubject(curr.queryParams);
const fragment = prevState ? prevState.fragment : new BehaviorSubject(curr.fragment); const fragment = prevState ? prevState.fragment : new BehaviorSubject(curr.fragment);
return new RouterState(root, queryParams, fragment, curr); return new RouterState(root, queryParams, fragment, curr);
} }
function createNode(curr:TreeNode<ActivatedRouteCandidate>, prevState?:TreeNode<ActivatedRoute>):TreeNode<ActivatedRoute> { function createNode(curr:TreeNode<ActivatedRouteSnapshot>, prevState?:TreeNode<ActivatedRoute>):TreeNode<ActivatedRoute> {
if (prevState && equalRouteCandidates(prevState.value.candidate, curr.value)) { if (prevState && equalRouteSnapshots(prevState.value.snapshot, curr.value)) {
const value = prevState.value; const value = prevState.value;
value.candidate = curr.value; value.snapshot = curr.value;
const children = createOrReuseChildren(curr, prevState); const children = createOrReuseChildren(curr, prevState);
return new TreeNode<ActivatedRoute>(value, children); return new TreeNode<ActivatedRoute>(value, children);
@ -24,9 +24,9 @@ function createNode(curr:TreeNode<ActivatedRouteCandidate>, prevState?:TreeNode<
} }
} }
function createOrReuseChildren(curr:TreeNode<ActivatedRouteCandidate>, prevState:TreeNode<ActivatedRoute>) { function createOrReuseChildren(curr:TreeNode<ActivatedRouteSnapshot>, prevState:TreeNode<ActivatedRoute>) {
return curr.children.map(child => { return curr.children.map(child => {
const index = prevState.children.findIndex(p => equalRouteCandidates(p.value.candidate, child.value)); const index = prevState.children.findIndex(p => equalRouteSnapshots(p.value.snapshot, child.value));
if (index >= 0) { if (index >= 0) {
return createNode(child, prevState.children[index]); return createNode(child, prevState.children[index]);
} else { } else {
@ -35,10 +35,10 @@ function createOrReuseChildren(curr:TreeNode<ActivatedRouteCandidate>, prevState
}); });
} }
function createActivatedRoute(c:ActivatedRouteCandidate) { function createActivatedRoute(c:ActivatedRouteSnapshot) {
return new ActivatedRoute(new BehaviorSubject(c.urlSegments), new BehaviorSubject(c.params), c.outlet, c.component, c); return new ActivatedRoute(new BehaviorSubject(c.urlSegments), new BehaviorSubject(c.params), c.outlet, c.component, c);
} }
function equalRouteCandidates(a: ActivatedRouteCandidate, b: ActivatedRouteCandidate): boolean { function equalRouteSnapshots(a: ActivatedRouteSnapshot, b: ActivatedRouteSnapshot): boolean {
return a._routeConfig === b._routeConfig; return a._routeConfig === b._routeConfig;
} }

View File

@ -1,28 +1,28 @@
import { UrlTree, UrlSegment } from './url_tree'; import { UrlTree, UrlSegment } from './url_tree';
import { flatten, first, merge } from './utils/collection'; import { flatten, first, merge } from './utils/collection';
import { TreeNode } from './utils/tree'; import { TreeNode } from './utils/tree';
import { RouterStateCandidate, ActivatedRouteCandidate } from './router_state'; import { RouterStateSnapshot, ActivatedRouteSnapshot } from './router_state';
import { Params, PRIMARY_OUTLET } from './shared'; import { Params, PRIMARY_OUTLET } from './shared';
import { RouterConfig, Route } from './config'; import { RouterConfig, Route } from './config';
import { Type } from '@angular/core'; import { Type } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
export function recognize(rootComponentType: Type, config: RouterConfig, url: UrlTree): Observable<RouterStateCandidate> { export function recognize(rootComponentType: Type, config: RouterConfig, url: UrlTree): Observable<RouterStateSnapshot> {
try { try {
const match = new MatchResult(rootComponentType, config, [url.root], {}, url._root.children, [], PRIMARY_OUTLET, null); const match = new MatchResult(rootComponentType, config, [url.root], {}, url._root.children, [], PRIMARY_OUTLET, null);
const roots = constructActivatedRoute(match); const roots = constructActivatedRoute(match);
const res = new RouterStateCandidate(roots[0], url.queryParameters, url.fragment); const res = new RouterStateSnapshot(roots[0], url.queryParameters, url.fragment);
return new Observable<RouterStateCandidate>(obs => { return new Observable<RouterStateSnapshot>(obs => {
obs.next(res); obs.next(res);
obs.complete(); obs.complete();
}); });
} catch(e) { } catch(e) {
return new Observable<RouterStateCandidate>(obs => obs.error(e)); return new Observable<RouterStateSnapshot>(obs => obs.error(e));
} }
} }
function constructActivatedRoute(match: MatchResult): TreeNode<ActivatedRouteCandidate>[] { function constructActivatedRoute(match: MatchResult): TreeNode<ActivatedRouteSnapshot>[] {
const activatedRoute = createActivatedRouteCandidate(match); const activatedRoute = createActivatedRouteSnapshot(match);
if (match.leftOverUrl.length > 0) { if (match.leftOverUrl.length > 0) {
const children = recognizeMany(match.children, match.leftOverUrl); const children = recognizeMany(match.children, match.leftOverUrl);
checkOutletNameUniqueness(children); checkOutletNameUniqueness(children);
@ -31,21 +31,21 @@ function constructActivatedRoute(match: MatchResult): TreeNode<ActivatedRouteCan
if (b.value.outlet === PRIMARY_OUTLET) return 1; if (b.value.outlet === PRIMARY_OUTLET) return 1;
return a.value.outlet.localeCompare(b.value.outlet) return a.value.outlet.localeCompare(b.value.outlet)
}); });
return [new TreeNode<ActivatedRouteCandidate>(activatedRoute, children)]; return [new TreeNode<ActivatedRouteSnapshot>(activatedRoute, children)];
} else { } else {
return [new TreeNode<ActivatedRouteCandidate>(activatedRoute, [])]; return [new TreeNode<ActivatedRouteSnapshot>(activatedRoute, [])];
} }
} }
function recognizeMany(config: Route[], urls: TreeNode<UrlSegment>[]): TreeNode<ActivatedRouteCandidate>[] { function recognizeMany(config: Route[], urls: TreeNode<UrlSegment>[]): TreeNode<ActivatedRouteSnapshot>[] {
return flatten(urls.map(url => recognizeOne(config, url))); return flatten(urls.map(url => recognizeOne(config, url)));
} }
function createActivatedRouteCandidate(match: MatchResult): ActivatedRouteCandidate { function createActivatedRouteSnapshot(match: MatchResult): ActivatedRouteSnapshot {
return new ActivatedRouteCandidate(match.consumedUrlSegments, match.parameters, match.outlet, match.component, match.route); return new ActivatedRouteSnapshot(match.consumedUrlSegments, match.parameters, match.outlet, match.component, match.route);
} }
function recognizeOne(config: Route[], url: TreeNode<UrlSegment>): TreeNode<ActivatedRouteCandidate>[] { function recognizeOne(config: Route[], url: TreeNode<UrlSegment>): TreeNode<ActivatedRouteSnapshot>[] {
const m = match(config, url); const m = match(config, url);
const primary = constructActivatedRoute(m); const primary = constructActivatedRoute(m);
const secondary = recognizeMany(config, m.secondary); const secondary = recognizeMany(config, m.secondary);
@ -54,7 +54,7 @@ function recognizeOne(config: Route[], url: TreeNode<UrlSegment>): TreeNode<Acti
return res; return res;
} }
function checkOutletNameUniqueness(nodes: TreeNode<ActivatedRouteCandidate>[]): TreeNode<ActivatedRouteCandidate>[] { function checkOutletNameUniqueness(nodes: TreeNode<ActivatedRouteSnapshot>[]): TreeNode<ActivatedRouteSnapshot>[] {
let names = {}; let names = {};
nodes.forEach(n => { nodes.forEach(n => {
let routeWithSameOutletName = names[n.value.outlet]; let routeWithSameOutletName = names[n.value.outlet];

View File

@ -1,4 +1,4 @@
import { RouterStateCandidate, ActivatedRouteCandidate } from './router_state'; import { RouterStateSnapshot, ActivatedRouteSnapshot } from './router_state';
import { TreeNode } from './utils/tree'; import { TreeNode } from './utils/tree';
import { ComponentResolver } from '@angular/core'; import { ComponentResolver } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
@ -7,11 +7,11 @@ import {forkJoin} from 'rxjs/observable/forkJoin';
import {fromPromise} from 'rxjs/observable/fromPromise'; import {fromPromise} from 'rxjs/observable/fromPromise';
import 'rxjs/add/operator/toPromise'; import 'rxjs/add/operator/toPromise';
export function resolve(resolver: ComponentResolver, state: RouterStateCandidate): Observable<RouterStateCandidate> { export function resolve(resolver: ComponentResolver, state: RouterStateSnapshot): Observable<RouterStateSnapshot> {
return resolveNode(resolver, state._root).map(_ => state); return resolveNode(resolver, state._root).map(_ => state);
} }
function resolveNode(resolver: ComponentResolver, node: TreeNode<ActivatedRouteCandidate>): Observable<any> { function resolveNode(resolver: ComponentResolver, node: TreeNode<ActivatedRouteSnapshot>): Observable<any> {
if (node.children.length === 0) { if (node.children.length === 0) {
return fromPromise(resolver.resolveComponent(<any>node.value.component).then(factory => { return fromPromise(resolver.resolveComponent(<any>node.value.component).then(factory => {
node.value._resolvedComponentFactory = factory; node.value._resolvedComponentFactory = factory;

View File

@ -8,7 +8,7 @@ import { createRouterState } from './create_router_state';
import { TreeNode } from './utils/tree'; import { TreeNode } from './utils/tree';
import { UrlTree, createEmptyUrlTree } from './url_tree'; import { UrlTree, createEmptyUrlTree } from './url_tree';
import { PRIMARY_OUTLET, Params } from './shared'; import { PRIMARY_OUTLET, Params } from './shared';
import { createEmptyState, RouterState, RouterStateCandidate, ActivatedRoute, ActivatedRouteCandidate} from './router_state'; import { createEmptyState, RouterState, RouterStateSnapshot, ActivatedRoute, ActivatedRouteSnapshot} from './router_state';
import { RouterConfig } from './config'; import { RouterConfig } from './config';
import { RouterOutlet } from './directives/router_outlet'; import { RouterOutlet } from './directives/router_outlet';
import { createUrlTree } from './create_url_tree'; import { createUrlTree } from './create_url_tree';
@ -162,17 +162,17 @@ export class Router {
private runNavigate(url:UrlTree, pop?:boolean):Observable<any> { private runNavigate(url:UrlTree, pop?:boolean):Observable<any> {
let state; let state;
const r = recognize(this.rootComponentType, this.config, url).mergeMap((newRouterStateCandidate) => { const r = recognize(this.rootComponentType, this.config, url).mergeMap((newRouterStateSnapshot) => {
return resolve(this.resolver, newRouterStateCandidate); return resolve(this.resolver, newRouterStateSnapshot);
}).map((routerStateCandidate) => { }).map((routerStateSnapshot) => {
return createRouterState(routerStateCandidate, this.currentRouterState); return createRouterState(routerStateSnapshot, this.currentRouterState);
}).map((newState:RouterState) => { }).map((newState:RouterState) => {
state = newState; state = newState;
}).mergeMap(_ => { }).mergeMap(_ => {
return new GuardChecks(state.candidate, this.currentRouterState.candidate, this.injector).check(this.outletMap); return new GuardChecks(state.snapshot, this.currentRouterState.snapshot, this.injector).check(this.outletMap);
}); });
r.subscribe((shouldActivate) => { r.subscribe((shouldActivate) => {
@ -192,7 +192,7 @@ export class Router {
class GuardChecks { class GuardChecks {
private checks = []; private checks = [];
constructor(private future: RouterStateCandidate, private curr: RouterStateCandidate, private injector: Injector) {} constructor(private future: RouterStateSnapshot, private curr: RouterStateSnapshot, private injector: Injector) {}
check(parentOutletMap: RouterOutletMap): Observable<boolean> { check(parentOutletMap: RouterOutletMap): Observable<boolean> {
const futureRoot = this.future._root; const futureRoot = this.future._root;
@ -202,8 +202,8 @@ class GuardChecks {
return forkJoin(this.checks.map(s => this.runCanActivate(s))).map(and); return forkJoin(this.checks.map(s => this.runCanActivate(s))).map(and);
} }
private traverseChildRoutes(futureNode: TreeNode<ActivatedRouteCandidate>, private traverseChildRoutes(futureNode: TreeNode<ActivatedRouteSnapshot>,
currNode: TreeNode<ActivatedRouteCandidate> | null, currNode: TreeNode<ActivatedRouteSnapshot> | null,
outletMap: RouterOutletMap): void { outletMap: RouterOutletMap): void {
const prevChildren = nodeChildrenAsMap(currNode); const prevChildren = nodeChildrenAsMap(currNode);
futureNode.children.forEach(c => { futureNode.children.forEach(c => {
@ -213,7 +213,7 @@ class GuardChecks {
forEach(prevChildren, (v, k) => this.deactivateOutletAndItChildren(outletMap._outlets[k])); forEach(prevChildren, (v, k) => this.deactivateOutletAndItChildren(outletMap._outlets[k]));
} }
traverseRoutes(futureNode: TreeNode<ActivatedRouteCandidate>, currNode: TreeNode<ActivatedRouteCandidate> | null, traverseRoutes(futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot> | null,
parentOutletMap: RouterOutletMap): void { parentOutletMap: RouterOutletMap): void {
const future = futureNode.value; const future = futureNode.value;
const curr = currNode ? currNode.value : null; const curr = currNode ? currNode.value : null;
@ -232,7 +232,7 @@ class GuardChecks {
private deactivateOutletAndItChildren(outlet: RouterOutlet): void {} private deactivateOutletAndItChildren(outlet: RouterOutlet): void {}
private runCanActivate(future: ActivatedRouteCandidate): Observable<boolean> { private runCanActivate(future: ActivatedRouteSnapshot): Observable<boolean> {
const canActivate = future._routeConfig ? future._routeConfig.canActivate : null; const canActivate = future._routeConfig ? future._routeConfig.canActivate : null;
if (!canActivate || canActivate.length === 0) return of(true); if (!canActivate || canActivate.length === 0) return of(true);
return forkJoin(canActivate.map(c => { return forkJoin(canActivate.map(c => {
@ -286,7 +286,7 @@ class ActivateRoutes {
{provide: ActivatedRoute, useValue: future}, {provide: ActivatedRoute, useValue: future},
{provide: RouterOutletMap, useValue: outletMap} {provide: RouterOutletMap, useValue: outletMap}
]); ]);
outlet.activate(future.candidate._resolvedComponentFactory, resolved, outletMap); outlet.activate(future.snapshot._resolvedComponentFactory, resolved, outletMap);
pushValues(future); pushValues(future);
} }
@ -299,19 +299,19 @@ class ActivateRoutes {
} }
function pushValues(route: ActivatedRoute): void { function pushValues(route: ActivatedRoute): void {
if (!shallowEqual(route.candidate.params, (<any>route.params).value)) { if (!shallowEqual(route.snapshot.params, (<any>route.params).value)) {
(<any>route.urlSegments).next(route.candidate.urlSegments); (<any>route.urlSegments).next(route.snapshot.urlSegments);
(<any>route.params).next(route.candidate.params); (<any>route.params).next(route.snapshot.params);
} }
} }
function pushQueryParamsAndFragment(state: RouterState): void { function pushQueryParamsAndFragment(state: RouterState): void {
if (!shallowEqual(state.candidate.queryParams, (<any>state.queryParams).value)) { if (!shallowEqual(state.snapshot.queryParams, (<any>state.queryParams).value)) {
(<any>state.queryParams).next(state.candidate.queryParams); (<any>state.queryParams).next(state.snapshot.queryParams);
} }
if (state.candidate.fragment !== (<any>state.fragment).value) { if (state.snapshot.fragment !== (<any>state.fragment).value) {
(<any>state.fragment).next(state.candidate.fragment); (<any>state.fragment).next(state.snapshot.fragment);
} }
} }

View File

@ -7,7 +7,7 @@ import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Type, ComponentFactory } from '@angular/core'; import { Type, ComponentFactory } from '@angular/core';
/** /**
* The state of the router at a particular moment in time. * The state of the router.
* *
* ### Usage * ### Usage
* *
@ -22,32 +22,33 @@ import { Type, ComponentFactory } from '@angular/core';
* ``` * ```
*/ */
export class RouterState extends Tree<ActivatedRoute> { export class RouterState extends Tree<ActivatedRoute> {
constructor(root: TreeNode<ActivatedRoute>, public queryParams: Observable<Params>, public fragment: Observable<string>, public candidate: RouterStateCandidate) { constructor(root: TreeNode<ActivatedRoute>, public queryParams: Observable<Params>, public fragment: Observable<string>, public snapshot: RouterStateSnapshot) {
super(root); super(root);
} }
} }
export function createEmptyState(rootComponent: Type): RouterState { export function createEmptyState(rootComponent: Type): RouterState {
const candidate = createEmptyStateCandidate(rootComponent); const snapshot = createEmptyStateSnapshot(rootComponent);
const emptyUrl = new BehaviorSubject([new UrlSegment("", {}, PRIMARY_OUTLET)]); const emptyUrl = new BehaviorSubject([new UrlSegment("", {}, PRIMARY_OUTLET)]);
const emptyParams = new BehaviorSubject({}); const emptyParams = new BehaviorSubject({});
const emptyQueryParams = new BehaviorSubject({}); const emptyQueryParams = new BehaviorSubject({});
const fragment = new BehaviorSubject(""); const fragment = new BehaviorSubject("");
const activated = new ActivatedRoute(emptyUrl, emptyParams, PRIMARY_OUTLET, rootComponent, candidate.root); const activated = new ActivatedRoute(emptyUrl, emptyParams, PRIMARY_OUTLET, rootComponent, snapshot.root);
return new RouterState(new TreeNode<ActivatedRoute>(activated, []), emptyQueryParams, fragment, candidate); return new RouterState(new TreeNode<ActivatedRoute>(activated, []), emptyQueryParams, fragment, snapshot);
} }
function createEmptyStateCandidate(rootComponent: Type): RouterStateCandidate { function createEmptyStateSnapshot(rootComponent: Type): RouterStateSnapshot {
const emptyUrl = [new UrlSegment("", {}, PRIMARY_OUTLET)]; const emptyUrl = [new UrlSegment("", {}, PRIMARY_OUTLET)];
const emptyParams = {}; const emptyParams = {};
const emptyQueryParams = {}; const emptyQueryParams = {};
const fragment = ""; const fragment = "";
const activated = new ActivatedRouteCandidate(emptyUrl, emptyParams, PRIMARY_OUTLET, rootComponent, null); const activated = new ActivatedRouteSnapshot(emptyUrl, emptyParams, PRIMARY_OUTLET, rootComponent, null);
return new RouterStateCandidate(new TreeNode<ActivatedRouteCandidate>(activated, []), emptyQueryParams, fragment); return new RouterStateSnapshot(new TreeNode<ActivatedRouteSnapshot>(activated, []), emptyQueryParams, fragment);
} }
/** /**
* Contains the information about a component loaded in an outlet. * Contains the information about a component loaded in an outlet. The information is provided through
* the params and urlSegments observables.
* *
* ### Usage * ### Usage
* *
@ -64,11 +65,24 @@ export class ActivatedRoute {
public params: Observable<Params>, public params: Observable<Params>,
public outlet: string, public outlet: string,
public component: Type | string, public component: Type | string,
public candidate: ActivatedRouteCandidate public snapshot: ActivatedRouteSnapshot
) {} ) {}
} }
export class ActivatedRouteCandidate { /**
* Contains the information about a component loaded in an outlet at a particular moment in time.
*
* ### Usage
*
* ```
* class MyComponent {
* constructor(route: ActivatedRoute) {
* const id: string = route.snapshot.params.id;
* }
* }
* ```
*/
export class ActivatedRouteSnapshot {
/** /**
* @internal * @internal
*/ */
@ -86,8 +100,21 @@ export class ActivatedRouteCandidate {
} }
} }
export class RouterStateCandidate extends Tree<ActivatedRouteCandidate> { /**
constructor(root: TreeNode<ActivatedRouteCandidate>, public queryParams: Params, public fragment: string) { * The state of the router at a particular moment in time.
*
* ### Usage
*
* ```
* class MyComponent {
* constructor(router: Router) {
* const snapshot = router.routerState.snapshot;
* }
* }
* ```
*/
export class RouterStateSnapshot extends Tree<ActivatedRouteSnapshot> {
constructor(root: TreeNode<ActivatedRouteSnapshot>, public queryParams: Params, public fragment: string) {
super(root); super(root);
} }
} }

View File

@ -1,7 +1,7 @@
import {DefaultUrlSerializer} from '../src/url_serializer'; import {DefaultUrlSerializer} from '../src/url_serializer';
import {UrlTree} from '../src/url_tree'; import {UrlTree} from '../src/url_tree';
import {Params, PRIMARY_OUTLET} from '../src/shared'; import {Params, PRIMARY_OUTLET} from '../src/shared';
import {ActivatedRoute, ActivatedRouteCandidate, RouterStateCandidate, createEmptyState} from '../src/router_state'; import {ActivatedRoute, ActivatedRouteSnapshot, RouterStateSnapshot, createEmptyState} from '../src/router_state';
import {createRouterState} from '../src/create_router_state'; import {createRouterState} from '../src/create_router_state';
import {recognize} from '../src/recognize'; import {recognize} from '../src/recognize';
import {RouterConfig} from '../src/config'; import {RouterConfig} from '../src/config';
@ -44,7 +44,7 @@ describe('create router state', () => {
}); });
}); });
function createState(config: RouterConfig, url: string): RouterStateCandidate { function createState(config: RouterConfig, url: string): RouterStateSnapshot {
let res; let res;
recognize(RootComponent, config, tree(url)).forEach(s => res = s); recognize(RootComponent, config, tree(url)).forEach(s => res = s);
return res; return res;

View File

@ -1,7 +1,7 @@
import {DefaultUrlSerializer} from '../src/url_serializer'; import {DefaultUrlSerializer} from '../src/url_serializer';
import {UrlTree} from '../src/url_tree'; import {UrlTree} from '../src/url_tree';
import {Params, PRIMARY_OUTLET} from '../src/shared'; import {Params, PRIMARY_OUTLET} from '../src/shared';
import {ActivatedRouteCandidate} from '../src/router_state'; import {ActivatedRouteSnapshot} from '../src/router_state';
import {recognize} from '../src/recognize'; import {recognize} from '../src/recognize';
describe('recognize', () => { describe('recognize', () => {
@ -196,7 +196,7 @@ describe('recognize', () => {
}); });
}); });
function checkActivatedRoute(actual: ActivatedRouteCandidate | null, url: string, params: Params, cmp: Function, outlet: string = PRIMARY_OUTLET):void { function checkActivatedRoute(actual: ActivatedRouteSnapshot | null, url: string, params: Params, cmp: Function, outlet: string = PRIMARY_OUTLET):void {
if (actual === null) { if (actual === null) {
expect(actual).toBeDefined(); expect(actual).toBeDefined();
} else { } else {