/*
	AJAX Signup and EditSettings extension object for UrbanDaddy.
	Author: Giles Copp
	
	Based on jQuery entirely, this is an object designed to handle AJAX signup.
	Events are automatically assigned to links with class "requireSignup".
*/

AjaxEditSettings = function(){};
$j.extend( AjaxEditSettings.prototype,
{
	originalSettingsHTML: null,
	stageOneHTML: "",
	stageTwoHTML: "",
	currentErrorMessage: "",
	overlayDiv: null,
	contentDiv: null,
	formErrors: [],
	uiLockEnabled: false,
	savedpw: "",
	savedpw_retype: "",
	
	getUILock: function()
	{
		if (ajaxEditSettings.uiLockEnabled)
		{
			this.log( "Hit UI lock, no action taken." );
			return false;
		}
		
		// this.log( "Obtaining UI lock" );
		ajaxEditSettings.uiLockEnabled = true;
		return true;
	},
	
	isIE6: function()
	{
		return ($j.browser.msie && $j.browser.version < 7);
	},
	
	releaseUILock: function()
	{
		// this.log( "Releasing UI lock" );
		ajaxEditSettings.uiLockEnabled = false;
	},
	
	launch: function( str, layout )
	{
		this.log( "launch() with layout '"+layout+"'" );
		
		this.contentDiv = $j('#ajaxEditSettingsHolder');
		
		// if we can't find the contentDiv then drop straight out.
		if (this.contentDiv.attr('id')!='ajaxEditSettingsHolder') 
		{
			this.log( "Failed to find popup div - EXITING." );
			return;
		}
		
		UDRefresh.refreshAds();
		
		// fade in overlay
		this.overlayDiv = $j('#signup_overlay');
		this.overlayDiv.css('display', 'block').height($j(document).height()); 
		this.overlayDiv.css('width', $j(window).width()); 
		this.overlayDiv.css('height', $j(document).height()); 
		this.overlayDiv.fadeTo('slow', .5);
		this.overlayDiv.bind('click', this.close);
		
		this.repositionOverlay();
		this.contentDiv.css('visibility', 'visible');
		this.contentDiv.show();
		
		// save the HTML the first time
		if (this.originalSettingsHTML==null) 
		{
			this.log( "saving settings." );
			this.originalSettingsHTML = this.contentDiv.html();
			this.savedpw = $j("#password").val();
			this.savedpw_retype = $j("#password_retype").val();
		}
		
		// focus first field
		$j("#ajaxEditSettingsHolder :input:visible:enabled:first").focus();
		
		$j(window).bind( 'resize', this.repositionOverlay );
		this.attachStageOneEvents();
		
		if (str) 
		{
			this.formErrors = [ str ];
			this.echoFormErrors();
		}
		
		return false;
	},
	
	close: function(e)
	{
		ajaxEditSettings.log( "close()" );
	
		if (ajaxEditSettings.isIE6()) $j('#ajaxEditSettingsHolder').css('visibility', 'hidden');
		else $j('#ajaxEditSettingsHolder').hide();
		
		$j('#signup_overlay').fadeTo('slow', 0, function() { $j('#signup_overlay').hide(); })
		
		// we'll remove the search overlay too: this can get triggered by tab focusing.
		// only do it if the filter categories DIV isn't showing, to avoid killing searches which
		// are open.
		if (!searchInProgress()) kill_ajax();
		
		// remove the reposition handler or this can go crazy on IE6.
		$j(window).unbind( 'resize', ajaxEditSettings.repositionOverlay );
		
		ajaxEditSettings.releaseUILock();
		
		ajaxFormUtils.updateAccountBadges();
	},
	
	cancel: function(e)
	{
		ajaxEditSettings.log( "cancel(): restoring HTML to "+ajaxEditSettings.contentDiv );
		ajaxEditSettings.close(e);
		ajaxEditSettings.contentDiv.html( ajaxEditSettings.originalSettingsHTML );
		$j("#password").val( ajaxEditSettings.savedpw );
		$j("#password_retype").val( ajaxEditSettings.savedpw_retype );
		ajaxEditSettings.attachStageOneEvents();
		ajaxEditSettings.releaseUILock();
	},
	
	goToStageOne: function()
	{
		if (!ajaxEditSettings.getUILock()) return;
		ajaxEditSettings.contentDiv.html( ajaxEditSettings.originalSettingsHTML );
		$j("#password").val( ajaxEditSettings.savedpw );
		$j("#password_retype").val( ajaxEditSettings.savedpw_retype );
		ajaxEditSettings.attachStageOneEvents();
		ajaxEditSettings.releaseUILock();
		ajaxEditSettings.repositionOverlay();
	},
	
	complete: function(e)
	{
		ajaxEditSettings.log( "complete()" );
		ajaxEditSettings.close( e );
	},
	
	repositionOverlay: function()
	{
		// ajaxEditSettings.log( "repositionOverlay()" );
		
		// 'this' refers to the window object in this function, so we must refer to the AjaxEditSettings
		// instance during this function.
		var top = $j('html').scrollTop() + ($j(window).height() - ajaxEditSettings.contentDiv.height())/2 - 20;
		if (top < ($j('html').scrollTop() + 20)) top = $j('html').scrollTop() + 20;
		ajaxEditSettings.contentDiv.css('top', top);
		ajaxEditSettings.contentDiv.css('left', $j(window).width()/2 - ajaxEditSettings.contentDiv.width()/2); 
		ajaxEditSettings.overlayDiv.css('width', $j(window).width()); 
		ajaxEditSettings.overlayDiv.css('height', $j(document).height()); 
	},
	
	onStageOneCheckboxClicked: function( evt )
	{
		// ajaxEditSettings.log( "onStageOneCheckboxClicked()" );
		var checkedEditions = $j(".ajaxEditionsHolder :checkbox:checked");
		var perksCheckbox = $j(".ajaxPerksHolder :checkbox");
		if (checkedEditions.length > 0) 
		{
			if (evt.target!=perksCheckbox[0] && evt.target.checked) perksCheckbox.attr('checked', 'checked');
			perksCheckbox.removeAttr('disabled');
		}
		else perksCheckbox.attr('disabled', 'disabled');
		
		ajaxEditSettings.rebuildDefaultEditions( checkedEditions );
	},
	
	rebuildDefaultEditions: function( checkedEditions )
	{
		var currentDefaultEdition = $j('#is_default_edition').val();
		
		// then we'll set the html of the contents.
		var options = '<option value="0"></option>';
		
		$j.each( $j(".ajaxEditionsHolder :checkbox:checked"), function()
		{
			var input = $j(this);
			var label = $j(this).next();
			if (input.attr("checked"))
			{
				isSelected = (currentDefaultEdition==input.val()) ? ' selected="selected"' : '';
				options += '<option value="'+input.val()+'"'+isSelected+'>'+label.text()+'</option>';
			}
		});
		
		$j('#is_default_edition').html(options);
	},
	
	/* Events on the checkboxes which are specific to Stage One. */
	attachStageOneEvents: function()
	{
		// ajaxEditSettings.log( "attachStageOneEvents()" );
		$j('.ajaxCloseBox').bind('click', ajaxEditSettings.cancel);
		$j("#ajaxEditSettingsHolder input:checkbox").bind("click", ajaxEditSettings.onStageOneCheckboxClicked);
		$j("#editSettingsCancelButton").bind("click", ajaxEditSettings.cancel);
	},
	
	/**
	 * Submits the main Edit Settings page.
	 */
	submit: function( formID )
	{
		if (!ajaxEditSettings.getUILock()) return;
		this.log( "submit()" );
		
		this.clearFormErrors();
		var jForm = $j("#"+formID);
		var emailField = $j("#"+formID+" :input:visible:enabled:first");
		var checkedBoxes = $j("#"+formID+" input:checked");
		var perksIsChecked = $j(".myUDpopupPerksHolder input:checked");
		
		// check form sufficiency
		// if (checkedBoxes.length == perksIsChecked.length) this.formErrors.push( "You must select at least one edition to subscribe to." );
		if ($j("#password").val()=="" || $j("#password_retype").val()=="" || ($j("#password").val()!=$j("#password_retype").val())) this.formErrors.push( "The passwords you enter must match." );
		if (!ajaxFormUtils.emailRegex.test( emailField.val() )) this.formErrors.push( "Invalid email." );
		if (this.formErrors.length > 0) 
		{
			this.echoFormErrors();
			this.releaseUILock();
			return false;
		}
		
		// because we want to re-display this info if they open it again, we're going to re-save the HTML
		ajaxFormUtils.updateDOMWithFormFields( formID );
		
		// switch to spinner
		$j(".buttonsToHide").hide();
		$j(".ajaxEditSettingsSpinner").show();
		
		// issue request
		$j.post( jForm.attr("action"), ajaxFormUtils.cleanAndSerialize( formID ), this.processStageOneResponse );
		
		return false;
	},
	
	processStageOneResponse: function( data )
	{
		ajaxEditSettings.log( "processStageOneResponse()" );
		
		// if this is an error, then echo it on the form
		if (data.indexOf('ERROR:')===0) 
		{
			ajaxEditSettings.formErrors = [ data.slice(6) ];
			ajaxEditSettings.echoFormErrors();
			ajaxEditSettings.releaseUILock();
			$j(".buttonsToHide").show();
			$j(".ajaxEditSettingsSpinner").hide();
			return;
		}
		
		$j('#ajaxEditSettingsHolder').html( data );
		$j('.ajaxCloseBox').bind('click', ajaxEditSettings.cancel);
		ajaxEditSettings.repositionOverlay();
		ajaxEditSettings.releaseUILock();
	},
	
	clearFormErrors: function()
	{
		ajaxEditSettings.formErrors = [];
		$j(".formErrors").html( "" );
	},
	
	/**
	 * Submits the confirmation page: do you really want to unsubscribe?
	 */
	submitConfirmation: function( formID )
	{
		if (!ajaxEditSettings.getUILock()) return;
		this.log( "submitConfirmation()" );
		
		var jForm = $j("#"+formID);
		
		// switch to spinner
		$j(".buttonsToHide").hide();
		$j(".ajaxEditSettingsSpinner").show();
		
		// issue request
		$j.post( jForm.attr("action"), ajaxFormUtils.cleanAndSerialize( formID ), this.processConfirmationResponse );
		
		return false;
	},
	
	processConfirmationResponse: function( data )
	{
		ajaxEditSettings.log( "processConfirmationResponse()" );
		$j('#ajaxEditSettingsHolder').html( data );
		$j('.ajaxCloseBox').bind('click', ajaxEditSettings.cancel);
		ajaxEditSettings.repositionOverlay();
		ajaxEditSettings.releaseUILock();
	},
	
	echoFormErrors: function( fieldID )
	{
		if (fieldID==undefined) fieldID = "ajaxEditSettingsErrors";
	
		var errors = this.formErrors.join("<br />");
		
		if (errors==undefined) errors = "An unknown error has occurred.";
		
		this.log( "Error: "+errors );
		$j('#'+fieldID).html( errors );
	},
	
	log: function( s ) { Logger.log( "AjaxEditSettings :: " + s ); }
});
var ajaxEditSettings = new AjaxEditSettings();

