WebCT fixer

By Splondike Last update Aug 11, 2008 — Installed 441 times.

There are 1 previous version of this script.

// ==UserScript==
// @name WebCT fixer
// @namespace http://my.opera.com/splondike/
// @description Attempts to make webct (tested on version 4.1.5.8) more user-friendly
// @include http://webct.anu.edu.au/*
// ==/UserScript==

(function(){
//The following boolean values (ie. true/false) turn on and off each of the additional functions

//Direct links to discussion boards on the homearea page
var directLinks = true;
//Reverses the order of discussion board topics (newest at the top)
var reverseTopics = true;
//Replaces attatchments with direct links (bypasses the attatchment window)
var directAttach = true;
//Makes the subject line the window title, rather than "Discussions"
//Also makes the window icon different
var addSubject = true;
//Makes the subject line a hyperlink to the discussion post in the discussion listing
var hyperLinkDiscussionSubject = true;
//This script adds a proper href attribute to links, meaning that 'Open in new tab' type things work.
//Set this attribute to true to make the new tab use frames (eg. navigation, logo) around the page, false for just the page
var useFramesInHref = true;

//Greasemonkey only uses 'load'
var evType = window.opera ? "DOMContentLoaded" : "load";
window.addEventListener(evType, function(e){
	//I'm typing this alot in the script
	var winLoc = window.location;
	var winTop = top.window.location;
	var baseURL = winLoc.protocol + "//" + winLoc.hostname;

	//Adds direct links to the discussion boards on the 'homearea' page if they don't already exist
	if(directLinks && winLoc.pathname.indexOf("homearea") != -1){
		var trs = getElementsByClassName("termtable")[0].getElementsByTagName("tr");
		//x=1 because the first tr is 'coursehead'
		for(var x=1;x<trs.length;x++){
			if(!trs[x].className)continue;

			if((x+2) >= trs.length || trs[x+2].className){
				var td = makeElement("td",{},"\u00a0\u00a0News:\u00a0");
				var a = makeElement("a",{ 
					href:trs[x+1].getElementsByTagName("a")[0].href.replace("serve_home", "student/serve_bulletin")
				});
				var img = makeElement("img",{
					src:"/web-ct/gb8/img/homearea/channels/large_discussions.gif",
					className:"alignMiddle",
					width:"20",
					height:"20",
					style:{
						opacity:"0.5",
						border:"0"
					}
				});
				a.appendChild(img);
				td.appendChild(a);
				var tr = document.createElement("tr");
				tr.appendChild(td);
				trs[0].parentNode.insertBefore(tr, trs[x+1].nextSibling);
			}
		}
	}

	//Makes the 'hash' links work (reads main frame url from the url hash)
	if((winLoc.search.length < 2) && (winLoc.pathname.indexOf("serve_home") != -1) || (winLoc.pathname.indexOf("serve_bulletin"))){
		//Adds a hash argument to set the location of the main frame
		var args = extractArgs(winLoc.hash);
		if(args['mainframeurl']){
			var name = args['mainframename'] || 'Webpage';
			//Overwrite function called to make the breadcrumbs (necessary; normally changes url) 
			if(!window.opera){
				unsafeWindow.update_breadcrumb(name, args['mainframeurl'],'',1,1,1,0);
			}else{
				window['bootstrap_course_nav'] = function(){
					write_course_nav_bar();
					//Also changes the main frame url
					top.update_breadcrumb(name, args['mainframeurl'],'',1,1,1,0);
				}
			}
		}
	}

	//Adds a normal href to the javascript links, allowing open-in-new-tab 
	var alinks = document.getElementsByTagName("a");
	Array.forEach(alinks, function(link){
		var oc = link.getAttribute("onclick");
		var href = link.href;

		if(!oc)return;
		if(oc.indexOf("update_breadcrumb") != -1){
			var chunks = oc.match("\\('(.+?)', '(.+?)'(?:.+?url_encoded\\('(.+?)')?");
			if(useFramesInHref){
				href = baseURL + winTop.pathname + winTop.search + "#mainframeurl=" + escape(chunks[2]);
				if(chunks[3])href += escape(chunks[3]);
				if(chunks[1])href += "&mainframename=" + escape(chunks[1]);
			}else{
				href = chunks[2];
				if(chunks[3])href += chunks[3];
			}
		}else if(oc.indexOf("doWindowOpen") != -1){
			href = oc.match("\\('(.+?)'")[1];
		}else if(oc.indexOf("go_home") != -1){
			href = (winTop+'').replace("student/serve_bulletin", "serve_home");
		}else if(oc.indexOf("go_to_mywebct") != -1){
			href = oc.match("'(.*?)'")[1];
		}else if(oc.indexOf("parent.location=") != -1){
			href = oc.match("'(.*?)'")[1];
		}else if(oc.indexOf("main_win.location=") != -1){
			href = oc.match("'(.*?)student")[1] + "serve_home";
		}
		link.href = href;
	});

	if(winLoc.search.indexOf("ACTION=LIST") != -1){
		//Reverses the order of the posts in discussion boards
		//TODO: Screws up if you 'expand' a topic inline
		if(reverseTopics){ 
		var tables = getElementsByClassName("datatable", "table");
		Array.forEach(tables, function(table){
			if(winLoc.search.indexOf("ARG2=Unread") == -1){
			var newTable = table.cloneNode(true);
			var newRows = newTable.getElementsByTagName("tr");
			var newTabBody = newRows[0].parentNode;

			for(var y=newRows.length - 4;y>=2;y--)
				newTabBody.appendChild(newRows[y]);
			for(var y=0;y<3;y++)
				newTabBody.appendChild(newRows[2]);

			table.parentNode.replaceChild(newTable, table);
			}
		});
		}

		//Makes the subject line a hyperlink to the topic
		if(hyperLinkDiscussionSubject){
			var links = getElementsByClassName("inline", "a");
			Array.forEach(links, function(link){
				link.appendChild(link.nextSibling.nextSibling);
			});
		}
	}

	//Replaces the attatchment window in discussion boards by direct links
	if(winLoc.search.indexOf("COMPILETHREAD") != -1){
		if(directAttach){
		var dividers = getElementsByClassName("divider", "tr");
		Array.forEach(dividers, function(divider){
			var attLink = divider.getElementsByTagName("a")[0];
			if(attLink){
			var attId = attLink.getAttribute("onclick").match(/([0-9]+)/)[0];
			XHRGet(baseURL + winLoc.pathname + "?VIEWMAILATTACH1+BULLETIN+" + attId, function(req, refLink){
				if(!req)return;

				var regex = new RegExp('href="(.+)"(?= target="right").+?>(.+)</a></label>(?:\\n|\\r)+<br>(\\(.+?\\))', 'gi');
				var attatchments = new Array();
				var matches;
				while(matches = regex.exec(req.responseText))
					attatchments.push(matches);

				Array.forEach(attatchments, function(chunks){
					var newLink = makeElement("a", {
						href:baseURL + chunks[1] + "+SAVE",
						style:{
							padding:"2px 0 2px 10px",
							marginRight:"5px",
							background:"url('/web-ct/en8/img/small_attachment.gif') no-repeat"
						}
					}, chunks[2] + " " + chunks[3]);
					refLink.parentNode.insertBefore(newLink, refLink);
				});
				refLink.parentNode.removeChild(refLink);
			}, attLink);
			}
		});
		}
		//Changes the subject line and title of threads
		if(addSubject){
			var subLine = getElementsByClassName("fieldvalue", "span")[0].childNodes[0].nodeValue;
			document.title = subLine;
			var link = makeElement("link", {rel:"icon", href:"/web-ct/en8/img/small_all_messages.gif"});
			document.getElementsByTagName("head")[0].appendChild(link);
		}
	}
},false);

//creates an element, and/or sets it's properties
function makeElement(elm, propArr, text){
	//Hackish fix for greasemonkey style object not being instanceof Object??
	elm = (elm.charAt)?document.createElement(elm):elm;
	for(var key in propArr)
		if(propArr[key] instanceof Object)
			makeElement(elm[key], propArr[key]);
		else
			elm[key] = propArr[key];

	if(text)elm.appendChild(document.createTextNode(text));
	return elm;
}

//Fixes the annoying 'unsupported' popup, and the back button for opera
if(window.opera){
	var oDMV = function(name, rtn){opera.defineMagicVariable(name, function(){return rtn}, null);}
	oDMV('is_js'   ,1.5);
	oDMV('is_opera',false);
	oDMV('is_gecko',true);
	oDMV('is_moz'  ,true);
	oDMV('is_moz17',true);
	opera.setOverrideHistoryNavigationMode("compatible");
}

// From dean.edwards.name, enumeration for objects with .length
if (!Array.forEach) { // mozilla already supports this
    Array.forEach = function(object, block, context) {
        for (var i = 0; i < object.length; i++) {
            block.call(context, object[i], i, object);
        }
    };
}

//Adapted from http://phpfi.com/238008 
function getElementsByClassName(className, tag, elm){
        var re = [];
        var xpathResult = document.evaluate(".//"+(tag||"*")+"[contains(concat(' ', @class, ' '), ' " + className + " ')]", elm || document, null, 0, null);
        var ele;
        while ((ele = xpathResult.iterateNext()))
            re.push(ele)
        return re;
}

function XHRGet(url, callback, extras){
	var req = new XMLHttpRequest();
	req.open("GET", url, true);
	req.onreadystatechange = function(){
		if(req.readyState != 4)return;
		if(req.status == 200){
			callback(req, extras);		
		}else{
			callback(false, extras);
		}
	}
	req.send("");
}

//Extracts arguments in the (encoded) arg1=foo&arg2=bar form and returns a dictionary
function extractArgs(argString){
	var rtnObj = new Object();
	if(!argString){return rtnObj;}
	var chunks = argString.replace("#","").split("&");
	Array.forEach(chunks, function(chunk){
		var nvPair = chunk.split("=");
		rtnObj[unescape(nvPair[0])] = unescape(nvPair[1]);
	}, this);
	return rtnObj;
}
})();