1
0
Fork 0
arangodb/html/admin/js/graphViewer/graph/edgeShaper.js

366 lines
9.3 KiB
JavaScript

/*jslint indent: 2, nomen: true, maxlen: 100, white: true plusplus: true */
/*global _, $, d3*/
/*global ColourMapper*/
////////////////////////////////////////////////////////////////////////////////
/// @brief Graph functionality
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed 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.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
/*
* flags example format:
* {
* shape: {
* type: EdgeShaper.shapes.ARROW
* }
* label: "key" \\ function(node)
* actions: {
* click: function(edge)
* }
* }
*
*
*/
function EdgeShaper(parent, flags, idfunc) {
"use strict";
var self = this,
toplevelSVG,
idFunction = function(d) {
return d.source._id + "-" + d.target._id;
},
noop = function (line, g) {
},
colourMapper = new ColourMapper(),
events,
addUpdate,
addShape = noop,
addLabel = noop,
addColor = noop,
unbindEvents = function() {
events = {
click: noop,
dblclick: noop,
mousedown: noop,
mouseup: noop,
mousemove: noop
};
addUpdate = noop;
},
addEvents = function (line, g) {
_.each(events, function (func, type) {
g.on(type, func);
});
},
bindEvent = function (type, func) {
if (type === "update") {
addUpdate = func;
} else if (events[type] === undefined) {
throw "Sorry Unknown Event " + type + " cannot be bound.";
} else {
events[type] = func;
}
},
addQue = function (line, g) {
addShape(line, g);
addLabel(line, g);
addColor(line, g);
addEvents(line, g);
},
shapeEdges = function (edges) {
if (edges !== undefined) {
var data, g, line;
data = self.parent
.selectAll(".link")
.data(edges, idFunction);
g = data
.enter()
.append("g")
.attr("class", "link") // link is CSS class that might be edited
.attr("id", idFunction);
line = g.append("line");
addQue(line, g);
data.exit().remove();
return g;
}
},
reshapeEdges = function () {
var g, line;
$(".link").empty();
g = self.parent
.selectAll(".link");
line = g.append("line");
addQue(line, g);
},
reshapeEdge = function (edge) {
$("#" + edge._id.toString().replace(/([ #;&,.+*~\':"!\^$\[\]()=>|\/])/g,'\\$1')).empty();
var g = self.parent
.selectAll(".link")
.filter(function (e) {
return e._id === edge._id;
}),
line = g.append("line");
addQue(line, g);
},
getCorner = function(s, t) {
return Math.atan2(t.y - s.y, t.x - s.x) * 180 / Math.PI;
},
getDistance = function(s, t) {
return Math.sqrt(
(t.y - s.y)
* (t.y - s.y)
+ (t.x - s.x)
* (t.x - s.x)
);
},
updateEdges = function () {
var edges = self.parent.selectAll(".link")
// Set source x coordinate for edge.
.attr("transform", function(d) {
return "translate("
+ d.source.x + ", "
+ d.source.y + ")"
+ "rotate("
+ getCorner(d.source, d.target)
+ ")";
});
edges.select("line")
.attr("x2", function(d) {
return getDistance(d.source, d.target);
});
addUpdate(edges);
},
parseShapeFlag = function (shape) {
$("svg defs marker#arrow").remove();
switch (shape.type) {
case EdgeShaper.shapes.NONE:
addShape = noop;
break;
case EdgeShaper.shapes.ARROW:
addShape = function (line, g) {
line.attr("marker-end", "url(#arrow)");
};
if (toplevelSVG.selectAll("defs")[0].length === 0) {
toplevelSVG.append("defs");
}
toplevelSVG
.select("defs")
.append("marker")
.attr("id", "arrow")
.attr("refX", "22")
.attr("refY", "5")
.attr("markerUnits", "strokeWidth")
.attr("markerHeight", "10")
.attr("markerWidth", "10")
.attr("orient", "auto")
.append("path")
.attr("d", "M 0 0 L 10 5 L 0 10 z");
break;
default:
throw "Sorry given Shape not known!";
}
},
parseLabelFlag = function (label) {
if (_.isFunction(label)) {
addLabel = function (line, g) {
g.append("text") // Append a label for the edge
.attr("text-anchor", "middle") // Define text-anchor
.attr("stroke", "black")
.text(label);
};
} else {
addLabel = function (line, g) {
g.append("text") // Append a label for the edge
.attr("text-anchor", "middle") // Define text-anchor
.attr("stroke", "black")
.text(function(d) {
return d[label] !== undefined ? d[label] : ""; // Which value should be used as label
});
};
}
addUpdate = function (edges) {
edges.select("text")
.attr("transform", function(d) {
return "translate("
+ getDistance(d.source, d.target) / 2
+ ", -3)";
});
};
},
parseActionFlag = function (actions) {
if (actions.reset !== undefined && actions.reset) {
unbindEvents();
}
_.each(actions, function(func, type) {
if (type !== "reset") {
bindEvent(type, func);
}
});
},
parseColorFlag = function (color) {
$("svg defs #gradientEdgeColor").remove();
switch (color.type) {
case "single":
addColor = function (line, g) {
line.attr("stroke", color.stroke);
};
break;
case "gradient":
if (toplevelSVG.selectAll("defs")[0].length === 0) {
toplevelSVG.append("defs");
}
var gradient = toplevelSVG
.select("defs")
.append("linearGradient")
.attr("id", "gradientEdgeColor");
gradient.append("stop")
.attr("offset", "0")
.attr("stop-color", color.source);
gradient.append("stop")
.attr("offset", "0.4")
.attr("stop-color", color.source);
gradient.append("stop")
.attr("offset", "0.6")
.attr("stop-color", color.target);
gradient.append("stop")
.attr("offset", "1")
.attr("stop-color", color.target);
addColor = function (line, g) {
line.attr("stroke", "url(#gradientEdgeColor)");
line.attr("y2", "0.0000000000000001");
};
break;
case "attribute":
addColor = function (line, g) {
g.attr("stroke", function(e) {
return colourMapper.getColour(e[color.key]);
});
};
break;
default:
throw "Sorry given colour-scheme not known";
}
},
parseConfig = function(config) {
if (config.shape !== undefined) {
parseShapeFlag(config.shape);
}
if (config.label !== undefined) {
parseLabelFlag(config.label);
}
if (config.actions !== undefined) {
parseActionFlag(config.actions);
}
if (config.color !== undefined) {
parseColorFlag(config.color);
}
};
self.parent = parent;
unbindEvents();
toplevelSVG = parent;
while (toplevelSVG[0][0] && toplevelSVG[0][0].ownerSVGElement) {
toplevelSVG = d3.select(toplevelSVG[0][0].ownerSVGElement);
}
if (flags === undefined) {
flags = {
color: {
type: "single",
stroke: "#686766"
}
};
}
if (flags.color === undefined) {
flags.color = {
type: "single",
stroke: "#686766"
};
}
parseConfig(flags);
if (_.isFunction(idfunc)) {
idFunction = idfunc;
}
/////////////////////////////////////////////////////////
/// Public functions
/////////////////////////////////////////////////////////
self.changeTo = function(config) {
parseConfig(config);
reshapeEdges();
updateEdges();
};
self.drawEdges = function (edges) {
var res = shapeEdges(edges);
updateEdges();
return res;
};
self.updateEdges = function () {
updateEdges();
};
self.reshapeEdge = function(edge) {
reshapeEdge(edge);
};
self.reshapeEdges = function() {
reshapeEdges();
};
}
EdgeShaper.shapes = Object.freeze({
"NONE": 0,
"ARROW": 1
});