function updateIndexTab(id)
{
	updateListSelected('home_list', id, "i_");
    for (var i = 1; i < 4; i++)
	{
		$("#tab_"+i).hide();
	}
	$("#tab_"+id).show();
}

function getTimeZoneOffset()
{
	var d = new Date();
	var localTime = d.getTime();
	var localOffset = d.getTimezoneOffset();	// return value in minutes by default
	return (localOffset * 60);
}

// tests if a character is in the list of "singles" or in one of the "ranges"
function charIsOneOf($theCharCode, $singles, $ranges)
{	if($theCharCode === 0)
	{	return false;
	}
	
	$singlesLen = $singles.length;
	for($n=0; $n<$singlesLen; $n+=1)
	{	if($theCharCode === $singles.charCodeAt($n))
		{	return true;
		}
	}
	
	$numberOfRanges = Math.floor($ranges.length/2);
	for($n=0; $n<$numberOfRanges; $n+=1)
	{	if( $ranges.charCodeAt(0+$n*2)<=$theCharCode && $theCharCode<= $ranges.charCodeAt(1+$n*2) )
		{	return true;
		}
	}
	return false;	
}

function isDigit(character)	// returns true if the character is a digit
{	if('0' <= character&&character <= '9')
		return true;
	else
		return false;
}
// NOTE THAT IF THIS IS CHANGED, ITS CORRESPONDING VALIDATION FUNCTION IN THE PHP SHOULD BE CHANGED AS WELL
// The php version should be in user_lib (as of this writing)
function validZip(allegedZipCode)	// returns true if the input is a 5-digit number
{	if(allegedZipCode != undefined && allegedZipCode.length == 5)
	{	for(n=0; n<5; n++)
		{	if(false == isDigit(allegedZipCode.charAt(n)))
			{	return false;
			}
		}
		return true;
	}
	else
		return false;
}
// NOTE THAT IF THIS IS CHANGED, ITS CORRESPONDING VALIDATION FUNCTION IN THE PHP SHOULD BE CHANGED AS WELL
// The php version should be in user_lib (as of this writing)
function validEmail(allegedEmail)	// returns true if the email is valid
{	if
	(	allegedEmail.length <= 255
		&& allegedEmail.lastIndexOf('@') == allegedEmail.indexOf('@')	// if there is only one instance of '@'
		&& allegedEmail.lastIndexOf('@') < allegedEmail.lastIndexOf('.')
	)
	{	for(var n=0; n<allegedEmail.length; n++)
		{	var c = allegedEmail.charAt(n);
			if(	'0' <= c&&c <= '9' || 'a' <= c&&c <= 'z' || 'A' <= c&&c <= 'Z' || 
				in_array(c,['!', '#', '$', '%', '&', '\'', '*', '_', '-', '+', '=', '?', '^', '`', '{', '}', '|', '@', '.', '~', '/']) )
			{}else
			{	return false;
			}
		}
	}else
	{	return false;
	}
	return true;
}

// returns:
// 1 on success
// -1 on password mismatch
// -2 on password too short
// -3 on password mismatch *and* too short
// NOTE THAT IF THIS IS CHANGED, ITS CORRESPONDING VALIDATION FUNCTION IN THE PHP SHOULD BE CHANGED AS WELL
// The php version should be in user_lib (as of this writing)
function validPassword(allegedPassword, allegedPassword2)
{	if(allegedPassword != allegedPassword2 && allegedPassword.length < 5)
	{	return -3;
	}else if(allegedPassword != allegedPassword2)
	{	return -1;
	}else if(allegedPassword.length < 5)
	{	return -2;
	}
	else
	{	return 1;
	}
}

// validates input settings data and returns errors to the correct IDs if there are some
// inputs has the possible keys: emailID, zipID, passID, pass2ID
// errIDs has the possible keys: emailErr, zipErr, passErr
function validateSettings(inputs, errIDs, emailCanBeBlank)
{	if(emailCanBeBlank==undefined){emailCanBeBlank=false;}	// emailCanBeBlank defaults to false
	
						
	okInput = true;	// assume good input - until proven otherwise

	if(inputs["emailID"] != undefined)
	{	$(errIDs["emailErr"]).html('');	// clear current error messag
		theEmail = $(inputs["emailID"]).val();
		if(validEmail(theEmail) == false && !(emailCanBeBlank==true && theEmail==""))
		{	$(errIDs["emailErr"]).html('<br><span style="color:red;">Email is not properly formatted</span>');
			okInput = false;
		}
	}
	if(inputs["zipID"] != undefined)
	{	$(errIDs["zipErr"]).html('');	// clear current error messag
		if(false == validZip($(inputs["zipID"]).val()))
		{	$(errIDs["zipErr"]).html('<br><span style="color:red;">Invalid zipcode.</span>');
			okInput = false;
		}
	}
	if(inputs["passID"] != undefined)
	{	// check for errors
		$(errIDs["passErr"]).html('');	// clear current error message
		passwordReturn = validPassword($(inputs["passID"]).val(), $(inputs["pass2ID"]).val())
		if(passwordReturn == -1)
		{	errorHTML = '<br><span style="color:red;">Passwords don\'t match.</span>';
			okInput = false;
		}else if(passwordReturn == -2)
		{	errorHTML = '<br><span style="color:red;">Password must be at least five characters long.</span>';
			okInput = false;
		}else if(passwordReturn == -3)
		{	errorHTML = '<br><span style="color:red;">Passwords do not match. Passwords also must be at least five characters long.</span>';
			okInput = false;
		}else
		{	errorHTML = '';
		}
		$(errIDs["passErr"]).html(errorHTML);
	}
	
	return okInput;
}

// concatenation of associative arrays
// values in ass2 (the second associative array) take precedence
function unionize(ass1, ass2)
{	ass3 = {}
	for(e in ass1)
	{	ass3[e] = ass1[e];
	}
	for(e in ass2)
	{	ass3[e] = ass2[e];
	}
	return ass3;
}

