MediaWiki:Network-graph.js: Difference between revisions

From SUDEP Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 14: Line 14:
function init() {
function init() {
Ctrl = {
Ctrl = {
json: null,
canvas: {
canvas: {
width: $('#network-graph').width(),
width: $('#network-graph').width(),
Line 197: Line 199:


// Read the data and use it to build the graph
// Read the data and use it to build the graph
var url = getBaseUrl() + '/site-links/network-graph.php?depth=' + depth + '&title=' + title;
//var url = getBaseUrl() + '/site-links/network-graph.php?depth=' + depth + '&title=' + title;
d3.json(url, function(json) {
//d3.json(url, function(json) {
if (json) {
// if (Ctrl.json) {
// Introduce the data to the force simulation
// }
Ctrl.force
//});
.nodes(json.nodes)
 
.links(json.links);
if (! Ctrl.json) {
$.ajax({
method:        'get',
url:            getBaseUrl() + '/site-links/network-graph.php,
data:          { depth: depth, title: title },
dataType:      'json',
success:        function(data, textStatus, jqXHR) {
Ctrl.json = json;
},
error:          function(jqXHR, textStatus, errorThrown) {
var msg;
if ('responseJSON' in jqXHR && 'error' in jqXHR.responseJSON) {
msg = jqXHR.responseJSON.error;
} else {
msg = textStatus + ': ' + errorThrown;
}
alert(msg);
},
});
}


// Add the links (lines and arrows)
var json;
Ctrl.svg
$.extend(json, Ctrl.json);
.selectAll('.link')
 
.data(json.links)
if (json) {
.enter()
// Introduce the data to the force simulation
.append('line')
Ctrl.force
.attr('class', 'link')
.nodes(json.nodes)
.style('stroke', Ctrl.line.color)
.links(json.links);
.style('stroke-width', Ctrl.line.strokeWidth)
 
.style('marker-end', function(node, index) { return 'url(#arrow0)'; }); //return 'url(#arrow' + node.level + ')'; });
// 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)
// Add the nodes (circles)
Ctrl.svg
Ctrl.svg
.selectAll('.node')
.selectAll('.node')
.data(json.nodes)
.data(json.nodes)
.enter()
.enter()
.append('g')
.append('g')
.attr('class', 'node')
.attr('class', 'node')
.on('dblclick', gotoPage)
.on('dblclick', gotoPage)
.append('circle')
.append('circle')
.attr('r', Ctrl.circle.radius)
.attr('r', Ctrl.circle.radius)
.style('fill', function(node, index) { return Ctrl.color[node.level]; })
.style('fill', function(node, index) { return Ctrl.color[node.level]; })
.call(Ctrl.force.drag);
.call(Ctrl.force.drag);


// Add a title to each node
// Add a title to each node
Ctrl.svg
Ctrl.svg
.selectAll('circle')
.selectAll('circle')
.append('title')
.append('title')
.text(function(node, index) {
.text(function(node, index) {
var title = node.name.replace(/_/g, ' ').toTitleCase();
var title = node.name.replace(/_/g, ' ').toTitleCase();
if (node.level == 0) { title += ' (this page)'; }
if (node.level == 0) { title += ' (this page)'; }
return title;
return title;
});
});


// Add the level number to each node
// Add the level number to each node
Ctrl.svg
Ctrl.svg
.selectAll('.node')
.selectAll('.node')
.append('text')
.append('text')
.attr('dx', Ctrl.circle.textXOffset)
.attr('dx', Ctrl.circle.textXOffset)
.attr('dy', Ctrl.circle.textYOffset)
.attr('dy', Ctrl.circle.textYOffset)
.text(function(node, index) { return node.level; })
.text(function(node, index) { return node.level; })
.style('font-size', Ctrl.circle.fontSize)
.style('font-size', Ctrl.circle.fontSize)
.style('fill', Ctrl.circle.fontColor)
.style('fill', Ctrl.circle.fontColor)
.style('font-weight', Ctrl.circle.fontWeight);
.style('font-weight', Ctrl.circle.fontWeight);


// Draw a border around the graph
// Draw a border around the graph
Ctrl.svg.append('rect')
Ctrl.svg.append('rect')
.attr('x', 0)
.attr('x', 0)
.attr('y', 0)
.attr('y', 0)
.attr('height', Ctrl.canvas.height)
.attr('height', Ctrl.canvas.height)
.attr('width', Ctrl.canvas.width)
.attr('width', Ctrl.canvas.width)
.style('stroke', Ctrl.canvas.borderColor)
.style('stroke', Ctrl.canvas.borderColor)
.style('fill', 'none')
.style('fill', 'none')
.style('stroke-width', Ctrl.canvas.borderWidth);
.style('stroke-width', Ctrl.canvas.borderWidth);


// Set up the tick handler
// Set up the tick handler
Ctrl.force.on('tick', function() { tickHandler(); });
Ctrl.force.on('tick', function() { tickHandler(); });


// Start the force simulation
// Start the force simulation
Ctrl.force.start();
Ctrl.force.start();
$('#network-graph-caption').html('Hover over a node to see the title of the article. Double-click on a node to view the article.');
$('#network-graph-caption').html('Hover over a node to see the title of the article. Double-click on a node to view the article.');
} else {
} else {
$('#network-graph-caption').html('No data is available for this graph.');
$('#network-graph-caption').html('No data is available for this graph.');
}
}
});
}
}



Revision as of 13:31, 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;

function init() {
	Ctrl = {
		json:	null,

		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 (?)

	return 9;
}

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 renderGraph() {
	$('#network-graph').html('<div id="network-graph-caption">Retrieving data for the network graph...</div>');

	init();

	var title = getTitle();
	var depth = getDepth();

	if (depth < 1) { depth = 1; }
	else if (depth > 9) { depth = 9; }

	// 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);

	// Read the data and use it to build the graph
	//var url = getBaseUrl() + '/site-links/network-graph.php?depth=' + depth + '&title=' + title;
	//d3.json(url, function(json) {
	//	if (Ctrl.json) {
	//	}
	//});

	if (! Ctrl.json) {
		$.ajax({
			method:         'get',
			url:            getBaseUrl() + '/site-links/network-graph.php,
			data:           { depth: depth, title: title },
			dataType:       'json',
			success:        function(data, textStatus, jqXHR) {
						Ctrl.json = json;
						
					},
			error:          function(jqXHR, textStatus, errorThrown) {
						var msg;
						if ('responseJSON' in jqXHR && 'error' in jqXHR.responseJSON) {
							msg = jqXHR.responseJSON.error;
						} else {
							msg = textStatus + ': ' + errorThrown;
						}
						alert(msg);
					},
		});
	}

	var json;
	$.extend(json, Ctrl.json);

	if (json) {
		// 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();
		$('#network-graph-caption').html('Hover over a node to see the title of the article. Double-click on a node to view the article.');
	} else {
		$('#network-graph-caption').html('No data is available for this graph.');
	}
}

function main() {
	$(window).resize(function() {
		renderGraph();
	});

	renderGraph();
}

$(document).ready(function() { main(); });