UNPKG

dc.graph

Version:

Graph visualizations integrated with crossfilter and dc.js

106 lines (96 loc) 4.44 kB
dc_graph.validate = function(title) { function falsy(objects, accessor, what, who) { var f = objects.filter(function(o) { return !accessor(o); }); return f.length ? [what + ' is empty for ' + f.length + ' of ' + objects.length + ' ' + who, f] : null; } function build_index(objects, accessor) { return objects.reduce(function(m, o) { m[accessor(o)] = o; return m; }, {}); } function not_found(index, objects, accessor, what, where, who) { var nf = objects.filter(function(o) { return !index[accessor(o)]; }).map(function(o) { return {key: accessor(o), value: o}; }); return nf.length ? [what + ' was not found in ' + where, Object.keys(index), 'for ' + nf.length + ' of ' + objects.length + ' ' + who, nf] : null; } function validate() { var diagram = _mode.parent(); var nodes = diagram.nodeGroup().all(), edges = diagram.edgeGroup().all(), ports = diagram.portGroup() ? diagram.portGroup().all() : []; var errors = []; function check(error) { if(error) errors.push(error); } check(falsy(nodes, diagram.nodeKey(), 'nodeKey', 'nodes')); check(falsy(edges, diagram.edgeSource(), 'edgeSource', 'edges')); check(falsy(edges, diagram.edgeTarget(), 'edgeTarget', 'edges')); var contentTypes = d3.set(diagram.content.enum()); var ct = dc_graph.functor_wrap(diagram.nodeContent()); var noContentNodes = nodes.filter(function(kv) { return !contentTypes.has(ct(kv)); }); if(noContentNodes.length) errors.push(['there are ' + noContentNodes.length + ' nodes with nodeContent not matching any content', noContentNodes]); var nindex = build_index(nodes, diagram.nodeKey()), eindex = build_index(edges, diagram.edgeKey()); check(not_found(nindex, edges, diagram.edgeSource(), 'edgeSource', 'nodes', 'edges')); check(not_found(nindex, edges, diagram.edgeTarget(), 'edgeTarget', 'nodes', 'edges')); check(falsy(ports, function(p) { return diagram.portNodeKey() && diagram.portNodeKey()(p) || diagram.portEdgeKey() && diagram.portEdgeKey()(p); }, 'portNodeKey||portEdgeKey', 'ports')); var named_ports = !diagram.portNodeKey() && [] || ports.filter(function(p) { return diagram.portNodeKey()(p); }); var anonymous_ports = !diagram.portEdgeKey() && [] || ports.filter(function(p) { return diagram.portEdgeKey()(p); }); check(not_found(nindex, named_ports, diagram.portNodeKey(), 'portNodeKey', 'nodes', 'ports')); check(not_found(eindex, anonymous_ports, diagram.portEdgeKey(), 'portEdgeKey', 'edges', 'ports')); if(diagram.portName()) { var pindex = build_index(named_ports, function(p) { return diagram.portNodeKey()(p) + ' - ' + diagram.portName()(p); }); if(diagram.edgeSourcePortName()) check(not_found(pindex, edges, function(e) { return diagram.edgeSource()(e) + ' - ' + d3.functor(diagram.edgeSourcePortName())(e); }, 'edgeSourcePortName', 'ports', 'edges')); if(diagram.edgeTargetPortName()) check(not_found(pindex, edges, function(e) { return diagram.edgeTarget()(e) + ' - ' + d3.functor(diagram.edgeTargetPortName())(e); }, 'edgeTargetPortName', 'ports', 'edges')); } function count_text() { return nodes.length + ' nodes, ' + edges.length + ' edges, ' + ports.length + ' ports'; } if(errors.length) { console.warn('validation of ' + title + ' failed with ' + count_text() + ':'); errors.forEach(function(err) { console.warn.apply(console, err); }); } else console.log('validation of ' + title + ' succeeded with ' + count_text() + '.'); } var _mode = { parent: property(null).react(function(p) { if(p) p.on('data.validate', validate); else _mode.parent().on('data.validate', null); }) }; return _mode; };