// XMLHttpRequest.js Copyright (C) 2008 Sergey Ilinsky (http://www.ilinsky.com)
//
// This work is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.

// This work is distributed in the hope that it will be useful,
// but without any warranty; without even the implied warranty of
// merchantability or fitness for a particular purpose. See the
// GNU Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

(function () {

	// Save reference to earlier defined object implementation (if any)
	var oXMLHttpRequest	= window.XMLHttpRequest;

	// Define on browser type
	var bGecko	= !!window.controllers,
		bIE		= window.document.all && !window.opera;

	// Constructor
	function cXMLHttpRequest() {
		this._object	= oXMLHttpRequest ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
		this._listeners	= [];
	};

	// BUGFIX: Firefox with Firebug installed would break pages if not executed
	if (bGecko && oXMLHttpRequest.wrapped)
		cXMLHttpRequest.wrapped	= oXMLHttpRequest.wrapped;

	// Constants
	cXMLHttpRequest.UNSENT				= 0;
	cXMLHttpRequest.OPENED				= 1;
	cXMLHttpRequest.HEADERS_RECEIVED	= 2;
	cXMLHttpRequest.LOADING				= 3;
	cXMLHttpRequest.DONE				= 4;

	// Public Properties
	cXMLHttpRequest.prototype.readyState	= cXMLHttpRequest.UNSENT;
	cXMLHttpRequest.prototype.responseText	= '';
	cXMLHttpRequest.prototype.responseXML	= null;
	cXMLHttpRequest.prototype.status		= 0;
	cXMLHttpRequest.prototype.statusText	= '';

	// Instance-level Events Handlers
	cXMLHttpRequest.prototype.onreadystatechange	= null;

	// Class-level Events Handlers
	cXMLHttpRequest.onreadystatechange	= null;
	cXMLHttpRequest.onopen				= null;
	cXMLHttpRequest.onsend				= null;
	cXMLHttpRequest.onabort				= null;

	// Public Methods
	cXMLHttpRequest.prototype.open	= function(sMethod, sUrl, bAsync, sUser, sPassword) {

		// When bAsync parameter value is ommited, use true as default
		if (arguments.length < 3)
			bAsync	= true;

		// Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
		this._async		= bAsync;

		// Set the onreadystatechange handler
		var oRequest	= this,
			nState		= this.readyState;

		// BUGFIX: IE - memory leak on page unload (inter-page leak)
		if (bIE) {
			var fOnUnload	= function() {
				if (oRequest._object.readyState != cXMLHttpRequest.DONE) {
					fCleanTransport(oRequest);
					// Safe to abort here since onreadystatechange handler removed
					oRequest.abort();
				}
			};
			if (bAsync)
				window.attachEvent("onunload", fOnUnload);
		}

		this._object.onreadystatechange	= function() {
			if (bGecko && !bAsync)
				return;

			// Synchronize state
			oRequest.readyState		= oRequest._object.readyState;

			//
			fSynchronizeValues(oRequest);

			// BUGFIX: Firefox fires unneccesary DONE when aborting
			if (oRequest._aborted) {
				// Reset readyState to UNSENT
				oRequest.readyState	= cXMLHttpRequest.UNSENT;

				// Return now
				return;
			}

			if (oRequest.readyState == cXMLHttpRequest.DONE) {
				//
				fCleanTransport(oRequest);
// Uncomment this block if you need a fix for IE cache
/*
				// BUGFIX: IE - cache issue
				if (!oRequest._object.getResponseHeader("Date")) {
					// Save object to cache
					oRequest._cached	= oRequest._object;

					// Instantiate a new transport object
					cXMLHttpRequest.call(oRequest);

					// Re-send request
					oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
					oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
					// Copy headers set
					if (oRequest._headers)
						for (var sHeader in oRequest._headers)
							if (typeof oRequest._headers[sHeader] == "string")	// Some frameworks prototype objects with functions
								oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);

					oRequest._object.onreadystatechange	= function() {
						// Synchronize state
						oRequest.readyState		= oRequest._object.readyState;

						if (oRequest._aborted) {
							//
							oRequest.readyState	= cXMLHttpRequest.UNSENT;

							// Return
							return;
						}

						if (oRequest.readyState == cXMLHttpRequest.DONE) {
							// Clean Object
							fCleanTransport(oRequest);

							// get cached request
							if (oRequest.status == 304)
								oRequest._object	= oRequest._cached;

							//
							delete oRequest._cached;

							//
							fSynchronizeValues(oRequest);

							//
							fReadyStateChange(oRequest);

							// BUGFIX: IE - memory leak in interrupted
							if (bIE && bAsync)
								window.detachEvent("onunload", fOnUnload);
						}
					};
					oRequest._object.send(null);

					// Return now - wait untill re-sent request is finished
					return;
				};
*/
				// BUGFIX: IE - memory leak in interrupted
				if (bIE && bAsync)
					window.detachEvent("onunload", fOnUnload);
			}

			// BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
			if (nState != oRequest.readyState)
				fReadyStateChange(oRequest);

			nState	= oRequest.readyState;
		};

		// Add method sniffer
		if (cXMLHttpRequest.onopen)
			cXMLHttpRequest.onopen.apply(this, arguments);

		if (arguments.length > 4)
			this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
		else
		if (arguments.length > 3)
			this._object.open(sMethod, sUrl, bAsync, sUser);
		else
			this._object.open(sMethod, sUrl, bAsync);

		// BUGFIX: Gecko - missing readystatechange calls in synchronous requests
		if (!bAsync && bGecko) {
			this.readyState	= cXMLHttpRequest.OPENED;

			fReadyStateChange(this);
		}
	};
	cXMLHttpRequest.prototype.send	= function(vData) {
		// Add method sniffer
		if (cXMLHttpRequest.onsend)
			cXMLHttpRequest.onsend.apply(this, arguments);

		// Be compatible with Prototype and other libraries
		this._object.setRequestHeader("X-Requested-With", "XMLHttpRequest");

		// BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
		// BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
		// BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
		if (vData && vData.nodeType) {
			vData	= window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
			if (!this._headers["Content-Type"])
				this._object.setRequestHeader("Content-Type", "application/xml");
		}

		this._object.send(vData);

		// BUGFIX: Gecko - missing readystatechange calls in synchronous requests
		if (bGecko && !this._async) {
			this.readyState	= cXMLHttpRequest.OPENED;

			// Synchronize state
			fSynchronizeValues(this);

			// Simulate missing states
			while (this.readyState < cXMLHttpRequest.DONE) {
				this.readyState++;
				fReadyStateChange(this);
				// Check if we are aborted
				if (this._aborted)
					return;
			}
		}
	};
	cXMLHttpRequest.prototype.abort	= function() {
		// Add method sniffer
		if (cXMLHttpRequest.onabort)
			cXMLHttpRequest.onabort.apply(this, arguments);

		// BUGFIX: Gecko - unneccesary DONE when aborting
		if (this.readyState > cXMLHttpRequest.UNSENT)
			this._aborted	= true;

		this._object.abort();

		// BUGFIX: IE - memory leak
		fCleanTransport(this);
	};
	cXMLHttpRequest.prototype.getAllResponseHeaders	= function() {
		return this._object.getAllResponseHeaders();
	};
	cXMLHttpRequest.prototype.getResponseHeader	= function(sName) {
		return this._object.getResponseHeader(sName);
	};
	cXMLHttpRequest.prototype.setRequestHeader	= function(sName, sValue) {
		// BUGFIX: IE - cache issue
		if (!this._headers)
			this._headers	= {};
		this._headers[sName]	= sValue;

		return this._object.setRequestHeader(sName, sValue);
	};

	// EventTarget interface implementation
	cXMLHttpRequest.prototype.addEventListener	= function(sName, fHandler, bUseCapture) {
		for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
			if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
				return;
		// Add listener
		this._listeners.push([sName, fHandler, bUseCapture]);
	};

	cXMLHttpRequest.prototype.removeEventListener	= function(sName, fHandler, bUseCapture) {
		for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
			if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
				break;
		// Remove listener
		if (oListener)
			this._listeners.splice(nIndex, 1);
	};

	cXMLHttpRequest.prototype.dispatchEvent	= function(oEvent) {
		var oEvent	= {
			'type':			oEvent.type,
			'target':		this,
			'currentTarget':this,
			'eventPhase':	2,
			'bubbles':		oEvent.bubbles,
			'cancelable':	oEvent.cancelable,
			'timeStamp':	oEvent.timeStamp,
			'stopPropagation':	function() {},	// There is no flow
			'preventDefault':	function() {},	// There is no default action
			'initEvent':		function() {}	// Original event object should be inited
		};

		// Execute onreadystatechange
		if (oEvent.type == "readystatechange" && this.onreadystatechange)
			(this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEvent]);

		// Execute listeners
		for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
			if (oListener[0] == oEvent.type && !oListener[2])
				(oListener[1].handleEvent || oListener[1]).apply(this, [oEvent]);
	};

	//
	cXMLHttpRequest.prototype.toString	= function() {
		return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
	};

	cXMLHttpRequest.toString	= function() {
		return '[' + "XMLHttpRequest" + ']';
	};

	// Helper function
	function fReadyStateChange(oRequest) {
		// Sniffing code
		if (cXMLHttpRequest.onreadystatechange)
			cXMLHttpRequest.onreadystatechange.apply(oRequest);

		// Fake event
		oRequest.dispatchEvent({
			'type':			"readystatechange",
			'bubbles':		false,
			'cancelable':	false,
			'timeStamp':	new Date + 0
		});
	};

	function fGetDocument(oRequest) {
		var oDocument	= oRequest.responseXML;
		// Try parsing responseText
		if (bIE && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
			oDocument	= new window.ActiveXObject("Microsoft.XMLDOM");
			oDocument.loadXML(oRequest.responseText);
		}
		// Check if there is no error in document
		if (oDocument)
			if ((bIE && oDocument.parseError != 0) || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
				return null;
		return oDocument;
	};

	function fSynchronizeValues(oRequest) {
		try {	oRequest.responseText	= oRequest._object.responseText;	} catch (e) {}
		try {	oRequest.responseXML	= fGetDocument(oRequest._object);	} catch (e) {}
		try {	oRequest.status			= oRequest._object.status;			} catch (e) {}
		try {	oRequest.statusText		= oRequest._object.statusText;		} catch (e) {}
	};

	function fCleanTransport(oRequest) {
		// BUGFIX: IE - memory leak (on-page leak)
		oRequest._object.onreadystatechange	= new window.Function;

		// Delete private properties
		delete oRequest._headers;
	};

	// Internet Explorer 5.0 (missing apply)
	if (!window.Function.prototype.apply) {
		window.Function.prototype.apply	= function(oRequest, oArguments) {
			if (!oArguments)
				oArguments	= [];
			oRequest.__func	= this;
			oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
			delete oRequest.__func;
		};
	};

	// Register new object with window
	window.XMLHttpRequest	= cXMLHttpRequest;
})();