AjaxSignup = function(){};
$j.extend( AjaxSignup.prototype,
{
	defaultErrorMessage: '<a class="notice" href="/myud#login">Already a member? Click here to log in.</a>',
	stageOneHTML: "",
	stageTwoHTML: "",
	stageThreeHTML: "",
	currentErrorMessage: "",
	overlayDiv: null,
	contentDiv: null,
	formErrors: [],
	uiLockEnabled: false,
	nextFormAction: "",
	accountCreatedCallback: null,
	signupClosedCallback: null,
	signupCompletedCallback: null,
	isSignedUp: false,
	currentProcess: null,
	process: { SIGNUP: "signup", INVITES: "invites" },
	
	getUILock: function()
	{
		if (ajaxSignup.uiLockEnabled)
		{
			this.log( "Hit UI lock, no action taken." );
			return false;
		}
		
		// this.log( "Obtaining UI lock" );
		ajaxSignup.uiLockEnabled = true;
		return true;
	},
	
	isIE6: function()
	{
		return ($j.browser.msie && $j.browser.version < 7);
	},
	
	isActive: function()
	{
		if (typeof(this.contentDiv)=='undefined' || this.contentDiv==null) return false;
		if (this.contentDiv.css('visibility')=='visible') return true;
		return false;
	},
	
	releaseUILock: function()
	{
		// this.log( "Releasing UI lock" );
		ajaxSignup.uiLockEnabled = false;
	},
	
	useNormalLayout: function()
	{
		this.log( "useNormalLayout()" );
		$j('#ajaxSignup_badgeSignup').show();
		$j('#ajaxSignup_badgePerks').hide();
	},
	
	usePerksLayout: function()
	{
		this.log( "usePerksLayout()" );
		$j('#ajaxSignup_badgeSignup').hide();
		$j('#ajaxSignup_badgePerks').show();
	},
	
	showPopup: function()
	{
		this.log( "showPopup(): process is "+this.currentProcess );
		
		switch( this.currentProcess )
		{
			case this.process.SIGNUP: var contentDivID = 'ajaxSignupHolder'; break;
			case this.process.INVITES: var contentDivID = 'ajaxSendInviteHolder'; break;
			default: return false; break;
		}
		this.contentDiv = $j('#'+contentDivID);
		
		// if we can't find the contentDiv then drop straight out.
		if (this.contentDiv.attr('id')!=contentDivID) 
		{
			this.log( "Failed to find popup div #"+contentDivID+", aborting." );
			return false;
		}
		
		// fade in overlay
		this.overlayDiv = $j('#signup_overlay');
		this.overlayDiv.css('display', 'block').height($j(document).height()); 
		this.overlayDiv.css('width', $j(window).width()); 
		this.overlayDiv.css('height', $j(document).height()); 
		this.overlayDiv.fadeTo('slow', .5);
		
		this.repositionOverlay();
		this.contentDiv.css('visibility', 'visible');
		this.contentDiv.show();
		
		// focus first field
		$j("#ajaxSignupHolder :input:visible:enabled:first").focus();
		
		$j(window).bind( 'resize', this.repositionOverlay );
		this.postProcessAjaxResponse();
		
		ajaxFormUtils.bindServiceRadioButtons( this.contentDiv );
		
		return true;
	},
	
	launch: function( str, layout )
	{
		this.log( "launch() with layout '"+layout+"'" );
		
		// if the user is signed up, then skip straight to the onUserLoggedIn function. the idea
		// here is that this function will call the post-flight callbacks, so we'll achieve the 
		// end goal of the signup without asking the user to go through the process.
		if (this.isSignedUp)
		{
			this.log( "User already signed up - going to post-flight processes." );
			if (ajaxSignup.signupCompletedCallback!=null) ajaxSignup.signupCompletedCallback();
			this.onUserLoggedIn();
			return false;
		}
		
		this.currentProcess = this.process.SIGNUP;
		
		var success = this.showPopup();
		
		if (success)
		{		
			this.attachStageOneEvents();
			
			// make layout choices
			switch( layout )
			{
				case "perks": this.usePerksLayout(); break;
				default: this.useNormalLayout(); break;
			}
			
			if (!str) str = this.defaultErrorMessage;
			this.formErrors = [ str ];
			this.echoFormErrors();
			
			// return false to suppress default link actions
			return false;
		}
	},
	
	launchInvites: function()
	{
		this.log( "launchInvites()" );
		
		this.currentProcess = this.process.INVITES;
		
		this.log( " - using process "+this.process+" / "+this.process.INVITES );
		
		var success = this.showPopup();
		
		// return false to suppress default link actions
		return false;
	},
	
	postProcessAjaxResponse: function()
	{
		// assign close box handlers
		$j('.ajaxSignupCloseBox').bind('click', this.cancelSignup);
		
		// if there's no completeCallback, hide bits that are class="myudOnly".
		if (ajaxSignup.signupCompletedCallback!=null) $j('.myudOnly').hide();
		
		// reposition
		this.repositionOverlay();
		this.releaseUILock();
	},
	
	cancelSignup: function(e)
	{
		ajaxSignup.log( "cancelSignup()" );
		ajaxSignup.close( e );
	},
	
	close: function(e)
	{
		ajaxSignup.log( "close()" );
	
		if (ajaxSignup.isIE6()) ajaxSignup.contentDiv.css('visibility', 'hidden');
		else  ajaxSignup.contentDiv.hide();
		
		$j('#signup_overlay').fadeTo('slow', 0, function() { $j('#signup_overlay').hide(); })
		
		// we'll remove the search overlay too: this can get triggered by tab focusing.
		// only do it if the filter categories DIV isn't showing, to avoid killing searches which
		// are open.
		if (!searchInProgress()) kill_ajax();
		
		// remove the reposition handler or this can go crazy on IE6.
		$j(window).unbind( 'resize', ajaxSignup.repositionOverlay );
		
		if (ajaxSignup.isSignedUp) ajaxFormUtils.updateAccountBadges();
	},
	
	complete: function(e)
	{
		ajaxSignup.log( "complete()" );
		ajaxSignup.close( e );
		
		if (ajaxSignup.signupCompletedCallback!=null) ajaxSignup.signupCompletedCallback();
		else top.location = "/myud";
	},
	
	repositionOverlay: function()
	{
		// ajaxSignup.log( "repositionOverlay()" );
		
		// 'this' refers to the window object in this function, so we must refer to the AjaxSignup
		// instance during this function.
		var top = $j('html').scrollTop() + ($j(window).height() - ajaxSignup.contentDiv.height())/2 - 20;
		if (top < ($j('html').scrollTop() + 20)) top = $j('html').scrollTop() + 20;
		ajaxSignup.contentDiv.css('top', top);
		ajaxSignup.contentDiv.css('left', $j(window).width()/2 - ajaxSignup.contentDiv.width()/2); 
		ajaxSignup.overlayDiv.css('width', $j(window).width()); 
		ajaxSignup.overlayDiv.css('height', $j(document).height()); 
	},
	
	onStageOneCheckboxClicked: function( evt )
	{
		var checkedBoxes = $j(".ajaxEditionsCheckboxHolder input:checked");
		var perksCheckbox = $j(".ajaxPerksCheckboxHolder input");
		if (checkedBoxes.length > 0)
		{
			if (evt.target!=perksCheckbox[0] && evt.target.checked) perksCheckbox.attr("checked", "checked");
			perksCheckbox.removeAttr("disabled");
		}
		else perksCheckbox.attr("disabled", "disabled");
	},
	
	/* Events on the checkboxes which are specific to Stage One. */
	attachStageOneEvents: function()
	{
		$j("#ajaxSignupHolder input").bind("click", this.onStageOneCheckboxClicked);
	},
	
	/**
	 * Submits the first stage of the AJAX signup, returns TRUE or FALSE.
	 */
	submitStageOne: function( formID )
	{
		if (!ajaxSignup.getUILock()) return;
		this.log( "submitStageOne()" );
		
		this.clearFormErrors();
		var jForm = $j("#"+formID);
		var emailField = $j("#ajaxSignupHolder #"+formID+" :input:visible:enabled:first");
		var checkedBoxes = $j("#ajaxSignupHolder #"+formID+" input:checked");
		var perksIsChecked = $j("#ajaxSignupHolder .Form1PerksHolder input:checked");
		
		// check form sufficiency
		if (checkedBoxes.length == perksIsChecked.length) this.formErrors.push( "You must select at least one edition to subscribe to." );
		if (!ajaxFormUtils.emailRegex.test( emailField.val() )) this.formErrors.push( "Invalid email." );
		if (this.formErrors.length > 0) 
		{
			this.echoFormErrors();
			this.releaseUILock();
			return false;
		}
		
		// switch to spinner
		$j(".buttonsToHide").hide();
		$j(".ajaxSignupSpinner").show();
		
		// issue request
		$j.post( jForm.attr("action"), ajaxFormUtils.cleanAndSerialize( formID ), this.processStageOneResponse );
		
		return false;
	},
	
	processStageOneResponse: function( data )
	{
		ajaxSignup.log( "processStageOneResponse()" );
		ajaxSignup.contentDiv.html( data );
		ajaxSignup.postProcessAjaxResponse();
	},
	
	submitStageTwo: function( formID )
	{
		if (!ajaxSignup.getUILock()) return;
		ajaxSignup.log( "submitStageTwo()" );
		
		this.clearFormErrors();
		var jForm = $j("#"+formID);
		
		// check passwords match
		// if ($j('#password').val()!==$j('#password_retype').val()) this.formErrors.push( "The new passwords you entered do not match." );
		if (this.formErrors.length > 0) 
		{
			this.echoFormErrors();
			this.releaseUILock();
			return false;
		}
		
		// switch to spinner
		$j(".buttonsToHide").hide();
		$j(".ajaxSignupSpinner").show();
		
		// issue request
		$j.post( jForm.attr("action"), ajaxFormUtils.cleanAndSerialize( formID ), this.processStageTwoResponse );
		
		return false;
	},
	
	skipStageTwo: function( nextURL )
	{
		if (!ajaxSignup.getUILock()) return;
		ajaxSignup.log( "skipStageTwo()" );
		
		// switch to spinner
		$j(".buttonsToHide").hide();
		$j(".ajaxSignupSpinner").show();
		
		$j.get( nextURL, '', this.processStageTwoResponse );
	},
	
	processStageTwoResponse: function( data )
	{
		ajaxSignup.log( "processStageTwoResponse()" );
		ajaxSignup.contentDiv.html( data );
		ajaxSignup.postProcessAjaxResponse();
		
		// assign listeners to the service buttons, so that we can assign the relevant email stem.
		ajaxFormUtils.bindServiceRadioButtons( ajaxSignup.contentDiv );
	},
	
	submitMiscEmails: function( formID )
	{
		if (!ajaxSignup.getUILock()) return;
		
		this.log( "submitMiscEmails( "+formID+" );" );
		
		var miscEmails = $j("#misc_emails").val();
		if (miscEmails=="") 
		{
			this.formErrors = [ "Please enter some email addresses to send invitations to." ];
			this.echoFormErrors( "miscEmailErrors" );
			$j('#misc_emails').blur();
			this.releaseUILock();
			return;
		}
		
		// add the referrer to the form.
		$j("#"+formID).append('<input type="hidden" name="referrer" value="'+ajaxSignup.getReferrer()+'" />');
		
		// switch to spinners
		$j(".buttonsToHide").hide();
		$j(".ajaxSignupSpinner").show();
		
		$j.post( $j("#"+formID).attr("action"), ajaxFormUtils.cleanAndSerialize( formID ), this.processMiscEmailsResponse );
	},
	
	// appends #signup to the URL, to indicate that despite the original referrer we are on the AJAX Signup
	getReferrer: function()
	{
		var r = self.location.href; 
		if (r.indexOf('#') >= 0) r = r.slice( 0, r.indexOf('#') );
		
		if (this.currentProcess==this.process.SIGNUP) return r + '#ajaxSignup';
		if (this.currentProcess==this.process.INVITES) return r + '#sendInvites';
		return r;
	},
	
	submitContactGrabber: function( formID, nextFormAction )
	{
		if (!ajaxSignup.getUILock()) return;
		
		this.log( "submitContactGrabber( "+formID+", "+nextFormAction+" )" );
		
		// switch to spinners
		$j(".buttonsToHide").hide();
		$j(".ajaxSignupSpinner").show();
		
		// save the next form action
		this.nextFormAction = nextFormAction;
		
		$j.post( $j("#"+formID).attr("action"), ajaxFormUtils.cleanAndSerialize( formID ), this.processContactGrabberResponse, "json" );
	},
	
	resetServiceImportFields: function( formID )
	{
		$j("#"+formID+" :input:visible:enabled:first").focus().select();
		// $j("#"+formID+" :text").val("");
		// $j("#"+formID+" :password").val("");
	},
	
	skipStageThree: function( nextURL )
	{
		if (!ajaxSignup.getUILock()) return;
		ajaxSignup.log( "skipStageThree()" );
		
		// switch to spinner
		$j(".buttonsToHide").hide();
		$j(".ajaxSignupSpinner").show();
		
		$j.get( nextURL, '', this.processStageThreeResponse );
	},
	
	skipStageFour: function( nextURL )
	{
		if (!ajaxSignup.getUILock()) return;
		ajaxSignup.log( "skipStageFour()" );
		
		// switch to spinner
		$j(".buttonsToHide").hide();
		$j(".ajaxSignupSpinner").show();
		
		$j.get( nextURL, '', this.processStageFourResponse );
	},
	
	processStageThreeResponse: function( data )
	{
		ajaxSignup.log( "processStageThreeResponse()" );
		ajaxSignup.contentDiv.html( data );
		ajaxSignup.postProcessAjaxResponse();
	},
	
	processStageFourResponse: function( data )
	{
		ajaxSignup.log( "processStageFourResponse()" );
		ajaxSignup.contentDiv.html( data );
		ajaxSignup.postProcessAjaxResponse();
	},
	
	processMiscEmailsResponse: function( data )
	{
		ajaxSignup.log( "processMiscEmailsResponse()" );
		
		// switch from spinners
		$j(".buttonsToHide").show();
		$j(".ajaxSignupSpinner").hide();
		
		// if this is an error, then echo it on the form
		if (data.indexOf('ERROR:')===0) 
		{
			ajaxSignup.formErrors = [ data.slice(6) ];
			ajaxSignup.echoFormErrors( "miscEmailErrors" );
			ajaxSignup.releaseUILock();
			return;
		}
		
		// otherwise, just use it as HTML
		ajaxSignup.contentDiv.html( data );
		ajaxSignup.postProcessAjaxResponse();
	},
	
	processContactGrabberResponse: function( data )
	{
		ajaxSignup.log( "processContactGrabberResponse(): data is "+data );
		
		// switch from spinners
		$j(".buttonsToHide").show();
		$j(".ajaxSignupSpinner").hide();
		
		ajaxSignup.clearFormErrors();
		if (data.result!=true) ajaxSignup.formErrors = [ data.data ];
		
		else
		{
			// store the info in the relevant fields.
			var inputFields = '<input type="hidden" name="sfContacts" id="temp" value="'+ajaxFormUtils.htmlspecialchars(JSON.stringify( data.data ))+'" />';
			$j("#serviceContacts").html( inputFields );
			
			// assuming we have no errors, submit this form to the next 
			if (ajaxSignup.formErrors.length==0)
			{
				$j.post( ajaxSignup.nextFormAction, ajaxFormUtils.cleanAndSerialize( 'sfContactGrabber_form' ), ajaxSignup.processImportedEmailsList );
				return;
			}
		}
		
		ajaxSignup.postProcessAjaxResponse();
		ajaxSignup.echoFormErrors( "ajaxContactGrabberErrors" );
	},
	
	processImportedEmailsList: function( data )
	{
		ajaxSignup.log( "processImportedEmailsList()" );
		ajaxSignup.contentDiv.html( data );
		ajaxSignup.postProcessAjaxResponse();
	},
	
	/**
	 * @function submitServiceEmails
	 * 
	 * Called from the final screen, where the user check / unchecks users and then submits the 
	 * final set.
	 */
	submitServiceEmails: function( formID )
	{
		if (!ajaxSignup.getUILock()) return;
		
		// ensure that one is selected
		var numChecked = $j("#"+formID+" :checkbox:checked").length;
		if (numChecked==0) 
		{
			ajaxSignup.formErrors = [ "You must select at least one contact in order to invite friends." ];
			ajaxSignup.echoFormErrors();
			ajaxSignup.releaseUILock();
			return;
		}
		
		// add the referrer to the form.
		$j("#"+formID).append('<input type="hidden" name="referrer" value="'+ajaxSignup.getReferrer()+'" />');
		
		// switch to spinners
		$j(".buttonsToHide").hide();
		$j(".ajaxSignupSpinner").show();
		
		$j.post( $j("#"+formID).attr("action"), ajaxFormUtils.cleanAndSerialize( formID ), this.processServiceEmailsResponse );
	},
	
	/**
	 * @function processServiceEmailsResponse
	 *
	 * Callback function from @submitServiceEmails. Symfony should return pure HTML to substitute
	 * into the system.
	 */
	processServiceEmailsResponse: function( data )
	{
		ajaxSignup.log( "processServiceEmailsResponse()" );
		ajaxSignup.contentDiv.html( data );
		ajaxSignup.postProcessAjaxResponse();
	},
	
	onUserLoggedIn: function()
	{
		ajaxSignup.log( "onUserLoggedIn()" );
		
		// update this, which will allow us to block further signup things.
		ajaxSignup.isSignedUp = true;
		
		if (ajaxSignup.accountCreatedCallback!=null) 
		{
			ajaxSignup.log( "executing accountCreatedCallback" );
			ajaxSignup.accountCreatedCallback();
		}
	},
	
	clearFormErrors: function()
	{
		ajaxSignup.formErrors = [];
		$j(".formErrors").html( "" );
	},
	
	echoFormErrors: function( fieldID )
	{
		var htmlStart = "";
		var htmlEnd = "";
	
		if (fieldID==undefined) fieldID = "ajaxSignupErrors";
		
		// for specific fields, nest the errors to add padding.
		else 
		{
			htmlStart = '<div class="formErrors">';
			htmlEnd = '</div>';
		}
	
		var errors = this.formErrors[0]; // .join("<br />");
		
		if (errors==undefined) errors = "An unknown error has occurred.";
		
		this.log( "Error: "+errors );
		$j('#'+fieldID).html( htmlStart + errors + htmlEnd );
	},
	
	log: function( s ) { Logger.log( "AjaxSignup :: " + s ); }
});
var ajaxSignup = new AjaxSignup();

