| // 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 observatory_element; |
| |
| import 'dart:async'; |
| import 'dart:html'; |
| import 'package:observatory/app.dart'; |
| import 'package:observatory/service.dart'; |
| import 'package:polymer/polymer.dart'; |
| |
| /// Base class for all Observatory custom elements. |
| @CustomTag('observatory-element') |
| class ObservatoryElement extends PolymerElement { |
| ObservatoryElement.created() : super.created(); |
| |
| ObservatoryApplication get app => ObservatoryApplication.app; |
| Page get page => app.currentPage; |
| |
| @override |
| void attached() { |
| super.attached(); |
| _startPoll(); |
| } |
| |
| @override |
| void attributeChanged(String name, var oldValue, var newValue) { |
| super.attributeChanged(name, oldValue, newValue); |
| } |
| |
| @override |
| void detached() { |
| super.detached(); |
| _stopPoll(); |
| } |
| |
| @override |
| void ready() { |
| super.ready(); |
| } |
| |
| /// Set to a non-null value to enable polling on this element. When the poll |
| /// timer fires, onPoll will be called. |
| @observable Duration pollPeriod; |
| Timer _pollTimer; |
| |
| /// Called every [pollPeriod] while the element is attached to the DOM. |
| void onPoll() { } |
| |
| void pollPeriodChanged(oldValue) { |
| if (pollPeriod != null) { |
| _startPoll(); |
| } else { |
| _stopPoll(); |
| } |
| } |
| |
| void _startPoll() { |
| if (pollPeriod == null) { |
| return; |
| } |
| if (_pollTimer != null) { |
| _pollTimer.cancel(); |
| } |
| _pollTimer = new Timer(pollPeriod, _onPoll); |
| } |
| |
| void _stopPoll() { |
| if (_pollTimer != null) { |
| _pollTimer.cancel(); |
| } |
| _pollTimer = null; |
| } |
| |
| void _onPoll() { |
| onPoll(); |
| if (pollPeriod == null) { |
| // Stop polling. |
| _stopPoll(); |
| return; |
| } |
| // Restart timer. |
| _pollTimer = new Timer(pollPeriod, _onPoll); |
| } |
| |
| /// Utility method for handling on-click of <a> tags. Navigates |
| /// within the application using the [LocationManager]. |
| void goto(MouseEvent event, var detail, Element target) { |
| app.locationManager.onGoto(event); |
| event.stopPropagation(); |
| } |
| |
| void onClickGoto(MouseEvent event) { |
| app.locationManager.onGoto(event); |
| event.stopPropagation(); |
| } |
| |
| String makeLink(String url, [ServiceObject obj]) { |
| if (obj != null) { |
| if (obj is Isolate) { |
| url = '${url}?isolateId=${Uri.encodeComponent(obj.id)}'; |
| } else { |
| if (obj.id == null) { |
| // No id |
| return url; |
| } |
| url = ('${url}?isolateId=${Uri.encodeComponent(obj.isolate.id)}' |
| '&objectId=${Uri.encodeComponent(obj.id)}'); |
| } |
| } |
| return url; |
| } |
| |
| /// Create a link that can be consumed by [goto]. |
| String gotoLink(String url, [ServiceObject obj]) { |
| return app.locationManager.makeLink(makeLink(url, obj)); |
| } |
| String gotoLinkForwardingParameters(String url, [ServiceObject obj]) { |
| return app.locationManager.makeLinkForwardingParameters(makeLink(url, obj)); |
| } |
| |
| String formatTimePrecise(double time) => Utils.formatTimePrecise(time); |
| String formatTimeMilliseconds(int millis) => |
| Utils.formatTimeMilliseconds(millis); |
| String formatTime(double time) => Utils.formatTime(time); |
| |
| String formatSeconds(double x) => Utils.formatSeconds(x); |
| |
| |
| String formatSize(int bytes) => Utils.formatSize(bytes); |
| |
| int parseInt(String value) => int.parse(value); |
| |
| String asStringLiteral(String value, [bool wasTruncated=false]) { |
| var result = new List(); |
| result.add("'".codeUnitAt(0)); |
| for (int codeUnit in value.codeUnits) { |
| if (codeUnit == '\n'.codeUnitAt(0)) result.addAll('\\n'.codeUnits); |
| else if (codeUnit == '\r'.codeUnitAt(0)) result.addAll('\\r'.codeUnits); |
| else if (codeUnit == '\f'.codeUnitAt(0)) result.addAll('\\f'.codeUnits); |
| else if (codeUnit == '\b'.codeUnitAt(0)) result.addAll('\\b'.codeUnits); |
| else if (codeUnit == '\t'.codeUnitAt(0)) result.addAll('\\t'.codeUnits); |
| else if (codeUnit == '\v'.codeUnitAt(0)) result.addAll('\\v'.codeUnits); |
| else if (codeUnit == '\$'.codeUnitAt(0)) result.addAll('\\\$'.codeUnits); |
| else if (codeUnit == '\\'.codeUnitAt(0)) result.addAll('\\\\'.codeUnits); |
| else if (codeUnit == "'".codeUnitAt(0)) result.addAll("'".codeUnits); |
| else if (codeUnit < 32) { |
| var escapeSequence = "\\u" + codeUnit.toRadixString(16).padLeft(4, "0"); |
| result.addAll(escapeSequence.codeUnits); |
| } else result.add(codeUnit); |
| } |
| if (wasTruncated) { |
| result.addAll("...".codeUnits); |
| } else { |
| result.add("'".codeUnitAt(0)); |
| } |
| return new String.fromCharCodes(result); |
| } |
| |
| void clearShadowRoot() { |
| // Remove all non-style elements. |
| // Have to do the following because removeWhere doesn't work on DOM child |
| // node lists. i.e. removeWhere((e) => e is! StyleElement); |
| var styleElements = []; |
| for (var child in shadowRoot.children) { |
| if (child is StyleElement) { |
| styleElements.add(child); |
| } |
| } |
| shadowRoot.children.clear(); |
| for (var style in styleElements) { |
| shadowRoot.children.add(style); |
| } |
| } |
| |
| void insertTextSpanIntoShadowRoot(String text) { |
| var spanElement = new SpanElement(); |
| spanElement.text = text; |
| shadowRoot.children.add(spanElement); |
| } |
| |
| void insertLinkIntoShadowRoot(String label, String href, [String title]) { |
| var anchorElement = new AnchorElement(); |
| anchorElement.href = href; |
| anchorElement.text = label; |
| if (title != null) { |
| anchorElement.title = title; |
| } |
| anchorElement.onClick.listen(onClickGoto); |
| shadowRoot.children.add(anchorElement); |
| } |
| |
| |
| var _onCopySubscription; |
| /// Exclude nodes from being copied, for example the line numbers and |
| /// breakpoint toggles in script insets. Must be called after [root]'s |
| /// children have been added, and only supports one node at a time. |
| void makeCssClassUncopyable(Element root, String className) { |
| var noCopyNodes = root.getElementsByClassName(className); |
| for (var node in noCopyNodes) { |
| node.style.setProperty('-moz-user-select', 'none'); |
| node.style.setProperty('-khtml-user-select', 'none'); |
| node.style.setProperty('-webkit-user-select', 'none'); |
| node.style.setProperty('-ms-user-select', 'none'); |
| node.style.setProperty('user-select', 'none'); |
| } |
| if (_onCopySubscription != null) { |
| _onCopySubscription.cancel(); |
| } |
| _onCopySubscription = root.onCopy.listen((event) { |
| // Mark the nodes as hidden before the copy happens, then mark them as |
| // visible on the next event loop turn. |
| for (var node in noCopyNodes) { |
| node.style.visibility = 'hidden'; |
| } |
| Timer.run(() { |
| for (var node in noCopyNodes) { |
| node.style.visibility = 'visible'; |
| } |
| }); |
| }); |
| } |
| } |