Added in support for new HPC Dashboard View, ability to retrieve HPC Dashboard view from views.py in separate function, support for log4javascript to provide integration with browser console for debugging, and leaflet support for MakiMarkers on encodings.
diff --git a/planetstack/core/static/log4javascript-1.4.6/test/xntest.js b/planetstack/core/static/log4javascript-1.4.6/test/xntest.js
new file mode 100644
index 0000000..1b8f475
--- /dev/null
+++ b/planetstack/core/static/log4javascript-1.4.6/test/xntest.js
@@ -0,0 +1,739 @@
+// Next three methods are primarily for IE5, which is missing them
+if (!Array.prototype.push) {
+	Array.prototype.push = function() {
+		for (var i = 0; i < arguments.length; i++){
+				this[this.length] = arguments[i];
+		}
+		return this.length;
+	};
+}
+
+if (!Array.prototype.shift) {
+	Array.prototype.shift = function() {
+		if (this.length > 0) {
+			var firstItem = this[0];
+			for (var i = 0; i < this.length - 1; i++) {
+				this[i] = this[i + 1];
+			}
+			this.length = this.length - 1;
+			return firstItem;
+		}
+	};
+}
+
+if (!Function.prototype.apply) {
+	Function.prototype.apply = function(obj, args) {
+		var methodName = "__apply__";
+		if (typeof obj[methodName] != "undefined") {
+			methodName += (String(Math.random())).substr(2);
+		}
+		obj[methodName] = this;
+
+		var argsStrings = new Array(args.length);
+		for (var i = 0; i < args.length; i++) {
+			argsStrings[i] = "args[" + i + "]";
+		}
+		var script = "obj." + methodName + "(" + argsStrings.join(",") + ")";
+		var returnValue = eval(script);
+		delete obj[methodName];
+		return returnValue;
+	};
+}
+
+/* -------------------------------------------------------------------------- */
+
+var xn = new Object();
+
+(function() {
+	// Utility functions
+
+	// Event listeners
+	var getListenersPropertyName = function(eventName) {
+		return "__listeners__" + eventName;
+	};
+
+	var addEventListener = function(node, eventName, listener, useCapture) {
+		useCapture = Boolean(useCapture);
+		if (node.addEventListener) {
+			node.addEventListener(eventName, listener, useCapture);
+		} else if (node.attachEvent) {
+			node.attachEvent("on" + eventName, listener);
+		} else {
+			var propertyName = getListenersPropertyName(eventName);
+			if (!node[propertyName]) {
+				node[propertyName] = new Array();
+
+				// Set event handler
+				node["on" + eventName] = function(evt) {
+					evt = module.getEvent(evt);
+					var listenersPropertyName = getListenersPropertyName(eventName);
+
+					// Clone the array of listeners to leave the original untouched
+					var listeners = cloneArray(this[listenersPropertyName]);
+					var currentListener;
+
+					// Call each listener in turn
+					while (currentListener = listeners.shift()) {
+						currentListener.call(this, evt);
+					}
+				};
+			}
+			node[propertyName].push(listener);
+		}
+	};
+
+	// Clones an array
+	var cloneArray = function(arr) {
+		var clonedArray = [];
+		for (var i = 0; i < arr.length; i++) {
+			clonedArray[i] = arr[i];
+		}
+		return clonedArray;
+	}
+
+	var isFunction = function(f) {
+		if (!f){ return false; }
+		return (f instanceof Function || typeof f == "function");
+	};
+
+	// CSS Utilities
+	
+	function array_contains(arr, val) {
+		for (var i = 0, len = arr.length; i < len; i++) {
+			if (arr[i] === val) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	function addClass(el, cssClass) {
+		if (!hasClass(el, cssClass)) {
+			if (el.className) {
+				el.className += " " + cssClass;
+			} else {
+				el.className = cssClass;
+			}
+		}
+	}
+
+	function hasClass(el, cssClass) {
+		if (el.className) {
+			var classNames = el.className.split(" ");
+			return array_contains(classNames, cssClass);
+		}
+		return false;
+	}
+
+	function removeClass(el, cssClass) {
+		if (hasClass(el, cssClass)) {
+			// Rebuild the className property
+			var existingClasses = el.className.split(" ");
+			var newClasses = [];
+			for (var i = 0; i < existingClasses.length; i++) {
+				if (existingClasses[i] != cssClass) {
+					newClasses[newClasses.length] = existingClasses[i];
+				}
+			}
+			el.className = newClasses.join(" ");
+		}
+	}
+
+	function replaceClass(el, newCssClass, oldCssClass) {
+		removeClass(el, oldCssClass);
+		addClass(el, newCssClass);
+	}
+
+	function getExceptionStringRep(ex) {
+		if (ex) {
+			var exStr = "Exception: ";
+			if (ex.message) {
+				exStr += ex.message;
+			} else if (ex.description) {
+				exStr += ex.description;
+			}
+			if (ex.lineNumber) {
+				exStr += " on line number " + ex.lineNumber;
+			}
+			if (ex.fileName) {
+				exStr += " in file " + ex.fileName;
+			}
+			return exStr;
+		}
+		return null;
+	}
+
+
+	/* ---------------------------------------------------------------------- */
+
+	/* Configure the test logger try to use FireBug */
+	var log, error;
+	if (window["console"] && typeof console.log == "function") {
+		log = function() {
+			if (xn.test.enableTestDebug) {
+				console.log.apply(console, arguments);
+			}
+		};
+		error = function() {
+			if (xn.test.enableTestDebug) {
+				console.error.apply(console, arguments);
+			}
+		};
+	} else {
+		log = function() {};
+	}
+
+	/* Set up something to report to */
+
+	var initialized = false;
+	var container;
+	var progressBarContainer, progressBar, overallSummaryText;
+	var currentTest = null;
+	var suites = [];
+	var totalTestCount = 0;
+	var currentTestIndex = 0;
+	var testFailed = false;
+	var testsPassedCount = 0;
+	var startTime;
+	
+	var log4javascriptEnabled = false;
+	
+	var nextSuiteIndex = 0;
+	
+	function runNextSuite() {
+		if (nextSuiteIndex < suites.length) {
+			suites[nextSuiteIndex++].run();
+		}
+	}
+	
+	var init = function() {
+		if (initialized) { return true; }
+		
+		container = document.createElement("div");
+		
+		// Create the overall progress bar
+		progressBarContainer = container.appendChild(document.createElement("div"));
+		progressBarContainer.className = "xn_test_progressbar_container xn_test_overallprogressbar_container";
+		progressBar = progressBarContainer.appendChild(document.createElement("div"));
+		progressBar.className = "success";
+
+		document.body.appendChild(container);
+
+		var h1 = progressBar.appendChild(document.createElement("h1"));
+		overallSummaryText = h1.appendChild(document.createTextNode(""));
+
+		initialized = true;
+		
+		// Set up logging
+		log4javascriptEnabled = !!log4javascript && xn.test.enable_log4javascript;
+		
+		function TestLogAppender() {}
+		
+		if (log4javascriptEnabled) {
+			TestLogAppender.prototype = new log4javascript.Appender();
+			TestLogAppender.prototype.layout = new log4javascript.PatternLayout("%d{HH:mm:ss,SSS} %-5p %m");
+			TestLogAppender.prototype.append = function(loggingEvent) {
+				var formattedMessage = this.getLayout().format(loggingEvent);
+				if (this.getLayout().ignoresThrowable()) {
+					formattedMessage += loggingEvent.getThrowableStrRep();
+				}
+				currentTest.addLogMessage(formattedMessage);
+			};
+			
+			var appender = new TestLogAppender();
+			appender.setThreshold(log4javascript.Level.ALL);
+			log4javascript.getRootLogger().addAppender(appender);
+			log4javascript.getRootLogger().setLevel(log4javascript.Level.ALL);
+		}
+
+		startTime = new Date();
+
+		// First, build each suite
+		for (var i = 0; i < suites.length; i++) {
+			suites[i].build();
+			totalTestCount += suites[i].tests.length;
+		}
+		
+		// Now run each suite
+		runNextSuite();
+	};
+	
+	function updateProgressBar() {
+		progressBar.style.width = "" + parseInt(100 * (currentTestIndex) / totalTestCount) + "%";
+		var s = (totalTestCount === 1) ? "" : "s";
+		var timeTaken = new Date().getTime() - startTime.getTime();
+		overallSummaryText.nodeValue = "" + testsPassedCount + " of " + totalTestCount + " test" + s + " passed in " + timeTaken + "ms";
+	}
+
+	addEventListener(window, "load", init);
+
+	/* ---------------------------------------------------------------------- */
+
+	/* Test Suite */
+	var Suite = function(name, callback, hideSuccessful) {
+		this.name = name;
+		this.callback = callback;
+		this.hideSuccessful = hideSuccessful;
+		this.tests = [];
+		this.log = log;
+		this.error = error;
+		this.expanded = true;
+		suites.push(this);
+	}
+
+	Suite.prototype.test = function(name, callback, setUp, tearDown) {
+		this.log("adding a test named " + name)
+		var t = new Test(name, callback, this, setUp, tearDown);
+		this.tests.push(t);
+	};
+
+	Suite.prototype.build = function() {
+		// Build the elements used by the suite
+		var suite = this;
+		this.testFailed = false;
+		this.container = document.createElement("div");
+		this.container.className = "xn_test_suite_container";
+
+		var heading = document.createElement("h2");
+		this.expander = document.createElement("span");
+		this.expander.className = "xn_test_expander";
+		this.expander.onclick = function() {
+			if (suite.expanded) {
+				suite.collapse();
+			} else {
+				suite.expand();
+			}
+		};
+		heading.appendChild(this.expander);
+		
+		this.headingTextNode = document.createTextNode(this.name);
+		heading.appendChild(this.headingTextNode);
+		this.container.appendChild(heading);
+
+		this.reportContainer = document.createElement("dl");
+		this.container.appendChild(this.reportContainer);
+
+		this.progressBarContainer = document.createElement("div");
+		this.progressBarContainer.className = "xn_test_progressbar_container";
+		this.progressBar = document.createElement("div");
+		this.progressBar.className = "success";
+		this.progressBar.innerHTML = "&nbsp;";
+		this.progressBarContainer.appendChild(this.progressBar);
+		this.reportContainer.appendChild(this.progressBarContainer);
+
+		this.expand();
+
+		container.appendChild(this.container);
+
+		// invoke callback to build the tests
+		this.callback.apply(this, [this]);
+	};
+
+	Suite.prototype.run = function() {
+		this.log("running suite '%s'", this.name)
+		this.startTime = new Date();
+
+		// now run the first test
+		this._currentIndex = 0;
+		this.runNextTest();
+	};
+
+	Suite.prototype.updateProgressBar = function() {
+		// Update progress bar
+		this.progressBar.style.width = "" + parseInt(100 * (this._currentIndex) / this.tests.length) + "%";
+		//log(this._currentIndex + ", " + this.tests.length + ", " + progressBar.style.width + ", " + progressBar.className);
+	};
+
+	Suite.prototype.expand = function() {
+		this.expander.innerHTML = "-";
+		replaceClass(this.reportContainer, "xn_test_expanded", "xn_test_collapsed");
+		this.expanded = true;
+	};
+
+	Suite.prototype.collapse = function() {
+		this.expander.innerHTML = "+";
+		replaceClass(this.reportContainer, "xn_test_collapsed", "xn_test_expanded");
+		this.expanded = false;
+	};
+
+	Suite.prototype.finish = function(timeTaken) {
+		var newClass = this.testFailed ? "xn_test_suite_failure" : "xn_test_suite_success";
+		var oldClass = this.testFailed ? "xn_test_suite_success" : "xn_test_suite_failure";
+		replaceClass(this.container, newClass, oldClass);
+
+		this.headingTextNode.nodeValue += " (" + timeTaken + "ms)";
+
+		if (this.hideSuccessful && !this.testFailed) {
+			this.collapse();
+		}
+		runNextSuite();
+	};
+
+	/**
+	 * Works recursively with external state (the next index)
+	 * so that we can handle async tests differently
+	 */
+	Suite.prototype.runNextTest = function() {
+		if (this._currentIndex == this.tests.length) {
+			// finished!
+			var timeTaken = new Date().getTime() - this.startTime.getTime();
+
+			this.finish(timeTaken);
+			return;
+		}
+
+		var suite = this;
+		var t = this.tests[this._currentIndex++];
+		currentTestIndex++;
+
+		if (isFunction(suite.setUp)) {
+			suite.setUp.apply(suite, [t]);
+		}
+		if (isFunction(t.setUp)) {
+			t.setUp.apply(t, [t]);
+		}
+
+		t._run();
+		
+		function afterTest() {
+			if (isFunction(suite.tearDown)) {
+				suite.tearDown.apply(suite, [t]);
+			}
+			if (isFunction(t.tearDown)) {
+				t.tearDown.apply(t, [t]);
+			}
+			suite.log("finished test [%s]", t.name);
+			updateProgressBar();
+			suite.updateProgressBar();
+			suite.runNextTest();
+		}
+		
+		if (t.isAsync) {
+			t.whenFinished = afterTest;
+		} else {
+			setTimeout(afterTest, 1);
+		}
+	};
+
+	Suite.prototype.reportSuccess = function() {
+	};
+
+	/* ---------------------------------------------------------------------- */
+	/**
+	 * Create a new test
+	 */
+	var Test = function(name, callback, suite, setUp, tearDown) {
+		this.name = name;
+		this.callback = callback;
+		this.suite = suite;
+		this.setUp = setUp;
+		this.tearDown = tearDown;
+		this.log = log;
+		this.error = error;
+		this.assertCount = 0;
+		this.logMessages = [];
+		this.logExpanded = false;
+	};
+
+	/**
+	 * Default success reporter, please override
+	 */
+	Test.prototype.reportSuccess = function(name, timeTaken) {
+		/* default success reporting handler */
+		this.reportHeading = document.createElement("dt");
+		var text = this.name + " passed in " + timeTaken + "ms";
+		
+		this.reportHeading.appendChild(document.createTextNode(text));
+
+		this.reportHeading.className = "success";
+		var dd = document.createElement("dd");
+		dd.className = "success";
+
+		this.suite.reportContainer.appendChild(this.reportHeading);
+		this.suite.reportContainer.appendChild(dd);
+		this.createLogReport();
+	};
+
+	/**
+	 * Cause the test to immediately fail
+	 */
+	Test.prototype.reportFailure = function(name, msg, ex) {
+		this.suite.testFailed = true;
+		this.suite.progressBar.className = "failure";
+		progressBar.className = "failure";
+		this.reportHeading = document.createElement("dt");
+		this.reportHeading.className = "failure";
+		var text = document.createTextNode(this.name);
+		this.reportHeading.appendChild(text);
+
+		var dd = document.createElement("dd");
+		dd.appendChild(document.createTextNode(msg));
+		dd.className = "failure";
+
+		this.suite.reportContainer.appendChild(this.reportHeading);
+		this.suite.reportContainer.appendChild(dd);
+		if (ex && ex.stack) {
+			var stackTraceContainer = this.suite.reportContainer.appendChild(document.createElement("code"));
+			stackTraceContainer.className = "xn_test_stacktrace";
+			stackTraceContainer.innerHTML = ex.stack.replace(/\r/g, "\n").replace(/\n{1,2}/g, "<br />");
+		}
+		this.createLogReport();
+	};
+	
+	Test.prototype.createLogReport = function() {
+		if (this.logMessages.length > 0) {
+			this.reportHeading.appendChild(document.createTextNode(" ("));
+			var logToggler = this.reportHeading.appendChild(document.createElement("a"));
+			logToggler.href = "#";
+			logToggler.innerHTML = "show log";
+			var test = this;
+			
+			logToggler.onclick = function() {
+				if (test.logExpanded) {
+					test.hideLogReport();
+					this.innerHTML = "show log";
+					test.logExpanded = false;
+				} else {
+					test.showLogReport();
+					this.innerHTML = "hide log";
+					test.logExpanded = true;
+				}
+				return false;
+			};
+
+			this.reportHeading.appendChild(document.createTextNode(")"));
+			
+			// Create log report
+			this.logReport = this.suite.reportContainer.appendChild(document.createElement("pre"));
+			this.logReport.style.display = "none";
+			this.logReport.className = "xn_test_log_report";
+			var logMessageDiv;
+			for (var i = 0, len = this.logMessages.length; i < len; i++) {
+				logMessageDiv = this.logReport.appendChild(document.createElement("div"));
+				logMessageDiv.appendChild(document.createTextNode(this.logMessages[i]));
+			}
+		}
+	};
+
+	Test.prototype.showLogReport = function() {
+		this.logReport.style.display = "inline-block";
+	};
+		
+	Test.prototype.hideLogReport = function() {
+		this.logReport.style.display = "none";
+	};
+
+	Test.prototype.async = function(timeout, callback) {
+		timeout = timeout || 250;
+		var self = this;
+		var timedOutFunc = function() {
+			if (!self.completed) {
+				var message = (typeof callback === "undefined") ?
+							"Asynchronous test timed out" : callback(self);
+				self.fail(message);
+			}
+		}
+		var timer = setTimeout(function () { timedOutFunc.apply(self, []); }, timeout)
+		this.isAsync = true;
+	};
+
+	/**
+	 * Run the test
+	 */
+	Test.prototype._run = function() {
+		this.log("starting test [%s]", this.name);
+		this.startTime = new Date();
+		currentTest = this;
+		try {
+			this.callback(this);
+			if (!this.completed && !this.isAsync) {
+				this.succeed();
+			}
+		} catch (e) {
+			this.log("test [%s] threw exception [%s]", this.name, e);
+			var s = (this.assertCount === 1) ? "" : "s";
+			this.fail("Exception thrown after " + this.assertCount + " successful assertion" + s + ": " + getExceptionStringRep(e), e);
+		}
+	};
+
+	/**
+	 * Cause the test to immediately succeed
+	 */
+	Test.prototype.succeed = function() {
+		if (this.completed) { return false; }
+		// this.log("test [%s] succeeded", this.name);
+		this.completed = true;
+		var timeTaken = new Date().getTime() - this.startTime.getTime();
+		testsPassedCount++;
+		this.reportSuccess(this.name, timeTaken);
+		if (this.whenFinished) {
+			this.whenFinished();
+		}
+	};
+
+	Test.prototype.fail = function(msg, ex)	{
+		if (typeof msg != "string") {
+			msg = getExceptionStringRep(msg);
+		}
+		if (this.completed) { return false; }
+		this.completed = true;
+		// this.log("test [%s] failed", this.name);
+		this.reportFailure(this.name, msg, ex);
+		if (this.whenFinished) {
+			this.whenFinished();
+		}
+	};
+	
+	Test.prototype.addLogMessage = function(logMessage) {
+		this.logMessages.push(logMessage);
+	};
+
+	/* assertions */
+	var displayStringForValue = function(obj) {
+		if (obj === null) {
+			return "null";
+		} else if (typeof obj === "undefined") {
+			return "undefined";
+		}
+		return obj.toString();
+	};
+
+	var assert = function(args, expectedArgsCount, testFunction, defaultComment) {
+		this.assertCount++;
+		var comment = defaultComment;
+		var i;
+		var success;
+		var values = [];
+		if (args.length == expectedArgsCount) {
+			for (i = 0; i < args.length; i++) {
+				values[i] = args[i];
+			}
+		} else if (args.length == expectedArgsCount + 1) {
+			comment = args[0];
+			for (i = 1; i < args.length; i++) {
+				values[i - 1] = args[i];
+			}
+		} else {
+			throw new Error("Invalid number of arguments passed to assert function");
+		}
+		success = testFunction(values);
+		if (!success) {
+			var regex = /\{([0-9]+)\}/;
+			while (regex.test(comment)) {
+				comment = comment.replace(regex, displayStringForValue(values[parseInt(RegExp.$1)]));
+			}
+			this.fail("Test failed on assertion " + this.assertCount + ": " + comment);
+		}
+	};
+
+	var testNull = function(values) {
+		return (values[0] === null);
+	};
+
+	Test.prototype.assertNull = function() {
+		assert.apply(this, [arguments, 1, testNull, "Expected to be null but was {0}"]);
+	}
+
+	var testNotNull = function(values) {
+		return (values[0] !== null);
+	};
+
+	Test.prototype.assertNotNull = function() {
+		assert.apply(this, [arguments, 1, testNotNull, "Expected not to be null but was {0}"]);
+	}
+
+	var testBoolean = function(values) {
+		return (Boolean(values[0]));
+	};
+
+	Test.prototype.assert = function() {
+		assert.apply(this, [arguments, 1, testBoolean, "Expected not to be equivalent to false"]);
+	};
+
+	var testTrue = function(values) {
+		return (values[0] === true);
+	};
+
+	Test.prototype.assertTrue = function() {
+		assert.apply(this, [arguments, 1, testTrue, "Expected to be true but was {0}"]);
+	};
+
+	Test.prototype.assert = function() {
+		assert.apply(this, [arguments, 1, testTrue, "Expected to be true but was {0}"]);
+	};
+
+	var testFalse = function(values) {
+		return (values[0] === false);
+	};
+
+	Test.prototype.assertFalse = function() {
+		assert.apply(this, [arguments, 1, testFalse, "Expected to be false but was {0}"]);
+	}
+
+	var testEquivalent = function(values) {
+		return (values[0] === values[1]);
+	};
+
+	Test.prototype.assertEquivalent = function() {
+		assert.apply(this, [arguments, 2, testEquivalent, "Expected to be equal but values were {0} and {1}"]);
+	}
+
+	var testNotEquivalent = function(values) {
+		return (values[0] !== values[1]);
+	};
+
+	Test.prototype.assertNotEquivalent = function() {
+		assert.apply(this, [arguments, 2, testNotEquivalent, "Expected to be not equal but values were {0} and {1}"]);
+	}
+
+	var testEquals = function(values) {
+		return (values[0] == values[1]);
+	};
+
+	Test.prototype.assertEquals = function() {
+		assert.apply(this, [arguments, 2, testEquals, "Expected to be equal but values were {0} and {1}"]);
+	}
+
+	var testNotEquals = function(values) {
+		return (values[0] != values[1]);
+	};
+
+	Test.prototype.assertNotEquals = function() {
+		assert.apply(this, [arguments, 2, testNotEquals, "Expected to be not equal but values were {0} and {1}"]);
+	}
+
+	var testRegexMatches = function(values) {
+		return (values[0].test(values[1]));
+	};
+
+	Test.prototype.assertRegexMatches = function() {
+		assert.apply(this, [arguments, 2, testRegexMatches, "Expected regex {0} to match value {1} but it didn't"]);
+	}
+
+	Test.prototype.assertError = function(f, errorType) {
+		try {
+			f();
+			this.fail("Expected error to be thrown");
+		} catch (e) {
+			if (errorType && (!(e instanceof errorType))) {
+				this.fail("Expected error of type " + errorType + " to be thrown but error thrown was " + e);
+			}
+		}
+	};
+
+	/**
+	 * Execute a synchronous test
+	 */
+	xn.test = function(name, callback) {
+		xn.test.suite("Anonymous", function(s) {
+			s.test(name, callback);
+		});
+	}
+
+	/**
+	 * Create a test suite with a given name
+	 */
+	xn.test.suite = function(name, callback, hideSuccessful) {
+		var s = new Suite(name, callback, hideSuccessful);
+	}
+})();
\ No newline at end of file