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

function Reload()
{
	// This is better than calling window.reload()
	window.location.href = window.location.href;
}

// 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(text)
{
	return(String(text).replace(/(^\s*|\s*$)/g, ""));
}

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 */

// If remaining_char_count_elem is not given,
// 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, remaining_char_count_elem)
{
  if (!textarea) {
    if (console && console.log){
      console.log("missing textarea for CharsRemaining() in ng.js");
    }
    return;
  }
	var chars_remaining = maxnum - textarea.value.length;
	if(chars_remaining < 0)
	{
		textarea.value = textarea.value.substring(0, maxnum);
		chars_remaining = 0;
	}

	if (!remaining_char_count_elem) {
		remaining_char_count_elem = document.getElementById(textarea.id + "_chars_remaining");
	}

	remaining_char_count_elem.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 CheckCharsRemainingInElem(id_or_elem, max_chars, max_chars_minus_html, remaining_char_count_elem)
{
	var elem = $(id_or_elem);
	var check_chars;

	if (max_chars_minus_html > 0) {
		check_chars = function () {
			CharactersRemainingMinusHTML(elem, max_chars, max_chars_minus_html);
		};
	} else {
		check_chars = function () {
			CharactersRemaining(elem, max_chars, remaining_char_count_elem, remaining_char_count_elem);
		};
	}

	check_chars();
	chars_remaining_timeouts[elem.identify()] = setInterval(check_chars, 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 $(element_name);
	}

	this.getElement = function ()
	{
		return GetElement();
	}

	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;
	var offset_hack = 0;			// Sometimes we need to offset the animation by some arbitrary amount

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

	this.isInProgress = function ()
	{
		return animation_in_progress;
	};

	this.SetOffsetHack = function(offset)
	{
		offset_hack = offset;
	}

	this.SetOriginalLinkText = function(text)
	{
		original_link_text = text;
	}

	function SlideDown()
	{
		new Effect.SlideDown(id, {duration: EFFECTS_DURATION, afterFinish: FadeInBox});
	}

	function getWrapper()
	{
		if (typeof id === 'string') {
			return $(id + '_wrapper');
		} else {
			return id.down();
		}
	}

	function getWrapperInner()
	{
		if (typeof id === 'string') {
			return $(id + '_wrapper_inner');
		} else {
			return getWrapper().down();
		}
	}

	function SlideUp()
	{
		var container_div = getWrapper();

		// 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 = getWrapper();
		var container_div_inner = getWrapperInner();

		// First we explicitly give ourselves the height of the space that opened up previously
		container_div.style.height = (offset_hack + 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 = getWrapper();
		container_div.style.height = (offset_hack + container_div.offsetHeight) + "px";

		// Alright, now we can fade out the inner but keep the height
		new Effect.Fade(getWrapperInner(), {duration: EFFECTS_DURATION, afterFinish: SlideUp});
	}

	function FinishOpen()
	{
		// Wipe out the "hard" height we gave ourselves during the transition
		getWrapper().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
		getWrapperInner().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()
	{
		if (typeof id === 'string') {
			return $(id + '_link');
		} else {
			return $(id.id + '_link');
		}
	}

	function GetOpenStatus()
	{
		var wrapper_element = getWrapper();
		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);
		}
	}
}

/* Static members/methods */
(function () {
	var window_displayers = {};

	WindowDisplayer.getFor = function (div)
	{
		div_id = $(div).identify();
		if (!window_displayers[div_id]) {
			window_displayers[div_id] = new WindowDisplayer(div);
		}

		return window_displayers[div_id];
	};

	WindowDisplayer.hasFor = function (div)
	{
		div_id = $(div).identify();

		return !!window_displayers[div_id]
	};
	
	WindowDisplayer.open = function (div, callback)
	{
		var window_displayer = WindowDisplayer.getFor(div);
		if (!window_displayer.isInProgress()) {
			return window_displayer.Open(callback);
		}
	};

	WindowDisplayer.close = function (div, callback)
	{
		var window_displayer = WindowDisplayer.getFor(div);
		if (!window_displayer.isInProgress()) {
			return WindowDisplayer.getFor(div).Close(callback);
		}
	};

})();

// 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;
	var is_big_button = false;

	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;
			is_big_button = /bigbtn/.test(original_button_class);
		}

		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 = is_big_button ? "bigbtn bigdead" : "btn dead";
		}
		else
		{
			button.className = deadclass_name;
		}
	}

	this.IsEnabled = function()
	{
		return(GetButtonElement().getElementsByTagName('a').length > 0);
	}

	this.Exists = function()
	{
		return(GetButtonElement());
	}

	this.Select = function()
	{
		var link_element = (GetButtonElement().getElementsByTagName("a"))[0];
		if(!(/highlight/.test(link_element.className)))
		{
			link_element.className += " highlight";
		}
	}

	this.Unselect = function()
	{
		var link_element = (GetButtonElement().getElementsByTagName("a"))[0];
		if(/highlight/.test(link_element.className))
		{
			link_element.className = link_element.className.replace(/ ?highlight/, "");
		}
	}

	this.getID = function()
	{
		return(id);
	}

	function GetButtonElement()
	{
		if(button_element == null)
		{
			button_element = $(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.SetOpaque = function(is_opaque)
	{
		wmode = is_opaque ? "opaque" : 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.
*/

// There's really no reason to have to pass in total here - rewrite this at some point
function CheckboxItems(total, prefix)
{
	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);
	}

	this.GetNumChecked = function()
	{
		return(ids.length);
	}
}

// instantiate holder for PHP variables
var PHP;
if (typeof PHP === 'undefined') {
	(function () {
		// keep the variable list private
		var vars = {};

		PHP = {
			get : function (var_name, default_value) {
				if (typeof vars[var_name] === 'undefined') {
					return default_value;
				} else {
					return vars[var_name];
				}
			},

			set : function (var_name, value) {
				return (vars[var_name] = value);
			}
		};
	})();
}

function NiGhtBox() {
	var EFFECTS_DURATION 				= 0.5;		// Seconds
	var div_to_display;
	var inner_div;
	var left_pos;
	var top_pos;

	this.isInitialized = function ()
	{
		return !!inner_div;
	}

	this.initDisplay = function (div, top_pos, left_pos)
	{
		// clear previous display if any
		if (inner_div && inner_div.parentNode) {
			inner_div.remove();
		}

		if (typeof div === 'string') {
			div = $('div');
		}

		// remove our div from the page, if it's in the page
		if (div.parentNode) {
			div.parentNode.removeChild(div);
		}

		// container divs center the element; we build them from the inside out
		div.className = 'vc-cntr';
		div.style.display = 'block';

		// if we already have containers, don't rebuild them
		if (! div_to_display) {
			var vc_in = document.createElement('div');
			vc_in.className = 'vc-in';
			vc_in.appendChild(div);

			var vc_mid = document.createElement('div');
			vc_mid.className = 'vc-mid';
			vc_mid.appendChild(vc_in);

			var vc_out = document.createElement('div');
			vc_out.className = 'vc-out';
			vc_out.appendChild(vc_mid);

			div_to_display = document.createElement('div');
			div_to_display.className = 'vc-abs';
			div_to_display.appendChild(vc_out);

			div_to_display.style.display = 'none';

			// add div to the page, immediately after the background div
			Element.insert(NiGhtBox.getOverlayBackground(), { after: div_to_display });

			NiGhtBox.centerElement(div_to_display);
		} else {
			var vc_mid = div_to_display.down().down();
		}
	};

	this.showDisplay = function ()
	{
		var myself = this;
		Effect.Appear(div_to_display, { duration: EFFECTS_DURATION });
	};

	this.hideDisplay = function ()
	{
		div_to_display.hide();
	};

	this.removeDisplay = function ()
	{
		div_to_display.remove();
	};
}

/* Class constants */
NiGhtBox.BACKGROUND_DIV = 'overlayblackout';
NiGhtBox.BACKGROUND_DIV_FF2MAC = 'overlayblackout_ffmac';

/* static methods */
(function () {
	var nightbox;
	NiGhtBox.getInstance = function ()
	{
		if (!NiGhtBox.hasInstance()) {
			nightbox = new NiGhtBox();
		}

		return nightbox;
	};

	NiGhtBox.hasInstance = function ()
	{
		return undefined !== nightbox;
	};

	NiGhtBox.centerElement = function (elem)
	{
		// attempt to center image in viewport
		var height = $(elem).getHeight();

		// if height or width is 0, centering fails
		if (!height) {
			return false;
		}

		var viewport_height = document.viewport.getHeight();

		var viewport_scroll_offsets = document.viewport.getScrollOffsets();
		var viewport_scroll_y = viewport_scroll_offsets.top;

		var form_top;

		if (height < viewport_height) {
			form_top = viewport_scroll_y + (viewport_height - height) / 2;
		} else {
			form_top = viewport_scroll_y;
		}

		elem.style.position = 'absolute';
		elem.style.top = form_top + 'px';
		elem.style.zIndex = 99;

		return true;
	}


	var is_firefox2_for_mac;
	// Necessary for an ugly hack - FF/Mac can't handle Flash combined with CSS opacity
	// UPDATE - RR 01/2009 - only a problem on FF2 for Mac
	NiGhtBox.isFirefox2Mac = function ()
	{
		if (undefined === is_firefox2_for_mac) {
			var userAgent = navigator.userAgent.toLowerCase();
			if (userAgent.indexOf('mac') != -1 && /firefox[\/\s](\d+\.\d+)/.test(userAgent)) {
				var ffversion = new Number(RegExp.$1);
				if (ffversion < 3) {
					is_firefox2_for_mac = true;
				}
			}

			is_firefox2_for_mac = false;
		}

		return is_firefox2_for_mac;
	}

	/**
	 * Set up our background and append it to the body,
	 * but do not display yet
	 *
	 * @returns background div
	 */
	var background_div;
	NiGhtBox.initOverlayBackground = function ()
	{
		if (undefined === background_div) {
			var body = document.getElementsByTagName('body').item(0);
			background_div = document.createElement('div');

			if (!NiGhtBox.isFirefox2Mac()) {
				background_div.id = NiGhtBox.BACKGROUND_DIV;
			} else {
				// Mac Firefox can't handle opacity stuff, so let's do a workaround.

				background_div.id = NiGhtBox.BACKGROUND_DIV_FF2MAC;
				background_div.style.backgroundImage = 'url(http://img.ngfiles.com/blackbox.png)';
				background_div.style.backgroundRepeat = 'repeat';
			}

			var scroll_dimensions = NiGhtBox.ScrollDimensionsInfo();

			background_div.style.width = '100%';
			background_div.style.height = scroll_dimensions[1] + 'px';
			background_div.style.display = 'none';

			body.insertBefore(background_div, body.firstChild);
		}

		return background_div;
	};

	NiGhtBox.getOverlayBackground = function ()
	{
		if (undefined === background_div) {
			NiGhtBox.initOverlayBackground();
		}

		return background_div;
	}

	NiGhtBox.hideOverlayBackground = function ()
	{
		NiGhtBox.showAds();
		$(NiGhtBox.initOverlayBackground()).hide();

		// the following will only run in ie6 and older browsers
		if (!window.XMLHttpRequest) {
			// fix IE6 select z-index bug
			var selects = document.getElementsByTagName('select');
			for (var i = 0; i < selects.length; ++i) {
				selects[i].style.visibility = 'visible';
			}
		}
	};

	NiGhtBox.showOverlayBackground = function ()
	{
		NiGhtBox.hideAds();
		$(NiGhtBox.initOverlayBackground()).show();

		// the following will only run in ie6 and older browsers
		if (!window.XMLHttpRequest) {
			// fix IE6 select z-index bug
			var selects = document.getElementsByTagName('select');
			for (var i = 0; i < selects.length; ++i) {
				selects[i].style.visibility = 'hidden';
			}
		}
	};

	var ads;
	NiGhtBox.getAds = function ()
	{
		if (ads === undefined) {
			ads = $$('iframe.horzad' , 'iframe.onethirdad');
		}

		return ads;
	};

	NiGhtBox.hideAds = function ()
	{
		NiGhtBox.getAds().each(function (elem) {
			elem.style.visibility = 'hidden';
		});
	};

	NiGhtBox.showAds = function ()
	{
		NiGhtBox.getAds().each(function (elem) {
			elem.style.visibility = 'visible';
		});
	};
})();

NiGhtBox.InnerDimensionsInfo = function () {
	var dimensions = new Array(2);

	if (document.documentElement && document.documentElement.clientWidth) {
		dimensions[0] = document.documentElement.clientWidth;
		dimensions[1] = document.documentElement.clientHeight;
	} else if (self.innerWidth) {
		dimensions[0] = self.innerWidth;
		dimensions[1] = self.innerHeight;
	} else if (document.body) {
		dimensions[0] = document.body.clientWidth;
		dimensions[1] = document.body.clientHeight;
	}

	return dimensions;
};

NiGhtBox.ScrollDimensionsInfo = function () {
	var dimensions = new Array(2);

	if (window.innerHeight && window.scrollMaxY) {
		dimensions[0] = document.body.scrollWidth;
		dimensions[1] = window.innerHeight + window.scrollMaxY;
	} else if(document.body.scrollHeight > document.body.offsetHeight) {
		dimensions[0] = document.body.scrollWidth;
		dimensions[1] = document.body.scrollHeight;
	} else {
		dimensions[0] = document.body.offsetWidth;
		dimensions[1] = document.body.offsetHeight;
	}

	return dimensions;
};

NiGhtBox.GetPageScrolledPosition = function () {
	var dimensions = new Array(2);

	if (self.pageYOffset) {
		dimensions[0] = self.pageXOffset;
		dimensions[1] = self.pageYOffset;
	} else if (document.documentElement && document.documentElement.scrollTop) {
		dimensions[0] = document.documentElement.scrollLeft;
		dimensions[1] = document.documentElement.scrollTop;
	} else if (document.body) {
		dimensions[0] = document.body.scrollLeft;
		dimensions[1] = document.body.scrollTop;
	}

	return dimensions;
};

/**
 * Loosely coupled version of some code in AjaxHandler
 */
XmlDom = Class.create({
	initialize: function(transport) {
		this._xml = transport.responseXML;
		this._xml_text = transport.responseText;
	},

	isValidDom: function ()
	{
		if (this._is_valid_dom === undefined) {
			this._is_valid_dom = this._xml && this._xml.documentElement;
		}

		return this._is_valid_dom;
	},

	getXml: function ()
	{
		return this._xml;
	},

	getXmlText: function ()
	{
		return this._xml_text;
	},

	getField: function (tag_name)
	{
		if (this.isValidDom()) {
			// Now check for this specific field
			var node_list = this._xml.documentElement.getElementsByTagName(tag_name);
			if (node_list && node_list.length == 1 && node_list[0].firstChild) {
				return node_list[0].firstChild.data;
			}
		}
	},

	hasField: function (tag_name)
	{
		if (this.isValidDom()) {
			var node_list = this._xml.documentElement.getElementsByTagName(tag_name);
			return node_list && node_list.length == 1 && node_list[0].firstChild;
		}
	},

	// the shittiness of DOM makes the following virtually impossible, so we cheat
	innerXML: function (tag_name)
	{
		if (this.isValidDom()) {
			// NOTE: [\s\S] matches any character
			var regex = new RegExp('<' + tag_name + '>([\\s\\S]*?)</' + tag_name + '>', 'm');
			var matches = this._xml_text.match(regex);
			if (matches) {
				return matches[1];
			}
		}
	}

});

NgXmlDom = Class.create(XmlDom, {
	STATUS_ERROR        : 0,
	STATUS_SUCCESS 			: 1,
	STATUS_ERROR_SILENT	: 2,

	validate: function(error_handler)
	{
		var response_status = this.getField('status');

		if (!response_status && 0 !== response_status) {
			alert('Server error.  Please wait a moment and try again.');

			if (PHP.get('DEBUG')) {
				alert(this._xml_text);
			}

			if (error_handler) {
				error_handler(this);
			}

			return false;
		} else {
			switch (parseInt(response_status)) {
				case this.STATUS_SUCCESS:
					
					return true;
					break;
				case this.STATUS_ERROR:
					alert('Error - ' + this.getField('errormsg'));

					if (error_handler) {
						error_handler(this);
					}

					return false;
					break;
				case this.STATUS_ERROR_SILENT:
					return false;
					break;
			}
		}
	}
});


