blob: 164f0798713239e77c0194eac928db5cfa37814e [file] [log] [blame]
// 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';
}
});
});
}
}