// this class (listPageUpdate is a javascript class) hooks up whats needed for updating lists of paged data - including the page control buttons
// controlType_in can be:
//		* "viewMore" (gives a "view more" button on the bottom)
//		* "forwardBack" (gives forward and back buttons on top and bottom)
// controlID is the base-ID that should be used to create page control buttons (no other ID should use this base ideally)
// next page is kept track of
// params_in shouldn't include page data
// the ajax page (callURL) should take in the post parameters 'startItem', 'endItem', 'numberDisplayed', and 'lastItemID'
// the ajax page should return:
//		"B,N,etc" 
//		where 	* B is either '0' or '1' as the first character - this indicates whether there are more pages or not
//				* N is an ID number (the last element ID displayed) - this allows subsequent pages to make sure the same entry isn't displayed more than once
//				* etc is the rest of the page 
function listPageUpdate(controlType_in, callURL_in, params_in, numPerPage_in, dumpSpaceID_in, controlID_in, controlWrapperA_in, controlWrapperB_in)
{	var self = this;	// reference to this object (otherwise I wouldn't be able to refer to this object from member functions)
	
	var controlType = controlType_in;			// stores what type of page controls are used
	
	self.callURL = callURL_in;					// public variable
	var numPerPage = numPerPage_in;
	self.params = params_in;					// public variable
	var dumpSpaceID = dumpSpaceID_in;
	var controlID = controlID_in;
	var controlWrapperA = controlWrapperA_in;
	var controlWrapperB = controlWrapperB_in;
	
	self.nextItem = 0;	// start with item 0	// public variable
	var numberDisplayed = 0;		// keeps track of how many items have been displayed (this has syncing problems, so it won't work for precise needs.. [eg it assumes that all items are written every time - even on the last page, which may not contain a full set of items])
	var lastReceivedID = false;		// makes sure that items aren't being written twice // false stands for unset
	
	$(document).ready(function()		// constructor
	{	update(0, numPerPage-1, "replace");
	});

	// this function should be called if the actual list in the database has lost some rows before self.nextItem (and thus the paging control needs to compensate)
	self.rollup = function(number)				// public privilaged function
	{	self.nextItem -= number;
	} 
	
	function IDsBase(uniqueNumber)
	{	return controlID+numberDisplayed+'_'+uniqueNumber;
	}
	
	function viewMoreControl(isMore, uniqueNumber)	//	generates a "view more" button
	{	if(isMore == false)
		{	return "";
		}else
		{	return controlWrapperA+'<div class="link" id="'+IDsBase(uniqueNumber)+'">'+"View&nbsp;More"+'</div>'+controlWrapperB;
		}
	}
	function viewMoreAfterDump(isMore, uniqueNumber)	
	{	if(isMore)
		{	var buttonID = "#"+IDsBase(uniqueNumber);
			$(buttonID).bind("click", function()	// bind control mechanism (and make sure page control button ("view more") is rebound after data is returned)
			{	$(buttonID).replaceWith("");		// remove button on click (make sure multiple clicks don't result in multiple data)
				update(self.nextItem, self.nextItem+numPerPage-1, "append");	// print next page
			});
		}	
	}
	
	function forwardBackControl(isMore, uniqueNumber)	//	generates a "view more" button
	{	renderBackButton = self.nextItem - 2*numPerPage >= 0;
		renderForwardButton = isMore;
		
		if(renderBackButton || renderForwardButton)	
		{	if(renderBackButton)
			{	backButton = '<span class="link" id="back_'+IDsBase(uniqueNumber)+'">'+"Previous"+'</span> / ';
			}else
			{	backButton = "";
			}
			
			middleSection = "Page "+Math.floor(self.nextItem/numPerPage);
			
			if(renderForwardButton)
			{	forwardButton = ' / <span class="link" id="forward_'+IDsBase(uniqueNumber)+'">'+"Next"+'</span>';
			}else
			{	forwardButton = "";
			}
		}else
		{	return "";
		}
		
		
		return 	controlWrapperA
					+backButton
					+middleSection
					+forwardButton
				+controlWrapperB;
	}
	function forwardBackAfterDump(isMore)	
	{	function forwardButtonSetup(uniqueNumber)
		{	forwardButtonID = "#forward_"+IDsBase(uniqueNumber);
			$(forwardButtonID).bind("click", function()	// bind control mechanism (and make sure page control button ("view more") is rebound after data is returned)
			{	$(forwardButtonID).replaceWith("");	// remove button on click (make sure multiple clicks don't result in multiple data)
				update(self.nextItem, self.nextItem+numPerPage-1, "replace");	// print next page
			});
		}
		function backButtonSetup(uniqueNumber)
		{	backButtonID = "#back_"+IDsBase(uniqueNumber);	
			$(backButtonID).bind("click", function()	// bind control mechanism (and make sure page control button ("view more") is rebound after data is returned)
			{	$(backButtonID).replaceWith("");	// remove button on click (make sure multiple clicks don't result in multiple data)
				update(self.nextItem - 2*numPerPage, self.nextItem - numPerPage-1, "replace");	// print last page
			});
		}
		
		if(isMore)
		{	//forwardButtonSetup(1);	// top
			forwardButtonSetup(2);		// bottom
		}
		
		if(self.nextItem - 2*numPerPage >= 0)	
		{	//backButtonSetup(1);		// top
			backButtonSetup(2);			// bottom
		}
	}
	
	function update(firstItem, lastItem, appendORreplace) 
	{	if(controlType == "viewMore")
		{	topControls 	= function(){return "";};
			bottomControls 	= viewMoreControl;
			afterDump 		= viewMoreAfterDump;
		}else	// make forwardBack the default // if(controlType == "forwardBack")
		{	topControls 	= function(){return "";};
			bottomControls 	= forwardBackControl;
			afterDump 		= forwardBackAfterDump;
		}
		
		self.nextItem = lastItem+1;
		numberDisplayed += lastItem-firstItem;
		
		postDataPages(appendORreplace, topControls, bottomControls, afterDump, firstItem, lastItem);
	}
	
	function postDataPages(appendORreplace, dumpSpaceControlGenerator_top, dumpSpaceControlGenerator_bottom, afterDump, startItem, endItem)
	{	$("#"+dumpSpaceID).html(loadingAnim());
		
		$.ajax
		({	"type": "POST", cache: false, "url": self.callURL, 
			"data": unionize(self.params, {"lastItemID":lastReceivedID, "startItem":startItem, "endItem": endItem, "numberDisplayed":numberDisplayed}),
			"success": function(data)
			{	if(data.charAt(0) == 1)
				{	isMore = true;
				}else
				{	isMore = false;
				}
				
				n=2;	// skip the first digit, and the expected comma
				IDnumber = "";
				while(isDigit(data.charAt(n)))		// get the last ID displayed from the returned data
				{	IDnumber += String(data.charAt(n));
					n+=1;
				}
				// data[n] should be another comma right now
				lastReceivedID = IDnumber;
				
				if(appendORreplace == "append")
				{	$("#"+dumpSpaceID).append(dumpSpaceControlGenerator_top(isMore, 1) + data.substring(n+1) + dumpSpaceControlGenerator_bottom(isMore, 2));
				}else
				{	$("#"+dumpSpaceID).html(dumpSpaceControlGenerator_top(isMore, 1) + data.substring(n+1) + dumpSpaceControlGenerator_bottom(isMore, 2));
				}
				afterDump(isMore);
			}
		});
	}	
}

