aboutsummaryrefslogtreecommitdiffstats
path: root/etc
diff options
context:
space:
mode:
authorFernando Jiménez Moreno <ferjmoreno@gmail.com>2020-02-19 17:18:25 +0100
committerFernando Jiménez Moreno <ferjmoreno@gmail.com>2020-02-21 11:11:01 +0100
commitc33a5173870990aa4a91cbf0677f266309305e71 (patch)
treecc114d3c287335d44dc7f00004f2fd1ed57529e8 /etc
parentf81a2f021e630b6d086a83fc90b917dbef89e888 (diff)
downloadservo-c33a5173870990aa4a91cbf0677f266309305e71.tar.gz
servo-c33a5173870990aa4a91cbf0677f266309305e71.zip
Keep layout viewer versions for both 2013 and 2020 engines
Diffstat (limited to 'etc')
-rw-r--r--etc/layout_viewer/viewer.html591
-rw-r--r--etc/layout_viewer/viewer_2020.html339
2 files changed, 603 insertions, 327 deletions
diff --git a/etc/layout_viewer/viewer.html b/etc/layout_viewer/viewer.html
index 76b85641e38..2deded56f8e 100644
--- a/etc/layout_viewer/viewer.html
+++ b/etc/layout_viewer/viewer.html
@@ -1,339 +1,276 @@
<!DOCTYPE html>
<html lang="en">
- <head>
- <meta charset="utf-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <title>Servo Layout Debugger</title>
-
- <!-- Bootstrap -->
- <link href="css/bootstrap.min.css" rel="stylesheet" />
-
- <!-- Treeview -->
- <link href="css/bootstrap-treeview.min.css" rel="stylesheet" />
-
- <!-- JSDiffPatch -->
- <link href="css/formatters/html.css" rel="stylesheet" />
-
- <!-- Custom -->
- <link href="css/main.css" rel="stylesheet" />
-
- <!--[if lt IE 9]>
- <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
- <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
- <![endif]-->
- </head>
- <body>
- <div class="container" role="main">
- <div class="row">
- <div class="col-sm-12">
- <h1>Servo Layout Viewer</h1>
- <p>
- Check the
- <a
- href="https://github.com/servo/servo/blob/master/etc/layout_viewer/README.md"
- >README</a
- >
- for instructions.
- </p>
- </div>
- </div>
- <div class="row">
- <div class="col-sm-4">
- <div class="row">
- <div class="col-sm-12">
- <div class="well">
- <input type="file" />
- </div>
- </div>
- </div>
- <div class="row">
- <div class="col-sm-12">
- <div id="trace-tree"></div>
- </div>
- </div>
- <div class="row">
- <div class="col-sm-12">
- <ul id="trace-list" class="list-group"></ul>
- </div>
- </div>
- </div>
- <div class="col-sm-8">
- <div class="row">
- <div class="col-sm-12">
- <div class="panel panel-default">
- <div class="panel-heading">
- Box Tree
- <a
- id="box-tree-collapse"
- class="tree-collapse"
- data-collapsed="0"
- ></a>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>Servo Layout Debugger</title>
+
+ <!-- Bootstrap -->
+ <link href="css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- Treeview -->
+ <link href="css/bootstrap-treeview.min.css" rel="stylesheet">
+
+ <!-- JSDiffPatch -->
+ <link href="css/formatters/html.css" rel="stylesheet">
+
+ <!-- Custom -->
+ <link href="css/main.css" rel="stylesheet">
+
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+ <div class="container" role="main">
+ <div class="row">
+ <div class="col-sm-12">
+ <h1> Servo Layout Viewer </h1>
+ <p> Check the <a href="https://github.com/servo/servo/blob/master/etc/layout_viewer/README.md">README</a> for instructions.</p>
</div>
- <div class="panel-body" id="box-tree"></div>
- </div>
- </div>
- <div class="col-sm-12">
- <div id="box-diffs"></div>
</div>
- </div>
- <div class="row">
- <div class="col-sm-12">
- <div class="panel panel-default">
- <div class="panel-heading">
- Fragment Tree
- <a
- id="fragment-tree-collapse"
- class="tree-collapse"
- data-collapsed="0"
- ></a>
+ <div class="row">
+ <div class="col-sm-4">
+ <div class="row">
+ <div class="col-sm-12">
+ <div class="well">
+ <input type=file>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-sm-12">
+ <div id="trace-tree">
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-sm-12">
+ <ul id="trace-list" class="list-group">
+ </ul>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-8">
+ <div class="row">
+ <div class="col-sm-12">
+ <div class='panel panel-default'>
+ <div class='panel-heading'>Flow Tree</div>
+ <div class='panel-body' id="flow-tree"></div>
+ </div>
+ </div>
+ <div class="col-sm-12">
+ <div id="flow-diffs"></div>
+ </div>
+ </div>
</div>
- <div class="panel-body" id="fragment-tree"></div>
- </div>
</div>
- <div class="col-sm-12">
- <div id="fragment-diffs"></div>
+ <div id="toolbar">
+ <a href="#" id="prev_trace">< Prev step</a>
+ |
+ <a href="#" id="next_trace">Next step ></a>
+ <br>
+ <input type="checkbox" name="show_unchanged" id="show_unchanged" />
+ <label for="show_unchanged">Show unchanged code</label>
+ <br>
+ <a href="#top">Back to top</a>
</div>
- </div>
</div>
- </div>
- </div>
-
- <!-- jQuery -->
- <script src="js/jquery.min.js"></script>
- <!-- Bootstrap -->
- <script src="js/bootstrap.min.js"></script>
- <!-- Treeview -->
- <script src="js/bootstrap-treeview.min.js"></script>
- <!-- JSDiffPatch -->
- <script src="js/bundle.min.js"></script>
- <script src="js/formatters.min.js"></script>
-
- <script>
- function get_inner_boxes(box) {
- const box_type = Object.keys(box)[0];
- switch (box_type) {
- case "BlockLevelBoxes":
- return box.BlockLevelBoxes;
- case "InlineFormattingContext":
- return box.InlineFormattingContext.inline_level_boxes;
- case "InlineBox":
- return box.InlineBox.children;
- case "SameFormattingContextBlock":
- case "Independent":
- case "Flow":
- case "OutOfFlowAbsolutelyPositionedBox":
- case "OutOfFlowFloatBox":
- case "Atomic":
- return box[box_type].contents;
- }
- return null;
- }
-
- function box_tree_from_container(container) {
- const box_type = Object.keys(container)[0];
- let inner_boxes = get_inner_boxes(container);
- let nodes = [];
- let text = box_type;
- if (Array.isArray(inner_boxes)) {
- if (!inner_boxes.length) {
- nodes = null;
- } else {
- for (let box in inner_boxes) {
- nodes.push(box_tree_from_container(inner_boxes[box]));
+
+ <!-- jQuery -->
+ <script src="js/jquery.min.js"></script>
+ <!-- Bootstrap -->
+ <script src="js/bootstrap.min.js"></script>
+ <!-- Treeview -->
+ <script src="js/bootstrap-treeview.min.js"></script>
+ <!-- JSDiffPatch -->
+ <script src="js/bundle.min.js"></script>
+ <script src="js/formatters.min.js"></script>
+
+ <script>
+ function get_base(trace_node) {
+ if (typeof(trace_node.data.base) == "undefined" && typeof(trace_node.data.block_flow) != "undefined") {
+ return trace_node.data.block_flow.base;
+ }
+ else {
+ return trace_node.data.base;
+ }
+ }
+
+ function create_flow_tree(trace_node) {
+ var base = get_base(trace_node);
+
+ var node = {
+ text: trace_node.class + " (" + base.id + ")",
+ id: base.id,
+ icon: "dummy",
+ href: "#diff-" + base.id
+ };
+
+ var children = [];
+ for (var i=0 ; i < base.children.length ; ++i) {
+ children.push(create_flow_tree(base.children[i]));
+ }
+
+ if (children.length > 0) {
+ node.nodes = children;
+ }
+
+ return node;
+ }
+
+ function create_flow_hash(trace_node, flow_hash) {
+ var base = get_base(trace_node);
+ flow_hash[base.id] = trace_node;
+
+ for (var i=0 ; i < base.children.length ; ++i) {
+ create_flow_hash(base.children[i], flow_hash);
+ }
+
+ delete base.children;
}
- }
- } else if (inner_boxes != null) {
- nodes.push(box_tree_from_container(inner_boxes));
- } else {
- if (box_type == "TextRun") {
- text += ` (${container.TextRun.text})`;
- }
- nodes = null;
- }
-
- let info;
- if (
- box_type != "BlockLevelBoxes" &&
- box_type != "InlineFormattingContext"
- ) {
- info = Object.assign({}, Object.values(container)[0]);
- delete info.children;
- delete info.contents;
- delete info.tag;
- }
-
- return {
- text,
- nodes,
- info
- };
- }
-
- function box_tree_from_bfc(bfc) {
- const { contains_floats, contents } = bfc;
- let block_container = Object.values(contents)[0];
- return {
- text: "BlockFormattingContext",
- info: {
- contains_floats
- },
- nodes: [box_tree_from_container(contents)]
- };
- }
-
- function create_fragment_tree(root) {
- let fragment = Object.values(root)[0];
- let node = {
- text: Object.keys(root)[0],
- id: fragment.debug_id,
- href: "#diff-" + fragment.debug_id
- };
-
- if (fragment.children) {
- let children = [];
- for (let i = 0; i < fragment.children.length; ++i) {
- children.push(create_fragment_tree(fragment.children[i]));
- }
-
- if (children.length > 0) {
- node.nodes = children;
- }
- }
-
- node.info = Object.assign({}, fragment);
- delete node.info.children;
- delete node.info.debug_id;
-
- return node;
- }
-
- function flatten_trace(trace_node) {
- const fragment_tree_root = Object.values(
- trace_node.fragment_tree.children
- )[0];
- return {
- fragment_tree: create_fragment_tree(fragment_tree_root),
- box_tree: box_tree_from_bfc(trace_node.box_tree)
- };
- }
-
- function create_trace_tree_node(trace_node) {
- const trace = flatten_trace(trace_node.pre);
-
- let tree_node = {
- text: trace_node.name,
- icon: "dummy",
- box_tree: trace.box_tree,
- fragment_tree: trace.fragment_tree
- };
-
- let node = Object.values(trace_node)[0];
- if (node.children) {
- let children = [];
- for (let i = 0; i < node.children.length; ++i) {
- children.push(create_trace_tree_node(node.children[i]));
- }
-
- if (children.length > 0) {
- tree_node.nodes = children;
- }
- }
-
- return tree_node;
- }
-
- function new_data_loaded(data) {
- jsondiffpatch.formatters.html.hideUnchanged();
-
- let node_color_hash = {};
- let tree = [create_trace_tree_node(data)];
- $("#trace-tree").treeview({ data: tree, levels: 3 });
- $("#trace-tree").on("nodeSelected", function(event, node) {
- $("#fragment-diffs").empty();
- $("#trace-tree")
- .treeview(true)
- .revealNode(node);
-
- const on_tree_node_selected = tree => (event, data) => {
- $(`#${tree}-diffs`).empty();
- if (!data.info) return;
- // XXX(ferjm) no diff for now.
- const delta = jsondiffpatch
- .create({
- objectHash: function(obj) {
- return JSON.stringify(obj);
+
+ function flatten_trace(trace_node) {
+ var flow_tree = create_flow_tree(trace_node.children[0]);
+
+ var flow_hash = {};
+ create_flow_hash(trace_node.children[0], flow_hash);
+
+ return {
+ tree: flow_tree,
+ flows: flow_hash,
+ }
+ }
+
+ function create_tree_node(trace_node) {
+ var pre_trace = flatten_trace(trace_node.pre);
+ var post_trace = flatten_trace(trace_node.post);
+
+ var tree_node = {
+ text: trace_node.name,
+ icon: "dummy",
+ flow_tree: pre_trace.tree, // assume pre/post trace always have same tree!
+ pre: pre_trace.flows,
+ post: post_trace.flows,
+ };
+
+ var children = [];
+
+ for (var i=0 ; i < trace_node.children.length ; ++i) {
+ children.push(create_tree_node(trace_node.children[i]));
}
- })
- .diff({}, data.info);
-
- const json = jsondiffpatch.formatters.html.format(delta, data.info);
-
- $(`#${tree}-diffs`).append(
- "<div class='panel panel-default'><div class='panel-heading'>" +
- data.text +
- "</div><div class='panel-body'>" +
- json +
- "</div></div>"
- );
- };
-
- const on_fragment_tree_node_selected = on_tree_node_selected(
- "fragment"
- );
- const on_box_tree_node_selected = on_tree_node_selected("box");
-
- $("#fragment-tree").treeview({
- data: [node.fragment_tree],
- levels: 100,
- enableLinks: false,
- emptyIcon: "glyphicon glyphicon-unchecked hidden-glyphicon",
- onNodeSelected: on_fragment_tree_node_selected
- });
-
- $("#box-tree").treeview({
- data: [node.box_tree],
- levels: 100,
- enableLinks: false,
- emptyIcon: "glyphicon glyphicon-unchecked hidden-glyphicon",
- onNodeSelected: on_box_tree_node_selected
- });
-
- ["fragment", "box"].forEach(key => {
- const collapsable = $(`#${key}-tree-collapse`);
- collapsable.html("Collapse all").on("click", () => {
- const collapsed = collapsable.data("collapsed");
- if (collapsed == 0) {
- $(`#${key}-tree`).treeview("collapseAll");
- } else {
- $(`#${key}-tree`).treeview("expandAll");
- }
- collapsable.html(collapsed == 0 ? "Expand all" : "Collapse all");
- collapsable.data("collapsed", collapsed == 0 ? 1 : 0);
+
+ if (children.length > 0) {
+ tree_node.nodes = children;
+ }
+
+ return tree_node;
+ }
+
+ function update_flow_tree_bgcolor(flow_tree_node, node_color_hash) {
+ flow_tree_node.backColor = node_color_hash[flow_tree_node.id];
+ if (flow_tree_node.nodes !== undefined) {
+ for (var i=0 ; i < flow_tree_node.nodes.length ; ++i) {
+ update_flow_tree_bgcolor(flow_tree_node.nodes[i], node_color_hash)
+ }
+ }
+ }
+
+ function new_data_loaded(data) {
+ jsondiffpatch.formatters.html.hideUnchanged();
+
+ var node_color_hash = {};
+ var tree = [ create_tree_node(data) ];
+ $('#trace-tree').treeview({data: tree, levels: 3});
+ $('#trace-tree').on('nodeSelected', function(event, node) {
+ $("#flow-diffs").empty();
+ $('#trace-tree').treeview(true).revealNode(node);
+
+ for (var key in node.pre) {
+ var flow_left = node.pre[key];
+ var flow_right = node.post[key];
+
+ var delta = jsondiffpatch.create({
+ objectHash: function(obj) {
+ if (obj.data !== undefined && obj.data.base !== undefined) {
+ return obj.data.base.id;
+ }
+ if (obj.id !== undefined) {
+ return obj.id;
+ }
+ if (obj.index !== undefined) {
+ // FlexItem and FlexLine
+ return obj.index;
+ }
+ return JSON.stringify(obj);
+ }
+ }).diff(flow_left, flow_right);
+
+ if (delta !== undefined) {
+ var diff_id = "diff-" + key;
+ $("#flow-diffs").append(
+ "<div class='panel panel-default' id='" +
+ diff_id +
+ "'><div class='panel-heading'>" +
+ flow_left.class + " (" + key + ")" +
+ "</div><div class='panel-body'></div></div>");
+
+ document.getElementById(diff_id).getElementsByClassName('panel-body')[0].innerHTML =
+ jsondiffpatch.formatters.html.format(delta, flow_left);
+ node_color_hash[key] = "rgba(255, 0, 0, 0.7)";
+ } else {
+ node_color_hash[key] = "rgb(212, 248, 212)";
+ }
+ }
+
+ update_flow_tree_bgcolor(node.flow_tree, node_color_hash);
+ $('#flow-tree').treeview({data: [node.flow_tree], levels: 100, enableLinks: true, emptyIcon: "glyphicon glyphicon-unchecked hidden-glyphicon"});
+ });
+
+ $('#trace-tree').treeview(true).selectNode(0);
+ }
+
+ function register_toggle_unchanaged_code_handler() {
+ var show_unchange_box = document.getElementById("show_unchanged");
+ show_unchange_box.addEventListener("change", function(evt){
+ jsondiffpatch.formatters.html.showUnchanged(show_unchange_box.checked, null, 800);
+ });
+ }
+
+ function register_prev_next_trace_node() {
+ var prev_btn = document.getElementById("prev_trace");
+ var next_btn = document.getElementById("next_trace");
+ prev_btn.addEventListener("click", function(evt){
+ var node_id = $("#trace-tree").treeview(true).getSelected()[0].nodeId;
+ // We deliberatly choose to ignore the node_id out of bound case,
+ // since it won't break the UI usability
+ $("#trace-tree").treeview(true).selectNode(node_id - 1);
+ });
+ next_btn.addEventListener("click", function(evt){
+ var node_id = $("#trace-tree").treeview(true).getSelected()[0].nodeId;
+ $("#trace-tree").treeview(true).selectNode(node_id + 1);
+ });
+ }
+
+ $( document ).ready(function() {
+ var upload = document.getElementsByTagName('input')[0];
+ upload.onchange = function (e) {
+ e.preventDefault();
+
+ var file = upload.files[0],
+ reader = new FileReader();
+ reader.onload = function (event) {
+ new_data_loaded(JSON.parse(event.target.result));
+ };
+
+ reader.readAsText(file);
+ return false;
+ };
+ register_toggle_unchanaged_code_handler();
+ register_prev_next_trace_node();
});
- });
- });
-
- $("#trace-tree")
- .treeview(true)
- .selectNode(0);
- }
-
- $(document).ready(function() {
- let upload = document.getElementsByTagName("input")[0];
- upload.onchange = function(e) {
- e.preventDefault();
-
- let file = upload.files[0],
- reader = new FileReader();
- reader.onload = function(event) {
- new_data_loaded(JSON.parse(event.target.result));
- };
-
- reader.readAsText(file);
- return false;
- };
- });
- </script>
- </body>
+ </script>
+ </body>
</html>
diff --git a/etc/layout_viewer/viewer_2020.html b/etc/layout_viewer/viewer_2020.html
new file mode 100644
index 00000000000..76b85641e38
--- /dev/null
+++ b/etc/layout_viewer/viewer_2020.html
@@ -0,0 +1,339 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <title>Servo Layout Debugger</title>
+
+ <!-- Bootstrap -->
+ <link href="css/bootstrap.min.css" rel="stylesheet" />
+
+ <!-- Treeview -->
+ <link href="css/bootstrap-treeview.min.css" rel="stylesheet" />
+
+ <!-- JSDiffPatch -->
+ <link href="css/formatters/html.css" rel="stylesheet" />
+
+ <!-- Custom -->
+ <link href="css/main.css" rel="stylesheet" />
+
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+ <div class="container" role="main">
+ <div class="row">
+ <div class="col-sm-12">
+ <h1>Servo Layout Viewer</h1>
+ <p>
+ Check the
+ <a
+ href="https://github.com/servo/servo/blob/master/etc/layout_viewer/README.md"
+ >README</a
+ >
+ for instructions.
+ </p>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-sm-4">
+ <div class="row">
+ <div class="col-sm-12">
+ <div class="well">
+ <input type="file" />
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-sm-12">
+ <div id="trace-tree"></div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-sm-12">
+ <ul id="trace-list" class="list-group"></ul>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-8">
+ <div class="row">
+ <div class="col-sm-12">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ Box Tree
+ <a
+ id="box-tree-collapse"
+ class="tree-collapse"
+ data-collapsed="0"
+ ></a>
+ </div>
+ <div class="panel-body" id="box-tree"></div>
+ </div>
+ </div>
+ <div class="col-sm-12">
+ <div id="box-diffs"></div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-sm-12">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ Fragment Tree
+ <a
+ id="fragment-tree-collapse"
+ class="tree-collapse"
+ data-collapsed="0"
+ ></a>
+ </div>
+ <div class="panel-body" id="fragment-tree"></div>
+ </div>
+ </div>
+ <div class="col-sm-12">
+ <div id="fragment-diffs"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- jQuery -->
+ <script src="js/jquery.min.js"></script>
+ <!-- Bootstrap -->
+ <script src="js/bootstrap.min.js"></script>
+ <!-- Treeview -->
+ <script src="js/bootstrap-treeview.min.js"></script>
+ <!-- JSDiffPatch -->
+ <script src="js/bundle.min.js"></script>
+ <script src="js/formatters.min.js"></script>
+
+ <script>
+ function get_inner_boxes(box) {
+ const box_type = Object.keys(box)[0];
+ switch (box_type) {
+ case "BlockLevelBoxes":
+ return box.BlockLevelBoxes;
+ case "InlineFormattingContext":
+ return box.InlineFormattingContext.inline_level_boxes;
+ case "InlineBox":
+ return box.InlineBox.children;
+ case "SameFormattingContextBlock":
+ case "Independent":
+ case "Flow":
+ case "OutOfFlowAbsolutelyPositionedBox":
+ case "OutOfFlowFloatBox":
+ case "Atomic":
+ return box[box_type].contents;
+ }
+ return null;
+ }
+
+ function box_tree_from_container(container) {
+ const box_type = Object.keys(container)[0];
+ let inner_boxes = get_inner_boxes(container);
+ let nodes = [];
+ let text = box_type;
+ if (Array.isArray(inner_boxes)) {
+ if (!inner_boxes.length) {
+ nodes = null;
+ } else {
+ for (let box in inner_boxes) {
+ nodes.push(box_tree_from_container(inner_boxes[box]));
+ }
+ }
+ } else if (inner_boxes != null) {
+ nodes.push(box_tree_from_container(inner_boxes));
+ } else {
+ if (box_type == "TextRun") {
+ text += ` (${container.TextRun.text})`;
+ }
+ nodes = null;
+ }
+
+ let info;
+ if (
+ box_type != "BlockLevelBoxes" &&
+ box_type != "InlineFormattingContext"
+ ) {
+ info = Object.assign({}, Object.values(container)[0]);
+ delete info.children;
+ delete info.contents;
+ delete info.tag;
+ }
+
+ return {
+ text,
+ nodes,
+ info
+ };
+ }
+
+ function box_tree_from_bfc(bfc) {
+ const { contains_floats, contents } = bfc;
+ let block_container = Object.values(contents)[0];
+ return {
+ text: "BlockFormattingContext",
+ info: {
+ contains_floats
+ },
+ nodes: [box_tree_from_container(contents)]
+ };
+ }
+
+ function create_fragment_tree(root) {
+ let fragment = Object.values(root)[0];
+ let node = {
+ text: Object.keys(root)[0],
+ id: fragment.debug_id,
+ href: "#diff-" + fragment.debug_id
+ };
+
+ if (fragment.children) {
+ let children = [];
+ for (let i = 0; i < fragment.children.length; ++i) {
+ children.push(create_fragment_tree(fragment.children[i]));
+ }
+
+ if (children.length > 0) {
+ node.nodes = children;
+ }
+ }
+
+ node.info = Object.assign({}, fragment);
+ delete node.info.children;
+ delete node.info.debug_id;
+
+ return node;
+ }
+
+ function flatten_trace(trace_node) {
+ const fragment_tree_root = Object.values(
+ trace_node.fragment_tree.children
+ )[0];
+ return {
+ fragment_tree: create_fragment_tree(fragment_tree_root),
+ box_tree: box_tree_from_bfc(trace_node.box_tree)
+ };
+ }
+
+ function create_trace_tree_node(trace_node) {
+ const trace = flatten_trace(trace_node.pre);
+
+ let tree_node = {
+ text: trace_node.name,
+ icon: "dummy",
+ box_tree: trace.box_tree,
+ fragment_tree: trace.fragment_tree
+ };
+
+ let node = Object.values(trace_node)[0];
+ if (node.children) {
+ let children = [];
+ for (let i = 0; i < node.children.length; ++i) {
+ children.push(create_trace_tree_node(node.children[i]));
+ }
+
+ if (children.length > 0) {
+ tree_node.nodes = children;
+ }
+ }
+
+ return tree_node;
+ }
+
+ function new_data_loaded(data) {
+ jsondiffpatch.formatters.html.hideUnchanged();
+
+ let node_color_hash = {};
+ let tree = [create_trace_tree_node(data)];
+ $("#trace-tree").treeview({ data: tree, levels: 3 });
+ $("#trace-tree").on("nodeSelected", function(event, node) {
+ $("#fragment-diffs").empty();
+ $("#trace-tree")
+ .treeview(true)
+ .revealNode(node);
+
+ const on_tree_node_selected = tree => (event, data) => {
+ $(`#${tree}-diffs`).empty();
+ if (!data.info) return;
+ // XXX(ferjm) no diff for now.
+ const delta = jsondiffpatch
+ .create({
+ objectHash: function(obj) {
+ return JSON.stringify(obj);
+ }
+ })
+ .diff({}, data.info);
+
+ const json = jsondiffpatch.formatters.html.format(delta, data.info);
+
+ $(`#${tree}-diffs`).append(
+ "<div class='panel panel-default'><div class='panel-heading'>" +
+ data.text +
+ "</div><div class='panel-body'>" +
+ json +
+ "</div></div>"
+ );
+ };
+
+ const on_fragment_tree_node_selected = on_tree_node_selected(
+ "fragment"
+ );
+ const on_box_tree_node_selected = on_tree_node_selected("box");
+
+ $("#fragment-tree").treeview({
+ data: [node.fragment_tree],
+ levels: 100,
+ enableLinks: false,
+ emptyIcon: "glyphicon glyphicon-unchecked hidden-glyphicon",
+ onNodeSelected: on_fragment_tree_node_selected
+ });
+
+ $("#box-tree").treeview({
+ data: [node.box_tree],
+ levels: 100,
+ enableLinks: false,
+ emptyIcon: "glyphicon glyphicon-unchecked hidden-glyphicon",
+ onNodeSelected: on_box_tree_node_selected
+ });
+
+ ["fragment", "box"].forEach(key => {
+ const collapsable = $(`#${key}-tree-collapse`);
+ collapsable.html("Collapse all").on("click", () => {
+ const collapsed = collapsable.data("collapsed");
+ if (collapsed == 0) {
+ $(`#${key}-tree`).treeview("collapseAll");
+ } else {
+ $(`#${key}-tree`).treeview("expandAll");
+ }
+ collapsable.html(collapsed == 0 ? "Expand all" : "Collapse all");
+ collapsable.data("collapsed", collapsed == 0 ? 1 : 0);
+ });
+ });
+ });
+
+ $("#trace-tree")
+ .treeview(true)
+ .selectNode(0);
+ }
+
+ $(document).ready(function() {
+ let upload = document.getElementsByTagName("input")[0];
+ upload.onchange = function(e) {
+ e.preventDefault();
+
+ let file = upload.files[0],
+ reader = new FileReader();
+ reader.onload = function(event) {
+ new_data_loaded(JSON.parse(event.target.result));
+ };
+
+ reader.readAsText(file);
+ return false;
+ };
+ });
+ </script>
+ </body>
+</html>