/*jslint bitwise: true, browser: true, eqeqeq: true, evil: true, immed: true, newcap: true, nomen: true, onevar: true, plusplus: true, regexp: true, undef: true, white: true, indent: 4 */
/* Google adsense variables */
var google_ad_client, google_ad_slot, google_ad_width, google_ad_height;
/*global window pageTracker _gat */
/* Help IE6 */
document.createElement("abbr");

/* Array extras */

/**
 * Returns the first index at which a given element can be found in the array,
 * or -1 if it is not present.
 * indexOf compares searchElement to elements of the Array using strict equality
 * (the same method used by the ===, or triple-equals, operator).
 *
 * @param {Object} searchElement Element to locate in the array.
 * @return {Number} The first index at which a given element can be found in the array, or -1 if it is not present.
 */
if (!Array.prototype.indexOf) {
	Array.prototype.indexOf = function (searchElement) {
		var from = Number(arguments[1]) || 0, len = this.length;
		for (from = from < 0 ? Math.ceil(from) + len : Math.floor(from); from < len; from += 1) {
			if (from in this && this[from] === searchElement) {
				return from;
			}
		}
		return -1;
	};
}

/**
 * Executes a provided function once per array element. The callback is invoked
 * only for indexes of the array which have assigned values; it is not invoked
 * for indexes which have been deleted or which have never been assigned values.
 *
 * @param  callback Function to execute for each element.
 */
