mermaid
Version:
Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams and gantt charts.
1,853 lines (1,498 loc) • 1.67 MB
JavaScript
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.mermaidAPI=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
},{}],2:[function(require,module,exports){
(function (process){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// resolves . and .. elements in a path array with directory names there
// must be no slashes, empty elements, or device names (c:\) in the array
// (so also no leading and trailing slashes - it does not distinguish
// relative and absolute paths)
function normalizeArray(parts, allowAboveRoot) {
// if the path tries to go above the root, `up` ends up > 0
var up = 0;
for (var i = parts.length - 1; i >= 0; i--) {
var last = parts[i];
if (last === '.') {
parts.splice(i, 1);
} else if (last === '..') {
parts.splice(i, 1);
up++;
} else if (up) {
parts.splice(i, 1);
up--;
}
}
// if the path is allowed to go above the root, restore leading ..s
if (allowAboveRoot) {
for (; up--; up) {
parts.unshift('..');
}
}
return parts;
}
// Split a filename into [root, dir, basename, ext], unix version
// 'root' is just a slash, or nothing.
var splitPathRe =
/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
var splitPath = function(filename) {
return splitPathRe.exec(filename).slice(1);
};
// path.resolve([from ...], to)
// posix version
exports.resolve = function() {
var resolvedPath = '',
resolvedAbsolute = false;
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
var path = (i >= 0) ? arguments[i] : process.cwd();
// Skip empty and invalid entries
if (typeof path !== 'string') {
throw new TypeError('Arguments to path.resolve must be strings');
} else if (!path) {
continue;
}
resolvedPath = path + '/' + resolvedPath;
resolvedAbsolute = path.charAt(0) === '/';
}
// At this point the path should be resolved to a full absolute path, but
// handle relative paths to be safe (might happen when process.cwd() fails)
// Normalize the path
resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
return !!p;
}), !resolvedAbsolute).join('/');
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
};
// path.normalize(path)
// posix version
exports.normalize = function(path) {
var isAbsolute = exports.isAbsolute(path),
trailingSlash = substr(path, -1) === '/';
// Normalize the path
path = normalizeArray(filter(path.split('/'), function(p) {
return !!p;
}), !isAbsolute).join('/');
if (!path && !isAbsolute) {
path = '.';
}
if (path && trailingSlash) {
path += '/';
}
return (isAbsolute ? '/' : '') + path;
};
// posix version
exports.isAbsolute = function(path) {
return path.charAt(0) === '/';
};
// posix version
exports.join = function() {
var paths = Array.prototype.slice.call(arguments, 0);
return exports.normalize(filter(paths, function(p, index) {
if (typeof p !== 'string') {
throw new TypeError('Arguments to path.join must be strings');
}
return p;
}).join('/'));
};
// path.relative(from, to)
// posix version
exports.relative = function(from, to) {
from = exports.resolve(from).substr(1);
to = exports.resolve(to).substr(1);
function trim(arr) {
var start = 0;
for (; start < arr.length; start++) {
if (arr[start] !== '') break;
}
var end = arr.length - 1;
for (; end >= 0; end--) {
if (arr[end] !== '') break;
}
if (start > end) return [];
return arr.slice(start, end - start + 1);
}
var fromParts = trim(from.split('/'));
var toParts = trim(to.split('/'));
var length = Math.min(fromParts.length, toParts.length);
var samePartsLength = length;
for (var i = 0; i < length; i++) {
if (fromParts[i] !== toParts[i]) {
samePartsLength = i;
break;
}
}
var outputParts = [];
for (var i = samePartsLength; i < fromParts.length; i++) {
outputParts.push('..');
}
outputParts = outputParts.concat(toParts.slice(samePartsLength));
return outputParts.join('/');
};
exports.sep = '/';
exports.delimiter = ':';
exports.dirname = function(path) {
var result = splitPath(path),
root = result[0],
dir = result[1];
if (!root && !dir) {
// No dirname whatsoever
return '.';
}
if (dir) {
// It has a dirname, strip trailing slash
dir = dir.substr(0, dir.length - 1);
}
return root + dir;
};
exports.basename = function(path, ext) {
var f = splitPath(path)[2];
// TODO: make this comparison case-insensitive on windows?
if (ext && f.substr(-1 * ext.length) === ext) {
f = f.substr(0, f.length - ext.length);
}
return f;
};
exports.extname = function(path) {
return splitPath(path)[3];
};
function filter (xs, f) {
if (xs.filter) return xs.filter(f);
var res = [];
for (var i = 0; i < xs.length; i++) {
if (f(xs[i], i, xs)) res.push(xs[i]);
}
return res;
}
// String.prototype.substr - negative index don't work in IE8
var substr = 'ab'.substr(-1) === 'b'
? function (str, start, len) { return str.substr(start, len) }
: function (str, start, len) {
if (start < 0) start = str.length + start;
return str.substr(start, len);
}
;
}).call(this,require('_process'))
},{"_process":3}],3:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
process.nextTick = (function () {
var canSetImmediate = typeof window !== 'undefined'
&& window.setImmediate;
var canMutationObserver = typeof window !== 'undefined'
&& window.MutationObserver;
var canPost = typeof window !== 'undefined'
&& window.postMessage && window.addEventListener
;
if (canSetImmediate) {
return function (f) { return window.setImmediate(f) };
}
var queue = [];
if (canMutationObserver) {
var hiddenDiv = document.createElement("div");
var observer = new MutationObserver(function () {
var queueList = queue.slice();
queue.length = 0;
queueList.forEach(function (fn) {
fn();
});
});
observer.observe(hiddenDiv, { attributes: true });
return function nextTick(fn) {
if (!queue.length) {
hiddenDiv.setAttribute('yes', 'no');
}
queue.push(fn);
};
}
if (canPost) {
window.addEventListener('message', function (ev) {
var source = ev.source;
if ((source === window || source === null) && ev.data === 'process-tick') {
ev.stopPropagation();
if (queue.length > 0) {
var fn = queue.shift();
fn();
}
}
}, true);
return function nextTick(fn) {
queue.push(fn);
window.postMessage('process-tick', '*');
};
}
return function nextTick(fn) {
setTimeout(fn, 0);
};
})();
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.binding = function (name) {
throw new Error('process.binding is not supported');
};
// TODO(shtylman)
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
},{}],4:[function(require,module,exports){
/**
* @license
* Copyright (c) 2012-2013 Chris Pettitt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
module.exports = {
graphlib: require("./lib/graphlib"),
dagre: require("./lib/dagre"),
intersect: require("./lib/intersect"),
render: require("./lib/render"),
util: require("./lib/util"),
version: require("./lib/version")
};
},{"./lib/dagre":11,"./lib/graphlib":12,"./lib/intersect":13,"./lib/render":28,"./lib/util":30,"./lib/version":31}],5:[function(require,module,exports){
var util = require("./util");
module.exports = {
"default": normal,
"normal": normal,
"vee": vee,
"undirected": undirected
};
function normal(parent, id, edge, type) {
var marker = parent.append("marker")
.attr("id", id)
.attr("viewBox", "0 0 10 10")
.attr("refX", 9)
.attr("refY", 5)
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", 8)
.attr("markerHeight", 6)
.attr("orient", "auto");
var path = marker.append("path")
.attr("d", "M 0 0 L 10 5 L 0 10 z")
.style("stroke-width", 1)
.style("stroke-dasharray", "1,0");
util.applyStyle(path, edge[type + "Style"]);
}
function vee(parent, id, edge, type) {
var marker = parent.append("marker")
.attr("id", id)
.attr("viewBox", "0 0 10 10")
.attr("refX", 9)
.attr("refY", 5)
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", 8)
.attr("markerHeight", 6)
.attr("orient", "auto");
var path = marker.append("path")
.attr("d", "M 0 0 L 10 5 L 0 10 L 4 5 z")
.style("stroke-width", 1)
.style("stroke-dasharray", "1,0");
util.applyStyle(path, edge[type + "Style"]);
}
function undirected(parent, id, edge, type) {
var marker = parent.append("marker")
.attr("id", id)
.attr("viewBox", "0 0 10 10")
.attr("refX", 9)
.attr("refY", 5)
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", 8)
.attr("markerHeight", 6)
.attr("orient", "auto");
var path = marker.append("path")
.attr("d", "M 0 5 L 10 5")
.style("stroke-width", 1)
.style("stroke-dasharray", "1,0");
util.applyStyle(path, edge[type + "Style"]);
}
},{"./util":30}],6:[function(require,module,exports){
var util = require("./util"),
addLabel = require("./label/add-label");
module.exports = createClusters;
function createClusters(selection, g) {
var clusters = g.nodes().filter(function(v) { return util.isSubgraph(g, v); }),
svgClusters = selection.selectAll("g.cluster")
.data(clusters, function(v) { return v; });
svgClusters.selectAll("*").remove();
svgClusters.enter()
.append("g")
.attr("class", "cluster")
.attr("id",function(v){
var node = g.node(v);
return node.id;
})
.style("opacity", 0);
util.applyTransition(svgClusters, g)
.style("opacity", 1);
svgClusters.each(function(v) {
var node = g.node(v),
thisGroup = d3.select(this);
d3.select(this).append("rect");
var labelGroup = thisGroup.append("g").attr("class", "label");
addLabel(labelGroup, node, node.clusterLabelPos);
});
svgClusters.selectAll("rect").each(function(c) {
var node = g.node(c);
var domCluster = d3.select(this);
util.applyStyle(domCluster, node.style);
});
util.applyTransition(svgClusters.exit(), g)
.style("opacity", 0)
.remove();
return svgClusters;
}
},{"./label/add-label":21,"./util":30}],7:[function(require,module,exports){
"use strict";
var _ = require("./lodash"),
addLabel = require("./label/add-label"),
util = require("./util"),
d3 = require("./d3");
module.exports = createEdgeLabels;
function createEdgeLabels(selection, g) {
var svgEdgeLabels = selection.selectAll("g.edgeLabel")
.data(g.edges(), function(e) { return util.edgeToId(e); })
.classed("update", true);
svgEdgeLabels.selectAll("*").remove();
svgEdgeLabels.enter()
.append("g")
.classed("edgeLabel", true)
.style("opacity", 0);
svgEdgeLabels.each(function(e) {
var edge = g.edge(e),
label = addLabel(d3.select(this), g.edge(e), 0, 0).classed("label", true),
bbox = label.node().getBBox();
if (edge.labelId) { label.attr("id", edge.labelId); }
if (!_.has(edge, "width")) { edge.width = bbox.width; }
if (!_.has(edge, "height")) { edge.height = bbox.height; }
});
util.applyTransition(svgEdgeLabels.exit(), g)
.style("opacity", 0)
.remove();
return svgEdgeLabels;
}
},{"./d3":10,"./label/add-label":21,"./lodash":24,"./util":30}],8:[function(require,module,exports){
"use strict";
var _ = require("./lodash"),
intersectNode = require("./intersect/intersect-node"),
util = require("./util"),
d3 = require("./d3");
module.exports = createEdgePaths;
function createEdgePaths(selection, g, arrows) {
var svgPaths = selection.selectAll("g.edgePath")
.data(g.edges(), function(e) { return util.edgeToId(e); })
.classed("update", true);
enter(svgPaths, g);
exit(svgPaths, g);
util.applyTransition(svgPaths, g)
.style("opacity", 1);
// Save DOM element in the path group, and set ID and class
svgPaths.each(function(e) {
var domEdge = d3.select(this);
var edge = g.edge(e);
edge.elem = this;
if (edge.id) {
domEdge.attr("id", edge.id);
}
util.applyClass(domEdge, edge["class"],
(domEdge.classed("update") ? "update " : "") + "edgePath");
});
svgPaths.selectAll("path.path")
.each(function(e) {
var edge = g.edge(e);
edge.arrowheadId = _.uniqueId("arrowhead");
var domEdge = d3.select(this)
.attr("marker-end", function() {
return "url(#" + edge.arrowheadId + ")";
})
.style("fill", "none");
util.applyTransition(domEdge, g)
.attr("d", function(e) { return calcPoints(g, e); });
util.applyStyle(domEdge, edge.style);
});
svgPaths.selectAll("defs *").remove();
svgPaths.selectAll("defs")
.each(function(e) {
var edge = g.edge(e),
arrowhead = arrows[edge.arrowhead];
arrowhead(d3.select(this), edge.arrowheadId, edge, "arrowhead");
});
return svgPaths;
}
function calcPoints(g, e) {
var edge = g.edge(e),
tail = g.node(e.v),
head = g.node(e.w),
points = edge.points.slice(1, edge.points.length - 1);
points.unshift(intersectNode(tail, points[0]));
points.push(intersectNode(head, points[points.length - 1]));
return createLine(edge, points);
}
function createLine(edge, points) {
var line = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
if (_.has(edge, "lineInterpolate")) {
line.interpolate(edge.lineInterpolate);
}
if (_.has(edge, "lineTension")) {
line.tension(Number(edge.lineTension));
}
return line(points);
}
function getCoords(elem) {
var bbox = elem.getBBox(),
matrix = elem.getTransformToElement(elem.ownerSVGElement)
.translate(bbox.width / 2, bbox.height / 2);
return { x: matrix.e, y: matrix.f };
}
function enter(svgPaths, g) {
var svgPathsEnter = svgPaths.enter()
.append("g")
.attr("class", "edgePath")
.style("opacity", 0);
svgPathsEnter.append("path")
.attr("class", "path")
.attr("d", function(e) {
var edge = g.edge(e),
sourceElem = g.node(e.v).elem,
points = _.range(edge.points.length).map(function() { return getCoords(sourceElem); });
return createLine(edge, points);
});
svgPathsEnter.append("defs");
}
function exit(svgPaths, g) {
var svgPathExit = svgPaths.exit();
util.applyTransition(svgPathExit, g)
.style("opacity", 0)
.remove();
util.applyTransition(svgPathExit.select("path.path"), g)
.attr("d", function(e) {
var source = g.node(e.v);
if (source) {
var points = _.range(this.pathSegList.length).map(function() { return source; });
return createLine({}, points);
} else {
return d3.select(this).attr("d");
}
});
}
},{"./d3":10,"./intersect/intersect-node":17,"./lodash":24,"./util":30}],9:[function(require,module,exports){
"use strict";
var _ = require("./lodash"),
addLabel = require("./label/add-label"),
util = require("./util"),
d3 = require("./d3");
module.exports = createNodes;
function createNodes(selection, g, shapes) {
var simpleNodes = g.nodes().filter(function(v) { return !util.isSubgraph(g, v); });
var svgNodes = selection.selectAll("g.node")
.data(simpleNodes, function(v) { return v; })
.classed("update", true);
svgNodes.selectAll("*").remove();
svgNodes.enter()
.append("g")
.attr("class", "node")
.style("opacity", 0);
svgNodes.each(function(v) {
var node = g.node(v),
thisGroup = d3.select(this),
labelGroup = thisGroup.append("g").attr("class", "label"),
labelDom = addLabel(labelGroup, node),
shape = shapes[node.shape],
bbox = _.pick(labelDom.node().getBBox(), "width", "height");
node.elem = this;
if (node.id) { thisGroup.attr("id", node.id); }
if (node.labelId) { labelGroup.attr("id", node.labelId); }
util.applyClass(thisGroup, node["class"],
(thisGroup.classed("update") ? "update " : "") + "node");
if (_.has(node, "width")) { bbox.width = node.width; }
if (_.has(node, "height")) { bbox.height = node.height; }
bbox.width += node.paddingLeft + node.paddingRight;
bbox.height += node.paddingTop + node.paddingBottom;
labelGroup.attr("transform", "translate(" +
((node.paddingLeft - node.paddingRight) / 2) + "," +
((node.paddingTop - node.paddingBottom) / 2) + ")");
var shapeSvg = shape(d3.select(this), bbox, node);
util.applyStyle(shapeSvg, node.style);
var shapeBBox = shapeSvg.node().getBBox();
node.width = shapeBBox.width;
node.height = shapeBBox.height;
});
util.applyTransition(svgNodes.exit(), g)
.style("opacity", 0)
.remove();
return svgNodes;
}
},{"./d3":10,"./label/add-label":21,"./lodash":24,"./util":30}],10:[function(require,module,exports){
// Stub to get D3 either via NPM or from the global object
module.exports = window.d3;
},{}],11:[function(require,module,exports){
/* global window */
var dagre;
if (require) {
try {
dagre = require("dagre");
} catch (e) {}
}
if (!dagre) {
dagre = window.dagre;
}
module.exports = dagre;
},{"dagre":53}],12:[function(require,module,exports){
/* global window */
var graphlib;
if (require) {
try {
graphlib = require("graphlib");
} catch (e) {}
}
if (!graphlib) {
graphlib = window.graphlib;
}
module.exports = graphlib;
},{"graphlib":32}],13:[function(require,module,exports){
module.exports = {
node: require("./intersect-node"),
circle: require("./intersect-circle"),
ellipse: require("./intersect-ellipse"),
polygon: require("./intersect-polygon"),
rect: require("./intersect-rect")
};
},{"./intersect-circle":14,"./intersect-ellipse":15,"./intersect-node":17,"./intersect-polygon":18,"./intersect-rect":19}],14:[function(require,module,exports){
var intersectEllipse = require("./intersect-ellipse");
module.exports = intersectCircle;
function intersectCircle(node, rx, point) {
return intersectEllipse(node, rx, rx, point);
}
},{"./intersect-ellipse":15}],15:[function(require,module,exports){
module.exports = intersectEllipse;
function intersectEllipse(node, rx, ry, point) {
// Formulae from: http://mathworld.wolfram.com/Ellipse-LineIntersection.html
var cx = node.x;
var cy = node.y;
var px = cx - point.x;
var py = cy - point.y;
var det = Math.sqrt(rx * rx * py * py + ry * ry * px * px);
var dx = Math.abs(rx * ry * px / det);
if (point.x < cx) {
dx = -dx;
}
var dy = Math.abs(rx * ry * py / det);
if (point.y < cy) {
dy = -dy;
}
return {x: cx + dx, y: cy + dy};
}
},{}],16:[function(require,module,exports){
module.exports = intersectLine;
/*
* Returns the point at which two lines, p and q, intersect or returns
* undefined if they do not intersect.
*/
function intersectLine(p1, p2, q1, q2) {
// Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994,
// p7 and p473.
var a1, a2, b1, b2, c1, c2;
var r1, r2 , r3, r4;
var denom, offset, num;
var x, y;
// Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x +
// b1 y + c1 = 0.
a1 = p2.y - p1.y;
b1 = p1.x - p2.x;
c1 = (p2.x * p1.y) - (p1.x * p2.y);
// Compute r3 and r4.
r3 = ((a1 * q1.x) + (b1 * q1.y) + c1);
r4 = ((a1 * q2.x) + (b1 * q2.y) + c1);
// Check signs of r3 and r4. If both point 3 and point 4 lie on
// same side of line 1, the line segments do not intersect.
if ((r3 !== 0) && (r4 !== 0) && sameSign(r3, r4)) {
return /*DONT_INTERSECT*/;
}
// Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0
a2 = q2.y - q1.y;
b2 = q1.x - q2.x;
c2 = (q2.x * q1.y) - (q1.x * q2.y);
// Compute r1 and r2
r1 = (a2 * p1.x) + (b2 * p1.yy) + c2;
r2 = (a2 * p2.x) + (b2 * p2.y) + c2;
// Check signs of r1 and r2. If both point 1 and point 2 lie
// on same side of second line segment, the line segments do
// not intersect.
if ((r1 !== 0) && (r2 !== 0) && (sameSign(r1, r2))) {
return /*DONT_INTERSECT*/;
}
// Line segments intersect: compute intersection point.
denom = (a1 * b2) - (a2 * b1);
if (denom === 0) {
return /*COLLINEAR*/;
}
offset = Math.abs(denom / 2);
// The denom/2 is to get rounding instead of truncating. It
// is added or subtracted to the numerator, depending upon the
// sign of the numerator.
num = (b1 * c2) - (b2 * c1);
x = (num < 0) ? ((num - offset) / denom) : ((num + offset) / denom);
num = (a2 * c1) - (a1 * c2);
y = (num < 0) ? ((num - offset) / denom) : ((num + offset) / denom);
return { x: x, y: y };
}
function sameSign(r1, r2) {
return r1 * r2 > 0;
}
},{}],17:[function(require,module,exports){
module.exports = intersectNode;
function intersectNode(node, point) {
return node.intersect(point);
}
},{}],18:[function(require,module,exports){
var intersectLine = require("./intersect-line");
module.exports = intersectPolygon;
/*
* Returns the point ({x, y}) at which the point argument intersects with the
* node argument assuming that it has the shape specified by polygon.
*/
function intersectPolygon(node, polyPoints, point) {
var x1 = node.x;
var y1 = node.y;
var intersections = [];
var minX = Number.POSITIVE_INFINITY,
minY = Number.POSITIVE_INFINITY;
polyPoints.forEach(function(entry) {
minX = Math.min(minX, entry.x);
minY = Math.min(minY, entry.y);
});
var left = x1 - node.width / 2 - minX;
var top = y1 - node.height / 2 - minY;
for (var i = 0; i < polyPoints.length; i++) {
var p1 = polyPoints[i];
var p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0];
var intersect = intersectLine(node, point,
{x: left + p1.x, y: top + p1.y}, {x: left + p2.x, y: top + p2.y});
if (intersect) {
intersections.push(intersect);
}
}
if (!intersections.length) {
console.log("NO INTERSECTION FOUND, RETURN NODE CENTER", node);
return node;
}
if (intersections.length > 1) {
// More intersections, find the one nearest to edge end point
intersections.sort(function(p, q) {
var pdx = p.x - point.x,
pdy = p.y - point.y,
distp = Math.sqrt(pdx * pdx + pdy * pdy),
qdx = q.x - point.x,
qdy = q.y - point.y,
distq = Math.sqrt(qdx * qdx + qdy * qdy);
return (distp < distq) ? -1 : (distp === distq ? 0 : 1);
});
}
return intersections[0];
}
},{"./intersect-line":16}],19:[function(require,module,exports){
module.exports = intersectRect;
function intersectRect(node, point) {
var x = node.x;
var y = node.y;
// Rectangle intersection algorithm from:
// http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes
var dx = point.x - x;
var dy = point.y - y;
var w = node.width / 2;
var h = node.height / 2;
var sx, sy;
if (Math.abs(dy) * w > Math.abs(dx) * h) {
// Intersection is top or bottom of rect.
if (dy < 0) {
h = -h;
}
sx = dy === 0 ? 0 : h * dx / dy;
sy = h;
} else {
// Intersection is left or right of rect.
if (dx < 0) {
w = -w;
}
sx = w;
sy = dx === 0 ? 0 : w * dy / dx;
}
return {x: x + sx, y: y + sy};
}
},{}],20:[function(require,module,exports){
var util = require("../util");
module.exports = addHtmlLabel;
function addHtmlLabel(root, node) {
var fo = root
.append("foreignObject")
.attr("width", "100000");
var div = fo
.append("xhtml:div");
var label = node.label;
switch(typeof label) {
case "function":
div.insert(label);
break;
case "object":
// Currently we assume this is a DOM object.
div.insert(function() { return label; });
break;
default: div.html(label);
}
util.applyStyle(div, node.labelStyle);
div.style("display", "inline-block");
// Fix for firefox
div.style("white-space", "nowrap");
// TODO find a better way to get dimensions for foreignObjects...
var w, h;
div
.each(function() {
w = this.clientWidth;
h = this.clientHeight;
});
fo
.attr("width", w)
.attr("height", h);
return fo;
}
},{"../util":30}],21:[function(require,module,exports){
var addTextLabel = require("./add-text-label"),
addHtmlLabel = require("./add-html-label"),
addSVGLabel = require("./add-svg-label");
module.exports = addLabel;
function addLabel(root, node, location) {
var label = node.label;
var labelSvg = root.append("g");
// Allow the label to be a string, a function that returns a DOM element, or
// a DOM element itself.
if (node.labelType === "svg") {
addSVGLabel(labelSvg, node);
} else if (typeof label !== "string" || node.labelType === "html") {
addHtmlLabel(labelSvg, node);
} else {
addTextLabel(labelSvg, node);
}
var labelBBox = labelSvg.node().getBBox();
var y;
switch(location) {
case "top":
y = (-node.height / 2);
break;
case "bottom":
y = (node.height / 2) - labelBBox.height;
break;
default:
y = (-labelBBox.height / 2);
}
labelSvg.attr("transform",
"translate(" + (-labelBBox.width / 2) + "," + y + ")");
return labelSvg;
}
},{"./add-html-label":20,"./add-svg-label":22,"./add-text-label":23}],22:[function(require,module,exports){
var util = require("../util");
module.exports = addSVGLabel;
function addSVGLabel(root, node) {
var domNode = root;
domNode.node().appendChild(node.label);
util.applyStyle(domNode, node.labelStyle);
return domNode;
}
},{"../util":30}],23:[function(require,module,exports){
var util = require("../util");
module.exports = addTextLabel;
/*
* Attaches a text label to the specified root. Handles escape sequences.
*/
function addTextLabel(root, node) {
var domNode = root.append("text");
var lines = processEscapeSequences(node.label).split("\n");
for (var i = 0; i < lines.length; i++) {
domNode
.append("tspan")
.attr("xml:space", "preserve")
.attr("dy", "1em")
.attr("x", "1")
.text(lines[i]);
}
util.applyStyle(domNode, node.labelStyle);
return domNode;
}
function processEscapeSequences(text) {
var newText = "",
escaped = false,
ch;
for (var i = 0; i < text.length; ++i) {
ch = text[i];
if (escaped) {
switch(ch) {
case "n": newText += "\n"; break;
default: newText += ch;
}
escaped = false;
} else if (ch === "\\") {
escaped = true;
} else {
newText += ch;
}
}
return newText;
}
},{"../util":30}],24:[function(require,module,exports){
/* global window */
var lodash;
if (require) {
try {
lodash = require("lodash");
} catch (e) {}
}
if (!lodash) {
lodash = window._;
}
module.exports = lodash;
},{"lodash":52}],25:[function(require,module,exports){
"use strict";
var util = require("./util"),
d3 = require("./d3");
module.exports = positionClusters;
function positionClusters(selection, g) {
var created = selection.filter(function() { return !d3.select(this).classed("update"); });
function translate(v) {
var node = g.node(v);
return "translate(" + node.x + "," + node.y + ")";
}
created.attr("transform", translate);
util.applyTransition(selection, g)
.style("opacity", 1)
.attr("transform", translate);
util.applyTransition(created.selectAll("rect"), g)
.attr("width", function(v) { return g.node(v).width; })
.attr("height", function(v) { return g.node(v).height; })
.attr("x", function(v) {
var node = g.node(v);
return -node.width / 2;
})
.attr("y", function(v) {
var node = g.node(v);
return -node.height / 2;
});
}
},{"./d3":10,"./util":30}],26:[function(require,module,exports){
"use strict";
var util = require("./util"),
d3 = require("./d3"),
_ = require("./lodash");
module.exports = positionEdgeLabels;
function positionEdgeLabels(selection, g) {
var created = selection.filter(function() { return !d3.select(this).classed("update"); });
function translate(e) {
var edge = g.edge(e);
return _.has(edge, "x") ? "translate(" + edge.x + "," + edge.y + ")" : "";
}
created.attr("transform", translate);
util.applyTransition(selection, g)
.style("opacity", 1)
.attr("transform", translate);
}
},{"./d3":10,"./lodash":24,"./util":30}],27:[function(require,module,exports){
"use strict";
var util = require("./util"),
d3 = require("./d3");
module.exports = positionNodes;
function positionNodes(selection, g) {
var created = selection.filter(function() { return !d3.select(this).classed("update"); });
function translate(v) {
var node = g.node(v);
return "translate(" + node.x + "," + node.y + ")";
}
created.attr("transform", translate);
util.applyTransition(selection, g)
.style("opacity", 1)
.attr("transform", translate);
}
},{"./d3":10,"./util":30}],28:[function(require,module,exports){
var _ = require("./lodash"),
layout = require("./dagre").layout;
module.exports = render;
// This design is based on http://bost.ocks.org/mike/chart/.
function render() {
var createNodes = require("./create-nodes"),
createClusters = require("./create-clusters"),
createEdgeLabels = require("./create-edge-labels"),
createEdgePaths = require("./create-edge-paths"),
positionNodes = require("./position-nodes"),
positionEdgeLabels = require("./position-edge-labels"),
positionClusters = require("./position-clusters"),
shapes = require("./shapes"),
arrows = require("./arrows");
var fn = function(svg, g) {
preProcessGraph(g);
var outputGroup = createOrSelectGroup(svg, "output"),
clustersGroup = createOrSelectGroup(outputGroup, "clusters"),
edgePathsGroup = createOrSelectGroup(outputGroup, "edgePaths"),
edgeLabels = createEdgeLabels(createOrSelectGroup(outputGroup, "edgeLabels"), g),
nodes = createNodes(createOrSelectGroup(outputGroup, "nodes"), g, shapes);
layout(g);
positionNodes(nodes, g);
positionEdgeLabels(edgeLabels, g);
createEdgePaths(edgePathsGroup, g, arrows);
var clusters = createClusters(clustersGroup, g);
positionClusters(clusters, g);
postProcessGraph(g);
};
fn.createNodes = function(value) {
if (!arguments.length) return createNodes;
createNodes = value;
return fn;
};
fn.createClusters = function(value) {
if (!arguments.length) return createClusters;
createClusters = value;
return fn;
};
fn.createEdgeLabels = function(value) {
if (!arguments.length) return createEdgeLabels;
createEdgeLabels = value;
return fn;
};
fn.createEdgePaths = function(value) {
if (!arguments.length) return createEdgePaths;
createEdgePaths = value;
return fn;
};
fn.shapes = function(value) {
if (!arguments.length) return shapes;
shapes = value;
return fn;
};
fn.arrows = function(value) {
if (!arguments.length) return arrows;
arrows = value;
return fn;
};
return fn;
}
var NODE_DEFAULT_ATTRS = {
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10,
rx: 0,
ry: 0,
shape: "rect"
};
var EDGE_DEFAULT_ATTRS = {
arrowhead: "normal",
lineInterpolate: "linear"
};
function preProcessGraph(g) {
g.nodes().forEach(function(v) {
var node = g.node(v);
if (!_.has(node, "label") && !g.children(v).length) { node.label = v; }
if (_.has(node, "paddingX")) {
_.defaults(node, {
paddingLeft: node.paddingX,
paddingRight: node.paddingX
});
}
if (_.has(node, "paddingY")) {
_.defaults(node, {
paddingTop: node.paddingY,
paddingBottom: node.paddingY
});
}
if (_.has(node, "padding")) {
_.defaults(node, {
paddingLeft: node.padding,
paddingRight: node.padding,
paddingTop: node.padding,
paddingBottom: node.padding
});
}
_.defaults(node, NODE_DEFAULT_ATTRS);
_.each(["paddingLeft", "paddingRight", "paddingTop", "paddingBottom"], function(k) {
node[k] = Number(node[k]);
});
// Save dimensions for restore during post-processing
if (_.has(node, "width")) { node._prevWidth = node.width; }
if (_.has(node, "height")) { node._prevHeight = node.height; }
});
g.edges().forEach(function(e) {
var edge = g.edge(e);
if (!_.has(edge, "label")) { edge.label = ""; }
_.defaults(edge, EDGE_DEFAULT_ATTRS);
});
}
function postProcessGraph(g) {
_.each(g.nodes(), function(v) {
var node = g.node(v);
// Restore original dimensions
if (_.has(node, "_prevWidth")) {
node.width = node._prevWidth;
} else {
delete node.width;
}
if (_.has(node, "_prevHeight")) {
node.height = node._prevHeight;
} else {
delete node.height;
}
delete node._prevWidth;
delete node._prevHeight;
});
}
function createOrSelectGroup(root, name) {
var selection = root.select("g." + name);
if (selection.empty()) {
selection = root.append("g").attr("class", name);
}
return selection;
}
},{"./arrows":5,"./create-clusters":6,"./create-edge-labels":7,"./create-edge-paths":8,"./create-nodes":9,"./dagre":11,"./lodash":24,"./position-clusters":25,"./position-edge-labels":26,"./position-nodes":27,"./shapes":29}],29:[function(require,module,exports){
"use strict";
var intersectRect = require("./intersect/intersect-rect"),
intersectEllipse = require("./intersect/intersect-ellipse"),
intersectCircle = require("./intersect/intersect-circle"),
intersectPolygon = require("./intersect/intersect-polygon");
module.exports = {
rect: rect,
ellipse: ellipse,
circle: circle,
diamond: diamond
};
function rect(parent, bbox, node) {
var shapeSvg = parent.insert("rect", ":first-child")
.attr("rx", node.rx)
.attr("ry", node.ry)
.attr("x", -bbox.width / 2)
.attr("y", -bbox.height / 2)
.attr("width", bbox.width)
.attr("height", bbox.height);
node.intersect = function(point) {
return intersectRect(node, point);
};
return shapeSvg;
}
function ellipse(parent, bbox, node) {
var rx = bbox.width / 2,
ry = bbox.height / 2,
shapeSvg = parent.insert("ellipse", ":first-child")
.attr("x", -bbox.width / 2)
.attr("y", -bbox.height / 2)
.attr("rx", rx)
.attr("ry", ry);
node.intersect = function(point) {
return intersectEllipse(node, rx, ry, point);
};
return shapeSvg;
}
function circle(parent, bbox, node) {
var r = Math.max(bbox.width, bbox.height) / 2,
shapeSvg = parent.insert("circle", ":first-child")
.attr("x", -bbox.width / 2)
.attr("y", -bbox.height / 2)
.attr("r", r);
node.intersect = function(point) {
return intersectCircle(node, r, point);
};
return shapeSvg;
}
// Circumscribe an ellipse for the bounding box with a diamond shape. I derived
// the function to calculate the diamond shape from:
// http://mathforum.org/kb/message.jspa?messageID=3750236
function diamond(parent, bbox, node) {
var w = (bbox.width * Math.SQRT2) / 2,
h = (bbox.height * Math.SQRT2) / 2,
points = [
{ x: 0, y: -h },
{ x: -w, y: 0 },
{ x: 0, y: h },
{ x: w, y: 0 }
],
shapeSvg = parent.insert("polygon", ":first-child")
.attr("points", points.map(function(p) { return p.x + "," + p.y; }).join(" "));
node.intersect = function(p) {
return intersectPolygon(node, points, p);
};
return shapeSvg;
}
},{"./intersect/intersect-circle":14,"./intersect/intersect-ellipse":15,"./intersect/intersect-polygon":18,"./intersect/intersect-rect":19}],30:[function(require,module,exports){
var _ = require("./lodash");
// Public utility functions
module.exports = {
isSubgraph: isSubgraph,
edgeToId: edgeToId,
applyStyle: applyStyle,
applyClass: applyClass,
applyTransition: applyTransition
};
/*
* Returns true if the specified node in the graph is a subgraph node. A
* subgraph node is one that contains other nodes.
*/
function isSubgraph(g, v) {
return !!g.children(v).length;
}
function edgeToId(e) {
return escapeId(e.v) + ":" + escapeId(e.w) + ":" + escapeId(e.name);
}
var ID_DELIM = /:/g;
function escapeId(str) {
return str ? String(str).replace(ID_DELIM, "\\:") : "";
}
function applyStyle(dom, styleFn) {
if (styleFn) {
dom.attr("style", styleFn);
}
}
function applyClass(dom, classFn, otherClasses) {
if (classFn) {
dom
.attr("class", classFn)
.attr("class", otherClasses + " " + dom.attr("class"));
}
}
function applyTransition(selection, g) {
var graph = g.graph();
if (_.isPlainObject(graph)) {
var transition = graph.transition;
if (_.isFunction(transition)) {
return transition(selection);
}
}
return selection;
}
},{"./lodash":24}],31:[function(require,module,exports){
module.exports = "0.4.10";
},{}],32:[function(require,module,exports){
/**
* Copyright (c) 2014, Chris Pettitt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
var lib = require("./lib");
module.exports = {
Graph: lib.Graph,
json: require("./lib/json"),
alg: require("./lib/alg"),
version: lib.version
};
},{"./lib":48,"./lib/alg":39,"./lib/json":49}],33:[function(require,module,exports){
var _ = require("../lodash");
module.exports = components;
function components(g) {
var visited = {},
cmpts = [],
cmpt;
function dfs(v) {
if (_.has(visited, v)) return;
visited[v] = true;
cmpt.push(v);
_.each(g.successors(v), dfs);
_.each(g.predecessors(v), dfs);
}
_.each(g.nodes(), function(v) {
cmpt = [];
dfs(v);
if (cmpt.length) {
cmpts.push(cmpt);
}
});
return cmpts;
}
},{"../lodash":50}],34:[function(require,module,exports){
var _ = require("../lodash");
module.exports = dfs;
/*
* A helper that preforms a pre- or post-order traversal on the input graph
* and returns the nodes in the order they were visited. This algorithm treats
* the input as undirected.
*
* Order must be one of "pre" or "post".
*/
function dfs(g, vs, order) {
if (!_.isArray(vs)) {
vs = [vs];
}
var acc = [],
visited = {};
_.each(vs, function(v) {
if (!g.hasNode(v)) {
throw new Error("Graph does not have node: " + v);
}
doDfs(g, v, order === "post", visited, acc);
});
return acc;
}
function doDfs(g, v, postorder, visited, acc) {
if (!_.has(visited, v)) {
visited[v] = true;
if (!postorder) { acc.push(v); }
_.each(g.neighbors(v), function(w) {
doDfs(g, w, postorder, visited, acc);
});
if (postorder) { acc.push(v); }
}
}
},{"../lodash":50}],35:[function(require,module,exports){
var dijkstra = require("./dijkstra"),
_ = require("../lodash");
module.exports = dijkstraAll;
function dijkstraAll(g, weightFunc, edgeFunc) {
return _.transform(g.nodes(), function(acc, v) {
acc[v] = dijkstra(g, v, weightFunc, edgeFunc);
}, {});
}
},{"../lodash":50,"./dijkstra":36}],36:[function(require,module,exports){
var _ = require("../lodash"),
PriorityQueue = require("../data/priority-queue");
module.exports = dijkstra;
var DEFAULT_WEIGHT_FUNC = _.constant(1);
function dijkstra(g, source, weightFn, edgeFn) {
return runDijkstra(g, String(source),
weightFn || DEFAULT_WEIGHT_FUNC,
edgeFn || function(v) { return g.outEdges(v); });
}
function runDijkstra(g, source, weightFn, edgeFn) {
var results = {},
pq = new PriorityQueue(),
v, vEntry;
var updateNeighbors = function(edge) {
var w = edge.v !== v ? edge.v : edge.w,
wEntry = results[w],
weight = weightFn(edge),
distance = vEntry.distance + weight;
if (weight < 0) {
throw new Error("dijkstra does not allow negative edge weights. " +
"Bad edge: " + edge + " Weight: " + weight);
}
if (distance < wEntry.distance) {
wEntry.distance = distance;
wEntry.predecessor = v;
pq.decrease(w, distance);
}
};
g.nodes().forEach(function(v) {
var distance = v === source ? 0 : Number.POSITIVE_INFINITY;
results[v] = { distance: distance };
pq.add(v, distance);
});
while (pq.size() > 0) {
v = pq.removeMin();
vEntry = results[v];
if (vEntry.distance === Number.POSITIVE_INFINITY) {
break;
}
edgeFn(v).forEach(updateNeighbors);
}
return results;
}
},{"../data/priority-queue":46,"../lodash":50}],37:[function(require,module,exports){
var _ = require("../lodash"),
tarjan = require("./tarjan");
module.exports = findCycles;
function findCycles(g) {
return _.filter(tarjan(g), function(cmpt) {
return cmpt.length > 1 || (cmpt.length === 1 && g.hasEdge(cmpt[0], cmpt[0]));
});
}
},{"../lodash":50,"./tarjan":44}],38:[function(require,module,exports){
var _ = require("../lodash");
module.exports = floydWarshall;
var DEFAULT_WEIGHT_FUNC = _.constant(1);
function floydWarshall(g, weightFn, edgeFn) {
return runFloydWarshall(g,
weightFn || DEFAULT_WEIGHT_FUNC,
edgeFn || function(v) { return g.outEdges(v); });
}
function runFloydWarshall(g, weightFn, edgeFn) {
var results = {},
nodes = g.nodes();
nodes.forEach(function(v) {
results[v] = {};
results[v][v] = { distance: 0 };
nodes.forEach(function(w) {
if (v !== w) {
results[v][w] = { distance: Number.POSITIVE_INFINITY };
}
});
edgeFn(v).forEach(function(edge) {
var w = edge.v === v ? edge.w : edge.v,
d = weightFn(edge);
results[v][w] = { distance: d, predecessor: v };
});
});
nodes.forEach(function(k) {
var rowK = results[k];
nodes.forEach(function(i) {
var rowI = results[i];
nodes.forEach(function(j) {
var ik = rowI[k];
var kj = rowK[j];
var ij = rowI[j];
var altDistance = ik.distance + kj.distance;
if (altDistance < ij.distance) {
ij.distance = altDistance;
ij.predecessor = kj.predecessor;
}
});
});
});
return results;
}
},{"../lodash":50}],39:[function(require,module,exports){
module.exports = {
components: require("./components"),
dijkstra: require("./dijkstra"),
dijkstraAll: require("./dijkstra-all"),
findCycles: require("./find-cycles"),
floydWarshall: require("./floyd-warshall"),
isAcyclic: require("./is-acyclic"),
postorder: require("./postorder"),
preorder: require("./preorder"),
prim: require("./prim"),
tarjan: require("./tarjan"),
topsort: require("./topsort")
};
},{"./components":33,"./dijkstra":36,"./dijkstra-all":35,"./find-cycles":37,"./floyd-warshall":38,"./is-acyclic":40,"./postorder":41,"./preorder":42,"./prim":43,"./tarjan":44,"./topsort":45}],40:[function(require,module,exports){
var topsort = require("./topsort");
module.exports = isAcyclic;
function isAcyclic(g) {
try {
topsort(g);
} catch (e) {
if (e instanceof topsort.CycleException) {
return false;
}
throw e;
}
return true;
}
},{"./topsort":45}],41:[function(require,module,exports){
var dfs = require("./dfs");
module.exports = postorder;
function postorder(g, vs) {
return dfs(g, vs, "post");
}
},{"./dfs":34}],42:[function(require,module,exports){
var dfs = require("./dfs");
module.exports = preorder;
function preorder(g, vs) {
return dfs(g, vs, "pre");
}
},{"./dfs":34}],43:[function(require,module,exports){
var _ = require("../lodash"),
Graph = require("../graph"),
PriorityQueue = require("../data/priority-queue");
module.exports = prim;
function prim(g, weightFunc) {
var result = new Graph(),
parents = {},
pq = new PriorityQueue(),
v;
function updateNeighbors(edge) {
var w = edge.v === v ? edge.w : edge.v,
pri = pq.priority(w);
if (pri !== undefined) {
var edgeWeight = weightFunc(edge);
if (edgeWeight < pri) {
parents[w] = v;
pq.decrease(w, edgeWeight);
}
}
}
if (g.nodeCount() === 0) {
return result;
}
_.each(g.nodes(), function(v) {
pq.add(v, Number.POSITIVE_INFINITY);
result.setNode(v);
});
// Start from an arbitrary node
pq.decrease(g.nodes()[0], 0);
var init = false;
while (pq.size() > 0) {
v = pq.removeMin();
if (_.has(parents, v)) {
result.setEdge(v, parents[v]);
} else if (init) {
throw new Error("Input graph is not connected: " + g);
} else {
init = true;
}
g.nodeEdges(v).forEach(updateNeighbors);
}
return result;
}
},{"../data/priority-queue":46,"../graph":47,"../lodash":50}],44:[function(require,module,exports){
var _ = require("../lodash");
module.exports = tarjan;
function tarjan(g) {
var index = 0,
stack = [],