| <!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> |