├── .gitignore ├── LICENSE ├── README.md ├── build.dart ├── dump-info-viewer.png ├── lib ├── dump_viz.dart └── src │ ├── dependency_view.dart │ ├── dependency_view.html │ ├── deps_icon.svg │ ├── diff_alg.dart │ ├── diff_view.dart │ ├── diff_view.html │ ├── drag_drop_view.dart │ ├── drag_drop_view.html │ ├── hierarchy_view.dart │ ├── hierarchy_view.html │ ├── history_state.dart │ ├── info_helper.dart │ ├── logical_row.dart │ ├── program_info_view.dart │ ├── program_info_view.html │ ├── tree_table.dart │ └── tree_table.html ├── pubspec.lock ├── pubspec.yaml ├── test ├── diff_test.dart ├── info_helper_test.dart └── sample.info.json └── web ├── favicon.ico ├── index.html ├── viewer.css └── viewer.dart /.gitignore: -------------------------------------------------------------------------------- 1 | packages 2 | .buildlog 3 | .pub 4 | build/ 5 | .packages 6 | .idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014, the Dart project authors. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following 11 | disclaimer in the documentation and/or other materials provided 12 | with the distribution. 13 | * Neither the name of Google LLC nor the names of its 14 | contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Repo deprecation notice 2 | 3 | **NOTE**: This repository has been deprecated; for information and tooling related to 4 | understanding the size of your compiled web applications, see instead 5 | [dart.dev/go/dart2js-info](https://dart.dev/go/dart2js-info). 6 | 7 | ## Dump-Info visualizer 8 | 9 | A web based visualizer for the dart2js `--dump-info` option. 10 | 11 | [Live Website](https://dart-lang.github.io/dump-info-visualizer/) 12 | 13 | ## Screenshot 14 | 15 | ![](dump-info-viewer.png) 16 | 17 | ## How to Build 18 | 19 | The dump-info-visualizer is a Pub project, so running `pub build` will 20 | generate all the files for the viewer. 21 | 22 | This repository also hosts the public version of the viewer which is located 23 | on the `gh-pages` branch. Any files pushed to `gh-pages` will be made public. 24 | 25 | In order to make your changes public, follow these instructions. 26 | 27 | * `git checkout master` Your changes should already be on the master branch 28 | when you deploy. 29 | * `pub build` Build all of the javascript and HTML files. 30 | * `mv build ../` Copy built files out of the project structure. 31 | * `git checkout gh-pages` The destination branch. 32 | * `rm -rf build` Remove old build. 33 | * `mv ../build ./` Copy new build in. 34 | * `git commit -a -m "your message here"` Commit the new build. 35 | * `git push origin gh-pages` Deploy to gh-pages. 36 | -------------------------------------------------------------------------------- /build.dart: -------------------------------------------------------------------------------- 1 | import 'package:polymer/builder.dart'; 2 | 3 | main(args) { 4 | build(entryPoints: ['web/index.html'], options: parseOptions(args)); 5 | } 6 | -------------------------------------------------------------------------------- /dump-info-viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dart-archive/dump-info-visualizer/9d0d1b363fc1fa9161bf9a28b3a1f5b70b93cf8a/dump-info-viewer.png -------------------------------------------------------------------------------- /lib/dump_viz.dart: -------------------------------------------------------------------------------- 1 | library dump_viz; 2 | 3 | export 'package:dump_viz/src/dependency_view.dart'; 4 | export 'package:dump_viz/src/diff_view.dart'; 5 | export 'package:dump_viz/src/drag_drop_view.dart'; 6 | export 'package:dump_viz/src/hierarchy_view.dart'; 7 | export 'package:dump_viz/src/history_state.dart'; 8 | export 'package:dump_viz/src/info_helper.dart'; 9 | export 'package:dump_viz/src/program_info_view.dart'; 10 | -------------------------------------------------------------------------------- /lib/src/dependency_view.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | library dump_viz.dependency_view; 6 | 7 | import 'dart:html' hide Selection; 8 | 9 | import 'package:polymer/polymer.dart'; 10 | 11 | import 'history_state.dart'; 12 | import 'info_helper.dart'; 13 | 14 | @CustomTag('dependency-view') 15 | class DependencyView extends PolymerElement { 16 | InfoHelper _dumpInfo; 17 | String _currentlyTargeting; 18 | 19 | TableSectionElement ownersTable; 20 | TableSectionElement currentTable; 21 | TableSectionElement ownedTable; 22 | 23 | DependencyView.created() : super.created() {} 24 | 25 | set target(String id) { 26 | $['information'].style.display = 'none'; 27 | $['tables'].style.display = 'block'; 28 | _currentlyTargeting = id; 29 | _populate(id); 30 | } 31 | 32 | String get target { 33 | return _currentlyTargeting; 34 | } 35 | 36 | set dumpInfo(InfoHelper dumpInfo) { 37 | TableSectionElement getTbody(TableElement table) => 38 | table.querySelector('tbody'); 39 | 40 | _dumpInfo = dumpInfo; 41 | ownersTable = getTbody($['in']); 42 | currentTable = getTbody($['current']); 43 | ownedTable = getTbody($['out']); 44 | } 45 | 46 | TableRowElement _generateRow(String id, String mask) { 47 | List path = _dumpInfo.path(id); 48 | if (path == null) return null; 49 | // TODO(TyOverby): Make a polymer element to abstract this mess 50 | return new TableRowElement() 51 | ..children.addAll([ 52 | // Name Column 53 | new TableCellElement()..text = path.join('.'), 54 | new TableCellElement()..text = mask, 55 | // Stats Column 56 | new TableCellElement() 57 | ..children.add(new SpanElement() 58 | ..text = '↖ ${_dumpInfo.reverseDependencies(id).length} | ' 59 | '${_dumpInfo.dependencies(id).length} ↘' 60 | ..style.float = 'right') 61 | ]) 62 | ..onClick.listen( 63 | (_) => HistoryState.switchTo(new HistoryState('dep', depTarget: id))); 64 | } 65 | 66 | void _populate(String id) { 67 | ownersTable.children.clear(); 68 | currentTable.children.clear(); 69 | ownedTable.children.clear(); 70 | 71 | List owners = _dumpInfo.reverseDependencies(id); 72 | List owned = _dumpInfo.dependencies(id); 73 | 74 | ownersTable.children.addAll(_sortedRows(owners)); 75 | currentTable.children.add(_generateRow(id, "")); 76 | ownedTable.children.addAll(_sortedRows(owned)); 77 | } 78 | 79 | Iterable _sortedRows(Iterable ids) { 80 | var sorted = ids.toList() 81 | ..sort( 82 | (sel1, sel2) => _dumpInfo.reverseDependencies(sel1.elementId).length - 83 | _dumpInfo.reverseDependencies(sel2.elementId).length); 84 | return sorted 85 | .map((s) => _generateRow(s.elementId, s.mask)) 86 | .where((a) => a != null); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/src/dependency_view.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /lib/src/deps_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 27 | 33 | 34 | 41 | 47 | 48 | 55 | 61 | 62 | 69 | 75 | 76 | 83 | 89 | 90 | 91 | 111 | 115 | 119 | 123 | 124 | 126 | 127 | 129 | image/svg+xml 130 | 132 | 133 | 134 | 135 | 136 | 141 | 148 | 152 | 155 | 160 | 165 | 166 | 174 | 175 | 179 | 182 | 187 | 192 | 193 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /lib/src/diff_alg.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | library dump_viz.diff_alg; 6 | 7 | import 'info_helper.dart'; 8 | 9 | class DiffItem { 10 | final String kind; 11 | final String path; 12 | final int diff; 13 | 14 | DiffItem(this.kind, this.path, this.diff); 15 | bool operator ==(other) { 16 | return other.kind == kind && other.path == path && other.diff == diff; 17 | } 18 | int get hashCode { 19 | int result = 17; 20 | result = 37 * result + kind.hashCode; 21 | result = 37 * result + path.hashCode; 22 | result = 37 * result + diff.hashCode; 23 | return result; 24 | } 25 | } 26 | 27 | List diff(InfoHelper before, InfoHelper after) { 28 | List changedElements = []; 29 | for (String path in before.joinedPaths) { 30 | String beforeId = before.idFromJoinedPath(path); 31 | int beforeSize = before.sizeOf(beforeId); 32 | if (beforeSize == null) continue; 33 | if (after.idFromJoinedPath(path) != null) { 34 | String afterId = after.idFromJoinedPath(path); 35 | int afterSize = after.sizeOf(afterId); 36 | if (afterSize == null) continue; 37 | int diff = afterSize - beforeSize; 38 | if (diff == 0) { 39 | continue; 40 | } else if (diff > 0) { 41 | changedElements.add(new DiffItem('partial-add', path, diff)); 42 | } else { 43 | changedElements.add(new DiffItem('partial-remove', path, diff)); 44 | } 45 | } else { 46 | changedElements.add(new DiffItem("full-remove", path, -beforeSize)); 47 | } 48 | } 49 | 50 | for (String path in after.joinedPaths) { 51 | String afterId = after.idFromJoinedPath(path); 52 | int afterSize = after.sizeOf(afterId); 53 | if (afterSize == null) continue; 54 | if (before.idFromJoinedPath(path) == null) { 55 | changedElements.add(new DiffItem("full-add", path, afterSize)); 56 | } 57 | } 58 | 59 | changedElements.sort((a, b) => -a.diff.abs().compareTo(b.diff.abs())); 60 | return changedElements; 61 | } 62 | -------------------------------------------------------------------------------- /lib/src/diff_view.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | library dump_viz.diff_view; 6 | 7 | import 'dart:convert'; 8 | import 'dart:html' hide Selection; 9 | 10 | import 'package:polymer/polymer.dart'; 11 | 12 | import 'diff_alg.dart'; 13 | import 'drag_drop_view.dart'; 14 | import 'info_helper.dart'; 15 | 16 | @CustomTag('diff-view') 17 | class DiffView extends PolymerElement { 18 | UListElement get _list => $['list']; 19 | InfoHelper currentlyLoaded; 20 | 21 | DiffView.created() : super.created(); 22 | 23 | InfoHelper _beforeContent; 24 | InfoHelper _afterContent; 25 | 26 | DragDropView get _beforeView => $['before-drop'] as DragDropView; 27 | DragDropView get _afterView => $['after-drop'] as DragDropView; 28 | 29 | ButtonElement get _beforeUseCurrent => 30 | $['before-current-btn'] as ButtonElement; 31 | ButtonElement get _afterUseCurrent => $['after-current-btn'] as ButtonElement; 32 | 33 | void ready() { 34 | assert(currentlyLoaded != null); 35 | 36 | _beforeView.onFile.map(_strToHelper).listen((InfoHelper ih) { 37 | _update(ih, null); 38 | }); 39 | 40 | _afterView.onFile.map(_strToHelper).listen((InfoHelper ih) { 41 | _update(null, ih); 42 | }); 43 | 44 | _beforeUseCurrent.onClick.listen((_) { 45 | _update(currentlyLoaded, null); 46 | }); 47 | 48 | _afterUseCurrent.onClick.listen((_) { 49 | _update(null, currentlyLoaded); 50 | }); 51 | } 52 | 53 | void _update(InfoHelper before, InfoHelper after) { 54 | if (before != null) { 55 | _beforeContent = before; 56 | } 57 | 58 | if (after != null) { 59 | _afterContent = after; 60 | } 61 | 62 | _diff(); 63 | } 64 | 65 | void _diff() { 66 | _list.children.clear(); 67 | 68 | if (_beforeContent == null || _afterContent == null) { 69 | return; 70 | } 71 | 72 | var diffItems = diff(_beforeContent, _afterContent); 73 | 74 | for (DiffItem diffItem in diffItems) { 75 | _addRow(diffItem); 76 | } 77 | } 78 | 79 | void _addRow(DiffItem row) { 80 | var e = new Element.tag('li') 81 | ..classes.add(row.kind) 82 | ..children.addAll([ 83 | new SpanElement()..text = row.path, 84 | new SpanElement() 85 | ..text = row.diff.toString() 86 | ..style.float = "right" 87 | ]); 88 | _list.children.add(e); 89 | } 90 | } 91 | 92 | InfoHelper _strToHelper(String input) => 93 | new InfoHelper.fromJson(JSON.decode(input)); 94 | -------------------------------------------------------------------------------- /lib/src/diff_view.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /lib/src/drag_drop_view.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | library dump_viz.dragdrop; 6 | 7 | import 'dart:async'; 8 | import 'dart:html'; 9 | 10 | import 'package:polymer/polymer.dart'; 11 | 12 | @CustomTag('drag-drop-view') 13 | class DragDropView extends PolymerElement { 14 | Element get _dropZone => $['drag-target']; 15 | Element get _fileUpload => $['file_upload']; 16 | 17 | DragDropView.created() : super.created(); 18 | 19 | final StreamController _streamController = 20 | new StreamController(); 21 | 22 | Stream get onFile => _streamController.stream; 23 | 24 | void ready() { 25 | _fileUpload.onChange.listen((event) { 26 | var file = (event.target as InputElement).files.first; 27 | _loadFile(file); 28 | }); 29 | 30 | _dropZone.onDragOver.listen((e) { 31 | e.stopPropagation(); 32 | e.preventDefault(); 33 | _dropZone.style.backgroundColor = 'rgb(200,200,200)'; 34 | }); 35 | 36 | _dropZone.onDrop.listen((e) { 37 | e.stopPropagation(); 38 | e.preventDefault(); 39 | File file = e.dataTransfer.files.first; 40 | _loadFile(file); 41 | }); 42 | } 43 | 44 | void hide() { 45 | _dropZone.style.display = 'none'; 46 | } 47 | 48 | void show() { 49 | _dropZone.style.display = 'block'; 50 | } 51 | 52 | void _loadFile(File file) { 53 | FileReader reader = new FileReader(); 54 | 55 | reader.onLoad.first.then((_) { 56 | String fileContents = reader.result; 57 | // Substring because fileContents contains the mime type 58 | var contents = 59 | window.atob(fileContents.substring(fileContents.indexOf(',') + 1)); 60 | _dropZone.style.backgroundColor = ''; 61 | _streamController.add(contents); 62 | }); 63 | reader.readAsDataUrl(file); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/src/drag_drop_view.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /lib/src/hierarchy_view.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | library dump_viz.hierarchy_view; 6 | 7 | import 'dart:html'; 8 | 9 | import 'package:polymer/polymer.dart'; 10 | 11 | import 'history_state.dart'; 12 | import 'info_helper.dart'; 13 | import 'logical_row.dart'; 14 | import 'tree_table.dart'; 15 | 16 | const _columnNames = const ['Kind', 'Name', 'Bytes', 'Bytes R', '%', 'Type']; 17 | 18 | const _columnHelp = const [ 19 | '', 20 | 'The given name of the element', 21 | 'The direct size attributed to the element', 22 | 'The sum of the sizes of all the elements that can ' 23 | 'only be reached from this element', 24 | 'The percentage of the direct size compared to the program size', 25 | 'The given type of the element' 26 | ]; 27 | 28 | const _columnSizes = const ["200px", null, "100px", "100px", "70px", null]; 29 | 30 | @CustomTag('hierarchy-view') 31 | class HierarchyView extends PolymerElement { 32 | HierarchyView.created() : super.created(); 33 | 34 | InfoHelper _model; 35 | 36 | TreeTable get _treeTable => $['treeTable']; 37 | SelectElement get _select => $['selectSort']; 38 | 39 | void ready() { 40 | super.ready(); 41 | 42 | // Sort by chosen sorting methods. 43 | _select.value = 'name'; 44 | 45 | _select.onChange.listen((e) { 46 | _treeTable.sort(_sortBy); 47 | }); 48 | } 49 | 50 | String get _sortBy => _select.value; 51 | 52 | void clear() { 53 | _treeTable.clear(); 54 | } 55 | 56 | void set dumpInfo(InfoHelper info) { 57 | this._model = info; 58 | _display(); 59 | _treeTable.sort(_sortBy); 60 | _treeTable.reset(); 61 | } 62 | 63 | void _display() { 64 | _treeTable.columnInfo(_columnNames, _columnHelp, _columnSizes); 65 | 66 | int programSize = _model.size; 67 | 68 | // A helper function for lazily constructing the tree 69 | LogicalRow buildTree(String id, bool isTop, HtmlElement tbody, int level) { 70 | Map node = _model.elementById(id); 71 | if (node['size'] == null) { 72 | node['size'] = _computeSize(node, _model.elementById); 73 | } 74 | node['size_percent'] = 75 | (100 * node['size'] / programSize).toStringAsFixed(2) + '%'; 76 | 77 | var row = new LogicalRow(node, _renderRow, tbody, level); 78 | _addMetadata(node, row, tbody, level + 1, _model.elementById); 79 | 80 | if (isTop) { 81 | _treeTable.addTopLevel(row); 82 | } 83 | 84 | if (node['children'] != null) { 85 | for (var childId in node['children']) { 86 | // Thunk! Lazy tree creation happens in this closure. 87 | row.addChild(() => buildTree(childId, false, tbody, level + 1)); 88 | } 89 | } 90 | return row; 91 | } 92 | 93 | // Start building the tree from the libraries because 94 | // libraries are always the top level. 95 | for (String libraryId in _model.allOfType('library').map((a) => a['id'])) { 96 | buildTree('$libraryId', true, _treeTable.tbody, 0).show(); 97 | } 98 | } 99 | 100 | /** 101 | * A helper method for adding rows that are not elements but instead provide 102 | * extra information about an element. 103 | */ 104 | void _addMetadata(Map node, LogicalRow row, 105 | HtmlElement tbody, int level, Function fetch) { 106 | 107 | // A helper method for generating a row-generating function. 108 | GenerateRowFunction renderSelfWith(Function renderFn, 109 | {int sortPriority: 0}) { 110 | void render(TreeTableRow row, LogicalRow lRow) { 111 | row.data = renderFn(); 112 | } 113 | return () { 114 | LogicalRow lrow = 115 | new LogicalRow(node, render, row.parentElement, level); 116 | lrow.sortable = false; 117 | lrow.nonSortablePriority = sortPriority; 118 | return lrow; 119 | }; 120 | } 121 | 122 | switch (node['kind']) { 123 | case 'function': 124 | case 'closure': 125 | case 'constructor': 126 | case 'method': 127 | // Side Effects 128 | row.addChild(renderSelfWith(() => [ 129 | _cell('side effects'), 130 | _cell(node['sideEffects'], colspan: '5') 131 | ])); 132 | // Modifiers 133 | if (node.containsKey('modifiers')) { 134 | (node['modifiers'] as Map).forEach((k, v) { 135 | if (v) { 136 | row.addChild(renderSelfWith( 137 | () => [_cell('modifier'), _cell(k, colspan: '5')])); 138 | } 139 | }); 140 | } 141 | // Return type 142 | row.addChild(renderSelfWith(() => [ 143 | _cell('return type'), 144 | _typeCell(node['returnType'], node['inferredReturnType'], 145 | colspan: '5') 146 | ])); 147 | // Parameters 148 | if (node.containsKey('parameters')) { 149 | for (Map param in node['parameters']) { 150 | String declaredType = param['declaredType'] == null 151 | ? "unavailable" 152 | : param['declaredType']; 153 | row.addChild(renderSelfWith(() => [ 154 | _cell('parameter'), 155 | _cell(param['name']), 156 | _typeCell(declaredType, param['type'], colspan: '4') 157 | ])); 158 | } 159 | } 160 | // Code 161 | if (node['code'] != null && node['code'].length != 0) { 162 | row.addChild(renderSelfWith(() => [ 163 | _cell('code'), 164 | _cell(node['code'], colspan: '5', pre: true) 165 | ], sortPriority: -1)); 166 | } 167 | break; 168 | case 'field': 169 | // Code 170 | if (node['code'] != null && node['code'].length != 0) { 171 | row.addChild(renderSelfWith(() => [ 172 | _cell('code'), 173 | _cell(node['code'], colspan: '5', pre: true) 174 | ], sortPriority: -1)); 175 | } 176 | // Types 177 | if (node['inferredType'] != null && node['type'] != null) { 178 | row.addChild(renderSelfWith(() => [ 179 | _cell('type'), 180 | _typeCell(node['type'], node['inferredType'], colspan: '5') 181 | ])); 182 | } 183 | break; 184 | case 'class': 185 | case 'library': 186 | // Show how much of the size we can't account for. 187 | row.addChild(renderSelfWith(() => [ 188 | _cell('scaffolding'), 189 | _cell('(unaccounted for)'), 190 | _cell(node['size'] - _computeSize(node, fetch, force: true), 191 | align: 'right') 192 | ])); 193 | break; 194 | } 195 | } 196 | 197 | void _renderRow(TreeTableRow row, LogicalRow logicalRow) { 198 | Map props = logicalRow.data; 199 | List cells = [_cell(props['kind']),]; 200 | 201 | switch (props['kind']) { 202 | case 'function': 203 | case 'closure': 204 | case 'constructor': 205 | case 'method': 206 | case 'field': 207 | var span = new SpanElement(); 208 | span.text = props['name']; 209 | 210 | var anchor = new AnchorElement(); 211 | anchor.onClick.listen((_) { 212 | HistoryState 213 | .switchTo(new HistoryState('dep', depTarget: props['id'])); 214 | }); 215 | anchor.children.add( 216 | new ImageElement(src: 'packages/dump_viz/src/deps_icon.svg') 217 | ..style.float = 'right'); 218 | 219 | cells.addAll([ 220 | new TableCellElement()..children.addAll([span, anchor]), 221 | _cell(props['size'], align: 'right'), 222 | _cell(_model.triviallyOwnedSize(props['id']), align: 'right'), 223 | _cell(props['size_percent'], align: 'right'), 224 | _cell(props['type'], pre: true) 225 | ]); 226 | break; 227 | case 'library': 228 | cells.addAll([ 229 | _cell(props['canonicalUri']), 230 | _cell(props['size'], align: 'right'), 231 | _cell(''), 232 | _cell(props['size_percent'], align: 'right'), 233 | _cell('') 234 | ]); 235 | break; 236 | case 'typedef': 237 | cells.addAll([ 238 | _cell(props['name']), 239 | _cell('0', align: 'right'), 240 | _cell('0', align: 'right'), 241 | _cell('0.00%', align: 'right') 242 | ]); 243 | break; 244 | case 'class': 245 | cells.addAll([ 246 | _cell(props['name']), 247 | _cell(props['size'], align: 'right'), 248 | _cell(''), 249 | _cell(props['size_percent'], align: 'right'), 250 | _cell(props['name'], pre: true) 251 | ]); 252 | break; 253 | default: 254 | throw new StateError("Unknown element type: ${props['kind']}"); 255 | } 256 | row.data = cells; 257 | } 258 | } 259 | 260 | TableCellElement _typeCell(String declaredType, String inferredType, 261 | {colspan: '1'}) => _verticalCell(new SpanElement() 262 | ..appendText('inferred: ') 263 | ..append(_span(inferredType, cssClass: 'preSpan')), new SpanElement() 264 | ..appendText('declared: ') 265 | ..append(_span(declaredType, cssClass: 'preSpan')), colspan: colspan); 266 | 267 | TableCellElement _verticalCell(dynamic upper, dynamic lower, 268 | {String align: 'left', String colspan: '1'}) { 269 | DivElement div = new DivElement(); 270 | div.children.addAll([ 271 | upper is SpanElement ? upper : _span(upper), 272 | new BRElement(), 273 | lower is SpanElement ? lower : _span(lower) 274 | ]); 275 | return _cell(div, align: align, colspan: colspan, pre: false); 276 | } 277 | 278 | SpanElement _span(dynamic contents, {String cssClass}) { 279 | SpanElement span = new SpanElement(); 280 | if (cssClass != null) span.classes.add(cssClass); 281 | if (contents is Node) { 282 | span.append(contents); 283 | } else { 284 | span.appendText('$contents'); 285 | } 286 | return span; 287 | } 288 | 289 | /** 290 | * A helper method for creating TableCellElements with options 291 | * for alignment, colspan and wrapping the inner text inside of 292 | * a
 element.
293 |  */
294 | TableCellElement _cell(dynamic text,
295 |     {String align: 'left', String colspan: '1', bool pre: false}) {
296 |   TableCellElement element = new TableCellElement()
297 |     ..style.textAlign = align
298 |     ..attributes['colspan'] = colspan;
299 | 
300 |   if (pre) {
301 |     PreElement pre = new PreElement();
302 |     pre.text = text.toString();
303 |     element.append(pre);
304 |   } else if (text is Node) {
305 |     element.children.add(text);
306 |   } else {
307 |     element.text = text.toString();
308 |   }
309 | 
310 |   return element;
311 | }
312 | 
313 | /**
314 |  * Compute the size of a node in the node tree by first checking to see if
315 |  * that node has a size property.  If it does, use that unless [force] is
316 |  * set to true.  Otherwise, aquire the size by summing the sizes of
317 |  * the children.
318 |  */
319 | int _computeSize(Map info, Function fetchElement,
320 |     {bool force: false}) {
321 |   if (info.containsKey('size') && info['size'] != null && !force) {
322 |     return _toInt(info['size']);
323 |   } else if (info.containsKey('children')) {
324 |     return info['children']
325 |         .map(fetchElement)
326 |         .map((a) => _computeSize(a, fetchElement))
327 |         .fold(0, (a, b) => a + b);
328 |   } else {
329 |     return 0;
330 |   }
331 | }
332 | 
333 | int _toInt(dynamic n) {
334 |   if (n is int) {
335 |     return n;
336 |   } else if (n is String) {
337 |     return int.parse(n);
338 |   } else {
339 |     return 0;
340 |   }
341 | }
342 | 


--------------------------------------------------------------------------------
/lib/src/hierarchy_view.html:
--------------------------------------------------------------------------------
 1 | 
 6 | 
 7 | 
 8 | 
 9 | 
10 | 
11 |   
26 | 
27 | 
28 | 


--------------------------------------------------------------------------------
/lib/src/history_state.dart:
--------------------------------------------------------------------------------
  1 | // Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
  2 | // for details. All rights reserved. Use of this source code is governed by a
  3 | // BSD-style license that can be found in the LICENSE file.
  4 | 
  5 | library dump_viz.history;
  6 | 
  7 | import 'dart:async';
  8 | import 'dart:html';
  9 | 
 10 | import 'dependency_view.dart';
 11 | 
 12 | abstract class HistoryState {
 13 |   /// Apply this history state and modify the view.
 14 |   void apply();
 15 | 
 16 |   /// Called when a HistoryState is about to be
 17 |   /// moved out of.
 18 |   void finalize();
 19 | 
 20 |   String get asUrl;
 21 | 
 22 |   /// Convert this history state to JSON to be serialized
 23 |   Map toJson();
 24 | 
 25 |   factory HistoryState(String type, {String depTarget: null}) {
 26 |     switch (type) {
 27 |       case 'info':
 28 |         return new _InfoHistoryState();
 29 |       case 'hier':
 30 |         return new _HierHistoryState(_lastHierPos);
 31 |       case 'dep':
 32 |         var target = depTarget;
 33 |         if (target == null) {
 34 |           target = _lastDepFocus;
 35 |         }
 36 |         return new _DepHistoryState(target);
 37 |       case 'diff':
 38 |         return new _DiffHistoryState(_lastDiffPos);
 39 |       default:
 40 |         return null;
 41 |     }
 42 |   }
 43 | 
 44 |   static void setup(Function slideSwitcher, Duration animationTime) {
 45 |     window.onPopState.listen((popStateEvent) {
 46 |       var newState = HistoryState.fromJson(popStateEvent.state);
 47 |       switchTo(newState, fromPop: true);
 48 |     });
 49 |     HistoryState._slideSwitcher = slideSwitcher;
 50 |     HistoryState._animationTime = animationTime;
 51 |   }
 52 | 
 53 |   static void switchTo(HistoryState newState, {fromPop: false}) {
 54 |     if (_currentState != null) {
 55 |       _currentState.finalize();
 56 |     }
 57 |     if (!fromPop) {
 58 |       window.history.pushState(newState.toJson(), "test", "?" + newState.asUrl);
 59 |     }
 60 |     newState.apply();
 61 |     _currentState = newState;
 62 |   }
 63 | 
 64 |   /// Convert json to a HistoryState
 65 |   static HistoryState fromJson(Map json) {
 66 |     switch (json['kind']) {
 67 |       case 'info':
 68 |         return new _InfoHistoryState();
 69 |       case 'hier':
 70 |         return new _HierHistoryState(json['pos']);
 71 |       case 'dep':
 72 |         return new _DepHistoryState(json['focus']);
 73 |       case 'diff':
 74 |         return new _DiffHistoryState(json['pos']);
 75 |       default:
 76 |         return null;
 77 |     }
 78 |   }
 79 | 
 80 |   // When modifying the history from outside the
 81 |   // back / forward buttons, we need to track these
 82 |   // values ourselves.
 83 |   static int _lastHierPos = 0;
 84 |   static int _lastDiffPos = 0;
 85 |   static String _lastDepFocus = null;
 86 |   static HistoryState _currentState = null;
 87 |   static Function _slideSwitcher;
 88 |   static Duration _animationTime;
 89 | }
 90 | 
 91 | class _InfoHistoryState implements HistoryState {
 92 |   String get asUrl => "slide=info";
 93 | 
 94 |   void apply() {
 95 |     HistoryState._slideSwitcher('info');
 96 |   }
 97 | 
 98 |   void finalize() {}
 99 | 
100 |   dynamic toJson() {
101 |     return {'kind': 'info'};
102 |   }
103 | }
104 | 
105 | class _DiffHistoryState implements HistoryState {
106 |   int pos;
107 |   _DiffHistoryState(this.pos);
108 | 
109 |   String get asUrl => "slide=diff";
110 | 
111 |   void apply() {
112 |     HistoryState._slideSwitcher('diff');
113 |     new Timer(HistoryState._animationTime * 3, () {
114 |       document.body.scrollTop = pos;
115 |     });
116 |   }
117 | 
118 |   void finalize() {
119 |     pos = document.body.scrollTop;
120 |     HistoryState._lastDiffPos = pos;
121 |     window.history.replaceState(this.toJson(), "", "");
122 |   }
123 | 
124 |   dynamic toJson() {
125 |     return {'kind': 'diff', 'pos': pos};
126 |   }
127 | }
128 | 
129 | class _HierHistoryState implements HistoryState {
130 |   int pos;
131 |   _HierHistoryState(this.pos);
132 | 
133 |   String get asUrl => "slide=hier";
134 | 
135 |   void apply() {
136 |     HistoryState._slideSwitcher('hier');
137 |     new Timer(HistoryState._animationTime * 3, () {
138 |       document.body.scrollTop = pos;
139 |     });
140 |   }
141 | 
142 |   void finalize() {
143 |     pos = document.body.scrollTop;
144 |     HistoryState._lastHierPos = pos;
145 |     window.history.replaceState(this.toJson(), "", "");
146 |   }
147 | 
148 |   dynamic toJson() {
149 |     return {'kind': 'hier', 'pos': pos};
150 |   }
151 | }
152 | 
153 | class _DepHistoryState implements HistoryState {
154 |   final String focus;
155 |   _DepHistoryState(this.focus);
156 | 
157 |   String get asUrl => "slide=dep&focus=$focus";
158 | 
159 |   void apply() {
160 |     DependencyView depview = querySelector('dependency-view');
161 |     if (focus != null) {
162 |       depview.target = focus;
163 |     }
164 |     HistoryState._slideSwitcher('dep');
165 |     HistoryState._lastDepFocus = focus;
166 |   }
167 | 
168 |   void finalize() {
169 |     HistoryState._lastDepFocus = focus;
170 |   }
171 | 
172 |   dynamic toJson() {
173 |     return {'kind': 'dep', 'focus': focus};
174 |   }
175 | }
176 | 


--------------------------------------------------------------------------------
/lib/src/info_helper.dart:
--------------------------------------------------------------------------------
  1 | // Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
  2 | // for details. All rights reserved. Use of this source code is governed by a
  3 | // BSD-style license that can be found in the LICENSE file.
  4 | 
  5 | library dump_viz.info_helper;
  6 | 
  7 | import 'dart:collection' show Queue;
  8 | 
  9 | class Selection {
 10 |   String elementId;
 11 |   final String mask;
 12 |   Selection(this.elementId, this.mask);
 13 | }
 14 | 
 15 | class InfoHelper {
 16 |   final int dumpVersion;
 17 | 
 18 |   // A Map of type (kind -> (id -> element properties)) that
 19 |   // stores the properties of elements.
 20 |   final Map>> _elementProperties;
 21 | 
 22 |   // A map storing the raw representation of the whole-program
 23 |   // properties that dump-info generates.
 24 |   final Map _programProperties;
 25 | 
 26 |   // A map from the ids of an element to the
 27 |   // properties of that element.
 28 |   final Map> _idToProperties = {};
 29 | 
 30 |   // A map of dependencies from an ID of an element
 31 |   // to the IDs of the elements that it depends on.
 32 |   final Map> _dependencies = {};
 33 | 
 34 |   // A map of depedencies from an ID of an element
 35 |   // to the IDS of the elements that depend on it.
 36 |   final Map> _reverseDependencies = {};
 37 | 
 38 |   // A mapping from an ID of an element to that elements path.
 39 |   // A path for a function might look like
 40 |   // [library name, class name, function name]
 41 |   final Map> _path = {};
 42 | 
 43 |   // A mapping from an id to a joined path.
 44 |   // A joined path might look like "libname.classname.functionname"
 45 |   final Map _joinedPath = {};
 46 | 
 47 |   // A mapping from a joined path to an id.
 48 |   final Map _reverseJoinedPath = {};
 49 | 
 50 |   Iterable> allOfType(String type) =>
 51 |       _elementProperties[type].values;
 52 | 
 53 |   List dependencies(String id) {
 54 |     var deps = _dependencies[id];
 55 |     if (deps == null) {
 56 |       return const [];
 57 |     }
 58 |     return deps;
 59 |   }
 60 | 
 61 |   List reverseDependencies(String id) {
 62 |     if (_reverseDependencies[id] != null) {
 63 |       return _reverseDependencies[id];
 64 |     } else {
 65 |       return const [];
 66 |     }
 67 |   }
 68 | 
 69 |   Iterable get joinedPaths => _reverseJoinedPath.keys;
 70 | 
 71 |   String joinedPathFromId(String id) {
 72 |     return _joinedPath[id];
 73 |   }
 74 | 
 75 |   String idFromJoinedPath(String path) {
 76 |     return _reverseJoinedPath[path];
 77 |   }
 78 | 
 79 |   int sizeOf(String id) => _idToProperties[id]['size'];
 80 | 
 81 |   Map properties(String id) => _idToProperties[id];
 82 |   List path(String id) => _path[id];
 83 | 
 84 |   String get compilationMoment =>
 85 |       _programProperties['compilationMoment'].toString();
 86 |   String get compilationDuration =>
 87 |       _programProperties['compilationDuration'].toString();
 88 |   String get dart2jsVersion => _programProperties['dart2jsVersion'];
 89 |   int get size => _programProperties['size'];
 90 |   bool get noSuchMethodEnabled => _programProperties['noSuchMethodEnabled'];
 91 | 
 92 |   // Given an id, returns the node associated with it.
 93 |   dynamic elementById(String id) {
 94 |     var split = id.split("/");
 95 |     return _elementProperties[split[0]][split[1]];
 96 |   }
 97 | 
 98 |   Selection selectionFor(dynamic input) {
 99 |     if (input is String) {
100 |       return new Selection(input, null);
101 |     } else if (input is Map) {
102 |       return new Selection(input['id'], input['mask']);
103 |     } else {
104 |       throw new ArgumentError("$input is unexpected.");
105 |     }
106 |   }
107 | 
108 |   factory InfoHelper.fromJson(Map json) => new InfoHelper(
109 |       json['dump_version'], json['elements'], json['holding'], json['program']);
110 | 
111 |   InfoHelper(
112 |       this.dumpVersion,
113 |       Map>> properties,
114 |       Map> deps,
115 |       Map programProperties)
116 |       : _elementProperties = properties,
117 |         _programProperties = programProperties {
118 |     // Set up dependencies
119 |     for (Map section in properties.values) {
120 |       for (var prop in section.values) {
121 |         String id = prop['id'];
122 |         _idToProperties[id] = prop;
123 |         if (deps[id] != null) {
124 |           _dependencies[id] = deps[id].map(selectionFor).toList();
125 |         }
126 |       }
127 |     }
128 | 
129 |     // Set up reverse dependencies
130 |     deps.forEach((e, deps) {
131 |       for (var dep in deps) {
132 |         Selection selection = selectionFor(dep);
133 |         _reverseDependencies
134 |             .putIfAbsent(selection.elementId, () => [])
135 |             .add(selection..elementId = e);
136 |       }
137 |     });
138 | 
139 |     // Set up paths
140 |     void traverseNames(Map node, List prevPath) {
141 |       List newPath = new List.from(prevPath)..add(node['name']);
142 |       String id = node['id'];
143 |       _path[id] = newPath;
144 |       String joined = newPath.join('.');
145 |       _joinedPath[id] = joined;
146 |       _reverseJoinedPath[joined] = id;
147 | 
148 |       if (node['children'] != null) {
149 |         for (String id in node['children']) {
150 |           traverseNames(elementById(id), newPath);
151 |         }
152 |       }
153 |     }
154 | 
155 |     if (properties.containsKey('library')) {
156 |       for (var node in properties['library'].values) {
157 |         traverseNames(node, []);
158 |       }
159 |     }
160 |   }
161 | 
162 |   bool _parentsAllContained(String id, Set container) =>
163 |       reverseDependencies(id).every((a) => container.contains(a.elementId));
164 | 
165 |   Set _triviallyReachedFrom(String id) {
166 |     Queue queue = new Queue();
167 |     Set owns = new Set();
168 | 
169 |     queue.add(id);
170 |     owns.add(id);
171 | 
172 |     while (queue.isNotEmpty) {
173 |       String next = queue.removeFirst();
174 |       for (String child in dependencies(next).map((a) => a.elementId)) {
175 |         if (!owns.contains(child) && _parentsAllContained(child, owns)) {
176 |           queue.add(child);
177 |           owns.add(child);
178 |         }
179 |       }
180 |     }
181 |     return owns;
182 |   }
183 | 
184 |   int triviallyOwnedSize(String id) => _triviallyReachedFrom(id)
185 |       .map((a) => properties(a)['size'])
186 |       .reduce((a, b) => a + b);
187 | }
188 | 


--------------------------------------------------------------------------------
/lib/src/logical_row.dart:
--------------------------------------------------------------------------------
  1 | import 'dart:html';
  2 | 
  3 | import 'tree_table.dart';
  4 | 
  5 | typedef LogicalRow GenerateRowFunction();
  6 | 
  7 | class LogicalRow {
  8 |   // If the row is currently in an opened state.
  9 |   bool open = false;
 10 | 
 11 |   // A pointer into the data
 12 |   final Map data;
 13 | 
 14 |   String get id => data['id'];
 15 | 
 16 |   // A list of 'soon to be children' row functions.
 17 |   // This is the key part of having the entire data
 18 |   // structure be lazily evaluated.
 19 |   final List generatorFns = [];
 20 | 
 21 |   // After the generatorFns array is processed when this
 22 |   // node is expanded, this children array will be filled with
 23 |   // [LogicalRow]s
 24 |   final List children = [];
 25 | 
 26 |   // If this row is sortable.  An example of a non-sortable row
 27 |   // would be the 'code' and 'parameters' rows of a function
 28 |   // element properties.
 29 |   bool sortable = true;
 30 | 
 31 |   // If this row is not sortable, use this priority instead.
 32 |   int nonSortablePriority = 0;
 33 | 
 34 |   // A function that is called when this tree needs to be rendered
 35 |   // into the DOM.
 36 |   final RenderFunction renderFunction;
 37 | 
 38 |   // The actual rendered row.
 39 |   TreeTableRow rowElement;
 40 | 
 41 |   // The TreeTableRow element.  Stored here to make show/hide easier.
 42 |   HtmlElement parentElement;
 43 | 
 44 |   // The depth of this row in the overall tree.
 45 |   final int level;
 46 | 
 47 |   // Stored comparator for the sorting function.
 48 |   // Because the tree is generated lazily, this needs to be stored to
 49 |   // be called on generation of future children.
 50 |   LogicalRowComparer sortComparator;
 51 | 
 52 |   LogicalRow(this.data, this.renderFunction, this.parentElement, this.level);
 53 | 
 54 |   // Add a child function lazily.
 55 |   GenerateRowFunction addChild(GenerateRowFunction genRowFn) {
 56 |     this.generatorFns.add(genRowFn);
 57 |     return genRowFn;
 58 |   }
 59 | 
 60 |   void click() {
 61 |     open = !open;
 62 |     if (open) {
 63 |       if (children.isEmpty) {
 64 |         children.addAll(generatorFns.map((a) => a()));
 65 |         // Resort because more children were generated.
 66 |         sort(sortComparator);
 67 |       }
 68 |       this.children.forEach((child) => child.show(before: this.rowElement));
 69 |     } else {
 70 |       this.children.forEach((child) => child.hide());
 71 |     }
 72 |     this.rowElement.setArrow(this.children.isNotEmpty, open);
 73 |   }
 74 | 
 75 |   void hide() {
 76 |     this.getElement().remove();
 77 |     if (this.open) {
 78 |       this.children.forEach((child) => child.hide());
 79 |     }
 80 |   }
 81 | 
 82 |   /**
 83 |    * Adds this row to the TreeTableElement.
 84 |    * [before] is an optional element that this row
 85 |    * will be inserted after.
 86 |    */
 87 |   void show({HtmlElement before}) {
 88 |     if (before != null) {
 89 |       // Place this element right after [before]
 90 |       int loc = this.parentElement.children.indexOf(before) + 1;
 91 |       print(loc);
 92 |       this.parentElement.children.insert(loc, this.getElement());
 93 |     } else {
 94 |       // Prepend this element into the table
 95 |       this.parentElement.children.insert(0, this.getElement());
 96 |       //this.parentElement.append(this.getElement());
 97 |     }
 98 |     this.rowElement.level = this.level;
 99 |     if (!this.rowElement.populated) {
100 |       this.renderFunction(this.rowElement, this);
101 |       this.rowElement.setArrow(this.generatorFns.isNotEmpty, open);
102 |       this.rowElement.populated = true;
103 |     }
104 |     if (this.open) {
105 |       // Open up children spatially after this element
106 |       this.children.forEach((child) => child.show(before: this.rowElement));
107 |     }
108 |   }
109 | 
110 |   TreeTableRow getElement() {
111 |     if (rowElement != null) {
112 |       return rowElement;
113 |     } else {
114 |       this.rowElement = new TreeTableRow(this);
115 |       return this.rowElement;
116 |     }
117 |   }
118 | 
119 |   void sort(LogicalRowComparer comparator) {
120 |     sortComparator = comparator;
121 |     this.children.sort(comparator);
122 |     this.children.forEach((child) => child.sort(comparator));
123 |   }
124 | }
125 | 
126 | typedef int LogicalRowComparer(LogicalRow a, LogicalRow b);
127 | 


--------------------------------------------------------------------------------
/lib/src/program_info_view.dart:
--------------------------------------------------------------------------------
 1 | // Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
 2 | // for details. All rights reserved. Use of this source code is governed by a
 3 | // BSD-style license that can be found in the LICENSE file.
 4 | 
 5 | library dump_viz.program_info_view;
 6 | 
 7 | import 'dart:html';
 8 | 
 9 | import 'package:polymer/polymer.dart';
10 | 
11 | import 'info_helper.dart';
12 | 
13 | @CustomTag('program-info-view')
14 | class ProgramInfoView extends PolymerElement {
15 |   InfoHelper _model;
16 | 
17 |   ProgramInfoView.created() : super.created();
18 | 
19 |   TableElement get _treeTable => $['prog-info'];
20 | 
21 |   set dumpInfo(InfoHelper dumpInfo) {
22 |     _model = dumpInfo;
23 |     _setupProgramwideInfo();
24 |   }
25 | 
26 |   void _extractClick(_) {
27 |     String text =
28 |         _model.allOfType('function').map((a) => "${a['name']}").join(', ');
29 |     text = Uri.encodeComponent('[$text]');
30 |     String encoded = 'data:text/plain;charset=utf-8,$text';
31 | 
32 |     AnchorElement downloadLink = new AnchorElement(href: encoded);
33 |     downloadLink.text = 'download file';
34 |     downloadLink.setAttribute('download', 'functions.txt');
35 |     downloadLink.click();
36 |   }
37 | 
38 |   void _setupProgramwideInfo() {
39 |     _treeTable.children.clear();
40 |     var map = {
41 |       'Program Size': '${_model.size}  bytes',
42 |       'Compile Time': _model.compilationMoment,
43 |       'Compile Duration': _model.compilationDuration,
44 |       'noSuchMethod Enabled': new SpanElement()
45 |         ..text = _model.noSuchMethodEnabled.toString()
46 |         ..style.background = _model.noSuchMethodEnabled ? "red" : "white",
47 |       // TODO(tyoverby): add support for loading files generated by
48 |       // TRACE_CALLS and compare them to the functions that are produced
49 |       // by dart2js.
50 |       'Extract Function Names': new ButtonElement()
51 |         ..text = 'extract'
52 |         ..onClick.listen(_extractClick)
53 |     };
54 | 
55 |     map.forEach((k, v) {
56 |       var row = _treeTable.addRow();
57 |       row.addCell()..text = k;
58 |       if (v is String) {
59 |         row.addCell()..text = v;
60 |       } else if (v is Element) {
61 |         row.addCell()..children.add(v);
62 |       } else {
63 |         throw new ArgumentError("Unexpected value in map: $v");
64 |       }
65 |     });
66 |   }
67 | }
68 | 


--------------------------------------------------------------------------------
/lib/src/program_info_view.html:
--------------------------------------------------------------------------------
 1 | 
 6 | 
 7 | 
 8 | 
 9 | 
10 | 
11 |   
14 | 
15 | 
16 | 


--------------------------------------------------------------------------------
/lib/src/tree_table.dart:
--------------------------------------------------------------------------------
  1 | // Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
  2 | // for details. All rights reserved. Use of this source code is governed by a
  3 | // BSD-style license that can be found in the LICENSE file.
  4 | 
  5 | library tree_table;
  6 | 
  7 | import 'dart:collection' show Queue;
  8 | import 'dart:html';
  9 | 
 10 | import 'package:observe/observe.dart';
 11 | import 'package:polymer/polymer.dart';
 12 | 
 13 | import 'logical_row.dart';
 14 | 
 15 | typedef void RenderFunction(TreeTableRow ttr, LogicalRow logicalRow);
 16 | 
 17 | /// The amount of padding to be added to each level in the tree.
 18 | const int _PADDING_SIZE = 25;
 19 | 
 20 | /**
 21 |  * A Polymer TreeTable element.
 22 |  */
 23 | @CustomTag('tree-table')
 24 | class TreeTable extends PolymerElement {
 25 |   // The top-level rows in the tree.
 26 |   List _rootNodes = [];
 27 | 
 28 |   // A set of the nodes that were opened in the tree-table
 29 |   // before the json was reloaded.  Storing this information
 30 |   // makes it possible to re-open the tree to where it
 31 |   // was before.
 32 |   Set _previouslyOpened = new Set();
 33 | 
 34 |   TreeTable.created() : super.created() {}
 35 | 
 36 |   factory TreeTable() {
 37 |     return document.createElement('tree-table') as TreeTable;
 38 |   }
 39 | 
 40 |   /**
 41 |    * Adds a row to the table that is at the top level of the
 42 |    * tree in the structure.
 43 |    */
 44 |   LogicalRow addTopLevel(LogicalRow child) {
 45 |     _rootNodes.add(child);
 46 |     return child;
 47 |   }
 48 | 
 49 |   HtmlElement get tbody => this.$['inner_table_body'];
 50 | 
 51 |   /**
 52 |    * Clears the table.  Used when reloading the file.
 53 |    */
 54 |   void clear() {
 55 |     Set openedPaths = new Set();
 56 |     Queue possiblyOpen = new Queue();
 57 |     possiblyOpen.addAll(_rootNodes);
 58 |     while (possiblyOpen.isNotEmpty) {
 59 |       LogicalRow next = possiblyOpen.removeFirst();
 60 |       if (next.open) {
 61 |         openedPaths.add(next.id);
 62 |         possiblyOpen.addAll(next.children);
 63 |       }
 64 |     }
 65 | 
 66 |     this._previouslyOpened = openedPaths;
 67 | 
 68 |     _rootNodes.clear();
 69 |     this.children.clear();
 70 |     this.$['inner_table_head'].children.clear();
 71 |   }
 72 | 
 73 |   void reset() {
 74 |     Queue couldBeOpened = new Queue();
 75 |     couldBeOpened.addAll(_rootNodes);
 76 |     while (couldBeOpened.isNotEmpty) {
 77 |       LogicalRow next = couldBeOpened.removeFirst();
 78 |       if (_previouslyOpened.contains(next.id)) {
 79 |         next.click();
 80 |         couldBeOpened.addAll(next.children);
 81 |       }
 82 |     }
 83 |   }
 84 | 
 85 |   /**
 86 |    * Sets the titles for the columns of the table.
 87 |    */
 88 |   void columnInfo(List names, List helps, List sizes) {
 89 |     for (int i = 0; i < names.length; i++) {
 90 |       TableCellElement cell = new TableCellElement();
 91 |       cell.style.textAlign = 'center';
 92 |       cell.text = names[i];
 93 |       cell.title = helps[i];
 94 |       String size = sizes[i];
 95 |       if (size != null) {
 96 |         cell.style.width = size;
 97 |       }
 98 |       this.$['inner_table_head'].children.add(cell);
 99 |     }
100 |   }
101 | 
102 |   /**
103 |    * Sorts this TreeTable by creating a comparator function
104 |    * for LogicalRows sorting on a key in the data section.
105 |    */
106 |   void sort(String key) {
107 |     var comparator = (LogicalRow a, LogicalRow b) {
108 |       if (a.sortable && !b.sortable) {
109 |         return 1;
110 |       } else if (!a.sortable && b.sortable) {
111 |         return -1;
112 |       } else if (!a.sortable && !b.sortable) {
113 |         return a.nonSortablePriority.compareTo(b.nonSortablePriority);
114 |       }
115 | 
116 |       var d1 = a.data[key];
117 |       var d2 = b.data[key];
118 |       if (d1 == null) d1 = '';
119 |       if (d2 == null) d2 = '';
120 |       if (d1 is num && d2 is num) {
121 |         return d1.compareTo(d2);
122 |       }
123 |       return d2.toString().compareTo(d1.toString());
124 |     };
125 | 
126 |     // Clear all of the rows in the table because
127 |     // we will be sorting the `logical` rows and then
128 |     // re-add them all.
129 |     tbody.children.clear();
130 |     this._rootNodes.sort(comparator);
131 | 
132 |     for (var rootNode in this._rootNodes) {
133 |       rootNode.sort(comparator);
134 |     }
135 | 
136 |     // Re-add the now-sorted rows.
137 |     for (var rootNode in this._rootNodes) {
138 |       rootNode.show();
139 |     }
140 |   }
141 | }
142 | 
143 | /**
144 |  * A TreeTableRow element.  TreeTableRows are only to be inserted into
145 |  * a TreeTable element.  Because TreeTableRows are nodes in the tree
146 |  * structure, they can have children added to them via the [addChild]
147 |  * method.
148 |  */
149 | @CustomTag('tree-table-row')
150 | class TreeTableRow extends TableRowElement with Polymer, Observable {
151 |   LogicalRow logicalRow;
152 |   // True if data has been rendered into this row.
153 |   bool populated = false;
154 | 
155 |   TreeTableRow.created() : super.created() {
156 |     polymerCreated();
157 |   }
158 | 
159 |   factory TreeTableRow(LogicalRow logicalRow) {
160 |     TreeTableRow ttr = document.createElement('tr', 'tree-table-row');
161 |     ttr.logicalRow = logicalRow;
162 |     ttr.onClick.listen((_) => ttr.logicalRow.click());
163 |     return ttr;
164 |   }
165 | 
166 |   // Set the cells in this row.
167 |   void set data(List elements) {
168 |     if (elements.isNotEmpty) {
169 |       this.$['content'].text = elements.first.text;
170 |     }
171 |     for (TableCellElement cell in elements.skip(1)) {
172 |       this.shadowRoot.append(cell);
173 |     }
174 |   }
175 | 
176 |   // Set the level of indentation for this row.
177 |   void set level(int level) {
178 |     this.$['first-cell'].style.paddingLeft = "${level * _PADDING_SIZE}px";
179 |   }
180 | 
181 |   void setArrow(bool hasChildren, bool open) {
182 |     if (hasChildren) {
183 |       if (open) {
184 |         this.$['arrow'].text = '▼';
185 |       } else {
186 |         this.$['arrow'].text = '▶';
187 |       }
188 |     } else {
189 |       this.$['arrow'].text = '○';
190 |     }
191 |   }
192 | }
193 | 


--------------------------------------------------------------------------------
/lib/src/tree_table.html:
--------------------------------------------------------------------------------
 1 | 
 6 | 
 7 | 
 8 | 
 9 |   
34 | 
35 | 
36 | 
37 |   
88 | 
89 | 
90 | 


--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
  1 | # Generated by pub
  2 | # See http://pub.dartlang.org/doc/glossary.html#lockfile
  3 | packages:
  4 |   analyzer:
  5 |     description:
  6 |       name: analyzer
  7 |       url: "https://pub.dartlang.org"
  8 |     source: hosted
  9 |     version: "0.27.6"
 10 |   args:
 11 |     description:
 12 |       name: args
 13 |       url: "https://pub.dartlang.org"
 14 |     source: hosted
 15 |     version: "0.13.6"
 16 |   async:
 17 |     description:
 18 |       name: async
 19 |       url: "https://pub.dartlang.org"
 20 |     source: hosted
 21 |     version: "1.11.2"
 22 |   barback:
 23 |     description:
 24 |       name: barback
 25 |       url: "https://pub.dartlang.org"
 26 |     source: hosted
 27 |     version: "0.15.2+9"
 28 |   boolean_selector:
 29 |     description:
 30 |       name: boolean_selector
 31 |       url: "https://pub.dartlang.org"
 32 |     source: hosted
 33 |     version: "1.0.2"
 34 |   browser:
 35 |     description:
 36 |       name: browser
 37 |       url: "https://pub.dartlang.org"
 38 |     source: hosted
 39 |     version: "0.10.0+2"
 40 |   charcode:
 41 |     description:
 42 |       name: charcode
 43 |       url: "https://pub.dartlang.org"
 44 |     source: hosted
 45 |     version: "1.1.0"
 46 |   cli_util:
 47 |     description:
 48 |       name: cli_util
 49 |       url: "https://pub.dartlang.org"
 50 |     source: hosted
 51 |     version: "0.0.1+2"
 52 |   code_transformers:
 53 |     description:
 54 |       name: code_transformers
 55 |       url: "https://pub.dartlang.org"
 56 |     source: hosted
 57 |     version: "0.4.2+3"
 58 |   collection:
 59 |     description:
 60 |       name: collection
 61 |       url: "https://pub.dartlang.org"
 62 |     source: hosted
 63 |     version: "1.9.1"
 64 |   convert:
 65 |     description:
 66 |       name: convert
 67 |       url: "https://pub.dartlang.org"
 68 |     source: hosted
 69 |     version: "2.0.1"
 70 |   core_elements:
 71 |     description:
 72 |       name: core_elements
 73 |       url: "https://pub.dartlang.org"
 74 |     source: hosted
 75 |     version: "0.7.1+6"
 76 |   crypto:
 77 |     description:
 78 |       name: crypto
 79 |       url: "https://pub.dartlang.org"
 80 |     source: hosted
 81 |     version: "2.0.1"
 82 |   csslib:
 83 |     description:
 84 |       name: csslib
 85 |       url: "https://pub.dartlang.org"
 86 |     source: hosted
 87 |     version: "0.13.2"
 88 |   dart_style:
 89 |     description:
 90 |       name: dart_style
 91 |       url: "https://pub.dartlang.org"
 92 |     source: hosted
 93 |     version: "0.2.10"
 94 |   glob:
 95 |     description:
 96 |       name: glob
 97 |       url: "https://pub.dartlang.org"
 98 |     source: hosted
 99 |     version: "1.1.3"
100 |   html:
101 |     description:
102 |       name: html
103 |       url: "https://pub.dartlang.org"
104 |     source: hosted
105 |     version: "0.12.2+2"
106 |   http:
107 |     description:
108 |       name: http
109 |       url: "https://pub.dartlang.org"
110 |     source: hosted
111 |     version: "0.11.3+9"
112 |   http_multi_server:
113 |     description:
114 |       name: http_multi_server
115 |       url: "https://pub.dartlang.org"
116 |     source: hosted
117 |     version: "2.0.2"
118 |   http_parser:
119 |     description:
120 |       name: http_parser
121 |       url: "https://pub.dartlang.org"
122 |     source: hosted
123 |     version: "3.0.2"
124 |   initialize:
125 |     description:
126 |       name: initialize
127 |       url: "https://pub.dartlang.org"
128 |     source: hosted
129 |     version: "0.6.2+2"
130 |   isolate:
131 |     description:
132 |       name: isolate
133 |       url: "https://pub.dartlang.org"
134 |     source: hosted
135 |     version: "0.2.3"
136 |   logging:
137 |     description:
138 |       name: logging
139 |       url: "https://pub.dartlang.org"
140 |     source: hosted
141 |     version: "0.11.3+1"
142 |   matcher:
143 |     description:
144 |       name: matcher
145 |       url: "https://pub.dartlang.org"
146 |     source: hosted
147 |     version: "0.12.0+2"
148 |   mime:
149 |     description:
150 |       name: mime
151 |       url: "https://pub.dartlang.org"
152 |     source: hosted
153 |     version: "0.9.3"
154 |   observe:
155 |     description:
156 |       name: observe
157 |       url: "https://pub.dartlang.org"
158 |     source: hosted
159 |     version: "0.13.3+1"
160 |   package_config:
161 |     description:
162 |       name: package_config
163 |       url: "https://pub.dartlang.org"
164 |     source: hosted
165 |     version: "1.0.0"
166 |   package_resolver:
167 |     description:
168 |       name: package_resolver
169 |       url: "https://pub.dartlang.org"
170 |     source: hosted
171 |     version: "1.0.2"
172 |   paper_elements:
173 |     description:
174 |       name: paper_elements
175 |       url: "https://pub.dartlang.org"
176 |     source: hosted
177 |     version: "0.7.1+2"
178 |   path:
179 |     description:
180 |       name: path
181 |       url: "https://pub.dartlang.org"
182 |     source: hosted
183 |     version: "1.3.9"
184 |   plugin:
185 |     description:
186 |       name: plugin
187 |       url: "https://pub.dartlang.org"
188 |     source: hosted
189 |     version: "0.2.0"
190 |   polymer:
191 |     description:
192 |       name: polymer
193 |       url: "https://pub.dartlang.org"
194 |     source: hosted
195 |     version: "0.16.4+2"
196 |   polymer_expressions:
197 |     description:
198 |       name: polymer_expressions
199 |       url: "https://pub.dartlang.org"
200 |     source: hosted
201 |     version: "0.13.1"
202 |   polymer_interop:
203 |     description:
204 |       name: polymer_interop
205 |       url: "https://pub.dartlang.org"
206 |     source: hosted
207 |     version: "0.1.2+1"
208 |   pool:
209 |     description:
210 |       name: pool
211 |       url: "https://pub.dartlang.org"
212 |     source: hosted
213 |     version: "1.2.4"
214 |   pub_semver:
215 |     description:
216 |       name: pub_semver
217 |       url: "https://pub.dartlang.org"
218 |     source: hosted
219 |     version: "1.3.0"
220 |   quiver:
221 |     description:
222 |       name: quiver
223 |       url: "https://pub.dartlang.org"
224 |     source: hosted
225 |     version: "0.21.4"
226 |   shelf:
227 |     description:
228 |       name: shelf
229 |       url: "https://pub.dartlang.org"
230 |     source: hosted
231 |     version: "0.6.5+3"
232 |   shelf_packages_handler:
233 |     description:
234 |       name: shelf_packages_handler
235 |       url: "https://pub.dartlang.org"
236 |     source: hosted
237 |     version: "1.0.0"
238 |   shelf_static:
239 |     description:
240 |       name: shelf_static
241 |       url: "https://pub.dartlang.org"
242 |     source: hosted
243 |     version: "0.2.4"
244 |   shelf_web_socket:
245 |     description:
246 |       name: shelf_web_socket
247 |       url: "https://pub.dartlang.org"
248 |     source: hosted
249 |     version: "0.2.1"
250 |   smoke:
251 |     description:
252 |       name: smoke
253 |       url: "https://pub.dartlang.org"
254 |     source: hosted
255 |     version: "0.3.6+2"
256 |   source_map_stack_trace:
257 |     description:
258 |       name: source_map_stack_trace
259 |       url: "https://pub.dartlang.org"
260 |     source: hosted
261 |     version: "1.1.3"
262 |   source_maps:
263 |     description:
264 |       name: source_maps
265 |       url: "https://pub.dartlang.org"
266 |     source: hosted
267 |     version: "0.10.1+1"
268 |   source_span:
269 |     description:
270 |       name: source_span
271 |       url: "https://pub.dartlang.org"
272 |     source: hosted
273 |     version: "1.2.3"
274 |   stack_trace:
275 |     description:
276 |       name: stack_trace
277 |       url: "https://pub.dartlang.org"
278 |     source: hosted
279 |     version: "1.6.7"
280 |   stream_channel:
281 |     description:
282 |       name: stream_channel
283 |       url: "https://pub.dartlang.org"
284 |     source: hosted
285 |     version: "1.5.0"
286 |   string_scanner:
287 |     description:
288 |       name: string_scanner
289 |       url: "https://pub.dartlang.org"
290 |     source: hosted
291 |     version: "1.0.0"
292 |   template_binding:
293 |     description:
294 |       name: template_binding
295 |       url: "https://pub.dartlang.org"
296 |     source: hosted
297 |     version: "0.14.0+4"
298 |   test:
299 |     description:
300 |       name: test
301 |       url: "https://pub.dartlang.org"
302 |     source: hosted
303 |     version: "0.12.15+7"
304 |   typed_data:
305 |     description:
306 |       name: typed_data
307 |       url: "https://pub.dartlang.org"
308 |     source: hosted
309 |     version: "1.1.3"
310 |   utf:
311 |     description:
312 |       name: utf
313 |       url: "https://pub.dartlang.org"
314 |     source: hosted
315 |     version: "0.9.0+3"
316 |   watcher:
317 |     description:
318 |       name: watcher
319 |       url: "https://pub.dartlang.org"
320 |     source: hosted
321 |     version: "0.9.7+3"
322 |   web_components:
323 |     description:
324 |       name: web_components
325 |       url: "https://pub.dartlang.org"
326 |     source: hosted
327 |     version: "0.12.3"
328 |   web_socket_channel:
329 |     description:
330 |       name: web_socket_channel
331 |       url: "https://pub.dartlang.org"
332 |     source: hosted
333 |     version: "1.0.4"
334 |   when:
335 |     description:
336 |       name: when
337 |       url: "https://pub.dartlang.org"
338 |     source: hosted
339 |     version: "0.2.0"
340 |   which:
341 |     description:
342 |       name: which
343 |       url: "https://pub.dartlang.org"
344 |     source: hosted
345 |     version: "0.1.3"
346 |   yaml:
347 |     description:
348 |       name: yaml
349 |       url: "https://pub.dartlang.org"
350 |     source: hosted
351 |     version: "2.1.10"
352 | sdks:
353 |   dart: ">=1.17.0-dev.6.2 <2.0.0"
354 | 


--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
 1 | name: dump_viz
 2 | author: Dart Team 
 3 | homepage: https://github.com/dart-lang/dump-info-visualizer/
 4 | description: A visualizer for the JSON data produced by the dart2js --dump-info command
 5 | environment:
 6 |   sdk: '>=1.11.0-dev <2.0.0'
 7 | dependencies:
 8 |   core_elements: '^0.7.1+2'
 9 |   paper_elements: '^0.7.1'
10 |   path: '^1.3.5'
11 |   polymer: '^0.16.3+1'
12 | dev_dependencies:
13 |   test: '^0.12.0'
14 | transformers:
15 | - polymer:
16 |     entry_points: web/index.html
17 | - $dart2js:
18 |     $include: "**/*.polymer.bootstrap.dart"
19 |     commandLineOptions:
20 |     - --trust-type-annotations
21 |     - --trust-primitives
22 |     - --dump-info
23 | 


--------------------------------------------------------------------------------
/test/diff_test.dart:
--------------------------------------------------------------------------------
 1 | // Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
 2 | // for details. All rights reserved. Use of this source code is governed by a
 3 | // BSD-style license that can be found in the LICENSE file.
 4 | library dump_viz.test.diff_test;
 5 | 
 6 | import 'package:dump_viz/src/info_helper.dart';
 7 | import 'package:dump_viz/src/diff_alg.dart';
 8 | 
 9 | import 'package:test/test.dart';
10 | 
11 | class FakeInfoHelper {
12 |   InfoHelper info;
13 |   FakeInfoHelper() {
14 |     info = new InfoHelper(3, {}, {}, {});
15 |   }
16 |   FakeInfoHelper.fromFuncs(List funcs) {
17 |     int i = 0;
18 |     Map>> elements = {};
19 |     Map> functions = {};
20 |     Map> libraries = {};
21 | 
22 |     elements['function'] = functions;
23 |     elements['library'] = libraries;
24 | 
25 |     Map liba = {'name': "LibA"};
26 |     libraries['0'] = liba;
27 |     List children = [];
28 |     liba['children'] = children;
29 | 
30 |     for (var func in funcs) {
31 |       functions['$i'] = func;
32 |       children.add('function/$i');
33 |       func['id'] = 'function/$i';
34 |       i++;
35 |     }
36 |     info = new InfoHelper(3, elements, {}, {});
37 |   }
38 | }
39 | 
40 | void main() {
41 |   test('empty', () {
42 |     var d = diff(new FakeInfoHelper().info, new FakeInfoHelper().info);
43 |     expect(d, equals([]));
44 |   });
45 |   test('change', () {
46 |     var one = [{'name': 'foo', 'size': 10}];
47 |     var two = [{'name': 'foo', 'size': 20}];
48 |     expect(diff(new FakeInfoHelper.fromFuncs(one).info,
49 |             new FakeInfoHelper.fromFuncs(two).info),
50 |         equals([new DiffItem('partial-add', 'LibA.foo', 10)]));
51 |   });
52 |   test('add/remove', () {
53 |     var one = [{'name': 'foo', 'size': 10}];
54 |     var two = [{'name': 'bar', 'size': 20}];
55 |     expect(diff(new FakeInfoHelper.fromFuncs(one).info,
56 |         new FakeInfoHelper.fromFuncs(two).info), equals([
57 |       new DiffItem('full-add', 'LibA.bar', 20),
58 |       new DiffItem('full-remove', 'LibA.foo', -10)
59 |     ]));
60 |   });
61 | }
62 | 


--------------------------------------------------------------------------------
/test/info_helper_test.dart:
--------------------------------------------------------------------------------
 1 | @TestOn('vm')
 2 | import 'dart:convert';
 3 | import 'dart:io';
 4 | 
 5 | import 'package:path/path.dart' as p;
 6 | import 'package:test/test.dart';
 7 | 
 8 | import 'package:dump_viz/src/info_helper.dart';
 9 | 
10 | Map _content;
11 | 
12 | void main() {
13 |   setUp(() async {
14 |     if (_content == null) {
15 |       var samplePath = p.join(p.current, 'test', 'sample.info.json');
16 |       var file = new File(samplePath);
17 |       var jsonString = await file.readAsString();
18 |       _content = JSON.decode(jsonString);
19 |     }
20 |   });
21 | 
22 |   test('bootstrap', () {
23 |     expect(_content, isNotEmpty);
24 |   });
25 | 
26 |   test("basics", () {
27 |     var info = new InfoHelper.fromJson(_content);
28 | 
29 |     expect(info.dumpVersion, 3);
30 |     expect(info.size, 929444);
31 |     expect(info.joinedPaths, hasLength(5148));
32 |   });
33 | }
34 | 


--------------------------------------------------------------------------------
/web/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dart-archive/dump-info-visualizer/9d0d1b363fc1fa9161bf9a28b3a1f5b70b93cf8a/web/favicon.ico


--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 7 | 
 8 | 
 9 |   
10 |     
11 |     Dump Info Visualizer
12 |     
13 |     
14 | 
15 |     
16 |     
17 |     
18 |     
19 | 
20 |     
21 |     
22 |     
23 |     
24 |     
25 |   
26 |   
27 |     
28 |       
29 |         
30 |           PROGRAM INFO
31 |         
32 |         
33 |           HIERARCHY
34 |         
35 |         
36 |           DEPENDENCIES
37 |         
38 |         
39 |           DIFF
40 |         
41 |         
47 |       
48 |     
49 |     
50 |
51 | 52 | 53 |
54 | 55 |
56 | 57 | 58 |
59 | 60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 |
68 | 69 |
70 | 71 | 72 | 74 |
75 | 76 | 77 | 78 |
79 | 80 |
81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /web/viewer.css: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file */ 2 | /* for details. All rights reserved. Use of this source code is governed by a */ 3 | /* BSD-style license that can be found in the LICENSE file. */ 4 | 5 | body { 6 | font-family: 'RobotoDraft', 'Open Sans', sans-serif; 7 | font-size: 14px; 8 | font-weight: normal; 9 | line-height: 1.2em; 10 | margin: 0px; 11 | } 12 | 13 | h1, p { 14 | color: #333; 15 | } 16 | 17 | #load-slide { 18 | text-align: center; 19 | } 20 | 21 | #load-slide button { 22 | margin: 1em; 23 | } 24 | 25 | #sample_container_id { 26 | width: 100%; 27 | height: 400px; 28 | position: relative; 29 | border: 1px solid #ccc; 30 | background-color: #fff; 31 | } 32 | 33 | paper-tabs { 34 | width: 600px; 35 | height: 50px; 36 | position: relative; 37 | top: -7px; 38 | } 39 | 40 | #dummy-toolbar { 41 | width: 100%; 42 | height: 50px; 43 | } 44 | 45 | core-toolbar { 46 | background-color: #00bcd4; 47 | color: #fff; 48 | box-shadow: 0px 3px 2px rgba(0,0,0,0.2); 49 | 50 | height: 50px; 51 | position: fixed; 52 | top: -60px; 53 | width: 100%; 54 | z-index: 2; 55 | 56 | -webkit-transition: top 0.30s ease-in-out; 57 | transition: top 0.30s ease-in-out; 58 | } 59 | 60 | #title { 61 | font-size: 21px; 62 | position: relative; 63 | top: -8px; 64 | } 65 | 66 | paper-tab { 67 | font-size: 14px; 68 | } 69 | 70 | .slidecontainer { 71 | background: red; 72 | margin: 0 auto; 73 | width: 95%; 74 | position: relative; 75 | } 76 | 77 | .slide { 78 | display: block; 79 | position: absolute; 80 | height: auto; 81 | left: 0; 82 | right: 0; 83 | margin-left: auto; 84 | margin-right: auto; 85 | margin-bottom: 15px; 86 | padding: 20px; 87 | 88 | 89 | overflow: hidden; 90 | -webkit-transition: opacity 0.10s ease-in-out, 91 | left 0.10s ease-in-out, 92 | max-height 0.2s ease-out 0.10s; 93 | transition: opacity 0.10s ease-in-out, 94 | left 0.10s ease-in-out, 95 | max-height 0.2s ease-out 0.10s; 96 | opacity: 0; 97 | box-shadow: 0px 2px 5px rgba(0,0,0,0.26); 98 | border-radius: 2px; 99 | } 100 | 101 | .slide.hidden { 102 | box-shadow: none; 103 | padding: 2px; 104 | } 105 | 106 | body /deep/ .preSpan { 107 | font-family: monospace; 108 | /*white-space: pre;*/ 109 | } 110 | -------------------------------------------------------------------------------- /web/viewer.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | library dump_viz.viewer; 6 | 7 | import 'dart:async'; 8 | import 'dart:convert'; 9 | import 'dart:html'; 10 | import 'dart:js'; 11 | 12 | import 'package:paper_elements/paper_ripple.dart'; 13 | import 'package:paper_elements/paper_tab.dart'; 14 | import 'package:polymer/polymer.dart'; 15 | 16 | import 'package:dump_viz/dump_viz.dart'; 17 | 18 | export 'package:polymer/init.dart'; 19 | 20 | const List _slides = const [ 21 | 'info', 22 | 'hier', 23 | 'dep', 24 | 'load', 25 | 'diff' 26 | ]; 27 | const Duration _animationTime = const Duration(milliseconds: 10); 28 | 29 | const _localStorageKey = 'dump_viz.last_file'; 30 | 31 | bool get hasCache => window.localStorage.containsKey(_localStorageKey); 32 | 33 | String _getCache() { 34 | if (!hasCache) { 35 | throw 'No value stored!'; 36 | } 37 | return window.localStorage[_localStorageKey]; 38 | } 39 | 40 | String _setCache(String value) => window.localStorage[_localStorageKey] = value; 41 | 42 | void _noSlide() { 43 | // Disable all of the slides and tabs 44 | for (String id in _slides) { 45 | var slide = document.querySelector('#$id-slide'); 46 | slide.style.opacity = '0'; 47 | //slide.style.left = '100px'; 48 | slide.style.left = '0px'; 49 | slide.style.maxHeight = '0px'; 50 | slide.style.zIndex = '0'; 51 | 52 | var tab = document.querySelector('#$id-tab'); 53 | if (tab != null) { 54 | tab.classes.remove('core-selected'); 55 | } 56 | } 57 | } 58 | 59 | void _switchSlide(String id, {bool fromMouse: false}) { 60 | _noSlide(); 61 | var slide = document.querySelector('#$id-slide'); 62 | slide.style.maxHeight = 'none'; 63 | slide.style.zIndex = '1'; 64 | 65 | new Timer(_animationTime, () { 66 | slide.style.opacity = '1'; 67 | slide.style.left = '0px'; 68 | var tab = document.querySelector('#$id-tab'); 69 | 70 | if (tab != null) { 71 | tab.classes.add('core-selected'); 72 | var tabs = document.querySelector('paper-tabs'); 73 | tabs.attributes['selected'] = tab.attributes['offset']; 74 | // Draw a ripple on the tab if we didn't already click on it. 75 | if (!fromMouse) { 76 | PaperRipple ripple = tab.shadowRoot.querySelector('paper-ripple'); 77 | var pos = { 78 | 'x': tabs.offsetLeft + tab.offsetLeft + tab.clientWidth / 2, 79 | 'y': 0 80 | }; 81 | ripple.jsElement.callMethod('downAction', [new JsObject.jsify(pos)]); 82 | window.animationFrame 83 | .then((_) => ripple.jsElement.callMethod('upAction', [])); 84 | } 85 | } 86 | }); 87 | } 88 | 89 | ButtonElement get _useLastButton => querySelector('#useLast') as ButtonElement; 90 | 91 | ButtonElement get _clearCacheButton => 92 | querySelector('#clearCache') as ButtonElement; 93 | 94 | @whenPolymerReady 95 | void init() { 96 | HistoryState.setup(_switchSlide, _animationTime); 97 | _noSlide(); 98 | _switchSlide('load'); 99 | 100 | bool alreadyLoaded = false; 101 | 102 | var dragDrop = querySelector('drag-drop-view') as DragDropView; 103 | var dependencyView = querySelector('dependency-view') as DependencyView; 104 | var diffView = querySelector('diff-view') as DiffView; 105 | var hierarchyView = querySelector('hierarchy-view') as HierarchyView; 106 | var programInfoView = querySelector('program-info-view') as ProgramInfoView; 107 | 108 | var tabs = querySelectorAll('paper-tab'); 109 | for (PaperTab tab in tabs) { 110 | tab.onClick.listen((_) { 111 | String link = tab.attributes['slide']; 112 | HistoryState.switchTo(new HistoryState(link)); 113 | }); 114 | } 115 | 116 | void loadJson(String jsonString) { 117 | var json; 118 | try { 119 | json = JSON.decode(jsonString) as Map; 120 | } catch (e) { 121 | window.console.error("Error parsing json"); 122 | window.console.error(e); 123 | return; 124 | } 125 | document.querySelector('core-toolbar').style.top = "0"; 126 | 127 | var info = new InfoHelper.fromJson(json); 128 | 129 | diffView.currentlyLoaded = info; 130 | 131 | if (alreadyLoaded) { 132 | hierarchyView.clear(); 133 | } else { 134 | HistoryState.switchTo(new HistoryState('info')); 135 | } 136 | 137 | var dumpVersion = info.dumpVersion as num; 138 | 139 | if (dumpVersion < 1 || dumpVersion > 5) { 140 | window.alert('Unknown dump-info version: $dumpVersion'); 141 | return; 142 | } 143 | 144 | dependencyView.dumpInfo = info; 145 | hierarchyView.dumpInfo = info; 146 | programInfoView.dumpInfo = info; 147 | 148 | alreadyLoaded = true; 149 | _updateButton(); 150 | } 151 | 152 | // When a file is loaded 153 | dragDrop.onFile.listen((json) { 154 | try { 155 | _setCache(json); 156 | } catch (e) { 157 | window.console.error( 158 | 'Could not populate cache. May be too big. Try the clear button.'); 159 | window.console.error(e); 160 | } 161 | loadJson(json); 162 | }); 163 | 164 | _clearCacheButton.onClick.listen((_) { 165 | window.localStorage.clear(); 166 | _updateButton(); 167 | }); 168 | 169 | _useLastButton.onClick.listen((_) { 170 | loadJson(_getCache()); 171 | }); 172 | 173 | _updateButton(); 174 | } 175 | 176 | void _updateButton() { 177 | _useLastButton.disabled = !hasCache; 178 | _clearCacheButton.disabled = window.localStorage.isEmpty; 179 | } 180 | --------------------------------------------------------------------------------