function updateFeedGroup(gid, fid)
{
    updateListSelected('group_list', gid, "g_");
    updateFeed(gid,fid);
}

function updateFeedType(fid, q, page)
{
    updateListSelected('feed_type', fid, "f_");
    showMainActionFeed(fid, q, page);
    try {
	$("#search_type").val(fid);
    }
    catch(e)
    {
	
    }
}

function updateProfileFeedType(fid, user, page)
{
    updateListSelected('feed_type', fid, "f_");
    showProfileActionFeed(fid, user);
}

function updateFeedTypeGroup(fid, gid, page)
{
	updateListSelected('feed_type', fid, "f_");
	updateFeed(gid, fid, page);
}

function loadingAnim(picOptions, additionalStyles)
{	return '<div style="text-align:center;"'+additionalStyles+'><br><br><img '+picOptions+' src="/media/loading2.gif"></div>';
}
function loadingAnimRaw(picOptions)
{	return '<img '+picOptions+' src="/media/loading2.gif">';
}

// the parameter 'page' is now ignored
// inputing -1 for fid will keep the feed number the same
function updateFeed(gid, fid, page)
{	if(updateFeed.currentSelectedFID == undefined)
	{	updateFeed.currentSelectedFID = 1;
	}
	
	if(fid == -1 || fid == undefined)
	{	fid = updateFeed.currentSelectedFID;
	}else
	{	updateFeed.currentSelectedFID = fid;
	}
	
	document.getElementById("news_feed_content").innerHTML = loadingAnim();
	$.ajax({
        type: "GET",
        cache: false,
        url: "/ajax/getMainFeed.php",
        data: "gid="+gid+"&fid="+fid+"&page="+page,
        success: function(msg){
            //document.getElementById("news_feed_content").innerHTML = msg;
        	$("#action_feed").html(msg);
            updateListSelected('feed_type', fid, "f_");
           
			$(".overlayButton").overlay({ 
                    // start exposing when overlay finishes loading (much smoother than doing this while its loading)
                    onLoad: function()  
					{	// this line does the magic. it makes the background image sit on top of the mask 
						this.getBackgroundImage().expose({color: '#fff'}); 
					},  
                     
                    // when overlay is closed take the expose instance and close it as well 
                    onClose: function() { 
                        $.expose.close(); 
                    } 
                     
                });
			
        }
     });
}

function closeOverlay()
{
	var api = $.overlay().close();
}

function getSelectedGroup()
{
    var groups = document.getElementById("group_list").getElementsByTagName('li');
    
    for (var anchor = 0; anchor < groups.length; anchor++)
    {
        
        var item = groups[anchor];
        if (document.getElementById(item.id).className == 'selected'){
            var group = item.id.replace("g_", "");
            return group;
        }
    }
}

function updateListSelected(main_element, selected_id, prefix)
{
    var groups = document.getElementById(main_element).getElementsByTagName('li');
    
    for (var anchor = 0; anchor < groups.length; anchor++)
    {
        var item = groups[anchor];
        if ( item.id != (prefix+selected_id) )
        {
            $("#"+item.id).removeClass("selected");
            document.getElementById(item.id).setAttribute("className", "");		// What does this do???
            
        }
        else
        {
            $("#"+item.id).addClass("selected");
            document.getElementById(item.id).setAttribute("className", "selected");	// and this
        }
    }
}
/*
$(document).ready(function(){
        $('.down-list').width($('.dropdown-menu').width()-2);
        $('.dropdown-menu').hover(
          function () {
            $('.menu-first', this).addClass('slide-down');
            $('.down-list', this).slideDown(100);
          },
          function () {
            obj = this;
            $('.down-list', this).slideUp(100, function(){ $('.menu-first', obj).removeClass('slide-down'); });
          }
        );
});
*/



function showGroupLink(gl)
{
	if (gl != '0')
	{
		document.getElementById("gl_"+gl).style.display = "block";
	}
}
function hideGroupLink(gl)
{
	if (gl != '0')
	{
		document.getElementById("gl_"+gl).style.display = "none";
	}
}

function commitToAction()
{
		$("#overlay_content").hide('fast');// = '';
		$("#success").show('fast');
}

function showActionDetails(id)
{
	//var more = document.getElementById("more_"+id);
	$("#more_"+id).toggle('fast');
}

// unused args are ignored (but must be in the funtion call
function dateUtilityCall(func, arg1, arg2, callback)
{	$.ajax
	({	type: "GET",
		cache: true,
		url: "/ajax/dateUtility.php",
		data: "func="+func+"&arg1="+escape(arg1)+"&arg2="+escape(arg2),
		success: callback
	});
}

/*
function numberExtendLeadingZerosToHundredsPlace(number)
{	if(number < 10)
	{	return String("00") + number;
	}else if(number < 100)
	{	return String("0") + number;
	}else
	{	return number;
	}
}

function number_format(number)
{	decimal = String(number).indexOf('.');
	
	for(n=decimal-3; n<String(number).length; n-=3)	// loop through and place a comma every three characters
	{	c = String(number).charAt(n);
		if('0' <= c&&c <= '9' || 'a' <= c&&c <= 'z' || 'A' <= c&&c <= 'z' || c=='_' || c=='@' || c=='.')
		{}else
		{	return false;
		}
	}	
	
	flooredNumber = Math.floor(number);
	if(flooredNumber >= 1000)
	{	thousandsPlace = Math.floor(flooredNumber/1000);
		thousandsFactor = floored
		return number_format(thousandsPlace)+","+numberExtendLeadingZerosToHundredsPlace(number-1000*thousandsPlace);
	}else
	{	return number
	}
}
*/

