CoreModule={"styleURL":"http:\/\/static.nexopia.com\/31400\/style","userURL":"http:\/\/www.nexopia.com\/users","scriptURL":"http:\/\/static.nexopia.com\/31400\/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"},"imageURL":"http:\/\/images.nexopia.com","wwwURL":"http:\/\/www.nexopia.com","staticFilesURL":"http:\/\/static.nexopia.com\/31400\/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"};
			CoreModule.coloredImgURL = function(color)
			{
				if (color.match('rgb'))
				{
					color = Nexopia.Utilities.getHexValue(color);
				}
				
				var rubyURL = "http://images.nexopia.com/recolour/__color__/31400";
				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');
							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"));
			
			// 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: "profile_edit",
	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;
		}
	}
});
/*
	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]);
		}
		else if(match = color.match(rgba))
		{
			match.shift();
			match.pop();
			
			for(j = 0; j < match.length; j++)
				match[j] = parseInt(match[j]);
			
			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) {
			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;
};
//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);
	}
};

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

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
});
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);});
		}
	},
	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();
		}
	}
};
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";
	},

	CheckNoBannedWords: function(text)
	{
		this.text = text;
		this.className = "CheckNoBannedWords";
	},

	// 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.CheckNoBannedWords.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).
				
	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".
*/
// TODO: use configuration objects instead of parameters to abstract out the minion init code
NexopiaPanel = function(cfg) {
	this.cfg = cfg;
};

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() {
		this.initOverlay();
		this.overlay.setBody(this.spinnerBody);
		this.overlay.render(document.body);
		this.overlay.center();
	},
	open: function(event) {
		if (event) {
			YAHOO.util.Event.preventDefault(event);
			var element = YAHOO.util.Event.getTarget(event);
			//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();
			}
		}
	},
	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;
	},
	postAndClose: function(event, element) {
		if (event) {
			YAHOO.util.Event.preventDefault(event);
		}
		
		this.disableButtons();
		
		var that = this;
		var innerForm = YAHOO.util.Dom.getElementsBy(function(el){return true;},'form',this.overlay.innerElement)[0];
		
		// 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) {
					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();
				},
				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');
			}
		}
	},	
	initializeButtons: function()
	{
		var closeButtons = YAHOO.util.Dom.getElementsByClassName('nexopia_panel_close', null, this.overlay.innerElement);
		for(var i = 0; i < closeButtons.length; i++)
		{
			YAHOO.util.Event.on(closeButtons[i], 'click', this.close, this, true);
		}
		
		var postAndCloseButtons = YAHOO.util.Dom.getElementsByClassName('nexopia_panel_save_and_close', null, this.overlay.innerElement);
		for(var i = 0; i < postAndCloseButtons.length; i++)
		{
			YAHOO.util.Event.on(postAndCloseButtons[i], 'click', this.postAndClose, this, true);
		}
		
		var redirectButtons = YAHOO.util.Dom.getElementsByClassName('nexopia_panel_redirect', null, this.overlay.innerElement);
		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();		
	}
};

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
	};
};

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

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

Overlord.assign({
	minion: "nexopia_panel:close",
	click: function(event, element) {
		YAHOO.util.Event.preventDefault(event);
		NexopiaPanel.current.close();
	}
});
//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);
	}
});
//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);
	}
});
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 {
						this.nodeQueue.appendChild(node);
						return true;
					}
				},
				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);
		
		// If we can't find the shouts box then we're on the Shoutfall page
		// and we need to redirect to the user's shouts page to send the reply.
		reply_to = Nexopia.json(element);
		
		var sidebar_shout_box = document.getElementById("sidebar_new_shout_text");
		
		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);
			}
		}
	},
	
	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",
	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: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: "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.focus();
		}
		// 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.prototype =
{
	initalize: function(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, 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, 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.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.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, config.pre, config.post]);
		return option;
	},
	
	// Selection and Caret Position Finding
	getSelectionRange: function()
	{
		var caret_position = [0,0];
		
		// IE Support
		if (document.selection)
		{
			this.text_box.focus();
			var range = document.selection.createRange();
			var stored_range = range.duplicate();
			stored_range.moveToElementText(this.text_box);
			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));
			
			this.text_box.focus();
		}
		else if (this.text_box.selectionStart || this.text_box.selectionStart == '0')
		{
			caret_position[0] = this.text_box.selectionStart;
			caret_position[1] = this.text_box.selectionEnd;
		}
		
		return caret_position;
	},
	
	setCaretPosition: function(position)
	{
		if(this.text_box.setSelectionRange)
		{
			this.text_box.focus();
			this.text_box.setSelectionRange(position,position);
		}
		else if(this.text_box.createTextRange)
		{
			var range = this.text_box.createTextRange();
			range.collapse(true);
			range.moveEnd('character', position);
			range.moveStart('character', position);
			range.select();
		}
	},
	
	// Insert Markup
	insert: function(open, close)
	{
		var current_scroll_position = this.text_box.scrollTop;
		this.text_box.focus();
			
		var current = this.text_box.value;
		var sel = this.getSelectionRange(this.text_box);
		
 		// 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]);
		
		this.text_box.value = beginning + open + middle + close + end;
		this.text_box.scrollTop = current_scroll_position;
		
		if(middle.length > 0)
			this.setCaretPosition((beginning + open + middle + close).length);
		else
			this.setCaretPosition((beginning + open).length);
	},
	
	// Selection saving and restoring
	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") 	
			this.stored_selection = document.selection.createRange();
		else
			this.stored_selection = this.getSelectionRange();
		// alert("stored selection " + this.stored_selection);
	},
	
	load_selection: function()
	{
		// IE
		if (document.selection && document.selection.createRange && document.selection.type != "None") {
				try
				{
					this.text_box.focus();
					this.setCaretPosition(this.stored_selection[0]);
					this.stored_selection.select();
				}
				catch(err)
				{
				//Handle errors here
				}
		} else {
			this.text_box.selectionStart = this.stored_selection[0];
			this.text_box.selectionEnd = this.stored_selection[1];
		}
	},
	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.Event.on(element, 'focus', function() {
					if (element.value == placeholder && firstTime) {
						element.value = "";
						firstTime = false;
					}
				});
			} 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.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';
		}
	}
};

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
});
AdManagerModule={};

