﻿if (!window.console) {
	window.console = {
		"assert": function() { },
		debug: function() { },
		dir: function() { },
		dirxml: function() { },
		error: function() { },
		exception: function() { },
		info: function() { },
		log: function() { },
		profile: function() { },
		profileEnd: function() { },
		time: function() { },
		timeEnd: function() { },
		trace: function() { },
		warn: function() { }
	};
}

jQuery.fn.exists = function() {
	return (this.is('*'));
};

jQuery.fn.indexOf = function(e) {
	for (var i = 0; i < this.length; i++) {
		if (this[i] === e) {
			return i;
		}
	}
	return -1;
};

jQuery.fn.hideOption = function() {
	this.each(function() {
		var me = $(this);
		if (me.is("option") && !me.parent().is("span")) {
			me.wrap("<span>").hide();
		}
	});
};

jQuery.fn.showOption = function() {
	this.each(function() {
		if (this.nodeName.toLowerCase() === "option") {
			var p = $(this).parent();
			var o = this;

			$(o).show();
			if (p.is("span")) {
				p.replaceWith(o);
			}
		} else {
			var me = $(this);
			var opt = $("option", me);

			me.replaceWith(opt);
			opt.show();
		}
	});
};

$.belowthefold = function(element) {
	var fold = $(window).height() + $(window).scrollTop();
	return (fold <= $(element).offset().top);
};

$.abovethetop = function(element) {
	var top = $(window).scrollTop();
	return (top >= $(element).offset().top + $(element).height());
};

$.rightofscreen = function(element) {
	var fold = $(window).width() + $(window).scrollLeft();
	return (fold <= $(element).offset().left);
};

$.leftofscreen = function(element) {
	var left = $(window).scrollLeft();
	return (left >= $(element).offset().left + $(element).width());
};

$.inviewport = function(element) {
	return !($.rightofscreen(element) || $.leftofscreen(element) || $.belowthefold(element) || $.abovethetop(element));
};

$.extend($.expr[':'], {
	"below-the-fold": function(a) {
		return $.belowthefold(a);
	},
	"above-the-top": function(a) {
		return $.abovethetop(a);
	},
	"left-of-screen": function(a) {
		return $.leftofscreen(a);
	},
	"right-of-screen": function(a) {
		return $.rightofscreen(a);
	},
	"inView": function(a) {
		return $.inviewport(a);
	}
});

jQuery.fn.boo = function(callback) {
	return this.each(function() {
		var element = $(this);
		var scrollTop = (document.documentElement.scrollTop || document.body.scrollTop);
		var offsetTop = element.offset().top;
		var windowHeight = (window.innerHeight && window.innerHeight < $(window).height()) ? window.innerHeight : $(window).height();

		if (parseInt(scrollTop + windowHeight) < offsetTop) {	//below the fold
			var previousElement = element.prev()[0];
			if (previousElement) {
				$.scrollTo(scrollTop + previousElement.offsetHeight, { duration: "100" });
			}
		} else if (scrollTop > offsetTop) {						//above the fold
			var nextElement = element.next()[0];
			if (nextElement) {
				$.scrollTo(scrollTop - nextElement.offsetHeight, { duration: "100" });
			}
		}

		element.css({ "background-color": "#FFF4C0" });

		element.animate({ "background-color": "#FFFFFF" }, "slow", function() {
			element.css({ "background-color": null });
			if (callback) callback();
		});

		element.css({ "background-color": null });
	});
};

jQuery.fn.cull = function() {
	return this.each(function() {
		$(this).animate({ opacity: "hide", height: "hide" }, "fast");
	});
};

Date.prototype.format = function(f) {
	var dddd = this.days[this.getDay()];
	var d = this.getDate();
	var dd = (d < 10) ? '0' + d : d;
	var mmmm = this.months[this.getMonth()];
	var mmm = this.shortmonths[this.getMonth()];
	var m = this.getMonth() + 1;
	var mm = (m < 10) ? '0' + m : m;
	var yyyy = String(this.getFullYear());
	var yy = yyyy.substring(2);
	var H = this.getHours();
	var h = (H > 12) ? H - 12 : H;
	var n = this.getMinutes();
	var s = this.getSeconds();
	var HH = (H < 10) ? '0' + H : H;
	var hh = (h < 10) ? '0' + h : h;
	var nn = (n < 10) ? '0' + n : n;
	var ss = (s < 10) ? '0' + s : s;
	var t = (H < 12) ? 'a' : 'p';
	var tt = (H < 12) ? "am" : "pm";
	
	var list = "dddd,dd,d,mmmm,mmm,mm,m,yyyy,yy,HH,H,hh,h,nn,n,ss,s,tt,t".split(',');

	var i;
	for (i = 0; i < list.length; i++) {
		f = f.replace(list[i], String.fromCharCode(i));	//BUG: badness! will hit \t, \r and \n present in f already!
	}													//TODO: use a match itterator instead, outputting non-matched chunks and replacing matched ones.

	for (i = 0; i < list.length; i++) {
		f = f.replace(String.fromCharCode(i), eval(list[i]));
	}

	return f;
};

var emailFieldValidation = function(value, element) {
	// fixed to match the new one Engage uses in the modified Castle validator:
	return this.optional(element) || /^(?:(\s*(?:"((?:[^"\\]|\\.)*)"))?\s*(?:<(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4})))>|(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4}))))\s*|\s*(?:<(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4})))>|(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4}))))\s*(?:("((?:[^"\\]|\\.)*)")\s*)?)(?:[,;]\s*)?$/i.test(value);
};
var emailFieldWithSwapoutValidation = function(value, element) {
	// fixed to match the new one Engage uses in the modified Castle validator:
	return this.optional(element) || /(\[.+\])|(^(?:(\s*(?:"((?:[^"\\]|\\.)*)"))?\s*(?:<(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4})))>|(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4}))))\s*|\s*(?:<(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4})))>|(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4}))))\s*(?:("((?:[^"\\]|\\.)*)")\s*)?)(?:[,;]\s*)?$)/i.test(value);
};
var emailListFieldValidation = function(value, element) {
	// added to match the new one Engage uses for matching comma or semicolon separated lists of email addresses:
	return this.optional(element) || /^(?:(\s*(?:"((?:[^"\\]|\\.)*)"))?\s*(?:<(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4})))>|(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4}))))\s*|\s*(?:<(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4})))>|(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4}))))\s*(?:("((?:[^"\\]|\\.)*)")\s*)?)(?:\s*[,;](?:(\s*(?:"((?:[^"\\]|\\.)*)"))?\s*(?:<(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4})))>|(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4}))))\s*|\s*(?:<(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4})))>|(([-!#-'*-+\/-9=?A-Z^-~]((?!\.\.)[!#-'*-+--9=?A-Z^-~])*)@(\[(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\]|(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|[0-9A-Z-]+(\.[0-9A-Z-]+)*\.(?:local(?:network)?|(?:museum|travel)|[A-Z]{2,4}))))\s*(?:("((?:[^"\\]|\\.)*)")\s*)?))*(?:[,;]\s*)?$/i.test(value);
};
if (jQuery.validator) {
	// Replace the existing email validation:
	jQuery.validator.addMethod("email", emailFieldValidation, "Please enter a valid email address.");
	// Install the new emailList validation:
	jQuery.validator.addMethod("emailList", emailListFieldValidation, "Please enter a list of valid email addresses, separated by comma or semicolon.");
	// And an email validation that accepts ESL swapout tags
	jQuery.validator.addMethod("emailWithSwapout", emailFieldWithSwapoutValidation, "Please enter a valid email address or database field.");
}
Date.prototype.days = new Array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday");
Date.prototype.shortdays = new Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
Date.prototype.shortmonths = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
Date.prototype.months = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");

