CoreModule={"imageURL":"http:\/\/images.nexopia.com","wwwURL":"http:\/\/www.nexopia.com","staticFilesURL":"http:\/\/static.nexopia.com\/35003\/files","userFilesURL":"http:\/\/users.nexopia.com","adminURL":"http:\/\/www.nexopia.com\/admin","selfURL":"http:\/\/www.nexopia.com\/my","adminSelfURL":"http:\/\/www.nexopia.com\/admin\/self","staticURL":"http:\/\/static.nexopia.com","uploadURL":"http:\/\/www.nexopia.com\/upload","styleURL":"http:\/\/static.nexopia.com\/35003\/style","userURL":"http:\/\/www.nexopia.com\/users","scriptURL":"http:\/\/static.nexopia.com\/35003\/script","smilies":{':)': "smile",':(': "frown",':D': "biggrin",':imslow:': "imslow",':p': "tongue",':P': "tongue",':blush:': "blush",':hearts:': "hearts",':rolleyes:': "rolleyes",':love:': "love",':rofl:': "rofl",':gjob:': "thumbs",':sex:': "smileysex",':err:': "err",';)': "wink",':clap:': "clap",':date:': "date",':cry:': "crying",':drool:': "drool",':eek:': "eek",':beer:': "beer",':foie:': "nono",':cool:': "cool",':shocked:': "shocked",':cussing:': "cussing",':shifty:': "shiftyeyes",':omfg:': "omfg",':confused:': "confused",':nuts:': "silly",':evil:': "evil",':gdate:': "girlgirl",':headache:': "headache",':headbang:': "headbang",':ham:': "smash",':high5:': "high5",':hug:': "hugs",':hungry:': "hungry",':iik:': "iik",':jawdrop:': "jawdrop",':jk:': "jk",':kiss:': "kiss",':lol:': "lol",':lonely:': "lonely",':bdate:': "manman",':moon:': "moon",':music:': "music",':O': "yawn",':nana:': "nana",':neutral:': "neutral",':party:': "party",':pee:': "pee",':phone:': "phone",':please:': "please",':pray:': "pray",':psyco:': "psyco",':puke:': "puke",':egrin:': "egrin",':transport:': "transport",':crazy:': "crazy",':skull:': "skull",':sleep:': "sleep",':tv:': "tv",':steaming:': "steaming",':stun:': "stun",':typing:': "typing",':throwball:': "throwball",':show:': "show",':speak:': "speak",':bjob:': "nogood-red",':wassup:': "wassup",':no:': "no"}};
			CoreModule.coloredImgURL = function(color)
			{
				if (color.match('rgb'))
				{
					color = Nexopia.Utilities.getHexValue(color);
				}
				
				var rubyURL = "http://images.nexopia.com/recolour/__color__/35003";
				return rubyURL.replace(/__color__/, color.replace(/#/, ''));
			};

Site = CoreModule;
Overlord = {
	jobs: {
		all: [], //list of all the jobs we need taken care of
		load: [], //list of jobs to be initialized for the window.onload event
		dom: [], //list of jobs to be initialized at the onDOMReady event
		available: [] //list of jobs to be initialized immediately after their elements are on the page
	},
	assign: function(job) {
		//you can pass in either just a raw object that would be the constructor argument to Overlord.Job
		//or an Overlord.Job object
		if (!this.Job.prototype.isPrototypeOf(job)) {
			job = new this.Job(job);
		}
		this.jobs.all.push(job);
		this.jobs[job.init].push(job);
	},
	minionAvailable: function(id) {
		this.summonMinions(YAHOO.util.Dom.get(id), 'available');
	},
	summonMinions: function(root, init) {
		
		start_begin = (new Date()).getTime();
		init = init || 'all';
		if (init) {
			
			var minionsList = [];
			//find the minion elements we care about and sort them by name
			if (init != 'available') {
				//Since we need to get the same list for both the load and the dom steps lets cache the first one
				//to complete and use it for the other saving ourselves a full dom scan.
				if (!root && this.fullMinionsList) {
					minionsList = this.fullMinionsList;
				} else {
					minionsList = this.findMinions(root);
					this.fullMinionsList = minionsList;
				}
			} else {
				minionsList = [root];
			}
			
			var minions = {};
			for (var i=0; i<minionsList.length; i++) {
				var minion = minionsList[i];
				var names = minion.getAttribute('minion_name');
				names = names.split(' ');
				for (var n=0; n<names.length; n++) {
					var name = names[n];
					minions[name] = minions[name] || [];
					minions[name].push(minion);
				}
			}
			
			//get the jobs we care about
			var jobs = this.jobs[init];
			//sort the jobs by priority
			jobs.sort(function(a, b) {return a.order - b.order;}); //lowest order happens first

			//assign jobs to their minions if their minions exist
			start_assigns = (new Date()).getTime();
			for (var j=0; j<jobs.length; j++) {
				var job = jobs[j];
				if (minions[job.minion]) {
					//assign each  minion with the name job.minion the job
					for (var m=0; m<minions[job.minion].length; m++) {
						try {
							job.assign(minions[job.minion][m]);
							//YAHOO.log("Assigned " + minion, 'time', 'Overlord');
						} catch (err) {
							YAHOO.log("***** Overlord: Assignment of the job "+job.minion+" failed on the minion #"+minions[job.minion][m].id+"."+minions[job.minion][m].className, 'error');
							YAHOO.log(err, 'error');
							console.error(err);
							throw err;
						}
					}
				}
			}
		}	
		
		duration_begin = ((new Date()).getTime() - start_begin);
		duration_assigns = ((new Date()).getTime() - start_assigns);
		if (init == 'available') {
			YAHOO.log("Assignment of " + root.getAttribute('minion_name') + " completed in " + duration_begin + "ms (" + duration_assigns + "ms for assigns).", 'time', 'Overlord');
		} else {
			YAHOO.log("Assignment for initialization period " + init + " completed in " + duration_begin + "ms (" + duration_assigns + "ms for assigns).", 'time', 'Overlord');
		}
	},
	findMinions: function(root) {
		root = [].concat(root);
		var found = [];
		for (var i=0; i<root.length; i++) {
			if (root[i] && Overlord.isMinion(root[i])) {
				found.push(root[i]); //getElementsBy doesn't include its root when it is searching
			}
			found = found.concat(YAHOO.util.Dom.getElementsBy(Overlord.isMinion, null, root[i]));
		}
		return found;
	},
	isMinion: function(element) {
		return element.getAttribute('minion_name');
	},
	toString: function() {
		var targetedNames = [];
		for (var j=0; j<this.jobs.length; j++) {
			targetedNames.push(this.jobs[j].minion);
		}
		return "Overlord<" + targetedNames.join(', ')+ ">";
		
	}
};

Overlord.Job = function(description) {
	this.tasks = {};
	for (task in description) {
		//Assume any task that is a function and we don't have a default 
		//for is an event to register.  The task click would contain a function 
		//for onclick.
		if (task == "priority") { //priority was used in ScriptManager, order should be used for Overlord
			if (console) {
				console.error(description);
			}
			throw "Attempted to use priority in an Overlord Job for " + description["minion"] + ".";
		} else if (this[task] === undefined && Function.prototype.isPrototypeOf(description[task])) {
			this.addTask(task, description[task]);
		} else {
			this[task] = description[task]; //override default properties
		}
	}
};

Overlord.Job.prototype = {
	minion: null, //the name of the minion this job should be assigned to
	load: null, //function called on load at page load or via ajax
	unload: null, //function called when the element is removed or unloaded
	scope: null, //optional object to execute in the scope of, if it is missing the minion will be used as a scope
	order: 0, //set this value to adjust the order the script is excuted in, lower numbers happen first
	tasks: null, //a map of event names to handling functions
	init: 'dom', //(load (the window.onload event) | dom (yui's ondomready event) | available (as soon as the element is in the dom)) 
	assign: function(minion) {
		
		var scope = this.scope || minion;
		if (this.load) {
			this.load.call(scope, minion);
		}
		for (task in this.tasks) {
			YAHOO.util.Event.on(minion, task, this.tasks[task], minion, scope);
		}
	},
	unassign: function(minion) {
		if (this.unload) {
			var scope = this.scope || minion;
			this.unload.call(scope, minion);
		}
	},
	addTask: function(name, task) {
		this.tasks[name] = task;
	}
};

YAHOO.util.Event.on(window, 'load', function() {Overlord.summonMinions(null, 'load');});
YAHOO.util.Event.onDOMReady(function() {Overlord.summonMinions(null, 'dom');});

//require overlord.js
function init_accordions()
{
	var speed = 0.15;

	var accordions = YAHOO.util.Dom.getElementsByClassName("accordion_view");
	for(j = 0; j < accordions.length; j++)
	{
		accordions[j].meta = {};
		
		var handles = YAHOO.util.Dom.getChildrenBy(accordions[j], function(el){return YAHOO.util.Dom.hasClass(el, 'accordion_handle');});
		var bodies = YAHOO.util.Dom.getChildrenBy(accordions[j], function(el){return YAHOO.util.Dom.hasClass(el, 'accordion_body');});
		if(handles.length != bodies.length) return;
		
		for(i = 0; i < handles.length; i++)
		{
			bodies[i].meta = {};    handles[i].meta = {};
			
			bodies[i].meta.originalHeight = YAHOO.util.Dom.getRegion(bodies[i]).bottom - YAHOO.util.Dom.getRegion(bodies[i]).top;
			bodies[i].meta.originalPaddingTop = parseInt(YAHOO.util.Dom.getStyle(bodies[i], "padding-top"), 10);
			
			// TODO: Fix this IE bug which only seems to come up in our framework
			if(YAHOO.env.ua.ie)
				collapsedHeight = 0;
			else
				collapsedHeight = 0;
			
			YAHOO.util.Dom.setStyle(bodies[i], "height", collapsedHeight);
			YAHOO.util.Dom.setStyle(bodies[i], "padding-top", 0);
			YAHOO.util.Dom.setStyle(bodies[i], "padding-bottom", 0);
			
			YAHOO.util.Event.on(handles[i], 'click', function(e, source)
			{
				handle = source[0];
				body = source[1];
				accordion = source[2];
				
				if(handle != accordion.meta.active[0])
				{
					var oldAttributes = {
						height: { to: collapsedHeight },
						paddingTop: { to: 0 }
					};
					var oldAnimation = new YAHOO.util.Motion(accordion.meta.active[1], oldAttributes, speed);
					var newAttributes = {
						height: { to: body.meta.originalHeight - body.meta.originalPaddingTop },
						paddingTop: { to: body.meta.originalPaddingTop }
					};
					var newAnimation = new YAHOO.util.Motion(body, newAttributes, speed);
					
					oldAnimation.animate();
					newAnimation.animate();

					YAHOO.util.Dom.removeClass(accordion.meta.active[0], "accordion_handle_active");
					YAHOO.util.Dom.addClass(handle, "accordion_handle_active");
					YAHOO.util.Dom.removeClass(accordion.meta.active[1], "accordion_body_active");
					YAHOO.util.Dom.addClass(body, "accordion_body_active");
					
					accordion.meta.active = [handle, body];
				}
				else
				{
/*
					var oldAttributes = {
						height: { to: collapsedHeight },
						paddingTop: { to: 0 }
					};
					var oldAnimation = new YAHOO.util.Motion(accordion.meta.active[1], oldAttributes, speed);
					
					oldAnimation.animate();
					
					YAHOO.util.Dom.removeClass(accordion.meta.active[0], "accordion_handle_active");
					YAHOO.util.Dom.removeClass(accordion.meta.active[1], "accordion_body_active");
					
					accordion.meta.active = ["null", "null"];
*/
				}
				
			}, [handles[i], bodies[i], accordions[j]]);
		}
		var firstAttributes = {
			height: { to: bodies[0].meta.originalHeight - bodies[0].meta.originalPaddingTop },
			paddingTop: { to: bodies[0].meta.originalPaddingTop }
		};
		var firstAnimation = new YAHOO.util.Motion(bodies[0], firstAttributes, speed);
		firstAnimation.animate();
		YAHOO.util.Dom.addClass(handles[0], "accordion_handle_active");
		YAHOO.util.Dom.addClass(bodies[0], "accordion_body_active");
		accordions[j].meta.active = [handles[0], bodies[0]];
		
/* 		accordions[j].meta.active = ["null", "null"]; */
	}
};

Overlord.assign({
	minion: "interests_init",
	load: init_accordions
});

Overlord.assign({
	minion: "skin_edit_wrapper",
	load: init_accordions
});
/*
	The element provided should be followed by these sibling classes:
	
	matches: A div that will hold the query results and allow the user to select one.
	location_id: A hidden field that will hold the id of the location. Defaults to 0.
	query_sublocations: A hidden field that will hold a true/false value indicating whether the location has sublocations
	location_type: A hidden field indicating the type of location to look up.
		- If empty: the query will be done on all locations
		- If "C": the query will be done on cities
		- If "S": the query will be done on states/provinces
		- If "N": the query will be done on countries (nations)
	location_name_default: A hidden field containing the default value for a location field (when there's no location selected)
*/
function LocationAutocomplete(element)
{
	this.locationNameField = element;
	this.locationMatchDiv = YAHOO.util.Dom.getNextSiblingBy(element, function(el){ return YAHOO.util.Dom.hasClass(el, 'matches'); });
	this.locationIDField = YAHOO.util.Dom.getNextSiblingBy(element, function(el){ return YAHOO.util.Dom.hasClass(el, 'location_id'); });
	this.locationQuerySubLocationsField = YAHOO.util.Dom.getNextSiblingBy(element, function(el){ return YAHOO.util.Dom.hasClass(el, 'query_sublocations'); });
	this.locationTypeField = YAHOO.util.Dom.getNextSiblingBy(element, function(el){ return YAHOO.util.Dom.hasClass(el, 'location_type'); });
	this.locationNameDefaultField = YAHOO.util.Dom.getNextSiblingBy(element, function(el){ return YAHOO.util.Dom.hasClass(el, 'location_name_default'); });
	this.locationNameDefault = this.locationNameDefaultField.value;
			
	this.initialize();
}

LocationAutocomplete.prototype = {
	initialize: function()
	{		
		if(this.locationTypeField.value && this.locationTypeField.value != "")
		{
			this.locationQuery = "/core/query/location/" + this.locationTypeField.value
		}
		else
		{
			this.locationQuery = "/core/query/location"
		}
		
	    // DataSource setup
	    this.locationDataSource = new YAHOO.widget.DS_XHR(this.locationQuery,
	        ["location", "name", "id", "extra", "query_sublocations"]);
	    this.locationDataSource.scriptQueryParam = "name";
	    this.locationDataSource.responseType = YAHOO.widget.DS_XHR.TYPE_XML;
	    this.locationDataSource.maxCacheEntries = 60;
		// this.locationDataSource.queryMatchSubset = true;

		this.locationDataSource.connXhrMode = "cancelStaleRequests";

	    // Instantiate AutoComplete
	    this.locationAutoLookup = new YAHOO.widget.AutoComplete(this.locationNameField.id, this.locationMatchDiv.id, this.locationDataSource);
	    
		// AutoComplete configuration
		this.locationAutoLookup.autoHighlight = true;
		// this.locationAutoLookup.typeAhead = true;
		this.locationAutoLookup.minQueryLength = 1;
		this.locationAutoLookup.queryDelay = 0;
		this.locationAutoLookup.forceSelection = true;
		this.locationAutoLookup.maxResultsDisplayed = 10;
	
		// Fix silly IE6 bug
		if (YAHOO.env.ua.ie > 5 && YAHOO.env.ua.ie <= 7)
		{
			this.locationAutoLookup.useIFrame = true;
		}
		
		var itemSelectHandler = function(eventType, args, obj) {
			var selectedElement = args[2];
			
			var name = selectedElement[0];
			var id = selectedElement[1];
			var extra = selectedElement[2];
			var subLocations = selectedElement[3];
			
			obj.locationIDField.value = id;
			obj.locationQuerySubLocationsField.value = subLocations;
		};
		this.locationAutoLookup.itemSelectEvent.subscribe(itemSelectHandler, this);
	
		var forceSelectionClearHandler = function(eventType, args, obj)
		{
			obj.locationIDField.value = 0;
			obj.locationQuerySubLocationsField.value = "true";
			obj.locationNameField.value = obj.locationNameDefault;
			YAHOO.util.Dom.addClass(obj.locationNameField, "default");
		};
		this.locationAutoLookup.selectionEnforceEvent.subscribe(forceSelectionClearHandler, this);
	
		// HTML display of results
		this.locationAutoLookup.formatResult = function(result, sQuery) {
			// This was defined by the schema array of the data source
			var name = result[0];
			var id = result[1];
			var extra = result[2];
			
			var extraInfo = "";
			if (extra != undefined && extra != "")
				extraInfo = "<br/>" + "<span style='font-size: 10px; color: grey'>" + " ("+ extra + ")" + "</span>";
			
			return name + extraInfo;
		};
		
		var self = this;
		YAHOO.util.Event.addListener(this.locationNameField, "focus", function() {
			if(self.locationNameField.value == self.locationNameDefault)
			{
				self.locationNameField.value = "";
				YAHOO.util.Dom.removeClass(self.locationNameField, "default");
			}
		});		
	}	
};

Overlord.assign({
	minion: "location_autocomplete",
	load: function(element) {
		new LocationAutocomplete(element);
	}
});

/*
	The element provided should be followed by these sibling classes:

	matches: A div that will hold the query results and allow the user to select one.
	school_id: A hidden field that will hold the id of the school. Defaults to 0.
	school_name_default: A hidden field containing the default value for a school field (when there's no school selected)
*/
function SchoolAutocomplete(element)
{
	this.schoolNameField = element;
	this.schoolMatchDiv = YAHOO.util.Dom.getNextSiblingBy(element, function(el){ return YAHOO.util.Dom.hasClass(el, 'matches'); });
	this.schoolIDField = YAHOO.util.Dom.getNextSiblingBy(element, function(el){ return YAHOO.util.Dom.hasClass(el, 'school_id'); });
	this.schoolNameDefaultField = YAHOO.util.Dom.getNextSiblingBy(element, function(el){ return YAHOO.util.Dom.hasClass(el, 'school_name_default'); });
	this.schoolNameDefault = this.schoolNameDefaultField.value;
	
	this.initialize();
}

SchoolAutocomplete.prototype = {
	initialize: function()
	{
	    // DataSource setup
	    this.schoolDataSource = new YAHOO.widget.DS_XHR("/core/query/school",
	        ["school", "name", "id", "extra"]);
	    this.schoolDataSource.scriptQueryParam = "name";
	    this.schoolDataSource.responseType = YAHOO.widget.DS_XHR.TYPE_XML;
	    this.schoolDataSource.maxCacheEntries = 60;

		this.schoolDataSource.connXhrMode = "cancelStaleRequests";

	    // Instantiate AutoComplete
	    this.schoolAutoLookup = new YAHOO.widget.AutoComplete(this.schoolNameField.id, this.schoolMatchDiv.id, this.schoolDataSource);
	    
		// AutoComplete configuration
		this.schoolAutoLookup.autoHighlight = true;

		this.schoolAutoLookup.minQueryLength = 1;
		this.schoolAutoLookup.queryDelay = 0.2;
		this.schoolAutoLookup.forceSelection = true;
		this.schoolAutoLookup.maxResultsDisplayed = 10;
	
		// Fix silly IE6 bug
		if (YAHOO.env.ua.ie > 5 && YAHOO.env.ua.ie <= 7)
		{
			this.schoolAutoLookup.useIFrame = true;
		}
		
		var itemSelectHandler = function(eventType, args, obj) {
			var selectedElement = args[2];
			
			var name = selectedElement[0];
			var id = selectedElement[1];
			var extra = selectedElement[2];
			
			obj.schoolIDField.value = id;
		};
		this.schoolAutoLookup.itemSelectEvent.subscribe(itemSelectHandler, this);
	
		var forceSelectionClearHandler = function(eventType, args, obj)
		{
			obj.schoolIDField.value = 0;
			obj.schoolNameField.value = obj.schoolNameDefault;
			YAHOO.util.Dom.addClass(obj.schoolNameField, "default");
		};
		this.schoolAutoLookup.selectionEnforceEvent.subscribe(forceSelectionClearHandler, this);
	
		// HTML display of results
		this.schoolAutoLookup.formatResult = function(result, sQuery) {
			// This was defined by the schema array of the data source
			var name = result[0];
			var id = result[1];
			var extra = result[2];
			
			var extraInfo = "";
			if (extra != undefined && extra != "")
				extraInfo = "<br/>" + "<span style='font-size: 10px; color: grey'>" + " ("+ extra + ")" + "</span>";
			
			return name + extraInfo;
		};
		
		var self = this;
		YAHOO.util.Event.addListener(this.schoolNameField, "focus", function() {
			if(self.schoolNameField.value == self.schoolNameDefault)
			{
				self.schoolNameField.value = "";
				YAHOO.util.Dom.removeClass(self.schoolNameField, "default");
			}
		});
	}	
};

Overlord.assign({
	minion: "school_autocomplete",
	load: function(element) {
		new SchoolAutocomplete(element);
	}
});
Overlord.assign({
	minion: "button_link",
	click: function(event, element) {
		path = element.getAttribute("path");
		if (path) {
			YAHOO.util.Event.preventDefault(event);
			document.location = path;
		}
	}
});

Overlord.assign({
	minion: "button",
	mousedown: function(event, element) {
		YAHOO.util.Dom.addClass(element, "pressed");
	},
	mouseup: function(event, element) {
		YAHOO.util.Dom.removeClass(element, "pressed");
	},
	order: -1
});
/*
	The ScriptManager allows you to attach scripts to elements specified by css3 selector rules.
	Load/Unload events get fired based on page load/unload as well as ajax load/unload through the ResponseHandler.
	Arbitrary events can also be attached (click, blur, focus, etc.) these events are also properly migrated
	when nodes are replaced through the ResponseHandler.
	
	Basic usage:
		ScriptManager.register("my css3 selector rule", {
			load: function(element) {
				//do some initialization
			},
			unload: function(element) {
				//do some uninitialization
			},
			click: function(event, element) {
				//onclick handler
			},
			arbitraryevent: function(event, element) {
				//onarbitraryevent handler
			},
			scope: someObject, //the object to execute all functions in the scope of
		});
	
	All options in the options object are optional.  As a general guideline it is best
	to avoid css selectors that aren't prefixed with some id, eg. ".myclass" is bad "#myid .myclass"
	is good.  This is necessary because doing full tree scans to find the classes on
	every page for every css selector for the entire site would be very expensive.  The id
	allows it to immediately eliminate pages without the id.
*/
ScriptManager = {
	toString: function() {
		return this.scripts.join("\n");
	},
	register: function(selector, options) {
		this.scripts.push(new ScriptManager.Script(selector, options));
	},
	setup: function(event, rootNodeOrNodes) {
		rootNodeOrNodes = [].concat(rootNodeOrNodes);
		this.scripts.sort(function(a,b) {
			return b.priority - a.priority;
		});
		for (var i=0; i<this.scripts.length; i++) {
			this.scripts[i].setup(rootNodeOrNodes);
		}
	},
	teardown: function(event, rootNode) {
		for (var i=0; i<this.scripts.length; i++) {
			this.scripts[i].teardown(rootNode);
		}
	},
	scripts: []
};

ScriptManager.Script = function(selector, options) {
	this.selector = selector;
	for (option in options) {
		//Assume any option that is a function and we don't have a default 
		//for is an event to register.  The option click would contain a function 
		//for onclick.
		if (this[option] === undefined && Function.prototype.isPrototypeOf(options[option])) {
			this.registerEventHandler(option, options[option]);
		}
		this[option] = options[option];
	}
};

ScriptManager.Script.prototype = {
	load: null, //function called on load at page load or via ajax
	unload: null, //function called when the element is removed or unloaded
	scope: null, //optional object to execute in the scope of, if it exists load and unload will be called on it
	limitToContext: false, //set this true if you don't want to check upwards from the inserted node when doing matches (generally should not be used for id based selectors)
	priority: 0, //set this value to adjust priority for a script, higher priorities load sooner
	toString: function() {
		return this.selector;
	},
	registerEventHandler: function(event, func) {
		if (!this.eventHandlers) {
			this.eventHandlers = {};
		}
		this.eventHandlers[event] = func;
	},
	setup: function(rootNodes) {
		var that = this;
		if (this.load || this.eventHandlers) {
			if (this.limitToContext) {
				for (var i=0; i<rootNodes.length; i++) {
					var rootNode = rootNodes[i];
					$(this.selector, rootNode).each(function(index, match){
						for (var event in that.eventHandlers) {
							YAHOO.util.Event.on(match, event, that.eventHandlers[event], match, that.scope);
						}
						if (that.load) {
							if (that.scope) {
								that.load.call(that.scope, match);
							} else {
								that.load.call(match, match);
							}
						}
					});
				}
			} else {
				$(this.selector).each(function(index, match){
					for (var i=0; i<rootNodes.length; i++) {
						var rootNode = rootNodes[i];
						if (!rootNode || rootNode == match || YAHOO.util.Dom.isAncestor(rootNode, match)) {
							for (var event in that.eventHandlers) {
								YAHOO.util.Event.on(match, event, that.eventHandlers[event], match, that.scope);
							}
							if (that.load) {
								if (that.scope) {
									that.load.call(that.scope, match);
								} else {
									that.load.call(match, match);
								}
							}
						}
					}
				});
			}
		}
	},
	teardown: function(rootNode) {
		if (this.unload) {
			var that = this;
			var context = null;
			if (this.limitToContext) {
				context = rootNode;
			}
			$(this.selector, context).each(function(index, match){
				if (!rootNode || context || rootNode == match || YAHOO.util.Dom.isAncestor(rootNode, match)) {
					if (that.scope) {
						that.unload.call(that.scope, match);
					} else {
						that.unload.call(match, match);
					}
				}
			});
		}
	}
};

YAHOO.util.Event.on(window, 'load', function() {
	if (!document.getElementById("disable_script_manager")) {
		ScriptManager.setup();
	}
});
YAHOO.util.Event.on(window, 'unload', ScriptManager.teardown, null, ScriptManager);
//require script_manager.js

function CharacterCounter(textField, remaining)
{
	textField.characterCounter = this;
	
	this.displayElement = YAHOO.util.Dom.get(textField.id + "_character_counter");
	if(!this.displayElement)
	{
		var brElement = document.createElement("br");
		var lengthText = document.createElement("span");
		lengthText.innerHTML = "Length: ";
		this.displayElement = document.createElement("span");
		this.displayElement.id = textField.id + "_character_counter";

		textField.parentNode.insertBefore(brElement, textField.nextSibling);
		textField.parentNode.insertBefore(lengthText, brElement.nextSibling);
		textField.parentNode.insertBefore(this.displayElement, this.displayElement.nextSibling);
	}
	
	this.textField = textField;
	this.maxLimit = parseInt(textField.getAttribute("maxlength"), 10);
	this.remaining = (remaining == true);
	
	YAHOO.util.Event.addListener(this.textField, "change", this.update, this, true);
	YAHOO.util.Event.addListener(this.textField, "keydown", this.update, this, true);
	YAHOO.util.Event.addListener(this.textField, "keyup", this.update, this, true);

	this.update();
}


CharacterCounter.prototype = {
	update: function()
	{
		if(this.textField.value.length > this.maxLimit)
		{
			this.textField.value = this.textField.value.substring(0, this.maxLimit);
		}
		
		if(this.remaining)
		{
			this.displayElement.innerHTML = parseInt(this.maxLimit) - parseInt(this.textField.value.length);
		}
		else
		{
			this.displayElement.innerHTML = this.textField.value.length + " / " + this.maxLimit;
		}
	}
};


Overlord.assign({
	minion: "show_character_count",
	load: function(element)
	{
		new CharacterCounter(element);
	},
	sope:this
});


Overlord.assign({
	minion: "show_character_count:remaining",
	load: function(element)
	{
		new CharacterCounter(element, true);
	},
	scope:this
});
//require script_manager.js

// icon init
Overlord.assign(
{
	minion: "skin_editor_icon",
	load: function(element)
	{
		init_custom_color_icons([element]);
	}
});

// action here
function init_custom_color_icons(icons)
{
	for(var i = 0; i < icons.length; i++)
	{
		if(!YAHOO.env.ua.ie)
		{
			//if the icon isn't loaded yet come back later and redo this function
			if (icons[i].width == 0)
			{
				YAHOO.util.Event.on(icons[i], 'load', function() {
					init_custom_color_icons([this]);
					this.initialized = true;
				});
				setTimeout(function() {
				 	if (!icons[i].initialized)
				 	{
				 		init_custom_color_icons([icons[i]]);
				 		icons[i].initialized = true;
					}
				}, 2000);
				return;
			}
			icons[i].initialized = true;
			
			if (icons[i].width == 0)
			{
				throw new Exception("Uninitialized image.");
			}
			
			var canvas = document.createElement('canvas');
			
			canvas.width = icons[i].width;
			canvas.height = icons[i].height;
			
			var j;
			icon_class_list = icons[i].className.split(" ");
			for(var j = 0; j < icon_class_list.length; j++)
			{
				YAHOO.util.Dom.addClass(canvas, icon_class_list[j]);
			}
			
			YAHOO.util.Dom.setStyle(canvas, "color", YAHOO.util.Dom.getStyle(icons[i], "color"));
			YAHOO.util.Dom.setStyle(canvas, "margin", YAHOO.util.Dom.getStyle(icons[i], "margin"));
			YAHOO.util.Dom.setStyle(canvas, "padding", YAHOO.util.Dom.getStyle(icons[i], "padding"));
			YAHOO.util.Dom.setStyle(canvas, "border", YAHOO.util.Dom.getStyle(icons[i], "border"));
			
			if(icons[i].parentNode)
			{
				var attached_events = YAHOO.util.Event.getListeners(icons[i]);
				
				if(attached_events)
				{
					for(var j = 0; j < attached_events.length; j++)
					{
						if(attached_events[j])
							YAHOO.util.Event.addListener(canvas, attached_events[j].type, attached_events[j].fn, attached_events[j].obj);
					}
				}
				
				icons[i].parentNode.replaceChild(canvas, icons[i]);
				canvas.maskImage = icons[i];
				canvas.src = canvas.maskImage.src;
				canvas.id = canvas.maskImage.id;
				icons[i] = canvas;
			}
			else
			{
				return;
			}
		}
		
		icons[i].setColor = function(color)
		{
			if(!YAHOO.env.ua.ie)
			{
				if (!this.getContext) return;
				var ctx = this.getContext('2d');
				
				ctx.globalCompositeOperation = "source-over";
				ctx.clearRect(0,0,this.width,this.height);
			
				try
				{	
					ctx.drawImage(this.maskImage, 0, 0, this.width, this.height);
				}
				catch(e){}
				
				ctx.globalCompositeOperation = "source-in";
				
				// For some reason, color can be false here, so we have to check for that before trying
				// to set it.
				if (color)
				{
					ctx.fillStyle = color;
					ctx.fillRect(0,0,this.width,this.height);
				}
			}
			else
			{
				var match = getRGB(color);
				
				if(match)
				{
					match[0] = match[0].toString(16);
					match[1] = match[1].toString(16);
					match[2] = match[2].toString(16);
					
					if(match[0].length == 1)
						match[0] = "0"+match[0];
					if(match[1].length == 1)
						match[1] = "0"+match[1];
					if(match[2].length == 1)
						match[2] = "0"+match[2];
					
					// Use this new color value in the IE filter
					this.style.filter ="filter: progid:DXImageTransform.Microsoft.MaskFilter(color=#000000) progid:DXImageTransform.Microsoft.MaskFilter(color=#"+match.join("")+");";
				}
			}
		};
		
		icons[i].resetColor = function()
		{
			revertColor(icons[i]);
			this.setColor(YAHOO.util.Dom.getStyle(this, "color"));
		};

		icons[i].resetColor();
		YAHOO.util.Dom.addClass(icons[i], 'custom_color_icon');
	}
}

/********** Custom Icons with Alpha in IE **********/

/*
	NOTE:
		Due to a DOCTYPE problem in IE custom icons in IE will be rendered to display as a block element.
		For consistancy I have forced other browsers to do the same.
*/

function init_custom_color_icons_alpha(icons)
{
	for(i = 0; i < icons.length; i++)
	{
		if(!YAHOO.env.ua.ie)
		{
			var canvas = document.createElement('canvas');
			
			canvas.width = icons[i].width;
			canvas.height = icons[i].height;
			
			YAHOO.util.Dom.addClass(canvas, "custom_color_icon_alpha");
			YAHOO.util.Dom.setStyle(canvas, "display", "block");
			
			YAHOO.util.Dom.setStyle(canvas, "color", YAHOO.util.Dom.getStyle(icons[i], "color"));
			YAHOO.util.Dom.setStyle(canvas, "margin", YAHOO.util.Dom.getStyle(icons[i], "margin"));
			YAHOO.util.Dom.setStyle(canvas, "padding", YAHOO.util.Dom.getStyle(icons[i], "padding"));
			YAHOO.util.Dom.setStyle(canvas, "border", YAHOO.util.Dom.getStyle(icons[i], "border"));
			
			icons[i].parentNode.replaceChild(canvas, icons[i]);
			canvas.maskImage = icons[i];
			icons[i] = canvas;
		}
		else
		{
			var wrapper = document.createElement('div');
		
			icons[i].maskImageSrc = icons[i].src;
			icons[i].width = YAHOO.util.Dom.getRegion(icons[i]).right - YAHOO.util.Dom.getRegion(icons[i]).left;
			icons[i].height = YAHOO.util.Dom.getRegion(icons[i]).bottom - YAHOO.util.Dom.getRegion(icons[i]).top;
			
			icons[i].src = Site.staticFilesURL + "/core/custom_color_icon/transparent.gif";
			icons[i].style.filter ="filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + icons[i].maskImageSrc + "', sizingMethod='scale');";
			
			
			YAHOO.util.Dom.setStyle(wrapper, "width", YAHOO.util.Dom.getStyle(icons[i], "width"));
			YAHOO.util.Dom.setStyle(wrapper, "height", YAHOO.util.Dom.getStyle(icons[i], "height"));
			
			YAHOO.util.Dom.addClass(wrapper, "custom_color_icon_alpha");
			YAHOO.util.Dom.setStyle(wrapper, "color", YAHOO.util.Dom.getStyle(icons[i], "color"));
			
			icons[i].parentNode.replaceChild(wrapper, icons[i]);
			wrapper.appendChild(icons[i]);
			
			icons[i] = wrapper;
		}
		
		icons[i].setColor = function(color)
		{
			if(!YAHOO.env.ua.ie)
			{
				if (!this.getContext) return;
				var ctx = this.getContext('2d');
				
				ctx.globalCompositeOperation = "source-over";
				ctx.clearRect(0,0,this.width,this.height);
				
				ctx.drawImage(this.maskImage, 0, 0, this.width, this.height);
				
				ctx.globalCompositeOperation = "source-in";
				
				ctx.fillStyle = color;
				ctx.fillRect(0,0,this.width,this.height);
			}
			else
			{
				var match = getRGB(color);
				
				match[0] = match[0].toString(16);
				match[1] = match[1].toString(16);
				match[2] = match[2].toString(16);
				
				if(match[0].length == 1)
					match[0] = "0"+match[0];
				if(match[1].length == 1)
					match[1] = "0"+match[1];
				if(match[2].length == 1)
					match[2] = "0"+match[2];
				
				// Use this new color value in the IE filter
				this.style.filter ="filter: progid:DXImageTransform.Microsoft.MaskFilter(color=#000000) progid:DXImageTransform.Microsoft.MaskFilter(color=#"+match.join("")+");";
			}
		};
		
		revertColor(icons[i]);
		//this makes it so that if you reinitialize with new css rules applying for the color they actually get picked up
		icons[i].setColor(YAHOO.util.Dom.getStyle(icons[i], "color"));
	}
};

//this undoes any color setting we have applied
//it is slightly dangerous in that it will clobber any color values set
//explicitly on the element that look like rgb(<r>,<g>,<b>) but we should
//be able to avoid that
function revertColor(element) {
	if (element && element.style.color && element.style.color.match(/^rgb\((\d*)\D*(\d*)\D*(\d*)\)\D*$/)) {
		element.style.color = "";
	}
}


function getRGB(color)
{
	try
	{
		var match = new Array();
	
		var rgb = /^rgb\((\d*)\D*(\d*)\D*(\d*)\)\D*$/;
		var rgba = /^rgba\((\d*)\D*(\d*)\D*(\d*)\D*(\d*)\)\D*$/;
		var hhh = /^#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])$/;
		var hhhhhh = /^#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})$/;
		
		if(match = color.match(rgb))
		{
			match.shift();
			
			for(j = 0; j < match.length; j++)
				match[j] = parseInt(match[j], 10);
		}
		else if(match = color.match(rgba))
		{
			match.shift();
			match.pop();
			
			for(j = 0; j < match.length; j++)
				match[j] = parseInt(match[j], 10);
			
			if(!match[3])
				return false;
		}
		else
		{
			if(match = color.match(hhh))
			{
				match.shift();
				for(j = 0; j < match.length; j++)
					match[j] = match[j].concat(match[j]);
			}
			else if(match = color.match(hhhhhh))
			{
				match.shift();
			}
	
			for(j = 0; j < match.length; j++)
				match[j] = parseInt(match[j],16);
		}
			
		return match;
	}
	catch(error)
	{
		return false;
	}
};
Nexopia = {
	JSONData: {},
	jsonTagData: {},
	json: function(id_or_el) {
		var json = null;
		var el = YAHOO.util.Dom.get(id_or_el);
		if (el) {
			if(el.attributes.json_id)
			{
				json = el.attributes.json_id.value;
			}
		}
		if (json) {
			return this.jsonTagData[json];
		} else {
			return null;
		}
	},
	areaBaseURI: function() {
		var match;
		if (match = document.location.href.match(new RegExp("(" + Site.adminSelfURL + "/.*?)(/|$)"))) {
			return match[1];
		} else if (document.location.href.match(new RegExp(Site.adminURL))) {
			return Site.adminURL;
		} else if (document.location.href.match(new RegExp(Site.selfURL))) {
			return Site.selfURL;
		} else if (match = document.location.href.match(new RegExp("(" + Site.userURL + "/.*?)(/|$)"))) {
			return match[1];
		} else {
			return Site.wwwURL;
		}
	}
};

Array.prototype.unique = function (compareFunction) {
	var r = new Array();
	o:for(var i = 0, n = this.length; i < n; i++)
	{
		for(var x = 0, y = r.length; x < y; x++)
		{
			if(!compareFunction)
			{
				if(r[x]==this[i])
				{
					continue o;
				}
			}
			else
			{
				if(compareFunction(r[x],this[i]))
				{
					continue o;
				}
			}
		}
		r[r.length] = this[i];
	}
	return r;
};

Array.prototype.includes = function(object)
{
	for(var o in this)
	{
		if(this[o] == object)
		{
			return true;
		}
	}
	
	return false;
};
//require nexopia.js
//require script_manager.js
Nexopia.DelayedImage = {
	//load the images for any img tags that have their src's on url attributes and are contained by element
	loadImages: function(element) {
		YAHOO.util.Dom.getElementsBy(function(img) {
			return img.attributes.url; //match any img tag inside of element that has a url attribute
		}, "img", element, this.loadImage);
	},
	loadImage: function(img) {
		if (img.attributes.url) {
			img.src = img.attributes.url.value; //set the src attribute to be the value of the url attribute
			img.removeAttribute('url');
		}
	},
	setDelayedSrc: function(element, src) {
		var attr = document.createAttribute('url');
		attr.value = src;
		element.setAttributeNode(attr);
	},
	scrollImages: [],
	scrollBuffer: 1000, //number of pixels of buffer space to give images when loading them
	registerScrollImage: function(element) {
		if (this.scrollImages.length == 0) {
			YAHOO.util.Event.on(window, 'scroll', this.loadScrollImages, this, true);
		}
		this.scrollImages.push({
			element: element,
			y: Nexopia.Utilities.getY(element)
		});
	},
	initializedScrollEvent: false,
	initializedScrollLoad: false,
	loadScrollImages: function() {
		var notLoaded = [];
		for (var i=0;i<this.scrollImages.length;i++) {
			if (this.scrollImages[i].y < Nexopia.Utilities.getScrollTop() + Nexopia.Utilities.getWindowHeight() + this.scrollBuffer) {
				this.loadImage(this.scrollImages[i].element);
			} else {
				notLoaded.push(this.scrollImages[i]);
			}
		}
		this.scrollImages = notLoaded;
		if (this.scrollImages.length == 0) {
			this.initializedScrollLoad = false;
			YAHOO.util.Event.removeListener(window, 'scroll', this.loadScrollImages);
		}
	},
	initialScrollLoad: function() {
		if (!this.initializedScrollLoad) {
			this.initializedScrollLoad = true;
			this.loadScrollImages();
		}
	}
};

Overlord.assign({
	minion: "user_content_image",
	load: Nexopia.DelayedImage.loadImage
});

Overlord.assign({
	minion: "delayed_image:scroll",
	load: Nexopia.DelayedImage.registerScrollImage,
	scope: Nexopia.DelayedImage
});

Overlord.assign({
	minion: "delayed_image:scroll",
	load: Nexopia.DelayedImage.initialScrollLoad,
	scope: Nexopia.DelayedImage,
	order: 1
});
Overlord.assign({
	minion: "errors:submit",
	click: function(event, element) {
		var spinner = new Spinner({ context: [element, "tr"], offset: [-24,2], lazyload: true });
		spinner.on();

		var form = YAHOO.util.Dom.getAncestorByTagName(element, 'form');
		YAHOO.util.Connect.setForm(form);
		YAHOO.util.Connect.asyncRequest('POST', form.action + ":Body", new ResponseHandler({
			success: function(o) { spinner.off(); },
			failure: function(o) { spinner.off(); },
			scope: this
		}), "ajax=true");
		
		YAHOO.util.Event.preventDefault(event);
	}
})
/*
	Javascript usage: In your javascript file call GlobalRegistry.register_handler to notify the system of your interest
	in a docid.  The params you pass are:
		docid: The a string with the id of the document you care about.
		func: The callback function you want to run when the document is loaded.
		obj: An arbitrary object that will be passed to the function.
		override_this: Boolean value, if true then func will execute in the scope of obj; that is in func (this == obj).
	
	Template usage:
		Simply include the attribute t:docid="some identifier string" on your root node.
*/
GlobalRegistry = {
	init: function() {
		YAHOO.util.Event.onDOMReady(this.initialize_docids, null, this, true);
	},
	register_docid: function(docid) {
		this.docids = this.docids.concat(docid);
	},
	register_handler: function(docid, func, obj, override_this, fire_multiple) {
		docid = [].concat(docid);
		var handler = {
			docid: docid,
			func: func,
			obj: obj,
			override_this: override_this,
			fire_multiple: fire_multiple,
			unfired: true
		};
		for (var i=0; i<handler.docid.length; i++) {
			if (this.handlers[handler.docid[i]]) {
				this.handlers[handler.docid[i]] = this.handlers[handler.docid[i]].concat(handler);
			} else {
				this.handlers[handler.docid[i]] = [handler];
			}
		}
	},
	initialize_docids: function() {
		for (var doc_index=0; doc_index < this.docids.length; doc_index++) {
			if (this.handlers[this.docids[doc_index]]) {
				var current_handlers = this.handlers[this.docids[doc_index]];
				for (var handler_index=0; handler_index < current_handlers.length; handler_index++) {
					var current_handler = current_handlers[handler_index];
					if (current_handler.unfired || current_handler.fire_multiple) {
						if (current_handler.override_this) {
						current_handler.obj.global_registry_function = current_handler.func;
						current_handler.obj.global_registry_function(current_handler.obj);
						current_handler.obj.global_registry_function = null;	
						} else {
							current_handler.func(obj);
						}
						current_handler.unfired = false;
					}
				}
			}
		}
	},
	docids: new Array(),
	handlers: {}
};

GlobalRegistry.init();

GlobalTimer = {
	callbacks: {},
	counter: 0,
	setTimeout: function(func, delay, obj, overrideScope) {
		this.callbacks[this.counter] = new Callback(func, obj, overrideScope);
		window.setTimeout("GlobalTimer.callbacks[" + this.counter + "].execute()", delay);
		this.counter++;
	}
}

Callback = function(func, obj, overrideScope) {
	this.func = func;
	this.obj = obj;
	this.overrideScope = overrideScope;
}

Callback.prototype = {
	execute: function() {
		if (this.overrideScope) {
			this.obj.__timer_func = this.func;
			this.obj.__timer_func(this.obj);
			delete this.obj["__timer_func"];	
		} else {
			this.func(this.obj);
		}
	}
};

/*
	Caution: use only when absolutely necessary. Great evil lies here. See NEX-2491.
*/
HorizontalScrollContent = 
{
	paddingAdjustment: 26,
	
	setupResize: function(element)
	{
		YAHOO.util.Event.addListener(window, "resize", this.resizeToAvailableWidth, element, this);
		this.resizeToAvailableWidth(null, element);
	},
	
	resizeToAvailableWidth: function(event, element)
	{
		if(!this.sidebarWidth)
		{
			var sidebar = document.getElementById('sidebar');
			this.sidebarWidth = sidebar.offsetWidth;
		}
				
		var browserWidth = YAHOO.util.Dom.getViewportWidth();
		var maxContentWidth = browserWidth - (this.sidebarWidth + HorizontalScrollContent.paddingAdjustment);
		YAHOO.util.Dom.setStyle(element, 'width', maxContentWidth + 'px');
	}
};

Overlord.assign({
	minion: "horizontal_scrolling_expand_to_sidebar",
	load: HorizontalScrollContent.setupResize,
	scope: HorizontalScrollContent
});
/*
	Provides a central location to subscribe to and post events. A new CustomEvent will be
	made for each eventName passed in. This provides an easy way for objects to communicate
	while not being tightly coupled. The listening object does not have to have any sort of
	reference to the firing object. It simply subscribes to events of a certain name and is
	responsible for determining whether or not it should respond to an event of that name
	when it is fired.
	
	Suggested use case:
	-------------------
	
	// In the listening object:
	
	function callback(event, args)
	{
		var paramHash = args[0];
		if(condition) // condition: the object wants to respond to the notification, based on the paramHash
		{
			// Do stuff...
		}
	};
	
	NotificationCenter.defaultNotificationCenter.subscribe("someEvent", {param1:value1, param2:value2});

	// In the notifying object:
	
	NotificationCenter.defaultNotificationCenter.fire("someEvent", {param1:value1, param2:value2});
*/
function NotificationCenter(theScope)
{
	this.events = {};
	this.notificationScope = theScope;
};

NotificationCenter.prototype = 
{
	subscribe: function(eventName, eventHandler)
	{	
		var e = this.eventForName(eventName);
		if(e)
		{
			e.subscribe(eventHandler, this.notificationScope);
		}
	},
	
	fire: function(eventName, eventInfoHash)
	{
		var e = this.eventForName(eventName);
		if(e) 
		{ 
			e.fire(eventInfoHash);
		}
	},
	
	unsubscribe: function(eventName, eventHandler)
	{
		var e = this.eventForName(eventName);
		if(e) 
		{ 
			e.unsubscribe(eventHandler, this.notificationScope);
		}
	},
	
	eventForName: function(eventName)
	{
		var e = this.events[eventName];
		if(!e)
		{
			e = new YAHOO.util.CustomEvent(eventName, this);
			this.events[eventName] = e;
		}
		
		return e;
	}
};

NotificationCenter.defaultNotificationCenter = new NotificationCenter(null);
function ResponseHandler(options) {
	YAHOO.lang.augmentObject(this, options, true);
	var that = this;
	var success = this.success;
	this.success = function(o) {
		that.handleResponse(o, this);
		success.call(this, o); //should execute in the scope passed to it by the yui event object
	};
	var failure = this.failure;
	this.failure = function(o) {
		that.handleResponse(o, this);
		failure.call(this, o); //should execute in the scope passed to it by the yui event object
	};
}

ResponseHandler.registeredHandlers = {};

ResponseHandler.registerIDHandler = function(id, func, obj, overrideScope) {
	ResponseHandler.registeredHandlers[id] = {
		func: func,
		obj: obj,
		overrideScope: overrideScope,
		execute: function() {
			if (overrideScope) {
				this.obj["__" + id] = func;
				this.obj["__" + id](this.obj);
				delete this.obj["__" + id];
			} else {
				this.func(this.obj);
			}
		}
	};
};

ResponseHandler.prototype = {
	success: function(o) {
	},
	failure: function(o) {
	},
	handleResponse: function(o, scope) {
		var xml = document.createElement("temp");
		//The prepended div is necessary to make IE6 parse script nodes that occur before content nodes.
		xml.innerHTML = "<div>THIS IS AN IE6 HACK</div>"+o.responseText;
		xml.removeChild(xml.firstChild);
		var setupNodes = [];
		//execute any script nodes we sent
		var scriptNodes = xml.getElementsByTagName('script');
		for (var i=0; i<scriptNodes.length;i++) {
			var script = scriptNodes[i].innerHTML;
			if (script.indexOf('<!--') == 0) {
				script = script.substring(4, script.lastIndexOf('//-->'));
			}
			eval(script);
		}

		var children = [];
		//we're going to remove nodes so we need to copy the array first
		for (i=0;i<xml.childNodes.length;i++) {
			children.push(xml.childNodes[i]);
		}
		for (i=0; i<children.length; i++) {
			var node = children[i];
			if (node.nodeType != 1) { //Node.ELEMENT_NODE (ie6 doesn't have this constant)
				continue;
			}
			var original = null;
			if (node.attributes && node.attributes.id) {
				original = document.getElementById(node.attributes.id.value);
			}
			if (original) {
				if (this.copyStyle) {
					for (var key in original.style) {
						try {
							node.style[key] = original.style[key];
						} catch (err) {
							//some properties can't be copied, if they can't that's fine just continue with the ones that can
						}
					}
				}
				original.parentNode.replaceChild(node, original);
				Overlord.summonMinions(node);
			} else if (this.newNode) {
				var inserted = this.newNode.call(scope, node);
				if (inserted) {
					Overlord.summonMinions(node);
				}
			}
			if (!original && !inserted && YAHOO.util.Dom.hasClass(node, 'info_message')) {
				var infoMessages = YAHOO.util.Dom.get('info_messages');
				if (infoMessages) {
					infoMessages.appendChild(node);
					YAHOO.util.Dom.setStyle(infoMessages, 'display', 'block');
				}
			}
			if (node.attributes && node.attributes.id && ResponseHandler.registeredHandlers[node.attributes.id.value]) {
				ResponseHandler.registeredHandlers[node.attributes.id.value].execute();
			}
		}
	},
	copyStyle: false //copy the style tag from the on screen element when substituting?
};

SecureForm = {
	getFormKey: function() {
		var form_key = document.getElementsByName('form_key[]')[0];
		return encodeURIComponent(form_key.value);
	},
	getRawFormKey: function() {
		return document.getElementsByName('form_key[]')[0].value;
	}
};
//require script_manager.js
//custom_color_icon.js

Overlord.assign({
	minion: "shade",
	load: function(element)
	{
		initialize_auto_shading(element);
	}
});

function initialize_auto_shading(elements)
{
	if(!elements)
		var elements = YAHOO.util.Dom.getElementsByClassName('shade_auto');
	else if(!YAHOO.lang.isArray(elements))
	{
		elements = [elements];
	}
	
	for(var i = 0; i < elements.length; i++)
	{
		element = elements[i];
		
		var backgroundColorEl = YAHOO.util.Dom.getAncestorBy(element, function(el) {
			var background = YAHOO.util.Dom.getStyle(el, 'background-color');
			return (background != "transparent" && background != 'rgba(0, 0, 0, 0)');
		});
		
		var shade_color = getRGB(YAHOO.util.Dom.getStyle(backgroundColorEl, "background-color"));
		
		if((shade_color[0] + shade_color[1] + shade_color[2])/3 >= 128)
		{
			darken(element, shade_color);
		}
		else
		{
			lighten(element, shade_color);
		}
	}
}

function lighten(element, shade_color)
{
	if(!YAHOO.util.Dom.hasClass(element, "no_shading"));
	{
		if(!YAHOO.util.Dom.hasClass(element, "no_shading_color"))
		{
			do_lighten(element, "color", shade_color);
		}
		if(!YAHOO.util.Dom.hasClass(element, "no_shading_background"))
		{
			do_lighten(element, "background-color", shade_color);
		}
		if(!YAHOO.util.Dom.hasClass(element, "no_shading_border"))
		{
			do_lighten(element, "border-top-color", shade_color);
			do_lighten(element, "border-bottom-color", shade_color);
			do_lighten(element, "border-left-color", shade_color);
			do_lighten(element, "border-right-color", shade_color);
		}
	}
	
	var children = YAHOO.util.Dom.getChildren(element);
	for(var i = 0; i < children.length; i++)
	{
		lighten(children[i], shade_color);
	}
}

function darken(element, shade_color)
{
	if(!YAHOO.util.Dom.hasClass(element, "no_shading"));
	{
		if(!YAHOO.util.Dom.hasClass(element, "no_shading_color"))
		{
			do_darken(element, "color", shade_color);
		}
		if(!YAHOO.util.Dom.hasClass(element, "no_shading_background"))
		{
			do_darken(element, "background-color", shade_color);
		}
		if(!YAHOO.util.Dom.hasClass(element, "no_shading_border"))
		{
			do_darken(element, "border-top-color", shade_color);
			do_darken(element, "border-bottom-color", shade_color);
			do_darken(element, "border-left-color", shade_color);
			do_darken(element, "border-right-color", shade_color);
		}
	}
	
	var children = YAHOO.util.Dom.getChildren(element);
	for(var i = 0; i < children.length; i++)
	{
		darken(children[i], shade_color);
	}
}

function do_lighten(element, property, shade_color)
{
	if(getRGB(YAHOO.util.Dom.getStyle(element, property)) != false)
	{
		var shade_value = 1 - getRGB(YAHOO.util.Dom.getStyle(element, property))[0]/255;
	
		var new_color = [];
		new_color[0] = Math.round(shade_color[0] + (255 - shade_color[0]) * shade_value);
		new_color[1] = Math.round(shade_color[1] + (255 - shade_color[1]) * shade_value);
		new_color[2] = Math.round(shade_color[2] + (255 - shade_color[2]) * shade_value);
		
		colorize(element, property, new_color);
	}
}

function do_darken(element, property, shade_color)
{
	if(getRGB(YAHOO.util.Dom.getStyle(element, property)) != false)
	{
		var shade_value = getRGB(YAHOO.util.Dom.getStyle(element, property))[0]/255;
		
		var new_color = [];
		new_color[0] = Math.round(shade_color[0] * shade_value);
		new_color[1] = Math.round(shade_color[1] * shade_value);
		new_color[2] = Math.round(shade_color[2] * shade_value);
		
		colorize(element, property, new_color);
	}
}

function colorize(element, property, color)
{
	if(YAHOO.util.Dom.hasClass(element, "custom_color_icon") || YAHOO.util.Dom.hasClass(element, "custom_color_icon_alpha"))
		element.setColor('rgb('+color.join(",")+')');
	
	if( !( element.tagName == "IMG" || element.tagName == "CANVAS" ) )
		YAHOO.util.Dom.setStyle(element, property, 'rgb('+color.join(",")+')');
};

/*
	cfg properties:
		lazyload:
			- if true, the spinner will be initialized upon calling "on". The "off" function will destroy it.
			- if false, the spinner will be initialized during construction. The "off" function will then simply hide it
		xy: [{x}, {y}]
			- {x} is the absolute horizontal position (from the left)
			- {y} is the absolute vertical position (from the top)
		context: [{element}, {corner}]
			- {element} is the DOM element you want the spinner to align to
			- {corner} is the corner of the element you want the spinner to align to:
				- tr: the top right corner of the spinner will align to the top right corner of the element
				- tl: the top left corner of the spinner will align to the top left corner of the element
				- br: the bottom right corner of the spinner will align to the bottom right corner of the element
				- bl: the bottom left corner of the spinner will align to the bottom left corner of the element
		offset: [{x}, {y}]
			- {x} is the number of pixels horizontally in from the corner
			- {y} is the number of pixels vertically in from the corner
	
		Note that "offset" only works with a "context", and if you use a "context", you shouldn't use an "xy" setting.
		
	Functions:
		on: Make the spinner visible.
		off: Destroy the spinner.
*/
function Spinner(cfg)
{
	this.cfg = cfg;
	
	if(!cfg.lazyload)
	{
		this.init(cfg);
	}
};

Spinner.prototype =
{
	init: function(cfg)
	{
		var positionCfg = null;
		if (cfg.xy)
		{
			positionCfg = { x: cfg.xy[0], y: cfg.xy[1] };
		}
		else if (cfg.context)
		{
			positionCfg = { context: [cfg.context[0], cfg.context[1], cfg.context[1]] };
		}

		var overlayCfg = YAHOO.lang.merge(
			{ visible: false, width: "16px", height: "16px" },
			positionCfg);

		this.overlay = new YAHOO.widget.Overlay("spinner", overlayCfg );
		this.overlay.setBody("<img src=\"" + Site.staticFilesURL + "/Legacy/images/spinner.gif\"/>");
		this.overlay.render(document.body);

		if (cfg.offset && cfg.context)
		{
			var xMod = cfg.offset[0];
			var yMod = cfg.offset[1];
			var directions = cfg.context[1].split("");

			if (directions[0] == "b")
			{
				yMod = yMod * -1;
			}

			if (directions[1] == "r")
			{
				xMod = xMod * -1;
			}

			this.overlay.cfg.setProperty("x", this.overlay.cfg.getProperty("x") + xMod);
			this.overlay.cfg.setProperty("y", this.overlay.cfg.getProperty("y") + yMod);
		}		
	},
	
	on: function()
	{
		if (this.cfg.lazyload)
		{
			this.init(this.cfg);
		}
		
		this.overlay.show();
	},
	
	off: function()
	{
		if (this.cfg.lazyload)
		{
			this.overlay.destroy();
		}
		else
		{
			this.overlay.hide();
		}
	}
};
//require nexopia.js
Nexopia.Utilities = {
	/*
		@img: id or element of the image to base actions on
		@options: {
			load: function that gets called when the img is ready (either immediately or after load)
			scope: scope the function gets called in, defaults to the image
			args: an array containing arguments to be passed to the function, defaults to [the image]
		}
	*/
	withImage: function(img, options) {
		var imgElement = YAHOO.util.Dom.get(img);
		if (!options.scope) {
			options.scope = imgElement;
		}
		if (!options.args) {
			options.args = [imgElement];
		}
		if (imgElement.complete) {
			options.load.apply(options.scope, options.args);
		} else {
			YAHOO.util.Event.on(imgElement, 'load', function() {options.load.apply(options.scope, options.args);});
		}
	},
	//withCanvas is useful for IE canvas initialization issues, it polls until the canvases have been setup by excanvas.js properly.
	withCanvas: function(canvas, options) {
		canvas = YAHOO.util.Dom.get(canvas);
		if (!options.scope) {
			options.scope = canvas;
		}
		if (!options.args) {
			options.args = [canvas];
		}
		if (!canvas.getContext) {
			YAHOO.lang.later(10, this, this.withCanvas, [canvas,options], false);
		} else {
			options.load.apply(options.scope, options.args);
		}
	},
	escapeHTML: function(string) {
		s = string.replace(/&nbsp;/g,' ').replace(/&/g,'&amp;').replace(/>/g,'&gt;').replace(/</g,'&lt;').replace(/"/g,'&quot;').replace(/&amp;hearts;/, '&hearts;');
		for (var i = 0; i <= 32; i++){
			s = s.replace('&#' + i + ';', "");
		}
		return s;
	},
	escapeURI: function(string) {
		return(escape(string).replace(/\+/g, '%2B'));
	},
	getHexValue: function(color)
	{
		rgb = color.replace(/rgb\((.*?),\s*(.*?),\s*(.*?)\s*\)/,'$1,$2,$3').split(',');
		return Nexopia.Utilities.getHex(rgb);
	},
	getHex: function(rgb)
	{
		return Nexopia.Utilities.toHex(rgb[0]) + Nexopia.Utilities.toHex(rgb[1]) + Nexopia.Utilities.toHex(rgb[2]);
	},
	toHex: function(n) 
	{
		if (n==null) {
			return "00";
		}
		n=parseInt(n, 10); 
		if (n==0 || isNaN(n)) {
			return "00";
		}
		n=Math.max(0,n); 
		n=Math.min(n,255); 
		n=Math.round(n);

		return "0123456789ABCDEF".charAt((n-n%16)/16) + "0123456789ABCDEF".charAt(n%16);
	},
	deduceImgColor: function(element)
	{
		var img = document.createElement("img");
		img.className = "color_icon";
		element.appendChild(img);
		var color = YAHOO.util.Dom.getStyle(img, 'color');
		element.removeChild(img);
		return color;
	},
	trim: function(str) {
		return str.replace(/^\s+|\s+$/g, '') ;
	},
	concatForm: function(baseForm, formToConcat) {
		// Normally we could just do formToConcat.elements, but IE fails miserably when forms are nested
		// and can't figure out the correct children, so we need to use a slightly less elegant appraoch
		// to getting our elements.
		var children = YAHOO.util.Dom.getElementsBy(
			function(e) { 
				return (
					e.tagName.toLowerCase() == 'input' ||
					e.tagName.toLowerCase() == 'select' ||
					e.tagName.toLowerCase() == 'textarea' ||
					e.tagName.toLowerCase() == 'button');
			}, null, formToConcat);
			
		for(var i = 0; i < children.length; i++)
		{
			if(children[i].name != null && children[i].name != "" && children[i].value != undefined)
			{
				// Aha! Normal form posting doesn't even pass along a checkbox that isn't checked, so
				// if we don't even want to create a form element if our source form has an unchecked
				// checkbox. Doing so would confuse the actual post of the concatenated form.
				if(	children[i].tagName.toLowerCase() == "input" && 
					(children[i].type == "checkbox" || children[i].type == "radio") && 
					!children[i].checked)
				{
					continue;
				}
				
				var input = document.createElement("input");
				input.type = "hidden";
				input.name = children[i].name;
				input.value = children[i].value;
				baseForm.appendChild(input);
			}
		}		
	},
	setCaretPosition: function(textarea, position)
	{
		if(textarea.setSelectionRange)
		{
			textarea.focus();
			textarea.setSelectionRange(position,position);
		}
		else if(textarea.createTextRange)
		{
			var range = textarea.createTextRange();
			range.collapse(true);
			range.moveEnd('character', position);
			range.moveStart('character', position);
			range.select();
		}
	},
	insertAfter: function(before, after) {
		if (before.nextSibling) {
			before.parentNode.insertBefore(after,before.nextSibling);
		} else {
			before.parentNode.appendChild(after);
		}
	},
	//getX and getY have been happily stolen from "Javascript: The Definitive Guide" by David Flanagan (5th edition)
	getX: function(element) {
		var x = 0;
		while (element) {
			x += element.offsetLeft;
			element = element.offsetParent;
		}
		return x;
	},
	getY: function(element) {
		var y = 0;
		while (element) {
			y += element.offsetTop;
			element = element.offsetParent;
		}
		return y;
	},
	//more happy theft from http://stackoverflow.com/questions/871399/cross-browser-method-for-detecting-the-scrolltop-of-the-browser-window
	//courtesy of kennebec
	getScrollTop: function() {
		if(typeof pageYOffset!= 'undefined'){
			//most browsers
			return pageYOffset;
		}
		else{
			var B= document.body; //IE 'quirks'
			var D= document.documentElement; //IE with doctype
			D= (D.clientHeight)? D: B;
			return D.scrollTop;
		}
	},
	getScrollLeft: function() {
		if(typeof pageXOffset!= 'undefined'){
			//most browsers
			return pageXOffset;
		}
		else{
			var B= document.body; //IE 'quirks'
			var D= document.documentElement; //IE with doctype
			D= (D.clientWidth)? D: B;
			return D.scrollLeft;
		}
	},
	//pilfered from http://snipplr.com/view/2638/height-of-window/ courtesy of Rob Zand
	getWindowHeight: function() {
		return window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
	},
	getWindowWidth: function() {
		return window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth;
	},
	stringToRGB: function(color_string) {
		return (new this.ColorConverter(color_string)).toRGB();
	},
	stringToHex: function(color_string) {
		return (new this.ColorConverter(color_string)).toHex();
	},
	ColorConverter: function(color_string) {
		this.ok = false;

		// strip any leading #
		if (color_string.charAt(0) == '#') { // remove # if any
			color_string = color_string.substr(1,6);
		}

		color_string = color_string.replace(/ /g,'');
		color_string = color_string.toLowerCase();

		// before getting into regexps, try simple matches
		// and overwrite the input
		var simple_colors = {
			aliceblue: 'f0f8ff',
			antiquewhite: 'faebd7',
			aqua: '00ffff',
			aquamarine: '7fffd4',
			azure: 'f0ffff',
			beige: 'f5f5dc',
			bisque: 'ffe4c4',
			black: '000000',
			blanchedalmond: 'ffebcd',
			blue: '0000ff',
			blueviolet: '8a2be2',
			brown: 'a52a2a',
			burlywood: 'deb887',
			cadetblue: '5f9ea0',
			chartreuse: '7fff00',
			chocolate: 'd2691e',
			coral: 'ff7f50',
			cornflowerblue: '6495ed',
			cornsilk: 'fff8dc',
			crimson: 'dc143c',
			cyan: '00ffff',
			darkblue: '00008b',
			darkcyan: '008b8b',
			darkgoldenrod: 'b8860b',
			darkgray: 'a9a9a9',
			darkgreen: '006400',
			darkkhaki: 'bdb76b',
			darkmagenta: '8b008b',
			darkolivegreen: '556b2f',
			darkorange: 'ff8c00',
			darkorchid: '9932cc',
			darkred: '8b0000',
			darksalmon: 'e9967a',
			darkseagreen: '8fbc8f',
			darkslateblue: '483d8b',
			darkslategray: '2f4f4f',
			darkturquoise: '00ced1',
			darkviolet: '9400d3',
			deeppink: 'ff1493',
			deepskyblue: '00bfff',
			dimgray: '696969',
			dodgerblue: '1e90ff',
			feldspar: 'd19275',
			firebrick: 'b22222',
			floralwhite: 'fffaf0',
			forestgreen: '228b22',
			fuchsia: 'ff00ff',
			gainsboro: 'dcdcdc',
			ghostwhite: 'f8f8ff',
			gold: 'ffd700',
			goldenrod: 'daa520',
			gray: '808080',
			green: '008000',
			greenyellow: 'adff2f',
			honeydew: 'f0fff0',
			hotpink: 'ff69b4',
			indianred : 'cd5c5c',
			indigo : '4b0082',
			ivory: 'fffff0',
			khaki: 'f0e68c',
			lavender: 'e6e6fa',
			lavenderblush: 'fff0f5',
			lawngreen: '7cfc00',
			lemonchiffon: 'fffacd',
			lightblue: 'add8e6',
			lightcoral: 'f08080',
			lightcyan: 'e0ffff',
			lightgoldenrodyellow: 'fafad2',
			lightgrey: 'd3d3d3',
			lightgreen: '90ee90',
			lightpink: 'ffb6c1',
			lightsalmon: 'ffa07a',
			lightseagreen: '20b2aa',
			lightskyblue: '87cefa',
			lightslateblue: '8470ff',
			lightslategray: '778899',
			lightsteelblue: 'b0c4de',
			lightyellow: 'ffffe0',
			lime: '00ff00',
			limegreen: '32cd32',
			linen: 'faf0e6',
			magenta: 'ff00ff',
			maroon: '800000',
			mediumaquamarine: '66cdaa',
			mediumblue: '0000cd',
			mediumorchid: 'ba55d3',
			mediumpurple: '9370d8',
			mediumseagreen: '3cb371',
			mediumslateblue: '7b68ee',
			mediumspringgreen: '00fa9a',
			mediumturquoise: '48d1cc',
			mediumvioletred: 'c71585',
			midnightblue: '191970',
			mintcream: 'f5fffa',
			mistyrose: 'ffe4e1',
			moccasin: 'ffe4b5',
			navajowhite: 'ffdead',
			navy: '000080',
			oldlace: 'fdf5e6',
			olive: '808000',
			olivedrab: '6b8e23',
			orange: 'ffa500',
			orangered: 'ff4500',
			orchid: 'da70d6',
			palegoldenrod: 'eee8aa',
			palegreen: '98fb98',
			paleturquoise: 'afeeee',
			palevioletred: 'd87093',
			papayawhip: 'ffefd5',
			peachpuff: 'ffdab9',
			peru: 'cd853f',
			pink: 'ffc0cb',
			plum: 'dda0dd',
			powderblue: 'b0e0e6',
			purple: '800080',
			red: 'ff0000',
			rosybrown: 'bc8f8f',
			royalblue: '4169e1',
			saddlebrown: '8b4513',
			salmon: 'fa8072',
			sandybrown: 'f4a460',
			seagreen: '2e8b57',
			seashell: 'fff5ee',
			sienna: 'a0522d',
			silver: 'c0c0c0',
			skyblue: '87ceeb',
			slateblue: '6a5acd',
			slategray: '708090',
			snow: 'fffafa',
			springgreen: '00ff7f',
			steelblue: '4682b4',
			tan: 'd2b48c',
			teal: '008080',
			thistle: 'd8bfd8',
			tomato: 'ff6347',
			turquoise: '40e0d0',
			violet: 'ee82ee',
			violetred: 'd02090',
			wheat: 'f5deb3',
			white: 'ffffff',
			whitesmoke: 'f5f5f5',
			yellow: 'ffff00',
			yellowgreen: '9acd32'
		};
		for (var key in simple_colors) {
			if (color_string == key) {
				color_string = simple_colors[key];
			}
		}
		// emd of simple type-in colors

		// array of color definition objects
		var color_defs = [
		{
			re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
			example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
			process: function (bits){
				return [
				parseInt(bits[1]),
				parseInt(bits[2]),
				parseInt(bits[3])
				];
			}
		},
		{
			re: /^([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})$/,
			example: ['#00ff00', '336699'],
			process: function (bits){
				return [
				parseInt(bits[1], 16),
				parseInt(bits[2], 16),
				parseInt(bits[3], 16)
				];
			}
		},
		{
			re: /^([\da-fA-F]{1})([\da-fA-F]{1})([\da-fA-F]{1})$/,
			example: ['#fb0', 'f0f'],
			process: function (bits){
				return [
				parseInt(bits[1] + bits[1], 16),
				parseInt(bits[2] + bits[2], 16),
				parseInt(bits[3] + bits[3], 16)
				];
			}
		}
		];

		// search through the definitions to find a match
		for (var i = 0; i < color_defs.length; i++) {
			var re = color_defs[i].re;
			var processor = color_defs[i].process;
			var bits = re.exec(color_string);
			if (bits) {
				channels = processor(bits);
				this.r = channels[0];
				this.g = channels[1];
				this.b = channels[2];
				this.ok = true;
			}

		}

		// validate/cleanup values
		this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
		this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
		this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);

		// some getters
		this.toRGB = function () {
			return [this.r,this.g,this.b];
		}
		this.toHex = function () {
			var r = this.r.toString(16);
			var g = this.g.toString(16);
			var b = this.b.toString(16);
			if (r.length == 1) r = '0' + r;
			if (g.length == 1) g = '0' + g;
			if (b.length == 1) b = '0' + b;
			return '#' + r + g + b;
		}
	}
};
Validation = {
	/*
		fields: Optional. If these are passed in, we'll initialize Validation.states with "none" for
	 			each so that when Validation.allStatesAreValid() is called, it will return false by
				default, even if the user hasn't clicked inside any of the fields.
	*/
	init: function(fields)
	{
		for(var i = 0; i < fields.length; i++)
		{
			Validation.states[fields[i]] = "none";
		}
	},
	
	// silent: optional - only update the Validation.states entry
	displayValidation: function(field_id, state, message, silent)
	{
		Validation.states[field_id] = state;
	
		if(silent != null && silent == true)	
		{
			return;
		}
		
		array_regexp = /(.*)\[(.*)\]/;
		field_index = "";
		if (field_id.match(array_regexp))
		{
			field_name = field_id.replace(array_regexp, "$1");
			index = field_id.replace(array_regexp, "$2");
			
			field_id = field_name;
			field_index = "[" + index + "]";
		}
		
		error_icon = document.getElementById(field_id + "_error_icon" + field_index);
		valid_icon = document.getElementById(field_id + "_valid_icon" + field_index);
		warning_icon = document.getElementById(field_id + "_warning_icon" + field_index);

		var active_icon = null;
		
		if (state == "error")
		{
			if (warning_icon) warning_icon.style.display = "none";
			if (error_icon) error_icon.style.display = "block";
			if (valid_icon) valid_icon.style.display = "none";
			
			active_icon = error_icon;
		}
		else if (state == "valid")
		{
			if (warning_icon) warning_icon.style.display = "none";
			if (error_icon) error_icon.style.display = "none";
			if (valid_icon) valid_icon.style.display = "block";
			
			active_icon = valid_icon;
		}
		else if (state == "warning")
		{
			if (warning_icon) warning_icon.style.display = "block";
			if (error_icon) error_icon.style.display = "none";
			if (valid_icon) valid_icon.style.display = "none";
			
			active_icon = warning_icon;
		}
		else
		{
			if (warning_icon) warning_icon.style.display = "none";
			if (error_icon) error_icon.style.display = "none";
			if (valid_icon) valid_icon.style.display = "none";
		}
		
		message_div = document.getElementById(field_id + "_vm" + field_index);
		if (message_div != null)
		{
			message_div.className = "validation_" + state + "_text";
			message_div.innerHTML = message;	
		}
		else if (message != "")
		{
			new YAHOO.widget.Tooltip(field_id + "_" + state + "_tooltip" + field_index, 
				{ context: active_icon.id, zIndex: 500, showdelay: 0, hidedelay: 0, text:message } );
		}
				
	},
	
	
	clientValidate: function()
	{
		
	},
	
	
	ajaxValidate: function(field_id, url, silent)
	{
		Validation.displayValidation(field_id, "none", "Checking...", silent);
		
		callback = {
			success: function(o) {
				xmlRoot = o.responseXML.documentElement;
				stateTag = xmlRoot.getElementsByTagName("state")[0];
				messageTag = xmlRoot.getElementsByTagName("message")[0];
				stateText = stateTag.firstChild == null ? "" : stateTag.firstChild.nodeValue;
				messageText = messageTag.firstChild == null ? "" : messageTag.firstChild.nodeValue;
				escapedMessageText = messageText.replace(/</, "&lt;").replace(/>/, "&gt;");
				
				Validation.displayValidation(field_id, stateText, escapedMessageText, silent);
			}
		};
		
		var formKeys = document.getElementsByName("form_key[]");
		var formKeyString = "";
		for(var i = 0; i < formKeys.length; i++)
		{
			formKeyString = formKeyString + "form_key[]=" + formKeys[i].value + "&";
		}
		
		value_field = document.getElementById(field_id);
		value = Nexopia.Utilities.escapeURI(value_field.value);
		
		YAHOO.util.Connect.asyncRequest('POST', url, callback, formKeyString + "&value=" + value);
	},
	
	
	/*
		When Validation.displayValidation() (or Validation.ajaxValidate()) is called for any field, we
		update Validation.states, which reflect the overall validity of the page. This function returns
		true if all of the fields that have been validated (or initialized) are in a valid (or warning)
		state and false if they are not.
		
		It's best to initialize the Validation.states first by passing an array of field names into
		Validation.init().
	*/
	allStatesAreValid: function()
	{
		for(var key in Validation.states)
		{
			var value = Validation.states[key];
			if (!(value == "valid" || value == "warning"))
			{
				return false;
			}
		}
		
		return true;
	}
}
Validation.states = [];


function ValidationResults(state, message)
{
	this.state = state;
	this.message = message;
}

/*
	A ValidationChain represents a collection of ValidationRules that will be checked in the order 
	they were added to the ValidationChain. The first rule that gives a ValidationResults in an
	"error" state will "break" the chain. No further validation will happen, and the ValidationResults
	that will be returned. If the entire ValidationChain contains rules that only resolve to "valid"
	or "warning" state ValidationResults, the ValidationChain will be considered to be "valid" and
	the ValidationResults from the last evaluated rule will be returned.
*/
function ValidationChain()
{
	this.client_chain = new Array();
	this.server_chain = new Array();
	this.none_override = null;
	this.valid_override = null;
}
ValidationChain.prototype =
{
	add: function(rule)
	{
		if (rule.isServerSide())
		{
			this.server_chain[this.server_chain.length] = rule;
		}
		else
		{
			this.client_chain[this.client_chain.length] = rule;
		}
	},

	/*
		Sets a rule that will pre-maturely break the Validation::Chain if it returns Validation::Results
		that have a state of :none. This override takes precedence over the valid_override.
	*/
	setNoneOverride: function(rule)
	{
		this.none_override = rule;
	},

	/*
		Sets a rule that will pre-maturely break the Validation::Chain if it returns Validation::Results
		that have a state of :valid. This override is trumped by the none_override.
	*/
	setValidOverride: function(rule)
	{
		this.valid_override = rule;
	},


	/*
		Validates the chain and returns Validation::Results in the following order:
		1. A none_override that has Validation::Results in a :none state.
		2. A valid_override that has Validation::Results in a :valid state.
		3. The Validation::Results of a rule that fails to resolve into a :valid or
			 :warning state.
		4. The Validation::Results of the rule that was last added to the chain.
	*/
	validate: function()
	{
		if (this.none_override != null)
		{
			results = this.none_override.validate();
			if (results.state == "none")
			{
				return results;
			}
		}

		if (this.valid_override != null)
		{
			results = this.valid_override.validate();
			if (results.state == "valid")
			{
				return results;
			}
		}
	
		results = new ValidationResults("valid","");
	
		// Do client-side validation
		for (var i = 0; i < this.client_chain.length; i++)
		{
			rule = this.client_chain[i];
			results = rule.validate();
			if (results.state == "error")
			{
				return results;
			}
		}
/*
		// Do any AJAX validation
		if (this.server_chain.length > 0)
		{
			// TODO: Change to the validation handler in Core!!!
			ruleString = "/account"
			for (var i = 0; i < this.server_chain.length; i++)
			{
				rule = this.server_chain[i];
				className = rule.className.toString();
				ruleString = ruleString + "/" + className;
			}

			alert(ruleString);
			// results = ajaxValidate(rules)
		}
*/
		return results;
	},
		
	ajaxValidate: function()
	{
		
	}
}


function ValidationSet()
{
	this.validation_results = new Array();
}
ValidationSet.prototype =
{
	/*	
		Add the given field and ValidationResults to representing it to the ValidationSet.
		
		field: 		The name/id of the field in the form. See ValidationDisplay for more information on 
					naming conventions.
		results: 	The ValidationResults to associate with the field.
		show_icon_for_valid:
					Can optionally "turn off" the default of showing a valid icon for ValidationResults in
					a "valid" state.
	*/
	add: function(field,results,show_icon_for_valid)
	{
		show_icon_for_valid = show_icon_for_valid || true;
		this.validation_results[field] = results;	
	},


	get_results: function(field)
	{
		return this.validation_results[field];
	},


	// Bind all Validation::Display objects in this Validation::Set to the given Template.
	bind: function(template)
	{
/*		@validation_displays.values.each { |display|
			display.bind(template);
*/
	}
}


function ValidationValueAccessor(field_id, field_value)
{
	this.field_id = field_id;
	this.field_value = field_value;
}
ValidationValueAccessor.prototype =
{
	value: function()
	{
		var field = document.getElementById(this.field_id);

		if (field.type == "checkbox")
		{
			return field.checked;
		}

		return field.value;
	}
};

ValidationRules = 
{
	ClearOnNil: function (value_accessors, static_values)
	{
		this.value = value_accessors[0];
		this.nil_state = static_values[0] || "none";
		this.filled_state = static_values[1] || "valid";
		this.className = "ClearOnNil";
	},

	ClearOnNilOrEmpty: function(value_accessors, static_values)
	{
		this.value = value_accessors[0];
		this.nil_or_empty_state = static_values[0] || "none";
		this.filled_state = static_values[1] || "valid";
		this.className = "ClearOnNilOrEmpty";
	},

	CheckLength: function(value_accessors, static_values)
	{
		this.value = value_accessors[0];
		this.field_name = static_values[0];
		this.min_length = static_values[1];
		this.max_length = static_values[2];
		this.className = "CheckLength";
	},

	CheckRetypeValueMatches: function(value_accessors, static_values)
	{
		this.value = value_accessors[0];
		this.retype_value = value_accessors[1];
		this.field_name = static_values[0];
		this.className = "CheckRetypeValueMatches";
	},

	CheckUsernameAvailable: function(username)
	{
		this.username = username;
		this.className = "CheckUsernameAvailable";
	},

	CheckEmailAvailable: function(email)
	{
		this.email = email;
		this.className = "CheckEmailAvailable";
	},

	CheckNotEmpty: function(value_accessors, static_values)
	{
		this.text = value_accessors[0];
		this.if_filled = static_values[0];
		this.identifier = static_values[1];
		this.className = "CheckNotEmpty";
	},


	CheckNotEqualTo: function(value_accessors, static_values)
	{
		this.value = value_accessors[0];
		this.check_value = static_values[0];
		this.error_msg = static_values[1];
		this.className = "CheckNotEqualTo";
	},
	
	
	CheckChecked: function(value_accessors, static_values)
	{
		this.value = value_accessors[0];
		this.error_msg = static_values[0];
		if(this.error_msg == null || this.error_msg == undefined)
		{
			this.error_msg = "";
		}
		this.className = "CheckChecked";
	},


	// TODO: Fix this... how to get the same character conversion as 127.chr in javascript?
	// Or should this just go into a server-side AJAX call?
	// Checks the given text for any illegal characters
	CheckIllegalCharacters: function(text)
	{
		this.text = text;
		this.className = "CheckIllegalCharacters";
	},

	CheckNoSpaces: function(value_accessors, static_values)
	{
		this.text = value_accessors[0];
		this.className = "CheckNoSpaces";
	},

	CheckAlphaCharactersExist: function(value_accessors, static_values)
	{
		this.text = value_accessors[0];
		this.className = "CheckAlphaCharactersExist";
	},

	CheckLegalUsername: function(username)
	{
		this.username = username;
		this.className = "CheckLegalUsername";
	},

	// Checks that the password is longer than 4 characters		
	CheckPasswordLength: function(value_accessors, static_values)
	{
		this.password = value_accessors[0];
		this.className = "CheckPasswordLength";
	},

	// Makes sure that the email address is of proper syntax
	CheckEmailSyntax: function(value_accessors, static_values)
	{
		this.email = value_accessors[0];
		this.className = "CheckEmailSyntax";
	},

	CheckEmailSupported: function(value_accessors, static_values)
	{
		this.email = value_accessors[0];
		this.className = "CheckEmailSupported";
	},

	// Checks that the email has been deleted for at least a week
	CheckEmailSufficientlyDead: function(email)
	{
		this.email = email;
		this.className = "CheckEmailSufficientlyDead";
	},

	// Checks that the email has not been banned
	CheckEmailNotBanned: function(email)
	{
		this.email = email;
		this.className = "CheckEmailNotBanned";
	},

	// Checks that the ip has not been banned
	CheckIPNotBanned: function(ip)
	{
		this.ip = ip;
		this.className = "CheckIPNotBanned";
	},

	CheckNotBanned: function(identifier, type)
	{
		this.identifier = identifier;
		this.type = type;
		this.className = "CheckNotBanned";
	},

	CheckTerms: function(date_of_birth, check_hash)
	{
		this.check_hash = check_hash;
		this.date_of_birth = date_of_birth;
		this.className = "CheckTerms";
	},

	CheckDateOfBirth: function(value_accessors, static_values)
	{
		this.year = value_accessors[0];
		this.month = value_accessors[1];
		this.day = value_accessors[2];
		this.className = "CheckDateOfBirth";
	},

	CheckLocation: function(value_accessors, static_values)
	{
		this.location = value_accessors[0];
		this.className = "CheckLocation";
	},

	CheckSex: function(value_accessors, static_values)
	{
		this.sex = value_accessors[0];
		this.className = "CheckSex";
	},

	CheckTimezone: function(value_accessors, static_values)
	{
		this.timezone = value_accessors[0];
		this.className = "CheckTimezone";
	},

	CheckPasswordStrength: function(value_accessors, static_values)
	{
		this.password = value_accessors[0];
		this.username = value_accessors[1];
		this.className = "CheckPasswordStrength";
	},
	
	CheckCaptcha: function(value_accessors, static_values)
	{
		this.value = value_accessors[0];
		this.nil_state = static_values[0] || "none";
		this.filled_state = static_values[1] || "valid";
		this.className = "CheckCaptcha";
	}
	
};

ValidationRules.CheckCaptcha.prototype = 
{
	validate: function()
	{
		
		return new ValidationResults("valid", "");
		
	},
	
	isServerSide: function()
	{
		return false;
	}
	
};


ValidationRules.ClearOnNil.prototype = 
{
	validate: function()
	{
		if (this.value.value() == null)
		{
			return new ValidationResults(this.nil_state, "");
		}
		else
		{
			return new ValidationResults(this.filled_state, "");
		}
	},


	isServerSide: function()
	{
		return false;
	}
};


ValidationRules.ClearOnNilOrEmpty.prototype = 
{
	validate: function()
	{
		if (this.value.value() == null || this.value == "")
		{
			return new ValidationResults(this.nil_or_empty_state, "");
		}
		else
		{
			return new ValidationResults(this.filled_state, "");
		}
	},


	isServerSide: function()
	{
		return false;
	}
};


ValidationRules.CheckLength.prototype =
{
	validate: function()
	{				
		if (this.value.value().length < this.min_length)
		{
			return new ValidationResults("error", this.field_name + " must be at least " + this.min_length + " characters long.");
		}
		else if (this.value.value().length > this.max_length)
		{
			return new ValidationResults("error", this.field_name + " must be shorter than " + this.max_length + " characters.");
		}
		else
		{
			return new ValidationResults("valid", "");
		}
	},


	isServerSide: function()
	{
		return false;
	}
};


ValidationRules.CheckRetypeValueMatches.prototype =
{
	validate: function()
	{
		if (this.value.value() != this.retype_value.value())
		{	
			return new ValidationResults("error", "Did not match the first " + this.field_name);
		}
		else
		{
			return new ValidationResults("valid", "");
		}
	},


	isServerSide: function()
	{
		return false;
	}
};

ValidationRules.CheckEmailAvailable.prototype =
{
	validate: function()
	{
		/* Would have to do an AJAX call here. */
	},


	isServerSide: function()
	{
		return true;
	}
};

ValidationRules.CheckUsernameAvailable.prototype =
{
	validate: function()
	{
		/* Would have to do an AJAX call here. */
	},


	isServerSide: function()
	{
		return true;
	}
};

ValidationRules.CheckNotEmpty.prototype =
{
	validate: function()
	{
		if (this.text.value() == "" && (this.if_filled == null || this.if_filled == ""))
		{
			text = "Cannot be blank";
			if (this.identifier != null)
			{
				text = this.identifier + " cannot be blank";
			}
		
			return new ValidationResults("error", text);
		}
		else
		{
			return new ValidationResults("valid", "");
		}
	},


	isServerSide: function()
	{
		return false;
	}	
};


ValidationRules.CheckNotEqualTo.prototype =
{
	validate: function()
	{
		if (this.value.value() == this.check_value)
		{
			return new ValidationResults("error", this.error_msg);
		}
		else
		{
			return new ValidationResults("valid", "");
		}
	},


	isServerSide: function()
	{
		return false;
	}	
};


ValidationRules.CheckChecked.prototype =
{
	validate: function()
	{
		if (this.value.value() == null || !this.value.value())
		{
			return new ValidationResults("error", this.error_msg);
		}
		else
		{
			return new ValidationResults("valid", "");
		}
	},


	isServerSide: function()
	{
		return false;
	}	
};


ValidationRules.CheckEmailAvailable.prototype =
{
	validate: function()
	{
		/* Would have to do an AJAX call here. */
	},


	isServerSide: function()
	{
		return true;
	}
};

ValidationRules.CheckIllegalCharacters.prototype = 
{
	validate: function()
	{
		r = /[^a-zA-Z0-9~\^\*\-\\|\]\}\[\{\.,;]/;
		m = this.text.value().match(r);
		if (m == null)
		{
			return new ValidationResults("valid", "");
		}
		else
		{
			return new ValidationResults("error", "Illegal character: " + Nexopia.Utilities.escapeHTML(m.toString()));
		}
	},


	isServerSide: function()
	{
		return true;
	}
};

ValidationRules.CheckNoSpaces.prototype = 
{
	validate: function()
	{
		m = this.text.value().match(/\s/);

		if (m == null)
		{
			return new ValidationResults("valid", "");
		}
		else
		{
			return new ValidationResults("valid", "No spaces allowed");
		}
	},


	isServerSide: function()
	{
		return false;
	}
};

ValidationRules.CheckAlphaCharactersExist.prototype = 
{
	validate: function()
	{
		m = this.text.value().match(/[^\d]/);

		if (m == null)
		{
			return new ValidationResults("error", "Must have letters.");
		}
		else
		{
			return new ValidationResults("valid", "");
		}
	},


	isServerSide: function()
	{
		return false;
	}
};

ValidationRules.CheckLegalUsername.prototype = 
{
	validate: function()
	{
		/* TODO: Do this in an AJAX call. */
	},


	isServerSide: function()
	{
		return true;
	}
};

ValidationRules.CheckPasswordLength.prototype = 
{	
	validate: function()
	{
		if (this.password.value().length < 4)
		{
			return new ValidationResults("error", "4 characters minimum");
		}
		else if (this.password.value().length > 32)
		{
			return new ValidationResults("error", "32 characters maximum");
		}
		else
		{
			return new ValidationResults("valid", "");
		}
	},


	isServerSide: function()
	{
		return false;
	}
};

ValidationRules.CheckEmailSupported.prototype =
{
	validate: function()
	{
		not_supported = /.*@((?:mailinator|dodgeit)\.com)$/;

		if (this.email.value().match(not_supported) != null)
		{
			holder = this.email.value().replace(not_supported, "$1");
			return new ValidationResults("error", holder + " cannot currently be used with Nexopia.");
		}

		return new ValidationResults("valid", "");
	},


	isServerSide: function()
	{
		return false;
	}
};


ValidationRules.CheckEmailSufficientlyDead.prototype = 
{
	validate: function()
	{
		// TODO: Do as AJAX call
	},


	isServerSide: function()
	{
		return true;
	}
};

ValidationRules.CheckEmailNotBanned.prototype =
{
	validate: function()
	{
		// TODO: Do as an AJAX call
		return new CheckNotBanned(this.email.value(), "E-mail").validate();
	},


	isServerSide: function()
	{
		return true;
	}
};

ValidationRules.CheckEmailSyntax.prototype =
{	
	validate: function()
	{
		// TODO: AJAX Call!!!
	},


	isServerSide: function()
	{
		return true;
	}
};

ValidationRules.CheckIPNotBanned.prototype =
{
	validate: function()
	{
		// TODO: Do as an AJAX call
		return new CheckNotBanned(this.ip.value(), "IP").validate();
	},


	isServerSide: function()
	{
		return true;
	}
};

ValidationRules.CheckNotBanned.prototype =
{	
	validate: function()
	{
		// TODO: Do as an AJAX call
	},


	isServerSide: function()
	{
		return true;
	}
};

ValidationRules.CheckTerms.prototype =
{
	validate: function()
	{
		// TODO: Make this an AJAX call
	},


	isServerSide: function()
	{
		return true;
	}
};

ValidationRules.CheckDateOfBirth.prototype =
{
	validate: function()
	{
		if (this.year.value() == null && this.month.value() == null && this.day.value() == null)
		{
			return new ValidationResults("none","");
		}
	
		if (this.year.value() == -1 || this.month.value() == -1 || this.day.value() == -1)
		{
			return new ValidationResults("error", "Invalid Date of Birth");
		}
		else
		{
			current = new Date();
			current_year = current.getFullYear();
			current_month = current.getMonth() + 1;
			current_day = current.getDate();
			age = current_year - this.year.value();

			if (this.month.value() > current_month || (this.month.value() == current_month && this.day.value() > current_day))
			{
				age = age - 1;
			}
		
			if (age < 13)
			{
				return new ValidationResults("error", "Must be 13 or over to join");
			}
			else if (age > 70)
			{
				return new ValidationResults("error", "Must be less than 70");
			}
			else
			{
				return new ValidationResults("valid", "");
			}
		}
	},


	isServerSide: function()
	{
		return false;
	}
};

ValidationRules.CheckLocation.prototype = 
{
	validate: function()
	{
		if (this.location.value() == null)
		{
			return new ValidationResults("none","");
		}

		if (this.location.value() == 0)
		{
			return new ValidationResults("error", "Must select a valid Location");
		}
		else
		{
			return new ValidationResults("valid", "");
		}			
	},


	isServerSide: function()
	{
		return false;
	}
};

ValidationRules.CheckSex.prototype =
{
	validate: function()
	{
		if (this.sex.value() == null)
		{
			return new ValidationResults("none","");
		}

		if (this.sex.value() == "")
		{
			return new ValidationResults("error", "Must select Male or Female");
		}
		else
		{
			return new ValidationResults("valid", "");
		}
	},


	isServerSide: function()
	{
		return false;
	}
};

ValidationRules.CheckTimezone.prototype =
{
	validate: function()
	{
		if (this.timezone.value() == null)
		{
			return new ValidationResults("none","");
		}

		if (this.timezone.value() == 0)
		{
			return new ValidationResults("error", "Must select a Timezone");
		}
		else
		{
			return new ValidationResults("valid", "");
		}
	},


	isServerSide: function()
	{
		return false;
	}
};

ValidationRules.CheckPasswordStrength.prototype =
{
	validate: function()
	{
		weak = false;
		if ( (!this.username.value() == null && this.username.value() != "" && !this.password.value().index(this.username.value()) == null) ||
			this.password.value() == "secret")
		{
			return new ValidationResults("warning", "Weak");
		}
		else if (
			this.password.value().match(/^[a-z]*$/) != null || 				// Warn if all lowercase letters
			this.password.value().match(/^[A-Z]*$/) != null ||				// Warn if all uppercase letters
			this.password.value().match(/^[a-zA-Z][a-z]*$/) != null)		// Warn if only first is uppercase and the rest are lowercase)
		{
			return new ValidationResults("warning", "Medium");
		}
		else
		{
			return new ValidationResults("valid", "Strong");
		}
	},


	isServerSide: function()
	{
		return false;
	}
};
CollapsibleModule={};

Collapsible = {
	
	toggle: function(event, element) {
		optionsState = Nexopia.json(element);
		optionsState.state = !optionsState.state;
		
		// Don't save state for anonymous users.
		if (parseInt(optionsState.userid, 10) > 0) {
			YAHOO.util.Connect.asyncRequest('POST', "/my/collapsible/" + optionsState.typeid + "/" + optionsState.owneruserid + "/" + optionsState.secondaryid + "/" + "toggle",  
				new ResponseHandler({}), "state=" + optionsState.state);
		}
		
	}
};

Overlord.assign({
	minion: "collapsible:toggle",
	click: Collapsible.toggle,
	order: 1
});
PanelsModule={};

/* 
	This is the base class for AsyncPanel and DivPanel. It should not be used directly. Use one of the
	subclasses. The subclasses are classically used via a minion_name on an HTML element.
	
	Optional attributes in addition to minion_name for all subclasses:
	
	exit_path: 	The path to post any included form and/or external form (on the page, but outside of the
				dialog, pointed to by "form_id") to. A spinner will again be displayed instead of the dialog,
				and control will return to the user once the post request has been made via ajax.

	form_id: 	The id of a form that is on the page, but not in the dialog box (this is especially useful 
				when you're selecting a bunch of elements on the page to do an operation on). Note that if
				there is also a form in the dialog, that form's data will be appended to the parameter string
				before setting the form referenced by "form_id" so that the parameters from both forms get
				passed to the "exit_path" handler.

	ajax_exit: 	"true" or "false". If "true" (which is the default if this attribute is not set), the panel
				will save via an async request and attempt to refresh any elements returned in the usual way
				that ResponseHandler refreshes elements. If "false", the panel will save via the regular
				form post (which means there needs to either be a form_id specified, a form within the dialog,
				or both) mechanism (as if the user had hit a "submit" button).
	
	new_profile_block_parent: Specifies the parent node for new objects returned by an ajax call. This is only relevant if
					 the ajax call will return an object with an id that doesn't currently exist on the page.
					 The new node will be inserted as the first child of the parent.
	
	Important: Inside the dialog, there should be a maximum of ONE form.
	
	Special element classes inside the dialog:
	
	button.nexopia_panel_close: 			A button with this class will get a click listener automatically added to it
	 										that will close the dialog.
	button.nexopia_panel_save_and_close: 	A button with this class will get a click listener automatically added to it
	 										that will post all the inner form data, plus any form data in an optional
											external form (identified by "form_id"), to the page handler specified by
											"exit_path".
											
	Custom YUI events that can be subscribed to:
	
	panelOpenedEvent: 	handler arguments(event)
	panelClosedEvent: 	handler arguments(event)
	panelSavedEvent: 	handler arguments(event)
	panelSetupEvent: 	handler arguments(event,element_id)
	
	A delegate can be set on the panel which can implement the following methods:
	
	validateBeforeOpen: 		return true if the dialog can still open, false otherwise
	validateBeforePostAndClose: return true if the dialog can proceed with posting and closing, false otherwise
	
	The inner form element of the panel is given as the first argument in each case.
*/
// TODO: use configuration objects instead of parameters to abstract out the minion init code
NexopiaPanel = function(cfg) {
	this.cfg = cfg;
	this.panelOpenedEvent = new YAHOO.util.CustomEvent("panelOpen", this);
	this.panelClosedEvent = new YAHOO.util.CustomEvent("panelClose", this);
	this.panelSavedEvent = new YAHOO.util.CustomEvent("panelSave", this);
	this.panelDisabled = false;
};

NexopiaPanel.prototype = {
	overlay: null, //the YUI panel widget
	spinnerBody: "<div id='nexopia_panel_spinner'><img src='"+Site.staticFilesURL+"/nexoskel/images/large_spinner.gif'/></div>",
	showSpinner: function() {
		if (this.overlay) {
			this.overlay.destroy();
			this.overlay = null;
		}
		this.initOverlay();
		this.overlay.setBody(this.spinnerBody);
		this.overlay.render(document.body);
		this.overlay.center();
	},
	open: function(event) {
		if(this.panelDisabled)
		{
			return;
		}
		
		if (event) {
			YAHOO.util.Event.preventDefault(event);
			var element = YAHOO.util.Event.getTarget(event);
			
			if(this.delegate && this.delegate.validateBeforeOpen)
			{
				var innerForm = YAHOO.util.Dom.getElementsBy(function(el){return true;},'form',this.overlay.innerElement)[0];
				if(!this.delegate.validateBeforeOpen(innerForm))
				{
					return;
				}
			}
			
			//linkBeforeOpenMap is a map of element ids to functions, if an element id is in the map
			//the function is executed with the element, if it returns false we do not open a panel
			if(NexopiaPanel.linkBeforeOpenMap[element.id])
			{
				if(!NexopiaPanel.linkBeforeOpenMap[element.id](element))
				{
					return;
				}
			}
		}
		
		NexopiaPanel.current = this;
		this.drawPanel();
		
		var forms = YAHOO.util.Dom.getElementsBy(function(el){return true;},'form',this.overlay.innerElement);
		if(forms.length > 0)
		{
			var inputs = YAHOO.util.Dom.getElementsBy(function(el){return true;},'input',forms[0]);
			if (inputs.length > 0)
			{
				inputs[0].focus();
			}
		}
		
		this.panelOpenedEvent.fire();
	},
	disableButtons: function()
	{
		// Remove any close button listeners
		var closeButtons = YAHOO.util.Dom.getElementsByClassName('nexopia_panel_close', null, this.overlay.innerElement);
		for(var i = 0; i < closeButtons.length; i++)
		{
			closeButtons[i].disabled = true;
			YAHOO.util.Event.removeListener(closeButtons[i], 'click');
		}
		
		// Remove any close button listeners
		var saveAndCloseButtons = YAHOO.util.Dom.getElementsByClassName('nexopia_panel_save_and_close', null, this.overlay.innerElement);
		for(var i = 0; i < saveAndCloseButtons.length; i++)
		{
			saveAndCloseButtons[i].disabled = true;
			YAHOO.util.Event.removeListener(saveAndCloseButtons[i], 'click');
		}
	},
	close: function(event, element) {
		if (event) {
			YAHOO.util.Event.preventDefault(event);
		}
		
		this.disableButtons();
		
		this.overlay.destroy();
		this.overlay = null;
				
		NexopiaPanel.current = null;
		this.panelClosedEvent.fire();
	},
	postAndClose: function(event, element) {
		if (event) {
			YAHOO.util.Event.preventDefault(event);
		}
		var innerForm = YAHOO.util.Dom.getElementsBy(function(el){return true;},'form',this.overlay.innerElement)[0];
		if(this.delegate && this.delegate.validateBeforePostAndClose)
		{
			if(!this.delegate.validateBeforePostAndClose(innerForm))
			{
				return;
			}
		}
		
		this.disableButtons();
		
		var that = this;		
		
		// If we have an external form_id, we'll want to set it as the form to use in the asyncRequest. However, if
		// we also have the internal form, we'll want to take all of it's data and append it to the asyncRequest's
		// parameter string
		var form = null;
		
		if (this.cfg.form_id)
		{
			form = document.createElement("form");
			YAHOO.util.Dom.setStyle(form, "display", "none");
			document.body.appendChild(form);

			Nexopia.Utilities.concatForm(form, innerForm);
			
			var outerForm = document.getElementById(this.cfg.form_id);
			Nexopia.Utilities.concatForm(form, outerForm);
		}
		else
		{
			form = innerForm;
		}

		if (this.cfg.ajax_exit)
		{
			YAHOO.util.Connect.setForm(form);

			this.showSpinner();
			// For some reason, the minified version of container doesn't seem to be able to set the zIndex properly,
			// so we are doing that again after the panel has been initialized.
			this.overlay.cfg.setProperty("zIndex", 500);

			YAHOO.util.Connect.asyncRequest(this.cfg.exit_method, this.cfg.exit_path, new ResponseHandler({
				success: function(o) {
					this.panelSavedEvent.fire();
					if (form && this.cfg.form_id)
					{
						document.body.removeChild(form);
					}
					that.close();
				},
				failure: function(o) {
					if (form && this.cfg.form_id)
					{
						document.body.removeChild(form);
					}
					that.close();
				},
				newNode: function(node) {
					if ( (this.cfg.new_profile_block_parent != null) && (this.cfg.new_profile_block_parent != "") ) {

						var newNodeParent = YAHOO.util.Dom.get(this.cfg.new_profile_block_parent);
						var firstBlock = YAHOO.util.Dom.getElementBy(function(candidate) {
							var minionName = candidate.getAttribute('minion_name');
							return (YAHOO.util.Dom.hasClass(candidate, 'place_holder') ||
									minionName && minionName.indexOf("profile:block") != -1);
						}, 'div', newNodeParent);
						
						//I don't know why parentNode is sometimes null here but I just added the check
						//rather than figuring it out due to time constraints. This can happen when no blocks
						//exist.
						if (firstBlock && firstBlock.parentNode) {
							firstBlock.parentNode.insertBefore(node, firstBlock);
						} else {
							newNodeParent.appendChild(node);
						}

						return true;
					} else {
						return false;
					}
				},
				scope: this
			}), "ajax=true");
		}
		else
		{
			form.method = this.cfg.exit_method;
			form.action = this.cfg.exit_path;
			form.submit();
		}
	},
	redirect: function()
	{
		window.location = this.cfg.exit_path;
	},
	//returns a reference to the yui overlay, initializing it if it doesn't exist.
	initOverlay: function() {
		if (!this.overlay) {
			this.overlay = new YAHOO.widget.Panel("nexopia_panel", {
				fixedcenter: false,
				visible: true,
				modal:true,
				close:false,
				draggable:false,
				zIndex: 500,
				iframe: true,
				underlay: "none"
			});
			this.overlay.render(document.body);
			
			// For some reason, the minified version of container doesn't seem to be able to set the zIndex properly,
			// so we are doing that again after the panel has been initialized.
			this.overlay.cfg.setProperty("zIndex", 500);
			
			// Fix for a problem in Firefox 2 where the panel won't show.
			if(YAHOO.env.ua.gecko < 1.9)
			{
				YAHOO.util.Dom.setStyle(this.overlay.innerElement, 'position', 'relative');
			}
		}
	},
	findButtonsWithClass: function(className) {
		return YAHOO.util.Dom.getElementsByClassName(className, 'button', this.overlay.innerElement).concat(
			YAHOO.util.Dom.getElementsByClassName(className, 'a', this.overlay.innerElement).concat(
			YAHOO.util.Dom.getElementsByClassName(className, 'img', this.overlay.innerElement).concat(
			YAHOO.util.Dom.getElementsByClassName(className, 'input', this.overlay.innerElement)
		)));
	},
	initializeButtons: function()
	{
		var closeButtons = this.findButtonsWithClass('nexopia_panel_close');
		for(var i = 0; i < closeButtons.length; i++)
		{
			YAHOO.util.Event.on(closeButtons[i], 'click', this.close, this, true);
		}
		
		var postAndCloseButtons = this.findButtonsWithClass('nexopia_panel_save_and_close');
		for(var i = 0; i < postAndCloseButtons.length; i++)
		{
			YAHOO.util.Event.on(postAndCloseButtons[i], 'click', this.postAndClose, this, true);
		}
		
		var redirectButtons = this.findButtonsWithClass('nexopia_panel_redirect');
		for(var i = 0; i < redirectButtons.length; i++)
		{
			YAHOO.util.Event.on(redirectButtons[i], 'click', this.redirect, this, true);
		}
	},
	render: function()
	{
		this.overlay.render(document.body);
		this.overlay.element = this.overlay.element.firstChild; //This bypasses a centering problem in some versions of firefox
		this.overlay.center();
		Overlord.summonMinions(this.overlay.element);

		this.initializeButtons();		
	},
	setDisabled:function(panelDisabled)
	{
		this.panelDisabled = panelDisabled;
	},
	setDelegate:function(delegate)
	{
		this.delegate = delegate;
	}
};

NexopiaPanel.createConfig = function(element)
{
	var ajaxExit = null;
	var ajaxExitString = element.getAttribute('ajax_exit');
	
	if (ajaxExitString == null || ajaxExitString == "")
	{
		ajaxExit = true;
	}
	else
	{
		ajaxExit = ajaxExitString == "true";
	}
	
	var exitMethod = element.getAttribute('exit_method');
	if (exitMethod == null || exitMethod == "")
	{
		exitMethod = "post";
	}

	return { 
		exit_path: element.getAttribute('exit_path'), 
		exit_method: exitMethod,
		form_id: element.getAttribute('form_id'), 
		ajax_exit: ajaxExit,
		new_profile_block_parent: element.getAttribute('new_profile_block_parent')
	};
};

NexopiaPanel.setup = function(element, panel)
{
	if (element.id) {
		NexopiaPanel.linkMap[element.id] = panel;
		NexopiaPanel.panelSetupEvent.fire(element.id);
	}
	YAHOO.util.Event.on(element, 'click', panel.open, panel, true);
};

NexopiaPanel.panelSetupEvent = new YAHOO.util.CustomEvent("panelSetup", null);

NexopiaPanel.linkBeforeOpenMap = {};

NexopiaPanel.linkMap = {};
NexopiaPanel.current = null; //This will refer to the currently open panel if a panel is open

NexopiaPanel.onSave = function(summoningElement, handler)
{
	var panel = NexopiaPanel.linkMap[summoningElement.id];
	if(panel)
	{
		panel.panelSavedEvent.subscribe(handler);
	}
	else
	{
		var setupHandler = function(event, elementID) 
		{
			if(elementID == summoningElement.id)
			{
				panel = NexopiaPanel.linkMap[summoningElement.id];
				panel.panelSavedEvent.subscribe(handler);
				NexopiaPanel.panelSetupEvent.unsubscribe(this);
			}
		};
		NexopiaPanel.panelSetupEvent.subscribe(setupHandler, this);
	}
};

NexopiaPanel.onClose = function(summoningElement, handler)
{
	var panel = NexopiaPanel.linkMap[summoningElement.id];
	if(panel)
	{
		panel.panelClosedEvent.subscribe(handler);
	}
	else
	{
		var setupHandler = function(event, elementID) 
		{
			if(elementID == summoningElement.id)
			{
				panel = NexopiaPanel.linkMap[summoningElement.id];
				panel.panelClosedEvent.subscribe(handler);
				NexopiaPanel.panelSetupEvent.unsubscribe(this);
			}
		};
		NexopiaPanel.panelSetupEvent.subscribe(setupHandler, this);
	}
};

NexopiaPanel.onOpen = function(summoningElement, handler)
{
	var panel = NexopiaPanel.linkMap[summoningElement.id];
	if(panel)
	{
		panel.panelOpenedEvent.subscribe(handler);
	}
	else
	{
		var setupHandler = function(event, elementID) 
		{
			if(elementID == summoningElement.id)
			{
				panel = NexopiaPanel.linkMap[summoningElement.id];
				panel.panelOpenedEvent.subscribe(handler);
				NexopiaPanel.panelSetupEvent.unsubscribe(this);
			}
		};
		NexopiaPanel.panelSetupEvent.subscribe(setupHandler, this);
	}
};

NexopiaPanel.setDelegate = function(summoningElement, delegate)
{
	var panel = NexopiaPanel.linkMap[summoningElement.id];
	if(panel)
	{
		panel.setDelegate(delegate);
	}
	else
	{
		var setupHandler = function(event, elementID) 
		{
			if(elementID == summoningElement.id)
			{
				panel = NexopiaPanel.linkMap[summoningElement.id];
				panel.setDelegate(delegate);
				NexopiaPanel.panelSetupEvent.unsubscribe(this);
			}
		};
		NexopiaPanel.panelSetupEvent.subscribe(setupHandler, this);
	}
};

Overlord.assign({
	minion: "nexopia_panel:close",
	click: function(event, element) {
		YAHOO.util.Event.preventDefault(event);
		NexopiaPanel.current.close();
	}
});

Overlord.assign({	
	minion: "nexopia_panel:capture",
	keypress: function(event, element) {
		
		
		// In Safari and Chrome hitting 'Enter' on a NexopiPanel will do a form submit and NOT do the JavaScript
		// code on the submit button. So we need this code to capture the keypress and do our JS submit.
		// This doesn't screw up submits in Firefox since it doesn't even see the keypress.
		// IE has one issue with the Freeform edit box and will submit the form if you hit 'Enter' in the
		// title box.
		if (event.keyCode == 13) {
			
			// We want to preserve the functionality of textboxes, so don't submit if we have one on the form.
			textareas = YAHOO.util.Dom.getElementsBy(function(e){ return true; }, "textarea", element);			
			if ( textareas.length == 0 ) {
				YAHOO.util.Event.preventDefault(event);
				NexopiaPanel.current.postAndClose(event, element);
			}
			
		}
	},
	order: 1
	
});

//require nexopia_panel.js
/* 
	This will automatically load a modal overlay panel when an element  with minion_name="async_panel"
   	is clicked.  The panel is loaded off of the path attribute on the element.  If the loading link has
	an id on it a reference to the panel can be retrieved with AsyncPanel.get(id).  The currently open
	panel will be available in NexopiaPanel.current.
	
	Panels can also be created purely in javascript using new AsyncPanel(path) and then calling open()
	on the resulting object.
	
	See nexopia_panel.js for more information.
*/
AsyncPanel = function(cfg) {
	this.constructor.superclass.constructor.call(this, cfg);
};

YAHOO.extend(AsyncPanel, NexopiaPanel, {
	drawPanel: function()
	{
		this.showSpinner();
		if (this.cfg.load_form) {
			YAHOO.util.Connect.setForm(this.cfg.load_form);
		}
		YAHOO.util.Connect.asyncRequest('POST', this.cfg.path, new ResponseHandler({
			success: function(o) {
				this.overlay.setBody(o.responseText);
				this.render();
			},
			failure: function(o) {
				this.close();
			},
			scope: this,
			cache: false
		}));		
	}
});

AsyncPanel.createConfig = function(element)
{
	return YAHOO.lang.merge(NexopiaPanel.createConfig(element), { 
		path: element.getAttribute('path'),
		//load_form can be specified to pass parameters through in the request to load the panel
		//the most obvious example of where we want to do this is an async panel for preview functionality
		//where we need to pass some user input through to be displayed as a preview.
		load_form: YAHOO.util.Dom.get(element.getAttribute('load_form'))
	});
};

Overlord.assign({
	minion: "async_panel",
	load: function(element) {
		var panel = new AsyncPanel(AsyncPanel.createConfig(element));
		NexopiaPanel.setup(element, panel);
	}
});

Overlord.assign({
	minion: "async_redirect",
	load: function(element) {
		window.location.href = Nexopia.JSONData['redirect_url'];
	}
});


//require nexopia_panel.js
// TODO: This panel will load from a hidden Div on the page
DivPanel = function(cfg) {
	this.constructor.superclass.constructor.call(this, cfg);
};

YAHOO.extend(DivPanel, NexopiaPanel, {
	drawPanel: function()
	{
		this.initOverlay();
		if (!this.div) { this.div = document.getElementById(this.cfg.div_id).cloneNode(true); }
		this.overlay.setBody(this.div);
		
		YAHOO.util.Dom.setStyle(this.div, "display", "block");
		
		this.render();
		this.overlay.center();
		
		// Due to extreme WTF-ery, IE 7 fails when repositioning a div which contains input elements that have
		// a position of "absolute". The input elements will basically get left behind where the div used to be
		// unless I cycle through those elements again after moving the div (i.e. cycle through them here) and
		// reset the position value to the same damn thing it was before. Sheesh!
		if (YAHOO.env.ua.ie > 5 && YAHOO.env.ua.ie <= 7)
		{
			var inputElements = this.div.getElementsByTagName('input');
			for(var i = 0; i < inputElements.length; i++)
			{
				var inputElement = inputElements[i];
				var position = YAHOO.util.Dom.getStyle(inputElement, 'position');
				if(position == 'absolute')
				{
					YAHOO.util.Dom.setStyle(inputElement, 'position', 'absolute');
				}
			}
		}
	}
});

DivPanel.createConfig = function(element)
{
	return YAHOO.lang.merge(NexopiaPanel.createConfig(element), { 
		div_id: element.getAttribute('div_id')
	});
};

Overlord.assign({
	minion: "div_panel",
	load: function(element) {
		var panel = new DivPanel(DivPanel.createConfig(element));
		NexopiaPanel.setup(element, panel);
	}
});
HelpPanel = function(element) {
	this.element = element;
	this.content = this.element.getAttribute('content');
	this.title = this.element.getAttribute('title');
	YAHOO.util.Event.on(this.element, 'click', this.open, this, true);
};

HelpPanel.prototype = {
	element: null, //The element the help panel is attached to, normally a [?] link
	overlay: null, //The YUI Panel widget
	content: "", //The text of the help panel
	title: "Information",
	open: function(event) {
		YAHOO.util.Event.preventDefault(event);
		var xy = YAHOO.util.Event.getXY(event);
		xy[0] = xy[0]-200;
		this.overlay = new YAHOO.widget.Overlay("help_panel", {
			fixedcenter: false,
			visible: true,
			xy: xy
		});
		this.overlay.setBody("<a id='help_panel_close' href='#close'><img src='"+Site.staticFilesURL+"/panels/images/close_help_panel.gif'/></a><h1>"+this.title+"</h1><div>" + this.content + "</div>");
		this.overlay.render(document.body);
		YAHOO.util.Dom.setStyle('help_panel', 'zIndex', 1000);
		YAHOO.util.Event.on('help_panel_close', 'click', this.close, this, true);
	},
	close: function(event) {
		if (event) {
			YAHOO.util.Event.preventDefault(event);
		}
		this.overlay.destroy();
	}
};

Overlord.assign({
	minion: "help",
	load: function(element) {
		new HelpPanel(element);
	}
});
SlateModule={};

Slate = function(name, delegate) {
	this.name = name;
	YAHOO.lang.augmentObject(delegate, this.delegate, false);
	this.delegate = delegate;
};

//comment to change the digest
Slate.prototype = {
	delegate: {
		panelConfig: function(slate, options) {
			return Slate.defaultPanelConfig;
		},
		htmlContent: function(slate, options) {
			return "<div>You need to implement the function htmlContent on the delegate you pass to Slate.register(name, delegate).\
				<a href='#close' minion_name='slate:"+slate.name+":close'>Close</a>\
			</div>";
		},
		summonMinionsOnUpdate: function(slate, options) { //Should we summon minions when we update the htmlContent? Will be called each time we update.
			return true;
		}
	}, //delegate defaults
	load: function(options) {
		if (!this.delegate.shouldLoad || this.delegate.shouldLoad(this, options)) {
			if (this.delegate.willLoad) {
				this.delegate.willLoad(this, options);
			}
			this.updateSlateWithContent(this.delegate.htmlContent(this, options), options);
			Slate.current = this;
			if (this.delegate.didLoad) {
				this.delegate.didLoad(this, options);
			}
		}
	},
	refresh: function(options) {
		if (this.yuiPanel) {
			this.load(options);
		}
	},
	close: function(options) {
		if (!this.delegate.shouldClose || this.delegate.shouldClose(this, options)) {
			if (this.delegate.willClose) {
				this.delegate.willClose(this, options);
			}
			this.closeSlate();
			if (this.delegate.didClose) {
				this.delegate.didClose(this, options);
			}
		}
	},
	save: function(options) {
		if (!this.delegate.shouldSave || this.delegate.shouldSave(this, options)) {
			if (this.delegate.willSave) {
				this.delegate.willSave(this, options);
			}
			this.saveSlate(this, options);
			if (this.delegate.didSave) {
				this.delegate.didSave(this, options);
			}
		}
	},
	updateSlateWithContent: function(htmlContent, options) {
		if (!this.yuiPanel) {
			this.buildSlate();
		}
		this.yuiPanel.setBody(htmlContent);
		this.yuiPanel.render(document.body);
		this.yuiPanel.center();
		if (this.delegate.summonMinionsOnUpdate(this, options)) {
			Overlord.summonMinions(this.yuiPanel.element);
		}
		var slate = YAHOO.util.Dom.get('slate');
		YAHOO.util.Dom.setStyle(slate, 'position', 'absolute');
		YAHOO.util.Dom.setStyle(slate, 'left', '-' + slate.offsetWidth/2 + 'px');
		YAHOO.util.Dom.setStyle(slate, 'top', '-' + slate.offsetHeight/2 + 'px');
	},
	buildSlate: function() {
		YAHOO.log('Slate '+this.name+' built.', 'info', 'Slate');
		this.yuiPanel = new YAHOO.widget.Panel("slate", this.delegate.panelConfig());
	},
	closeSlate: function() {
		if (this.yuiPanel) {
			this.yuiPanel.destroy();
			this.yuiPanel = null;
		}
		Slate.current = null;
		YAHOO.log('Slate '+this.name+' closed.', 'info', 'Slate');
	},
	saveSlate: function() {
		YAHOO.log('Slate '+this.name+' saved.', 'info', 'Slate');
	}
};

Slate.slateTypes = {};

Slate.defaultPanelConfig = {
	fixedcenter: false,
	visible: true,
	modal:true,
	close:false,
	draggable:false,
	zIndex: 500,
	iframe: true,
	underlay: "none"
};

Slate.register = function(name, options) {
	var slate = new Slate(name, options);
	this.slateTypes[name] = slate;
	Overlord.assign({
		minion: "slate:" + name + ":load",
		click: function(event, element) {
			YAHOO.util.Event.preventDefault(event);
			slate.load(Slate.parseOptions(element));
		}
	});
	Overlord.assign({
		minion: "slate:" + name + ":close",
		click: function(event, element) {
			YAHOO.util.Event.preventDefault(event);
			slate.close(Slate.parseOptions(element));
		}
	});
	Overlord.assign({
		minion: "slate:" + name + ":save",
		click: function(event, element) {
			YAHOO.util.Event.preventDefault(event);
			slate.save(Slate.parseOptions(element));
		},
		order: -1
	});

	return slate;
};

Slate.parseOptions = function(element) {
	var options = {};
	for (var i=0;i<element.attributes.length;i++) {
		options[element.attributes[i].name] = element.attributes[i].value;
	}
	return options;
};

Slate.register('async', {
	spinner: "<div class='large_spinner'></div>",
	htmlContent: function(slate, options) {
		if (!this.asyncContent) {
			YAHOO.util.Connect.asyncRequest('POST', options.path, new ResponseHandler({
				success: function(o) {
					this.asyncContent = o.responseText;
					slate.refresh(options);
				},
				scope: this
			}), options.postData);
			return this.spinner;
		} else {
			return this.asyncContent;
		}
	},
	didClose: function(slate, options) {
		this.asyncContent = null;
	},
	willSave: function(slate, options) {
		var form = YAHOO.util.Dom.getElementsBy(function() {return true;}, 'form', slate.yuiPanel.element)[0];
		if (form) {
			YAHOO.util.Connect.setForm(form);
			var path = options.path || form.action;
		} else {
			var path = options.path;
		}
		if (path) {
			YAHOO.util.Connect.asyncRequest('POST', path, new ResponseHandler({
				success: function(o) {
					slate.close(options);
				},
				failure: function(o) {
					slate.close(options);
				}
			}), options.postData);
		} else {
			slate.close(options);
		}
		this.asyncContent = this.spinner;
		slate.refresh();
	}
});

Slate.register('node', {
	node: null,
	htmlContent: function(slate, options) {
		if (!this.node) {
			this.node = YAHOO.util.Dom.get(options.nodeId);
		}
		var temp = document.createElement('temp');
		temp.appendChild(this.node);
		YAHOO.util.Dom.setStyle(this.node, 'display', 'block');
		return temp.innerHTML;
	},
	didClose: function(slate, options) {
		if (this.node) {
			YAHOO.util.Dom.setStyle(this.node, 'display', 'none');
			document.body.appendChild(this.node);
			this.node = null;
		}
	}
});
ShoutsModule={};

ShoutFall = {
	maxShouts: 50, //maximum shouts to have on the page at once
	pollFrequency: 5000, //ms
	displayFrequency: 2000, //ms
	idleTimeout: 300000, //ms, 5 minutes
	lastShout: 0,
	filterType: "public",
	filterID: 0,
	pollTimer: null,
	idleTimer: null,
	panel: null,
	nodeQueue: null,
	body: null, //this is set by a load handler for performance reasons
	noShouts: null, //optional node set by a load handler that should be hidden the first time a shout is displayed.
	init: function(element) {
		this.pollTimer = YAHOO.lang.later(this.pollFrequency, this, this.refresh, null, true);
		YAHOO.lang.later(this.displayFrequency, this, this.displayNext, null, true);
		this.idleTimer = YAHOO.lang.later(this.idleTimeout, this, this.timeout, null, false);
		var extraData = Nexopia.json(element);
		this.lastShout = extraData['priority'];
		this.filterType = extraData['filter_type'];
		this.filterID = extraData['filter_id'];
		this.nodeQueue = YAHOO.util.Dom.getElementsByClassName('node_queue', 'div', element)[0];
		this.animation = YAHOO.util.Dom.get('animation');
		YAHOO.util.Event.on(document.body, 'click', this.resetIdle, this, true);
		YAHOO.util.Event.on(document.body, 'keypress', this.resetIdle, this, true);
	},
	setBody: function(element) {
		this.body = element;
	},
	restart: function() {
		YAHOO.util.Dom.setStyle("shouts_timeout", 'display', 'none');
		this.pollTimer = YAHOO.lang.later(this.pollFrequency, this, this.refresh, null, true);
		this.idleTimer = YAHOO.lang.later(this.idleTimeout, this, this.timeout, null, false);

		var bannerElementShoutPage = document.getElementById('banner_iframe_3');
		var bannerElementHeader = document.getElementById('banner_iframe_2');
		var bannerElementSidebar = document.getElementById('banner_iframe_5');
		if(Banner && Banner.refreshBanner) {
			if (bannerElementShoutPage)
				Banner.refreshBanner(bannerElementShoutPage, Banner.BIGBOX);
			if (bannerElementHeader)
				Banner.refreshBanner(bannerElementHeader, Banner.LEADERBOARD);
			if (bannerElementSidebar)
				Banner.refreshBanner(bannerElementSidebar, Banner.SKY160);
		}
	},
	refresh: function() {
		if (this.nodeQueue.childNodes.length < (this.pollFrequency/this.displayFrequency)) {
			YAHOO.util.Connect.asyncRequest('POST', Site.wwwURL + '/shouts/refresh/' + this.filterType + '/' + this.filterID, new ResponseHandler({ 
				newNode: function(node) {
					if (YAHOO.util.Dom.hasClass(node, 'last_shout')) {
						this.lastShout = node.innerHTML;
						return false;
					} else if (node.tagName.toUpperCase() != "SCRIPT") {
						this.nodeQueue.appendChild(node);
						return true;
					} else {
						return false;
					}
				},
				scope: this
			}), 'last_shout=' + this.lastShout);
		}
	},
	displayNext: function() {
		var nextNode = this.nodeQueue.firstChild;
		if (nextNode) {
			if (this.noShouts) {
				YAHOO.util.Dom.setStyle(this.noShouts, 'display', 'none');
				this.noShouts = null;
			}
			YAHOO.util.Dom.setStyle(nextNode, 'visibility', 'hidden');
			YAHOO.util.Dom.setStyle(nextNode, 'position', 'absolute');
			if (this.body.firstChild) {
				this.body.insertBefore(nextNode, this.body.firstChild);
			} else {
				this.body.appendChild(nextNode);
			}
			if (this.body.childNodes.length > this.maxShouts) {
				this.body.removeChild(this.body.lastChild);
			}
			if (this.animation.checked) {
				var positionAnim = new YAHOO.util.Anim(this.body, {
					top: { by: nextNode.offsetHeight+12 } //12 pixel padding doesn't get noticed by the offset height, sorry about the magic number
				}, 0.4, YAHOO.util.Easing.easeIn);
				var that = this;
				positionAnim.onComplete.subscribe(function() {
					YAHOO.util.Dom.setStyle(that.body, 'top', '0px');
					YAHOO.util.Dom.setStyle(nextNode, 'position', 'static');
					var shoutBox = YAHOO.util.Dom.getElementsByClassName('shout_box', 'div', nextNode)[0];
					var widthAnim = new YAHOO.util.Anim(shoutBox, {
						width: { from: 0, to: shoutBox.offsetWidth-12 }
					}, 0.4);
					YAHOO.util.Dom.setStyle(shoutBox, 'width', '0px');
					var img = YAHOO.util.Dom.getElementsByClassName('picture', 'img', nextNode)[0];
					YAHOO.util.Dom.setStyle(img, 'opacity', '0');
					var opacityAnim = new YAHOO.util.Anim(img, {
						opacity: { from: 0, to: 1 }
					}, 0.4); 
					YAHOO.util.Dom.setStyle(nextNode, 'visibility', 'visible');
					widthAnim.animate();
					opacityAnim.animate();
				});
				positionAnim.animate();
			} else {
				YAHOO.util.Dom.setStyle(nextNode, 'position', 'static');
				YAHOO.util.Dom.setStyle(nextNode, 'visibility', 'visible');
			}
		}
	},
	timeout: function() {
		YAHOO.util.Dom.setStyle("shouts_timeout", 'display', 'block');
		this.pollTimer.cancel();
		this.idleTimer = null;
	},
	resetIdle: function() {
		if (this.idleTimer) {
			this.idleTimer.cancel();
			this.idleTimer = YAHOO.lang.later(this.idleTimeout, this, this.timeout, null, false);
		}
	},
	setAnimation: function(event, element) {
		var form = YAHOO.util.Dom.getAncestorByTagName(element, 'form');
		if (Nexopia.json(form)) {
			YAHOO.util.Connect.setForm(form);
			YAHOO.util.Connect.asyncRequest('POST', form.action, { }, "void=void");
		}
	}
};

Overlord.assign({
	minion: "shout_fall",
	load: ShoutFall.init,
	scope: ShoutFall
});

Overlord.assign({
	minion: "shout_fall:restart",
	click: ShoutFall.restart,
	scope: ShoutFall
});

Overlord.assign({
	minion: "shout_fall:animate",
	click: ShoutFall.setAnimation,
	scope: ShoutFall
});

Overlord.assign({
	minion: "shouts:display",
	load: ShoutFall.setBody,
	scope: ShoutFall
});

Overlord.assign({
	minion: "shout_fall:no_shouts",
	load: function(element) {
		ShoutFall.noShouts = element;
	}
});
Shouts = {
	MAX_LENGTH: 140,

	blinkSidebarShout: function() {
		var shoutBlock = YAHOO.util.Dom.get('static_shout_div').parentNode;
		var startColor = YAHOO.util.Dom.getStyle(shoutBlock, 'background-color');
		var blinkColor = YAHOO.util.Dom.getStyle(shoutBlock.parentNode, 'background-color');
		var anim = new YAHOO.util.ColorAnim(shoutBlock, {backgroundColor: { to: blinkColor } }, 0.25);
		anim.animate();
		anim.onComplete.subscribe(function() {
			var anim = new YAHOO.util.ColorAnim(shoutBlock, {backgroundColor: { to: startColor } }, 0.25); 
			anim.animate();
		});
	},
	
	sidebarSubmit: function(event, element) {
		YAHOO.util.Event.preventDefault(event);

		if (Shouts.inSubmit) {
			return;
		}
		Shouts.inSubmit = true;

		var sidebar_shout_button = document.getElementById("sidebar_shout_button");
		sidebar_shout_button.disabled = true;
		
		var new_shout = document.getElementById("sidebar_new_shout_text");	
		
		this.limitShoutLength(null, new_shout);	
		
		if (!new_shout.value.match(/^\s*$/)) {

			var shout_form = YAHOO.util.Dom.getAncestorByTagName(element, "form");
			YAHOO.util.Connect.setForm(shout_form);

			var spinner = document.getElementById("sidebar_shouts_spinner");
			YAHOO.util.Dom.setStyle(spinner, "display", "inline");

			YAHOO.util.Connect.asyncRequest(shout_form.attributes.method.value, shout_form.attributes.action.value, new ResponseHandler({
				success: function(o) { 
					var spinner = document.getElementById("sidebar_shouts_spinner");
					YAHOO.util.Dom.setStyle(spinner, "display", "none");
					var new_shout = document.getElementById("sidebar_new_shout_text");				
					new_shout.value = "";

					var sidebar_shout_button = document.getElementById("sidebar_shout_button");
					Shouts.shoutCollapse();
					sidebar_shout_button.disabled = false;
					Shouts.inSubmit = false;
			
				},
				failure: function(o) { 
			 		spinner = document.getElementById("sidebar_shouts_spinner");
					YAHOO.util.Dom.setStyle(spinner, "display", "none");
					newest_shout = document.getElementById("newest_shout");
					newest_shout.innerHTML = "There was an error submitting your shout.  Try again in a sec.";

					var sidebar_shout_button = document.getElementById("sidebar_shout_button");
					Shouts.shoutCollapse();
					sidebar_shout_button.disabled = false;
					Shouts.inSubmit = false;
				},
				scope: this
			}), "ajax=true");
		}
		
		// The user tried to shout an empty string or just whitespace. Re-enable the Shout button so the user can try and shout something else.
		else {
			sidebar_shout_button.disabled = false;
			Shouts.inSubmit = false;
		}
		
	},
	
	writeShout: function(event, element) {		
		YAHOO.util.Event.preventDefault(event);
		
		if (Shouts.inSubmit) {
			return;
		}
		Shouts.inSubmit = true;

		var shout_button = document.getElementById("shout_button");
		shout_button.disabled = true;
		
		var new_shout = document.getElementById("new_shout_text");
		
		this.limitShoutLength(null, new_shout);
		if (!new_shout.value.match(/^\s*$/)) {
			
			var spinner = document.getElementById("shouts_spinner");
			YAHOO.util.Dom.setStyle(spinner, "display", "inline");
			
			// There's probably a way better way of doing this, but I'm trying to 
			// tell the ruby side what page we're coming from so I can go back to that
			// page after the post.
			var page_reference = document.getElementById("page_reference");
			var shout_form = YAHOO.util.Dom.getAncestorByTagName(element, "form");
			var last_child = YAHOO.util.Dom.getLastChild(shout_form);
			var referer = document.createElement("input");
			YAHOO.util.Dom.setAttribute(referer, "name", "page_reference");
			YAHOO.util.Dom.setAttribute(referer, "value", page_reference.value);
			YAHOO.util.Dom.setAttribute(referer, "type", "hidden");
			
			YAHOO.util.Dom.insertAfter(referer, last_child);
			shout_form.submit();
		}
		
	},
	
	shoutExpand: function(event, element) {
		shout_new = document.getElementById("new_shout_div");
		shout_static = document.getElementById("static_shout_div");

		YAHOO.util.Dom.setStyle(shout_static, "display", "none");
		YAHOO.util.Dom.setStyle(shout_new, "display", "block");
		
		shout_box = YAHOO.util.Dom.getElementsByClassName("shout_write", "textarea", shout_new)[0];
		shout_box.value = "";
		shout_box.focus();
		Nexopia.Utilities.setCaretPosition(shout_box, 0);
	},

	shoutCollapse: function(event, element) {
		if (event) {
			YAHOO.util.Event.preventDefault(event);
		}
		
		// We collapse after the user submits a shout or cancels a shout. In either case,
		// we want to clear out the previous value.
		var shout_box = document.getElementById("sidebar_new_shout_text");
		shout_box.value = "";
		
		// Manually update the character counter
		if(shout_box.characterCounter)
		{	
			shout_box.characterCounter.update();
		}
		
		shout_new = document.getElementById("new_shout_div");
		shout_static = document.getElementById("static_shout_div");
		
		YAHOO.util.Dom.setStyle(shout_static, "display", "block");
		YAHOO.util.Dom.setStyle(shout_new, "display", "none");		
		
	},	
	
	shoutReply: function(event, element) {
		YAHOO.util.Event.preventDefault(event);
		
		var reply_to = Nexopia.json(element);

		// if we can't find the Shouts box we'll have to ajax it in.
		var sidebar_shout_box = document.getElementById("sidebar_new_shout_text");		
		if( sidebar_shout_box == null ) {
			
			YAHOO.util.Connect.asyncRequest('POST', "/sidebar/shouts/0", new ResponseHandler({ 
				newNode: function(node) {
					if (node.tagName.toUpperCase() != "SCRIPT") {

						// Insert the shout box into the top of the sidebar.
						var first_sidebar_block = document.getElementById("main_sidebar_block").firstChild;
						YAHOO.util.Dom.insertBefore(node, first_sidebar_block);

						// Get the shout box we just added and expand it as if the user clicked in it.
						var sidebar_shout_box = document.getElementById("sidebar_new_shout_text");
						Shouts.shoutExpand(event, sidebar_shout_box);

						// add the reply text
						var reply_text = "@" + reply_to + " ";
						sidebar_shout_box.value = reply_text;
						sidebar_shout_box.focus();

						this.setCaretPosition(reply_text.length);

						// The floating header will cover the textbox unless the entire page is bumped down a bit
						window.scrollBy(0, -65);

						return true;
												
					} else {
						return false;
					}
				},
				scope: this
			}));
			
		} else {
			Shouts.shoutExpand(event, sidebar_shout_box);
			Shouts.blinkSidebarShout();
		
			var reply_text = "@" + reply_to + " ";
			sidebar_shout_box.value = reply_text;
			sidebar_shout_box.focus();
		
			this.setCaretPosition(reply_text.length);
		
			// The floating header will cover the textbox unless the entire page is bumped down a bit
			window.scrollBy(0, -65);
		
		}
	},

	// Set's the focus on the reply box if we're loading the page with 
	replyFocus: function(element) {
		if (element.innerHTML != "" ) {
			element.focus();
			this.setCaretPosition(element.innerHTML.length);
		}
	},
	
	deleteShout: function(event, element) {
		YAHOO.util.Event.preventDefault(event);
		
		var delete_form_key = document.getElementById('delete_form_key').value;

		YAHOO.util.Connect.asyncRequest("POST", element.href, new ResponseHandler({
			success: function(o) {
		
			},
			failure: function(o) {
			},
			scope: this
		}), "form_key[]=" + delete_form_key);
		
		
	},
		
	sidebarSubmitOnEnter: function(event, element) {
		if (!element.value.match(/^\s*$/)) {		
			if (event.keyCode == 13) {
				this.limitShoutLength(event, element);
				YAHOO.util.Event.preventDefault(event);
				Shouts.sidebarSubmit(event, element);
			}
		}
	},
	
	submitOnEnter: function(event, element) {
		if (!element.value.match(/^\s*$/)) {
			if (event.keyCode == 13) {
				this.limitShoutLength(event, element);
				YAHOO.util.Event.preventDefault(event);
				Shouts.writeShout(event, element);
			}
		}
	},

	PMMode: false,
	PMRegex: /^PM\s+(\S+)\s+(.*)/i,
	considerSwitchingToPM: function(event, element) {
		if (!this.PMMode && element.value.match(this.PMRegex)) {
			this.switchToPM();
		} else if (this.PMMode && !element.value.match(this.PMRegex)) {
			this.switchToShout();
		}
	},
	switchToPM: function() {
		if (this.submitButton) {
			this.PMMode = true;
			this.submitButton.innerHTML = "Send PM";
		}
	},
	switchToShout: function() {
		if (this.submitButton) {
			this.PMMode = false;
			this.submitButton.innerHTML = "Shout";
		}
	},
	limitShoutLength: function(event, element) {
		
		// Finds and removes the nasty &shy; characters that push ruby over it's 140 char limit - note that
		// while ruby counts the &shy; characters towards the length of the string, javascript itself does
		// not. - Andrew
		if (element.value.match(/\u00ad/g)) {
			element.value = element.value.replace(/\u00ad/g, '');
		}
		
		if (element.value.length > this.MAX_LENGTH) {
			element.value = element.value.substr(0,this.MAX_LENGTH);
		}
	},
	
	// Selection and Caret Position Finding
	getSelectionRange: function()
	{
		
		var new_shout = document.getElementById("sidebar_new_shout_text");				
		var caret_position = [0,0];
		
		// IE Support
		if (document.selection)
		{
			new_shout.focus();
			var range = document.selection.createRange();
			var stored_range = range.duplicate();
			stored_range.moveToElementText(new_shout);
			stored_range.setEndPoint( 'EndToEnd', range );
			
			caret_position[0] = stored_range.text.length - range.text.length;
			caret_position[1] = caret_position[0] + range.text.length;
	
			// alert(YAHOO.lang.dump(caret_position));
			
			new_shout.focus();
		}
		else if (new_shout.selectionStart || new_shout.selectionStart == '0')
		{
			caret_position[0] = new_shout.selectionStart;
			caret_position[1] = new_shout.selectionEnd;
		}
		
		return caret_position;
	},
	
	setCaretPosition: function(position)
	{
		var new_shout = document.getElementById("sidebar_new_shout_text");
		
		if(new_shout.setSelectionRange)
		{
			new_shout.focus();
			new_shout.setSelectionRange(position,position);
		}
		else if(new_shout.createTextRange)
		{
			var range = new_shout.createTextRange();
			range.collapse(true);
			range.moveEnd('character', position);
			range.moveStart('character', position);
			range.select();
		}
	}
	
};

Overlord.assign({
	minion: "shouts:sidebar_submit",
	load: function(element) {
		this.submitButton = element;
	},
	click: Shouts.sidebarSubmit,
	scope: Shouts,
	init: 'available'
});

Overlord.assign({
	minion: "shouts:expand",
	focus: Shouts.shoutExpand,
	keypress: Shouts.shoutExpand,
	scope: Shouts,
	init: 'available'
});

Overlord.assign({
	minion: "shouts:collapse",
	click: Shouts.shoutCollapse,
	scope: Shouts,
	init: 'available'
});

Overlord.assign({
	minion: "shouts:reply",
	click: Shouts.shoutReply,
	scope: Shouts
});

Overlord.assign({
	minion: "shouts:reply_focus",
	load: Shouts.replyFocus,
	scope: Shouts
});

Overlord.assign({
	minion: "shouts:delete_shout",
	click: Shouts.deleteShout,
	scope: Shouts
});

Overlord.assign({
	minion: "shouts:sidebar:submit_on_enter",
	keypress: Shouts.sidebarSubmitOnEnter,
	scope: Shouts,
	init: 'available'
});

Overlord.assign({
	minion: "shouts:maxlength",
	keyup: Shouts.limitShoutLength,
	scope: Shouts,
	init: 'available'
});


Overlord.assign({
	minion: "shouts:switch_to_pm",
	keyup: Shouts.considerSwitchingToPM,
	scope: Shouts
});


Overlord.assign({
	minion: "shouts:submit_on_enter",
	keypress: Shouts.submitOnEnter,
	scope: Shouts
});

Overlord.assign({
	minion: "shouts:write_shout",
	click: Shouts.writeShout,
	scope: Shouts
});


ShoutsEdit = 
{
	toggleSubmitSchool: function(event, element)
	{
		if(event) YAHOO.util.Event.preventDefault(event);
		
		var schoolNameInput = document.getElementById('school_name');
		var hideSchoolInput = document.getElementById('hide_school');
		
		var chooseSchoolButton = document.getElementById('choose_school-button');
		var chooseSchoolButtonParent = YAHOO.util.Dom.getAncestorByClassName(chooseSchoolButton, 'custom_button');
		
		var submitSchoolDiv = document.getElementById("submit_school_div"); 
		var submitIsOff = YAHOO.util.Dom.hasClass(submitSchoolDiv, 'hidden');
		if(submitIsOff)
		{
			if(chooseSchoolButton)
			{
				chooseSchoolButton.disabled = true;
				YAHOO.util.Dom.addClass(chooseSchoolButtonParent, 'yui-button-disabled');
				YAHOO.util.Dom.addClass(chooseSchoolButtonParent, 'yui-push-button-disabled');
			}
			
			schoolNameInput.disabled = true;
			hideSchoolInput.disabled = true;
			
			YAHOO.util.Dom.removeClass(submitSchoolDiv, 'hidden');
			if(element) YAHOO.util.Dom.addClass(element.parentNode, 'hidden');
		}
		else
		{
			if(chooseSchoolButton)
			{
				chooseSchoolButton.disabled = false;
				YAHOO.util.Dom.removeClass(chooseSchoolButtonParent, 'yui-button-disabled');
				YAHOO.util.Dom.removeClass(chooseSchoolButtonParent, 'yui-push-button-disabled');
			}
			
			schoolNameInput.disabled = false;
			hideSchoolInput.disabled = false;
			
			YAHOO.util.Dom.addClass(submitSchoolDiv, 'hidden');
		}
	},
	submitSchool: function(event, element)
	{
		YAHOO.util.Event.preventDefault(event);
		
		var form = YAHOO.util.Dom.getAncestorByTagName(element, 'form');
		YAHOO.util.Connect.setForm(form);
		YAHOO.util.Connect.asyncRequest('POST', '/my/shouts/edit/school/submit', new ResponseHandler(
		{
			success: function(o) {
				YAHOO.util.Dom.removeClass('confirm_school_submission', 'hidden');
				ShoutsEdit.toggleSubmitSchool();
			},
			scope: this
		}), "ajax=true");
	}
};

Overlord.assign({
	minion: "shouts_edit:toggle_submit_school",
	click: ShoutsEdit.toggleSubmitSchool,
	scope: ShoutsEdit
});

Overlord.assign({
	minion: "shouts_edit:submit_school",
	click: ShoutsEdit.submitSchool,
	scope: ShoutsEdit
});
EnhancedTextInputModule={};

Nexopia.enhanced_text_editor_list = [];

Overlord.assign({
	minion: "basic_text_input",
	load: function(element)
	{
		EnhancedTextInput.attachHelpers(element);
	}
});

Overlord.assign({
	minion: "enhanced_text_input",
		load: function(element)
		{
			var tmp = new EnhancedTextInput(element);
			// initialize_auto_shading(element.parentNode); //removed due to performance problems particularly in IE
		}
});

Overlord.assign({
	minion: "enhanced_text_input_delayed",
	click: function(event, element) {
		if (Nexopia.enhanced_text_editor_list[element.id] == null) {
			var tmp = new EnhancedTextInput(element);
			tmp.text_box.setCaretPosition(0);
		}
		// initialize_auto_shading(element.parentNode); //removed due to performance problems particularly in IE
	}
});

function EnhancedTextInput(element)
{
	if(element.id != null && element.id != "")
	{
		Nexopia.enhanced_text_editor_list[element.id] = this;
	}
	this.initalize(element);
};

EnhancedTextInput.attachHelpers = function(element)
{
	// Selection and Caret Position Finding
	element.getSelectionRange = function()
	{
		var caret_position = [0,0];

		// IE Support
		if (document.selection)
		{
			element.focus();
			var range = document.selection.createRange();
			var stored_range = range.duplicate();
			stored_range.moveToElementText(element);
			stored_range.setEndPoint( 'EndToEnd', range );

			caret_position[0] = stored_range.text.length - range.text.length;
			caret_position[1] = caret_position[0] + range.text.length;

			// alert(YAHOO.lang.dump(caret_position));

			element.focus();
		}
		else if (element.selectionStart || element.selectionStart == '0')
		{
			caret_position[0] = element.selectionStart;
			caret_position[1] = element.selectionEnd;
		}

		return caret_position;
	};

	element.setCaretPosition = function(position)
	{
		if(element.setSelectionRange)
		{
			element.focus();
			element.setSelectionRange(position,position);
		}
		else if(element.createTextRange)
		{
			var range = element.createTextRange();
			range.collapse(true);
			range.moveEnd('character', position);
			range.moveStart('character', position);
			range.select();
		}
	};

	// Insert Markup
	element.insert = function(open, close)
	{
		var current_scroll_position = element.scrollTop;
		element.focus();

		var current = element.value;
		var sel = element.getSelectionRange(element);

 		// alert("insert " + YAHOO.lang.dump(sel));

		var beginning = current.slice(0, sel[0]);
		var middle = current.slice(sel[0], sel[1]);
		var end = current.slice(sel[1]);

		element.value = beginning + open + middle + close + end;
		element.scrollTop = current_scroll_position;

		if(middle.length > 0)
			element.setCaretPosition((beginning + open + middle + close).length);
		else
			element.setCaretPosition((beginning + open).length);
	};

	// Selection saving and restoring
	element.save_selection = function()
	{
		// IE (the third condition keeps IE from jumping to the top of the page when there's no selection)
		if (document.selection && document.selection.createRange && document.selection.type != "None") 	
			element.stored_selection = document.selection.createRange();
		else
			element.stored_selection = element.getSelectionRange();
		// alert("stored selection " + element.stored_selection);
	};

	element.load_selection = function()
	{
		// IE
		if (document.selection && document.selection.createRange && document.selection.type != "None") {
				try
				{
					element.focus();
					element.setCaretPosition(element.stored_selection[0]);
					element.stored_selection.select();
				}
				catch(err)
				{
				//Handle errors here
				}
		} else {
			element.selectionStart = element.stored_selection[0];
			element.selectionEnd = element.stored_selection[1];
		}
	};
};

EnhancedTextInput.prototype =
{
	initalize: function(element)
	{
		EnhancedTextInput.attachHelpers(element);
		
		this.text_box = element;
		this.text_box_width = YAHOO.util.Dom.getRegion(this.text_box).right - YAHOO.util.Dom.getRegion(this.text_box).left;
		if (this.text_box_width == 0){
			this.text_box_width = parseInt(element.style.width, 10);
		}
		
		this.text_box_height = YAHOO.util.Dom.getRegion(this.text_box).bottom - YAHOO.util.Dom.getRegion(this.text_box).top;
		if (this.text_box_height == 0){
			this.text_box_height = parseInt(element.style.height, 10);
		}
		
		this.wrap_text_box();
		this.populate(this.population());
		this.activate_tab(0);		
	},
	
	population: function()
	{
		var that = this;
		
		return [
			{ name: "Format", content: [
				{type: "button", label: "B", pre: "[b]", post: "[/b]" },
				{type: "button", label: "I", pre: "[i]", post: "[/i]" },
				{type: "button", label: "U", pre: "[u]", post: "[/u]" },
				{type: "button", label: "Strike", pre: "[strike]", post: "[/strike]" },
				{type: "button", label: "Super", pre: "[sup]", post: "[/sup]" },
				{type: "button", label: "Sub", pre: "[sub]", post: "[/sub]" },
				{type: "menu", label: "Size", options: [
					{label: "Tiny", pre: "[size=1]", post: "[/size]" },
					{label: "Small", pre: "[size=2]", post: "[/size]" },
					{label: "Normal", pre: "[size=3]", post: "[/size]" },
					{label: "Large", pre: "[size=4]", post: "[/size]" },
					{label: "Huge", pre: "[size=5]", post: "[/size]" },
					{label: "Enormous", pre: "[size=6]", post: "[/size]" },
					{label: "Gargantuan", pre: "[size=7]", post: "[/size]" }
				]},
				{type: "menu", label: "Color", options: [
					{label: "Dark Red", color: "darkred", pre: "[color=darkred]", post: "[/color]" },
					{label: "Red", color: "red", pre: "[color=red]", post: "[/color]" },
					{label: "Orange", color: "orange", pre: "[color=orange]", post: "[/color]" },
					{label: "Brown", color: "brown", pre: "[color=brown]", post: "[/color]" },
					{label: "Yellow", color: "yellow", pre: "[color=yellow]", post: "[/color]" },
					{label: "Green", color: "green", pre: "[color=green]", post: "[/color]" },
					{label: "Olive", color: "olive", pre: "[color=olive]", post: "[/color]" },
					{label: "Cyan", color: "cyan", pre: "[color=cyan]", post: "[/color]" },
					{label: "Blue", color: "blue", pre: "[color=blue]", post: "[/color]" },
					{label: "Dark Blue", color: "darkblue", pre: "[color=darkblue]", post: "[/color]" },
					{label: "Indigo", color: "indigo", pre: "[color=indigo]", post: "[/color]" },
					{label: "Violet", color: "violet", pre: "[color=violet]", post: "[/color]" },
					{label: "White", color: "white", pre: "[color=white]", post: "[/color]" },
					{label: "Black", color: "black", pre: "[color=black]", post: "[/color]" }
				]},
				{type: "menu", label: "Font", options: [
					{label: "Arial", pre: "[font=arial]", post: "[/font]" },
					{label: "Times", pre: "[font=times]", post: "[/font]" },
					{label: "Courier", pre: "[font=courier]", post: "[/font]" },
					{label: "Impact", pre: "[font=impact]", post: "[/font]" },
					{label: "Geneva", pre: "[font=geneva]", post: "[/font]" },
					{label: "Optima", pre: "[font=optima]", post: "[/font]" },
					{label: "Verdana", pre: "[font=verdana]", post: "[/font]" }
				]}
			]},
			{ name: "Align", content: [
				{type: "button", label: "Left", pre: "[left]", post: "[/left]" },
				{type: "button", label: "Center", pre: "[center]", post: "[/center]" },
				{type: "button", label: "Right", pre: "[right]", post: "[/right]" },
				{type: "button", label: "Justify", pre: "[justify]", post: "[/justify]" }
			]},
			{ name: "Insert", content: [
				{type: "button", label: "Quote", pre: "[quote]", post: "[/quote]" },
				{type: "button", label: "Image", pre: "[img]", post: "[/img]" },
				{type: "button", label: "Link", pre: "[url]", post: "[/url]" },
				{type: "button", label: "User", pre: "[user]", post: "[/user]" },
				{type: "button", label: "Line", pre: "\n[hr]\n", post: "" },
				{type: "menu", label: "List", options: [
					{label: "Bullet", pre: "[list]\n[*]", post: "\n[/list]\n" },
					{label: "Numbered", pre: "[list=1]\n[*]", post: "\n[/list]\n" },
					{label: "Alphabetic", pre: "[list=a]\n[*]", post: "\n[/list]\n" },
					{label: "Roman", pre: "[list=i]\n[*]", post: "\n[/list]\n" }
				]}
			]},
 			{ name: "Smilies", content: [{type: "smilies", smilies: Site.smilies}], action: function() {
				Nexopia.DelayedImage.loadImages(that.enhanced_text_box_wrapper);
			}},
 			{ name: "Preview", content: [{type: "preview"}], action: function(){that.get_preview();} }
		 ];
	},
	
	populate: function(population)
	{
		for(var i = 0; i < population.length; i++)
		{
			// Create Handle
			var handle = document.createElement('div');
			YAHOO.util.Dom.addClass(handle, "tab_handle");
			handle.innerHTML = population[i].name;
			this.tab_handles.appendChild(handle);
			YAHOO.util.Event.on(handle, 'mousedown', function(e, args){
				args[0].save_selection();
			}, [this.text_box, i]);
			
			YAHOO.util.Event.on(handle, 'click', function(e, args){
				args[0].activate_tab(args[1]);
				if(population[args[1]].action) {
					population[args[1]].action();
				}
			}, [this, i]);
			
			YAHOO.util.Event.on(handle, 'mouseup', function(e, args){
				args[0].load_selection();
			}, [this.text_box, i]);
			
			// Create Body
			var body_div = document.createElement('div');
			YAHOO.util.Dom.addClass(body_div, "tab_body");
			if (population[i].name != "Preview")
			{
				YAHOO.util.Dom.addClass(body_div, "sub_menu");
			}	
			for(var j = 0; j < population[i].content.length; j++)
			{
				var config = population[i].content[j];
				var el = this["build_"+config.type](config, body_div);
				if (el) {
					body_div.appendChild(el);
				}
			}
			YAHOO.util.Dom.setStyle(body_div, 'display', 'none');
			this.tab_bodies.appendChild(body_div);
		}
	},
	
	activate_tab: function(which)
	{
		var handles = YAHOO.util.Dom.getChildren(this.tab_handles);
		for(var i = 0; i < handles.length; i++)
		{
			YAHOO.util.Dom.setStyle(handles[i], 'font-weight', 'normal');
		}
		var bodies = YAHOO.util.Dom.getChildren(this.tab_bodies);
		for(var i = 0; i < bodies.length; i++)
		{
			YAHOO.util.Dom.setStyle(bodies[i], 'display', 'none');
		}
		
		YAHOO.util.Dom.setStyle(YAHOO.util.Dom.getChildren(this.tab_handles)[which], 'font-weight', 'bold');
		YAHOO.util.Dom.setStyle(YAHOO.util.Dom.getChildren(this.tab_bodies)[which], 'display', 'block');
		// if (this.stored_selection) {
		// 	this.setCaretPosition(this.stored_selection)
		// }
	},
	
	wrap_text_box: function()
	{
		this.enhanced_text_box_wrapper = document.createElement('div');
		YAHOO.util.Dom.setStyle(this.enhanced_text_box_wrapper, 'width', this.text_box_width + 'px');
		YAHOO.util.Dom.addClass(this.enhanced_text_box_wrapper, "enhanced_text_box_wrapper");
		
		this.tab_bar = document.createElement('div');
		YAHOO.util.Dom.addClass(this.tab_bar, "tab_bar");
		this.enhanced_text_box_wrapper.appendChild(this.tab_bar);
		
		this.tab_handles = document.createElement('div');
		YAHOO.util.Dom.addClass(this.tab_handles, "tab_handles");
		this.tab_bar.appendChild(this.tab_handles);
		
		this.tab_bodies = document.createElement('div');
		YAHOO.util.Dom.addClass(this.tab_bodies, "tab_bodies");
		this.tab_bar.appendChild(this.tab_bodies);
		
		this.text_box.parentNode.replaceChild(this.enhanced_text_box_wrapper, this.text_box);
		this.enhanced_text_box_wrapper.appendChild(this.text_box);
	},
	
	build_button: function(config, parent)
	{
		var button = document.createElement('input');
		button.type = "button";
		button.value = config.label;
		parent.appendChild(button);
		
		new YAHOO.widget.Button(button, {
			onclick: {
				fn: function(e, args) {
					this.text_box.insert(config.pre, config.post);
				},
				scope: this
			}
		});
		
		return false;
	},

	build_preview: function(config, parent)
	{
		this.preview_tab_bar = parent;
	},

	get_preview: function()
	{
		var saved_color = YAHOO.util.Dom.getStyle(this.preview_tab_bar, 'background-color');
		
		YAHOO.util.Dom.setStyle(this.preview_tab_bar, 'position', 'absolute');
		YAHOO.util.Dom.setStyle(this.preview_tab_bar, 'height', this.text_box_height-1-4+'px');
		YAHOO.util.Dom.setStyle(this.preview_tab_bar, 'width', this.text_box_width-2-12+'px');
		YAHOO.util.Dom.setStyle(this.preview_tab_bar, 'overflow', 'auto');
		YAHOO.util.Dom.setStyle(this.preview_tab_bar, 'padding', '6px');
		YAHOO.util.Dom.setStyle(this.preview_tab_bar, 'padding-top', '2px');
		YAHOO.util.Dom.setStyle(this.preview_tab_bar, 'padding-bottom', '2px');
		
		YAHOO.util.Dom.setStyle(this.preview_tab_bar, 'background-color', saved_color);
		
		this.text_box.blur();
		
		this.preview_tab_bar.innerHTML = "Loading Preview...";
		
		YAHOO.util.Connect.asyncRequest('POST', Site.wwwURL + '/enhanced_text_input/preview:Body', new ResponseHandler(
		{
			success: function(o)
			{
				this.preview_tab_bar.innerHTML = o.responseText;
				Nexopia.DelayedImage.loadImages(this.preview_tab_bar);
			},
			failure: function(o)
			{
				this.preview_tab_bar.innerHTML = 'Error Generating Preview.';
			},
			scope: this
		}), 'source_text='+encodeURIComponent(this.text_box.value));
	},

	build_smilies: function(config)
	{
		
		this.smilies = document.createElement("div");

		this.left_arrow = document.createElement("img");
		this.left_arrow.className = 'color_icon';
		this.left_arrow.src = Site.coloredImgURL(this.deduceArrowColor()) + "/core/images/arrow_left.gif";

		this.right_arrow = document.createElement("img");
		this.right_arrow.className = 'color_icon';
		this.right_arrow.src = Site.coloredImgURL(this.deduceArrowColor()) + "/core/images/arrow_right.gif";

		var internal_wrapper = document.createElement("div");
		var wrapper = document.createElement("div");

		YAHOO.util.Dom.setStyle(wrapper, "width", (parseInt(this.text_box_width, 10) - 22) + "px");
		
		var break_clear = document.createElement("br");
		YAHOO.util.Dom.addClass(break_clear, 'clear');
		
		this.smilies.appendChild(this.left_arrow);
		wrapper.appendChild(internal_wrapper);
		this.smilies.appendChild(wrapper);
		this.smilies.appendChild(this.right_arrow);
		this.smilies.appendChild(break_clear);

		YAHOO.util.Event.on(this.left_arrow, 'click', function(event) {
			var anim = new YAHOO.util.Scroll(wrapper, { scroll: { by: [-parseInt(wrapper.style.width, 10)+50, 0] } }, 0.5, YAHOO.util.Easing.easeOutStrong);
			anim.animate();
		});
		YAHOO.util.Event.on(this.right_arrow, 'click', function(event) {
			var anim = new YAHOO.util.Scroll(wrapper, { scroll: { by: [parseInt(wrapper.style.width, 10)-50, 0] } }, 0.5, YAHOO.util.Easing.easeOutStrong);
			anim.animate();
		});
		
		YAHOO.util.Dom.addClass(this.smilies, 'smilies');
		for (var symbol in config.smilies) {
			if(symbol != ":P")
			{
				var emoticon = this.build_emoticon({symbol: symbol, name: config.smilies[symbol]});
				internal_wrapper.appendChild(emoticon);
			}
		}

		return this.smilies;
	},
	
	build_emoticon: function(config)
	{
		var emoticon = document.createElement("img");
		Nexopia.DelayedImage.setDelayedSrc(emoticon, Site.staticFilesURL + "/Legacy/smilies/" + config.name + ".gif");
		emoticon.alt = config.symbol;
		YAHOO.util.Event.on(emoticon, 'click', function(e)
		{
			this.text_box.insert(" " + config.symbol + " ", "");
		}, this, true);
		return emoticon;
	},
	
	build_menu: function(config, parent)
	{
		var button = document.createElement("input");
		button.type = "button";
		button.value = config.label;

		var menu = document.createElement("ul");
		for (var i=0; i<config.options.length; i++) {
			var option = this.build_option(config.options[i]);
			menu.appendChild(option);
		}
		
		parent.appendChild(button);

		yuiMenu = new YAHOO.widget.Button(button, {
			type: "menu",
			menu: menu,
			lazyloadmenu: false
		});
		
		yuiMenu.getMenu().cfg.setProperty("iframe", true);
		yuiMenu.getMenu().cfg.setProperty("zindex", "500");

		return false;
	},
	
	build_option: function(config)
	{
		var option = document.createElement("li");
		if (config.color)
		{
			YAHOO.util.Dom.addClass(option, "no_shading_color");
			option.style.color = config.color;
		}
		option.innerHTML = config.label;
		YAHOO.util.Event.on(option, 'mousedown', function(e, args)
		{
			args[0].insert(args[1], args[2]);
		}, [this.text_box, config.pre, config.post]);
		return option;
	},
	deduceArrowColor: function()
	{
		var iconColor = '000000';
		
		var profileDiv = document.getElementById('profile');
		if (profileDiv)
		{
			var primaryDiv = document.createElement('div');
			primaryDiv.className = 'primary_block';
			profileDiv.appendChild(primaryDiv);
			iconColor = Nexopia.Utilities.deduceImgColor(primaryDiv);
			profileDiv.removeChild(primaryDiv);
		}
		return iconColor;
	}
};

Overlord.assign({
	minion: "placeholder",
	load: function(element) {
		if (element.value == "" && !YAHOO.env.ua.webkit) { //if it's webkit do nothing because webkit deals with placeholder on its own
			var placeholder = element.getAttribute('placeholder');
			var firstTime = true;
			if (element.type != 'password') { //if it's not a password field then just fill it and clear it on focus
				element.value = placeholder;
				YAHOO.util.Dom.addClass(element, 'placeholder');
				
				YAHOO.util.Event.on(element, 'focus', function() {
					if (element.value == placeholder && firstTime) {
						element.value = "";
						firstTime = false;
						YAHOO.util.Dom.removeClass(element, 'placeholder');
					}
				});
			} else {
				var replacementEl = document.createElement('input');
				replacementEl.value = placeholder;
				replacementEl.type = 'text';
				replacementEl.className = element.className;
				replacementEl.id = element.id;
				element.style.display = 'none';
				element.parentNode.insertBefore(replacementEl, element);
				YAHOO.util.Event.on(replacementEl, 'focus', function() {
					if (firstTime) {
						firstTime = false;
						element.style.display = 'block';
						element.focus();
						replacementEl.parentNode.removeChild(replacementEl);
					}
				});
			}
		}
	}
});
NexoskelModule={};

Banner = {
	BANNER: 1,
	LEADERBOARD: 2,
	BIGBOX: 3,
	SKY120: 4,
	SKY160: 5,
	BUTTON60: 6,
	VULCAN: 7,
	LINK: 8,
	
	refreshBanner: function(element,bannerType)
	{
		element.src = "/bannerview.php?size="+bannerType+"&pageid=" + element.getAttribute("page_id");
	}
};

Overlord.assign({
	minion: "banner:leaderboard",
	load: function(element) {
		Banner.refreshBanner(element, Banner.LEADERBOARD);
	}
});

Overlord.assign({
	minion: "banner:sky160",
	load: function(element) {
		Banner.refreshBanner(element, Banner.SKY160);
	}
});

Overlord.assign({
	minion: "banner:bigbox",
	load: function(element) {
		Banner.refreshBanner(element, Banner.BIGBOX);
	}
});
function DateSelector(callingObject)
{
	this.removedDayOptions = new Array();
	this.callingObject = callingObject;
	
	this.daySelector = YAHOO.util.Dom.getElementsByClassName("day", "select", this.callingObject)[0];
	this.monthSelector = YAHOO.util.Dom.getElementsByClassName("month", "select", this.callingObject)[0];
	this.yearSelector = YAHOO.util.Dom.getElementsByClassName("year", "select", this.callingObject)[0];
	
	YAHOO.util.Event.on(this.monthSelector, "change", this.updateDays, this, true);
	YAHOO.util.Event.on(this.yearSelector, "change", this.updateDays, this, true);
}


DateSelector.prototype =
{
	updateDays: function()
	{
		if (this.daySelector != null)
		{
			var dayIndex = this.daySelector.selectedIndex;
		}
		var monthIndex = this.monthSelector.selectedIndex;
		var yearIndex = this.yearSelector.selectedIndex;
	
		if (this.daySelector != null)
		{
			var dayOptions = this.daySelector.options;
		}
		var monthOptions = this.monthSelector.options;
		var yearOptions = this.yearSelector.options;
	
		if (this.daySelector != null)
		{
			var day = dayOptions[dayIndex].value;
		}
		var month = monthOptions[monthIndex].value;
		var year = yearOptions[yearIndex].value;
	
		if (this.daySelector != null)
		{	
			var maxDays = 31;
			//	January, March, May, July, August, October, and December have 31 days
			if (this.arrayHas([1,3,5,7,8,10,12], month))
			{
				maxDays = 31;
			}
			// April, June, September, and November have 30 days
			else if (this.arrayHas([4,6,9,11], month))
			{
				maxDays = 30;
			}
			// February has 29 days on a leap year and 28 otherwise
			else if (this.arrayHas([2], month))
			{
				if (year % 4 == 0 || year == -1)
				{
					maxDays = 29;
				}
				else
				{
					maxDays = 28;
				}
			}

			// Restore any removed days
			while(this.removedDayOptions.length > 0) 
			{
				dayOptions[dayOptions.length] = this.removedDayOptions.pop(); 
			}
	
			// Remove days that fall after the maximum number of days for the month.
			// The reason for the -1 in the while clause is that one of the dayOptions
			// option items is used to contain the "Day" label.
			while(dayOptions.length - 1 > maxDays) 
			{
				this.removedDayOptions.push(dayOptions[dayOptions.length - 1]);
				dayOptions[dayOptions.length - 1] = null;
			}
	
			if (day > maxDays)
			{
				this.daySelector.selectedIndex = maxDays;
			}
		}
	},


	arrayHas: function(arrayObject, object)
	{
		for(var i=0; i < arrayObject.length; i++)
		{
			if (arrayObject[i] == object)
			{
				return true;
			}
		}
	
		return false;
	}
}


DateSelectorInitializer = {
	init: function()
	{
		elements = YAHOO.util.Dom.getElementsByClassName("date_selector", "div");
		for (var i = 0; i < elements.length; i++)
		{
			new DateSelector(elements[i]);		
		}
	}
}


GlobalRegistry.register_handler("date_selector", DateSelectorInitializer.init, DateSelectorInitializer, true);
Overlord.assign({
	minion: "admin_menu",
	change: function(event, element) {
		if (YAHOO.util.Dom.get('admin_menu_new_window').checked) {
			window.open(element.value);
		} else {
			document.location = element.value;
		}
	}
});

Overlord.assign({
	minion: "debug_info:toggle_view",
	click: function(event, element) {
		YAHOO.util.Event.preventDefault(event);
		
		var display = YAHOO.util.Dom.getStyle('debug_info_container', 'display');
		if(display == 'block')
		{
			display = 'none';
		}
		else
		{
			display = 'block';
		}
		YAHOO.util.Dom.setStyle('debug_info_container', 'display', display);
	}
});
Header = {
	resetUsername: function(event, el) {
		if (el.value == "Username") {
			el.value = "";
		}
	}
};

Overlord.assign({
	minion: "header:username",
	focus: Header.resetUsername,
	keypress: Header.resetUsername,
	scope: Header
});

Overlord.assign({
	minion: "header:site_banner",
	click: function() {
		document.location = Site.wwwURL;
	}
});
Menus = {
	//menus are set in initScrollEvent
	menu: null,
	menuShim: null,
	bannerHeight: 90,
	fixed: false,

	updatePosition: function() {
		var top = YAHOO.util.Dom.getDocumentScrollTop();
		if (!this.fixed && top > this.bannerHeight) {
			this.fixed = true;
			YAHOO.util.Dom.addClass(this.menu, 'fixed');
			YAHOO.util.Dom.addClass(this.menuShim, 'fixed');
		} else if (this.fixed && top <= this.bannerHeight) {
			this.fixed = false;
			YAHOO.util.Dom.removeClass(this.menu, 'fixed');
			YAHOO.util.Dom.removeClass(this.menuShim, 'fixed');
		}
	},

	initScrollEvent: function() {
		YAHOO.util.Event.on(window, 'scroll', this.updatePosition, this, true);
		this.menu = document.getElementById('menus');
		this.menuShim = document.getElementById('top_menu_shim');
		this.bannerHeight = document.getElementById('site_header').offsetHeight - this.menuShim.offsetHeight;
		this.updatePosition();
	}
};

Sidebar = {
	
	sublinkToggle: function(event, element){
		YAHOO.util.Event.preventDefault(event);
		
		if (element.innerHTML == "[-]") {
			message_links = YAHOO.util.Dom.getElementsByClassName( "sublink_list", "div", element.parentNode );
			YAHOO.util.Dom.setStyle(message_links, "display", "none");
			element.innerHTML = "[+]";
			
		} else {
			message_links = YAHOO.util.Dom.getElementsByClassName( "sublink_list", "div", element.parentNode );
			YAHOO.util.Dom.setStyle(message_links, "display", "block");
			element.innerHTML = "[-]";
		}
		
	},
	
	browseToggle: function(event, element){
		YAHOO.util.Event.preventDefault(event);
		
		if (element.innerHTML == "[-]") {
			quick_searches = YAHOO.util.Dom.getElementsByClassName( "quick_searches", "table", element.parentNode );
			search_options = YAHOO.util.Dom.getElementsByClassName( "search_options_container", "div", element.parentNode );
			YAHOO.util.Dom.setStyle(quick_searches, "display", "none");
			YAHOO.util.Dom.setStyle(search_options, "display", "none");
			element.innerHTML = "[+]";
			
		} else {
			quick_searches = YAHOO.util.Dom.getElementsByClassName( "quick_searches", "table", element.parentNode );
			search_options = YAHOO.util.Dom.getElementsByClassName( "search_options_container", "div", element.parentNode );
			YAHOO.util.Dom.setStyle(quick_searches, "display", "block");
			YAHOO.util.Dom.setStyle(search_options, "display", "block");
			element.innerHTML = "[-]";
		}
		
	},
	
	passwordBoxSwap: function(event, element){		
		password_box = document.getElementById("join_password");
		YAHOO.util.Dom.setStyle(element, "display", "none");
		YAHOO.util.Dom.setStyle(password_box, "display", "block");
		password_box.focus();
	},
	
	setupSearch: function(element)
	{		
		var autoComplete = new YAHOO.widget.AutoComplete("sidebar_search", "sidebar_friends_results", new YAHOO.widget.DS_JSArray(Nexopia.json(element), {
			queryMatchContains: true
		}), {
			animHoriz: false,
			animVert: false
		});
		autoComplete.itemSelectEvent.subscribe(function(event, args) {
			// 13 is the key code for the enter key.
			// We only want to continue to the user's profile page upon pressing enter
			if(args[0]._nKeyCode == 13)
			{
				var username = args[2][0];
				window.location = "/users/" + encodeURIComponent(username);
			}
		});
		
	},
	
	focusSearchField: function(event, element)
	{
		if(element.value == 'Name, Username, or Email')
		{
			YAHOO.util.Dom.removeClass(element, 'default');
			element.value = '';
		}
	},
	
	blurSearchField: function(event, element)
	{
		if(element.value == '')
		{
			YAHOO.util.Dom.addClass(element, 'default');
			element.value = 'Name, Username, or Email';
		}
	},
	
	clearAllCommentNotices: function(event, element)
	{
		YAHOO.util.Event.preventDefault(event);
		YAHOO.util.Connect.asyncRequest("POST", element.href, new ResponseHandler({}), 'ajax=true');
	}
};

Overlord.assign({
	minion: "sidebar:sublink:toggle",
	click: Sidebar.sublinkToggle,
	scope: Sidebar,
	order: 2
});

Overlord.assign({
	minion: "sidebar:browse:toggle",
	click: Sidebar.browseToggle,
	scope: Sidebar,
	order: 2
});

Overlord.assign({
	minion: "sidebar:password_box_swap",
	focus: Sidebar.passwordBoxSwap,
	scope: Sidebar
});

Overlord.assign({
	minion: "sidebar:search",
	load: Sidebar.setupSearch,
	focus: Sidebar.focusSearchField,
	blur: Sidebar.blurSearchField,
	scope: Sidebar
});

Overlord.assign({
	minion: "sidebar:comments:clear_comment_notices",
	click: Sidebar.clearAllCommentNotices,
	scope: Sidebar
});

Overlord.assign({
	minion: "sidebar:comments:truncated_notification",
	load: function(element) {
		new Truncator(element, {
			width:174,
			height:14,
			tooltip: true,
			by_word: false
		});
	}
});
AdManagerModule={};

GoogleBanner = {
	PUBLISHER_ID:  "ca-pub-5130698294741465",
	DIMENSIONS: {
		Nexopia_BigBox_Homepage_300x250: [300,250],
		Nexopia_BigBox_Messages_300x250: [300,250],
		Nexopia_BigBox_Messages2_300x250: [300,250],
		Nexopia_BigBox_Public_Pages_300x250: [300,250],
		Nexopia_BigBox_PublicPages2_300x250: [300,250],
		Nexopia_BigBox_ROS_300x250: [300,250],
		Nexopia_Bigbox_Pollresponse_300x250: [300,250],
		Nexopia_BigBox_Pollresponse2_300x250: [300,250],
		Nexopia_BigBox_Forumreply_300x250: [300,250],
		Nexopia_BigBox_Forumreply2_300x250: [300,250],
		Nexopia_BigBox_Rogersforum_300x250: [300,250],
		Nexopia_Leaderboard_Forums_728x90: [728,90],
		Nexopia_Leaderboard_Forumreply_728x90: [728,90],
		Nexopia_Leaderboard_Homepage_728x90: [728,90],
		Nexopia_Leaderboard_Messages_728x90: [728,90],
		Nexopia_Leaderboard_Plus_728x90: [728,90],
		Nexopia_Leaderboard_Public_Pages_728x90: [728,90],
		Nexopia_Leaderboard_Pollresponse_728x90: [728,90],
		Nexopia_Leaderboard_Rogersforum_728x90: [728,90],
		Nexopia_Leaderboard_Recent_Visitors_728x90: [728,90],
		Nexopia_Leaderboard_ROS_728x90: [728,90],
		Nexopia_Skyscraper_Forumreply_160x600: [160,600],
		Nexopia_Skyscraper_Forums_160x600: [160,600],
		Nexopia_Skyscraper_Homepage_160x600: [160,600],
		Nexopia_Skyscraper_Messages_160x600: [160,600],
		Nexopia_Skyscraper_Plus_160x600: [160,600],
		Nexopia_Skyscraper_Public_Pages_160x600: [160,600],
		Nexopia_Skyscraper_Recent_Visitors_160x600: [160,600],
		Nexopia_Skyscraper_ROS_160x600: [160,600],
		Nexopia_Skyscraper_Pollresponse_160x600: [160,600],
		Nexopia_Skyscraper_Rogersforum_160x600: [160,600]
	},
	SIZES: {
		skyscraper: [160,600],
		bigbox: [300,250],
		leaderboard: [728,90]
	},
	serve: function(tag) {
		if (GoogleBanner.DIMENSIONS[tag]) {
			GA_googleFillSlotWithSize(GoogleBanner.PUBLISHER_ID, tag, GoogleBanner.DIMENSIONS[tag][0], GoogleBanner.DIMENSIONS[tag][1]);
		} else {
			YAHOO.log("Attempted to server banner for tag " + tag + ", which does not exist.", 'GoogleAdManager');
		}
	}
};

YAHOO.util.Event.onDOMReady(function() {
	GoogleBanner.serve = function(tag) {
		YAHOO.log("Attempted to serve a banner for tag " + tag + " after DOM initialization.", 'GoogleAdManager');
	};
});
RapModule={};


