MediaWiki:Network-graph.js: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
Line 119: | Line 119: | ||
// Return the depth to use for the graph (?) | // Return the depth to use for the graph (?) | ||
return | var depth = 9; | ||
if (depth < 1) { depth = 1; } | |||
else if (depth > 9) { depth = 9; } | |||
return depth; | |||
} | } | ||
Line 148: | Line 153: | ||
}; | }; | ||
function | function loadData(depth, title) { | ||
$('#network-graph').html('<div id="network-graph-caption">Retrieving data for the network graph...</div>'); | $('#network-graph').html('<div id="network-graph-caption">Retrieving data for the network graph...</div>'); | ||
$.ajax({ | |||
method: 'get', | |||
url: getBaseUrl() + '/site-links/network-graph.php', | |||
data: { depth: getDepth(), title: getTitle() }, | |||
dataType: 'json', | |||
success: function(data, textStatus, jqXHR) { | |||
Json = data; | |||
$('#network-graph-caption').html('Hover over a node to see the title of the article. Double-click on a node to view the article.'); | |||
}, | |||
error: function(jqXHR, textStatus, errorThrown) { | |||
$('#network-graph-caption').html('No data is available for this graph.'); | |||
console.log('loadData: jqXHR =', jqXHR, '; textStatus =', textStatus, '; errorThrown =', errorThrown); | |||
}, | |||
}); | |||
} | |||
function renderGraph() { | |||
init(); | init(); | ||
// Create the canvas | // Create the canvas | ||
Line 197: | Line 213: | ||
.gravity(Ctrl.physical.gravity); | .gravity(Ctrl.physical.gravity); | ||
// | // Introduce the data to the force simulation | ||
Ctrl.force | |||
.nodes(json.nodes) | |||
.links(json.links); | |||
// Add the links (lines and arrows) | |||
Ctrl.svg | |||
.selectAll('.link') | |||
.data(json.links) | |||
.enter() | |||
.append('line') | |||
.attr('class', 'link') | |||
.style('stroke', Ctrl.line.color) | |||
.style('stroke-width', Ctrl.line.strokeWidth) | |||
.style('marker-end', function(node, index) { return 'url(#arrow0)'; }); //return 'url(#arrow' + node.level + ')'; }); | |||
// Add the nodes (circles) | |||
Ctrl.svg | |||
.selectAll('.node') | |||
.data(json.nodes) | |||
.enter() | |||
Ctrl. | .append('g') | ||
.attr('class', 'node') | |||
.on('dblclick', gotoPage) | |||
.append('circle') | |||
.attr('r', Ctrl.circle.radius) | |||
.style('fill', function(node, index) { return Ctrl.color[node.level]; }) | |||
.call(Ctrl.force.drag); | |||
// Add a title to each node | |||
Ctrl.svg | |||
.selectAll('circle') | |||
.append('title') | |||
.text(function(node, index) { | |||
var title = node.name.replace(/_/g, ' ').toTitleCase(); | |||
if (node.level == 0) { title += ' (this page)'; } | |||
. | return title; | ||
}); | |||
// Add the level number to each node | |||
Ctrl.svg | |||
.selectAll('.node') | |||
.append('text') | |||
.attr('dx', Ctrl.circle.textXOffset) | |||
.attr('dy', Ctrl.circle.textYOffset) | |||
.text(function(node, index) { return node.level; }) | |||
.style('font-size', Ctrl.circle.fontSize) | |||
.style('fill', Ctrl.circle.fontColor) | |||
.style('font-weight', Ctrl.circle.fontWeight); | |||
// Draw a border around the graph | |||
Ctrl.svg.append('rect') | |||
.attr('x', 0) | |||
.attr('y', 0) | |||
.attr('height', Ctrl.canvas.height) | |||
.attr('width', Ctrl.canvas.width) | |||
.style('stroke', Ctrl.canvas.borderColor) | |||
.style('fill', 'none') | |||
.style('stroke-width', Ctrl.canvas.borderWidth); | |||
// Set up the tick handler | |||
Ctrl.force.on('tick', function() { tickHandler(); }); | |||
// Start the force simulation | |||
Ctrl.force.start(); | |||
} | } | ||
Line 308: | Line 285: | ||
}); | }); | ||
loadData(); | |||
renderGraph(); | renderGraph(); | ||
} | } | ||
$(document).ready(function() { main(); }); | $(document).ready(function() { main(); }); |
Revision as of 13:51, 24 June 2019
/* Name: MediaWiki:Network-graph.js Purpose: Build the network graph for the page being displayed Parameters: title = the internal title of the reference (e.g., Cerebral_hemispheric_lateralization_in_cardiac_autonomic_control) depth = the number of levels to include in the graph (1 - 9) Author: Alan O'Neill Release Date: June 24, 2019 */ var Ctrl; var Json; function init() { Ctrl = { canvas: { width: $('#network-graph').width(), height: $('#network-graph').width(), borderColor: 'black', borderWidth: '1px' }, color: [ '#e6194B', // red '#f58231', // orange '#ffe119', // yellow '#3cb44b', // green '#42d4f4', // cyan '#4363d8', // blue '#000075', // navy '#f032e6', // magenta '#800000', // maroon '#9A6324', // brown ], circle: { radius: 14, textXOffset: -4, textYOffset: 4, fontSize: '12px', fontColor: 'white', fontWeight: 'bold', }, arrow: { size: 6, strokeWidth: 1, // 0 = no arrow color: 'grey', }, line: { strokeWidth: 2, color: 'grey', }, physical: { charge: -10, linkDistance: 10, distance: 120, gravity: .01, }, force: null, svg: null, } } String.prototype.toTitleCase = function() { return this.replace( /\w\S*/g, function(txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); } ); } function getQuery(name) { // Return the value of a parameter in the query string // name - the name in the name/value pair var value = ''; var query = window.location.search.substring(1).split('&'); for (var i = 0; i < query.length; i++) { var nv = query[i].split('='); if (nv[0] == name) { value = nv[1]; break; } } return value; } function getBaseUrl() { // Return the base URL for the site (e.g., 'https://testtestsudepwiki.pathologyjhmi.edu') var baseUrl = location.protocol + '//' + location.host + location.pathname; var i = baseUrl.indexOf('/index.php'); if (i < 0) { i = baseUrl.indexOf('/site-links'); } baseUrl = baseUrl.substr(0, i); return baseUrl; } function getTitle() { // Return the title of the article from the URL var title = getQuery('title'); if (title.length == 0) { var delim = '/index.php/'; title = location.pathname.substr(location.pathname.indexOf(delim) + delim.length); // e.g., Cerebral_hemispheric_lateralization_in_cardiac_autonomic_control } return title; } function getDepth() { // Return the depth to use for the graph (?) var depth = 9; if (depth < 1) { depth = 1; } else if (depth > 9) { depth = 9; } return depth; } function tickHandler() { Ctrl.svg .selectAll('.link') .attr('x1', function(link) { return link.source.x; }) .attr('y1', function(link) { return link.source.y; }) .attr('x2', function(link) { return link.target.x; }) .attr('y2', function(link) { return link.target.y; }); Ctrl.svg .selectAll('.node') .attr('transform', function(node) { return 'translate(' + node.x + ',' + node.y + ')'; }); } function gotoPage(node, index) { // Go to the page associated with a node // node - the node that was double clicked (object) // index - the index of the node that was double clicked // When node.level is zero, the node refers to the page being displayed if (node.level > 0) { window.location.href = getBaseUrl() + '/index.php/' + node.name; } else { alert("You're already viewing this page."); } }; function loadData(depth, title) { $('#network-graph').html('<div id="network-graph-caption">Retrieving data for the network graph...</div>'); $.ajax({ method: 'get', url: getBaseUrl() + '/site-links/network-graph.php', data: { depth: getDepth(), title: getTitle() }, dataType: 'json', success: function(data, textStatus, jqXHR) { Json = data; $('#network-graph-caption').html('Hover over a node to see the title of the article. Double-click on a node to view the article.'); }, error: function(jqXHR, textStatus, errorThrown) { $('#network-graph-caption').html('No data is available for this graph.'); console.log('loadData: jqXHR =', jqXHR, '; textStatus =', textStatus, '; errorThrown =', errorThrown); }, }); } function renderGraph() { init(); // Create the canvas Ctrl.svg = d3.select('#network-graph') .append('svg') .attr('width', Ctrl.canvas.width) .attr('height', Ctrl.canvas.height); // Define the arrow head for (var level = 0; level < depth; level++ ) { let fullSize = Ctrl.arrow.size; let halfSize = Math.floor(fullSize / 2); Ctrl.svg .append('defs') .selectAll('marker') .data(['arrow' + level]) .enter() .append('marker') .attr('id', function(node, index) { return node; }) // e.g., 'arrow0' .attr('viewBox', '0 -' + halfSize + ' ' + fullSize + ' ' + fullSize) .attr('refX', function(node, index) { return Math.floor(Ctrl.circle.radius / 2) + Ctrl.arrow.size; }) .attr('refY', 0) .attr('markerWidth', fullSize) .attr('markerHeight', fullSize) .attr('orient', 'auto') .append('path') .attr('d', function(node, index) { return 'M0,-' + halfSize + 'L' + fullSize + ',0L0,' + halfSize + 'L' + fullSize + ',0L0,-' + halfSize; }) .style('stroke', Ctrl.arrow.color) .style('stroke-width', Ctrl.arrow.strokeWidth) } // Define the characteristics of the force simulation Ctrl.force = d3.layout .force() .size([Ctrl.canvas.width, Ctrl.canvas.height]) .charge(Ctrl.physical.charge) .linkDistance(Ctrl.physical.linkDistance) .distance(Ctrl.physical.distance) .gravity(Ctrl.physical.gravity); // Introduce the data to the force simulation Ctrl.force .nodes(json.nodes) .links(json.links); // Add the links (lines and arrows) Ctrl.svg .selectAll('.link') .data(json.links) .enter() .append('line') .attr('class', 'link') .style('stroke', Ctrl.line.color) .style('stroke-width', Ctrl.line.strokeWidth) .style('marker-end', function(node, index) { return 'url(#arrow0)'; }); //return 'url(#arrow' + node.level + ')'; }); // Add the nodes (circles) Ctrl.svg .selectAll('.node') .data(json.nodes) .enter() .append('g') .attr('class', 'node') .on('dblclick', gotoPage) .append('circle') .attr('r', Ctrl.circle.radius) .style('fill', function(node, index) { return Ctrl.color[node.level]; }) .call(Ctrl.force.drag); // Add a title to each node Ctrl.svg .selectAll('circle') .append('title') .text(function(node, index) { var title = node.name.replace(/_/g, ' ').toTitleCase(); if (node.level == 0) { title += ' (this page)'; } return title; }); // Add the level number to each node Ctrl.svg .selectAll('.node') .append('text') .attr('dx', Ctrl.circle.textXOffset) .attr('dy', Ctrl.circle.textYOffset) .text(function(node, index) { return node.level; }) .style('font-size', Ctrl.circle.fontSize) .style('fill', Ctrl.circle.fontColor) .style('font-weight', Ctrl.circle.fontWeight); // Draw a border around the graph Ctrl.svg.append('rect') .attr('x', 0) .attr('y', 0) .attr('height', Ctrl.canvas.height) .attr('width', Ctrl.canvas.width) .style('stroke', Ctrl.canvas.borderColor) .style('fill', 'none') .style('stroke-width', Ctrl.canvas.borderWidth); // Set up the tick handler Ctrl.force.on('tick', function() { tickHandler(); }); // Start the force simulation Ctrl.force.start(); } function main() { $(window).resize(function() { renderGraph(); }); loadData(); renderGraph(); } $(document).ready(function() { main(); });