| <!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/main/etc/layout_viewer/README">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'>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> | |
| <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> | |
| <!-- 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; | |
| } | |
| 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])); | |
| } | |
| 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(); | |
| }); | |
| </script> | |
| </body> | |
| </html> |