// ==UserScript==
// @name           Blackboard and WebCT Forum - Social Analysis Tool
// @namespace      BBFSA
// @description    Forum Social Analysis Data Extraction and Visualiation Tool
// @include        http://*.qut.edu.au/webapps/discussionboard/do/message?action=message_tree&course_id=*&conf_id=*&forum_id=*&nav=discussion_board&thread_id=*&message_id=*
// @include        http://*.qut.edu.au/webapps/discussionboard/do/message?action=list_messages&course_id=*&conf_id=*&forum_id=*&nav=discussion_board&thread_id=*&message_id=*
// @include        https://*.ubc.ca/webct/urw/*/newMessageThread.dowebct?discussionaction=mDisplay&topicid=*&areaid=*&fromTopic=*&homePage=*
// @include        http://vista.uow.edu.au/webct/urw/*/newMessageThread.dowebct?discussionaction=mDisplay&topicid=*&areaid=*&fromTopic=*
// @include        https://*.ubc.ca/webct/newMessageThread.dowebct
// @include        http://vista.uow.edu.au/webct/newMessageThread.dowebct
// ==/UserScript==

// Global Functions

function stripSpaces(x) {
	// Removes spaces from a string
	return (x.replace(/^\W+/,'')).replace(/\W+$/,'');
}

function getTextAfterChar(Text, charToStart)
{
	// Returns all characters in a string after the specified substring
	var len = Text.length;
	var posStart = Text.indexOf(charToStart);
	return Text.substring(posStart,len);
}

function getRequestVars()
{
	// Returns an array with all querystring parameters
	var request= new Array(); 
	var vals=location.search.substr(1).split("&");
	for (var i in vals) 
	{ 
		vals[i] = vals[i].replace(/\+/g, " ").split("="); 
		request[unescape(vals[i][0])] = unescape(vals[i][1]); 
	} 
	return request;
}

function InsertSNALink(urlString)
{
	// Insert an SNA Link
	urlString  = getTextAfterChar(urlString,'&');

	var buildHTML = ""; 

	
	if (LMS == "Blackboard")
	{
		navbar = document.getElementById('checkboxPicker');
		buildHTML += '&nbsp; <a href="/webapps/discussionboard/do/message?action=message_tree';
		buildHTML += urlString + '">Perform Social Network Analysis</a>';
	}
	else if (LMS == "WebCT")
	{
		navbar = document.getElementById('pages');
		buildHTML += '&nbsp; <a href="Javascript:expandCollapseAll(\'mExpandAll\');';
		buildHTML += '">Perform Social Network Analysis</a>';
	}

	if (navbar) 
	{
       		var newLink = document.createElement("span");
       		newLink.innerHTML = buildHTML;
		//navbar.parentNode.insertBefore(newLink, nav.nextSibling);
		if (LMS == "Blackboard")
		{
			navbar.parentNode.insertBefore(newLink, navbar.nextSibling);
		}
		else if (LMS == "WebCT")
		{
			navbar.appendChild(newLink);
			//alert(navbar);
		}
	}

}

function GenerateGraphML(nodes,edges)
{
	// Generates a the Social Network Graph in the GraphML XML Format

	var graphML;

	graphML = '<?xml version="1.0" encoding="UTF-8"?>\n';
	graphML += '<graphml xmlns="http://graphml.graphdrawing.org/xmlns" ';
	graphML += 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ';
	graphML += 'xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns ';
	graphML += 'http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">\n';
	graphML += '<graph id="G" edgedefault="directed">\n';

	// Generate the nodes
	for (person in nodes)
	{
		graphML += '<node id="' + person + '"/>\n'; 
	}

	// Generate the edges (ie links between nodes)

	for (reply in edges)
	{
		fromToArray = reply.split("_");
		graphML += '<edge source="' + fromToArray[0] + '" target="' + fromToArray[1] + '"/>' + edges[reply] + '</edge>' + '\n';
	}

	graphML += '</graph>\n';
	graphML += '</graphml>\n';

	return graphML;
}

