blob: 86c8c813a176165030d68d84ffe327cc0ed2c551 [file] [log] [blame]
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Document initialization.
*/
$(document).ready(function ()
{
zoom = d3.behavior.zoom("#svg-main").on("zoom", function() {
var ev = d3.event;
d3.select("#svg-main g")
.attr("transform", "translate(" + ev.translate + ") scale(" + ev.scale + ")");
});
zoom.scaleExtent([0.3, 3])
});
//The current JSON file
var JSONData;
//The informations for the iterations
var iterationIds = new Array();
var iterationGraphs = new Array();
var iterationWidths = new Array();
var iterationHeights = new Array();
//The zoom element
var zoom;
//informations about the enviroment
var svgWidth;
var svgHeight;
//Renders and draws the graph
function drawGraph(data, svgID){
JSONData = data;
//First step: precompute all iteration graphs
//find all iterations
iterationNodes = searchForIterationNodes();
//add the graphs of iterations and their sizes + Ids to arrays
if (iterationNodes != null) {
for (var i in iterationNodes) {
var itNode = iterationNodes[i];
iterationIds.push(itNode.id);
var g0 = loadJsonToDagre(itNode);
iterationGraphs.push(g0);
var r = new dagreD3.Renderer();
var l = dagreD3.layout()
.nodeSep(20)
.rankDir("LR");
l = r.layout(l).run(g0, d3.select("#svg-main"));
iterationWidths.push(l._value.width);
iterationHeights.push(l._value.height);
//Clean svg
$("#svg-main g").empty();
}
}
//Continue normal
var g = loadJsonToDagre(data);
var selector = svgID + " g";
var renderer = new dagreD3.Renderer();
var layout = dagreD3.layout()
.nodeSep(20)
.rankDir("LR");
var svgElement = d3.select(selector);
layout = renderer.layout(layout).run(g, svgElement);
svgHeight = layout._value.height;
svgWidth = layout._value.width;
var svg = d3.select("#svg-main")
.attr("width", $(document).width() - 15)
.attr("height", $(document).height() - 15 - 110)
// .attr("viewBox", "0 0 "+ ($(document).width() - 150) +" "+($(document).height() - 15 - 110))
.call(zoom);
// This should now draw the precomputed graphs in the svgs... .
for (var i in iterationIds) {
var workset = searchForNode(iterationIds[i]);
renderer = new dagreD3.Renderer();
layout = dagreD3.layout()
.nodeSep(20)
.rankDir("LR");
selector = "#svg-"+iterationIds[i]+" g";
svgElement = d3.select(selector);
layout = renderer.layout(layout).run(iterationGraphs[i], svgElement);
}
//enable Overlays and register function for overlay-infos
$("a[rel]").overlay({
onBeforeLoad: function(){
var id = this.getTrigger().attr("nodeID")
showProperties(id);
}
});
}
//Creates the dagreD3 graph object
//Responsible for adding nodes and edges
function loadJsonToDagre(data){
//stores all nodes that are in current graph -> no edges to nodes which are outside of current iterations!
var existingNodes = new Array;
var g = new dagreD3.Digraph();
//Find out whether we are in an iteration or in the normal json
//Bulk variables
var partialSolution;
var nextPartialSolution;
//Workset variables
var workset;
var nextWorkset;
var solutionSet;
var solutionDelta;
if (data.nodes != null) {
//This is the normal json data
var toIterate = data.nodes;
} else {
//This is an iteration, we now store special iteration nodes if possible
var toIterate = data.step_function;
partialSolution = data.partial_solution;
nextPartialSolution = data.next_partial_solution;
workset = data.workset;
nextWorkset = data.next_workset;
solutionSet = data.solution_set;
solutionDelta = data.solution_delta;
}
for (var i in toIterate) {
var el = toIterate[i];
//create node, send additional informations about the node if it is a special one
if (el.id == partialSolution) {
g.addNode(el.id, { label: createLabelNode(el, "partialSolution"), nodesWithoutEdges: ""} );
} else if (el.id == nextPartialSolution) {
g.addNode(el.id, { label: createLabelNode(el, "nextPartialSolution"), nodesWithoutEdges: ""} );
} else if (el.id == workset) {
g.addNode(el.id, { label: createLabelNode(el, "workset"), nodesWithoutEdges: ""} );
} else if (el.id == nextWorkset) {
g.addNode(el.id, { label: createLabelNode(el, "nextWorkset"), nodesWithoutEdges: ""} );
} else if (el.id == solutionSet) {
g.addNode(el.id, { label: createLabelNode(el, "solutionSet"), nodesWithoutEdges: ""} );
} else if (el.id == solutionDelta) {
g.addNode(el.id, { label: createLabelNode(el, "solutionDelta"), nodesWithoutEdges: ""} );
} else {
g.addNode(el.id, { label: createLabelNode(el, ""), nodesWithoutEdges: ""} );
}
existingNodes.push(el.id);
//create edgdes from predecessors to current node
if (el.predecessors != null) {
for (var j in el.predecessors) {
if (existingNodes.indexOf(el.predecessors[j].id) != -1) {
g.addEdge(null, el.predecessors[j].id, el.id, { label: createLabelEdge(el.predecessors[j]) });
} else {
var missingNode = searchForNode(el.predecessors[j].id);
if (missingNode.alreadyAdded != true) {
missingNode.alreadyAdded = true;
g.addNode(missingNode.id, {label: createLabelNode(missingNode, "mirror")});
g.addEdge(null, missingNode.id, el.id, { label: createLabelEdge(missingNode) });
}
}
}
}
}
return g;
}
//create a label of an edge
function createLabelEdge(el) {
var labelValue = "";
if (el.ship_strategy != null || el.local_strategy != null) {
labelValue += "<div style=\"font-size: 100%; border:2px solid; padding:5px\">";
if (el.ship_strategy != null) {
labelValue += el.ship_strategy;
}
if (el.temp_mode != undefined) {
labelValue += " (" + el.temp_mode + ")";
}
if (el.local_strategy != undefined) {
labelValue += ",<br>" + el.local_strategy;
}
labelValue += "</div>";
}
return labelValue;
}
//creates the label of a node, in info is stored, whether it is a special node (like a mirror in an iteration)
function createLabelNode(el, info) {
// if (info != "") {
// console.log("The node " + el.id + " is a " + info);
// }
//true, if the node is a special node from an iteration
var specialIterationNode = (info == "partialSolution" || info == "nextPartialSolution" || info == "workset" || info == "nextWorkset" || info == "solutionSet" || info == "solutionDelta" );
var labelValue = "<div style=\"margin-top: 0\">";
//set color of panel
if (info == "mirror") {
labelValue += "<div style=\"border-color:#a8a8a8; border-width:4px; border-style:solid\">";
} else if (specialIterationNode) {
labelValue += "<div style=\"border-color:#CD3333; border-width:4px; border-style:solid\">";
} else {
//there is no info value, set normal color
if (el.pact == "Data Source") {
labelValue += "<div style=\"border-color:#4ce199; border-width:4px; border-style:solid\">";
} else if (el.pact == "Data Sink") {
labelValue += "<div style=\"border-color:#e6ec8b; border-width:4px; border-style:solid\">";
} else {
labelValue += "<div style=\"border-color:#3fb6d8; border-width:4px; border-style:solid\">";
}
}
//Nodename
if (info == "mirror") {
labelValue += "<div><a nodeID=\""+el.id+"\" href=\"#\" rel=\"#propertyO\"><h3 style=\"text-align: center; "
+ "font-size: 150%\">Mirror of " + el.pact + " (ID = "+el.id+")</h3></a>";
} else {
labelValue += "<div><a nodeID=\""+el.id+"\" href=\"#\" rel=\"#propertyO\"><h3 style=\"text-align: center; "
+ "font-size: 150%\">" + el.pact + " (ID = "+el.id+")</h3></a>";
}
if (el.contents == "") {
labelValue += "</div>";
} else {
var stepName = el.contents;
//clean stepName
stepName = shortenString(stepName);
labelValue += "<h4 style=\"text-align: center; font-size: 130%\">" + stepName + "</h4></div>";
}
//If this node is an "iteration" we need a different panel-body
if (el.step_function != null) {
labelValue += extendLabelNodeForIteration(el.id);
} else {
//Otherwise add infos
if (specialIterationNode) {
labelValue += "<h5 style=\"font-size:115%; text-align: center; color:#CD3333\">" + info + " Node</h5>";
}
if (el.parallelism != "") {
labelValue += "<h5 style=\"font-size:115%\">Parallelism: " + el.parallelism + "</h5>";
}
if (el.driver_strategy != undefined) {
labelValue += "<h5 style=\"font-size:115%\">Driver Strategy: " + el.driver_strategy + "</h5";
}
}
//close divs
labelValue += "</div></div>";
return labelValue;
}
//Extends the label of a node with an additional svg Element to present the iteration.
function extendLabelNodeForIteration(id) {
var svgID = "svg-" + id;
//Find out the position of the iterationElement in the iterationGraphArray
var index = iterationIds.indexOf(id);
//Set the size and the width of the svg Element as precomputetd
var width = iterationWidths[index] + 70;
var height = iterationHeights[index] + 40;
var labelValue = "<div id=\"attach\"><svg id=\""+svgID+"\" width="+width+" height="+height+"><g transform=\"translate(20, 20)\"/></svg></div>";
return labelValue;
}
//presents properties for a given nodeID in the propertyCanvas overlay
function showProperties(nodeID) {
$("#propertyCanvas").empty();
node = searchForNode(nodeID);
var phtml = "<div style='overflow-y: scroll; max-height:490px; overflow-x:hidden'><h3>Properties of "+ shortenString(node.contents) + " - ID = " + nodeID + "</h3>";
phtml += "<div class=\"row\">";
phtml += "<div class=\"col-sm-12\"><h4>Pact Properties</h4>";
phtml += "<table class=\"table\">";
phtml += tableRow("Operator", (node.driver_strategy == undefined ? "None" : node.driver_strategy));
phtml += tableRow("Parallelism", (node.parallelism == undefined ? "None" : node.parallelism));
phtml += tableRow("Subtasks-per-instance", (node.subtasks_per_instance == undefined ? "None" : node.subtasks_per_instance));
phtml += "</table></div>";
phtml += "<div class=\"col-sm-12\"><h4>Global Data Properties</h4>";
phtml += "<table class=\"table\">";
if (node.global_properties != null) {
for (var i = 0; i < node.global_properties.length; i++) {
var prop = node.global_properties[i];
phtml += tableRow((prop.name == undefined ? '(unknown)' : prop.name),(prop.value == undefined ? "(none)" : prop.value));
}
} else {
phtml += tableRow("Global Properties", "None");
}
phtml += "</table></div>";
phtml += "<div class=\"col-sm-12\"><h4>Local Data Properties</h4>";
phtml += "<table class=\"table\">";
if (node.local_properties != null) {
for (var i = 0; i < node.local_properties.length; i++) {
var prop = node.local_properties[i];
phtml += tableRow((prop.name == undefined ? '(unknown)' : prop.name),(prop.value == undefined ? "(none)" : prop.value));
}
} else {
phtml += tableRow("Local Properties", "None");
}
phtml += "</table></div></div>";
phtml += "<div class=\"row\">";
phtml += "<div class=\"col-sm-12\"><h4>Size Estimates</h4>";
phtml += "<table class=\"table\">";
if (node.estimates != null) {
for (var i = 0; i < node.estimates.length; i++) {
var prop = node.estimates[i];
phtml += tableRow((prop.name == undefined ? '(unknown)' : prop.name),(prop.value == undefined ? "(none)" : prop.value));
}
} else {
phtml += tableRow("Size Estimates", "None");
}
phtml += "</table></div>";
phtml += "<div class=\"col-sm-12\"><h4>Cost Estimates</h4>";
phtml += "<table class=\"table\">";
if (node.costs != null) {
for (var i = 0; i < node.costs.length; i++) {
var prop = node.costs[i];
phtml += tableRow((prop.name == undefined ? '(unknown)' : prop.name),(prop.value == undefined ? "(none)" : prop.value));
}
} else {
phtml += tableRow("Cost Estimates", "None");
}
phtml += "</table></div>";
phtml += "</div></div>";
$("#propertyCanvas").append(phtml);
}
//searches in the global JSONData for the node with the given id
function searchForNode(nodeID) {
for (var i in JSONData.nodes) {
var el = JSONData.nodes[i];
if (el.id == nodeID) {
return el;
}
//look for nodes that are in iterations
if (el.step_function != null) {
for (var j in el.step_function) {
if (el.step_function[j].id == nodeID) {
return el.step_function[j];
}
}
}
}
}
//searches for all nodes in the global JSONData, that are iterations
function searchForIterationNodes() {
var itN = new Array();
for (var i in JSONData.nodes) {
var el = JSONData.nodes[i];
if (el.step_function != null) {
itN.push(el);
}
}
return itN;
}
//creates a row for a table with two collums
function tableRow(nameX, valueX) {
var htmlCode = "";
htmlCode += "<tr><td align=\"left\">" + nameX + "</td><td align=\"right\">" + valueX + "</td></tr>";
return htmlCode;
}
//Split a string into multiple lines so that each line has less than 30 letters.
function shortenString(s) {
//make sure that name does not contain a < (because of html)
if (s.charAt(0) == "<") {
s = s.replace("<", "&lt;");
s = s.replace(">", "&gt;");
}
sbr = ""
while (s.length > 30) {
sbr = sbr + s.substring(0, 30) + "<br>"
s = s.substring(30, s.length)
}
sbr = sbr + s
return sbr;
}
//activates the zoom buttons
function activateZoomButtons() {
$("#zoomIn").click(function() {
console.log("Clicked zoom in");
if (zoom.scale() < 2.99) {
var svg = d3.select("#svg-main");
//Calculate and store new values in zoom object
var translate = zoom.translate();
var v1 = translate[0] * (zoom.scale() + 0.1 / (zoom.scale()));
var v2 = translate[1] * (zoom.scale() + 0.1 / (zoom.scale()));
zoom.scale(zoom.scale() + 0.1);
zoom.translate([v1, v2]);
//Transform svg
svg.select("#svg-main g")
.attr("transform", "translate(" + v1 + ","+ v2 + ") scale(" + zoom.scale() + ")");
}
});
$("#zoomOut").click(function() {
if (zoom.scale() > 0.31) {
var svg = d3.select("#svg-main");
//Calculate and store new values in zoom object
zoom.scale(zoom.scale() - 0.1);
var translate = zoom.translate();
var v1 = translate[0] * (zoom.scale() - 0.1 / (zoom.scale()));
var v2 = translate[1] * (zoom.scale() - 0.1 / (zoom.scale()));
zoom.translate([v1, v2]);
//Transform svg
svg.select("#svg-main g")
.attr("transform", "translate(" + v1 + ","+ v2 + ") scale(" + zoom.scale() + ")");
}
});
}