function RunSearch(searchname, type)
{
	// Prevent messing with this
	if((type != "portal") && (type != "audio"))
	{
		return;
	}

	// First make sure the search terms aren't blank
	var terms_element = document.getElementById(searchname + "_terms");
	var terms = terms_element.value;
	if(terms.value == "")
	{
		alert("Please enter your search terms.");
		terms_element.focus();
		return;
	}

	// Let's also manually escape "/" if it's in the terms
	terms = terms.replace(/\//, "%1b");		// %1b = ESC in ASCII

	// Ready to go - run the search
	window.location.href = "http://redirect.ngfiles.com/" + type + "/search/" + SelectValue(document.getElementById(searchname + "_type")) + "/" + terms;
}

function NewWindow(url, height, width, myname)
{
	if(typeof myname == "undefined")
	{
		myname = "newwin" + GetRandomNumber(1000000, 9999999);
	}

	var winprops = 'height=' + height + ',width=' + width + ',top=' + ((screen.height - height) / 2) + ',left=' + ((screen.width - width) / 2) + ',status=yes,scrollbars=yes,resizable=yes,toolbar=no,location=no,menubar=no';

	win = window.open(url, myname, winprops);
	win.window.focus();
}


// Check whether a given number is an integer.
function IsValidInteger(num)
{
	return((num.length != 0) && (!(/[^0-9]+/.exec(num))));
}

// Takes a regular expression or regular string.
function CountOccurences(val, expr)
{
	var parts = val.split(expr);
	return(parts.length-1);
}

// Let's get rid of whitespace.
function Trim(ourtext)
{
	var our_pattern = /(^\s*|\s*$)/g;
	ourtext = ourtext.replace(our_pattern, '');
	return(ourtext);
}

function Round(num, precision)
{
	if(typeof precision == "undefined")
	{
		precision = 0;
	}

	var multiplier = Math.pow(10, precision);

	return(Math.round(num * multiplier) / multiplier);
}

// Utility function to get the value of a <select> element
function GetSelectValue(form_name, select_name)
{
	var index = document.forms[form_name].elements[select_name].selectedIndex;
	return(document.forms[form_name].elements[select_name].options[index].value);
}

// Blah, the one above sucks (and I put it there).  This one's better.
function SelectValue(select)
{
	if(typeof select == "string")		// Assume this is the id of the select object
	{
		return(SelectValue(document.getElementById(select)));
	}
	else if(typeof select == "object")	// Assume this is the select object itself
	{
		return(select.options[select.selectedIndex].value);
	}
	else								// Eh... ?!?
	{
		alert("Don't know how to do a SelectValue on " + select);
		return(null);
	}
}

function GetTextValue(field_id)
{
	var field = document.getElementById(field_id);
	return((field) ? field.value : null);
}

// Utility function to get the value of a radio button
function GetRadioValue(form_name, radio_name)
{
	var radio = document.forms[form_name].elements[radio_name];

	for(var i=0; i<radio.length; i++)
	{
		if(radio[i].checked == true)
		{
			return(radio[i].value);
		}
	}

	return(null);
}

// This function has proved expensive for slow computers, so let's try unrolling it.
function FormatNumber(num)
{
	num = num.toString();
	var len = num.length;

	if(len <= 3)
	{
		return(num);
	}
	else if(len <= 6)
	{
		return(num.substring(0, len - 3) + "," + num.substring(len - 3, len));
	}
	else if(len <= 9)
	{
		return(num.substring(0, len - 6) + "," + num.substring(len - 6, len - 3) + "," + num.substring(len - 3, len));
	}
	else if(len <= 12)
	{
		return(num.substring(0, len - 9) + "," + num.substring(len - 9, len - 6) + "," + num.substring(len - 6, len - 3) + "," + num.substring(len - 3, len));
	}
	else	// We can only handle numbers up to 999,999,999,999
	{
		return(-1);
	}
}

function GetRandomNumber(low, high)
{
	return(Math.floor((Math.random() * (high - low + 1)) + low));
}

function GetPercentage(part, total)
{
	if(part == 0)
	{
		return(0);
	}

	return(FormatNumber((part/total)*100));
}

function HandleClick(element_name)
{
	var this_element = document.getElementById(element_name);
	if(this_element.disabled)
	{
		return;
	}

	if(this_element.type == "checkbox")
	{
		this_element.checked = !(this_element.checked);
	} else if(this_element.type == "radio")
	{
		this_element.checked = true;
	}
}

function ClearTextArea(textarea, maxnum, maxnumminushtml)
{
	if(confirm("Are you sure you want to clear this textarea?"))
	{
		textarea.value = "";

		if(typeof maxnumminushtml != "undefined")
		{
			CharactersRemainingMinusHTML(textarea, maxnum, maxnumminushtml);
		} else
		{
			CharactersRemaining(textarea, maxnum);
		}
	}
}

// This relies on the fact that there's a hidden form variable with the contents of whatever was previously there.
// the hidden form value should be named in the manner textareaname+ '_hidden'
// It also relies on the fact that the textarea has a "characters remaining" style thing already outlined.
function ResetForm(textarea, maxnum, maxnumminushtml)
{
	if(confirm("Are you sure you want to reset the field for this textarea?"))
	{
		textarea.value = unescape(document.getElementById(textarea.name + "_hidden").value);

		if(typeof maxnumminushtml != "undefined")
		{
			CharactersRemainingMinusHTML(textarea, maxnum, maxnumminushtml);
		} else
		{
			CharactersRemaining(textarea, maxnum);
		}
	}
}

/* HTML FUNCTIONS */

// Assumes there's a span/div id identical to that of the textarea, with the exception that its name has "_chars_remaining"
// appended to it. So, the textarea might be called 'body' and the span/div id would be called 'body_chars_remaining'
// Usage: <textarea ... onkeyup="CharactersRemaining(this);" onkeydown="CharactersRemaining(this);" onchange="CharactersRemaining(this);">
function CharactersRemaining(textarea, maxnum)
{
	var chars_remaining = maxnum - textarea.value.length;
	if(chars_remaining < 0)
	{
		textarea.value = textarea.value.substring(0, maxnum);
		chars_remaining = 0;
	}

	document.getElementById(textarea.id + "_chars_remaining").innerHTML = FormatNumber(chars_remaining);
}

// Like above, but we have to have _chars_remaining_minus_html
function CharactersRemainingMinusHTML(textarea, maxnum, maxnumminushtml)
{
	CharactersRemaining(textarea, maxnumminushtml);

	var chars_remaining = maxnum - textarea.value.replace(/<\/?(a|i(ns)?|b|u|em|strong).*?>/ig, '').length;
	if(chars_remaining < 0)
	{
		chars_remaining = 0;
	}

	document.getElementById(textarea.id + "_chars_remaining_minus_html").innerHTML = FormatNumber(chars_remaining);
}

// Use this to kick off our chars remaining stuff.  Recalculating on each keypress is too intensive.
var chars_remaining_timeouts = new Array();
function CheckCharsRemaining(id, max_chars, max_chars_minus_html)
{
	if(max_chars_minus_html > 0)
	{
		CharactersRemainingMinusHTML(document.getElementById(id), max_chars, max_chars_minus_html);
	}
	else
	{
		CharactersRemaining(document.getElementById(id), max_chars);
	}

	chars_remaining_timeouts[id] = setTimeout("CheckCharsRemaining('" + id + "', " + max_chars + ", " + max_chars_minus_html + ")", 1500);
}

function StopCharsRemaining(id)
{
	clearTimeout(chars_remaining_timeouts[id]);
}

function GetAge(month, day, year, nowmonth, nowday, nowyear)
{
	var age = nowyear-year;

	if(month<nowmonth)
	{
		age--;
	} else
	if((month == nowmonth) && (day > nowday))
	{
		age--;
	}

	return(age);
}

function CheckDate(month, day, year)
{
	var test_date = new Date(month + "/" + day + "/" + year);
	if((test_date.getMonth()+1)==month)
	{
		return(true);
	}
	return(false);
}

// Expects at least one argument - clear any number of arbitrary form fields by document.getElementById()
function ClearFields()
{
	for(var i=0; i<arguments.length; i++)
	{
		document.getElementById(arguments[i]).value = "";
	}
}

// Do we have an array element needle in haystack already?
function InArray(needle, haystack)
{
	for(var i=0; i<haystack.length; i++)
	{
		if(needle == haystack[i])
		{
			return(true);
		}
	}

	return(false);
}

// Use these functions to bounce to a Portal/Audio submission from anywhere (NG or userpages)
function JumpToMovie(select)
{
	if(select.selectedIndex != 0)
	{
		window.location.href = "http://redirect.ngfiles.com/portal/view/" + select.options[select.selectedIndex].value;
	}
}

function JumpToAudio(select)
{
	if(select.selectedIndex != 0)
	{
		window.location.href = "http://redirect.ngfiles.com/audio/listen/" + select.options[select.selectedIndex].value;
	}
}

// Utility function to take some HTML in a string and give back its DOM representation
function DOMNodeFromHTML(html)
{
	var holder_node = Builder.node("div");
	holder_node.innerHTML = html;

	for(i=0; i<holder_node.childNodes.length; i++)
	{
		if(holder_node.childNodes[i].nodeType == 1)		// 1 = element/tag
		{
			return(holder_node.childNodes[i]);
		}
	}

	return(null);
}

// Function to scroll to an element within a page.
function ScrollToElement(elementid)
{
	new Effect.ScrollTo(elementid);
}

function ToggleDisplayElement(element, displaytype)
{
	document.getElementById(element).style.display = displaytype;
}

// These are strictly for opening/closing review mod windows.
var reviewmod_win;
function OpenReviewModWindow(url)
{
	reviewmod_win = window.open("http://redirect.ngfiles.com" + url);
}
function CloseReviewModWindow()
{
	reviewmod_win.close();
}

// This comes into play when doing emoticons.  Assumptions:
// 1) You have a hidden form field called "icon" to store the chosen icon ID
// 2) Each icon is in a div called "smileyXYZ" where XYZ is that icon's ID

function MakeSmileySelection(id)
{
	var hidden_icon_element = document.getElementById("icon");

	// First see if there's an old one to deactivate
	var old_id = hidden_icon_element.value;
	if(old_id != "")
	{
		document.getElementById("smiley" + old_id).className = "smiley_off";
	}

	// Now activate the new one
	document.getElementById("smiley" + id).className = "smiley_on";
	hidden_icon_element.value = id;
}

// Class to handle animating text when all we're doing is adding dots on the end (like "Deleting...")
function DotAnimatedText(element_name, animate_class)
{
	// Constants
	var NUM_ANIMATION_DOTS = 5;					// Max number of dots to animate
	var ANIMATION_CYCLE_TIME = 0.5;				// How many seconds should it take to animate all the dots?

	// Private variables
	var timeout_handle = null;					// Handle to the next animation step
	var dot_count = 1;							// Used to increment the dots in our animation
	var animation_text = "";					// Gets set when we kick off the animation
	var original_animation_html = "";			// Store the text we're starting with
	var add_class = (typeof animate_class == "string");
	var element = null;

	this.Start = function(text_to_animate)
	{
		original_animation_html = GetElement().innerHTML;
		animation_text = text_to_animate;
		StepAnimation();
	}

	this.Stop = function()
	{
		// Clear any pending animations
		EndAnimation();

		// Restore our original text
		SetElementHTML(original_animation_html);
	}

	function StepAnimation()
	{
		var newtext = "";

		if(add_class)
		{
			newtext += "<span class=\"" + animate_class + "\">";
		}

		newtext += animation_text;

		// Build the text with the correct number of dots
		for(var i=1; i<=dot_count; i++)
		{
			newtext += ".";
		}

		// Increment the dot counter
		dot_count++;
		if(dot_count > NUM_ANIMATION_DOTS)
		{
			dot_count = 1;
		}

		if(add_class)
		{
			newtext += "</span>";
		}

		// Stick the text back out there
		SetElementHTML(newtext);

		// Set a timer to call ourselves later
		timeout_handle = setTimeout(StepAnimation, (ANIMATION_CYCLE_TIME / NUM_ANIMATION_DOTS) * 1000);
	}

	function GetElement()
	{
		return(document.getElementById(element_name));
	}

	function SetElementHTML(html)
	{
		var element = GetElement();
		if(element)
		{
			element.innerHTML = html;
		}
		else
		{
			// This means we tried to set the HTML of an element that's now gone.
			// Simply kill our animation (if necessary) and go home.
			EndAnimation();
		}
	}

	function EndAnimation()
	{
		if(timeout_handle != null)
		{
			clearTimeout(timeout_handle);
			timeout_handle = null;
		}
	}
}

// This class takes care of the "slide & fade" action that we use everywhere
function WindowDisplayer(id, renderhack_callback, open_link_text)
{
	var EFFECTS_DURATION 				= 0.5;		// Seconds

	var open_callback = null;
	var close_callback = null;
	var animation_in_progress = false;
	var original_link_text = null;	// Will be set upon first open
	var self = this;

	this.Open = function(callback)
	{
		if(!(animation_in_progress))
		{
			animation_in_progress = true;
			SetLinkText();
			open_callback = (typeof callback == "function" ? callback : null);
			SlideDown();
		}
	}

	this.Close = function(callback)
	{
		if(!(animation_in_progress))
		{
			animation_in_progress = true;
			SetLinkText();
			close_callback = (typeof callback == "function" ? callback : null);
			FadeOutBox();
		}
	}

	this.Toggle = function(callback)
	{
		GetOpenStatus() ? self.Close(callback) : self.Open(callback);
	}

	this.IsOpen = function()
	{
		return(GetOpenStatus());
	}

	function SlideDown()
	{
		new Effect.SlideDown(id, {duration: EFFECTS_DURATION, afterFinish: FadeInBox});
	}

	function SlideUp()
	{
		var container_div = document.getElementById(id + "_wrapper");

		// Hide the container for the box that just got faded out (and is already invisible)
		container_div.style.visibility = "hidden";

		// Now do the slide!
		new Effect.SlideUp(id, {duration: EFFECTS_DURATION, afterFinish: FinishClose});
	}

	function FadeInBox()
	{
		var container_div = document.getElementById(id + "_wrapper");
		var container_div_inner = document.getElementById(id + "_wrapper_inner");

		// First we explicitly give ourselves the height of the space that opened up previously
		container_div.style.height = container_div.offsetHeight + "px";

		// Now that we've set the height, hide the content (don't worry, it's invisible)
		container_div_inner.style.display = "none";

		// And now make it visible (it's display: none so we won't see it yet)
		container_div.style.visibility = "visible";

		// Finally - do the fade-in, in the space we just opened up
		new Effect.Appear(container_div_inner, {duration: EFFECTS_DURATION, afterFinish: FinishOpen});
	}

	function FadeOutBox()
	{
		// We need to "harden" our height again before we do this
		var container_div = document.getElementById(id + "_wrapper");
		container_div.style.height = container_div.offsetHeight + "px";

		// Alright, now we can fade out the inner but keep the height
		new Effect.Fade(id + "_wrapper_inner", {duration: EFFECTS_DURATION, afterFinish: SlideUp});
	}

	function FinishOpen()
	{
		// Wipe out the "hard" height we gave ourselves during the transition
		document.getElementById(id + "_wrapper").style.height = null;

		RenderingHacks();

		if(open_callback != null)
		{
			open_callback();
			RenderingHacks();		// Doing this twice might not be necessary in all cases.  Oh well.  !@#$%*.
		}

		animation_in_progress = false;
	}

	function FinishClose()
	{
		// Now that we've closed our outermost div, we can give display back to the inner wrapper
		document.getElementById(id + "_wrapper_inner").style.display = "block";

		RenderingHacks();

		if(close_callback != null)
		{
			close_callback();
			RenderingHacks();
		}

		animation_in_progress = false;
	}

	function SetLinkText()
	{
		var element = GetLinkElement();

		if((element) && (typeof open_link_text != "undefined"))
		{
			if(original_link_text == null)		// Let's store away what's there now
			{
				original_link_text = GetLinkElement().firstChild.data;
			}

			GetLinkElement().firstChild.data = (GetOpenStatus() ? original_link_text : open_link_text);
		}
	}

	function GetLinkElement()
	{
		return(document.getElementById(id + "_link"));
	}

	function GetOpenStatus()
	{
		var wrapper_element = document.getElementById(id + "_wrapper");
		return((wrapper_element) && (wrapper_element.style.visibility != "hidden"));
	}

	// This function is necessary to work around different browser quirks.  Maybe one day... it'll go away...
	function RenderingHacks()
	{
		if(typeof renderhack_callback == "function")
		{
			renderhack_callback(id);
		}
	}
}

// Class for doing "in progress" animations in box headers
function HeaderAnimator(element_id, animation_text)
{
	// ================ CONSTANTS ================
	var ANIMATION_CLASSNAME = "i-activity";		// The class we apply while the animation is happening

	// ================ PRIVATE variables ================
	var animation_running = false;				// Is the "Leaving Comment..." animation running?
	var button_link_html = new Array();			// The HTML from the link inside our button
	var button_classes = new Array();			// All buttons don't have the same class
	var animated_text = new DotAnimatedText(element_id + "_header");

	// This is the original stuff (pre-animation) text that appears in the header
	var original_text = GetHeader().firstChild.data;
	var original_classname = GetHeader().className;

	// // Let's try to force a pre-load of the activity image that the CSS uses
	var activity_image = new Image();
	activity_image.src = "http://img.ngfiles.com/hicons/i67.gif";		// Referenced in the i-activity class

	// ================ PUBLIC functions ================
	this.Start = function(text_to_animate)
	{
		if(!(animation_running))
		{
			animation_running = true;

			// Let's see if they're passing in the text to animate here
			if(typeof text_to_animate == "string")
			{
				animation_text = text_to_animate;
			}

			// This kicks off our dot animation
			animated_text.Start(animation_text);

			// Swap in the hourglass image
			GetHeader().className = ANIMATION_CLASSNAME;

			// Deactivate the button
			MakeButtonsDead();
		}
	}

	this.Stop = function()
	{
		if(animation_running)
		{
			animation_running = false;

			// Stop any running animation
			animated_text.Stop();

			// Swap back in the original icon image
			GetHeader().className = original_classname;

			// Make the button clickable again
			MakeButtonsLive();
		}
	}

	// ================ PRIVATE functions ================
	function MakeButtonsLive()
	{
		var button_elements = GetButtons();
		var button;

		for(var i=0; i<button_elements.length; i++)
		{
			button = button_elements[i];

			// Replace the dead button guts with the live one
			button.innerHTML = button_link_html[i];
			button.className = button_classes[i];
		}
	}

	function MakeButtonsDead()
	{
		var button_elements = GetButtons();
		var button;

		for(var i=0; i<button_elements.length; i++)
		{
			button = button_elements[i];

			// Store the old button link
			button_link_html[i] = button.innerHTML;
			button_classes[i] = button.className;

			// Now replace the live button guts with the dead one
			button.innerHTML = "<span>" + button.firstChild.innerHTML + "</span>";
			button.className = "btn dead";
		}
	}

	function GetHeader()
	{
		return(document.getElementById(element_id + "_header"));
	}

	function GetButtons()
	{
		var all_spans = GetHeader().parentNode.getElementsByTagName("span");
		var buttons = new Array();

		for(var i=0; i<all_spans.length; i++)
		{
			if(/_button$/.test(all_spans[i].id))
			{
				buttons[buttons.length] = all_spans[i];
			}
		}

		return(buttons);
	}
}

// Function for adding and immediately removing a space to some text.  Forces the browser to re-render stuff.
function AddRemoveSpace(element)
{
	if(typeof element.value == "string")	// For form elements
	{
		element.value += " ";
		element.value = element.value.substring(0, element.value.length - 1);
	}
	else					// Normal text embedded in the page
	{
		element.firstChild.data += " XYZ";
		element.firstChild.data = element.firstChild.data.substring(0, element.firstChild.data.length - 4);
	}
}

// Class for the buttons we use all over the damn place.  id should be in the outermost span
function Button(id, class_name, deadclass_name)
{
	var button_element = null;
	var original_button_html = null;
	var original_button_class = null;

	this.Enable = function()
	{
		var button = GetButtonElement();
		button.innerHTML = original_button_html;
		button.className = original_button_class;
	}

	this.Disable = function(disabled_text)
	{
		var button = GetButtonElement();

		if(original_button_html == null)
		{
			original_button_html = button.innerHTML;
		}

		if(original_button_class == null)
		{
			original_button_class = button.className;
		}

		if(typeof disabled_text == "undefined")
		{
			// No disable text passed in - let's try to find it ourselves
			var element = button.firstChild;
			while((element.firstChild) && (element.firstChild.nodeType == 1))
			{
				element = element.firstChild;
			}

			disabled_text = element.firstChild.data;
		}

		if(typeof class_name == "string")
		{
			disabled_text = "<span class=\"" + class_name + "\">" + disabled_text + "</span>";
		}

		button.innerHTML = "<span>" + disabled_text + "</span>";

		if(typeof deadclass_name != "string")
		{
			button.className = "btn dead";
		} else
		{
			button.className = deadclass_name;
		}
	}

	function GetButtonElement()
	{
		if(button_element == null)
		{
			button_element = document.getElementById(id);
		}

		return(button_element);
	}
}

// Use this to properly write out the HTML for any Flash.
function FlashWriter(url, width, height)
{
	// Options here are "window", "opaque", and "transparent"
	var DEFAULT_WINDOW_SETTING = "window";

	// Defaults for optional stuff below
	var quality = "high";
	var id = "flash" + GetRandomNumber(1000000, 9999999);
	var wmode = DEFAULT_WINDOW_SETTING;
	var script_access = "sameDomain";
	var allow_fullscreen = "false";
	var params = null;
	var has_flash = LookForFlashPlugin();

	this.SetQuality = function(new_quality)
	{
		quality = new_quality;
	}

	this.SetID = function(new_id)
	{
		id = new_id;
	}

	this.SetTransparent = function(is_transparent)
	{
		wmode = is_transparent ? "transparent" : DEFAULT_WINDOW_SETTING;
	}

	this.SetFullScreen = function(fullscreen)
	{
		allow_fullscreen = fullscreen ? "true" : "false";
	}

	this.SetScriptAccess = function(new_script_access)
	{
		script_access = new_script_access;
	}

	this.SetParams = function(new_params)
	{
		params = new_params;
	}

	this.ToString = function()
	{
		var str = "";

		if(has_flash)
		{
			str += '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="' + width + '" height="' + height + '" id="' + id + '">';

			str += '<param name="allowScriptAccess" value="' + script_access + '" />';
			str += '<param name="allowFullScreen" value="' + allow_fullscreen + '" />';
			str += '<param name="movie" value="' + url + '" /><param name="quality" value="' + quality + '" />';
			str += '<param name="wmode" value="' + wmode + '" />';

			if(params != null)
			{
				// IE needs this
				str += '<param name="flashvars" value="' + params + '" />';
			}

			str += '<embed src="' + url + '" quality="' + quality + '" ';

			if(params != null)
			{
				// Non-IE browsers need this
				str += 'flashvars="' + params + '" ';
			}

			str += 'wmode="' + wmode + '" width="' + width + '" height="' + height + '" name="' + id + '" allowScriptAccess="' + script_access + '" allowFullScreen="' + allow_fullscreen + '" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />';
			str += '</object>';
		}
		else
		{
			str += '<p style="text-align: center; margin-top: 2em; margin-bottom: 2em; padding-top: 3em; padding-bottom: 3em; background: #333333">You don\'t appear to have <a target="_blank" href="http://getflash.ngfiles.com">Flash</a> installed. <a target="_blank" href="http://getflash.ngfiles.com">Click here</a> to get it (it\'s free).</p>';
		}

		return(str);
	}

	this.Print = function()
	{
		document.write(this.ToString());
	}

	function LookForFlashPlugin()
	{
		var flash_versions = 12;

		// Code swiped from http://www.dangrossman.info/2007/01/03/detecting-flash-and-java-with-javascript/
		if (navigator.plugins && navigator.plugins.length) {
			// Netscape style plugin detection
			for (x = 0; x <navigator.plugins.length; x++) {
				if (navigator.plugins[x].name.indexOf('Shockwave Flash') != -1) {
					return(true);
				}
			}
		}
		else if (window.ActiveXObject) {
			// ActiveX style plugin detection
			for (x = 2; x <= flash_versions; x++) {
				try {
					oFlash = eval("new ActiveXObject('ShockwaveFlash.ShockwaveFlash." + x + "');");
					if (oFlash) {
						return(true);
					}
				}
				catch(e) { }
			}
		}
		
		return(false);
	}
}

/*

Class to handle checkbox items for our checkbox element controls.

Assumes that the checkboxes have ids of prefix + NUM, where NUM is 0 through
total elements.

*/

function CheckboxItems(num, prefix)
{
	var total = num;
	var ids = new Array();
	var keys = new Array();

	// Returns a checkbox element, with the given id.
	function GetElement(id)
	{
		return(document.getElementById(prefix +  id));
	}

	// Sets any checked boxes to unchecked and vice-versa
	this.ToggleAll = function()
	{
		// Clear out the ids
		ids = new Array();
		keys = new Array();

		// Set out temporary values.
		var is_checked;
		var is_disabled;
		var element;

		for(var i=0; i<total; i++)
		{
			element = GetElement(i);

			is_checked = element.checked;
			is_disabled = element.disabled;

			if((!(is_checked)) && (!(is_disabled)))
			{
				// We add it into our list of ids (and keys), since it is about to get checked.
				keys[keys.length] = i;
				ids[ids.length] = element.value;
			}

			if(!(is_disabled))
			{
				element.checked = (!(is_checked));
			}
		}
	}

	// We need this for our global list of ids, so if it's checked then we
	// need to know about it.
	this.Toggle = function(id)
	{
		var element = GetElement(id);

		if((element.checked) && (!(InArray(element.value, ids))))
		{
			// We bang it straight in here.
			keys[keys.length] = id;
			ids[ids.length] = element.value;
		} else
		if((!(element.checked)) && (InArray(element.value, ids)))
		{
			// We were previously checked by our toggle button.
			for(var i=0; i<total; i++)
			{
				if(element.value == ids[i])
				{
					keys.splice(i, 1);
					ids.splice(i, 1);
					break;
				}
			}
		}
	}

	// Clears all checkbox elements, even if previously unchecked.
	this.ClearAll = function()
	{
		ids = new Array();
		keys = new Array();

		for(var i=0; i<total; i++)
		{
			GetElement(i).checked = false;
		}
	}

	// Selects all checkbox elements, whether previously checked or not.
	this.SelectAll = function()
	{
		ids = new Array();
		keys = new Array();

		for(var i=0; i<total; i++)
		{
			GetElement(i).checked = true;
			keys[keys.length] = i;
			ids[ids.length] = GetElement(i).value;
		}
	}

	this.Enable = function()
	{
		for(var i=0; i<total; i++)
		{
			GetElement(i).disabled = false;
		}
	}

	this.Disable = function()
	{
		for(var i=0; i<total; i++)
		{
			GetElement(i).disabled = true;
		}
	}

	this.GetKey = function(num)
	{
		return(keys[num]);
	}

	this.GetKeys = function()
	{
		return(keys);
	}

	this.GetID = function(num)
	{
		return(ids[num]);
	}

	this.GetIDS = function()
	{
		return(ids);
	}

	this.IsToggled = function()
	{
		return(ids.length > 0);
	}
}