function GenerateVNAFileFormat(nodes,edges)
{
	// Generates a the Social Network Graph in the GraphML XML Format

	var vnaBuilder = "";

	vnaBuilder += '*Node data\n';
	vnaBuilder += 'ID posts\n';

	// Generate the nodes
	for (person in nodes)
	{
		vnaBuilder += '"' + person + '" ' + nodes[person] +'\n'; 
	}

	// Generate the edges (ie links between nodes)

	vnaBuilder += '*Tie data\n';
	vnaBuilder += 'from to talk strength\n';


	for (reply in edges)
	{
		fromToArray = reply.split("_");
		vnaBuilder += '"' + fromToArray[0] + '" "' + fromToArray[1] + '" 1 ' + edges[reply] + '\n';
	}

	return vnaBuilder;
}

function PostReplyTable(edges)
{
	// Generates a Post-Reply Frequency Table

	var tableBuilder = "";

	tableBuilder += "<h2>Post-Reply Frequency Table</h2><table border='1'>";
	tableBuilder += "<tr><td>Poster</td><td>Replied To</td><td>Frequency</td></tr>";

	// Generate the edges (ie links between nodes)

	for (reply in edges)
	{
		fromToArray = reply.split("_");
		tableBuilder += "<tr><td>" + fromToArray[0] + "</td><td>" + fromToArray[1] + "</td><td>" + edges[reply] + "</td></tr>";
	}

	tableBuilder += '</table>\n';

	return tableBuilder;
}


function PerformSocialAnalysis()
{
	// Extract SNA Data
	var forumusers = [];
	var postsbyusers = "";
	var totalposts = 0;
	var threadowners = [];
	var replies = [];

	var formWithTable = document.getElementById("treeForm");
	//alert(formWithTable.getAttribute("name"));

	var table = formWithTable.getElementsByTagName("table");
	var rows = table[0].getElementsByTagName("tr");
	//alert("rows:" + rows.length);
	var network = "";

	network = network + "<table border='1'>";
	network = network + "<tr><td>Post ID</td><td>Thread Depth</td><td>Posted By</td><td>Posted On</td><td>Reply To</td></tr>";

	for(i = 0; i < rows.length; i++)
	{
		var cols = rows[i].getElementsByTagName("td");
		var rowtext = "--";
		//alert("cols:" + cols.length);
		for (j = 0; j < cols.length; j++)
		{
			//rowtext = rowtext + " " + cols[j].innerHTML;
			if (j==3)
			{
				columnWithLink = cols[j].getElementsByTagName("a");
				relationship = columnWithLink[1].getAttribute("href");
				relationship = relationship.substring(relationship.indexOf('(')+1,relationship.length-1);
                       		relationshiplist = relationship.split(",");
				postid = relationshiplist[1].substring(1,relationshiplist[1].length-1);
				threaddepth =	getTextAfterChar(relationshiplist[2].substring(1,relationshiplist[2].length-

1),'_').substring(1);


               		}
			else if (j==4)
			{
				posted_by = cols[j].innerHTML;
				posted_by = posted_by.substring(posted_by.indexOf('>'),posted_by.length);
				posted_by = stripSpaces(posted_by);

			if (forumusers[posted_by])
			{
				forumusers[posted_by] = forumusers[posted_by] + 1;
			}
			else
			{
				forumusers[posted_by] = 1;
			}
			}
			else if (j==5)
			{
				posted_on = cols[j].innerHTML;
				posted_on = stripSpaces(posted_on);
			}

		}


		threadowners[threaddepth] = posted_by;
		//alert(threadowners[threaddepth]);

		if (threaddepth.length != 1)
		{
			threadReplyDepth = threaddepth.substring(0,threaddepth.lastIndexOf('_'));//threaddepth.length-2
			reply_to = threadowners[threadReplyDepth];

			//alert(threaddepth + " " + threadReplyDepth);

			sna_relationship = posted_by + "_" + reply_to;
			if (replies[sna_relationship])
			{
				replies[sna_relationship] += 1;
			}
			else
			{
				replies[sna_relationship] = 1;
			}
		}
		else
		{
			// Originating Thread
			reply_to = "-";
		}

		

		totalposts = totalposts + 1;

		network = network + "<tr><td>" + postid + "</td><td>" + threaddepth;
		network += "</td><td>" + posted_by + "</td><td>" + posted_on + "</td><td>" + reply_to + "</td></tr>";
       		//network = network + postid + " " + threaddepth + " " + posted_by +" " + posted_on + "<br>";
	}

	network = network + "</table>";
	//alert(network);

	postsbyusers = postsbyusers + "<table border='1'>";
	postsbyusers = postsbyusers + "<tr><td>Forum Users</td><td>Number of Posts</td></tr>";
	for (i in forumusers)
	{
		postsbyusers = postsbyusers + "<tr><td>" + i + "</td><td>" + forumusers[i] + "</td></tr>";
	}
	postsbyusers = postsbyusers + "</table>";

	//alert(network + postsbyusers + "\n" + "totalposts:" + totalposts);

	var forumstats = document.createElement("div");
	BuildHTML = "<hr><h2>Blackboard Forum Social Network Analysis</h2><p>Total Posts: " + totalposts + postsbyusers + "<p>" + network;
	BuildHTML += PostReplyTable(replies);
	BuildHTML += "<h2>GraphML Output</h2>";
	BuildHTML += "<textarea rows='15' cols='100'>" + GenerateGraphML(forumusers,replies) + "</textarea>";

	BuildHTML += "<h2>VNA File Format</h2>";
	BuildHTML += "<textarea rows='15' cols='100'>" + GenerateVNAFileFormat(forumusers,replies) + "</textarea><br>";
	BuildHTML += "Note: VNA Format is used by <a href='http://www.analytictech.com/Netdraw/netdraw.htm'>NetDraw</a>";
	forumstats.innerHTML = BuildHTML;

	formWithTable.parentNode.insertBefore(forumstats, formWithTable.nextSibling);
}