if (!Array.prototype.forEach) {
	Array.prototype.forEach = function (callback) {
		var i = 0, len = this.length, thisp = arguments[1];
		for (; i < len; i += 1) {
			if (i in this) {
				callback.call(thisp, this[i], i, this);
			}
		}
	};
}

if (!Array.prototype.filter) {
	Array.prototype.filter = function (func, thisVal) {
		var len = this.length, ret = [], val, i = null;
		for (i = 0; i < len; i += 1) {
			val = this[i] || this.charAt && this.charAt(i);
			if (func.call(thisVal, val, i, this)) {
				ret[ret.length] = val;
			}
		}
		return ret;
	};
}


/* UEH namespace */
var UEH = {};

/* Date extensions */
UEH.Date = {};

/* Number extensions */
UEH.Number = {};

/* HTMLElement extensions */
UEH.HTMLElement = {};

/* Application */
UEH.Application = {};

/* Add special stylesheet to the document to support some of the scripts */
UEH.Application.javascriptCSS = function javascriptCSS() {
	window.setTimeout(function () {
		var node = document.createElement("link");
		node.href = (document.location.hostname === "unitedelementsofhate.net" ? "http://assets.unitedelementsofhate.net" : "") + "/stylesheets/javascript.css";
		node.media = "all";
		node.rel = "stylesheet";
		node.type = "text/css";
		document.documentElement.firstChild.appendChild(node);
	}, 0);
};
UEH.Application.javascriptCSS();

/**
 * Loosly parse the W3C defined subset of ISO 8601 (http://www.w3.org/TR/NOTE-datetime)
 * The used regex allows the time part to omit the timezone designation.
 *
 * @param  {String} str A string representing a date
 * @return {Date}       The parsed date or null of parsing was not possible.
 */
UEH.Date.parseISO8601 = function parseISO8601(str) {
	var matcher, matches, YYYY, MM, DD, hh, mm, ss, s, offset, parsedDate = null;

	// Description of the regex:
	// (\d{4})  Year, exactly 4 digits, captured as group $1
	// (?:-(0[1-9]|1[0-2])  Month, a dash followed by 01..12. The numbers representing the month is group $2
	// (?:-(0[1-9]|[12]\d|3[01])  Day, a dash followed by 01..31.  The numbers representing the day is group $3
	// (?:T  Start of the time part
	// ([01]\d|2[0-3]):([0-5]\d)  Hour (00...23) and minutes (00..59) separated by a colon (:). Group $4 and $5
	// (?::([0-5]\d)  A colon followed by seconds (00..59) available as group $6
	// (?:\.(\d+))?)? A dot followed by one or more numbers representing microseconds. Group $7
	// (?:(Z|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))?)? Either a Z or the timezone offset. Group $8
	// If there is a timezone offset (group $8 isn't the letter Z) group $9 is either a plus or a minus,
	// group $10 and $11 contain the hour and minute part of the offset.
	matcher = /^(\d{4})(?:-(0[1-9]|1[0-2])(?:-(0[1-9]|[12]\d|3[01])(?:T([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d)(?:\.(\d+))?)?(?:(Z|([+\-])([01][0-9]|2[0-3]):([0-5][0-9]))?)?)?)?)?$/;

	if (matcher.test(str) === true) {
		matches = str.match(matcher);
		YYYY = matches[1];
		MM = matches[2] ? matches[2] - 1 : 0;
		DD = matches[3] ? matches[3] : 1;
		hh = matches[4] ? matches[4] : 0;
		mm = matches[5] ? matches[5] : 0;
		ss = matches[6] ? matches[6] : 0;
		s  = matches[7] ? matches[7].substr(0, 3) : 0; // Maximum precision of miliseconds in javascript is 3.

		parsedDate = new Date();
		parsedDate.setTime(Date.UTC(YYYY, MM, DD, hh, mm, ss, s));
		if (matches[8] && matches[8] !== "Z") {
			offset = 0;
			offset = (window.parseInt(matches[10], 10) * 60 + window.parseInt(matches[11], 10)) * 60;
			if (matches[9] === "+") {
				offset *= -1;
			}
			parsedDate.setTime(parsedDate.getTime() + offset * 1000);
		}
	}

	return parsedDate;
};

/**
 * Format a string according to a given formatting string containing format specifiers.
 * These format spcifiers are replaced by the function to the corresponding values to
 * represent the time specified in date. They all begin with a percentage (%) sign, and are:
 * %a           Abbreviated weekday name                             (Thu).
 * %A           Full weekday name                               (Thursday).
 * %b           Abbreviated month name                               (Aug).
 * %B           Full month name                                   (August).
 * %c           Date and time representation    (Thu Aug 23 14:55:02 2001).
 * %d           Day of the month (01-31)                              (23).
 * %H           Hour in 24h format (00-23)                            (14).
 * %I           Hour in 12h format (01-12)                            (02).
 * %m           Month as a decimal number (01-12)                     (08).
 * %M           Minute (00-59)                                        (55).
 * %p           AM or PM designation                                  (PM).
 * %S           Second (00-61)                                        (02).
 * %w           Weekday as a decimal number with Sunday as 0 (0-6)     (4).
 * %y           Year, last two digits (00-99)                         (01).
 * %Y           Year                                                (2001).
 * %%           A % sign                                               (%).
 *
 * @param  {Date}   date         The date that needs to be formatted.
 * @param  {String} stringFormat Formatting string containing any combination of regular characters and special format specifiers.
 * @return {String}              The date formatted according to the formatting string.
 */
