Skip to content

Commit

Permalink
Add Visualized tab to playground
Browse files Browse the repository at this point in the history
Uses https://github.com/scienceai/jsonld-vis (Apache License 2.0)
...which uses https://github.com/d3/d3 (BSD-3)
...and https://github.com/Caged/d3-tip (MIT)

Fixes json-ld#383
  • Loading branch information
BigBlueHat committed Jan 26, 2017
1 parent 1ebf5f3 commit 3a394ab
Show file tree
Hide file tree
Showing 4 changed files with 345 additions and 0 deletions.
13 changes: 13 additions & 0 deletions playground/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/g/[email protected](css/bootstrap-responsive.min.css),[email protected](codemirror.css+addon/lint/lint.css+addon/hint/show-hint.css+theme/neat.css)">
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/fontawesome/3.2.1/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="./playground.css">
<link rel="stylesheet" type="text/css" href="./jsonld-vis.css">
<link rel="stylesheet" type="text/css" href="../static/css/rdf-font/rdf-font.css">


Expand Down Expand Up @@ -321,6 +322,12 @@ <h3>Bitcoin Private Key (secp256k1)</h3>
<span>Normalized</span>
</a>
</li>
<li>
<a id="tab-visualized" href="#pane-visualized" data-toggle="tab" name="tab-visualized">
<i class="icon-archive"></i>
<span>Visualized</span>
</a>
</li>
<li>
<a id="tab-signed-rsa" href="#pane-signed-rsa" data-toggle="tab" name="tab-signed-rsa">
<i class="icon-pencil"></i>
Expand Down Expand Up @@ -354,6 +361,9 @@ <h3>Bitcoin Private Key (secp256k1)</h3>
<div id="pane-normalized" class="tab-pane">
<textarea id="normalized" class="codemirror-output"></textarea>
</div>
<div id="pane-visualized" class="tab-pane">
<div id="visualized" style="background: white"></div>
</div>
<div id="pane-signed-rsa" class="tab-pane">
<textarea id="signed-rsa" class="codemirror-output"></textarea>
</div>
Expand All @@ -379,6 +389,9 @@ <h3>Bitcoin Private Key (secp256k1)</h3>
<!-- sccdn scripts -->
<script src="//cdn.jsdelivr.net/g/[email protected],[email protected],[email protected],[email protected],[email protected](codemirror.min.js+addon/lint/lint.js+addon/edit/matchbrackets.js+addon/edit/closebrackets.js+addon/display/placeholder.js+addon/hint/show-hint.js+mode/ntriples/ntriples.js+mode/javascript/javascript.js)"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jsonld/0.4.2/jsonld.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3-tip/0.6.7/d3-tip.min.js"></script>
<script src="./jsonld-vis.js"></script>

<!-- local scripts -->
<!-- <script src="./jsonld.js"></script> -->
Expand Down
34 changes: 34 additions & 0 deletions playground/jsonld-vis.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@import url("http://fonts.googleapis.com/css?family=Open+Sans:300,400,600");

svg {
border: none;
}

.node {
cursor: pointer;
}

.node text {
font-size: 12px;
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, sans-serif;
fill: #333333;
}

.d3-tip {
font-size: 14px;
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, sans-serif;
color: #333333;
border: 1px solid #CCCCCC;
border-radius: 5px;
padding: 10px 20px;
max-width: 250px;
word-wrap: break-word;
background-color: rgba(255, 255, 255, 0.9);
text-align: left;
}

.link {
fill: none;
stroke: #DADFE1;
stroke-width: 1px;
}
291 changes: 291 additions & 0 deletions playground/jsonld-vis.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
(function() {
'use strict';

function jsonldVis(jsonld, selector, config) {
if (!arguments.length) return jsonldVis;
config = config || {};

var h = config.h || 600
, w = config.w || 800
, maxLabelWidth = config.maxLabelWidth || 250
, transitionDuration = config.transitionDuration || 750
, transitionEase = config.transitionEase || 'cubic-in-out'
, minRadius = config.minRadius || 5
, scalingFactor = config.scalingFactor || 2;

var i = 0;

var tree = d3.layout.tree()
.size([h, w]);

var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });

var svg = d3.select(selector).append('svg')
.attr('width', w)
.attr('height', h)
.append('g')
.attr('transform', 'translate(' + maxLabelWidth + ',0)');

var tip = d3.tip()
.direction(function(d) {
return d.children || d._children ? 'w' : 'e';
})
.offset(function(d) {
return d.children || d._children ? [0, -3] : [0, 3];
})
.attr('class', 'd3-tip')
.html(function(d) {
return '<span>' + d.valueExtended + '</span>';
});

svg.call(tip);

var root = jsonldTree(jsonld);
root.x0 = h / 2;
root.y0 = 0;
root.children.forEach(collapse);

function changeSVGWidth(newWidth) {
if (w !== newWidth) {
d3.select(selector + ' > svg').attr('width', newWidth);
}
}

function jsonldTree(source) {
var tree = {};

if ('@id' in source) {
tree.isIdNode = true;
tree.name = source['@id'];
if (tree.name.length > maxLabelWidth / 9) {
tree.valueExtended = tree.name;
tree.name = '...' + tree.valueExtended.slice(-Math.floor(maxLabelWidth / 9));
}
} else {
tree.isIdNode = true;
tree.isBlankNode = true;
// random id, can replace with actual uuid generator if needed
tree.name = '_' + Math.random().toString(10).slice(-7);
}

var children = [];
Object.keys(source).forEach(function(key) {
if (key === '@id' || key === '@context' || source[key] === null) return;

var valueExtended, value;
if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
children.push({
name: key,
children: [jsonldTree(source[key])]
});
} else if (Array.isArray(source[key])) {
children.push({
name: key,
children: source[key].map(function(item) {
if (typeof item === 'object') {
return jsonldTree(item);
} else {
return { name: item };
}
})
});
} else {
valueExtended = source[key];
value = valueExtended;
if (value.length > maxLabelWidth / 9) {
value = value.slice(0, Math.floor(maxLabelWidth / 9)) + '...';
children.push({
name: key,
value: value,
valueExtended: valueExtended
});
} else {
children.push({
name: key,
value: value
});
}
}
});

if (children.length) {
tree.children = children;
}

return tree;
}