GoogleBanner = {
	PUBLISHER_ID:  "ca-pub-5130698294741465",
	DIMENSIONS: {
		Nexopia_BigBox_Homepage_300x250: [300,250],
		Nexopia_BigBox_Messages_300x250: [300,250],
		Nexopia_BigBox_Public_Pages_300x250: [300,250],
		Nexopia_BigBox_ROS_300x250: [300,250],
		Nexopia_BigBox_Forumreply_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_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]
	},
	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={};

InterstitialModule={};

/*
	The fun with the interstitial_head is for the head frame when skin_frames is being used.
	It communicates across the frame to blank out the header.
*/
if(YAHOO.interstitial == undefined){
	YAHOO.namespace ("interstitial");
}

YAHOO.interstitial.Interstitial = {
	init: function()
	{
		if (!this.interstitial_display) 
		{
			var width = YAHOO.util.Dom.getViewportWidth();
			var padding = width - 700;
			var offset = padding/2;
		
			if(offset <= 0)
			{
				offset = 10;
			}
		
			this.interstitial_display = new YAHOO.widget.Panel("interstitial_display",  
				{ xy: [offset, 40],
					width: "700px", 
					fixedcenter: false, 
					close: false, 
					draggable: false, 
					zindex:100000,
					modal: true,
					visible: false
				} );
			var el = document.getElementById("interstitial");
			el.parentNode.removeChild(el);
			
			var top_button_bar = document.createElement("div");
			top_button_bar.className = "button_bar";
			top_button_bar.id = "button_bar_top";
			YAHOO.util.Dom.setStyle(top_button_bar, "position", "absolute");
			YAHOO.util.Dom.setStyle(top_button_bar, "top", "6px");
			YAHOO.util.Dom.setStyle(top_button_bar, "right", "6px");
			el.appendChild(top_button_bar);
			
			var bottom_button_bar = document.createElement("div");
			bottom_button_bar.className = "button_bar";
			bottom_button_bar.id = "button_bar_bottom";
			YAHOO.util.Dom.setStyle(bottom_button_bar, "position", "absolute");
			YAHOO.util.Dom.setStyle(bottom_button_bar, "bottom", "6px");
			YAHOO.util.Dom.setStyle(bottom_button_bar, "right", "6px");
			el.appendChild(bottom_button_bar);
			
			// Done Buttons
			this.done_button_top = new YAHOO.widget.Button({
				id: "done_buton_top",
				type: "button",
				label: "Close",
				container: "button_bar_top"
			});
			
			this.done_button_top.on("click", this.hide_interstitial);
			
			YAHOO.util.Dom.setStyle(el, "display", "block");
			this.interstitial_display.setBody(el.innerHTML);
			var links = el.getElementsByTagName('a');
			for (var i=0;i<links.length;i++) {
				YAHOO.util.Event.on(links[i], 'click', this.hide_interstitial, this, true);
			}
			
			this.interstitial_display.render(document.body);
			
			YAHOO.util.Dom.setStyle("interstitial_display", "border", "none");
		}
		// Show the Panel
		this.interstitial_display.show();
		if(parent && parent.head && parent.head.YAHOO && parent.head.YAHOO.interstitial && parent.head.YAHOO.interstitial.InterstitialHead.interstitial_head_display)
		{
			parent.head.YAHOO.interstitial.InterstitialHead.interstitial_head_display.show();
		}
  },

	hide_interstitial: function()
	{
		if(YAHOO.interstitial.Interstitial.interstitial_display)
		{
			YAHOO.interstitial.Interstitial.interstitial_display.hide();
			if(parent && parent.head && parent.head.YAHOO && parent.head.YAHOO.interstitial)
			{
				parent.head.YAHOO.interstitial.InterstitialHead.interstitial_head_display.hide();
			}
		}
	}
};

YAHOO.interstitial.InterstitialHead = {
	init: function(el)
	{
		var width = YAHOO.util.Dom.getViewportWidth();
		var padding = width - 700;
		var offset = padding/2;
		
		if(offset <= 0)
		{
			offset = 10;
		}
		
		this.interstitial_head_display = new YAHOO.widget.Panel("interstitial_display_head",  
				{ xy: [offset, 40],
					width: "700px", 
					fixedcenter: false, 
					close: false, 
					draggable: false, 
					zindex: 100000,
					modal: true,
					visible: false
				} );
				
		this.interstitial_head_display.setBody("");
		this.interstitial_head_display.render(parent.head.document.body);
	}
};

Overlord.assign({
	minion: "interstitial",
	load: function(element) {
		YAHOO.interstitial.Interstitial.init();
	},
	order: 10	
});

Overlord.assign({
	minion: "interstitial_head",
	load: function(element) {
		YAHOO.interstitial.Interstitial.init();
	},
	order: 10
});