function PerformSocialAnalysisWebCT()
{

	// Extract SNA Data
	var forumusers = [];
	var postsbyusers = "";
	var totalposts = 0;
	var threadowners = [];
	var replies = [];

	var currRoot = "";

	//var formWithTable = document.getElementById("messageViewForm");


	var table = document.getElementById("datatable");
	//formWithTable.getElementsByTagName("table");


	var rows = table.tBodies[0].rows;//table.getElementsByTagName("tr");


	//alert("rows:" + rows.length);
	

	var network = "";

	network = network + "<table border='1'>";
	network = network + "<tr><td>Post ID</td><td>Thread Depth</td><td>Posted By</td><td>Posted On</td><td>Reply To</td></tr>";

	for(i = 0; i < rows.length; i++) 
	{
		var cols = table.tBodies[0].rows[i].cells; //rows[i].getElementsByTagName("td");
		var rowtext = "--";
		//alert("cols:" + cols.length);
		for (j = 0; j < cols.length; j++)
		{
			//alert(cols[j].innerHTML);
			if (j==0)
			{
				inputtag = cols[j].getElementsByTagName("input");
				postid = inputtag[0].getAttribute("id");
				threaddepth = inputtag[0].getAttribute("indent"); 

				if (threaddepth=="0")
				{
					currRoot = postid;
				}

               		}
			else if (j==1)
			{

               		}
			else if (j==5)
			{
				
				posted_by = cols[j].innerHTML;
				
				
				posted_by = posted_by.substring(posted_by.indexOf('>')+1,posted_by.length);
				posted_by = posted_by.substring(0,posted_by.indexOf('<'));
				//alert(posted_by);

				if (posted_by == "")
				{
					posted_by = cols[j].innerHTML;
					posted_by = posted_by.substring(2,posted_by.length-2);
				}

				
				if (forumusers[posted_by])
				{
					forumusers[posted_by] = forumusers[posted_by] + 1;
				}
				else
				{
					forumusers[posted_by] = 1;
				}
			}
			else if (j==6)
			{
				posted_on = cols[j].innerHTML;
				//alert(posted_on);
				//posted_on = stripSpaces(posted_on);
			}
		}

		threadowners[currRoot+"_"+threaddepth] = posted_by;

		if (threaddepth=="0")
		{
			reply_to = "-";
		}
		else 
		{
			reply_to = threadowners[currRoot+"_"+ (parseInt(threaddepth)-1)];
			sna_relationship = posted_by + "_" + reply_to;
			if (replies[sna_relationship])
			{
				replies[sna_relationship] += 1;
			}
			else
			{
				replies[sna_relationship] = 1;
			}
		}
		
		totalposts = totalposts + 1;
		network = network + "<tr><td>" + postid + "</td><td>" + threaddepth;
		network += "</td><td>" + posted_by + "</td><td>" + posted_on + "</td><td>" + reply_to + "</td></tr>";
       		//network = network + postid + " " + threaddepth + " " + posted_by +" " + posted_on + "<br>";
	}

	network = network + "</table>";


	postsbyusers = postsbyusers + "<table border='1'>";
	postsbyusers = postsbyusers + "<tr><td>Forum Users</td><td>Number of Posts</td></tr>";
	for (i in forumusers)
	{
		postsbyusers = postsbyusers + "<tr><td>" + i + "</td><td>" + forumusers[i] + "</td></tr>";
	}
	postsbyusers = postsbyusers + "</table>";

	//alert(network + postsbyusers + "\n" + "totalposts:" + totalposts);

	var forumstats = document.createElement("div");
	BuildHTML = "<div align='left'><hr><h2>WebCT Forum Social Network Analysis</h2><p>Total Posts: " + totalposts + postsbyusers + 

"<p>";

	BuildHTML += network;

	BuildHTML += PostReplyTable(replies);
	BuildHTML += "<h2>GraphML Output</h2>";
	BuildHTML += "<textarea rows='15' cols='100'>" + GenerateGraphML(forumusers,replies) + "</textarea>";

	BuildHTML += "<h2>VNA File Format</h2>";
	BuildHTML += "<textarea rows='15' cols='100'>" + GenerateVNAFileFormat(forumusers,replies) + "</textarea><br>";
	BuildHTML += "Note: VNA Format is used by <a href='http://www.analytictech.com/Netdraw/netdraw.htm'>NetDraw</a></div>";

	forumstats.innerHTML = BuildHTML;
	
	navbar = document.getElementById('pages');
	navbar.appendChild(forumstats);

}