// ugly function grabbed from online
function number_format(nStr)
{	nStr += '';
	x = nStr.split('.');
	x1 = x[0];
	x2 = x.length > 1 ? '.' + x[1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) 
	{	x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	return x1 + x2;
}

// determines the number of days in that month for a given year
function daysInMonth(iMonth, iYear)
{	return 32 - new Date(iYear, iMonth, 32).getDate();
}


function facebookLogin()
{	window.location = "fb_login.php";

	/*var page = location.href.substring(0, location.href.lastIndexOf('\/'));
	var uri = new Object();
	getURL(uri);
	if(true)//uri.page == 'index' || uri.page == 'index.php')
	{	
	}*/
}

function renderFBLoginButton(id, callback, modifyUser, userType)
{	if(callback == undefined)
	{	callback = function(successType)	 // default value
		{	if(successType == "alreadyLoggedIn")
			{	window.location = "home.php";
			}else if(successType == "noob")
			{	window.location = "home.php?fbnoob";
			}else
			{	window.location = "home.php";
			}
		};	
	}
	if(modifyUser == undefined)	// default value
	{	modifyUser = 0;
	}
	if(userType == undefined)	// default value
	{	userType = 0;	// USER_TYPE_NORMAL
	}
	
	$("#"+id).html
	(	'<img style="cursor:pointer;" src="http://static.ak.fbcdn.net/images/fbconnect/login-buttons/connect_light_medium_long.gif" alt="Connect"/>'
	);
	
	$("#"+id).click(function()
	{	//alert("FB button clicked");
		FB.Connect.requireSession(function()
		{	//alert("FB.Connect.requireSession");
			$.ajax
			({	"type": "GET", cache: false, "url": "fb_login.php", 
				"data": {"ajaxCallback":true, "modifyUser": modifyUser, "userType":userType},
				"success": function(data)
				{	callback(data);
				}
			});
		});
	});
}

// javascript class to represent a form (its really simple, but just so namespaces aren't polluted)
function signInForm(signInFormElementID_in, fbLoginID_in, windowLocation_redirect_in, writeFBLoginButton, loginButtonName, encryptionKeyset_in)
{	var self = this;	// reference to this object (otherwise I wouldn't be able to refer to this object from member functions)

	self.theFormType = "";					// public variable
	var keyID = signInFormElementID_in;		// this is the ID of the element that should contain the log in form (it is also used as a prefix for the IDs of all the elements written dynamically from this class)
	var fbLoginID = fbLoginID_in;
	var windowLocation_redirect = windowLocation_redirect_in;
	self.passwordFloater = undefined;
	
	var encryptionKeyset = encryptionKeyset_in;
	
	var loginButtonHTML = '<input type="submit" name="login" class="input_button" value="'+loginButtonName+'">';
		
	// this is basically a constructor
	$(document).ready(function()		// opens the login window for facebook, and then runs facebookLogin on completion
	{	if(writeFBLoginButton)
		{	renderFBLoginButton(fbLoginID);
			theOrText = '';//'<td> or </td>';
		}else
		{	theOrText = '';
		}	
		
		$("#"+keyID).html
		(	'<form method="post" action="" id="'+keyID+'_formElement">'+
				'<table>'+
					'<tr style="font-size:7pt;">'+
						'<td></td>'+
						'<td></td>'+
						'<td class="link" id="'+keyID+'_rememberPasswordButton" style="text-align:right;"></td>'+
						'<td></td>'+
						'<td class="link" id="'+keyID+'_forgotPasswordButton" style="text-align:right;"></td>'+
						'<td></td>'+
						'<td></td>'+
						'<td></td>'+
					'</tr>'+
					'<tr><td id="'+keyID+'_errorBox"></td>'+
						'<td id="'+keyID+'_emailSpanLabel"></td> <td><input type="text" title="Email" id="'+keyID+'_email" name="email" class="input_text_short" style="width:170px;">&nbsp;</td>'+
						'<td id="'+keyID+'_passwordSpanLabel"></td> <td id="'+keyID+'_passwordSpan"></td>'+
						'<td id="'+keyID+'_lockpic"></td>'+
						'<td id="'+keyID+'_loginButton"></td>'+
						theOrText+
					'</tr>'+
				'</table>'+
			'</form>'
		);
		
		setupTitle("#"+keyID+"_email");
		
		$("#"+keyID+"_formElement").submit(function()
		{	return submitTheForm();
		});
		
		$("#"+keyID+"_rememberPasswordButton").click(function()		// hook up "remembered password" button, which sets up login submission
		{	if(self.theFormType != "login")
			{	$(""+keyID+"_#errorBox").html("");		// clear error box on toggle
				
				$("#"+keyID+"_forgotPasswordButton").html("Forgot your password?");
				$("#"+keyID+"_rememberPasswordButton").html("<span id='"+keyID+"switchToFBLogin'>Sign in with Facebook</span>");
				$("#"+keyID+"_loginButton").html(loginButtonHTML);
				$("#"+keyID+"_emailSpanLabel").html("");
				$("#"+keyID+"_passwordSpan").html('<input type="password" title="Password" id="'+keyID+'_password" name="password" class="input_text_short" style="width:100px;">&nbsp;');
				$("#"+keyID+"_lockpic").html('<img src="/media/lock.png">');
				$("#"+keyID+"_passwordSpanLabel").html("");
				self.theFormType = "login";
				
				self.passwordFloater = new setupTitleFloater(keyID+'_password');
				//setupTitle("#"+keyID+'_password');
				//passwordObj = new setupPasswordInput(keyID+'_password');
				
				$("#"+keyID+"switchToFBLogin").click(function()
				{	$("#"+keyID+"_FBsignindiv").show();
					$("#"+keyID).hide();
				});
			}
		});
		
		$("#"+keyID+"_forgotPasswordButton").click(function()		// hook up "forgot password" button, which sets up password emailing
		{	if(self.theFormType == "login")
			{	$("#"+keyID+"_errorBox").html("");		// clear error box on toggle
				
				$("#"+keyID+"_rememberPasswordButton").html("Remembered your password?");
				$("#"+keyID+"_forgotPasswordButton").html("");
				$("#"+keyID+"_loginButton").html('<input type="submit" name="login" class="input_button" value="Email Password">');
				$("#"+keyID+"_emailSpanLabel").html("Type In Your Email: ");
				$("#"+keyID+"_passwordSpan").html("");
				$("#"+keyID+"_lockpic").html("");
				$("#"+keyID+"_passwordSpanLabel").html("");
				self.theFormType = "forgot";
			}
		});
		
		$("#"+keyID+"_rememberPasswordButton").click();		// set up login by throwing the click event for this id
	});

	function submitTheForm()
	{	$("#"+keyID+"_errorBox").html("");		// clear error box on submission
		
		if(self.theFormType == "login")
		{	submitLoginInfo();
		}else
		{	emailPasswordRequest();
		}
		return false;
	}
	
	function submitLoginInfo() 
	{	var keys = new RSAKeys(encryptionKeyset);
		$("#"+keyID+"_loginButton").html(loadingAnimRaw('style="height:30px;"'));
		$.ajax
		({	"type":"POST", "url":"/login.php", 		// post login data
			"data":
			{	"emailPassCombo": keys.enc($("#"+keyID+"_email").val() +" "+ $("#"+keyID+'_password').val()),
				//"email": keys.enc($("#"+keyID+"_email").val()),
				//"password": keys.enc($("#"+keyID+'_password').val()),
				"timezoneOffset": getTimeZoneOffset(),
				"modulus":encryptionKeyset["n"]
			},
			"success":function(data)
			{	if(data == "1")
				{	window.location = windowLocation_redirect;
					return;
				}else if(data == "oldKey")
				{	$("#"+keyID+"_errorBox").html('<div class="error" style="font-size:10px;">Your encryption key is old. Please reload the page.</div>');
				}else
				{	$("#"+keyID+"_errorBox").html('<div class="error" style="font-size:10px;">Incorrect Email/Password Combo</div>');
				}
				
				$("#"+keyID+"_loginButton").html(loginButtonHTML);
			},
			"error":function(XMLHttpRequest, textStatus, errorThrown)
			{	if(textStatus == "error")
				{	errorToDisplay = "1";
				}else if(textStatus == "timeout")
				{	errorToDisplay = "2";
				}else
				{	errorToDisplay = "3";
				}
				$("#"+keyID+"_errorBox").html('<div class="error" style="font-size:10px;">'+errorToDisplay+'. Qenso could not be reached. Please try again in a few minutes.</div>');
				$("#"+keyID+"_loginButton").html(loginButtonHTML);
			},
			"timeout":15000,
			"dataType":"text"
		});
	}
	function emailPasswordRequest()
	{	if(validEmail($("#"+keyID+"_email").val()))
		{	var loginButtonBefore = $("#"+keyID+"_loginButton").html();
			$("#"+keyID+"_loginButton").html("Submitting...");
			
			$.post
			(	"/ajax/emailPassword.php", 		// post login data
				{	"email": $("#"+keyID+"_email").val()
				},
				function(data)
				{	if(data == "success")
					{	$("#"+keyID+"_loginButton").html('<span style="color:green;">Success!</span>');
					}else
					{	$("#"+keyID+"_errorBox").html('<div class="error">That email does not exist yet. Try signing up.</div>');
					}
					$("#"+keyID+"_loginButton").html(loginButtonBefore);
				}, "text"
			);
		}else
		{	$("#"+keyID+"_errorBox").html('<div class="error">Invalid email address.</div>');
		}
	}
}

// disallows choosing a date before now, 
// now should be an associative array of the form {"year":year, "month":month, "day":day}
// dateFields should be an associative array of the form {"yearID":yearID, "monthID":monthID, "dayID":dayID} where the IDs are IDs of select tags
// defaultDates should be an associative array of the form {"year":year, "month":month, "day":day}
function updateFutureDateFields(now, dateFields, defaultDates)
{	nowYear = now["year"];
	nowMonth = now["month"];
	nowDay = now["day"];
	
	selectedYear = $(dateFields["yearID"]).val();
	selectedMonth = $(dateFields["monthID"]).val();
	selectedDay = $(dateFields["dayID"]).val();
	
	// if loading the edit contest page
	// if there is currently no selected date - load default date
	if(selectedYear == null)
	{	selectedYear = defaultDates["year"];
		selectedMonth = defaultDates["month"];
		selectedDay = defaultDates["day"];
	}
	
	if(parseInt(selectedYear) == parseInt(nowYear))
	{	var startMonth = nowMonth;
		if(parseInt(selectedMonth) == parseInt(nowMonth))
		{	var startDay = nowDay
		}else
		{	var startDay = 1;
		}
	}else
	{	var startMonth = 1;
		var startDay = 1;
	}
	
	startYearHTML = "";
	for(n=nowYear; n<=Number(nowYear)+1; n++)
	{	if(selectedYear == n)
		{	selected = 'selected="yes"';
		}else
		{	selected = '';
		}
		
		startYearHTML += '<option value="'+n+'" '+selected+'>'+n+'</option>';
	}
	
	startMonthHTML = "";
	months = [0, 'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
	for(n=Number(startMonth); n<=12; n++)
	{	if(selectedMonth == n)
		{	selected = 'selected="yes"';
		}else
		{	selected = '';
		}
		startMonthHTML += '<option value="'+n+'" '+selected+'>'+months[n]+'</option>';
	}
	
	startDayHTML = "";
	for(n=startDay; n<=daysInMonth(selectedMonth, selectedYear); n++)
	{	if(selectedDay == n)
		{	selected = 'selected="yes"';
		}else
		{	selected = '';
		}
		
		startDayHTML += '<option value="'+n+'" '+selected+'>'+n+'</option>';
	}
	
	$(dateFields["yearID"]).html(startYearHTML);
	$(dateFields["monthID"]).html(startMonthHTML);
	$(dateFields["dayID"]).html(startDayHTML);
}

// endingDates should be an associative array of the form {"year":year, "month":month, "day":day}
function extendDurationOverlaySetup(buttonBase, overlayID, actionID, actionTitle, endingDates)
{	$("#"+buttonBase+actionID).click(function()
	{	$(overlayID).html
		(	'<h3>Edit the Ending Date for: '+actionTitle+'</h3>'+
			 '<table>'+
			 	'<tr><td class="label">Expires Midnight On:</td>'+
					'<td><select name="startMonth" id="startMonth'+actionID+'"></select>&nbsp;'+
						'<select name="startDay" id="startDay'+actionID+'"></select>&nbsp;'+
						'<select name="startYear" id="startYear'+actionID+'"></select>'+
					'</td>'+
				'</tr><td></td>'+
			 		'<td id="changeExpirationButton'+actionID+'"><input type="submit" name="submit" class="input_button" value="Submit">'+
			 			'<br><span class="small_text">(note: you might need to refresh the page after the change)</span>'+
					'</td>'+
				'</tr>'+
			 '</table>'
		);
		updateDateChoices(actionID, endingDates);
		$('#startYear'+actionID+', #startMonth'+actionID+', #startDay'+actionID).change(function()
		{	updateDateChoices(actionID, endingDates);
		});
	
		$("#changeExpirationButton"+actionID).click(function()
		{	extendedToYear = $("#startYear"+actionID).val();
			extendedToMonth = $("#startMonth"+actionID).val();
			extendedToDay = $("#startDay"+actionID).val();
			$.ajax
			({	"type": "POST", cache: false, "url": "ajax/editAction.php?id="+actionID, 
				"data": {"submit_expirationChange":true, "year":extendedToYear, "month":extendedToMonth, "day":extendedToDay},
				success: function(data)
				{	if(data == "success")
					{	endingDates["year"] = extendedToYear;
						endingDates["month"] = extendedToMonth;
						endingDates["day"] = extendedToDay;
						$(overlayID).html('<div class="success">You extended the expiration date for <b>'+actionTitle+'</b> to '+extendedToMonth+'/'+extendedToDay+'/'+extendedToYear+'</div>');	
					}else
					{	$(overlayID).html('<div class="error">There was an error changing the date</div>');
					}
				}
			});
		});
	});
}

function updateDateChoices(actionID, endingDates)
{	updateFutureDateFields
	(	endingDates,
		{"yearID":"#startYear"+actionID, "monthID":"#startMonth"+actionID, "dayID":"#startDay"+actionID},
		endingDates
	);
}

function setUpViewLeadersFollowers(userID, type, dumpLocation, itemsPerPage)
{	callOnceOnly(function()
	{	new listPageUpdate
		(	"forwardBack",
			"/ajax/updateLeadersFollowersList.php", 
			{"id": userID, "type":type}, 
			itemsPerPage, dumpLocation, "pageControls", "<tr><td>","</td></tr>"
		);
	}, type);
}

// this might be what a singleton is defined as - i'm not sure
// if id is set, it is used inplace of the callback to determine whether its been called before or not
function callOnceOnly(callback, id)
{	if(callOnceOnly.callbacks == undefined)
	{	callOnceOnly.callbacks = {};
	}
	// only set up once per type
	if(id == undefined)
	{	if(callOnceOnly.callbacks[callback] == true)
		{	return;
		}
	}else
	{	if(callOnceOnly.callbacks[id] == true)
		{	return;
		}
	}
	
	callback();
	
	if(id == undefined)
	{	callOnceOnly.callbacks[callback] = true;
	}else
	{	callOnceOnly.callbacks[id] = true;
	}
	
	
}

// rev4 stuff
function showMainFeed(type, userID)
{	$("#mainFeedNav1, #mainFeedNav2, #mainFeedNav4").attr("class", "not_selected");
    //$("#mainFeedNav2").attr("class", "not_selected");
    //$("#mainFeedNav3").attr("class", "not_selected");	// invite
    //$("#mainFeedNav4").attr("class", "not_selected");	// contests
    $("#feed, #invites, #contests, #leaders, #followers, #storiesFeedDiv").hide();
	//$("#invites").hide();
	//$("#contests").hide();
	//$("#leaders").hide();
	//$("#followers").hide();
    
    if(type == 1)
    {	$("#mainFeedNav"+type).attr("class", "selected");
		
		callOnceOnly(function()
		{	var listPageObj_stories = new listPageUpdate
			(	"forwardBack",
				"/ajax/storiesFeed_update.php", 
				{"profile_id": userID}, 
				10, "storiesFeedDiv", "pageControls", "<div style='text-align:center;'>","</div>"
			);
		});
		$("#storiesFeedDiv").show();
	}else if(type == 2)
	{	$("#mainFeedNav"+type).attr("class", "selected");
		ajaxUpdate("#feed", "/ajax/getMainFeed.php", "type="+type);
		
		$("#feed").show();
	}
	else if(type == 3)
	{	$("#invites").show();
	}else if(type == 4)
	{	$("#contests").show();
		$("#mainFeedNav"+type).attr("class", "selected");
	}else if(type == "leaders")
	{	$("#leaders").show();
		setUpViewLeadersFollowers(userID, "leaders", "memberList_leaders");
	}else if(type == "followers")
	{	$("#followers").show();
		setUpViewLeadersFollowers(userID, "followers", "memberList_followers");
	}else if(type == "searchUsers")
	{	searchUsers();	// this is defined in home_template (which is a bad idea btw, to put the definition in a template file, and the call in the main js library...)
		$("#feed").show();
	}
}

function showProfileFeed(type, user)
{	$("#mainFeedNav1, #mainFeedNav2").attr("class", "not_selected");
    $("#feed, #storiesFeedDiv, #followers, #leaders").hide();
		
	if(type == 1) 
	{	$("#mainFeedNav"+type).attr("class", "selected");
			callOnceOnly(function()
		{	var listPageObj_stories = new listPageUpdate
			(	"forwardBack",
				"/ajax/storiesFeed_update.php", 
				{	"profile_id": user,
					"storiesType": "profile"
				}, 
				10, "storiesFeedDiv", "pageControls", "<div style='text-align:center;'>","</div>"
			);
		});
		$("#storiesFeedDiv").show();
    }
	else if(type == "leaders")
	{	$("#leaders").show();
		setUpViewLeadersFollowers(user, "leaders", "memberList_leaders");
	}else if(type == "followers")
	{	$("#followers").show();
		setUpViewLeadersFollowers(user, "followers", "memberList_followers");
	}
    else 
	{	$("#mainFeedNav"+type).attr("class", "selected");
		ajaxUpdate("#feed", "/ajax/getProfileFeed.php", "type="+type+"&user="+user);
		$("#feed").show();
    }   
    
}

function showMainActionFeed(type, q, page)
{	if(type == 1)	// from my friends
	{	$("#searchDescription").html("Search for actions posted by your friends");
		$("#searchDescription").show();
	}else if(type == 2)	// featured actions
	{	$("#searchDescription").html("Search for actions from all Open feeds");
		$("#searchDescription").show();
	}else
	{	$("#searchDescription").hide();
	}
	
	if(!q)
	{	q = "";
	}
	ajaxUpdate("#action_feed", "/ajax/getMainActionFeed.php", "type="+type+"&q="+q);
}

function showProfileActionFeed(type, user)
{	if(type == 2)
	{	var listPageObj_actionsDone = new listPageUpdate
		(	"forwardBack",
			"/ajax/storiesFeed_update.php", 
			{	"profile_id": user,
				"storiesType": "profileActionsDone"
			}, 
			10, "action_feed", "pageControls", "<div style='text-align:center;'>","</div>"
		);
	}else
	{	ajaxUpdate("#action_feed", "/ajax/getProfileActionFeed.php", "type="+type+"&user="+user);
	}
}

function ajaxUpdate(div, url, data)
{
    $(div).html(loadingAnim());
   	$.ajax({
        type: "GET",
        cache: false,
        url: url,
        data: data,
        success: function(msg)
		{	$(div).html( msg );
		}
     });
}
// data should be an associative array
function ajaxDo(url, data, callback, type)
{	if(type==undefined)
	{	type="POST"		// default value
	}
	
	$.ajax
	({	"type": 	type,
        "url": 		url,
        "cache": 	false,
		"data":		data, 
		"success": 	callback,
		"timeout": 	10000,
		"dataType":	"text"
	});
}

function joinUser(leader, leaderName)
{	if(leaderName == undefined)
	{	noLeaderName = true;
	}else
	{	noLeaderName = false;
	}
		
	var div = "#join_"+leader;
	var data = "l="+leader+"&s="+1;	// 1 is FRIEND_STATUS_REQUEST
	var selectorToReplace = ".joinButtonDiv_"+leader;
	
	//ajaxUpdate(div, "/ajax/joinUser.php", data);
	
	ajaxDo("/ajax/joinUser.php", data, function(result)
	{	if(result == "Request Sent")
		{	$(selectorToReplace).replaceWith("Request Sent");
		}
		else if(result == "Joined")
		{	if(noLeaderName)
			{	$(selectorToReplace).replaceWith("You are now following this user");
			}else
			{	$(selectorToReplace).replaceWith("You are now following "+leaderName);
			}
		}else
		{	$(selectorToReplace).replaceWith(result);
		}
	}, "GET");	
}
function leaveUser(leader, buttonSelector, leaderName)
{	leaveUser_withCallback(leader, leaderName, function(result)
	{	if(result == "Not Joined")
		{	$(buttonSelector).html("You are no longer following "+leaderName);
		}
	});
}

// call back takes the parameter 'result', which holds the output of joinUser.php
function leaveUser_withCallback(leader, leaderName, callback)
{	ajaxDo(	"/ajax/joinUser.php", {"l":leader, "s":3}, callback, "GET");	// 3 is FRIEND_STATUS_REJECT
}

function answerRequest(user, status)
{
	var div = "#req_"+user;
	var data = "user_id="+user+"&s="+status;
	
	ajaxUpdate(div, "/ajax/answerRequest.php", data);
}

// sets up a title for elements matching the selector
function setupTitle(selector)
{	$(selector).focus(function(srcc)
    {
        if ($(this).val() == $(this)[0].title)
        {
            $(this).removeClass("defaultTextActive");
            $(this).val("");
        }
    });
    
    $(selector).blur(function()
    {
        if ($(this).val() == "")
        {
            $(this).addClass("defaultTextActive");
            $(this).val($(this)[0].title);
        }
    });
    
    $(selector).blur();
}

// sets up a title for an element
function setupTitleFloater(inputID)
{	var self = this;
	
	self.placeTitle = function()
	{	offset = $("#"+inputID).offset();	// top left of the "Confirmed" div
		
		$("#title_"+inputID).css		// set position and width for the pipeline text
		({	"top" : offset.top+2,
			"left" : offset.left+3
		});
	}
	
	// this is basically a constructor
	$(function()		// opens the login window for facebook, and then runs facebookLogin on completion
	{	$("#"+inputID).after('<div id="title_'+inputID+'" style="display:none;position:absolute;" class="defaultTextActive">'+$("#"+inputID).attr("title")+'</div>');
		
		$(window).resize(self.placeTitle);
		
		$("#"+inputID).focus(function(srcc)
	    {	$("#title_"+inputID).css({"display":"none"});
	    });
	    $("#"+inputID).blur(function()
	    {	if($(this).val() == "")
	        {	$("#title_"+inputID).css({"display":""});
	        }
	    });
	    $("#title_"+inputID).click(function()
	    {	$("#"+inputID).focus();
	    });
	    
		setTimeout(function()	// I think this delays it slightly so the page has a chance to lay itself out before the title is aligned
		{	$(window).resize();
	    	$("#"+inputID).blur();
		}, 0);
		
		/*
		$(document).ready(function(){placePipeline();placePipeline();});	// i'm not sure why placePipeline needs to be run twice to work properly...
		
		
		<div id="pipelineText" style="text-align:center; font-weight:700; border:solid 1px green;">Pipeline</div>	
		
			/*$("#"+inputID).after('<input type="password" name="doesntMatter" id="realPassword_'+inputID+'" style="display:none;">');
		$("#realPassword_"+inputID).val("");
		
		$("#"+inputID).keyup(function(event)
		{	valLength = $("#"+inputID).val().length;
			realPasswordLength = $("#realPassword_"+inputID).val().length;
			
			if(valLength < realPasswordLength)
			{	$("#realPassword_"+inputID).val
				(	$("#realPassword_"+inputID).val().substr(0,valLength)
				);
			}
			
			lastChar = $("#"+inputID).val().charAt(valLength-1);
			$("#realPassword_"+inputID).val
			(	$("#realPassword_"+inputID).val()+
				$("#"+inputID).val().substr(realPasswordLength)
			);
			
			astrisks = "";
			for(n=0; n<valLength; n++)
			{	astrisks+="*";
			}
			$("#"+inputID).val(astrisks);
		});
		*/
	});
}

// objectList is for internal use only (to prevent infinite loops)
function printr(theObj, maxdepth, curDepth)
{	if(curDepth == undefined)
	{	curDepth=0
	}
	if(maxdepth != undefined)
	{	if(curDepth >= maxdepth)
		{	return "more...";
		}	
	}
	
	var isObj;
	if((typeof theObj) == "array" || (typeof theObj) == "object" || (typeof theObj) == "function")
	{	isObj = true;
	}else
	{	isObj = false;
	}
	
	if(isObj)
    {	result = "<ul>";
    	for(var p in theObj)
		{	result += "<li>["+p+"] => "+printr(theObj[p])+"</li>";
    	}
    	result += "</ul>";
    	return result;
  	}
  	else
  		return theObj;
}

function replaceAll(theString, replaceThis, withThis)
{	return theString.replace(new RegExp(replaceThis, 'g'), withThis);
}

function implode(separator, array)
{	var result = "";
	var first = true;
	for(n in array)
	{	if(first == false)
		{	result += separator;
		}
		result += array[n];
		first = false;
	}
	return result;
}

// javascript class used to set up an autocomplete input, and keep track of the results
// Methods:
	// rmSelection
	// clearSelections
	// addSelection
	// closeSelectionBox
	// openSelectionBox
// matchChoices should be an array where each element is an array of data
	// examples:
		// [ [x, y, z], [a, b, c], [d, e, f], etc ] or
		// [ {"a":x, "b":y, etc}, etc ]
// options should be an associative array containing one of the following:
	// "searchIndex": the index in each row to search by
	// "resultIndex": the index in each row to write as the result (usally an ID of some kind)
	// "formatMatch": a callback that should return html for formatting the data of each match
		// recives the parameter: data
	// "onSelect" (optional): a callback that runs when a new choice is selected
		// receives the parameters: type, data, selectionsIndex
			// type will either be "userDefined" or "selected"
			// data will either be a row from matchChoices, or a submitted user-defined value
			// selectionsIndex is the index in self.selections where the value was stored at (and from where it can be removed)
function autoCompleteList(inputID_in, valueID_in, matchChoices_in, options)
{	var self = this;
	
	var inputID = inputID_in;
	var valueID = valueID_in;
	var matchChoices = matchChoices_in;
	var searchIndex = options["searchIndex"];
	var resultIndex = options["resultIndex"];
	var match = options["formatMatch"];
	var onSelect;
	if("onSelect" in options)
	{	onSelect = options["onSelect"];
	}else
	{	onSelect = function(){};	// noOp
	}
	
	self.selections = [];	// public variable
	
	// constructor
	$(function()
	{	$("#"+inputID).autocomplete(matchChoices, 
		{	"minChars": 0,
			"matchContains": true,
			"autoFill": false,
			"selectFirst":false,
			"formatItem": options["formatMatch"],
			"formatMatch": function(row /*, i, max*/)	// max is the "total number of data" - does this mean the number of members in the associative array?  
			{	return row[searchIndex];
			}
		});
		
		$("#"+inputID).keypress(function(event)
		{	if(event.keyCode == 13)
		    {	if($("#"+inputID).val() != "")
		    	{	handleSelection("userDefined", $("#"+inputID).val());
		    		self.closeSelectionBox();
				}
				return false;
		    }
		});
		$("#"+inputID).result(function(event, data, formatted)
		{	handleSelection("selected", data);
		});
		$("#"+inputID).blur(function()
		{	setTimeout(self.closeSelectionBox, 1000);	// settimeout is neccessary for firefox and safari because they close the selection box *before* the click event is handled for selecting an option
		});
	});

	// removes a selection from the list given its index (in self.selections)
	self.rmSelection = function(index)
	{	self.selections[index]=undefined;//self.selections.splice(index, 1);
		updateValueField();
	};
	// removes a selection from the list given its index (in self.selections)
	self.clearSelections = function()
	{	self.selections=[];
		updateValueField();
	};

	// adds a value to the list
		// type is the type to emulate ("selected" or "userDefined")
		// data is the data to add
			// if type == "userDefined" and data is not an array, it will try to find the selection in the matchChoices list 
	self.addSelection = function(type, data)
	{	if(type!="userDefined" && false == (data instanceof Object))
		{	for(n in matchChoices)
			{	if(matchChoices[n][resultIndex] == data)
				{	data = matchChoices[n];
					break;
				}
			}
		}
		
		handleSelection(type, data);
		self.closeSelectionBox();
	};

	// closes the list of matches (available to select)
	self.closeSelectionBox = function()
	{	$(".ac_results").hide();	// hide the results box
	};
	// shows the list of matches (available to select)
	self.openSelectionBox = function()
	{	$(".ac_results").show();	// hide the results box
	};
	
	function updateValueField()
	{	var listToWrite = [];
		for(n in self.selections)
		{	if(self.selections[n] != undefined)
			{	listToWrite[listToWrite.length] = self.selections[n];
			}
		}
		
		$("#"+valueID).val(implode(",", listToWrite));	// update the value field
	}
	
	// escapes commas with backslashes (and backslashes with backslashes)
	// DOESN'T DO ANYTHING RIGHT NOW - replaceAll needs to be more generalized for this
	function escapeCSVmember(value)
	{	var result = value;
		///result = replaceAll(String(value), "\\", "\\\\");	// replace \ with \\
		//result = replaceAll(String(value), ",", "\\,");		// replace , with \,
		return result;
	}	
	
	function handleSelection(type, data)
	{	nextIndex = self.selections.length;
		if(type == "userDefined")
		{	self.selections[nextIndex] = escapeCSVmember(data);
		}else
		{	self.selections[nextIndex] = escapeCSVmember(data[resultIndex]);
		}
		updateValueField();
		
		onSelect(type, data, nextIndex, $("#"+inputID_in).val());
	}
	
	// moves a value from one array to another
	/*function moveBetweenArrays(index, fromArray, toArray, toIndex)
	{	toArray[toArray.length] = fromArray.splice(index, 1);
	}
	*/
}

// used for autocomplete
function createRemovableItem(selectionID, index, displayInfo, autoCompleteObject, onRemove)
{	if(onRemove == undefined)
	{	onRemove = function(){};
	}
	
	var selectionVisualID = 'selection_'+selectionID+index;
	var selectionVisual_rmID = 'rm_selection_'+selectionID+index;
	
	selectionFormat = 
	'<div id="'+selectionVisualID+'" class="create_receivers">'+
		'<span class="delete" id="'+selectionVisual_rmID+'">x</span>'+
		displayInfo+
	'</div>';
	
	$("#"+selectionID).append(selectionFormat);
	$("#"+selectionVisual_rmID).click(function()
	{	autoCompleteObject.rmSelection(index);
		$("#"+selectionVisualID).hide();
		onRemove();
	});
}

// trims whitespace - I got this online
function trim(stringToTrim)
{	return stringToTrim.replace(/^\s+|\s+$/g,"");
}

function emailListString_toArray(string)
{	var semiColonList = string.split(",");
	var emailList = [];
	for(n in semiColonList)
	{	var untrimedList = semiColonList[n].split(";");
		for(j in untrimedList)
		{	emailList = emailList.concat(trim(untrimedList[j]));
		}
	}
	return emailList;
}

function verifyEmailList(string)
{	var list = emailListString_toArray(string);
	for(n in list)
	{	if(false == validEmail(list[n]))
		{	return list[n];
		}
	}
	// else 
	return true;
}

// attachments should be an object that contains 0 or more of the following members:
	// name - a string: The name of the story (basically a title I think)
	// href - a link associated with the story 
	// caption - the content of the story ? (then what is description?)
	// media - an array of objects. Those objects include:
		// image - {"type": "image", "src": URL of the image, "href": ? } 
function facebookPost(story, attachments)
{	FB.Connect.streamPublish(story, attachments);
}

// sets the selection range of an input field (or fields)
function setSelectionRangeX(selector, selectionStart, selectionEnd) 
{	$(selector).each(function()
	{	if(this.createTextRange)	// IE 
		{	var range = this.createTextRange(); 
			range.collapse(true); 
			range.moveEnd('character', selectionEnd); 
			range.moveStart('character', selectionStart); 
			range.select(); 
		}else if(this.setSelectionRange)	// real browsers :) 
		{	this.focus(); 
			this.setSelectionRange(selectionStart, selectionEnd); 
		} 
	});
}

// like in_array in php, returns true if needle is found in haystack (which should be an array)
function in_array(needle, haystack)
{	for(var n in haystack)
	{	if(haystack[n] == needle)
		{	return true;
		}
	}
	//else
	return false;
}