var ajaxFormUtils = 
{
	emailRegex: /[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/i,

	/**
	 * @function inlineFieldLabel
	 *
	 * Nabbed from http://www.johnandcailin.com/blog/john/using-jquery-place-prompt-text-your-exposed-drupal-filters
	 * Allows fields to have prompt text inside the field which disappears when you click on them,
	 * and is removed before form submission.
	 * 
	 * Usage: on DOM ready: inlineFieldLabel('#signupForm', '#first_name_field', 'First Name');
	 */
	inlineFieldLabel: function (formid, inputid, label)
	{
		var fieldLabel = label;			// string to put in your text input
		var textInput = $j(inputid);	// your text input field
		
		/* add the field label css class to the form field and set the value */
		if (textInput.val()=="") textInput.addClass("intra-field-label").val(fieldLabel);
		
		/* remove the placeholder string when field gets focus */
		textInput.focus(function()
		{
			ajaxSignup.log( "focus for "+this.id );
			if($j(this).val() == fieldLabel ) $j(this).removeClass("intra-field-label").val("");
		});
		
		/* add the field label string when field looses focus */
		textInput.blur(function()
		{
			ajaxSignup.log( "blur for "+this.id );
			if($j(this).val() == "") $j(this).addClass("intra-field-label").val( fieldLabel );
		});
	},
	
	/**
	 * @function cleanAndSerialize
	 *
	 * This function cleans off intra-field labels before submitting the data.
	 */
	cleanAndSerialize: function( formID )
	{
		$j.each( $j("#"+formID+" :input"), function()
		{
			if ($j(this).hasClass("intra-field-label")) $j(this).val("");
		});
		return $j("#"+formID).serialize();
	},
	
	/** 
	 * @function updateDOMWithFormFields
	 *
	 * This is called when we're saving an HTML form which may have been modified by the user.
	 * Firefox's innerHTML does not retain user changes (unlike IE) so this corrects that.
	 */
	updateDOMWithFormFields: function( formID )
	{
		ajaxEditSettings.log( "updateDOMWithFormFields()" );
		$j("#"+formID+" input, #"+formID+" textarea, #"+formID+" select").each(function(i, o)
		{
			if (o.id!="")
			{
				var field = $(o.id);
				switch( field.type )
				{
					case "select":
					case "select-one":
						for (var i=0; i<field.options.length; i++) 
						{
							if (i == field.selectedIndex) field.options[field.selectedIndex].setAttribute("selected", "selected");
							else field.options[i].removeAttribute("selected");
						}
						break;
					
					case "text":
					case "textarea":
					case "password":
						field.setAttribute("value", field.value);
						break;
					
					case "checkbox":
					case "radio":
						if (field.checked) field.setAttribute("checked", "checked");
						else field.removeAttribute("checked");
						break;
				}
			}
		});
	},
	
	/** 
	 * @function updateAccountBadges
	 *
	 * Updates any account-specific sections in the page below.
	 */
	updateAccountBadges: function()
	{
		// update the top links and badge
		if ($j('#restTopLinks').length > 0) 		$j('#restTopLinks').load('/nav/loginNavbar');
		if ($j('#headSignUpHolder').length > 0) $j('#headSignUpHolder').load('/nav/myudBadgeHome');
		if ($j('#badgeHolder').length > 0) 			$j('#badgeHolder').load('/nav/myudBadge');
	},
	
	bindServiceRadioButtons: function( jQueryContext )
	{
		// bind the clicks
		$j(".serviceRadioButton", jQueryContext).bind('click', ajaxFormUtils.onServiceRadioButtonClick);
		
		// assign the currently selected one
		$j(".serviceRadioButton:checked", jQueryContext).each(function(){
			ajaxFormUtils.setSuffixForService( jQueryContext, this.id );
		});
	},
	
	onServiceRadioButtonClick: function()
	{
		ajaxFormUtils.setSuffixForService( $j(this).parent().parent(), this.id );
	},
	
	setSuffixForService: function( jQueryContext, serviceButtonId )
	{
		var label = $j('.service_username_suffix', jQueryContext);
		var stem = "";
		
		var idPartsArray = String(serviceButtonId).split("_");
		var serviceName = idPartsArray.pop();
		
		switch( serviceName )
		{
			case "Gmail":
				var stem = "@ gmail.com";
				break;
			case "Yahoo":
				var stem = "@ yahoo.com";
				break;
			case "AOL":
				var stem = "@ aol.com";
				break;
			case "Hotmail":
				var stem = "Please enter your full email address.";
				break;
		}
		
		label.html( stem );
	},
	
	/** 
	 * @function htmlspecialchars
	 *
	 * Analogous to PHP's function of the same name, this is designed for content which is going
	 * into textareas or input values.
	 */
	htmlspecialchars: function(str) 
	{
		if (typeof(str) != "string") return str;
	
		str = str.replace(/&/g, "&amp;"); /* must do &amp; first */
		str = str.replace(/"/g, "&quot;");
		str = str.replace(/'/g, "&#039;");
		str = str.replace(/</g, "&lt;");
		str = str.replace(/>/g, "&gt;");
		
		return str;
	}
}

$j(document).bind('ready', function () 
{
	$j('.requiresSignup').bind("click", function(e){ return ajaxSignup.launch(); });
	$j('.editSettings').bind("click", function(e){ return ajaxEditSettings.launch(); });
});

/*
	JSON parser / stringifier for data exchange.
    http://www.JSON.org/json2.js
    2008-11-19
*/
if (!this.JSON) {
    JSON = {};
}
(function () {
    function f(n) {
        
        return n < 10 ? '0' + n : n;
    }
    if (typeof Date.prototype.toJSON !== 'function') {
        Date.prototype.toJSON = function (key) {
            return this.getUTCFullYear()   + '-' +
                 f(this.getUTCMonth() + 1) + '-' +
                 f(this.getUTCDate())      + 'T' +
                 f(this.getUTCHours())     + ':' +
                 f(this.getUTCMinutes())   + ':' +
                 f(this.getUTCSeconds())   + 'Z';
        };
        String.prototype.toJSON =
        Number.prototype.toJSON =
        Boolean.prototype.toJSON = function (key) {
            return this.valueOf();
        };
    }
    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;
    function quote(string) {
        escapable.lastIndex = 0;
        return escapable.test(string) ?
            '"' + string.replace(escapable, function (a) {
                var c = meta[a];
                return typeof c === 'string' ? c :
                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"' :
            '"' + string + '"';
    }
    function str(key, holder) {
        var i,          
            k,          
            v,          
            length,
            mind = gap,
            partial,
            value = holder[key];
        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }
        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }
        switch (typeof value) {
        case 'string':
            return quote(value);
        case 'number':
            return isFinite(value) ? String(value) : 'null';
        case 'boolean':
        case 'null':
            return String(value);
        case 'object':
            if (!value) {
                return 'null';
            }
            gap += indent;
            partial = [];
            if (Object.prototype.toString.apply(value) === '[object Array]') {
                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }
                v = partial.length === 0 ? '[]' :
                    gap ? '[\n' + gap +
                            partial.join(',\n' + gap) + '\n' +
                                mind + ']' :
                          '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }
            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    k = rep[i];
                    if (typeof k === 'string') {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {
                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }
            v = partial.length === 0 ? '{}' :
                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
                        mind + '}' : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }
    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {
            var i;
            gap = '';
            indent = '';
            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }
            } else if (typeof space === 'string') {
                indent = space;
            }
            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                     typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }
            return str('', {'': value});
        };
    }
    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {
            var j;
            function walk(holder, key) {
                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }
            if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
                j = eval('(' + text + ')');
                return typeof reviver === 'function' ?
                    walk({'': j}, '') : j;
            }
            throw new SyntaxError('JSON.parse');
        };
    }
})();