UEH.Date.format = function format(date, stringFormat) {
	var zeroPadded = ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09"],
	zeropad = function zeropad(n) {
		return zeroPadded[n] || n;
	},
	shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
	longDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
	shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
	longMonths = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
	formattingHelper = ["%a", "%A", "%b", "%B", "%c", "%d", "%H", "%I", "%m", "%M", "%p", "%S", "%w", "%y", "%Y", "%%"];
	UEH.Date.format = function format(date, stringFormat) {
		var i = -1, specifier, replacement = null;
		while ((specifier = formattingHelper[i += 1])) {
			if (stringFormat.indexOf(specifier) > -1) {
				switch (specifier) {
				case "%a":
					replacement = shortDays[date.getDay()];
					break;
				case "%A":
					replacement = longDays[date.getDay()];
					break;
				case "%b":
					replacement = shortMonths[date.getMonth()];
					break;
				case "%B":
					replacement = longMonths[date.getMonth()];
					break;
				case "%c":
					replacement = date.toString();
					break;
				case "%d":
					replacement = zeropad(date.getDate());
					break;
				case "%H":
					replacement = zeropad(date.getHours());
					break;
				case "%I":
					replacement = zeropad((date.getHours() + 12) % 12);
					break;
				case "%m":
					replacement = zeropad(date.getMonth() + 1);
					break;
				case "%M":
					replacement = zeropad(date.getMinutes());
					break;
				case "%p":
					replacement = date.getHours() < 12 ? "AM" : "PM";
					break;
				case "%S":
					replacement = zeropad(date.getSeconds());
					break;
				case "%w":
					replacement = date.getDay();
					break;
				case "%y":
					replacement = zeropad(date.getFullYear() % 100);
					break;
				case "%Y":
					replacement = date.getFullYear();
					break;
				case "%%":
					replacement = "%";
				}
				stringFormat = stringFormat.replace(specifier, replacement);
			}
		}
		return stringFormat;
	};

	return UEH.Date.format(date, stringFormat);
};

/**
* A human language approximation of the distance between two units of time.
* @param  {Date}   from  Point in time considered in the past relative to the reference point.
* @param  {Date}   to    Reference point, the time considered to be "now" to the observer.
* @return {String}       The approximate distance between two units of time.
*/
UEH.Date.humanizedTimeAgo = function humanizedTimeAgo(from, to) {
	var returnValue, x = Math.abs(Math.round((to - from) / 60000));
	if (x < 2) {
		returnValue = ((x - 1) % 2 ? "less than " : "") + "a minute";
	}
	else if (x < 45) {
		returnValue = x + " minutes";
	}
	else if (x < 90) {
		returnValue = "about 1 hour";
	}
	else if (x < 1440) {
		returnValue = "about " + Math.floor(x / 60) + " hours";
	}
	else if (x < 2880) {
		returnValue = "1 day";
	}
	else if (x < 43200) {
		returnValue = Math.floor(x / 1440) + " days";
	}
	else if (x < 86400) {
		returnValue = "about 1 month";
	}
	else if (x < 525600) {
		returnValue = Math.floor(x / 43200) + "minutes";
	}
	else if (x < 1051200) {
		returnValue = "about 1 year";
	}
	else {
		returnValue = "over " + Math.floor(x / 525600) + " years";
	}

	return returnValue + " ago";
};

/**
 * Ordinalize a given number.
 *
 * @param  {Number}  n  Number in need of ordinalization.
 * @return {String}     The given number plus st, nd, rd or th.
 */
UEH.Number.ordinalize =	function ordinalize(n) {
	n = window.parseInt(n, 10);
	if (!window.isNaN(n)) {
		if (11 <= n % 100 && n % 100 <= 13) {
			return n + "th";
		}
		switch (n % 10) {
		case  1:
			return n + "st";
		case  2:
			return n + "nd";
		case  3:
			return n + "rd";
		default:
			return n + "th";
		}
	}
	return n;
};

// Developed by Robert Nyman, http://www.robertnyman.com
// Code/licensing: http://code.google.com/p/getelementsbyclassname/
// Slightly adapted.
UEH.HTMLElement.getElementsByClassName = function getElementsByClassName(className, tag, elm) {
	// Native support
	if (document.getElementsByClassName) {
		UEH.HTMLElement.getElementsByClassName = function (className, tag, elm) {
			elm = elm || document;
			var elements = elm.getElementsByClassName(className),
			returnElements = Array.prototype.slice.call(elements, 0);
			if (tag && (tag = tag.toUpperCase())) {
				returnElements = returnElements.filter(function (value) {
					return value.nodeName === tag.toUpperCase();
				});
			}
			return returnElements;
		};
	}
	// Xpath
	else if (document.evaluate) {
		UEH.HTMLElement.getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
			classesToCheck = "",
			xhtmlNamespace = "http://www.w3.org/1999/xhtml",
			namespaceResolver = document.documentElement.namespaceURI === xhtmlNamespace ? xhtmlNamespace : null,
			returnElements = [],
			elements, node, i, currentClassName;
			for (i = 0; (currentClassName = classes[i]); i += 1) {
				classesToCheck += "[contains(concat(' ', @class, ' '), ' " + currentClassName + " ')]";
			}
			try	{
				elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
			}
			catch (e) {
				elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
			}
			while ((node = elements.iterateNext())) {
				returnElements.push(node);
			}
			return returnElements;
		};
	}
	// Itteration
	else {
		UEH.HTMLElement.getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
			classesToCheck = [],
			elements = tag === "*" && elm.all ? elm.all : elm.getElementsByTagName(tag),
			returnElements = [],
			i, j, current, currentClassName, match;
			for (i = 0; (currentClassName = classes[i]); i += 1) {
				classesToCheck.push(new RegExp("(^|\\s)" + currentClassName + "(\\s|$)"));
			}
			for (i = 0; (current = elements[i]); i += 1) {
				match = false;
				for (j = 0; (currentClassName = classesToCheck[j]); j += 1) {
					match = currentClassName.test(current.className);
					if (!match) {
						break;
					}
				}
				if (match) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}

	return UEH.HTMLElement.getElementsByClassName(className, tag, elm);
};

/**
 * Find a specific ancestor node by tag and/or classname.
 * @param {HTMLElement}  element
 * @param {String}       tagName
 * @param {String}       className
 *
 * @return {HTMLElement} The specified ancestor node or null if nothing is found.
 */
UEH.HTMLElement.findAncestor = function findAncestor(element) {
	var tagName = typeof arguments[1] !== "undefined" ?  arguments[1].toLowerCase() : null,
		className = arguments[2], ancestor = element;
	while ((ancestor = ancestor.parentNode) && ancestor.tagName !== "HTML") {
		if ((!tagName || ancestor.tagName.toLowerCase() === tagName) && (!className || UEH.HTMLElement.hasClass(ancestor, className))) {
			return ancestor;
		}
	}
	return null;
};

