Index: third_party/pkg/route_hierarchical/lib/client.dart |
diff --git a/third_party/pkg/route_hierarchical/lib/client.dart b/third_party/pkg/route_hierarchical/lib/client.dart |
deleted file mode 100644 |
index 14d737fa8dc03b6d3f52227c4e872411953a28e6..0000000000000000000000000000000000000000 |
--- a/third_party/pkg/route_hierarchical/lib/client.dart |
+++ /dev/null |
@@ -1,757 +0,0 @@ |
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-library route.client; |
- |
-import 'dart:async'; |
-import 'dart:html'; |
- |
-import 'package:logging/logging.dart'; |
- |
-import 'src/utils.dart'; |
- |
-import 'url_matcher.dart'; |
-export 'url_matcher.dart'; |
-import 'url_template.dart'; |
- |
-part 'route_handle.dart'; |
- |
- |
-final _logger = new Logger('route'); |
-const _PATH_SEPARATOR = '.'; |
- |
-typedef void RoutePreEnterEventHandler(RoutePreEnterEvent event); |
-typedef void RouteEnterEventHandler(RouteEnterEvent event); |
-typedef void RouteLeaveEventHandler(RouteLeaveEvent event); |
- |
-/** |
- * [Route] represents a node in the route tree. |
- */ |
-abstract class Route { |
- /** |
- * Name of the route. Used when querying routes. |
- */ |
- String get name; |
- |
- /** |
- * A path fragment [UrlMatcher] for this route. |
- */ |
- UrlMatcher get path; |
- |
- /** |
- * Parent route in the route tree. |
- */ |
- Route get parent; |
- |
- /** |
- * Indicates whether this route is currently active. Root route is always |
- * active. |
- */ |
- bool get isActive; |
- |
- /** |
- * Returns parameters for the currently active route. If the route is not |
- * active the getter returns null. |
- */ |
- Map get parameters; |
- |
- /** |
- * Whether to trigger the leave event when only the parameters change. |
- */ |
- bool get dontLeaveOnParamChanges; |
- |
- /** |
- * Returns a stream of [RouteEnterEvent] events. The [RouteEnterEvent] event |
- * is fired when route has already been made active, but before subroutes |
- * are entered. The event starts at the root and propagates from parent to |
- * child routes. |
- */ |
- @Deprecated("use [onEnter] instead.") |
- Stream<RouteEnterEvent> get onRoute; |
- |
- /** |
- * Returns a stream of [RoutePreEnterEvent] events. The [RoutePreEnterEvent] |
- * event is fired when the route is matched during the routing, but before |
- * any previous routes were left, or any new routes were entered. The event |
- * starts at the root and propagates from parent to child routes. |
- * |
- * At this stage it's possible to veto entering of the route by calling |
- * [RoutePreEnterEvent.allowEnter] with a [Future] returns a boolean value |
- * indicating whether enter is permitted (true) or not (false). |
- */ |
- Stream<RoutePreEnterEvent> get onPreEnter; |
- |
- /** |
- * Returns a stream of [RouteLeaveEvent] events. The [RouteLeaveEvent] |
- * event is fired when the route is being left. The event starts at the leaf |
- * route and propagates from child to parent routes. |
- * |
- * At this stage it's possible to veto leaving of the route by calling |
- * [RouteLeaveEvent.allowLeave] with a [Future] returns a boolean value |
- * indicating whether leave is permitted (true) or not (false). |
- * |
- * Note: that once child routes have been notified of the leave they will not |
- * be notified of the subsequent veto by any parent route. See: |
- * https://github.com/angular/route.dart/issues/28 |
- */ |
- Stream<RouteLeaveEvent> get onLeave; |
- |
- /** |
- * Returns a stream of [RouteEnterEvent] events. The [RouteEnterEvent] event |
- * is fired when route has already been made active, but before subroutes |
- * are entered. The event starts at the root and propagates from parent |
- * to child routes. |
- */ |
- Stream<RouteEnterEvent> get onEnter; |
- |
- void addRoute({String name, Pattern path, bool defaultRoute: false, |
- RouteEnterEventHandler enter, RoutePreEnterEventHandler preEnter, |
- RouteLeaveEventHandler leave, mount, dontLeaveOnParamChanges: false}); |
- |
- /** |
- * Queries sub-routes using the [routePath] and returns the matching [Route]. |
- * |
- * [routePath] is a dot-separated list of route names. Ex: foo.bar.baz, which |
- * means that current route should contain route named 'foo', the 'foo' route |
- * should contain route named 'bar', and so on. |
- * |
- * If no match is found then [:null:] is returned. |
- */ |
- @Deprecated("use [findRoute] instead.") |
- Route getRoute(String routePath); |
- |
- /** |
- * Queries sub-routes using the [routePath] and returns the matching [Route]. |
- * |
- * [routePath] is a dot-separated list of route names. Ex: foo.bar.baz, which |
- * means that current route should contain route named 'foo', the 'foo' route |
- * should contain route named 'bar', and so on. |
- * |
- * If no match is found then [:null:] is returned. |
- */ |
- Route findRoute(String routePath); |
- |
- /** |
- * Create an return a new [RouteHandle] for this route. |
- */ |
- RouteHandle newHandle(); |
- |
- String toString() => '[Route: $name]'; |
-} |
- |
-/** |
- * Route is a node in the tree of routes. The edge leading to the route is |
- * defined by path. |
- */ |
-class RouteImpl extends Route { |
- @override |
- final String name; |
- @override |
- final UrlMatcher path; |
- @override |
- final RouteImpl parent; |
- |
- final _routes = <String, RouteImpl>{}; |
- final StreamController<RouteEnterEvent> _onEnterController; |
- final StreamController<RoutePreEnterEvent> _onPreEnterController; |
- final StreamController<RouteLeaveEvent> _onLeaveController; |
- RouteImpl _defaultRoute; |
- RouteImpl _currentRoute; |
- RouteEvent _lastEvent; |
- @override |
- final bool dontLeaveOnParamChanges; |
- |
- @override |
- @Deprecated("use [onEnter] instead.") |
- Stream<RouteEvent> get onRoute => onEnter; |
- @override |
- Stream<RouteEvent> get onPreEnter => _onPreEnterController.stream; |
- @override |
- Stream<RouteEvent> get onLeave => _onLeaveController.stream; |
- @override |
- Stream<RouteEvent> get onEnter => _onEnterController.stream; |
- |
- RouteImpl._new({this.name, this.path, this.parent, |
- this.dontLeaveOnParamChanges: false}) |
- : _onEnterController = |
- new StreamController<RouteEnterEvent>.broadcast(sync: true), |
- _onPreEnterController = |
- new StreamController<RoutePreEnterEvent>.broadcast(sync: true), |
- _onLeaveController = |
- new StreamController<RouteLeaveEvent>.broadcast(sync: true); |
- |
- @override |
- void addRoute({String name, Pattern path, bool defaultRoute: false, |
- RouteEnterEventHandler enter, RoutePreEnterEventHandler preEnter, |
- RouteLeaveEventHandler leave, mount, dontLeaveOnParamChanges: false}) { |
- if (name == null) { |
- throw new ArgumentError('name is required for all routes'); |
- } |
- if (name.contains(_PATH_SEPARATOR)) { |
- throw new ArgumentError('name cannot contain dot.'); |
- } |
- if (_routes.containsKey(name)) { |
- throw new ArgumentError('Route $name already exists'); |
- } |
- |
- var matcher = path is UrlMatcher ? path : new UrlTemplate(path.toString()); |
- |
- var route = new RouteImpl._new(name: name, path: matcher, parent: this, |
- dontLeaveOnParamChanges: dontLeaveOnParamChanges); |
- |
- route..onPreEnter.listen(preEnter) |
- ..onEnter.listen(enter) |
- ..onLeave.listen(leave); |
- |
- if (mount != null) { |
- if (mount is Function) { |
- mount(route); |
- } else if (mount is Routable) { |
- mount.configureRoute(route); |
- } |
- } |
- |
- if (defaultRoute) { |
- if (_defaultRoute != null) { |
- throw new StateError('Only one default route can be added.'); |
- } |
- _defaultRoute = route; |
- } |
- _routes[name] = route; |
- } |
- |
- @override |
- Route getRoute(String routePath) => findRoute(routePath); |
- |
- @override |
- Route findRoute(String routePath) { |
- var routeName = routePath.split(_PATH_SEPARATOR).first; |
- if (!_routes.containsKey(routeName)) { |
- _logger.warning('Invalid route name: $routeName $_routes'); |
- return null; |
- } |
- var routeToGo = _routes[routeName]; |
- var childPath = routePath.substring(routeName.length); |
- return childPath.isEmpty ? routeToGo : |
- routeToGo.getRoute(childPath.substring(1)); |
- } |
- |
- String _getHead(String tail, Map queryParams) { |
- if (parent == null) return tail; |
- if (parent._currentRoute == null) { |
- throw new StateError('Router $parent has no current router.'); |
- } |
- _populateQueryParams(parent._currentRoute._lastEvent.parameters, |
- parent._currentRoute, queryParams); |
- return parent._getHead(parent._currentRoute._reverse(tail), queryParams); |
- } |
- |
- String _getTailUrl(String routePath, Map parameters, Map queryParams) { |
- var routeName = routePath.split('.').first; |
- if (!_routes.containsKey(routeName)) { |
- throw new StateError('Invalid route name: $routeName'); |
- } |
- var routeToGo = _routes[routeName]; |
- var tail = ''; |
- var childPath = routePath.substring(routeName.length); |
- if (childPath.isNotEmpty) { |
- tail = routeToGo._getTailUrl( |
- childPath.substring(1), parameters, queryParams); |
- } |
- _populateQueryParams(parameters, routeToGo, queryParams); |
- return routeToGo.path.reverse( |
- parameters: _joinParams(parameters, routeToGo._lastEvent), tail: tail); |
- } |
- |
- void _populateQueryParams(Map parameters, Route route, Map queryParams) { |
- parameters.keys.forEach((String prefixedKey) { |
- if (prefixedKey.startsWith('${route.name}.')) { |
- var key = prefixedKey.substring('${route.name}.'.length); |
- if (!route.path.urlParameterNames().contains(key)) { |
- queryParams[prefixedKey] = parameters[prefixedKey]; |
- } |
- } |
- }); |
- } |
- |
- Map _joinParams(Map parameters, RouteEvent lastEvent) => lastEvent == null |
- ? parameters |
- : new Map.from(lastEvent.parameters)..addAll(parameters); |
- |
- /** |
- * Returns a URL for this route. The tail (url generated by the child path) |
- * will be passes to the UrlMatcher to be properly appended in the |
- * right place. |
- */ |
- String _reverse(String tail) => |
- path.reverse(parameters: _lastEvent.parameters, tail: tail); |
- |
- /** |
- * Create an return a new [RouteHandle] for this route. |
- */ |
- @override |
- RouteHandle newHandle() { |
- _logger.finest('newHandle for $this'); |
- return new RouteHandle._new(this); |
- } |
- |
- /** |
- * Indicates whether this route is currently active. Root route is always |
- * active. |
- */ |
- @override |
- bool get isActive => |
- parent == null ? true : identical(parent._currentRoute, this); |
- |
- /** |
- * Returns parameters for the currently active route. If the route is not |
- * active the getter returns null. |
- */ |
- @override |
- Map get parameters { |
- if (isActive) { |
- return _lastEvent == null ? {} : new Map.from(_lastEvent.parameters); |
- } |
- return null; |
- } |
-} |
- |
-/** |
- * Route enter or leave event. |
- */ |
-abstract class RouteEvent { |
- final String path; |
- final Map parameters; |
- final Route route; |
- |
- RouteEvent(this.path, this.parameters, this.route); |
-} |
- |
-class RoutePreEnterEvent extends RouteEvent { |
- final _allowEnterFutures = <Future<bool>>[]; |
- |
- RoutePreEnterEvent(path, parameters, route) : super(path, parameters, route); |
- |
- RoutePreEnterEvent._fromMatch(_Match m) |
- : this(m.urlMatch.tail, m.urlMatch.parameters, m.route); |
- |
- /** |
- * Can be called on enter with the future which will complete with a boolean |
- * value allowing ([:true:]) or disallowing ([:false:]) the current |
- * navigation. |
- */ |
- void allowEnter(Future<bool> allow) { |
- _allowEnterFutures.add(allow); |
- } |
-} |
- |
-class RouteEnterEvent extends RouteEvent { |
- |
- RouteEnterEvent(path, parameters, route) : super(path, parameters, route); |
- |
- RouteEnterEvent._fromMatch(_Match m) |
- : this(m.urlMatch.match, m.urlMatch.parameters, m.route); |
-} |
- |
-class RouteLeaveEvent extends RouteEvent { |
- final _allowLeaveFutures = <Future<bool>>[]; |
- |
- RouteLeaveEvent(path, parameters, route) : super(path, parameters, route); |
- |
- /** |
- * Can be called on enter with the future which will complete with a boolean |
- * value allowing ([:true:]) or disallowing ([:false:]) the current |
- * navigation. |
- */ |
- void allowLeave(Future<bool> allow) { |
- _allowLeaveFutures.add(allow); |
- } |
- |
- RouteLeaveEvent _clone() => new RouteLeaveEvent(path, parameters, route); |
-} |
- |
-/** |
- * Event emitted when routing starts. |
- */ |
-class RouteStartEvent { |
- /** |
- * URI that was passed to [Router.route]. |
- */ |
- final String uri; |
- |
- /** |
- * Future that completes to a boolean value of whether the routing was |
- * successful. |
- */ |
- final Future<bool> completed; |
- |
- RouteStartEvent._new(this.uri, this.completed); |
-} |
- |
-abstract class Routable { |
- void configureRoute(Route router); |
-} |
- |
-/** |
- * Stores a set of [UrlPattern] to [Handler] associations and provides methods |
- * for calling a handler for a URL path, listening to [Window] history events, |
- * and creating HTML event handlers that navigate to a URL. |
- */ |
-class Router { |
- final bool _useFragment; |
- final Window _window; |
- final Route root; |
- final _onRouteStart = |
- new StreamController<RouteStartEvent>.broadcast(sync: true); |
- final bool sortRoutes; |
- bool _listen = false; |
- |
- /** |
- * [useFragment] determines whether this Router uses pure paths with |
- * [History.pushState] or paths + fragments and [Location.assign]. The default |
- * value is null which then determines the behavior based on |
- * [History.supportsState]. |
- */ |
- Router({bool useFragment, Window windowImpl, bool sortRoutes: true}) |
- : this._init(null, useFragment: useFragment, windowImpl: windowImpl, |
- sortRoutes: sortRoutes); |
- |
- |
- Router._init(Router parent, {bool useFragment, Window windowImpl, |
- this.sortRoutes}) |
- : _useFragment = (useFragment == null) |
- ? !History.supportsState |
- : useFragment, |
- _window = (windowImpl == null) ? window : windowImpl, |
- root = new RouteImpl._new(); |
- |
- /** |
- * A stream of route calls. |
- */ |
- Stream<RouteStartEvent> get onRouteStart => _onRouteStart.stream; |
- |
- /** |
- * Finds a matching [Route] added with [addRoute], parses the path |
- * and invokes the associated callback. |
- * |
- * This method does not perform any navigation, [go] should be used for that. |
- * This method is used to invoke a handler after some other code navigates the |
- * window, such as [listen]. |
- */ |
- Future<bool> route(String path, {Route startingFrom}) { |
- var future = _route(path, startingFrom); |
- _onRouteStart.add(new RouteStartEvent._new(path, future)); |
- return future; |
- } |
- |
- Future<bool> _route(String path, Route startingFrom) { |
- var baseRoute = startingFrom == null ? root : _dehandle(startingFrom); |
- _logger.finest('route $path $baseRoute'); |
- var treePath = _matchingTreePath(path, baseRoute); |
- var cmpBase = baseRoute; |
- var tail = path; |
- // Skip all routes that are unaffected by this path. |
- treePath = treePath.skipWhile((_Match matchedRoute) { |
- var skip = cmpBase._currentRoute == matchedRoute.route && |
- !_paramsChanged(cmpBase, matchedRoute.urlMatch); |
- if (skip) { |
- cmpBase = matchedRoute.route; |
- tail = matchedRoute.urlMatch.tail; |
- } |
- return skip; |
- }).toList(); |
- |
- if (treePath.isEmpty) return new Future.value(true); |
- |
- var preEnterFutures = _preEnter(tail, treePath); |
- |
- return Future.wait(preEnterFutures).then((List<bool> results) { |
- return results.any((v) => v == false) |
- ? false |
- : _processNewRoute(cmpBase, treePath, tail); |
- }); |
- } |
- |
- List<Future<bool>> _preEnter(String tail, List<_Match> treePath) { |
- var preEnterFutures = <Future<bool>>[]; |
- treePath.forEach((_Match matchedRoute) { |
- var preEnterEvent = new RoutePreEnterEvent._fromMatch(matchedRoute); |
- matchedRoute.route._onPreEnterController.add(preEnterEvent); |
- preEnterFutures.addAll(preEnterEvent._allowEnterFutures); |
- }); |
- return preEnterFutures; |
- } |
- |
- Future<bool> _processNewRoute(Route startingFrom, List<_Match> treePath, |
- String path) { |
- return _leaveOldRoutes(startingFrom, treePath).then((bool allowed) { |
- if (allowed) { |
- var base = startingFrom; |
- treePath.forEach((_Match matchedRoute) { |
- var event = new RouteEnterEvent._fromMatch(matchedRoute); |
- _unsetAllCurrentRoutes(base); |
- base._currentRoute = matchedRoute.route; |
- base._currentRoute._lastEvent = event; |
- matchedRoute.route._onEnterController.add(event); |
- base = matchedRoute.route; |
- }); |
- return true; |
- } |
- return false; |
- }); |
- } |
- |
- Future<bool> _leaveOldRoutes(RouteImpl startingFrom, List<_Match> treePath) { |
- if (treePath.isEmpty) return new Future.value(true); |
- |
- var currentRoute = startingFrom._currentRoute; |
- if (currentRoute != null && |
- currentRoute.dontLeaveOnParamChanges && |
- identical(currentRoute, treePath.last.route)) { |
- return new Future.value(true); |
- } |
- |
- var event = new RouteLeaveEvent('', {}, startingFrom); |
- return _leaveCurrentRoute(startingFrom, event); |
- } |
- |
- List _matchingRoutes(String path, RouteImpl baseRoute) { |
- var routes = baseRoute._routes.values.toList(); |
- if (sortRoutes) { |
- routes.sort((r1, r2) => r1.path.compareTo(r2.path)); |
- } |
- return routes.where((r) => r.path.match(path) != null).toList(); |
- } |
- |
- List<_Match> _matchingTreePath(String path, RouteImpl baseRoute) { |
- final treePath = <_Match>[]; |
- Route matchedRoute; |
- do { |
- matchedRoute = null; |
- List matchingRoutes = _matchingRoutes(path, baseRoute); |
- if (matchingRoutes.isNotEmpty) { |
- if (matchingRoutes.length > 1) { |
- _logger.warning("More than one route matches $path $matchingRoutes"); |
- } |
- matchedRoute = matchingRoutes.first; |
- } else { |
- if (baseRoute._defaultRoute != null) { |
- matchedRoute = baseRoute._defaultRoute; |
- } |
- } |
- if (matchedRoute != null) { |
- var match = _getMatch(matchedRoute, path); |
- treePath.add(new _Match(matchedRoute, match)); |
- baseRoute = matchedRoute; |
- path = match.tail; |
- } |
- } while (matchedRoute != null); |
- return treePath; |
- } |
- |
- bool _paramsChanged(RouteImpl baseRoute, UrlMatch match) { |
- var lastEvent = baseRoute._currentRoute._lastEvent; |
- return lastEvent.path != match.match || |
- !mapsShallowEqual(lastEvent.parameters, match.parameters); |
- } |
- |
- /// Navigates to a given relative route path, and parameters. |
- Future go(String routePath, Map parameters, |
- {Route startingFrom, bool replace: false}) { |
- var queryParams = {}; |
- var baseRoute = startingFrom == null ? this.root : _dehandle(startingFrom); |
- var newTail = baseRoute._getTailUrl(routePath, parameters, queryParams) + |
- _buildQuery(queryParams); |
- String newUrl = baseRoute._getHead(newTail, queryParams); |
- _logger.finest('go $newUrl'); |
- return route(newTail, startingFrom: baseRoute).then((success) { |
- if (success) _go(newUrl, null, replace); |
- return success; |
- }); |
- } |
- |
- /// Returns an absolute URL for a given relative route path and parameters. |
- String url(String routePath, {Route startingFrom, Map parameters}) { |
- var baseRoute = startingFrom == null ? this.root : _dehandle(startingFrom); |
- parameters = parameters == null ? {} : parameters; |
- var queryParams = {}; |
- var tail = baseRoute._getTailUrl(routePath, parameters, queryParams); |
- return (_useFragment ? '#' : '') + baseRoute._getHead(tail, queryParams) + |
- _buildQuery(queryParams); |
- } |
- |
- String _buildQuery(Map queryParams) { |
- if (queryParams.isEmpty) return ''; |
- var query = queryParams.keys.map((key) => |
- '$key=${Uri.encodeComponent(queryParams[key])}').join('&'); |
- return '?$query'; |
- } |
- |
- Route _dehandle(Route r) => r is RouteHandle ? r._getHost(r): r; |
- |
- UrlMatch _getMatch(Route route, String path) { |
- var match = route.path.match(path); |
- // default route |
- if (match == null) return new UrlMatch('', '', {}); |
- match.parameters.addAll(_parseQuery(route, path)); |
- return match; |
- } |
- |
- Map _parseQuery(Route route, String path) { |
- var params = {}; |
- if (path.indexOf('?') == -1) return params; |
- var queryStr = path.substring(path.indexOf('?') + 1); |
- queryStr.split('&').forEach((String keyValPair) { |
- List<String> keyVal = _parseKeyVal(keyValPair); |
- if (keyVal[0].startsWith('${route.name}.')) { |
- var key = keyVal[0].substring('${route.name}.'.length); |
- if (key.isNotEmpty) params[key] = Uri.decodeComponent(keyVal[1]); |
- } |
- }); |
- return params; |
- } |
- |
- List<String> _parseKeyVal(keyValPair) { |
- if (keyValPair.isEmpty) return const ['', '']; |
- var splitPoint = keyValPair.indexOf('=') == -1 ? |
- keyValPair.length : keyValPair.indexOf('=') + 1; |
- var key = keyValPair.substring(0, splitPoint + |
- (keyValPair.indexOf('=') == -1 ? 0 : -1)); |
- var value = keyValPair.substring(splitPoint); |
- return [key, value]; |
- } |
- |
- void _unsetAllCurrentRoutes(RouteImpl r) { |
- if (r._currentRoute != null) { |
- _unsetAllCurrentRoutes(r._currentRoute); |
- r._currentRoute = null; |
- } |
- } |
- |
- Future<bool> _leaveCurrentRoute(RouteImpl base, RouteLeaveEvent e) => |
- Future |
- .wait(_leaveCurrentRouteHelper(base, e)) |
- .then((values) => values.fold(true, (c, v) => c && v)); |
- |
- List<Future<bool>> _leaveCurrentRouteHelper(RouteImpl base, RouteLeaveEvent e) { |
- var futures = []; |
- if (base._currentRoute != null) { |
- List<Future<bool>> pendingResponses = <Future<bool>>[]; |
- // We create a copy of the route event |
- var event = e._clone(); |
- base._currentRoute._onLeaveController.add(event); |
- futures..addAll(event._allowLeaveFutures) |
- ..addAll(_leaveCurrentRouteHelper(base._currentRoute, event)); |
- } |
- return futures; |
- } |
- |
- /** |
- * Listens for window history events and invokes the router. On older |
- * browsers the hashChange event is used instead. |
- */ |
- void listen({bool ignoreClick: false, Element appRoot}) { |
- _logger.finest('listen ignoreClick=$ignoreClick'); |
- if (_listen) throw new StateError('listen can only be called once'); |
- _listen = true; |
- if (_useFragment) { |
- _window.onHashChange.listen((_) { |
- route(_normalizeHash(_window.location.hash)).then((allowed) { |
- // if not allowed, we need to restore the browser location |
- if (!allowed) _window.history.back(); |
- }); |
- }); |
- route(_normalizeHash(_window.location.hash)); |
- } else { |
- String getPath() => |
- '${_window.location.pathname}${_window.location.hash}'; |
- |
- _window.onPopState.listen((_) { |
- route(getPath()).then((allowed) { |
- // if not allowed, we need to restore the browser location |
- if (!allowed) _window.history.back(); |
- }); |
- }); |
- route(getPath()); |
- } |
- if (!ignoreClick) { |
- if (appRoot == null) appRoot = _window.document.documentElement; |
- _logger.finest('listen on win'); |
- appRoot.onClick |
- .where((MouseEvent e) => !(e.ctrlKey || e.metaKey || e.shiftKey)) |
- .where((MouseEvent e) => e.target is AnchorElement) |
- .listen((MouseEvent e) { |
- AnchorElement anchor = e.target; |
- if (anchor.host == _window.location.host) { |
- _logger.finest('clicked ${anchor.pathname}${anchor.hash}'); |
- e.preventDefault(); |
- var path = _useFragment |
- ? _normalizeHash(anchor.hash) |
- : '${anchor.pathname}'; |
- route(path).then((allowed) { |
- if (allowed) _go(path, null, false); |
- }); |
- } |
- }); |
- } |
- } |
- |
- String _normalizeHash(String hash) => hash.isEmpty ? '' : hash.substring(1); |
- |
- /** |
- * Navigates the browser to the path produced by [url] with [args] by calling |
- * [History.pushState], then invokes the handler associated with [url]. |
- * |
- * On older browsers [Location.assign] is used instead with the fragment |
- * version of the UrlPattern. |
- */ |
- Future<bool> gotoUrl(String url) => |
- route(url).then((success) { |
- if (success) _go(url, null, false); |
- }); |
- |
- void _go(String path, String title, bool replace) { |
- if (title == null) title = ''; |
- if (_useFragment) { |
- if (replace) { |
- _window.location.replace('#$path'); |
- } else { |
- _window.location.assign('#$path'); |
- } |
- (_window.document as HtmlDocument).title = title; |
- } else { |
- if (replace) { |
- _window.history.replaceState(null, title, path); |
- } else { |
- _window.history.pushState(null, title, path); |
- } |
- } |
- } |
- |
- /** |
- * Returns the current active route path in the route tree. |
- * Excludes the root path. |
- */ |
- List<Route> get activePath { |
- var res = <RouteImpl>[]; |
- var route = root; |
- while (route._currentRoute != null) { |
- route = route._currentRoute; |
- res.add(route); |
- } |
- return res; |
- } |
- |
- /** |
- * A shortcut for router.root.findRoute(). |
- */ |
- Route findRoute(String routePath) => root.findRoute(routePath); |
-} |
- |
-class _Match { |
- final RouteImpl route; |
- final UrlMatch urlMatch; |
- |
- _Match(this.route, this.urlMatch); |
-} |