// Get querystring
var querystring = document.location.href;

// Work out Forum page type - message list or forum tree/thread
var RequestVars = getRequestVars();
var actionRequetVar;
var ForumPageType;
var LMS;


if (RequestVars["action"])
{
	// This is a Blackboard Page
	actionRequetVar = RequestVars["action"];
	ForumPageType = ((actionRequetVar == "message_tree") ? "message_tree" : "list_messages");
	LMS = "Blackboard";
}
else if (RequestVars["discussionaction"])
{
	// This is a WebCT Page
	actionRequetVar = RequestVars["discussionaction"];
	ForumPageType = "WebCTForum";
	LMS = "WebCT";
}
else if ((querystring.indexOf("webct/newMessageThread.dowebct")!=-1))
{	// && (document.form.messageViewForm.discussionaction.value=="mCollapseAll")
	// An Expanded Thread view is displayed in WebCT
	ForumPageType = "WebCTForumThread";
	LMS = "WebCT";	
}

//alert(querystring.indexOf("webct/newMessageThread.dowebct"));
//alert(ForumPageType);

if (ForumPageType == "list_messages")
{
	InsertSNALink(querystring);
}
else if (ForumPageType == "message_tree")
{
	PerformSocialAnalysis();
}
else if (ForumPageType == "WebCTForum")
{
	//alert("WebCT");
	InsertSNALink(querystring);
	//PerformSocialAnalysis();
}
else if (ForumPageType == "WebCTForumThread")
{
	//alert("WebCT Expanded Thread");
	PerformSocialAnalysisWebCT();
//alert(document.messageViewForm.discussionaction.value);
}