Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(125)

Side by Side Diff: third_party/pkg/route_hierarchical/lib/client.dart

Issue 180843004: Revert revision 33053 (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library route.client; 5 library route.client;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:collection'; 8 import 'dart:collection';
9 import 'dart:html'; 9 import 'dart:html';
10 10
11 import 'package:logging/logging.dart'; 11 import 'package:logging/logging.dart';
12 12
13 import 'url_matcher.dart'; 13 import 'url_matcher.dart';
14 export 'url_matcher.dart'; 14 export 'url_matcher.dart';
15 import 'url_template.dart'; 15 import 'url_template.dart';
16 16
17 17
18 final _logger = new Logger('route'); 18 final _logger = new Logger('route');
19 19
20 typedef RoutePreEnterEventHandler(RoutePreEnterEvent path); 20 typedef RouteEventHandler(RouteEvent path);
21 typedef RouteEnterEventHandler(RouteEnterEvent path);
22 typedef RouteLeaveEventHandler(RouteLeaveEvent path);
23 21
24 /** 22 /**
25 * A helper Router handle that scopes all route event subsriptions to it's 23 * A helper Router handle that scopes all route event subsriptions to it's
26 * instance and provides an convinience [discard] method. 24 * instance and provides an convinience [discard] method.
27 */ 25 */
28 class RouteHandle implements Route { 26 class RouteHandle implements Route {
29 Route _route; 27 Route _route;
30 final StreamController<RoutePreEnterEvent> _onPreEnterController; 28 final StreamController<RouteEvent> _onRouteController;
31 final StreamController<RouteEnterEvent> _onEnterController; 29 final StreamController<RouteEvent> _onLeaveController;
32 final StreamController<RouteLeaveEvent> _onLeaveController; 30 Stream<RouteEvent> get onRoute => _onRouteController.stream;
33 31 Stream<RouteEvent> get onLeave => _onLeaveController.stream;
34 @deprecated 32 StreamSubscription _onRouteSubscription;
35 Stream<RouteEnterEvent> get onRoute => onEnter;
36 Stream<RoutePreEnterEvent> get onPreEnter => _onPreEnterController.stream;
37 Stream<RouteEnterEvent> get onEnter => _onEnterController.stream;
38 Stream<RouteLeaveEvent> get onLeave => _onLeaveController.stream;
39
40 StreamSubscription _onPreEnterSubscription;
41 StreamSubscription _onEnterSubscription;
42 StreamSubscription _onLeaveSubscription; 33 StreamSubscription _onLeaveSubscription;
43 List<RouteHandle> _childHandles = <RouteHandle>[]; 34 List<RouteHandle> _childHandles = <RouteHandle>[];
44 35
45 RouteHandle._new(Route this._route) 36 RouteHandle._new(Route this._route)
46 : _onEnterController = 37 : _onRouteController =
47 new StreamController<RouteEnterEvent>.broadcast(sync: true), 38 new StreamController<RouteEvent>.broadcast(sync: true),
48 _onPreEnterController =
49 new StreamController<RoutePreEnterEvent>.broadcast(sync: true),
50 _onLeaveController = 39 _onLeaveController =
51 new StreamController<RouteLeaveEvent>.broadcast(sync: true) { 40 new StreamController<RouteEvent>.broadcast(sync: true) {
52 _onEnterSubscription = _route.onEnter.listen(_onEnterController.add); 41 _onRouteSubscription = _route.onRoute.listen(_onRouteController.add);
53 _onPreEnterSubscription =
54 _route.onPreEnter.listen(_onPreEnterController.add);
55 _onLeaveSubscription = _route.onLeave.listen(_onLeaveController.add); 42 _onLeaveSubscription = _route.onLeave.listen(_onLeaveController.add);
56 } 43 }
57 44
58 /// discards this handle. 45 /// discards this handle.
59 void discard() { 46 void discard() {
60 _logger.finest('discarding handle for $_route'); 47 _logger.finest('discarding handle for $_route');
61 _onPreEnterSubscription.cancel(); 48 _onRouteSubscription.cancel();
62 _onEnterSubscription.cancel();
63 _onLeaveSubscription.cancel(); 49 _onLeaveSubscription.cancel();
64 _onEnterController.close(); 50 _onRouteController.close();
65 _onLeaveController.close(); 51 _onLeaveController.close();
66 _childHandles.forEach((RouteHandle c) => c.discard()); 52 _childHandles.forEach((RouteHandle c) => c.discard());
67 _childHandles.clear(); 53 _childHandles.clear();
68 _route = null; 54 _route = null;
69 } 55 }
70 56
71 /// Not supported. Overridden to throw an error. 57 /// Not supported. Overridden to throw an error.
72 void addRoute({String name, Pattern path, bool defaultRoute: false, 58 void addRoute({String name, Pattern path, bool defaultRoute: false,
73 RouteEnterEventHandler enter, RoutePreEnterEventHandler preEnter, 59 RouteEventHandler enter, RouteEventHandler leave, mount}) =>
74 RouteLeaveEventHandler leave, mount}) =>
75 throw new UnsupportedError('addRoute is not supported in handle'); 60 throw new UnsupportedError('addRoute is not supported in handle');
76 61
77 /// See [Route.getRoute] 62 /// See [Route.getRoute]
78 Route getRoute(String routePath) { 63 Route getRoute(String routePath) {
79 Route r = _assertState(() => _getHost(_route).getRoute(routePath)); 64 Route r = _assertState(() => _getHost(_route).getRoute(routePath));
80 if (r == null) return null; 65 if (r == null) return null;
81 var handle = r.newHandle(); 66 var handle = r.newHandle();
82 if (handle != null) { 67 if (handle != null) {
83 _childHandles.add(handle); 68 _childHandles.add(handle);
84 } 69 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 /// See [Route.path] 110 /// See [Route.path]
126 UrlMatcher get path => _route.path; 111 UrlMatcher get path => _route.path;
127 112
128 /// See [Route.name] 113 /// See [Route.name]
129 String get name => _route.name; 114 String get name => _route.name;
130 115
131 /// See [Route.parent] 116 /// See [Route.parent]
132 Route get parent => _route.parent; 117 Route get parent => _route.parent;
133 } 118 }
134 119
135 childRoute({String name, Pattern path, bool defaultRoute: false,
136 RouteEnterEventHandler enter, RoutePreEnterEventHandler preEnter,
137 RouteLeaveEventHandler leave, mount}) => (Route route) =>
138 route.addRoute(name: name, path: path, defaultRoute: defaultRoute,
139 enter: enter, preEnter: preEnter, leave: leave, mount: leave);
140
141 /** 120 /**
142 * Route is a node in the tree of routes. The edge leading to the route is 121 * Route is a node in the tree of routes. The edge leading to the route is
143 * defined by path. 122 * defined by path.
144 */ 123 */
145 class Route { 124 class Route {
146 final String name; 125 final String name;
147 final Map<String, Route> _routes = new LinkedHashMap<String, Route>(); 126 final Map<String, Route> _routes = new LinkedHashMap<String, Route>();
148 final UrlMatcher path; 127 final UrlMatcher path;
149 final StreamController<RouteEnterEvent> _onEnterController; 128 final StreamController<RouteEvent> _onRouteController;
150 final StreamController<RoutePreEnterEvent> _onPreEnterController; 129 final StreamController<RouteEvent> _onLeaveController;
151 final StreamController<RouteLeaveEvent> _onLeaveController;
152 final Route parent; 130 final Route parent;
153 Route _defaultRoute; 131 Route _defaultRoute;
154 Route _currentRoute; 132 Route _currentRoute;
155 RouteEvent _lastEvent; 133 RouteEvent _lastEvent;
156 134
157 @deprecated 135 Stream<RouteEvent> get onRoute => _onRouteController.stream;
158 Stream<RouteEvent> get onRoute => onEnter;
159
160 Stream<RouteEvent> get onPreEnter => _onPreEnterController.stream;
161 Stream<RouteEvent> get onLeave => _onLeaveController.stream; 136 Stream<RouteEvent> get onLeave => _onLeaveController.stream;
162 Stream<RouteEvent> get onEnter => _onEnterController.stream;
163 137
164 Route._new({this.name, this.path, this.parent}) 138 Route._new({this.name, this.path, this.parent})
165 : _onEnterController = 139 : _onRouteController =
166 new StreamController<RouteEnterEvent>.broadcast(sync: true), 140 new StreamController<RouteEvent>.broadcast(sync: true),
167 _onPreEnterController =
168 new StreamController<RoutePreEnterEvent>.broadcast(sync: true),
169 _onLeaveController = 141 _onLeaveController =
170 new StreamController<RouteLeaveEvent>.broadcast(sync: true); 142 new StreamController<RouteEvent>.broadcast(sync: true);
171 143
172 void addRoute({String name, Pattern path, bool defaultRoute: false, 144 void addRoute({String name, Pattern path, bool defaultRoute: false,
173 RouteEnterEventHandler enter, RoutePreEnterEventHandler preEnter, 145 RouteEventHandler enter, RouteEventHandler leave, mount}) {
174 RouteLeaveEventHandler leave, mount}) {
175 if (name == null) { 146 if (name == null) {
176 throw new ArgumentError('name is required for all routes'); 147 throw new ArgumentError('name is required for all routes');
177 } 148 }
178 if (_routes.containsKey(name)) { 149 if (_routes.containsKey(name)) {
179 throw new ArgumentError('Route $name already exists'); 150 throw new ArgumentError('Route $name already exists');
180 } 151 }
181 152
182 var matcher; 153 var matcher;
183 if (!(path is UrlMatcher)) { 154 if (!(path is UrlMatcher)) {
184 matcher = new UrlTemplate(path.toString()); 155 matcher = new UrlTemplate(path.toString());
185 } else { 156 } else {
186 matcher = path; 157 matcher = path;
187 } 158 }
188 var route = new Route._new(name: name, path: matcher, parent: this); 159 var route = new Route._new(name: name, path: matcher, parent: this);
189 160
190 if (preEnter != null) {
191 route.onPreEnter.listen(preEnter);
192 }
193 if (enter != null) { 161 if (enter != null) {
194 route.onEnter.listen(enter); 162 route.onRoute.listen(enter);
195 } 163 }
196 if (leave != null) { 164 if (leave != null) {
197 route.onLeave.listen(leave); 165 route.onLeave.listen(leave);
198 } 166 }
199 167
200 if (mount != null) { 168 if (mount != null) {
201 if (mount is Function) { 169 if (mount is Function) {
202 mount(route); 170 mount(route);
203 } else if (mount is Routable) { 171 } else if (mount is Routable) {
204 mount.configureRoute(route); 172 mount.configureRoute(route);
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
318 if (_lastEvent == null) return {}; 286 if (_lastEvent == null) return {};
319 return new Map.from(_lastEvent.parameters); 287 return new Map.from(_lastEvent.parameters);
320 } 288 }
321 return null; 289 return null;
322 } 290 }
323 } 291 }
324 292
325 /** 293 /**
326 * Route enter or leave event. 294 * Route enter or leave event.
327 */ 295 */
328 abstract class RouteEvent { 296 class RouteEvent {
329 final String path; 297 final String path;
330 final Map parameters; 298 final Map parameters;
331 final Route route; 299 final Route route;
300 var _allowLeaveFutures = <Future<bool>>[];
332 301
333 RouteEvent(this.path, this.parameters, this.route); 302 RouteEvent(this.path, this.parameters, this.route);
334 }
335
336 class RoutePreEnterEvent extends RouteEvent {
337
338 var _allowEnterFutures = <Future<bool>>[];
339
340 RoutePreEnterEvent(path, parameters, route) : super(path, parameters, route);
341 303
342 /** 304 /**
343 * Can be called on enter with the future which will complete with a boolean 305 * Can be called on leave with the future which will complete with a boolean
344 * value allowing (true) or disallowing (false) the current navigation.
345 */
346 void allowEnter(Future<bool> allow) {
347 _allowEnterFutures.add(allow);
348 }
349 }
350
351 class RouteEnterEvent extends RouteEvent {
352
353 RouteEnterEvent(path, parameters, route) : super(path, parameters, route);
354 }
355
356 class RouteLeaveEvent extends RouteEvent {
357
358 var _allowLeaveFutures = <Future<bool>>[];
359
360 RouteLeaveEvent(path, parameters, route) : super(path, parameters, route);
361
362 /**
363 * Can be called on enter with the future which will complete with a boolean
364 * value allowing (true) or disallowing (false) the current navigation. 306 * value allowing (true) or disallowing (false) the current navigation.
365 */ 307 */
366 void allowLeave(Future<bool> allow) { 308 void allowLeave(Future<bool> allow) {
367 _allowLeaveFutures.add(allow); 309 _allowLeaveFutures.add(allow);
368 } 310 }
369 311
370 RouteLeaveEvent _clone() => new RouteLeaveEvent(path, parameters, route); 312 RouteEvent _clone() => new RouteEvent(path, parameters, route);
371 } 313 }
372 314
373 /** 315 /**
374 * Event emitted when routing starts. 316 * Event emitted when routing starts.
375 */ 317 */
376 class RouteStartEvent { 318 class RouteStartEvent {
377 319
378 /** 320 /**
379 * URI that was passed to [Router.route]. 321 * URI that was passed to [Router.route].
380 */ 322 */
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
430 372
431 /** 373 /**
432 * Finds a matching [Route] added with [addRoute], parses the path 374 * Finds a matching [Route] added with [addRoute], parses the path
433 * and invokes the associated callback. 375 * and invokes the associated callback.
434 * 376 *
435 * This method does not perform any navigation, [go] should be used for that. 377 * This method does not perform any navigation, [go] should be used for that.
436 * This method is used to invoke a handler after some other code navigates the 378 * This method is used to invoke a handler after some other code navigates the
437 * window, such as [listen]. 379 * window, such as [listen].
438 */ 380 */
439 Future<bool> route(String path, {Route startingFrom}) { 381 Future<bool> route(String path, {Route startingFrom}) {
440 var future = _route(path, startingFrom); 382 var future = _route(path, startingFrom: startingFrom);
441 _onRouteStart.add(new RouteStartEvent._new(path, future)); 383 _onRouteStart.add(new RouteStartEvent._new(path, future));
442 return future; 384 return future;
443 } 385 }
444 386
445 Future<bool> _route(String path, Route startingFrom) { 387 Future<bool> _route(String path, {Route startingFrom}) {
446 var baseRoute = startingFrom == null ? root : _dehandle(startingFrom); 388 var baseRoute = startingFrom == null ? this.root : _dehandle(startingFrom);
447 _logger.finest('route $path $baseRoute'); 389 _logger.finest('route $path $baseRoute');
448 var treePath = _matchingTreePath(path, baseRoute); 390 Route matchedRoute;
449 Route cmpBase = baseRoute; 391 List matchingRoutes = baseRoute._routes.values.where(
450 var tail = path; 392 (r) => r.path.match(path) != null).toList();
451 // Skip all routes that are unaffected by this path. 393 if (!matchingRoutes.isEmpty) {
452 treePath = treePath.skipWhile((_Match matchedRoute) { 394 if (matchingRoutes.length > 1) {
453 var skip = cmpBase._currentRoute == matchedRoute.route && 395 _logger.warning("More than one route matches $path $matchingRoutes");
454 !_paramsChanged(cmpBase, matchedRoute.urlMatch);
455 if (skip) {
456 cmpBase = matchedRoute.route;
457 tail = matchedRoute.urlMatch.tail;
458 } 396 }
459 return skip; 397 matchedRoute = matchingRoutes.first;
460 }); 398 } else {
461 // TODO(pavelgj): weird things happen without this line... 399 if (baseRoute._defaultRoute != null) {
462 treePath = treePath.toList(); 400 matchedRoute = baseRoute._defaultRoute;
463 if (treePath.isEmpty) { 401 }
464 return new Future.value(true);
465 } 402 }
466 var preEnterFutures = _preEnter(tail, treePath); 403 if (matchedRoute != null) {
467 return Future.wait(preEnterFutures).then((List<bool> results) { 404 var match = _getMatch(matchedRoute, path);
468 if (results.fold(true, (a, b) => a && b)) { 405 if (matchedRoute != baseRoute._currentRoute ||
469 return _processNewRoute(cmpBase, treePath, tail); 406 _paramsChanged(baseRoute, match)) {
407 return _processNewRoute(baseRoute, path, match, matchedRoute);
408 } else {
409 baseRoute._currentRoute._lastEvent =
410 new RouteEvent(match.match, match.parameters,
411 baseRoute._currentRoute);
412 return _route(match.tail, startingFrom: matchedRoute);
470 } 413 }
471 return false; 414 } else if (baseRoute._currentRoute != null) {
472 }); 415 var event = new RouteEvent('', {}, baseRoute);
473 } 416 return _leaveCurrentRoute(baseRoute, event).then((success) {
474 417 if (success) {
475 List<Future<bool>> _preEnter(String tail, Iterable<_Match> treePath) { 418 baseRoute._currentRoute = null;
476 List<Future<bool>> preEnterFutures = <Future<bool>>[]; 419 }
477 treePath.forEach((_Match matchedRoute) { 420 return success;
478 tail = matchedRoute.urlMatch.tail; 421 });
479 var preEnterEvent = new RoutePreEnterEvent(tail, matchedRoute.urlMatch.par ameters, matchedRoute.route);
480 matchedRoute.route._onPreEnterController.add(preEnterEvent);
481 preEnterFutures.addAll(preEnterEvent._allowEnterFutures);
482 });
483 return preEnterFutures;
484 }
485
486 Future<bool> _processNewRoute(Route startingFrom, Iterable<_Match> treePath, S tring path) {
487 return _leaveOldRoutes(startingFrom, treePath).then((bool allowed) {
488 if (allowed) {
489 var base = startingFrom;
490 var tail = path;
491 treePath.forEach((_Match matchedRoute) {
492 tail = matchedRoute.urlMatch.tail;
493 var event = new RouteEnterEvent(matchedRoute.urlMatch.match,
494 matchedRoute.urlMatch.parameters, matchedRoute.route);
495 _unsetAllCurrentRoutes(base);
496 base._currentRoute = matchedRoute.route;
497 base._currentRoute._lastEvent = event;
498 matchedRoute.route._onEnterController.add(event);
499 base = matchedRoute.route;
500 });
501 return true;
502 }
503 return false;
504 });
505 }
506
507 Future<bool> _leaveOldRoutes(Route startingFrom, Iterable<_Match> treePath) {
508 if (treePath.isEmpty) {
509 return new Future.value(true);
510 } 422 }
511 var event = new RouteLeaveEvent('', {}, startingFrom); 423 return new Future.value(true);
512 return _leaveCurrentRoute(startingFrom, event);
513 }
514
515 Iterable<_Match> _matchingTreePath(String path, Route baseRoute) {
516 List<_Match> treePath = <_Match>[];
517 Route matchedRoute;
518 do {
519 matchedRoute = null;
520 List matchingRoutes = baseRoute._routes.values.where(
521 (r) => r.path.match(path) != null).toList();
522 if (!matchingRoutes.isEmpty) {
523 if (matchingRoutes.length > 1) {
524 _logger.warning("More than one route matches $path $matchingRoutes");
525 }
526 matchedRoute = matchingRoutes.first;
527 } else {
528 if (baseRoute._defaultRoute != null) {
529 matchedRoute = baseRoute._defaultRoute;
530 }
531 }
532 if (matchedRoute != null) {
533 var match = _getMatch(matchedRoute, path);
534 treePath.add(new _Match(matchedRoute, match));
535 baseRoute = matchedRoute;
536 path = match.tail;
537 }
538 } while (matchedRoute != null);
539 return treePath;
540 } 424 }
541 425
542 bool _paramsChanged(Route baseRoute, UrlMatch match) { 426 bool _paramsChanged(Route baseRoute, UrlMatch match) {
543 return baseRoute._currentRoute._lastEvent.path != match.match || 427 return baseRoute._currentRoute._lastEvent.path != match.match ||
544 !_mapsEqual(baseRoute._currentRoute._lastEvent.parameters, 428 !_mapsEqual(baseRoute._currentRoute._lastEvent.parameters,
545 match.parameters); 429 match.parameters);
546 } 430 }
547 431
548 bool _mapsEqual(Map a, Map b) { 432 bool _mapsEqual(Map a, Map b) {
549 if (a.keys.length != b.keys.length) { 433 if (a.keys.length != b.keys.length) {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
586 470
587 String _buildQuery(Map queryParams) { 471 String _buildQuery(Map queryParams) {
588 var query = queryParams.keys.map((key) => 472 var query = queryParams.keys.map((key) =>
589 '$key=${Uri.encodeComponent(queryParams[key])}').join('&'); 473 '$key=${Uri.encodeComponent(queryParams[key])}').join('&');
590 if (query.isEmpty) { 474 if (query.isEmpty) {
591 return ''; 475 return '';
592 } 476 }
593 return '?$query'; 477 return '?$query';
594 } 478 }
595 479
596 Route _dehandle(Route r) => r is RouteHandle ? r._getHost(r): r; 480 Route _dehandle(Route r) {
481 if (r is RouteHandle) {
482 return (r as RouteHandle)._getHost(r);
483 }
484 return r;
485 }
597 486
598 UrlMatch _getMatch(Route route, String path) { 487 UrlMatch _getMatch(Route route, String path) {
599 var match = route.path.match(path); 488 var match = route.path.match(path);
600 if (match == null) { // default route 489 if (match == null) { // default route
601 return new UrlMatch('', '', {}); 490 return new UrlMatch('', '', {});
602 } 491 }
603 _parseQuery(route, path).forEach((k, v) { match.parameters[k] = v; }); 492 _parseQuery(route, path).forEach((k, v) { match.parameters[k] = v; });
604 return match; 493 return match;
605 } 494 }
606 495
(...skipping 20 matching lines...) Expand all
627 return ['', '']; 516 return ['', ''];
628 } 517 }
629 var splitPoint = keyValPair.indexOf('=') == -1 ? 518 var splitPoint = keyValPair.indexOf('=') == -1 ?
630 keyValPair.length : keyValPair.indexOf('=') + 1; 519 keyValPair.length : keyValPair.indexOf('=') + 1;
631 var key = keyValPair.substring(0, splitPoint + 520 var key = keyValPair.substring(0, splitPoint +
632 (keyValPair.indexOf('=') == -1 ? 0 : -1)); 521 (keyValPair.indexOf('=') == -1 ? 0 : -1));
633 var value = keyValPair.substring(splitPoint); 522 var value = keyValPair.substring(splitPoint);
634 return [key, value]; 523 return [key, value];
635 } 524 }
636 525
526 Future<bool> _processNewRoute(Route base, String path, UrlMatch match,
527 Route newRoute) {
528 _logger.finest('_processNewRoute $path');
529 var event = new RouteEvent(match.match, match.parameters, newRoute);
530 // before we make this a new current route, leave the old
531 return _leaveCurrentRoute(base, event).then((bool allowNavigation) {
532 if (allowNavigation) {
533 _unsetAllCurrentRoutes(base);
534 base._currentRoute = newRoute;
535 base._currentRoute._lastEvent = event;
536 newRoute._onRouteController.add(event);
537 return _route(match.tail, startingFrom: newRoute);
538 }
539 return false;
540 });
541 }
542
637 void _unsetAllCurrentRoutes(Route r) { 543 void _unsetAllCurrentRoutes(Route r) {
638 if (r._currentRoute != null) { 544 if (r._currentRoute != null) {
639 _unsetAllCurrentRoutes(r._currentRoute); 545 _unsetAllCurrentRoutes(r._currentRoute);
640 r._currentRoute = null; 546 r._currentRoute = null;
641 } 547 }
642 } 548 }
643 549
644 Future<bool> _leaveCurrentRoute(Route base, RouteLeaveEvent e) => 550 Future<bool> _leaveCurrentRoute(Route base, RouteEvent e) =>
645 Future.wait(_leaveCurrentRouteHelper(base, e)) 551 Future.wait(_leaveCurrentRouteHelper(base, e))
646 .then((values) => values.fold(true, (c, v) => c && v)); 552 .then((values) => values.fold(true, (c, v) => c && v));
647 553
648 List<Future<bool>> _leaveCurrentRouteHelper(Route base, RouteLeaveEvent e) { 554 List<Future<bool>> _leaveCurrentRouteHelper(Route base, RouteEvent e) {
649 var futures = []; 555 var futures = [];
650 if (base._currentRoute != null) { 556 if (base._currentRoute != null) {
651 List<Future<bool>> pendingResponses = <Future<bool>>[]; 557 List<Future<bool>> pendingResponses = <Future<bool>>[];
652 // We create a copy of the route event 558 // We create a copy of the route event
653 var event = e._clone(); 559 var event = e._clone();
654 base._currentRoute._onLeaveController.add(event); 560 base._currentRoute._onLeaveController.add(event);
655 futures.addAll(event._allowLeaveFutures); 561 futures.addAll(event._allowLeaveFutures);
656 futures.addAll(_leaveCurrentRouteHelper(base._currentRoute, event)); 562 futures.addAll(_leaveCurrentRouteHelper(base._currentRoute, event));
657 } 563 }
658 return futures; 564 return futures;
659 } 565 }
660 566
661 /** 567 /**
662 * Listens for window history events and invokes the router. On older 568 * Listens for window history events and invokes the router. On older
663 * browsers the hashChange event is used instead. 569 * browsers the hashChange event is used instead.
664 */ 570 */
665 void listen({bool ignoreClick: false, Element appRoot}) { 571 void listen({bool ignoreClick: false}) {
666 _logger.finest('listen ignoreClick=$ignoreClick'); 572 _logger.finest('listen ignoreClick=$ignoreClick');
667 if (_listen) { 573 if (_listen) {
668 throw new StateError('listen can only be called once'); 574 throw new StateError('listen can only be called once');
669 } 575 }
670 _listen = true; 576 _listen = true;
671 if (_useFragment) { 577 if (_useFragment) {
672 _window.onHashChange.listen((_) { 578 _window.onHashChange.listen((_) {
673 route(_normalizeHash(_window.location.hash)).then((allowed) { 579 route(_normalizeHash(_window.location.hash)).then((allowed) {
674 // if not allowed, we need to restore the browser location 580 // if not allowed, we need to restore the browser location
675 if (!allowed) { 581 if (!allowed) {
676 _window.history.back(); 582 _window.history.back();
677 } 583 }
678 }); 584 });
679 }); 585 });
680 route(_normalizeHash(_window.location.hash)); 586 route(_normalizeHash(_window.location.hash));
681 } else { 587 } else {
682 _window.onPopState.listen((_) { 588 _window.onPopState.listen((_) {
683 var path = '${_window.location.pathname}${_window.location.hash}'; 589 var path = '${_window.location.pathname}${_window.location.hash}';
684 route(path).then((allowed) { 590 route(path).then((allowed) {
685 // if not allowed, we need to restore the browser location 591 // if not allowed, we need to restore the browser location
686 if (!allowed) { 592 if (!allowed) {
687 _window.history.back(); 593 _window.history.back();
688 } 594 }
689 }); 595 });
690 }); 596 });
691 } 597 }
692 if (!ignoreClick) { 598 if (!ignoreClick) {
693 if (appRoot == null) {
694 appRoot = _window.document.documentElement;
695 }
696 _logger.finest('listen on win'); 599 _logger.finest('listen on win');
697 appRoot.onClick.listen((MouseEvent e) { 600 _window.onClick.listen((Event e) {
698 if (!e.ctrlKey && !e.metaKey && !e.shiftKey && e.target is AnchorElement ) { 601 if (e.target is AnchorElement) {
699 AnchorElement anchor = e.target; 602 AnchorElement anchor = e.target;
700 if (anchor.host == _window.location.host) { 603 if (anchor.host == _window.location.host) {
701 _logger.finest('clicked ${anchor.pathname}${anchor.hash}'); 604 _logger.finest('clicked ${anchor.pathname}${anchor.hash}');
702 e.preventDefault(); 605 e.preventDefault();
703 var path; 606 var path;
704 if (_useFragment) { 607 if (_useFragment) {
705 path = _normalizeHash(anchor.hash); 608 path = _normalizeHash(anchor.hash);
706 } else { 609 } else {
707 path = '${anchor.pathname}'; 610 path = '${anchor.pathname}';
708 } 611 }
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
764 List<Route> get activePath { 667 List<Route> get activePath {
765 var res = <Route>[]; 668 var res = <Route>[];
766 var current = root; 669 var current = root;
767 while (current._currentRoute != null) { 670 while (current._currentRoute != null) {
768 current = current._currentRoute; 671 current = current._currentRoute;
769 res.add(current); 672 res.add(current);
770 } 673 }
771 return res; 674 return res;
772 } 675 }
773 } 676 }
774
775 class _Match {
776 final Route route;
777 final UrlMatch urlMatch;
778
779 _Match(this.route, this.urlMatch);
780 }
OLDNEW
« no previous file with comments | « third_party/pkg/route_hierarchical/example/full/root-view.html ('k') | third_party/pkg/route_hierarchical/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698