var submission_controller = new function()
{
	// These match up with other places
	var TYPE_FLASH_PORTAL						= 1;
	var TYPE_AUDIO_PORTAL						= 2;

	// How long, in seconds, we want it to take for reviews to show up on the page
	var SHOW_REVIEW_EFFECT_DURATION				= 1;

	// These are our different components in the page
	var favorites_controller = null;
	var vote_controller = new SubmissionVoteController();
	var review_controller = null;
	var whistle_controller = null;

	// Some stuff we need to know about this submission
	var id = null;
	var type = null;
	var votekeys = null;
	var filesize = null;

	this.Init = function(this_id, this_type, key_array, this_filesize)
	{
		if((id == null) && (type == null) && (votekeys == null) && (filesize == null))
		{
			id = this_id;
			type = this_type;
			votekeys = key_array;

			if(typeof filesize != "undefined")
			{
				filesize = this_filesize;
			}
		}
		
		this.pop_window = null;
	}

	this.ShowLoginBox = function()
	{
		new Effect.ScrollTo("mainframe", {duration: 0.7, afterFinish: DoHighlight});

		var times = 1;

		function DoHighlight()
		{
			if(times <= 3)
			{
				times++;
				new Effect.Highlight("wholeloginbox", {duration: 0.7, startcolor: '#ff9900', endcolor: '#000000', afterFinish: DoHighlight});
			}
		}
	}

	// This function runs at the bottom of the page - when everything is essentially loaded.
	// We avoid the onload event because sometimes ads are slow to load, and onload waits for them.
	this.PageLoaded = function()
	{
		ShowOpenLink();
		if(filesize != null)		// For under judgment stuff
		{
			vote_controller.SetActivateTimer();
		}
	}

	this.PopViewer = function(height, width, extra)
	{
		if (!extra) {
			extra = "";
		} else {
			extra = "&"+extra;
		}
		var url = "/portal/viewer.php?id=" + id + "&key=" + escape(document.getElementById("submission_key").value)+extra;
		this.pop_window = NewWindow(url, height, width, 'ng_flash_popup_'+id);
	}

	this.GetPopWindow = function()
	{
		return this.pop_window;
	}
	
	this.GetFavoritesController = function()
	{
		if(favorites_controller == null)
		{
			favorites_controller = new SubmissionFavoritesController();
		}

		return(favorites_controller);
	}

	this.GetVoteController = function(testkey)
	{
		var found = false;
		for(var i=0; i<votekeys.length; i++)
		{
			if(votekeys[i] == testkey)
			{
				found = true;
				break;
			}
		}

		if((found) && (testkey != null))
		{
			return(vote_controller);
		}
		else
		{
			DoIllegalVoteAttempt();
			return(null);
		}
	}

	this.GetReviewController = function()
	{
		if(review_controller == null)
		{
			review_controller = new SubmissionReviewController();
		}

		return(review_controller);
	}

	this.GetWhistleController = function()
	{
		if(whistle_controller == null)
		{
			whistle_controller = new SubmissionWhistleController("blowwhistle");
		}

		return(whistle_controller);
	}

	this.WriteUnderJudgmentReview = function()
	{
		var review_controller = this.GetReviewController();
		var whistle_controller = this.GetWhistleController();

		if(whistle_controller.IsOpen())
		{
			// Gotta close the whistle window first, then open the review window
			var callback = function() {
				review_controller.GetWriteReviewWindow().Open();
			};
			StopCharsRemaining("blowwhistle_body");
			whistle_controller.Close(callback);
		}
		else
		{
			// Just open the review window
			review_controller.GetWriteReviewWindow().Toggle();
		}
	}

	this.BlowWhistle = function(reason)
	{
		var review_controller = this.GetReviewController();
		var whistle_controller = this.GetWhistleController();

		if(review_controller.GetWriteReviewWindow().IsOpen())
		{
			// If the review window is open, we need to close that before we open the whistle stuff
			var callback = function() {
				whistle_controller.BlowWhistle(reason);
			};
			review_controller.GetWriteReviewWindow().Close(callback);
		}
		else
		{
			// Just open the whistle window, nothing fancy
			whistle_controller.BlowWhistle(reason);
		}
	}

	this.IllegalVoteAttempt = function()
	{
		DoIllegalVoteAttempt();
	}

	function DoIllegalVoteAttempt()
	{
		var url = "";

		if(type == TYPE_AUDIO_PORTAL)
		{
			url = "/audio/badvote";
		}
		else
		{
			url = "/portal/badvote";
		}

		window.location.href = url;
	}

	// This function draws visual attention to the "Play this Movie" link (for Flash Portal)
	function ShowOpenLink()
	{
		var NUM_PULSES			= 5;		// In seconds
		var EFFECT_LENGTH		= 5;		// In seconds, for the entire thing total
		var INTERVAL_LENGTH		= (EFFECT_LENGTH * 1000) / (NUM_PULSES * 2);	// In milliseconds
		var BLINK_COLOR			= "#ffffff";

		var pulses = 0;					// Number of times the effect has run
		//var link_element = document.getElementById("launchpopin_link");
		//var original_color = link_element.style.color;
		//var current_color = original_color;

		//StepEffect();

		function StepEffect()
		{
			if(current_color == original_color)
			{
				current_color = BLINK_COLOR;
			}
			else
			{
				current_color = original_color;
				pulses++;
			}

			link_element.style.color = current_color;

			if(pulses < NUM_PULSES)
			{
				setTimeout(StepEffect, INTERVAL_LENGTH);
			}
		}
	}

	// Inline class to handle adding either an artist or a submission to favorites
	function SubmissionFavoritesController()
	{
		var AJAX_ADD_FAVORITES_PAGE		= "/ajax/addfavorite.php";
		var FAVORITE_FLASH_URL			= "/account/favorites/flash";
		var FAVORITE_AUDIO_URL			= "/account/favorites/audio";

		var fav_artist_animators = new Array();
		var fav_submission_animators = new Array();
		var link_position = 1;
		var submission_save_in_progress = false;

		this.AddArtist = function(user_id)
		{
			// Kick off our animation
			GetFavoriteArtist(user_id).className = 'working_sm';

			var params = new Array();
			params["user_id"] = user_id;

			if(type == TYPE_FLASH_PORTAL)
			{
				params["fav_type"] = 2;
			}
			else
			{
				params["fav_type"] = 4;
			}

			var ajax = new AjaxRequest(AJAX_ADD_FAVORITES_PAGE);
			ajax.Send(params, HandleFavoriteArtistSave, GetFavoriteArtist(user_id).className = '');
		}

		this.AddSubmission = function(position)
		{
			if(!(submission_save_in_progress))
			{
				submission_save_in_progress = true;

				// Store this for the case where there are multiple links on the page
				link_position = position;

				// Kick off our animation
				var animate_text = (link_position == 1) ? "Adding to Favorites" : "Adding";
				GetFavoriteSubmissionAnimator().Start(animate_text);

				var params = new Array();
				params["id"] = id;
				params["type"] = type;

				if(type == TYPE_FLASH_PORTAL)
				{
					params["fav_type"] = 1;
				}
				else
				{
					params["fav_type"] = 3;
				}

				var ajax = new AjaxRequest(AJAX_ADD_FAVORITES_PAGE);
				ajax.Send(params, HandleFavoriteSubmissionSave, StopFavoriteSubmissionAnimation);
			}
		}

		function HandleFavoriteArtistSave(response)
		{
			var user_id = response.GetField("user_id");

			// First stop the animation
			GetFavoriteArtist(user_id).className = '';

			// Now all we have to do is change the link to "Manage Favorite Artists"
			var link_element = GetFavoriteArtist(user_id).firstChild;
			link_element.firstChild.data = "Manage Favorite Artists";
			link_element.href = GetManagePage(type) + "/artists";

			// It's the child that gets this class.
			link_element.className = 'ac_faved ac_wide';
		}

		function HandleFavoriteSubmissionSave(response)
		{
			// First stop the animation
			StopFavoriteSubmissionAnimation();

			// Now all we have to do is change the link to "Manage Favorite Audio"
			var link_element = document.getElementById("favorite_submission_link1").firstChild;
			link_element.href = GetManagePage(type);

			if(type == TYPE_FLASH_PORTAL)
			{
				link_element.firstChild.data = "Manage Favorite Flash";
			}
			else if(type == TYPE_AUDIO_PORTAL)
			{
				link_element.firstChild.data = "Manage Favorite Audio";
			}

			// If there's a second "Add to Favorites", let's eliminate that entirely
			var extra_element = document.getElementById("favorite_submission_extra");
			if(extra_element)
			{
				extra_element.style.display = "none";
			}
		}

		function GetManagePage(type)
		{
			switch(type)
			{
				case TYPE_FLASH_PORTAL:
					return(FAVORITE_FLASH_URL);
				case TYPE_AUDIO_PORTAL:
					return(FAVORITE_AUDIO_URL);
			}
		}

		function StopFavoriteSubmissionAnimation()
		{
			GetFavoriteSubmissionAnimator().Stop();
			submission_save_in_progress = false;
		}

		function GetFavoriteArtist(user_id)
		{
			if(!(fav_artist_animators[user_id]))
			{
				fav_artist_animators[user_id] = $("favorite_author_link" + user_id);
			}

			return(fav_artist_animators[user_id]);
		}

		function GetFavoriteSubmissionAnimator()
		{
			if(!(fav_submission_animators[link_position]))
			{
				fav_submission_animators[link_position] = new DotAnimatedText("favorite_submission_link" + link_position, "working");
			}

			return(fav_submission_animators[link_position]);
		}
	}

	// Inline class to handle all aspects of voting
	function SubmissionVoteController()
	{
		var AJAX_VOTE_PAGE 			= "/ajax/submitvote.php";
		var FAQ_VOTING_PAGE			= "/lit/faq#fp_voting_on_submissions";
		var vote_num = null;
		var animated_text = null;
		var vote_message_box = new WindowDisplayer("aftervote");

		this.DoVote = function(vote)
		{
			// Let's make sure the vote is valid
			if(!((vote === 0) || (vote === 1) || (vote === 2) || (vote === 3) || (vote === 4) || (vote === 5)))
			{
				alert("Illegal vote - " + vote);
				return;
			}

			// OK, let's start our "doing something" animation
			// NOTE - in this case we want to delay the actual submit until the start animation is finished.
			vote_num = vote;
			StartAnimation();			// This will eventually kick off DoActualVoteSubmit
		}

		this.SetActivateTimer = function()
		{
			setTimeout(ActivateUnderJudgmentVotebar, Round((filesize / 1024) * 2));
		}

		function DoActualVoteSubmit()
		{
			var params = new Array();
			params["vote"] = vote_num;
			params["id"] = id;
			params["type"] = type;
			params["key"] = document.getElementById("submission_key").value;

			var ajax = new AjaxRequest(AJAX_VOTE_PAGE);
			ajax.Send(params, HandleVoteSave, HandleVoteError);
		}

		function HandleVoteSave(response)
		{
			// Stop the animation and note that we were a success
			GetAnimatedText().Stop();
			SetVoteBarText("You gave this submission a vote of \"" + vote_num + "\"");

			// Now slap our new score/num. votes into their place
			var score_element = document.getElementById("submission_score");
			if(score_element)		// Under judgment stuff doesn't have this
			{
				score_element.className = "yellow";
				score_element.firstChild.data = response.GetField("score");

				var num_votes_element = document.getElementById("submission_num_votes");
				num_votes_element.className = "yellow";
				num_votes_element.firstChild.data = response.GetField("num_votes");

				// Let's also show the user how that vote changed the score
				var score_change_element = document.getElementById("submission_score_change");
				score_change_element.innerHTML = "(" + response.GetField("score_change") + ")";

				var score_change_type = response.GetField("score_change_type");
				var score_change_class;

				if(score_change_type == "raise")
				{
					score_change_class = "green";
				}
				else if(score_change_type == "lower")
				{
					score_change_class = "red";
				}
				else
				{
					score_change_class = "yellow";
				}

				score_change_element.className = score_change_class;
			}

			// Finally, slide open the window that shows how much their vote was worth
			document.getElementById("aftervote_msg").innerHTML = response.GetField("response_msg");
			vote_message_box.Open();
		}

		function HandleVoteError(errorcode)
		{
			GetAnimatedText().Stop();

			if(errorcode == null)
			{
				// This is a network/server error - re-enable voting
				var hide_votebar_callback = function() {
					document.getElementById("votebar_wrapper_hidden").style.display = "none";
				};

				new Effect.Appear("votebar_wrapper", {duration: 0.01, afterFinish: hide_votebar_callback});
			}
			else
			{
				// This is an application specific error
				var errormsg = "";

				if(errorcode == 2)		// VOTE_ERROR_TOO_MUCH_TIME_ELAPSED
				{
					errormsg = "Refresh page to re-enable voting";
				}
				else if(errorcode == 3)	// VOTE_ERROR_ALREADY_VOTED_TODAY
				{
					errormsg = "You've <a href=\"" + FAQ_VOTING_PAGE + "\">already voted</a> on this today";
				}
				else if(errorcode == 4)	// VOTE_ERROR_IP_ZONE_ALREADY_VOTED
				{
					errormsg = "A <a href=\"" + FAQ_VOTING_PAGE + "\">similar address</a> voted on this today";
				}
				else					// VOTE_ERROR_GENERIC
				{
					errormsg = "Error has occurred - voting disabled";
				}

				SetVoteBarText(errormsg);
			}
		}

		function StartAnimation()
		{
			// Load up the "submitting..." text
			SetVoteBarText("<span id=\"votetext_dots\" class=\"voting\">Submitting vote</span>");

			// And now fade in the disabled voting bar, all fancy-like
			new Effect.Fade("votebar_wrapper", {duration: 0.5});
			new Effect.Appear("votebar_wrapper_hidden", {duration: 0.5, afterFinish: DoActualVoteSubmit});
			GetAnimatedText().Start("Submitting Vote");
		}

		function ActivateUnderJudgmentVotebar()
		{
			new Effect.Fade("votebar_wrapper_hidden", {duration: 0.5});
			new Effect.Appear("votebar_wrapper", {duration: 0.5});
		}

		function SetVoteBarText(text)
		{
			document.getElementById("votetext").innerHTML = text;
		}

		function GetAnimatedText()
		{
			if(animated_text == null)
			{
				animated_text = new DotAnimatedText("votetext_dots");
			}

			return(animated_text);
		}
	}

	// Inline class to handle the review-related stuff
	function SubmissionReviewController()
	{
		var write_review_window = new WriteReviewWindow("writereview" + id, "Don't review");
		var share_this_window = new WindowDisplayer("sharethis" + id, null, "Don't share");
		var display_review_window = new DisplayReviewWindow("topreviews" + id);
		var respond_review_window = new Array();

		this.ToggleReviewWindow = function()
		{
			share_this_window.IsOpen() ? share_this_window.Toggle(write_review_window.Toggle) : write_review_window.Toggle();
		}

		this.ToggleShareWindow = function()
		{
			write_review_window.IsOpen() ? write_review_window.Toggle(share_this_window.Toggle) : share_this_window.Toggle();
		}

		this.SaveReview = function()
		{
			write_review_window.Save(SaveSuccessHandler);
		}

		this.GetWriteReviewWindow = function()
		{
			return(write_review_window);
		}

		this.GetRespondReviewWindow = function(review_id)
		{
			if(typeof respond_review_window[review_id] == "undefined")
			{
				respond_review_window[review_id] = new RespondReviewWindow("review_response" + review_id, "Don't respond");
			}

			return(respond_review_window[review_id]);
		}

		function SaveSuccessHandler(review_id, review_html)
		{
			if(display_review_window.Exists())
			{
				// This is for stuff that's passed judgment
				var callback = function() {
					display_review_window.Add(review_id, review_html);

					if(display_review_window.NumReviews() == 1)
					{
						display_review_window.Open();
					}
				};
			}
			else
			{
				// This stuff is under judgment - reviews aren't visible yet
				var callback = function() {
					alert("Thanks for submitting your review!\nIf this submission passes judgment, you'll be able to see your review here soon.");
				};
			}

			write_review_window.Close(callback);
		}

		// This class extends WindowDisplayer
		function WriteReviewWindow(element_id, open_link_text)
		{
			var AJAX_SAVE_REVIEW_PAGE 				= "/ajax/newreview.php";
			var NO_RATING = "-";

			var save_animator = new HeaderAnimator(element_id, "Saving Review");

			// Necessary to complete the inheritance
			this.base = WindowDisplayer;
			this.base(element_id, null, open_link_text);		// Call our "parent" constructor

			var last_rating = NO_RATING;		// Use this to keep track of a user's previous rating
			var save_handler = null;	// Callback to use once the review is saved

			this.Save = function(onsave_callback)
			{
				// First make sure they've acknowledged the agreement (if they need to)
				var agree_element = document.getElementById(element_id + "_agreed");
				if((agree_element) && (!(agree_element.checked)))
				{
					alert("You must read and agree to the review-writing guidelines before continuing.");
					return;
				}

				// Now check for a subject
				var summary_element = document.getElementById(element_id + "_summary");
				if(summary_element.value.length == 0)
				{
					alert("You must enter a summary for your review.");
					summary_element.focus();
					return;
				}

				if(GetRating() == NO_RATING)
				{
					alert("Please give a star rating for your review.");
					return;
				}

				// Finally, check for a review body
				var body_element = document.getElementById(element_id + "_body");
				if(body_element.value.length == 0)
				{
					alert("You can't submit a blank review.");
					body_element.focus();
					return;
				}

				// OK, if we made it here, we have enough to submit this thing.
				save_handler = onsave_callback;

				// Start up our "Saving..." animation
				save_animator.Start();

				var params = new Array();
				params["agreed"] = 1;
				params["summary"] = summary_element.value;
				params["body"] = body_element.value;
				params["rating"] = GetRating();
				params["id"] = id;
				params["type"] = type;

				var ajax = new AjaxRequest(AJAX_SAVE_REVIEW_PAGE);
				ajax.Send(params, HandleReviewSave, HandleReviewSaveError);
			}

			this.SetRating = function(rating)
			{
				// First clear the old rating, if there was one
				ClearPreviousRating();

				// OK, set the text to reflect this new rating
				document.getElementById(element_id + "_rating_text").firstChild.data = rating;

				// Now set the appropriate star class
				document.getElementById(element_id + "_rating" + rating).className += " current-rating";

				// Keep track of this for next time and we're done
				last_rating = rating;
			}

			this.ClearRating = function()
			{
				ClearPreviousRating();
			}

			function HandleReviewSave(response)
			{
				// This means we saved the review successfully.  Let's display it!
				save_handler(response.GetField("review_id"), response.GetField("review_html"));
			}

			function HandleReviewSaveError()
			{
				save_animator.Stop();
			}

			function GetRating()
			{
				return(document.getElementById(element_id + "_rating_text").firstChild.data);
			}

			function ClearPreviousRating()
			{
				// Ditch our old rating (if there was one)
				if(last_rating != NO_RATING)
				{
					document.getElementById(element_id + "_rating" + last_rating).className = "star" + last_rating;
					document.getElementById(element_id + "_rating_text").firstChild.data = "0";
					last_rating = 0;
				}
			}
		}
		WriteReviewWindow.prototype = new WindowDisplayer;

		function DisplayReviewWindow(window_id)
		{
			var AVG_REVIEW_SCORE_PRECISION			= 1;		// Decimal places

			// Necessary to complete the inheritance
			this.base = WindowDisplayer;
			this.base(window_id);				// Call our "parent" constructor

			this.Exists = function()
			{
				return(document.getElementById("review_anchor"));
			}

			this.Add = function(review_id, review_html)
			{
				var review = DOMNodeFromHTML(review_html);
				var num_reviews = GetNumReviews();

				// We need to increment the # of reviews
				SetNumReviews(num_reviews + 1);

				// We also need to change the "Review this" link - gotta find the strong tag with the score
				var review_spans = review.getElementsByTagName("strong");
				var new_score = 0;

				for(var i=0; i<review_spans.length; i++)
				{
					if(review_spans[i].id == "review" + review_id + "_score")
					{
						new_score = parseInt(review_spans[i].firstChild.data);
						document.getElementById(window_id + "_link_container").innerHTML = "<strong>Your review: " + new_score + " / 10</strong>";
						break;
					}
				}

				// Now we update/show the review box
				if(num_reviews > 0)
				{
					// Update the current avg. score
					var current_avg_score = parseInt(document.getElementById("avg_review_score").firstChild.data);
					var new_avg_score = ((current_avg_score * num_reviews) + new_score) / (num_reviews + 1);
					document.getElementById("avg_review_score").firstChild.data = Round(new_avg_score, AVG_REVIEW_SCORE_PRECISION);

					// The review is still hidden at this point, when we add it...
					AddReviewHTML(review);

					// ... but now we'll display it.
					new Effect.Appear("review" + review_id, {duration: SHOW_REVIEW_EFFECT_DURATION});
				}
				else
				{
					// No reviews yet, so just set the average score to this review's score
					document.getElementById("avg_review_score").firstChild.data = Round(new_score, AVG_REVIEW_SCORE_PRECISION);

					// No reviews yet, so show the entire reviews box
					AddReviewHTML(review, true);
				}
			}

			this.NumReviews = function()
			{
				return(GetNumReviews());
			}

			function AddReviewHTML(review, is_hidden)
			{
				var anchor_div = document.getElementById("review_anchor");

				// Add this review to the box and slide it in
				if((typeof is_hidden == "undefined") || (!(is_hidden)))
				{
					// Stick on a <hr>
					var element_hr = Builder.node("div", {className: "hr"}, [
						Builder.node("hr")
					]);
					anchor_div.parentNode.insertBefore(element_hr, anchor_div.nextSibling);
				}
				else
				{
					// The HTML comes back hidden.  We need it to be visible.
					review.style.display = "block";
				}

				// Now put in the review in the review box
				anchor_div.parentNode.insertBefore(review, anchor_div.nextSibling);
			}

			function GetNumReviews()
			{
				var num_reviews = (document.getElementById("num_reviews_link").firstChild.data.match(/^([0-9,]+) reviews?$/))[1];
				num_reviews = num_reviews.replace(/,/, "");			// Beware the comma
				return(parseInt(num_reviews));
			}

			function SetNumReviews(num_reviews)
			{
				var num_reviews_element = document.getElementById("num_reviews_link");

				if(GetNumReviews() > 0)
				{
					// Link to reviews already exists, just place the number inside it
					num_reviews_element.firstChild.data = FormatNumber(num_reviews) + " reviews";
				}
				else
				{
					// No link to read reviews yet, so we gotta create it
					var reviews_url = "";
					if(type == TYPE_FLASH_PORTAL)
					{
						reviews_url = "/portal/reviews/" + id;
					}
					else if(type == TYPE_AUDIO_PORTAL)
					{
						reviews_url = "/audio/reviews/" + id;
					}

					var num_reviews_link = Builder.node("a", {id: "num_reviews_link", href: reviews_url}, "1 review");

					// Now swap out the old one and put in the new
					num_reviews_element.parentNode.replaceChild(num_reviews_link, num_reviews_element);
				}
			}
		}
		DisplayReviewWindow.prototype = new WindowDisplayer;
	}

	function SubmissionWhistleController(element_id)
	{
		var WHISTLE_MAX_CHARS					= 255;
		var AJAX_BLOW_WHISTLE_PAGE 				= "/ajax/blowwhistle.php";

		// Different reasons someone might blow the whistle
		var REASON_STOLEN						= 1;
		var REASON_MALICIOUS					= 2;
		var REASON_UNSUITABLE					= 3;

		// Necessary to complete the inheritance
		this.base = WindowDisplayer;
		this.base(element_id);				// Call our "parent" constructor

		var whistle_reason = null;
		var header_animator = new HeaderAnimator(element_id, "Blowing Whistle");

		this.BlowWhistle = function(reason)
		{
			if(whistle_reason == reason)
			{
				if(this.IsOpen())
				{
					// We're already open - close it
					StopCharsRemaining("blowwhistle_body");
					this.Close();
					SetReason(null);
				}
				else
				{
					// This happens when the review window is open and the user clicks a whistle link
					CheckCharsRemaining("blowwhistle_body", WHISTLE_MAX_CHARS);
					this.Open();
				}
			}
			else
			{
				CheckCharsRemaining("blowwhistle_body", WHISTLE_MAX_CHARS);

				if(!(this.IsOpen()))
				{
					this.Open();
				}

				SetReason(reason);
			}
		}

		this.Submit = function()
		{
			var reason_element = document.getElementById(element_id + "_body");
			if(reason_element.value == "")
			{
				alert("You must enter your reason for blowing the whistle.");
				return;
			}

			header_animator.Start();

			var params = new Array();
			params["id"] = id;
			params["reason"] = whistle_reason;
			params["reason_body"] = reason_element.value;

			var ajax = new AjaxRequest(AJAX_BLOW_WHISTLE_PAGE);
			ajax.Send(params, HandleWhistleSubmitSave, HandleWhistleSubmitError);
		}

		function HandleWhistleSubmitSave(response)
		{
			var callback = function() {
				header_animator.Stop();

				var msg = "Thanks! You have successfully blown the whistle on this submission.\n\n";
				msg += "You should still vote on this,  based on its quality. This way, if it turns out to be appropriate for the site,  your vote will be counted.\n\n";
				msg += "The site administrators will be checking into this shortly.";
				alert(msg);
			};

			submission_controller.GetWhistleController().Close(callback);
		}

		function HandleWhistleSubmitError()
		{
			header_animator.Stop();
		}

		function SetReason(reason)
		{
			whistle_reason = reason;

			if(reason != null)
			{
				var reason_text = "";

				if(reason == REASON_STOLEN)
				{
					reason_text = "stolen";
				}
				else if(reason == REASON_MALICIOUS)
				{
					reason_text = "malicious";
				}
				else if(reason == REASON_UNSUITABLE)
				{
					reason_text = "unsuitable";
				}

				document.getElementById("whistle_descriptor").innerHTML = reason_text;
			}
		}
	}
	SubmissionWhistleController.prototype = new WindowDisplayer;
}();
