| // 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. |
| |
| part of app; |
| |
| class LocationManager { |
| final ObservatoryApplication _app; |
| |
| /// [internalArguments] are parameters specified after a '---' in the |
| /// application URL. |
| final Map<String, String> internalArguments = new Map<String, String>(); |
| |
| Uri? _uri; |
| |
| /// [uri] is the application uri. Application uris consist of a path and |
| /// the queryParameters map. |
| Uri get uri => _uri!; |
| |
| LocationManager(this._app) { |
| web.window.onPopState.listen(_onBrowserNavigation); |
| // Determine initial application path. |
| var applicationPath = '${web.window.location.hash}'; |
| if ((web.window.location.hash == '') || (web.window.location.hash == '#')) { |
| // Observatory has loaded but no application path has been specified, |
| // use the default. |
| // By default we navigate to the VM page. |
| applicationPath = Uris.vm(); |
| } |
| // Update current application path. |
| web.window.history.replaceState( |
| applicationPath.toJS, |
| web.document.title, |
| applicationPath, |
| ); |
| _updateApplicationLocation(applicationPath); |
| } |
| |
| bool getBoolParameter(String name, bool defaultValue) { |
| var value = uri.queryParameters[name]; |
| if ("true" == value) return true; |
| if ("false" == value) return false; |
| return defaultValue; |
| } |
| |
| /// Called whenever the browser changes the location bar (e.g. forward or |
| /// back button press). |
| void _onBrowserNavigation(web.PopStateEvent event) { |
| _updateApplicationLocation(web.window.location.hash); |
| _visit(); |
| } |
| |
| /// Given an application url, generate an href link. |
| String makeLink(String url) => '#$url'; |
| |
| /// Update the application location. After this function returns, |
| /// [uri] and [debugArguments] will be updated. |
| _updateApplicationLocation(String url) { |
| if (url == Uris.vmConnect()) { |
| // When we go to the vm-connect page, drop all notifications. |
| _app.notifications.deleteAll(); |
| } |
| |
| // Chop off leading '#'. |
| if (url.startsWith('#')) { |
| url = url.substring(1); |
| } |
| // Fall through handles '#/' |
| // Chop off leading '/'. |
| if (url.startsWith('/')) { |
| url = url.substring(1); |
| } |
| // Parse out debug arguments. |
| if (url.contains('---')) { |
| var chunks = url.split('---'); |
| url = chunks[0]; |
| if ((chunks.length > 1) && (chunks[1] != '')) { |
| internalArguments.clear(); |
| try { |
| internalArguments.addAll(Uri.splitQueryString(chunks[1])); |
| } catch (e) { |
| Logger.root.warning('Could not parse debug arguments ${e}'); |
| } |
| } |
| } |
| _uri = Uri.parse(url); |
| } |
| |
| /// Add [url] to the browser history. |
| _addToBrowserHistory(String url) { |
| web.window.history.pushState(url.toJS, web.document.title, url); |
| } |
| |
| /// Notify the current page that something has changed. |
| _visit() { |
| Chain.capture( |
| () => _app._visit(_uri!, internalArguments), |
| onError: (e, st) { |
| if (e is IsolateNotFound) { |
| var newPath = _app.vm.isDisconnected |
| ? '/vm-connect' |
| : '/isolate-reconnect'; |
| var parameters = <String, dynamic>{}; |
| parameters.addAll(_uri!.queryParameters); |
| parameters['originalUri'] = _uri.toString(); |
| parameters['isolateId'] = parameters['isolateId']; |
| var generatedUri = new Uri( |
| path: newPath, |
| queryParameters: parameters, |
| ); |
| go(makeLink(generatedUri.toString()), true); |
| return; |
| } |
| // Surface any uncaught exceptions. |
| _app.handleException(e, st); |
| }, |
| ); |
| } |
| |
| /// Navigate to [url]. |
| void go(String url, [bool addToBrowserHistory = true]) { |
| if (addToBrowserHistory) { |
| _addToBrowserHistory(url); |
| } |
| _updateApplicationLocation(url); |
| _visit(); |
| } |
| |
| /// Starting with the current uri path and queryParameters, update |
| /// queryParameters present in [updateParameters], then generate a new uri |
| /// and navigate to that. |
| goReplacingParameters( |
| Map updatedParameters, [ |
| bool addToBrowserHistory = true, |
| ]) { |
| go(makeLinkReplacingParameters(updatedParameters), addToBrowserHistory); |
| } |
| |
| makeLinkReplacingParameters(Map updatedParameters) { |
| var parameters = new Map<String, dynamic>.from(_uri!.queryParameters); |
| updatedParameters.forEach((k, v) { |
| parameters[k] = v; |
| }); |
| // Ensure path starts with a slash. |
| var path = uri.path.startsWith('/') ? uri.path : '/${uri.path}'; |
| var generatedUri = new Uri(path: path, queryParameters: parameters); |
| return makeLink(generatedUri.toString()); |
| } |
| |
| goForwardingParameters(String newPath, [bool addToBrowserHistory = true]) { |
| go(makeLinkForwardingParameters(newPath), addToBrowserHistory); |
| } |
| |
| makeLinkForwardingParameters(String newPath) { |
| var parameters = _uri!.queryParameters; |
| var generatedUri = new Uri(path: newPath, queryParameters: parameters); |
| return makeLink(generatedUri.toString()); |
| } |
| |
| /// Utility event handler when clicking on application url link. |
| void onGoto(web.MouseEvent event) { |
| if ((event.button > 0) || |
| event.metaKey || |
| event.ctrlKey || |
| event.shiftKey || |
| event.altKey) { |
| // Mouse event is not a left-click OR |
| // mouse event is a left-click with a modifier key: |
| // let browser handle. |
| return; |
| } |
| event.preventDefault(); |
| // 'currentTarget' is the dom element that would process the event. |
| // If we use 'target' we might get an <em> element or somesuch. |
| web.HTMLElement target = event.currentTarget as web.HTMLElement; |
| go(target.attributes.getNamedItem('href')!.value); |
| } |
| } |