Index: pkg/analysis_server/tool/instrumentation/page/page_writer.dart |
diff --git a/pkg/analysis_server/tool/instrumentation/page/page_writer.dart b/pkg/analysis_server/tool/instrumentation/page/page_writer.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..492f86ac328dbcd2911f309c253a98e0865318a8 |
--- /dev/null |
+++ b/pkg/analysis_server/tool/instrumentation/page/page_writer.dart |
@@ -0,0 +1,307 @@ |
+// Copyright (c) 2016, 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. |
+ |
+import 'dart:convert'; |
+ |
+import '../log/log.dart'; |
+import '../server.dart'; |
+ |
+typedef void Writer(StringSink sink); |
+ |
+/** |
+ * A class used to write an HTML page. |
+ */ |
+abstract class PageWriter { |
+ /** |
+ * The object used to escape special HTML characters. |
+ */ |
+ static final HtmlEscape htmlEscape = new HtmlEscape(); |
+ |
+ /** |
+ * Initialize a newly create page writer. |
+ */ |
+ PageWriter(); |
+ |
+ /** |
+ * Return the length of the common prefix for time stamps associated with the |
+ * given log [entries]. |
+ */ |
+ int computePrefixLength(List<LogEntry> entries) { |
+ int length = entries.length; |
+ if (length < 2) { |
+ return 0; |
+ } |
+ String firstTime = entries[0].timeStamp.toString(); |
+ String lastTime = entries[length - 1].timeStamp.toString(); |
+ int prefixLength = 0; |
+ int timeLength = firstTime.length; |
+ while (prefixLength < timeLength && |
+ firstTime.codeUnitAt(prefixLength) == |
+ lastTime.codeUnitAt(prefixLength)) { |
+ prefixLength++; |
+ } |
+ return prefixLength; |
+ } |
+ |
+ /** |
+ * Return an escaped version of the given [unsafe] text. |
+ */ |
+ String escape(String unsafe) { |
+ return htmlEscape.convert(unsafe); |
+ } |
+ |
+ /** |
+ * Write the body of the page (without the 'body' tag) to the given [sink]. |
+ */ |
+ void writeBody(StringSink sink); |
+ |
+ /** |
+ * Write the given [date] to the given [sink]. |
+ */ |
+ void writeDate(StringSink sink, DateTime date) { |
+ String isoString = date.toIso8601String(); |
+ int index = isoString.indexOf('T'); |
+ String dateString = isoString.substring(0, index); |
+ String timeString = isoString.substring(index + 1); |
+ sink.write(dateString); |
+ sink.write(' at '); |
+ sink.write(timeString); |
+ } |
+ |
+ /** |
+ * Write the body of the page (without the 'body' tag) to the given [sink]. |
+ */ |
+ void writeMenu(StringSink sink) { |
+ sink.writeln('<div class="menu">'); |
+ sink.write('<a href="${WebServer.logPath}" class="menuItem">Log</a>'); |
+ sink.write(' • '); |
+ sink.write('<a href="${WebServer.statsPath}" class="menuItem">Stats</a>'); |
+ sink.writeln('</div>'); |
+ } |
+ |
+ /** |
+ * Write the contents of the instrumentation log to the given [sink]. |
+ */ |
+ void writePage(StringSink sink) { |
+ sink.writeln('<!DOCTYPE html>'); |
+ sink.writeln('<html lang="en-US">'); |
+ sink.writeln('<head>'); |
+ sink.writeln('<meta charset="utf-8">'); |
+ sink.writeln( |
+ '<meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0">'); |
+ sink.writeln('<title>Instrumentation Log</title>'); |
+ sink.writeln('<style>'); |
+ writeStyleSheet(sink); |
+ sink.writeln('</style>'); |
+ sink.writeln('<script>'); |
+ writeScripts(sink); |
+ sink.writeln('</script>'); |
+ sink.writeln('</head>'); |
+ sink.writeln('<body>'); |
+ writeBody(sink); |
+ sink.writeln('</body>'); |
+ sink.writeln('</html>'); |
+ } |
+ |
+ /** |
+ * Write the scripts for the page (without the 'script' tag) to the given |
+ * [sink]. |
+ */ |
+ void writeScripts(StringSink sink) { |
+ // No common scripts. |
+ } |
+ |
+ /** |
+ * Write the content of the style sheet (without the 'script' tag) for the |
+ * page to the given [sink]. |
+ */ |
+ void writeStyleSheet(StringSink sink) { |
+ sink.writeln('a {'); |
+ sink.writeln(' color: #000000;'); |
+ sink.writeln(' text-decoration: none;'); |
+ sink.writeln('}'); |
+ sink.writeln('a.menuItem {'); |
+ sink.writeln(' font-weight: bold;'); |
+ sink.writeln('}'); |
+ sink.writeln('body {'); |
+ sink.writeln(' font-family: sans-serif;'); |
+ sink.writeln(' height: 100%;'); |
+ sink.writeln(' margin: 0px;'); |
+ sink.writeln(' overflow: hidden;'); |
+ sink.writeln(' padding: 0px;'); |
+ sink.writeln(' width: 100%;'); |
+ sink.writeln('}'); |
+ sink.writeln('div.columnHeader {'); |
+ sink.writeln('}'); |
+ sink.writeln('div.button {'); |
+ sink.writeln(' display: inline-block;'); |
+ sink.writeln(' border-radius: 4px;'); |
+ sink.writeln(' border: 1px solid;'); |
+ sink.writeln(' height: 16px;'); |
+ sink.writeln(' text-align: center;'); |
+ sink.writeln(' vertical-align: middle;'); |
+ sink.writeln(' width: 16px;'); |
+ sink.writeln('}'); |
+ sink.writeln('div.inset {'); |
+ sink.writeln(' padding: 10px;'); |
+ sink.writeln('}'); |
+ sink.writeln('div.menu {'); |
+ sink.writeln(' background-color: #cce6ff;'); |
+ sink.writeln(' padding: 5px;'); |
+ sink.writeln('}'); |
+ sink.writeln('html {'); |
+ sink.writeln(' height: 100%;'); |
+ sink.writeln(' width: 100%;'); |
+ sink.writeln('}'); |
+ sink.writeln('span.button {'); |
+ sink.writeln(' border-radius: 5px;'); |
+ sink.writeln(' border: 1px solid;'); |
+ sink.writeln(' height: 16px;'); |
+ sink.writeln(' width: 16px;'); |
+ sink.writeln('}'); |
+ sink.writeln('span.error {'); |
+ sink.writeln(' color: #ff0000;'); |
+ sink.writeln('}'); |
+ sink.writeln('span.gray {'); |
+ sink.writeln(' color: #777777;'); |
+ sink.writeln('}'); |
+ sink.writeln('span.label {'); |
+ sink.writeln(' font-weight: bold;'); |
+ sink.writeln('}'); |
+ sink.writeln('table.fullWidth {'); |
+ sink.writeln(' border: 0px;'); |
+ sink.writeln(' width: 100%;'); |
+ sink.writeln('}'); |
+ sink.writeln('td.halfWidth {'); |
+ sink.writeln(' width: 50%;'); |
+ sink.writeln(' vertical-align: top;'); |
+ sink.writeln('}'); |
+ sink.writeln('td.int {'); |
+ sink.writeln(' text-align: right;'); |
+ sink.writeln('}'); |
+ sink.writeln('th {'); |
+ sink.writeln(' text-align: left;'); |
+ sink.writeln('}'); |
+ sink.writeln('th.narrow {'); |
+ sink.writeln(' width: 16px;'); |
+ sink.writeln('}'); |
+ |
+ sink.writeln('#container {'); |
+ sink.writeln(' height: 100%;'); |
+ sink.writeln(' min-height: 100%;'); |
+ sink.writeln(' position: relative;'); |
+ sink.writeln(' width: 100%;'); |
+ sink.writeln('}'); |
+ sink.writeln('#content {'); |
+ sink.writeln(' height: 90%;'); |
+ sink.writeln(' width: 100%;'); |
+ sink.writeln('}'); |
+ } |
+ |
+ /** |
+ * Write to the given [sink] the HTML required to display content in two |
+ * columns. The content of the columns will be written by the functions |
+ * [writeLeftColumn], [writeCenterColumn] and [writeRightColumn] and will be |
+ * contained in 'div' elements with the id's [leftColumnId], [centerColumnId] |
+ * and [rightColumnId]. |
+ */ |
+ void writeThreeColumns( |
+ StringSink sink, |
+ String leftColumnId, |
+ Writer writeLeftColumn, |
+ String centerColumnId, |
+ Writer writeCenterColumn, |
+ String rightColumnId, |
+ Writer writeRightColumn) { |
+ sink.writeln('<div>'); |
+ sink.writeln(' <div>'); |
+ sink.writeln(' <div id="$leftColumnId">'); |
+ sink.writeln(' <div class="inset">'); |
+ writeLeftColumn(sink); |
+ sink.writeln(' </div>'); |
+ sink.writeln(' </div>'); |
+ sink.writeln(' <div id="$rightColumnId">'); |
+ sink.writeln(' <div class="inset">'); |
+ writeRightColumn(sink); |
+ sink.writeln(' </div>'); |
+ sink.writeln(' </div>'); |
+ sink.writeln(' <div id="$centerColumnId">'); |
+ sink.writeln(' <div class="inset">'); |
+ writeCenterColumn(sink); |
+ sink.writeln(' </div>'); |
+ sink.writeln(' </div>'); |
+ sink.writeln(' </div>'); |
+ sink.writeln('</div>'); |
+ } |
+ |
+ /** |
+ * Writeto the given [sink] the styles needed by a three column section where |
+ * the columns have the ids [leftColumnId], [centerColumnId] and |
+ * [rightColumnId]. |
+ */ |
+ void writeThreeColumnStyles(StringSink sink, String leftColumnId, |
+ String centerColumnId, String rightColumnId) { |
+ sink.writeln('#$leftColumnId {'); |
+ sink.writeln(' float: left;'); |
+ sink.writeln(' height: 100%;'); |
+ sink.writeln(' overflow: auto;'); |
+ sink.writeln(' width: 33%;'); |
+ sink.writeln('}'); |
+ sink.writeln('#$centerColumnId {'); |
+ sink.writeln(' height: 100%;'); |
+ sink.writeln(' overflow: auto;'); |
+ sink.writeln(' width: 33%;'); |
+ sink.writeln('}'); |
+ sink.writeln('#$rightColumnId {'); |
+ sink.writeln(' float: right;'); |
+ sink.writeln(' height: 100%;'); |
+ sink.writeln(' overflow: auto;'); |
+ sink.writeln(' width: 33%;'); |
+ sink.writeln('}'); |
+ } |
+ |
+ /** |
+ * Write to the given [sink] the HTML required to display content in two |
+ * columns. The content of the columns will be written by the functions |
+ * [writeLeftColumn] and [writeRightColumn] and will be contained in 'div' |
+ * elements with the id's [leftColumnId] and [rightColumnId]. |
+ */ |
+ void writeTwoColumns(StringSink sink, String leftColumnId, |
+ Writer writeLeftColumn, String rightColumnId, Writer writeRightColumn) { |
+ sink.writeln('<div>'); |
+ sink.writeln(' <div>'); |
+ sink.writeln(' <div id="$leftColumnId">'); |
+ sink.writeln(' <div class="inset">'); |
+ writeLeftColumn(sink); |
+ sink.writeln(' </div>'); |
+ sink.writeln(' </div>'); |
+ sink.writeln(' <div id="$rightColumnId">'); |
+ sink.writeln(' <div class="inset">'); |
+ writeRightColumn(sink); |
+ sink.writeln(' </div>'); |
+ sink.writeln(' </div>'); |
+ sink.writeln(' </div>'); |
+ sink.writeln('</div>'); |
+ } |
+ |
+ /** |
+ * Writeto the given [sink] the styles needed by a two column section where |
+ * the columns have the ids [leftColumnId] and [rightColumnId]. |
+ */ |
+ void writeTwoColumnStyles( |
+ StringSink sink, String leftColumnId, String rightColumnId) { |
+ sink.writeln('#$leftColumnId {'); |
+ sink.writeln(' float: left;'); |
+ sink.writeln(' height: 100%;'); |
+ sink.writeln(' overflow: auto;'); |
+ sink.writeln(' width: 50%;'); |
+ sink.writeln('}'); |
+ sink.writeln('#$rightColumnId {'); |
+ sink.writeln(' float: right;'); |
+ sink.writeln(' height: 100%;'); |
+ sink.writeln(' overflow: auto;'); |
+ sink.writeln(' width: 50%;'); |
+ sink.writeln('}'); |
+ } |
+} |