/**
 * Get the HTML5 proposed userdata attribute value.
 * @param element
 * @param name
 */
UEH.HTMLElement.dataset = function dataset(element, name) {
	var returnValue;
	// Try the new HTML5 proposed userdata attribute first.
	if (element.dataset) {
		returnValue = element.dataset[name];
	}
	else { // if (element.hasAttribute("data-gravatar")) { // cannot be used because of IE < 8
		returnValue = element.getAttribute("data-" + name);
	}
	return typeof returnValue === "string" && returnValue.length > 0 ? returnValue : null;
};

/**
 * @param el
 * @param type
 * @pram fn
 */
UEH.HTMLElement.addEvent = (function () {
	if (window.addEventListener) {
		return function (el, type, fn) {
			el.addEventListener(type, fn, false);
		};
	}
	else if (window.attachEvent) {
		return function (el, type, fn) {
			var f = function () {
				fn.call(el, window.event);
			};
			el.attachEvent('on' + type, f);
		};
	}
	return function () {};
}());

UEH.HTMLElement.hasClass = function hasClass(element, className) {
	return (new RegExp("(?:^|\\s)" + className + "(?:\\s|$)")).test(element.className);
};

UEH.HTMLElement.addClass = function addClass(element, className) {
	if (!UEH.HTMLElement.hasClass(element, className)) {
		element.className += " " + className;
	}
};

UEH.HTMLElement.removeClass = function removeClass(element, className) {
	element.className = element.className.replace(new RegExp("(?:^|\\s)" + className + "(?:\\s|$)", "g"), "");
};

/*global getElementsByClassName dataset addEvent hasClass addClass removeClass */
UEH.HTMLElement.exports = "var getElementsByClassName = UEH.HTMLElement.getElementsByClassName, dataset = UEH.HTMLElement.dataset, addEvent = UEH.HTMLElement.addEvent, hasClass = UEH.HTMLElement.hasClass, addClass = UEH.HTMLElement.addClass, removeClass = UEH.HTMLElement.removeClass;";

UEH.Application.log = function log(category, action, optional_label, optional_value) {
	if (pageTracker && pageTracker._trackEvent) {
		window.setTimeout(function () {
			pageTracker._trackEvent(category, action, optional_label, optional_value);
		}, 0);
	}
};

/**
 * Find nodes marked up using the datetime microformat and rewrite the time to
 * a prettier date, making use of the users timezone and showing a approximation
 * of passed time if the date is less than 24 hours ago.
 */
UEH.Application.prettyDates = function prettyDates() {
	var i, abbr, date,
	now = new Date(),
	abbrs = UEH.HTMLElement.getElementsByClassName("date", "abbr");

	for (i = 0; (abbr = abbrs[i]); i += 1) {
		if (abbr.firstChild) {
			date = UEH.Date.parseISO8601(abbr.title);
			if ((now - date) / 60000 < 24 * 60) {
				abbr.firstChild.data = UEH.Date.humanizedTimeAgo(date, now);
			}
			else {
				abbr.firstChild.data = UEH.Date.format(date, "%d %b %Y @ %H:%M")
				.replace(/ 0([0-9]):/, " $1:")
				.replace(/ 0([1-9])/, " $1");
			}
		}
	}
};

/**
 * Add gravatars to the message author display.
 */
UEH.Application.gravatars = function gravatars() {
	var url = /^url\("?([^")]+)"?\);?$/, 
	i = -1, author, authors, gravatar, fallback = null;

	authors = UEH.HTMLElement.getElementsByClassName("author");
	while ((author = authors[i += 1])) {
		if ((gravatar = UEH.HTMLElement.dataset(author, "gravatar"))) {
			if (!fallback || fallback !== "?s=80&r=r") {
				if (window.getComputedStyle) {
					fallback = document.defaultView.getComputedStyle(author, null).getPropertyValue("background-image");
				}
				else if (author.currentStyle) {
					fallback = author.currentStyle.backgroundImage;
				}
				fallback = fallback === "none" ? "?s=80&r=r" : "?s=80&r=r&d=" + fallback.replace(url, "$1");
			}
			author.style.backgroundImage = "url(http://www.gravatar.com/avatar/" + gravatar + fallback + ")";
		}
	}
};

/**
 * Help the user pick the right timezone
 */
UEH.Application.countryTimezoneSelectHelper = function countryTimezoneSelectHelper() {
	var countrySelect = document.getElementById("event_country"),
		timezoneSelect = document.getElementById("event_timezone_identifier"),
		selectedTimezone = null, container;
	
	// Exit early if there's something missing.
	if (!countrySelect || !timezoneSelect) {
		return;
	}
	// The allready selected timezone.
	selectedTimezone = timezoneSelect.value !== "" ? timezoneSelect.value : null;
	container = UEH.HTMLElement.findAncestor(timezoneSelect, "div");
	
	function clearTimezoneSelect() {
		while (timezoneSelect.options[0]) {
			timezoneSelect.remove(0);
		}
	}

	function selectCountryTimezone() {
		var i = 0, selected,
			timezones = UEH.HTMLElement.dataset(countrySelect.options[countrySelect.selectedIndex], "timezones").split(" ");
		container.style.display = "";
		timezoneSelect.disabled = false;
		clearTimezoneSelect();
		if (timezones.length === 1 || ["Chile", "Ecuador", "Portugal", "Spain"].indexOf(countrySelect.value) > -1) {
			timezoneSelect.options[0] = new Option(timezones[0].replace(/_/g, " "), timezones[0], true, true);
			timezoneSelect.disabled = true;
			container.style.display = "none";
		}
		else {
			if (timezones.indexOf(selectedTimezone) > -1) {
				selected = selectedTimezone;
			}
			else {
				// corrections for odd default timezones
				switch (timezones[0]) {
				case "Australia/Lord_Howe":
					selected = "Australia/Sydney";
					break;
				case "America/Noronha":
					selected = "America/Sao_Paulo";
					break;
				case "America/St_Johns":
					selected = "America/Toronto";
					break;
				case "Europe/Kaliningrad":
					selected = "Europe/Moscow";
					break;
				default:
					selected = timezones[0];
				}
			}
			timezones = timezones.sort();
			for (i = 0; i < timezones.length; i += 1) {
				timezoneSelect.options[timezoneSelect.options.length] = new Option(timezones[i].replace(/_/g, " "), timezones[i], timezones[i] === selected, timezones[i] === selected);
			}
		}
	}

	selectCountryTimezone();
	UEH.HTMLElement.addEvent(countrySelect, "change", selectCountryTimezone);
	UEH.HTMLElement.addEvent(countrySelect, "keypress", selectCountryTimezone);
};

