var dot = {};
dot.dotQuote = function(s) {
return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
};
dot.nodeAttrs = function(node, focusId, depth) {
var kind = node.kindPrefix;
var isFocus = (node.id === focusId);
var fill, font, shape;
switch (kind) {
case 'SL':
fill = '#4A90D9'; font = '#ffffff'; shape = 'box,rounded'; break;
case 'ADR': case 'POL':
fill = '#7B4FBF'; font = '#ffffff'; shape = 'box'; break;
case 'STD':
fill = '#9B59B6'; font = '#ffffff'; shape = 'box'; break;
case 'PRD': case 'SPEC':
fill = '#E67E22'; font = '#222222'; shape = 'box,rounded'; break;
case 'REQ':
fill = '#F39C12'; font = '#222222'; shape = 'box'; break;
case 'ISS': case 'IMP': case 'CHR': case 'RSK':
fill = '#C0392B'; font = '#ffffff'; shape = 'box'; break;
case 'IDE':
fill = '#27AE60'; font = '#222222'; shape = 'box'; break;
case 'RV':
fill = '#1ABC9C'; font = '#222222'; shape = 'box'; break;
case 'REC':
fill = '#95A5A6'; font = '#222222'; shape = 'box'; break;
case 'ASM': case 'DEC':
fill = '#3498DB'; font = '#ffffff'; shape = 'box'; break;
case 'QUE': case 'CON':
fill = '#8E44AD'; font = '#ffffff'; shape = 'box'; break;
case 'REV':
fill = '#A04000'; font = '#ffffff'; shape = 'box'; break;
default:
fill = '#95A5A6'; font = '#222222'; shape = 'box'; break;
}
return {
label: node.id,
fillcolor: fill,
fontcolor: font,
shape: shape,
penwidth: isFocus ? 3.0 : 1.0,
tooltip: node.id + ': ' + node.title + ' \u00b7 ' + (node.kindLabel || node.kindPrefix) + ' \u00b7 ' + node.status
};
};
dot._EDGE_COLORS = {
'depends': { color: '#aaaaaa', fontcolor: '#aaaaaa' },
'requires': { color: '#aaaaaa', fontcolor: '#aaaaaa' },
'refines': { color: '#4A90D9', fontcolor: '#2563eb' },
'details': { color: '#4A90D9', fontcolor: '#2563eb' },
'implements': { color: '#27AE60', fontcolor: '#166534' },
'satisfies': { color: '#27AE60', fontcolor: '#166534' },
'blocks': { color: '#C0392B', fontcolor: '#991b1b' },
'parent': { color: '#7B4FBF', fontcolor: '#6d28d9' },
'child': { color: '#7B4FBF', fontcolor: '#6d28d9' },
'related': { color: '#95A5A6', fontcolor: '#64748b' },
'see also': { color: '#95A5A6', fontcolor: '#64748b' },
'resolves': { color: '#E67E22', fontcolor: '#c2410c' },
'addresses': { color: '#E67E22', fontcolor: '#c2410c' }
};
dot.edgeAttrs = function(edge, depth) {
var key = edge.label.toLowerCase();
var entry = dot._EDGE_COLORS[key] || { color: '#888888', fontcolor: '#555555' };
return {
label: edge.label,
tooltip: edge.id,
color: entry.color,
fontcolor: entry.fontcolor
};
};
dot.graphToDot = function(neighbourhood, focusId, depth) {
var lines = [];
lines.push('digraph G {');
lines.push(' rankdir=LR;');
lines.push(' bgcolor="transparent";');
lines.push(' nodesep=0.45;');
lines.push(' ranksep=0.8;');
lines.push('');
var sortedIds = [];
neighbourhood.nodes.forEach(function(_, id) {
sortedIds.push(id);
});
sortedIds.sort();
sortedIds.forEach(function(id) {
var node = state.graph.nodes.get(id);
if (!node) return;
var attrs = dot.nodeAttrs(node, focusId, depth);
lines.push(' "' + dot.dotQuote(id) + '" [' +
'label="' + dot.dotQuote(attrs.label) + '", ' +
'style="filled", ' +
'fillcolor="' + attrs.fillcolor + '", ' +
'fontcolor="' + attrs.fontcolor + '", ' +
'shape="' + attrs.shape + '", ' +
'penwidth=' + attrs.penwidth + ', ' +
'tooltip="' + dot.dotQuote(attrs.tooltip) + '"' +
'];');
});
var sortedEdges = neighbourhood.edges.slice().sort(function(a, b) {
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
});
sortedEdges.forEach(function(edge) {
var attrs = dot.edgeAttrs(edge, depth);
lines.push(' "' + dot.dotQuote(edge.source) + '" -> "' + dot.dotQuote(edge.target) + '" [' +
'label="' + dot.dotQuote(attrs.label) + '", ' +
'color="' + attrs.color + '", ' +
'fontcolor="' + attrs.fontcolor + '", ' +
'tooltip="' + dot.dotQuote(attrs.tooltip) + '"' +
'];');
});
lines.push('}');
return lines.join('\n');
};