Index: lib/runtime/messages_widget.dart |
diff --git a/lib/runtime/messages_widget.dart b/lib/runtime/messages_widget.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4c4d24882d24e97bdfa9e00dea8cf73921540158 |
--- /dev/null |
+++ b/lib/runtime/messages_widget.dart |
@@ -0,0 +1,120 @@ |
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
Siggi Cherem (dart-lang)
2015/03/07 02:43:01
this is adapted from the code in polymer. Simplifi
|
+// 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. |
+ |
+/// This library provides a single function called injectLogs which when called |
+/// will request a logs json file and build a small widget out of them which |
+/// groups the logs by level. |
+library polymer.build.log_injector; |
+ |
+import 'dart:async'; |
+import 'dart:convert'; |
+import 'dart:html'; |
+ |
+import 'package:path/path.dart' as path; |
+import 'package:source_span/source_span.dart'; |
+import 'package:dev_compiler/src/summary.dart'; |
+ |
+main() async => displayMessages(await HttpRequest.getString('messages.json')); |
+ |
+void displayMessages(String data) { |
+ var summary = GlobalSummary.parse(JSON.decode(data)); |
+ var messagesByLevel = _getMessagesByLevel(summary); |
+ if (messagesByLevel.isEmpty) return; |
+ |
+ // Build the wrapper, menu, and content divs. |
+ var menuWrapper = new DivElement()..classes.add('menu'); |
+ var contentWrapper = new DivElement()..classes.add('content'); |
+ var wrapperDiv = new DivElement() |
+ ..classes.add('dev-compiler-messages') |
+ ..append(menuWrapper) |
+ ..append(contentWrapper); |
+ |
+ var selectedMenu = new _Selection(); |
+ var selectedContent = new _Selection(); |
+ |
+ // Add a menu item, content section, and messages for each log level. |
+ messagesByLevel.forEach((level, messages) { |
+ var contentItem = new DivElement()..classes.add(level); |
+ var menuItem = new Element.html('<div class="$level">' |
+ '$level <span class="num">(${messages.length})</span>' |
+ '</div>'); |
+ menuWrapper.append(menuItem); |
+ contentWrapper.append(contentItem); |
+ menuItem.onClick.listen((_) { |
+ selectedMenu.select(menuItem); |
+ selectedContent.select(contentItem); |
+ }); |
+ |
+ // TODO(sigmund): add permanent links to error messages like in polymer. |
+ for (var m in messages) { |
+ var message = _hyperlinkUrls(_escape(m.message)); |
+ var span = m.span; |
+ var sb = new StringBuffer(); |
+ sb.write('<div class="message"><div class="text $level">$message</div>'); |
+ if (span != null) { |
+ sb.write('<div class="location">' |
+ ' <span class="location">${span.start.toolString}</span></div>' |
+ ' <span class="text">${_escape(span.text)}</span></div></div>'); |
+ } |
+ sb.write('</div>'); |
+ |
+ var logElement = new Element.html('$sb', |
+ validator: new NodeValidatorBuilder.common() |
+ ..allowNavigation(new _OpenUriPolicy())); |
+ contentItem.append(logElement); |
+ var messageElement = logElement.querySelector('div.text'); |
+ messageElement.onClick.listen((e) { |
+ if (e.target == messageElement) { |
+ messageElement.classes.toggle('expanded'); |
+ } |
+ }); |
+ } |
+ }); |
+ |
+ document.body.append(wrapperDiv); |
+} |
+ |
+/// Toggles classes to match which item of a list of items is selected. |
+class _Selection { |
+ Element _selected; |
+ |
+ select(Element newItem) { |
+ if (_selected == newItem) { |
+ _selected = null; |
+ } else { |
+ if (_selected != null) { |
+ _selected.classes.remove('active'); |
+ } |
+ _selected = newItem; |
+ } |
+ newItem.classes.toggle('active'); |
+ } |
+} |
+ |
+final _urlRegex = new RegExp('http://[^ ]*'); |
+final _escaper = new HtmlEscape(); |
+String _hyperlinkUrls(String text) => text.replaceAllMapped(_urlRegex, |
+ (m) => '<a href="${m.group(0)}" target="blank">${m.group(0)}</a>'); |
+String _escape(String text) => _escaper.convert(text); |
+ |
+class _OpenUriPolicy implements UriPolicy { |
+ bool allowsUri(String uri) => true; |
+} |
+ |
+Map<String, List<MessageSummary>> _getMessagesByLevel(GlobalSummary messages) { |
+ var visitor = new _Visitor(); |
+ messages.accept(visitor); |
+ return visitor.messagesByLevel; |
+} |
+ |
+class _Visitor extends RecursiveSummaryVisitor { |
+ final Map<String, List<MessageSummary>> messagesByLevel = {}; |
+ |
+ @override |
+ void visitMessage(MessageSummary message) { |
+ var level = message.level.toLowerCase(); |
+ messagesByLevel.putIfAbsent(level, () => []); |
+ messagesByLevel[level].add(message); |
+ } |
+} |