UEH.Application.countryStateSelectHelper = function countryStateSelectHelper() {
	var countrySelect = document.getElementById("event_country"),
		stateSelect = document.getElementById("event_state"), container;

	// Exit early if there's something missing.
	if (!countrySelect || !stateSelect) {
		return;
	}
	container = UEH.HTMLElement.findAncestor(stateSelect, "div");

	function toggleStateSelect() {
		container.style.display = "none";
		stateSelect.disabled = true;
		if (countrySelect.value === "United States of America") {
			stateSelect.disabled = false;
			container.style.display = "";
		}
	}

	toggleStateSelect();
	UEH.HTMLElement.addEvent(countrySelect, "change", toggleStateSelect);
	UEH.HTMLElement.addEvent(countrySelect, "keypress", toggleStateSelect);
};

UEH.Application.tabbedFieldsets = function tabbedFieldsets(form) {
	eval(UEH.HTMLElement.exports);
	eval(UEH.Application.exports);

	var fieldsets = form.getElementsByTagName("fieldset"), heights = [],
		active = document.location.hash.substr(1), tabList = document.createElement("ul"),
		submit = getElementsByClassName("submit", "div", form)[0],
		log_action = form.action.replace(/http:\/\//, "");

	log_action = "Form: " + log_action.substr(log_action.indexOf("/"));

	function toggleTabs(active) {
		var focus = null;
		Array.prototype.forEach.call(fieldsets, function (fieldset, i) {
			if (i === (active || 0)) {
				addClass(fieldset, "active");
				fieldset.style.display = "block";
			}
			else {
				//removeClass(fieldset, "active");
				fieldset.style.display = "none";
			}
			if (i === active) {
				Array.prototype.forEach.call(form.elements, function (element) {
					if (!focus) {
						var suitable = false;
						if (fieldset.compareDocumentPosition) {
							suitable = !!(fieldset.compareDocumentPosition(element) & 16);
						}
						else if (fieldset.contains) {
							suitable = fieldset.contains(element);
						}
						if (suitable && element.type !== "hidden") {
							focus = element;
						}
					}
				});
			}
			if (tabList.childNodes[i]) {
				(i === (active || 0) ? addClass : removeClass)(tabList.childNodes[i], "active");
			}
		});
		if (focus) {
			focus.focus();
		}
	}

	function createTab(text, anchor) {
		var li, a;
		(a = document.createElement("a")).appendChild(document.createTextNode(text));
		a.href = "#" + anchor;
		addEvent(a, "click", function (e) {
			log(log_action, "Tab switch", this.href.substr(this.href.indexOf("#") + 1));
			toggleTabs(Array.prototype.indexOf.call(fieldsets, document.getElementById(this.href.substr(this.href.indexOf("#") + 1))));
			if (e.preventDefault) {
				e.preventDefault();
			}
			else {
				e.returnValue = false;
			}
			return false;
		});
		(li = document.createElement("li")).appendChild(a);
		return li;
	}

	function createButtons(i) {
		var div, button;
		(div = i + 1 === fieldsets.length ? submit : document.createElement("div")).className = "submit";
		[1, -1].forEach(function (j) {
			if (i + j > -1 && i + j !== fieldsets.length) {
				button = document.createElement("button");
				button.setAttribute("type", "button");
				button.appendChild(document.createElement("span")).appendChild(document.createTextNode(String.fromCharCode(i + j < i ? 9664 : 9654)));
				button.className = (button.title = i + j < i ? "Previous" : "Next").toLowerCase();
				button.onclick = function () {
					log(log_action, "Button " + (i + j < i ? "previous" : "next"), fieldsets[i + j].id);
					toggleTabs(i + j);
				};
				div.insertBefore(button, div.firstChild);
			}
		});
		return div;
	}

	addClass(tabList, "tabs");

	Array.prototype.forEach.call(fieldsets, function (fieldset, i) {
		var name = fieldset.removeChild(fieldset.getElementsByTagName("legend")[0]);
		name = name.textContent || name.innerText;
		if (fieldset.id === active) {
			active = i;
		}
		tabList.appendChild(createTab(name, fieldset.id));
		getElementsByClassName("content", "div", fieldset)[0].appendChild(createButtons(i));
		heights.push(fieldset.offsetHeight);
		addClass(fieldset, "tab");
	});
	
	toggleTabs(typeof active === "string" ? null : active);
	form.insertBefore(tabList, form.firstChild);
	form.style.height = (Math.max.apply(null, heights) + form.offsetHeight) + "px";
};

UEH.Application.editLinks = function editLinks() {
	eval(UEH.HTMLElement.exports);
	var ajax = new XMLHttpRequest();
	ajax.open("GET", " /user/created.json", true);
	ajax.onreadystatechange = function () {
		if (this.readyState === XMLHttpRequest.DONE) {
			var key, value, singular, element = null, json;
			if (window.JSON && JSON.parse) {
				json = JSON.parse(ajax.responseText);
			}
			else {
				json = eval("(" + ajax.responseText + ")");
			}
			for (key in json) {
				if (json.hasOwnProperty(key)) {
					if (key === "admin" && json[key] === "all") {
						addClass(document.body, "editable");
					}
					else {
						singular = key.split("").pop() === "s" ? key.substring(0, key.length - 1) : key;
						singular = singular == 'newsgroup' ? 'message' : singular;
						while ((value = json[key].pop())) {
							if ((element = document.getElementById(singular + "_" + value))) {
								addClass(element, "editable");
							}
						}
					}
				}
			}
		}
	};
	ajax.send(null);
};

UEH.Application.eventForm = function eventForm() {
	var ajax, div, fragment, header, child = null,
		container = document.getElementById("add-event");
	if (container) {
		/* Load the form */
		ajax = new XMLHttpRequest();
		ajax.open("GET", " /events/new", true);
		ajax.onreadystatechange = function () {
			if (this.readyState === XMLHttpRequest.DONE) {
				/* Process the form */
				div = document.createElement("div");
				fragment = document.createDocumentFragment();
				div.innerHTML = ajax.responseText;
				while (div.firstChild) {
					child = fragment.appendChild(div.firstChild);
					if (child.nodeType === 1 && child.className === "header") {
						fragment.removeChild(child);
					}
				}
				header = UEH.HTMLElement.getElementsByClassName("header", null, container)[0].cloneNode(true);
				container.innerHTML = "";
				container.appendChild(header);
				container.appendChild(fragment);
				/* Make the form "pretty" */
				UEH.Application.countryTimezoneSelectHelper();
				UEH.Application.countryStateSelectHelper();
				UEH.Application.BBCodeEditor.init();
				UEH.Application.tabbedFieldsets(container.getElementsByTagName("form")[0]);
			}
		};
		ajax.send(null);
	}
};

UEH.Application.SmoothScroll = {
	init: function init() {
		var anchors = Array.prototype.filter.call(document.links, function (v, i) {
			return v.href.indexOf("#") > -1;
		});
		anchors.forEach(function (anchor) {
			UEH.HTMLElement.addEvent(anchor, "click", UEH.Application.SmoothScroll.smoothScroll);
		});
	},
	smoothScroll: function smoothScroll(e) {
		var id = this.href.substr(this.href.indexOf("#") + 1),
			viewport = window.innerHeight || document.documentElement.clientHeight,
			total = document.documentElement.scrollHeight,
			element = id === "" ? document.body : document.getElementById(id), targetY = 0, scrollAmount, interval;
		if (element) {
			if (e.preventDefault) {
				e.preventDefault();
			}
			else {
				e.returnValue = false;
			}
			do {
				targetY += element.offsetTop;
				element = element.offsetParent;
			}
			while (element);
			targetY = viewport + targetY > total ? total - viewport : targetY;
			scrollAmount = (targetY - (window.scrollY || document.documentElement.scrollTop)) / 8;
			if (scrollAmount !== 0) {
				interval = window.setInterval(function () {
					var current = (window.scrollY || document.documentElement.scrollTop);
					window.scrollBy(0, scrollAmount);
					if (scrollAmount > 0 ? current + scrollAmount >= targetY : current + scrollAmount <= targetY) {
						window.scrollTo(0, targetY);
						window.clearInterval(interval);
						window.location.hash = "#" + id;
					}
				}, 50);
			}
		}
	}
};

UEH.Application.MultiSelect = {
	init: function init() {
		var selects = Array.prototype.filter.call(document.getElementsByTagName("select"), function (v, i) {
			return v.multiple;
		});
		selects.forEach(function (select) {
			var target = select.cloneNode(true), i, option;
			function toggle(e) {
				var option = e.target || e.srcElement;
				(option.parentNode === select ? target : select).appendChild(option);
			}
			select.id = select.id + "_src";
			select.name = select.name + "_src";
			select.parentNode.appendChild(target);
			for (i = 0; (option = select.options[i]); i += 1) {
				if (option.selected) {
					select.removeChild(option);
					i -= 1;
				}
				else {
					UEH.HTMLElement.addEvent(option, "dblclick", toggle);
				}
			}
			for (i = 0; (option = target.options[i]); i += 1) {
				if (!option.selected) {
					target.removeChild(option);
					i -= 1;
				}
				else {
					UEH.HTMLElement.addEvent(option, "dblclick", toggle);
				}
			}
			UEH.HTMLElement.addEvent(select.form, "submit", function () {
				var i;
				for (i = 0; i < target.options.length; i += 1) {
					target.options[i].selected = true;
				}
			});
		});
	}
};

UEH.Application.BBCodeEditor = {
	init: function init() {
		eval(UEH.Application.BBCodeEditor.exports);
		var textareas = UEH.HTMLElement.getElementsByClassName("bbcode", "textarea"),
		buttons = [
			{description: "Bold", className: "bold", tag: "b"},
			{description: "Italic", className: "italic", tag: "i"},
			{description: "Strikethrough", className: "strike", tag: "s"},
			{description: "List", className: "list", tag: "list" },
			{description: "|", className: "separator"},
			{description: "Link", className: "url", method: url },
			{description: "Image", className: "image", method: image },
			{description: "YouTube", className: "youtube", method: youtube }
		];
		textareas.forEach(function (textarea) {
			createToolbar();
			
			function createToolbar() {
				var toolbar = document.createElement("div"),
					buttonList = toolbar.appendChild(document.createElement("ul"));
				toolbar.className = "bbcode-toolbar";
				buttons.forEach(function (button) {
					appendToolbarButton(buttonList, button);
				});
				textarea.parentNode.insertBefore(toolbar, textarea);
				return toolbar;
			}

			function appendToolbarButton(buttonList, button) {
				var buttonListItem = document.createElement("li"),
				buttonEl;
				buttonListItem.className = button.className;
				if (button.className !== "separator") {
					buttonEl = document.createElement("button");
					buttonListItem.appendChild(buttonEl);
					buttonEl.appendChild(document.createTextNode(button.description));
					if (buttonEl.type !== "button") {
						buttonEl.setAttribute("type", "button");
					}
					UEH.HTMLElement.addEvent(buttonEl, "click", button.method ?
						function () {
							button.method(textarea);
						} :
						function () {
							tag(textarea, button.tag, button.open);
						}
					);
				}
				else {
					buttonListItem.appendChild(document.createTextNode(button.description));
				}
				buttonList.appendChild(buttonListItem);
				return buttonListItem;
			}
		});
	},

	insert: function insert(textarea, start_tag, end_tag) {
		var start, end, range, insertion_cursor, insert_position, scrollTop;
		if (!end_tag) {
			start_tag += " ";
			end_tag = "";
		}
		if (textarea.selectionStart || textarea.selectionStart === 0) {
			start = textarea.selectionStart;
			end = textarea.selectionEnd;
			insertion_cursor = textarea.value.substring(start, end) === "";
			scrollTop = textarea.scrollTop;
			textarea.value = textarea.value.substring(0, start) +
				start_tag + textarea.value.substring(start, end) + end_tag +
				textarea.value.substring(end, textarea.value.length);
			textarea.focus();
			insert_position = (textarea.value.substring(0, insertion_cursor ? start : end + start_tag.length + 1) + start_tag).length;
			textarea.setSelectionRange(insert_position, insert_position);
			textarea.scrollTop = scrollTop;
		}
		else if (document.selection) {
			textarea.focus();
			range = document.selection.createRange();
			if (end_tag === "") {
				range.collapse(false);
			}
			insertion_cursor = range.text.length < 1;
			range.text = start_tag + range.text + end_tag;
			if (insertion_cursor) {
				range.move("character", -end_tag.length);
			}
			range.select();
		}
		else {
			textarea.value += start_tag + end_tag;
			textarea.focus();
		}
	},

	tag: function tag(textarea, tag, open) {
		var start_tag = "[" + tag + "]",
		end_tag = open ? "" : "[\/" + tag + "]";
		UEH.Application.BBCodeEditor.insert(textarea, start_tag, end_tag);
	},

	url: function url(textarea) {
		var url = prompt("Please enter the url", "http://");
		if (url) {
			UEH.Application.BBCodeEditor.insert(textarea, "[url=" + url + "]", "[\/url]");
		}
	},

	image: function image(textarea) {
		var url = prompt("Please enter the image url", "http://");
		if (url) {
			UEH.Application.BBCodeEditor.insert(textarea, "[img]" + url + "[\/img]");
		}
	},

	youtube: function youtube(textarea) {
		var url = prompt("Please enter the youtube video url", "http://"), text;
		if (url) {
			text = "[youtube]" + url + "[\/youtube]";
			UEH.Application.BBCodeEditor.insert(textarea, text);
		}
	},

	exports: "var insert = UEH.Application.BBCodeEditor.insert, tag = UEH.Application.BBCodeEditor.tag, url = UEH.Application.BBCodeEditor.url, image = UEH.Application.BBCodeEditor.image, youtube = UEH.Application.BBCodeEditor.youtube;"
};

UEH.Application.voteWidget = function voteWidget() {
	var widgetForms, widget = document.getElementById("vote-widget");
	if (widget) {
		widgetForms = widget.getElementsByTagName("form");
		Array.prototype.forEach.call(widgetForms, function (form) {
			function vote(e) {
				if (e.preventDefault) {
					e.preventDefault();
				}
				else {
					e.returnValue = false;
				}
				var i = 0, qs = [], ajax = new XMLHttpRequest(), div;
				for (i = 0; i < form.elements.length; i += 1) {
					if (form.elements[i].name) {
						qs.push(encodeURIComponent(form.elements[i].name) + "=" + encodeURIComponent(form.elements[i].value));
					}
				}
				ajax.open("POST", form.action, true);
				ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
				ajax.onreadystatechange = function () {
					div = document.createElement("div");
					div.innerHTML = ajax.responseText;
					if (this.readyState === XMLHttpRequest.DONE) {
						widget.parentNode.replaceChild(div.firstChild, widget);
						UEH.Application.voteWidget();
					}
				};
				widget.innerHTML = "<p>Thanks for voting!</p>";
				ajax.send(qs.join("&"));
			}
			UEH.HTMLElement.addEvent(form, "submit", vote);
		});
	}
};

UEH.Application.googleAnalytics = function googleAnalytics() {
	setTimeout(function () {
		var node = document.createElement("script"), init;
		node.src = "http://www.google-analytics.com/ga.js";
		node.type = "text/javascript";
		document.documentElement.firstChild.appendChild(node);
		init = setInterval(function () {
			if (typeof _gat !== 'undefined') {
				clearInterval(init);
				pageTracker = _gat._getTracker("UA-4686009-1");
				pageTracker._initData();
				pageTracker._trackPageview();
			}
		}, 100);
	}, 0);
};

UEH.Application.init = function init() {
	var i, form;
	UEH.Application.googleAnalytics();
	UEH.Application.prettyDates();
	UEH.Application.gravatars();
	if (/^\/(?:newsgroup|events|messages|comments|music\/releases|music\/artists)/.test(document.location.pathname)) {
		UEH.Application.editLinks();
	}
	UEH.Application.BBCodeEditor.init();
	UEH.Application.countryTimezoneSelectHelper();
	UEH.Application.countryStateSelectHelper();
	UEH.Application.SmoothScroll.init();
	for (i = 0; (form = document.forms[i]); i += 1) {
		if (UEH.HTMLElement.hasClass(form, "tabbed")) {
			UEH.Application.tabbedFieldsets(form);
		}
	}
	UEH.Application.eventForm();
	UEH.Application.MultiSelect.init();
	UEH.Application.voteWidget();
};

/*global log */
UEH.Application.exports = "var log = UEH.Application.log";

/* Audioplayer options */
UEH.Application.AudioPlayer = { options: {
	width: 300,
	initialvolume: 80,
	animation: "no",
	bg: "1A0C0C", /* Background */
	leftbg: "1A0C0C", /* Speaker icon/Volume control background */
	lefticon: "F6F5F3", /* Speaker icon */
	voltrack: "B7ADA1", /* Volume track */
	volslider: "F6F5F3", /* Volume slider */
	rightbg: "1A0C0C", /* Play/Pause button background */
	rightbghover: "1A0C0C", /* Play/Pause button background (hover state) */
	righticon: "F6F5F3", /* Play/Pause icon */
	righticonhover: "F6F5F3", /* Play/Pause icon (hover state) */
	loader: "009900", /* Loading bar */
	track: "F6F5F3", /* Loading/Progress bar track backgrounds */
	tracker: "B7ADA1", /* Progress track */
	border: "F6F5F3", /* Progress bar border */
	skip: "F6F5F3", /* Previous/Next skip buttons */
	text: "1A0C0C" /* Text */
} };