function update(source) {
var nodes = tree.nodes(root).reverse();
var links = tree.links(nodes);

nodes.forEach(function(d) { d.y = d.depth * maxLabelWidth; });

var node = svg.selectAll('g.node')
.data(nodes, function(d) { return d.id || (d.id = ++i); });

var nodeEnter = node.enter()
.append('g')
.attr('class', 'node')
.attr('transform', function(d) { return 'translate(' + source.y0 + ',' + source.x0 + ')'; })
.on('click', click);

nodeEnter.append('circle')
.attr('r', 0)
.style('stroke-width', function(d) {
return d.isIdNode ? '2px' : '1px';
})
.style('stroke', function(d) {
return d.isIdNode ? '#F7CA18' : '#4ECDC4';
})
.style('fill', function(d) {
if (d.isIdNode) {
return d._children ? '#F5D76E' : 'white';
} else {
return d._children ? '#86E2D5' : 'white';
}
})
.on('mouseover', function(d) { if (d.valueExtended) tip.show(d); })
.on('mouseout', tip.hide);

nodeEnter.append('text')
.attr('x', function(d) {
var spacing = computeRadius(d) + 5;
return d.children || d._children ? -spacing : spacing;
})
.attr('dy', '4')
.attr('text-anchor', function(d) { return d.children || d._children ? 'end' : 'start'; })
.text(function(d) { return d.name + (d.value ? ': ' + d.value : ''); })
.style('fill-opacity', 0);

var maxSpan = Math.max.apply(Math, nodes.map(function(d) { return d.y + maxLabelWidth; }));
if (maxSpan + maxLabelWidth + 20 > w) {
changeSVGWidth(maxSpan + maxLabelWidth);
d3.select(selector).node().scrollLeft = source.y0;
}

var nodeUpdate = node.transition()
.duration(transitionDuration)
.ease(transitionEase)
.attr('transform', function(d) { return 'translate(' + d.y + ',' + d.x + ')'; });

nodeUpdate.select('circle')
.attr('r', function(d) { return computeRadius(d); })
.style('stroke-width', function(d) {
return d.isIdNode ? '2px' : '1px';
})
.style('stroke', function(d) {
return d.isIdNode ? '#F7CA18' : '#4ECDC4';
})
.style('fill', function(d) {
if (d.isIdNode) {
return d._children ? '#F5D76E' : 'white';
} else {
return d._children ? '#86E2D5' : 'white';
}
});

nodeUpdate.select('text').style('fill-opacity', 1);

var nodeExit = node.exit().transition()
.duration(transitionDuration)
.ease(transitionEase)
.attr('transform', function(d) { return 'translate(' + source.y + ',' + source.x + ')'; })
.remove();

nodeExit.select('circle').attr('r', 0);
nodeExit.select('text').style('fill-opacity', 0);

var link = svg.selectAll('path.link')
.data(links, function(d) { return d.target.id; });

link.enter().insert('path', 'g')
.attr('class', 'link')
.attr('d', function(d) {
var o = { x: source.x0, y: source.y0 };
return diagonal({ source: o, target: o });
});

link.transition()
.duration(transitionDuration)
.ease(transitionEase)
.attr('d', diagonal);

link.exit().transition()
.duration(transitionDuration)
.ease(transitionEase)
.attr('d', function(d) {
var o = { x: source.x, y: source.y };
return diagonal({ source: o, target: o });
})
.remove();

nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}

function computeRadius(d) {
if (d.children || d._children) {
return minRadius + (numEndNodes(d) / scalingFactor);
} else {
return minRadius;
}
}

function numEndNodes(n) {
var num = 0;
if (n.children) {
n.children.forEach(function(c) {
num += numEndNodes(c);
});
} else if (n._children) {
n._children.forEach(function(c) {
num += numEndNodes(c);
});
} else {
num++;
}
return num;
}

function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}

update(d);

// fast-forward blank nodes
if (d.children) {
d.children.forEach(function(child) {
if (child.isBlankNode && child._children) {
click(child);
}
});
}
}

function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}

update(root);
}

if (typeof module !== 'undefined' && module.exports) {
module.exports = jsonldVis;
} else {
d3.jsonldVis = jsonldVis;
}
})();
7 changes: 7 additions & 0 deletions playground/playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,13 @@
options.format = 'application/nquads';
promise = processor.normalize(input, options);
}
else if(playground.activeTab === 'tab-visualized') {
// early return because this isn't an editor
return new Promise(function() {
$('#visualized').empty();
d3.jsonldVis(input, '#visualized');
});
}
else if(playground.activeTab === 'tab-signed-rsa') {
options.format = 'application/ld+json';

Expand Down

0 comments on commit 3a394ab

Please sign in to comment.