/*globals*/
var currentDataTable = null;
var alphas = [];

var Util = {
	SiteName: "",
	init: function(siteName) {
		this.SiteName = siteName;
		var importantMotd = $(".important-message");
		importantMotd.find("img.attention, a.close").click(function() {
			var messageContent = importantMotd.find("div.message-content");
			var attentionImg = importantMotd.find("img.attention");
			if (messageContent.is(":visible")) {
				messageContent.slideUp(function() {
					attentionImg.show("fast", function() {
						importantMotd.addClass("closed");
						Util.cookies.create("isImportantMessageOpen", false, 365);
					});
				});
			} else {
				attentionImg.hide("fast", function() {
					messageContent.slideDown(function() {
						importantMotd.removeClass("closed");
						Util.cookies.create("isImportantMessageOpen", true, 365);
					});
				});
			}
		});

		$("body").addClass("yui-skin-sam");
		$("input:text:visible:first").focus();

		$(document).keypress(function(e) {
			if (e.target.type == "textarea") {
				return true;
			}
			if (e.keyCode == 13) {
				return false;
			}
			return true;
		});

		// Filter dialogs submit on enter key press
		$("#form-logic-selectitem").keyup(function(e) {
			if (e.keyCode == 13) {
				$("#logic-item-submit").trigger("click");
			}
		});

		// Ensure accept text is always uppercase and not just look that way with css	//QUESTION: why?  the accept regex is case-insensitive so it doesn't matter!
		$("input.accept").keyup(function() {
			var input = $(this);
			var upperValue = input.val().toUpperCase();
			input.val(upperValue);
		});

		var me = this;

		if (me.browserTest() == false) {
			$("#non-trusted-browser").show();
		}

		me.setupAjaxDefaults();
		me.setupCalendars();
		me.externalizeLinks();
		me.setupConfirmationFader();

		me.toggleFullscreenView.init();
		me.collapsablePanels.init();
		me.accountSwitcher.init();
		me.buttonDisabler.init();
		me.widgets.init();
		me.help.init();
		me.tabs.init();
		me.contactUs.init();
		/*Titanium Release
		if (Modinizer.localstorage) { me.localStorage.init(); }*/
	},

	browserTest: function() {
		var userAgent = navigator.userAgent.toLowerCase();
		var trustedBrowser, browser;
		browser = {
			version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [0, '0'])[1],
			safari: /webkit/.test(userAgent),
			opera: /opera/.test(userAgent),
			msie: /msie/.test(userAgent) && !/opera/.test(userAgent),
			mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent)
		};
		trustedBrowser = ((browser.msie && browser.version > 6) || browser.safari || browser.opera || browser.mozilla);
		return trustedBrowser;
	},

	focus: function(c) {
		c = $('#' + c);
		if (c.exists()) {
			try {
				c[0].select();
			}
			catch (e) { }
			try {
				c[0].focus();
			}
			catch (e) { }
		}
	},

	externalizeLinks: function() {
		$("a[rel*=external]").live("click", function() {
			window.open(this.href);
			return false;
		});
	},

	columnOrder: {
		getColumnOrderCookiePath: function() {
			var parts = window.location.pathname.split('/');
			var keptParts = [""]; 						// initialise with the initial blank already there
			var guidRE = /^[A-Za-z0-9_\-]{22}$/;

			for (var i = 1; i < parts.length; i++) {	// start at 1 to skip the always empty [0]
				if (parts[i].indexOf('.') > -1 || guidRE.test(parts[i])) {
					break;
				}

				keptParts.push(parts[i]);
			}

			return keptParts.join('/');
		},

		getColumnNameList: function(columns) {
			if (Array.map) {
				return Array.map(columns, function(column) { return column.key; });
			} else {	// Yet another #&%^ IE fix...!
				var keys = [];
				for (var i = 0; i < columns.length; i++) {
					keys[i] = columns[i].key;
				}
				return keys;
			}
		},

		saveColumnOrderCookie: function(key, columns) {
			Util.cookies.create(
				"columnOrder_" + key,
				Util.columnOrder.getColumnNameList(columns).join(','),
				365,
				Util.columnOrder.getColumnOrderCookiePath()
			);
		},

		loadColumnOrderCookie: function(key, columns) {
			var savedOrder = Util.cookies.read("columnOrder_" + key);

			if (savedOrder) {
				savedOrder = unescape(savedOrder).split(',');

				var nameLookup = Util.columnOrder.getColumnNameList(columns);
				var newColumns = [];

				for (var j = 0; j < savedOrder.length; j++) {
					var name = savedOrder[j];
					var index = -1;

					for (var i = 0; i < nameLookup.length; i++) {
						if (name == nameLookup[i]) {
							index = i;
							break;
						}
					}

					if (index != -1) {
						newColumns.push(columns[index]);

						columns.splice(index, 1);
						nameLookup.splice(index, 1);
					}
				}

				newColumns = newColumns.concat(columns);

				columns = newColumns;
			}

			Util.columnOrder.saveColumnOrderCookie(key, columns);

			return columns;
		},

		handleColumnReorderEvent: function(key, args) {
			if (arguments.length == 1) {
				return function(subsequentArgs) {
					Util.columnOrder.handleColumnReorderEvent(key, subsequentArgs);
				};
			}

			var savedOrder = Util.cookies.read("columnOrder_" + key);

			if (savedOrder) {	// otherwise we don't have the list of columns to work from...
				savedOrder = unescape(savedOrder).split(',');

				var oldIndex = -1;
				for (var i = 0; i < savedOrder.length; i++) {
					if (savedOrder[i] == args.column.key) {
						oldIndex = i;
						break;
					}
				}

				if (oldIndex == -1) {
					return null; 	// bail out if we can't find the col being reordered - wait for a new pageload to fix the cookie...
				}

				if (oldIndex < savedOrder.length && savedOrder[oldIndex] == args.column.key) {
					savedOrder.splice(oldIndex, 1);

					savedOrder.splice(args.column.getKeyIndex(), 0, args.column.key);
				}

				Util.cookies.create(
					"columnOrder_" + key,
					savedOrder.join(','),
					365,
					Util.columnOrder.getColumnOrderCookiePath()
				);
			}
			return null;
		}
	},

	constructYuiDataTable: function(id, url, fields, columns, config, usepager, schema, columnOrderAlreadyHandled) {
		if (!columnOrderAlreadyHandled && config.draggableColumns) {
			columns = Util.columnOrder.loadColumnOrderCookie(id, columns);
		}

		if (schema == null) {
			schema = {
				totalRecords: "totalRecords"
			};
		}

		var rnd = parseInt(Math.random() * 99999999);
		var qsSeparator = (url.indexOf('?') == -1)
			? '?'
			: '&';

		var dataSource = new YAHOO.util.DataSource(
			url + qsSeparator + "tfif=" + rnd + '&',
			{
				responseType: YAHOO.util.DataSource.TYPE_JSON,
				connXhrMode: "queueRequests",
				responseSchema: {
					resultsList: "records",
					metaFields: schema,
					fields: fields
				}
			}
		);

		dataSource.preParseData = dataSource.doBeforeParseData;
		dataSource.doBeforeParseData = function(oRequest, result, callback) {
			Util.hideErrors();

			if (result.model) {
				if (result.model.WasSystemException) {
					window.location.href = "/AjaxError/?errorReference=" + result.model.ErrorReference;
					return null;
				}

				Util.showFeedback(result);
				return null;
			};

			return this.preParseData(oRequest, result, callback);
		};

		// IMPORTANT - IE8 will not send request to server if set as GET.
		dataSource.connMethodPost = true;

		config.width = "auto";
		config.generateRequest = function(request) {
			var qs = "sort=" + request.sortedBy.key
				+ "&dir=" + ((request.sortedBy.dir == "yui-dt-asc") ? "asc" : "desc")
				+ "&startIndex=" + request.pagination.recordOffset
				+ "&results=" + request.pagination.rowsPerPage
				+ "&action=" + config.action;

			return qs;
		};

		var paginatorId = id + "_paginator";
		var pager = $('#' + paginatorId);

		if (pager.length > 0) {
			pager.remove();
		}

		if (usepager) {
			$('#' + id).after('<div id="' + paginatorId + '" class="pagination"></div>');

			config.paginator = new YAHOO.widget.Paginator({
				containers: [paginatorId],
				rowsPerPage: config.pageSize,
				totalRecords: YAHOO.widget.Paginator.VALUE_UNLIMITED,
				pageLinks: 10,
				alwaysVisible: false
			});

			var currentPage = (config.startIndex / config.pageSize) + 1;
			config.paginator.setPage(currentPage, true);
		}

		config.MSG_LOADING = '<div class="whiteoutHolder"><div class="whiteout">&nbsp;</div><img class="yui-loader" src="/Assets/images/widget/loading.gif"/></div>';
		YAHOO.widget.DataTable.CLASS_LOADING = "yui-dt-loadingInitial";
		var dataTable = new YAHOO.widget.DataTable(id, columns, dataSource, config);

		if (!columnOrderAlreadyHandled && config.draggableColumns) {
			dataTable.subscribe("columnReorderEvent", Util.columnOrder.handleColumnReorderEvent(id));
		}

		currentDataTable = dataTable;

		dataTable.doBeforePaginatorChange = function() {
			var table = $(dataTable.getTableEl());
			var offset = table.closest(".yui-dt").get(0).scrollLeft;
			var holder = table.find(".yui-dt-loading");

			if (holder.length == 0) {
				holder = table.find(".yui-dt-loadingInitial");
			}

			var message = null;

			if (holder.length == 1) {
				holder = holder.find(".whiteoutHolder");
				holder.css({ "left": offset + "px" });
				message = holder.closest(".yui-dt-liner").html();
			}

			dataTable.showTableMessage(message || config.MSG_LOADING, "yui-dt-loading");
			return true;
		};

		// modify the default OnDataReturned to fix yet another IE7 bug:
		//NOTE: if someone overrides this they need to check IE7 still works as intended rather than leaving the spinner around.
		dataTable.OnDataReturned = ($.browser.msie && $.browser.version == 7)
			? function() {
				dataTable.showTableMessage("");
				dataTable.hideTableMessage();
			}
			: function() { };

		dataTable.handleDataReturnPayload = function(request, response, payload) {
			payload = payload || {};

			payload.totalRecords = response.meta.totalRecords;
			payload.totalRecordsFormatted = YAHOO.util.Number.format(response.meta.totalRecords, { thousandsSeparator: ',' });

			if (config.paginator) {
				config.paginator.setTotalRecords(payload.totalRecords, true);

				if (response.meta.totalRecords > 0 && response.results.length == 0) {
					var startIndex = payload.pagination.recordOffset;
					var totalRecords = payload.totalRecords;

					if (startIndex >= totalRecords) {
						config.paginator.setPage(config.paginator.getPreviousPage(), true);
					}

					dataTable.Refresh();
					return payload;
				}
			}

			dataTable.OnDataReturned(request, response, payload);
			return payload;
		};

		dataTable.Refresh = function(success, failure) {
			var startIndex = 0;

			if (config.paginator) {
				startIndex = (config.paginator.getCurrentPage() - 1) * config.paginator.getRowsPerPage();
			}

			var request = config.initialRequest;
			request = request.replace(/startIndex=\d+/ig, "startIndex=" + startIndex);

			dataSource.sendRequest(
				request,
				{
					success: success || dataTable.onDataReturnInitializeTable,
					failure: failure || function() { },
					scope: dataTable,
					argument: dataTable.getState()
				}
			);
		};

		YAHOO.widget.DataTable.Formatter.externalViewUrl = function(elCell, oRecord, oColumn, oData) {
			elCell.innerHTML = oData
				? '<a href="' + oData + '" class="view-data" title="View" rel="external">View</a>'
				: "";
		};

		YAHOO.widget.DataTable.Formatter.viewUrl = function(elCell, oRecord, oColumn, oData) {
			elCell.innerHTML = oData
				? '<a href="' + oData + '" class="view-data" title="View">View</a>'
				: "";
		};

		YAHOO.widget.DataTable.Formatter.editUrl = function(elCell, oRecord, oColumn, oData) {
			elCell.innerHTML = oData
				? '<a href="' + oData + '" class="edit-data" title="Edit">Edit</a>'
				: "";
		};

		YAHOO.widget.DataTable.Formatter.deleteUrl = function(elCell, oRecord, oColumn, oData) {
			elCell.innerHTML = oData
				? '<a href="' + oData + '" class="delete-data" title="Delete">Delete</a>'
				: "";
		};

		return {
			DataSource: dataSource,
			DataTable: dataTable,
			Config: config
		};
	},

	setupCalendars: function(options) {
		var dateConfig = {
			duration: "",
			showTime: false,
			constrainInput: false,
			dateFormat: "d M yy",
			firstDay: 1,
			changeMonth: true,
			changeYear: true,
			yearRange: "1910:2030",
			hideIfNoPrevNext: true,
			showOn: "button",
			buttonImage: "/Assets/Images/calendar.png",
			buttonImageOnly: true
		};
		var dateTimeConfig = {
			duration: "",
			constrainInput: false,
			dateFormat: "d M yy",
			firstDay: 1,
			changeMonth: true,
			changeYear: true,
			yearRange: "1910:2030",
			hideIfNoPrevNext: true,
			showOn: "button",
			buttonImage: "/Assets/Images/calendar.png",
			buttonImageOnly: true,
			showTime: true,
			stepMinutes: 5,
			time24h: true
		};
		var inputs = $("input.datetime, input.date");
		var visibleOnly = false;

		if (options) {
			if (options.input) {
				inputs = options.input;
			}
			if (options.visibleOnly) {
				visibleOnly = options.visibleOnly;
			}
		}

		if (inputs.length == 0) {
			return;
		}

		$(inputs).each(function() {
			var input = $(this);
			var defaultConfig = input.hasClass("datetime")
				? dateTimeConfig
				: dateConfig;

			if (input.hasClass("hasDatepicker")) {
				return;
			}

			if (visibleOnly && input.is(":hidden")) {
				return;
			}

			if (input.hasClass("future-only")) {
				var date = new Date();
				var thisYear = date.getFullYear();
				var endYear = date.getFullYear() + 5;

				defaultConfig["yearRange"] = thisYear + ':' + endYear;
			}

			input.datepicker(defaultConfig);

			var config = input.data("config");

			if (config) {
				if (config.minDate) {
					input.datepicker("option", "minDate", config.minDate);
				}

				if (config.maxDate) {
					input.datepicker("option", "maxDate", config.maxDate);
				}
			}
		});
	},

	setupAjaxDefaults: function() {
		var doc = $(document);

		$.ajaxSetup({
			cache: false,
			data: {}
		});

		doc.ajaxStart(function() {
			if ($("#ajax-alpha").length == 0) {
				$("body").prepend('<div id="ajax-alpha"/>');
			}
		});

		doc.ajaxStop(function() {
			$("#ajax-alpha").remove();
		});

		doc.ajaxError(function(event, request) {
			if (request.status == "403") {
				window.location.href = "/Login";
			} else if (request.status == "404") {
				window.location.href = "/404";
			} else {
				try {
					var responseJson = eval('(' + request.responseText + ')');
					if (responseJson.ErrorReference) {
						window.location.href = "/AjaxError/?errorReference=" + responseJson.ErrorReference;
						return;
					}
				} catch (x) { /*FallThru*/ }

				if (console && console.log) {
					console.log("Ajax Error:", request.responseText);
				}
				alert("Sorry, but the operation you requested has failed.");
				$("#ajax-alpha").remove();
			}
		});
	},

	setupConfirmationFader: function() {
		$(".confirmation").animate({ opacity: 1.0 }, 3000).fadeOut("slow");
	},

	setupKeyListener: function(selector) {
		$(selector).find("input").keydown(function(e) {
			if (e.keyCode == 13) {
				$(this).parents("form").submit();
				return false;
			}
			return true;
		});
	},

	dialogDragAndDrop: function(target) {
		var handler = target.find("h3:first");

		if (handler.exists()) {
			handler.attr("id", "handler");
		}

		YAHOO.setUpDialogDrop = function(id) {
			YAHOO.setUpDialogDrop.superclass.constructor.apply(this, arguments);

			var me = this;
			var theDialog = $('#' + id);
			var currentWindow = $(window);

			me.initConstraints(currentWindow, theDialog, false);

			$(window).resize(function() {
				me.initConstraints(currentWindow, theDialog, true);
			});
		};

		YAHOO.extend(YAHOO.setUpDialogDrop, YAHOO.util.DD, {
			initConstraints: function(currentWindow, theDialog, isResize) {
				this.clearConstraints();

				var width = theDialog.width() + 40;

				var left = parseInt(theDialog.css("left").replace("px", ""));
				var top = parseInt(theDialog.css("top").replace("px", ""));

				this.setXConstraint(left, currentWindow.width() - left - width);
				this.setYConstraint(top, currentWindow.height() - top - 64);

				if (isResize) {
					this.endDrag();
				}
			},

			endDrag: function() {
				if (target.find("h3:visible:first").is(":inView") == false) {
					target.css({
						top: 0,
						left: (($(window).width() - target.width() - 40) / 2) + "px"
					});
				}
			}
		});

		var dialog = new YAHOO.setUpDialogDrop(target.attr("id"));
		dialog.DDM = YAHOO.util.DDM; // IE8 fix
		dialog.setHandleElId("handler");
	},


	showDialog: function(dialog, callback, updateExistingDialog) {
		var target = $(dialog);
		var alpha = $("div.alpha");
		var zIndex = 101;

		// if target id exists in alphas then jump out
		if (!updateExistingDialog && $(alphas).indexOf(target.attr("id")) != -1) {
			return;
		}

		if (alpha.length == 0) {
			alpha = $(
				"<div/>",
				{
					css: {
						opacity: '0'
					},
					"class": "alpha"
				}
			).prependTo("body");
		}

		// jump through alphas to find highest zIndex (set the underlay zIndex to zIndex -1)
		if (alphas.length > 0) {
			for (dialog in alphas) {
				var curDialog = $('#' + alphas[dialog]);

				if (curDialog.css("z-index") >= zIndex) {
					zIndex = parseInt(curDialog.css("z-index"), 10) + 100;
				}
			}
		}

		target.unbind("keypress").keypress(function(e) {
			if (e.keyCode == /*esc*/27) {
				Util.hideDialog(target);
			}
		}).css({ zIndex: zIndex });

		Util.applyScroll(target, 0.1);
		Util.dialogDragAndDrop(target);

		if (updateExistingDialog) {
			if (callback) {
				callback();
			}
			return;
		}

		alpha.css({ zIndex: zIndex - 1 }).fadeTo(
			300,
			0.3,
			function() {
				target.show(
					300,
					function() {
						// add current dialog
						alphas.push(target.attr("id"));

						if (callback) {
							callback();
						}
					}
				).find(":text:visible:first")
					.focus();
			}
		);
	},

	applyScroll: function(target, offset, skipCentering, heightAdjustment) {
		var offsetScale = offset || 0.1;
		var heightOffset = heightAdjustment || 69;
		var scrollDiv = target.find("div.dialog-scroll");
		var currentWindow = $(window);
		var windowHeight = currentWindow.height();
		var scrollHeight = (windowHeight - (windowHeight * offsetScale)) - heightOffset;

		if (scrollDiv.exists() == false) {
			if (target.find("form").exists()) {
				target.find("form").each(function() {
					var form = $(this);
					form.find("> div:not(div.form-buttons)").wrapAll('<div class="dialog-scroll"/>');
				});
			} else {
				target.find("> div:not(div.form-buttons)").wrapAll('<div class="dialog-scroll"/>');
			}
			scrollDiv = target.find("div.dialog-scroll");
		}

		scrollDiv.css({ "max-height": (scrollHeight / scrollDiv.size()) + "px" });
		target.css({ top: 0 });

		if (!skipCentering) {
			var left = (currentWindow.width() - target.width() - 40) / 2;
			target.css({ left: left + "px" });
		}
	},

	hideDialog: function(dialog, callback) {
		var target = $(dialog);

		// if target id doesn't exist in alphas then jump out
		if ($(alphas).indexOf(target.attr("id")) == -1) {
			return;
		}

		target.animate(
			{ top: (target.height() * -1) - 1000 },
			300,
			function() {
				// underlay alpha, zIndex for underlay, whether to hide the underlay when finished (opacity: 0)
				var underlay = $("div.alpha");
				var zIndex = 0;
				var remove;

				// remove last dialog
				alphas.pop();

				// jump through alphas to find highest zIndex and - 1 to set the underlay zIndex
				if (alphas.length > 0) {
					for (var theDialog in alphas) {
						var curDialog = $('#' + alphas[theDialog]);

						if (curDialog.css("z-index") > zIndex) {
							zIndex = curDialog.css("z-index");
						}
					}
					// hide underlay when complete
					remove = false;
				} else {
					remove = true;
				}

				underlay.fadeTo(
					300,
					remove
						? 0
						: 0.3,
					function() {
						// no other dialogs are visible so hide underlay
						if (remove) {
							underlay.remove();
						} else {
							underlay.css({ zIndex: zIndex - 1 });
						}

						if (callback) {
							callback();
						}
					}
				);
			}
		).height("auto");
		$(window).unbind("resize");
	},


	isError: function(result) {
		if (!result) {
			return false;
		}

		var thereIsAModelStateError = result.modelStates;
		if (thereIsAModelStateError) {
			return true;
		}

		if (!result.model) {
			return false;
		}

		return (!result.model.WasSystemException && result.model.ErrorMessage);
	},

	showFeedback: function(result, selector) {
		var currentSelector = $(selector);
		var confirmation = selector
			? currentSelector.find(".confirmation")
			: $(".confirmation");
		var summary = selector
			? currentSelector.find(".fail")
			: $(".fail:first");
		var system = selector
			? currentSelector.find(".system")
			: $(".system");
		var warnings = selector
			? currentSelector.find(".warnings")
			: $(".warnings");

		Util.showConfirmationMessage(result, confirmation);
		Util.showSystemError(result, system);
		Util.showWarnings(result, warnings);

		if (summary.find(".fail-list ol").length == 0) {
			summary.find(".fail-list").append("<ol/>");
		}
		var list = summary.find(".fail-list ol");
		list.empty();

		// we want to show an error if the model state is invalid OR there was an error message that was not a system exception.
		// the system exception messages are shown in the system error messages list.

		var thereIsAModelStateError = result.modelStates;
		var thereIsANonFatalErrorMessage = (!result.model.WasSystemException && result.model.ErrorMessage);

		if (thereIsANonFatalErrorMessage) {
			list.append("<li><label>" + result.model.ErrorMessage + "</label></li>");
		}

		if (thereIsAModelStateError) {
			for (var i = 0; i < result.modelStates.length; i++) {
				var e = $('#' + result.modelStates[i].Key);

				e.addClass("error");
				for (var j = 0; j < result.modelStates[i].Errors.length; j++) {
					list.append('<li><label for=\"' + result.modelStates[i].Key + '">' + result.modelStates[i].Errors[j] + "</label></li>");
				}
			}
		}

		if (thereIsAModelStateError || thereIsANonFatalErrorMessage) {
			list.show();
			summary.fadeIn("slow");
			if (summary.is(":inView") == false) {
				var visibleDialog = $("div.dialog:visible").find("div.dialog-scroll");

				if (visibleDialog.is('*')) {
					visibleDialog.scrollTo(summary, { duration: "300" });
				} else {
					$.scrollTo(summary, { duration: "300" });
				}
			}
		}
	},

	hideErrors: function(element) {
		element = element || $("#content");

		var el = $(element);

		Util.hideSystemErrors(element);

		el.find(".fail").hide();
		el.find(".fail-list ol").empty();
		el.find(".error").removeClass("error");
	},

	showSystemError: function(result, element) {
		var el = $(element);
		var list = el.find(".system-list ol");

		if (!result.model.WasSystemException) {
			el.hide();
			return;
		}

		if (list.exists() == false) {
			el.find(".system-list").append("<ol/>");
			list = el.find(".system-list ol");
		}

		list.empty();
		list.append("<li><label>" + result.model.ErrorMessage + "</label></li>");
		list.append("<li><label>The error reference number is: " + result.model.ErrorReference + "</label></li>");
		list.show();

		el.fadeIn("slow");
	},

	hideSystemErrors: function(element) {
		var el = $(element);

		el.find(".system").hide();
		el.find(".system-list ol").empty();
	},

	showWarnings: function(result, element) {
		var el = $(element);
		var list = el.find(".warning-list ol");

		if (!result.model.Warnings || result.model.Warnings.length == 0) {
			el.hide();
			return;
		}

		if (list.exists() == false) {
			el.find(".warning-list").append("<ol/>");
			list = el.find(".warning-list ol");
		}

		list.empty();
		for (var i = 0; i < result.model.Warnings.length; i++) {
			list.append("<li>" + result.model.Warnings[i] + "</li>");
		}

		list.show();

		el.fadeIn("slow");
	},

	showConfirmationMessage: function(result, element) {
		if (result.model && result.model.ConfirmationMessage) {
			var el = $(element);

			if (el.find('p').length == 0) {
				el.append("<p></p>");
			}

			el.find('p').html(result.model.ConfirmationMessage);
			el.fadeIn("slow")
				.animate({ opacity: 1 }, 3000)
				.fadeOut("slow");
		}
	},


	convertRelativePathsToAbsolute: function(data) {
		// Firefox 3.6 "helpfully" replaces any absolute urls on the domain with an equivalent relative url,
		// however this results in "..\..\.." at the start of local domain urls (which is problematic) ...
		// so we need to fix up any of these mangled urls.

		var domain = window.location.host;
		data = data.replace(/((href|src)\s*=\s*["']?)(\.\.\/)+/g, "$1http://" + domain + '/'); //BUG: forces http - should really check for https too...
		return data;
	},


	ckEditor_Setup: function(elementID, options) {	//FIXME: Rename to something more standard.
		var ckeditor = CKEDITOR.instances[elementID];
		var width = 580;
		var height = options && options.height || 200;

		if (ckeditor) {
			CKEDITOR.remove(ckeditor);

			var exist = $("#cke_" + elementID);
			if (exist.length > 0) {
				$(exist).remove();
			}
		}

		// okay so there is a slight hack here cause when in a dialog the max width of the editor is 580.
		// but when we are in the page 580 is too narrow, so we detect if we are in a dialog :)
		if ($('#' + elementID).parents(".dialog").length == 0) {
			width = 620;
		}

		ckeditor = CKEDITOR.replace(elementID, {
			resize_minHeight: height,
			resize_minWidth: width,
			resize_maxWidth: width,
			width: width,
			height: height,
			resize_enabled: true,
			toolbarCanCollapse: false,
			removePlugins: "elementspath,save,link,image,forms,scayt", // forms requires image but we don't use it anyway.
			extraPlugins: "Share,Merge,Media,Connect,uqlink,uqimage,htmldataprocessorOverride",
			customConfig: "",
			language: "en",
			toolbar:
			[
				["Bold", "Italic", "TextColor", "NumberedList", "BulletedList", "Media", "Anchor", "Link", "Unlink", "PasteText", "PasteFromWord", "Maximize", "Font", "FontSize"], ["Source"],
				'/',
				["JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyBlock", "Table"],
				options && options.merge ? ["Merge"] : null,
				options && options.connect ? ["Connect"] : null,
				options && options.share ? ["Share"] : null
			],
			protectedSource: [
			//	/{{(.(?!}}))+[^}]}}/g,	// hide directives from WYSIWYG - still visible in Source mode.
				/\<\w+\([^\>]+\>/g, 	// hide old-style directiveArg-functions from WYSIWYG - still visible in Source mode.
				/\[\<[^\>]+\>\]/g		// hide old-style replacement-function from WYSIWYG - still visible in Source mode.
			]
		});

		if (options && options.merge) {
			ckeditor.merge = {
				attachedTo: elementID,
				dialogUrl: options.merge.dialogUrl,
				dataUrl: options.merge.dataUrl
			};
		}

		if (options && options.connect) {
			ckeditor.connect = {
				attachedTo: elementID,
				dialogUrl: options.connect.dialogUrl
			};
		}

		if (options && options.share) {
			ckeditor.share = {
				attachedTo: elementID,
				dialogUrl: options.share.dialogUrl,
				editShareItemUrl: options.share.editShareItemUrl
			};

			// If the user double-clicks a share icon, trigger the share content dialog to edit the share.
			ckeditor.on(
				"doubleclick",
				function(e) {
					if (ShareContentDialog.editShareLink(ckeditor, e.data.element)) {
						e.cancel();
					}
				},
				null, null,
				1
			);
		}

		// Override existing insertHtml function to include fix for Firefox 3.6
		ckeditor.insertHtml = function(data) {
			data = Util.convertRelativePathsToAbsolute(data);

			ckeditor.fire("insertHtml", data);
		};

		// Turn off the automatic SCAYT spell-checking.
		ckeditor.config.scayt_autoStartup = false;

		// *** Tweaks of the CKEditor's link dialog...
		// Hide the advanced tab:
		ckeditor.config.linkShowAdvancedTab = false;

		ckeditor.config.dialog_backgroundCoverColor = "black";
		ckeditor.config.dialog_backgroundCoverOpacity = "0.3";

		// for some reason we can't successfully bind to the event against the instance (ckeditor) and have to use the global one (CKEDITOR)...
		// ...but we only want to bind the event once:
		if (CKEDITOR.uEngageLinkDialogEventAttached) {
			return;
		}
		CKEDITOR.uEngageLinkDialogEventAttached = true;

		// Hijack the dialog at creation to edit it...
		CKEDITOR.on("dialogDefinition", function(ev) {
			// Take the dialog name and its definition from the event data.
			var dialogName = ev.data.name;
			var dialogDefinition = ev.data.definition;

			// Check if the definition is from the dialog we're interested on (the "Link" dialog).
			if (dialogName == "link") {
				dialogDefinition.minHeight = 85;

				// Get a reference to the "Link Info" tab.
				var infoTab = dialogDefinition.getContents("info");

				// Remove the "Link Type" combo and the "Browser Server" button from the "info" tab.
				//infoTab.remove("linkType");	--see below, removing an option instead...
				infoTab.remove("browse");

				//var linkType = infoTab.get("linkType");
				//linkType.items = [["URL", "url"], ["E-mail", "email"]];	// removed @1["Link to anchor in the text", "anchor"]

				// Restrict the available protocols
				//NOTE: this means that news:// links are un-enterable as it still recognises that prefix and strips it, but now can't set the protocol dropdown to that value.
				var protocol = infoTab.get("protocol");
				protocol.items = [["http://"], ["https://"], ["ftp://"]]; // removed @3["news://"] and @4["<other>", ""]

				// Get the Target tab...
				var targetTab = dialogDefinition.getContents("target");
				// Remove some things...
				targetTab.remove("fullscreen");
				targetTab.remove("dependent");
				targetTab.remove("left");
				targetTab.remove("top");

				// Fix the layout so it looks a bit better...
				targetTab.get("width").widths = ["20%", "80%"];
				targetTab.get("height").widths = ["20%", "80%"];

				// Restrict the linkTargetTypes and rename one:
				var linkTargetType = targetTab.get("linkTargetType");
				linkTargetType.items = [["<not set>", "notSet"], ["<popup window>", "popup"], ["New Window", "_blank"]]; // renamed "New Window (_blank)" to "New Window"; removed @1["<frame>", "frame"], @4["Topmost window (_top)", "_top"], @5["Same window (_self)", "_self"] and @6["Parent window (_parent)", "_parent"]

				// Add the warning message HTML under everything else...
				targetTab.add({
					id: "warningMessage",
					type: "html",
					html: "<span><strong>Warning:</strong> Popups will probably not work in emails.</span>",
					hidden: true
				});

				// Save the current onChange so we can call it later:
				linkTargetType.onChange2 = linkTargetType.onChange;

				// Replace the onChange with one that hides the warning message the same way the popup stuff hides, then call the old one to do the rest:
				linkTargetType.onChange = function() {
					var dialog = this.getDialog();
					var warningMessage = dialog.getContentElement("target", "warningMessage");
					var value = this.getValue();

					if (!warningMessage)
						return;

					warningMessage = warningMessage.getElement();

					switch (value) {
						case "popup":
							warningMessage.show();
							break;
						default:
							warningMessage.hide();
					}

					this.onChange2();
				};
			}
		});
	},

	ckEditor_Save: function(elementID) {	//FIXME: Rename to something more standard.
		var ckeditor = CKEDITOR.instances[elementID];

		if (!ckeditor) {
			alert("ckEditor_Save instance not found " + elementID);
			return;
		}

		var element = $('#' + elementID);
		if (element.length == 0) {
			alert("ckEditor_Save form element not found " + elementID);
			return;
		}

		var data = ckeditor.getData();
		data = Util.convertRelativePathsToAbsolute(data);

		// escape non-ASCII characters as &#xxxx; entities:
		data = data.replace(/[^\t\r\n -~]/g, function(m) {
			return "&#" + m.charCodeAt() + ';';
		});

		element.val(data);
	},

	setupServiceLinkDropDownLists: function() {
		$("select.service_links").livequery("change", function() {
			var select = $(this);
			var value = select.val();

			if (value && value.length > 0) {
				var element = $('#' + select.attr("name")).get(0);
				var ckeditor = CKEDITOR.instances[element.id];
				if (ckeditor) {
					if (ckeditor.mode == "source") {
						Util.insertAtCaret(ckeditor.textarea.$, value);
					} else {
						ckeditor.insertHtml(value);
					}
				} else {
					Util.insertAtCaret(element, value);
				}
			}
			select[0].selectedIndex = 0;
		});
	},

	set_iframe_url: function(iframe, url) {
		if (iframe.contentWindow && iframe.contentDocument) {
			iframe.contentDocument.location.href = url;
		} else {
			iframe.contentWindow.location.href = url;
		}

		if ($(iframe).closest("div.dialog").exists()) {
			return;
		}

		if ($("#html_tab").exists() && $("#html_tab").closest("div.layout-options").exists() == false) {
			if ($("#cover").exists() == false) {
				$("#html_tab").prepend("<div id=\"cover\"/>");
			}
		}
	},


	resize_iframe: function(iframe) {
		if (iframe) {
			var body = $(iframe.contentWindow.document.body),
			height;

			if (body.find("div.clearfixer").exists() == false && body.html() != "") {
				body.append('<div class="clearfixer" style="height:1px; clear:both"></div>');
			}

			if (body.find(":first").is("pre")) {			// text
				height = $.browser.msie
					? body.find(":first").height() + 60
					: body.find(":first").height() + 40;
			} else {										// html
				height = $.browser.msie
					? body[0].scrollHeight + 60
					: body[0].scrollHeight + 10;
			}

			// we need to animate the iframe as IE is a bit slow and doesn't initially resize the hidden iframe properly (tabs)
			$(iframe).animate({ "opacity": '1' }, 10).css({ height: height, width: "100%" });
		}
	},

	reload_iframe: function(iframe) {
		if (iframe.contentWindow && iframe.contentDocument) {	// modern browsers IE8, FF
			iframe.contentDocument.location.reload(true);
		} else {												// IE 7 and lower
			iframe.contentWindow.location.reload(true);
		}
	},

	remove_cover: function() {
		$("#cover").remove();
	},

	CollapsableSlideTime: 200,
	collapsablePanels: {
		init: function() {
			$("div.collapsable.open").next().show();

			$("div.collapsable").live("click", function() {
				var panel = $(this);
				var panelContent = panel.next("div.collapsable-content");

				if (panelContent.is(":visible")) {
					Util.collapsablePanels.close(panel, panelContent);
				} else {
					Util.collapsablePanels.open(panel, panelContent);
				}
			});
		},

		open: function(panel, panelContent) {
			panel.addClass("open");
			panelContent.slideDown(Util.CollapsableSlideTime);
		},

		close: function(panel, panelContent) {
			panelContent.slideUp(Util.CollapsableSlideTime, function() { panel.removeClass("open"); });
		}
	},

	accountSwitcher: {
		init: function() {
			$("#account-switcher.multi").find('p').click(Util.accountSwitcher.open);
		},

		checkMouseClick: function(e) {
			var t;

			e = e || window.event;
			if (e.target) {
				t = e.target;
			} else if (e.srcElement) {
				t = e.srcElement;
			} else {
				return;
			}

			if (t.nodeType == 3) {
				t = t.parentNode;
			}

			if ($(t).closest("#account-switcher").exists() == false) {
				Util.accountSwitcher.close();
			}
		},

		open: function() {
			var switcher = $("#switcher");
			if (switcher.html() == "") {
				$.ajax({
					dataType: "html",
					url: "/Home/AccountSwitcher",
					success: function(result) {
						switcher.html(result);

						switcher.slideDown(function() {
							$("#account-switcher.multi").addClass("open");
						});
						$(document).mousedown(Util.accountSwitcher.checkMouseClick);
					}
				});
			} else {
				switcher.slideDown(function() {
					$("#account-switcher.multi").addClass("open");
				});
				$(document).mousedown(Util.accountSwitcher.checkMouseClick);
			}
		},

		close: function() {
			$("#switcher").slideUp(function() {
				$("#account-switcher.multi").removeClass("open");
			});
			$(document).unbind("mousedown");
		}
	},

	buttonDisabler: {
		init: function() {
			$("a.disabled").each(function() {
				$(this).find("img").css({ "background-color": "#ECECEC", "opacity": "0.5" });
			}).live("click", function(e) { e.preventDefault(); });
		}
	},

	toggleFullscreenView: {
		init: function() {
			var toggle = $("a.togglefullscreen");
			Util.toggleFullscreenView.checkIsItFullScreenOnLoad();

			toggle.click(function() {
				Util.toggleFullscreenView.checkIsItFullScreen();
			});
		},

		checkIsItFullScreenOnLoad: function() {
			var toggle = $("a.togglefullscreen");

			if ($("body").hasClass("fullscreen")) {
				toggle.text("Normal Screen");
			}
			else {
				toggle.text("Full Screen");
			}
		},

		checkIsItFullScreen: function() {
			var toggle = $("a.togglefullscreen");
			var isItFullScreen = Util.cookies.read("showfullscreen");

			if (isItFullScreen == "true") {
				$("body").removeClass("fullscreen");
				Util.cookies.create("showfullscreen", "false", 365);
				toggle.text("Full Screen");
			} else {
				$("body").addClass("fullscreen");
				Util.cookies.create("showfullscreen", "true", 365);
				toggle.text("Normal Screen");
			}
		}
	},

	cookies: {
		read: function(name) {
			var nameEq = name + '=';
			var ca = document.cookie.split(';');
			for (var i = 0; i < ca.length; i++) {
				var c = ca[i].replace(/^\s*|\s*$/g, ""); // stoopid IE doesn't know "".trim()!!!
				if (c.indexOf(nameEq) == 0) {
					return c.substring(nameEq.length, c.length);
				}
			}
			return null;
		},

		create: function(name, value, days, path) {
			path = $.browser.msie
				? '/'				// IE sux at following the path rules properly so dump them.
				: path || '/';
			var expiry = new Date();
			expiry.setTime(expiry.getTime() + (days * 24 * 60 * 60 * 1000));
			document.cookie = name + '=' + escape(value) + "; expires=" + expiry.toUTCString() + "; path=" + path;
		},

		erase: function(name, path) {
			path = path || '/';
			var expiry = "Thu, 01 Jan 1970 00:00:01 GMT";
			document.cookie = name + "=; expires=" + expiry + "; path=" + path;
		}
	},

	widgets: {
		manageWidgets: null,

		init: function() {
			Util.widgets.manageWidgets = $("div.manage-widget");

			var widgetsUl = $("ul.widgets");

			if (widgetsUl.exists() == false) {
				return;
			}
			$("a.collapse-widget").live("click", function() {
				var title = $(this).toggleClass("collapsed").closest("div.title");

				title.next().slideToggle(200, function() {
					title.toggleClass("open");
				});
			});
			Util.widgets.manageWidgets.find("> a").live("click", function() {
				var openMe = $(this).next("ul");

				if (openMe.is(":visible")) {
					Util.widgets.close();
				} else {
					Util.widgets.open(openMe);
				}
			});
		},
		open: function(openMe) {
			$(document).mousedown(Util.widgets.checkMouseClick);
			Util.widgets.manageWidgets.find("ul:visible").not(openMe).hide();
			openMe.show();
		},
		close: function() {
			$(document).unbind("mousedown");
			Util.widgets.manageWidgets.find("ul:visible").hide();
		},
		checkMouseClick: function(e) {
			e = e || window.event;
			var t = e.target || e.srcElement;

			if (t.nodeType == 3) {
				t = t.parentNode;
			}

			if ($(t).closest("div").hasClass("manage-widget") == false) {
				Util.widgets.close();
			}
		}
	},

	help: {
		init: function() {
			var header = $("#sidebar").find(".context-menu.help").find("h3");
			Util.help.checkIsHelpOpenOnLoad();
			header.click(function() { Util.help.checkIsHelpOpen(); });
		},

		checkIsHelpOpenOnLoad: function() {
			var toggle = $("#sidebar").find(".context-menu.help"),
				ul = toggle.find("ul");

			if ($("body").hasClass("help-open")) {
				ul.show();
				toggle.addClass("open");
			}
		},

		checkIsHelpOpen: function() {
			var toggle = $("#sidebar").find(".context-menu.help");
			var ul = toggle.find("ul");
			var isHelpOpen = Util.cookies.read("ishelpopen");
			var body = $("body");

			if (isHelpOpen == "true") {
				body.removeClass("help-open");
				ul.slideUp(200);
				toggle.removeClass("open");
				Util.cookies.create("ishelpopen", "false", 365);
			} else {
				body.addClass("help-open");
				ul.slideDown(200);
				toggle.addClass("open");
				Util.cookies.create("ishelpopen", "true", 365);
			}
		}
	},

	tabs: {
		init: function() {
			$("ul.tabs > li > a").click(function(e) {
				e.preventDefault();
				var me = $(this);
				var divToShow = me.attr("href");
				var tabs = me.closest("ul.tabs");

				if (divToShow.indexOf('#') == 0) {
					var divToHide = tabs.find("li.selected").find('a').attr("href");
					if (divToHide.indexOf('#') == 0) {
						$(divToHide).hide();
					}

					$(divToShow).show();
				}

				if (me.parent().hasClass("selected")) {
					return;
				} else {
					tabs.find("li.selected").removeClass("selected");
					me.parent().addClass("selected");
				}
			});
		}
	},

	insertAtCaret: function(element, value, selection) {
		$(element).replaceSelection(value, selection);
	},

	contactUs: {
		init: function() {
			$("#contactus").click(function(e) {
				e.preventDefault();

				var dlgContainer = $("#contactus-dialog-container");
				if (dlgContainer.find("#contact_form").exists()) {
					Util.showDialog($("#contact_form"));
					return;
				}

				$.ajax({
					dataType: "html",
					url: $(this).find('a').attr("href"),
					success: function(result) {
						dlgContainer.children().remove();
						dlgContainer.append(result);

						var dialog = $("#contact_form");

						var cancelContactDialog = $("#cancel-contact-dialog");
						var contactSubmitDialog = $("#contact-submit-dialog");

						var iframe = $("#contactus_iframe");
						var windowHeight = $(window).height();
						var height = (windowHeight - (windowHeight * 0.2)) - 70;
						var contents;

						if (height < 531) {
							iframe.css({ height: height });
						}

						iframe.load(function() {
							contents = iframe.contents();

							var contentHeight = contents.height();

							if (contentHeight > iframe.height()) {
								iframe.css({ height: contentHeight });
							}

							var inputs = contents.find("input.textfield");

							if (inputs.exists()) {
								var details = $("#contactus").find('a').attr("rel").split('&');
								var vars = [], hash;

								for (var i = 0; i < details.length; i++) {
									hash = details[i].split('=');
									vars.push(hash[0]);
									vars[hash[0]] = hash[1];
								}

								if (inputs[0])
									inputs[0].value = vars["name"];
								
								if (inputs[1])
									inputs[1].value = vars["email"];
							}

							if (iframe.showMe) {
								iframe.showMe = false;
								Util.showDialog(dialog);
							} else {
								$("#loading_message").hide();
								$("#inner_loading_container").css({ visibility: "visible" });
							}
						});

						cancelContactDialog.click(function() {
							Util.hideDialog(dialog);
						});
						contactSubmitDialog.click(function() {
							contents.find("form").trigger("submit");
						});

						if (iframe.showMe == null) {
							iframe.showMe = false;
							Util.showDialog(dialog);
						} else {
							iframe.showMe = true;
							iframe[0].src = iframe[0].src;
							iframe.height("530px");
						}
						cancelContactDialog.find("span").text("Cancel");
						contactSubmitDialog.show();
					}
				});
			});
		}
	},

	localStorage: {
		init: function() {
			var me = this;
			var html = me.getItem("accountSwitcher");

			//account switcher html
			if (!html) {
				var accountSwitcherHtml;

				//TODO: ajax call to get account switcher HTML

				me.setItem("accountSwitcher", accountSwitcherHtml);	//BUG: accountSwitcherHtml is unassigned!
			} else {
				$("#account-switcher").html(html);
			}
		},
		getItem: function(key) {
			return localStorage.getItem(key);
		},
		setItem: function(key, value) {
			return localStorage.setItem(key, value);
		},
		removeItem: function(key) {
			return localStorage.removeItem(key);
		}
	},

	Url: {
		// public method for url encoding
		encode: function(string) {
			return escape(this._utf8_encode(string));
		},

		// public method for url decoding
		decode: function(string) {
			return this._utf8_decode(unescape(string));
		},

		// private method for UTF-8 encoding
		_utf8_encode: function(string) {
			string = string.replace(/\r\n/g, "\n");
			var utftext = "";
			for (var n = 0; n < string.length; n++) {
				var c = string.charCodeAt(n);
				if (c < 128) {
					utftext += String.fromCharCode(c);
				}
				else if ((c > 127) && (c < 2048)) {
					utftext += String.fromCharCode((c >> 6) | 192);
					utftext += String.fromCharCode((c & 63) | 128);
				}
				else {
					utftext += String.fromCharCode((c >> 12) | 224);
					utftext += String.fromCharCode(((c >> 6) & 63) | 128);
					utftext += String.fromCharCode((c & 63) | 128);
				}
			}
			return utftext;
		},

		// private method for UTF-8 decoding
		_utf8_decode: function(utftext) {
			var string = "";
			var i = 0;
			var c;
			var c2;
			while (i < utftext.length) {
				c = utftext.charCodeAt(i);
				if (c < 128) {
					string += String.fromCharCode(c);
					i++;
				}
				else if ((c > 191) && (c < 224)) {
					c2 = utftext.charCodeAt(i + 1);
					string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
					i += 2;
				}
				else {
					c2 = utftext.charCodeAt(i + 1);
					var c3 = utftext.charCodeAt(i + 2);
					string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
					i += 3;
				}
			}
			return string;
		}
	}
};

var Support = {
	canvas: function() {
		return !!document.createElement("canvas").getContext;
	},
	geolocation: function() {
		return !!navigator.geolocation;
	},
	localstorage: function() {
		return !!window.localStorage;
	},
	database: function() {
		return !!window.openDatabase;
	}
};

