document.write("<sc"+"ript type=\"text/javascript\" src=\"/scripts/seed/weseed_api.js\"></scr"+"ipt>");
document.write("<sc"+"ript type=\"text/javascript\" src=\"/scripts/seed/iphone_history.js\"></scr"+"ipt>");
function trace(b){var c="trace";var d="clearTrace";if(!document.getElementById(c)){var e=document.createElement('div');e.id=c;e.style.position="fixed";e.style.top="10px";e.style.left="10px";e.style.opacity=".8";e.style.filter="alpha(opacity=80)";e.style.width="375px";e.style.minHeight="20px";e.style.maxHeight="300px";e.style.overflow="auto";e.style.backgroundColor="#000000";e.style.border="5px solid #f1f1f1";e.style.MozBorderRadius="10px";e.style.color="#f1f1f1";e.style.textAlign="left";e.style.padding="15px";e.style.zIndex="3000";document.body.appendChild(e);var a=document.createElement('a');a.id=d;a.className="clear";a.style.display="block";a.style.margin="0 0 5px";a.style.cursor="pointer";e.appendChild(a)}var f=document.getElementById(c);f.innerHTML+="<hr>\n";f.innerHTML+="<code>"+b+"</code>";var g=document.getElementById(d);g.innerHTML="close";g.onclick=function(){document.body.removeChild(f)}}

// EVENT HANDLER OBJECT
var eventHandlerMobile = {
	// WHERE THE MAGIC HAPPENS
	bind:function(o) {
		//trace("eventHandlerMobile::bind( )");
		var o = o || {};
		var method = o.method || "";
		var arrMethods = method.split(",");
		for (var i in arrMethods) {			
			this[$.trim(arrMethods[i])]();
		}
	},
	// BIND EVENTS TO ELEMENTS ON THE INITIAL PAGE LOAD
	onload:function() {
		//trace("...eventHandlerMobile::onload( )");
		for (var i in this) {
			if (i != 'bind' && i != 'onload' && i != 'loadHome') {
				//trace("......dispatching " + i + "( )");
				this[i]();
			}
		}
	},
	addLinks:function(){
		$("a[rel='Profile'],\
			a[rel='Home'],a[rel='Talk'],a[rel='Users'],\
			a[rel='Company'],a[rel='Passion'],\
			a[rel='Commentary']").unbind().click(function(){
				new WeSeed().transition();
				$("#messages, #error").empty();
				var link = $(this).attr("href") || "";
				var rel = $(this).attr("rel") || "";
				$("body").attr("id","area"+rel);
				var method;
				if (rel) {
					var methodName = "get{}".format(rel);
					if (new WeSeed()[methodName]) {
						method = methodName;
					} else {
						method = (new WeSeed()[rel])?rel:"";
					}
				} 
				var callback = function() {
					$("p.iLoading").hide();
					$("#"+rel).show();
					eventHandlerMobile.onload();
				}
				if (method) {
					new WeSeed()[method](link,callback);
				} 
				return false;
			});
	},
	login:function(){
		/* LOGIN PAGE ACTION */
		$(".signInLink").unbind().click(function(){
			new WeSeed().showLogin();
		});
		$(".signUpLink").unbind().click(function(){
			new WeSeed().showSignup();
		});
		$(".forgotPasswordLink").unbind().click(function(){
			if ($("#signin_username").val() && !$("#fpEmail").val()) {
				$("#fpEmail").val($("#signin_username").val());
			} 
			new WeSeed().showPassword();
		});

		$("#frmSignIn").unbind().submit(function(){
			new WeSeed().login($("#signin_username").val(),$("#signin_password").val());
			$("#signin_password,#signin_username").each(function(){this.blur()});
			
		});
		$("#frmSignIn .btnLetsGo").unbind().click(function(){
			$("#frmSignIn")[0].submit();
		});
		
		$("#frmJoin").unbind().submit(function(e){
			e.stopPropagation();
			var email = $("#signup_username").val();
			var password = $("#signup_newPassword").val();
			var passwordConfirm = $("#signup_confirmPassword").val();
			if (Validator.validate()) {
				//first blur all elements to hide the keyboard
				$("#signup_username,#signup_firstName,#signup_lastName,#signup_screenName,#signup_newPassword,#signup_confirmPassword,#signup_cashBalance").each(function(){this.blur()});
				
				new WeSeed().signup($("#frmJoin").serializeArray());
			} else {
				return false;
			}
		});
		$("#frmJoin .btnLetsGo").unbind().click(function(e){
			e.stopPropagation();
			$("#frmJoin")[0].submit();
		});
		
		$("#frmForgotPassword").unbind().submit(function(){
			$("#fpEmail").each(function(){this.blur()});
			new WeSeed().requestResetPassword($("#fpEmail").val());
		});
		$("#frmForgotPassword .btnLetsGo").unbind().click(function(){
			$("#frmForgotPassword")[0].submit();
		});
		$("#frmChangePassword").unbind().submit(function(){
			$("#frmChangePassword").find("input").each(function(){this.blur()});
			new WeSeed().resetPassword($("#resetPasswordEmail").val(), $("#newPassword").val(), $("#confirmPassword").val());
		});
		$("#frmChangePassword .btnLetsGo").unbind().click(function(){
			$("#frmChangePasswor")[0].submit();
		});
	},
	search:function(){
		/* Search function */
		//
		$("#srchSuggest").unbind().submit(function() {
			$("#searchSuggest")[0].blur();
			new WeSeed().searchCompanies($(this).find("input[name='q']").val());		
		});
		$("#searchSuggest").keyup(function() {
			if (!$("#searchSuggest").val().trim()) {
				//clear the search results
				$("#CompanySearchResults").hide();
				$(".hiddenOnSearch").show();
				$(".shownOnSearch").hide();
			}	
			return true;	
		});
	},
	portfolio:function(){
		/* Stop Following Portfolio */ 
		$(".btnFollow.follow").unbind().click(function(){
			var followId = $(this).attr("fid");
			new WeSeed().follow(followId);
		});
		$(".iClose,.btnFollow.unfollow").unbind().click(function(){
			var followId = $(this).attr("fid");
			new WeSeed().unfollow(followId);
		});
		
		$("#ShowPositions").unbind().click(function(){
			$("#PositionsContainer").toggle();
			var text = "{} Stocks".format($("#PositionsContainer").is(":visible")?"Hide":"Show");
			$("#ShowPositions").find(".btnLabel.ctrElements").text(text);
		});
	},
	trade:function(){
		//add an event to show the buy/sell pane;
		$("#BuySell").unbind().click(function(){
			$("#frmBuy").toggle();
		});
		$("#frmBuy").unbind().submit(function() {
			var companyId = $("#TradeCompanyId").val();
			var op = $("#TradeType").val();
			var quantity = $("#quantity").val();
			$("#quantity")[0].blur();
			new WeSeed().trade(companyId,op,quantity); 
		});
		$("#BuyWeShares").unbind().click(function(){
			$("#frmBuy").submit();
			return false;
		});
	}
}

$(function(){	
	if (!new HistoryManager().hasHistory()) {
		new WeSeed().getHome();
	} else {
		eventHandlerMobile.onload();
	}
});

function WeSeed() {

	if (WeSeed.__instance) {
		return WeSeed.__instance;
	} else {
		//trace("Creating WeSeed( ) Singleton");
		WeSeed.__instance = this;
	}
	
	var errorCallback = function(){
		new Marshaler().showErrors({errors:["failed - please try again"]});
	}
	this.rest = new WeSeedRest(null, errorCallback);
	this.isLoggedIn = false;
	this.__loginCallback = null;
}
WeSeed.prototype.getHome = function(doNotAddHistory) {
	//trace("...WeSeed::getHome( )");
	var callback = this.__prepareChangeAndGetCallback("#Home");
	if (!doNotAddHistory) {
		new HistoryManager().addHistory("new WeSeed().getHome()");
		trackPage("home");
	}
	
	$(".contentLoad").hide();
	$(".results").hide();
	
	if ($("#Home").hasClass("homeLoaded")) {
		callback();
	} else {
		$("#Home").addClass("homeLoaded")
		var homeCallback = function() {
			$("body").attr("id","areaHome");
			
			//could optimize this by not calling these methods if the data is there, but hiding
			new WeSeed().getCompanies(callback);
			new WeSeed().getTopContributors(callback);
	
			//check to see if we're logged in
			var loggedInCallback = function(results) {
				var hasAuthError = false;
				var errors = results.errors;
				for (var i=0;i<errors.length;i++) {
					if (errors[i].name == "AUTHENTICATION") {
						hasAuthError = true;
						break;
					}
				}
				if (!hasAuthError) {
					var user = results.results[0];
					new WeSeed().showLoggedIn(user);
					new WeSeed().rest.getPortfolio("current");
				}
		
			}
			new WeSeed().rest.getProfile("current",loggedInCallback);
	
		}
		this.getPassions(homeCallback);
	}
}
WeSeed.prototype.getCompanies = function(callback,parentSelectorPrefix,childSelectorPrefix) {
	//trace("...WeSeed::getCompanies( )");
	var successCallback = function(results) {
		new Marshaler().marshal(results, "{}.secBody.mobile.companies".format(parentSelectorPrefix), "{}.mobile.company".format(childSelectorPrefix), callback);
	}
	this.rest.getCompanies(successCallback);
}
WeSeed.prototype.searchCompanies = function(searchCriteria,callback) {
	//trace("...WeSeed::searchCompanies( )");
	new HistoryManager().addHistory("new WeSeed().getHome(true);new WeSeed().searchCompanies('{}')".format(searchCriteria));
	trackPage("searchCompanies?searchCriteria={}".format(searchCriteria));
	$("p.iLoading").show();
	var successCallback = function(results) {
		var newCallback = function() {
			//add .alt to every other div.citem starting with the first one
			$("div.citem:even").addClass("alt");
			
			//fill in the number of results in #NumberOfSearchResults
			var howMany = "no results";
			if (results.results.length == 1) {
				howMany = "1 result:";
			} else if (results.results.length >= 1) {
				howMany = "{} results:".format(results.results.length);
			}
			$("#NumberOfSearchResults").text("Search returned {}".format(howMany));
			$("#searchQuestion > .hiddenOnSearch").hide();
			$("#searchQuestion > .shownOnSearch").show();
			$("#searchQuestion > .results").show();
			
			eventHandlerMobile.onload();
			
			if (callback) {
				callback();
			}
			$("p.iLoading").hide();
		}
		new Marshaler().marshal(results, "#CompanySearchResults", ".mobile.companySearchResult", newCallback);
	}
	this.rest.searchCompanies(searchCriteria,successCallback);
}

WeSeed.prototype.getCompany = function(companyId) {
	//trace("...WeSeed::getCompany( )");
	var callback = this.__prepareChangeAndGetCallback("#Company");
	new HistoryManager().addHistory("new WeSeed().getCompany({})".format(companyId));
	trackPage("company?companyId={}".format(companyId));
	$("#frmBuy").hide();
	$("#BuySell").parent(".btns").hide();
	var successCallback = function(results) {
		new Marshaler().marshal(results, "#Company", "", callback);
		//star rating
		var company = results.results[0];
		$("li.currentRating").css("width","{}px".format(company.averageRating * 18));

		//get the buzz
		var commentarySuccessCallback = function(results) {
			new Marshaler().marshal(results, "#CompanyCommentary", ".commentaryContainer", callback, ".companyCommentaryAssociate");
		}
		new WeSeed().rest.getGroupCommentary(company.groupId, commentarySuccessCallback);

		//buy/sell button
		//get the current user's portfolio (should be cached) and determine if they own this stock, and how much cache they have
		var portfolioCallback = function(portfolioResults) {
			if (!portfolioResults.errors.length) {
				var portfolio = portfolioResults.results[0];
				var cash = portfolio.cashHoldings;
				var canSell = portfolio.positions[company.symbol];
				var canBuy = (eval(company.price) < eval(cash));
				var buttonLabel = "";
				if (canBuy && canSell) {
					buttonLabel = "Buy/Sell";
				} else if (canBuy) {
					buttonLabel = "Buy";
				} else if (canSell) {
					buttonLabel = "Sell";
				} 
				if (buttonLabel) {
					$("#BuySell").parent(".btns").show();
					$("#BuySell").find(".buySell").text(buttonLabel).show();
					
					if(canBuy && canSell) {
						$("#BuyOrSellContainer").show()
					} else {
						$("#BuyOrSellContainer").hide()
					}
					if (canBuy && !canSell) {
						$(".txtBuySell").text("buy");
						$("#TradeType").val("buy");
					} else if (!canBuy && canSell) {
						$(".txtBuySell").text("sell");
						$("#TradeType").val("sell");
					} else {
						$(".txtBuySell").text($("#TradeType").val());
					}
					var quantity = 0;
					if (company && company.symbol) {
						var position = portfolio.positions[company.symbol];
						if (position) {
							quantity = position.quantity
						}
					} 
					$("#CurrentlyOwnSharesNumber").text(quantity);
				} else {
					$("#BuySell").parent(".btns").hide();
					$("#frmBuy").hide();
				}
			} else {
				$("#BuySell").parent(".btns").hide();
				$("#frmBuy").hide();
			}
		}
		new WeSeed().rest.getPortfolio("current",portfolioCallback);
	}
	this.rest.getCompany(companyId,successCallback);

	var passionSuccessCallback = function(results) {
		new Marshaler().marshal(results, "#CompanyPassions", ".mobile.passion", callback, ".companyPassionsAssociate");
	}
	this.rest.getCompanyPassions(companyId, passionSuccessCallback);
}

WeSeed.prototype.trade = function(companyId,op,quantity) {
	//trace("...WeSeed::trade( )");
	new WeSeed().transition();

	var callback = function(results) {
		//a little hack so that we can clear the cache of the current user's portfolio
		var userId = "current";
		new WeSeed().rest.getPortfolio(userId,null,null,true);
		
		new WeSeed().getProfile(userId);
	}
	
	this.rest[op](companyId,quantity,callback);
}

WeSeed.prototype.getPassions = function(callback) {
	//trace("...WeSeed::getPassions( )");
	var successCallback = function(results) {
		new Marshaler().marshal(results, "#PassionContainer", ".mobile.passion", callback);
	}
	this.rest.getPassions(successCallback);
}
WeSeed.prototype.getPassion = function(passionId) {
	//trace("...WeSeed::getPassion( )");
	var callback = this.__prepareChangeAndGetCallback("#Passion");
	new HistoryManager().addHistory("new WeSeed().getPassion({})".format(passionId));
	trackPage("passion?passionId={}".format(passionId));
	var successCallback = function(results) {
		new Marshaler().marshal(results, "#Passion", "", callback);
		if (results && results.results) {
			var commentaryCallback = function(results) {
				new Marshaler().marshal(results, "#PassionCommentary", ".commentaryContainer", callback,".passionCommentaryAssociate");
			}
			new WeSeed().rest.getGroupCommentary(results.results[0].groupId,commentaryCallback);
		}
	}
	this.rest.getPassion(passionId,successCallback);
	this.getPassionCompanies(passionId,callback);
}
WeSeed.prototype.getPassionCompanies = function(passionId, callback) {
	//trace("...WeSeed::getPassionCompanies( )");
	var successCallback = function(results) {
		new Marshaler().marshal(results, "#PassionCompanies", ".mobile.company", callback);
		$("#Passions").show();
	}
	this.rest.getPassionCompanies(passionId,successCallback);
}

WeSeed.prototype.getCommentary = function(commentaryId) {
	//trace("...WeSeed::getCommentary( )");
	var callback = this.__prepareChangeAndGetCallback("#Commentary");
	new HistoryManager().addHistory("new WeSeed().getCommentary({})".format(commentaryId));
	trackPage("commentary?commentaryId={}".format(commentaryId));
	var successCallback = function(results) {
		new Marshaler().marshal(results, "#Commentary", "", callback);

		//get all the comments on this commentary 
		var commentsCallback = function(commentResults) {
			new Marshaler().marshal(commentResults, "#commentsGossip", ".singleComment", callback);
			if (!commentResults.results.length) {
				$(".commentsGossipAssociate").hide();
			}
		}
		new WeSeed().rest.getCommentaryComments(commentaryId,commentsCallback);
	}
	this.rest.getCommentary(commentaryId,successCallback);
}

WeSeed.prototype.getTopContributors = function(callback) {
	//trace("...WeSeed::getTopContributors( )");
	var successCallback = function(results) {
		new Marshaler().marshal(results, "#TopContributors", ".mobile.contributor", callback);
		//add "view all members"
		$("#TopContributors").append('<li class="noImg"><a href="1" rel="Users">View All Members</a></li>');
		eventHandlerMobile.onload();
	}
	this.rest.getTopContributors(successCallback);
}
WeSeed.prototype.getUsers = function(page) {
	//trace("...WeSeed::getUsers( )");
	page = page||1;
	var callback = this.__prepareChangeAndGetCallback("#Users");
	new HistoryManager().addHistory("new WeSeed().getUsers('{}')".format(page));
	trackPage("users?page={}".format(page));
	var successCallback = function(results) {
		var hasAuthError = false;
		var errors = results.errors;
		if (errors.length) {
			for (var i=0;i<errors.length;i++) {
				if (errors[i].name == "AUTHENTICATION") {
					hasAuthError = true;
					break;
				}
			}
		}
		if (!hasAuthError) {
			new Marshaler().marshal(results, "#UsersContainer", ".mobile.user", callback);
			//add "view all members"
			if (!results.info["lastPage"] || results.info.lastPage > page) {
				$("#UsersContainer").append('<li class="noImg"><a href="{}" rel="Users">Next Page...</a></li>'.format(eval(page) + 1));
			}
			eventHandlerMobile.onload();
		} else {
			var serializedLoginCallback = "new WeSeed().getUsers({})".format(page);
			//make asynchronous so that the view will properly update
			window.setTimeout(function(){new WeSeed().showLogin(serializedLoginCallback)},100);
		}
	}
	this.rest.getUsers(page,successCallback);
}

WeSeed.prototype.getProfile = function(userId, unSerializeableCallback) {
	//trace("...WeSeed::getProfile( )");
	userId = (new WeSeed().loggedInUserId==userId)?"current":userId;
	var callback = this.__prepareChangeAndGetCallback("#Profile");
	new HistoryManager().addHistory("new WeSeed().getProfile('{}')".format(userId));
	trackPage("profile?userId={}".format(userId));

	$("#PositionsContainer").hide();
	var successCallback = function(results) {
		var newCallback;
		if (unSerializeableCallback) {
			newCallback = function() {
				if (callback) {
					callback();
				}
				if (unSerializeableCallback) {
					//this callback does not get serialized
					//and thus will not be called when the user clicks
					//back to this page via the browser history
					unSerializeableCallback();
				}
			}
		} else {
			newCallback = callback;
		}
		new WeSeed().showProfileFromResults(results,newCallback, userId);
	}
	this.rest.getProfile(userId,successCallback);
}


WeSeed.prototype.showProfileFromResults = function(results, callback, userId) {
	//trace("...WeSeed::showProfileFromResults( )");
	new Marshaler().clearErrorsAndMessages();
	
	if (results.results.length) {
		var user = results.results[0]

		//marshal the profile which is in the results
		new Marshaler().marshal(results, "#ProfileContainer", "");
		new Marshaler().marshal(results, "#PortfolioContainer", "");
		new Marshaler().marshal(results, "#FollowButton", "");
		new Marshaler().marshal(results, "#FolloweesHeader", "");
		
		//change the follow button depending on whether this profile is for the logged on user
		if (user.isLoggedOnUser) {
			$("#FollowButton").hide()
		} else {
			$("#FollowButton").show()
		}
		//or whether this profile is already followed 
		if (user.isFollowedByLoggedOnUser) {
			$("#FollowDirective").text("Stop Following");
			$("#FollowButton .btnFollow").removeClass("follow").addClass("unfollow");
		} else {
			$("#FollowDirective").text("Follow");
			$("#FollowButton .btnFollow").removeClass("unfollow").addClass("follow");
		}

		//get the portfolio
		var portfolioSuccessCallback = function(pResults) {
			new Marshaler().marshal(pResults, "#PortfolioContainer", "", callback);
			
			//color code for positive and negative
			var decorator = function() {
				$(".gainLoss").each(function(){
					$(this).removeClass("gain").removeClass("loss");
					try {
						var val = $(this).text();
						val = val.replace("$","").trim();
						val = eval(val);
						var className = "";
						if (val > 0) {
							className = "gain";
						} else if (val < 0) {
							className = "loss"
						}
						$(this).addClass(className)
					} catch (e) {
					
					}
				});
				
				$(".currency").each(function(){
					$(this).text("${}".format($(this).text().toCurrency()));
				});
				
				$(".commas").each(function(){
					$(this).text($(this).text().toCurrency().split(".")[0]);
				});
			}
			if (user.isLoggedOnUser) {
				//personalize by saying "YOU" instead of the username
				$("#PortfolioContainer").find(".your").text("YOUR");
				$("#PortfolioContainer").find(".you").text("YOU");
				$("body").attr("id","areaProfile");

				//if the user is the current user, then unwind the positions and package it as a results object
				//then marshal positions into the document
				var positionResults = {errors:[],messages:[],info:{},results:[]};
				var positions = pResults.results[0].positions;
				for (var symbol in positions) {
					positions[symbol].WStype = "Position";
					positionResults.results.push(positions[symbol]);
				}
				if (positionResults.results.length) {
					var text = "{} Stocks".format($("#PositionsContainer").is(":visible")?"Hide":"Show");
					$("#ShowPositions").parent().show().find(".btnLabel.ctrElements").text(text);
				}
				var newCallback = function() {
					if(callback) {
						callback();
					}
					$("#PositionsContainer").hide();
					decorator();
					
				}
				new Marshaler().marshal(positionResults, "#PositionsContainer", ".positionsChild",newCallback,"#PositionsContainer");
			} else {
				$("#PositionsContainer").hide();
				$("#ShowPositions").parent().hide();			
				decorator();
			}
		}
		//a little hackery with the userId so that we can cache the current user's portfolio
		var userId = (new WeSeed().loggedInUserId==user.id)?"current":user.id;
		new WeSeed().rest.getPortfolio(userId,portfolioSuccessCallback);
	
		//get the list of users that you follow
		var followSuccessCallback = function(fResults) {
			new Marshaler().marshal(fResults, "#FolloweesContainer", ".followee", callback,"#FolloweesHeader");
			//only show "unfollow" button if the profile is for the logged on user
			if (user.isLoggedOnUser) {
				$(".iClose").show();
			} else {
				$(".iClose").hide();
			}
			eventHandlerMobile.onload();
		}
		new WeSeed().rest.getFollowees(user.id,followSuccessCallback);
		
	} else {
			if (userId) {
				var serializedLoginCallback = "new WeSeed().getProfile(\"{}\")".format(userId);
			}
			//make asynchronous so that the view will properly update
			window.setTimeout(function(){new WeSeed().showLogin(serializedLoginCallback)},100);
	}
	new Marshaler().showErrors(results);
}

WeSeed.prototype.callLoginCallback = function() {
	//trace("...WeSeed::callLoginCallback( )");
	var called = false;
	if (this.__loginCallback) {
		eval(this.__loginCallback);
		this.__loginCallback = null;
		called = true;
	}
	return called;
}
/** serializedLoginCallback is a string that will be eval'd after login successfully completes*/
WeSeed.prototype.showLogin = function(serializedLoginCallback) {
	//trace("...WeSeed::showLogin( )");
	$("p.iLoading").hide();
	if (!this.isLoggedIn) {
		new HistoryManager().addHistory("new WeSeed().showLogin('{}')".format(serializedLoginCallback));
		trackPage("showLogin");
		$(".contentLoad").hide();
		$("#LoginForm").show();
		$(".loginTitle").text("Let's Buy Some Companies");
		if (serializedLoginCallback) {
			this.__loginCallback = serializedLoginCallback;
		}	
	} else if (serializedLoginCallback) {
		eval(serializedLoginCallback);
	}
}

WeSeed.prototype.showLoggedIn = function(user) {
	//trace("...WeSeed::showLoggedIn( )");
	//change any dom elements
	$("div.signUpInfo").html("Hello {}!".format(user.screenName));
	$("#signOutLink").show();
	document.title = "{} - {}".format(document.title.split("-")[0].trim(),user.screenName);
	this.isLoggedIn = true;
	this.loggedInUserId = user.id;
}

WeSeed.prototype.login = function(username,password, callback) {
	//trace("...WeSeed::login( )");
	new Marshaler().clearErrorsAndMessages();
	
	new WeSeed().transition();

	var newCallback = function() {
		if (callback) {
			callback();
		}
		$("p.iLoading").hide();
		$("#Profile").slideDown();
		eventHandlerMobile.onload();
		
	}
	var successCallback = function(results) {
		//determine if we're logged in
		if (!results.errors.length) {
			var user = results.results[0]
			var called = new WeSeed().callLoginCallback();
			if (!called) {
				//do we need to add a history point here?
				new WeSeed().showProfileFromResults(results, newCallback);
			}
			new WeSeed().showLoggedIn(user);
			
		} else {
			var showLoginFunction = function() {
				new WeSeed().showLogin();
				new Marshaler().showErrors(results);
			};
			window.setTimeout(showLoginFunction,200);
		}
	}
	this.rest.login(username,password,successCallback);
}

WeSeed.prototype.showSignup = function(serializedLoginCallback) {
	//trace("...WeSeed::showSignup( )");
	new HistoryManager().addHistory("new WeSeed().showSignup('{}')".format(serializedLoginCallback));
	trackPage("showSignup");
	$(".contentLoad").hide();
	$("#signup_newPassword").val("");
	$("#signup_confirmPassword").val("");
	$("#SignupForm").show();
	$(".loginTitle").text("Let's Buy Some Companies");
	$("p.iLoading").hide();
	if (serializedLoginCallback) {
		this.__loginCallback = serializedLoginCallback;
	}	
}
WeSeed.prototype.signup = function(userData, callback) {
	//trace("...WeSeed::signup( )");
	new Marshaler().clearErrorsAndMessages();
	new WeSeed().transition();

	var newCallback = function() {
		if (callback) {
			callback();
		}
		$("p.iLoading").hide();
		$("#Profile").show();
		eventHandlerMobile.onload();
		
	}
	var successCallback = function(results) {
		//determine if we're logged in
		if (!results.errors.length) {
			var user = results.results[0]

			if (!new WeSeed().callLoginCallback()) {
				new WeSeed().showProfileFromResults(results, newCallback);
			}
			
			new WeSeed().showLoggedIn(user);
		} else {
			window.setTimeout(function(){new WeSeed().showSignup(); new Marshaler().showErrors(results)},100);
		}
	}
	this.rest.signup(userData ,successCallback);
}

WeSeed.prototype.showPassword = function(serializedLoginCallback) {
	//trace("...WeSeed::showPassword( )");
	new HistoryManager().addHistory("new WeSeed().showPassword('{}')".format(serializedLoginCallback));
	trackPage("showPassword");
	$(".contentLoad").hide();
	$("#PasswordForm").show();
	$(".loginTitle").text("Forgot Pasword?");
	$("p.iLoading").hide();
	if (serializedLoginCallback) {
		this.__loginCallback = serializedLoginCallback;
	}	
}
WeSeed.prototype.requestResetPassword = function(email) {
	//trace("...WeSeed::requestResetPassword( )");
	var successCallback = function(results) {
		new Marshaler().showErrors(results);
		new Marshaler().showMessages(results);
		if (!results.errors.length) {
			new WeSeed().showPasswordRequested(email);
		} else {
			new Marshaler().showErrors(results);
		}
	}
	this.rest.requestResetPassword(email, successCallback);
}
WeSeed.prototype.showResetPassword = function(token, serializedLoginCallback) {
	//trace("...WeSeed::showResetPassword( )");
	new HistoryManager().addHistory("new WeSeed().showResetPassword('{}','{}')".format(token,serializedLoginCallback));
	trackPage("showResetPassword");
	if (serializedLoginCallback) {
		this.__loginCallback = serializedLoginCallback;
	}	
	var successCallback = function(results) {
		var hasAuthError = false;
		var errors = results.errors;
		if (errors.length) {
			for (var i=0;i<errors.length;i++) {
				if (errors[i].name == "AUTHENTICATION") {
					hasAuthError = true;
					break;
				}
			}
		}
		$(".contentLoad").hide();
		$("p.iLoading").hide();
		if (!hasAuthError) {
			var email = results.info["email"];
			$("#ChangePasswordForm").show();
			$(".loginTitle").text("Change password for account with email: {}".format(email));
			$("#resetPasswordEmail").val(email);
		} else {
			new WeSeed().showPassword();
			new Marshaler().showErrors(results);			
		}
	}
	this.rest.checkResetPasswordToken(token, successCallback);
}

WeSeed.prototype.showPasswordRequested = function(email) {
	//trace("...WeSeed::showPasswordRequested( )");
	new HistoryManager().addHistory("new WeSeed().showPasswordRequested()");
	trackPage("showPasswordRequested");
	$(".contentLoad").hide();
	$("p.iLoading").hide();
	$("#PasswordRequested").show();
	$(".loginTitle").text("Forgot Pasword?");
	$("#PasswordRequested").find(".secBody").text("Your password has been reset.  Click on the link sent to {} to create your new password.".format(email));
	
}
WeSeed.prototype.resetPassword = function(email, newPassword, confirmPassword) {
	//trace("...WeSeed::resetPassword( )");
	var resetPasswordCallback = function(results) {
		var hasAuthError = false;
		var errors = results.errors;
		if (errors.length) {
			for (var i=0;i<errors.length;i++) {
				if (errors[i].name == "AUTHENTICATION") {
					hasAuthError = true;
					break;
				}
			}
		}
		if (!hasAuthError) {
			new WeSeed().login(email, newPassword);
		} else {
			new Marshaler().showErrors(results);
	
		}
	}

	this.rest.resetPassword(email, newPassword, confirmPassword, resetPasswordCallback);
}

WeSeed.prototype.follow = function(userId,callback) {
	//trace("...WeSeed::follow( )");
	this.__toggleFriend(userId,"follow",callback)
}
WeSeed.prototype.unfollow = function(userId,callback) {
	//trace("...WeSeed::unfollow( )");
	this.__toggleFriend(userId,"unfollow",callback)
}
WeSeed.prototype.__toggleFriend = function(userId,methodName,callback) {
	//trace("...WeSeed::__toggleFriend( )");
	var successCallback = function(results) {
		new Marshaler().clearErrorsAndMessages();
		new Marshaler().showErrors(results);
		new Marshaler().showMessages(results);

		//show the current user's profile
		var successCallback = function() {
			//scroll to the followees
			window.setTimeout(function(){window.scrollTo(window.document.body.scrollLeft,$("#FolloweesHeader").eq(0).offset().top)},500);
		}
		new WeSeed().getProfile("current", successCallback);
	}
	this.rest[methodName](userId, successCallback);
}
/** selector is the jquery selector of the dom element to show once the view has changed*/
WeSeed.prototype.__prepareChangeAndGetCallback = function(selector) {
	//trace("...WeSeed::__prepareChangeAndGetCallback( )");
	new WeSeed().transition();
	$("#messages, #error").empty();
	
	var callback = function() {
		$("p.iLoading").hide();
		$(selector).show();
		eventHandlerMobile.onload();
	};
	return callback;
}

WeSeed.prototype.transition = function() {
	//trace("...WeSeed::transition( )");
	$("p.iLoading").show();
	$(".contentLoad").hide();
}

function Marshaler() {
	if (!Marshaler.__instance) {
		Marshaler.__instance = this;
	} else {
		return Marshaler.__instance; 
	}
}
Marshaler.prototype.clearErrorsAndMessages = function() {
	this.showErrors(null);
	this.showMessages(null);
}
Marshaler.prototype.__showMessages = function(messages, selector) {
	var html = "";
	if (messages && messages.length) {
		html = "<ul><li>{}</li></ul>".format(messages.join("</li><li>"));
	}
	$(selector).html(html);
}
Marshaler.prototype.showErrors = function(results) {
	var errors = null;
	if (results) {
		if (results.errors) {
			errors = map(function(x){return x.message},results.errors);
		} else {
			errors = results;
		}
	}
	this.__showMessages(errors,"#errors,.errors");
	
	//find the first visible error container
	var containers = $(".errors").parent(":visible");
	if (containers.length && errors && errors.length) {
		window.scrollTo(window.document.body.scrollLeft,containers.eq(0).offset().top);
	} 
}
Marshaler.prototype.showMessages = function(results) {
	var messages = null;
	if (results) {
		messages = results.messages;
	}
	this.__showMessages(messages,"#messages");
}
Marshaler.prototype.marshal = function(results, parentSelector, childSelector, callback, associateSelector) {
	//get the parent
	var parent = $(parentSelector);
	//if there is no child, or child is the same as parent, then assume only one result
	var child;
	if (!childSelector || (childSelector == parentSelector)) {
		child = parent;
		if (results.results.length) { 
			results.results = [results.results[0]]
		}
	} else {
		if (parent[0].nodeName.toLowerCase() == "table") {
			parent = parent.find("tbody");
		}
		var templateChild = parent.children(":first").clone();
		parent.empty();
		parent.append(templateChild);
		if (results.results.length) { 
			templateChild.show();
		} else {
			templateChild.hide();
		}
		child = parent.children("{}:first".format(childSelector));
	}
	
	if (child != parent && !results.results.length && associateSelector) {
		//hide any associates when there are no results
		$(associateSelector).hide();
	} else if (associateSelector) {
		$("{}:not(.substituted)".format(associateSelector)).show();
	}
	//clean up after any substitutions
	child.find(".substituted").remove();
	
	//iterate over results and marshal values from result into child element
	for (var i=0;i<results.results.length;i++) {
		//clone the child element and make a new one
		var clone = null;
		if (i+1 < results.results.length) {
			clone = child.clone()
		}
		var result = results.results[i];
		var objectName = result.WStype.substring(0,1).toLowerCase() + result.WStype.substring(1);
		for (var name in result) {
			var value = result[name];
			value = (value=="null")?"":value; 
			//find the element for this property of the result 
			var els = child.find(".mobile.{}.{}".format(objectName,name));	
			for (var ii=0;ii<els.length;ii++) {
				var el = $(els[ii]);
				el.show();
				//check the className for an attribute
				var classNames = el.attr("class").split(" ");
				var attributeName = null;
				for (var j=0;j<classNames.length;j++) {
					var className = classNames[j];
					if (className.startsWith("attribute\\=")) {
						attributeName = className.split("=")[1];
						break;
					}
				}
				
				if (isArray(value)) {
						var childEl = el.children().eq(0);
						for (var j=0;j<value.length;j++) {
							var childClone = null;
							if (j+1 < value.length) {
								childClone = childEl.clone()
							}
							
							this.marshalValue(childEl,value,attributeName);

							if (childClone) {
								childEl = childClone;
								childClone.appendTo(el);
							}
						} 
				} else {
					this.marshalValue(el,value,attributeName);
				}
			}
		}
		if (clone) {
			child = clone;
			child.appendTo(parentSelector);
		}
		child.show();
		parent.show();
	}
	this.showErrors(results);
	this.showMessages(results);
	
	if (callback) {
		callback(results);
	}
}
Marshaler.prototype.marshalValue = function(element,value,attributeName) {
	var hasSubstitution = false;
	if (attributeName) {
		hasSubstitution = element.attr(attributeName).contains(SUBSTITUTION_TOKEN);
	} else {
		hasSubstitution = element.text().contains(SUBSTITUTION_TOKEN);
	}	
	
	var clone = null;
	if (hasSubstitution) {
		//if there is a string substitution to be made, then we'll need to make a pristine copy of the original dom element
		//that way, we'll preserve the string substitution token after we perform the substitution
		//the marshaller will take care of removing the "used" elements and replacing them with the pristine ones the next time it marshals
		clone = element.clone();
		clone.hide();
	}
	
	if (attributeName) {
		if (hasSubstitution) {
			element.attr(attributeName, element.attr(attributeName).format(value));
		} else {
			element.attr(attributeName, value);
		}
	} else {
		if (hasSubstitution) {
			element.text(element.text().format(value));
		} else {
			element.text(value);
		}
	}
	
	if (clone) {
		element.before(clone[0]);
		//the "substituted" class is how the marshaler will know which elements to remove when it wants to "reset" the dom
		element.addClass("substituted");
	}
}

function Cache() {
	if (!Cache.__instance) {
		Cache.__instance = this;
	} else {
		return Cache.__instance; 
	}
}

var Validator = {};
Validator.validate = function() {
	var errors = [];
	var ids = ["signup_username","signup_firstName","signup_lastName","signup_screenName","signup_newPassword","signup_confirmPassword"];
	var empties = filter(function(x){return !$("#{}".format(x)).val()},ids);
	var emptyLabels = [];
	for (var i=0;i<empties.length;i++) {
		emptyLabels.push($("label[for='{}']".format($("#{}".format(empties[i]))[0].name)).text());
	}
	if (emptyLabels.length) {
		errors.push("The following fields cannot be empty: {}".format(emptyLabels.join(", "))); 
	}
	if (!errors.length && ! Validator.email($("#signup_username").val())) {
		errors.push("Please enter a valid email address to sign up"); 
	}
	if (!errors.length && ($("#signup_newPassword").val() != $("#signup_confirmPassword").val())) {
		errors.push("Your password and confirmation do not match; please enter them once again"); 
	}
	if (errors.length) {
		new Marshaler().showErrors(errors);
	}
	return (errors.length)?false:true;
}
Validator.email = function(email) {
	//same heuristic as in formValidation.js without all 12k of code
	var atPos  = email.indexOf("@");
	var dotPos = email.lastIndexOf(".");
	var isValid = true;		
	if (atPos < 1 || dotPos-atPos < 2) {
		isValid = false;
	}
	return isValid;
}

//String goodies
var SUBSTITUTION_TOKEN = "{}";
String.prototype.format = function() {
	var regExp = new RegExp("\\{\\}");
	var s = this;
	for (var i=0;i<arguments.length;i++) {
		var arg = arguments[i];
		arg = (arg == null)?"":arg;
		s = s.replace(regExp,arg);
	}
	if (arguments.length == 0) {
		s = s.replace(regExp,"");
	}
	return s;
}

String.prototype.startsWith = function(start) {
	return (this.indexOf(start) == 0);
}
String.prototype.contains = function(s) {
	return (this.indexOf(s) > -1);
}
String.prototype.trim = function () {
    return this.replace(/^\s+|\s+$/, "");
}
String.prototype.toCurrency = function() {
	var s = this.replace(",","");
	var number = parseFloat(s);
	var minus = (number<0)?"-":"";
	number = Math.abs(number);
	var integer = Math.floor(number);
	var decimal = number - integer;
	integer = new String(isNaN(integer)?0:integer);
	decimal = isNaN(decimal)?0.0:decimal;
	decimal = new String(Math.round((100*decimal)));
	decimal = (decimal.length<2)?"0{}".format(decimal):decimal;
	s = "";
	var index = 0;
	for (var i=integer.length-1;i>=0;i--) {
		if (!(index % 3) && index>0) {
			s = ",{}".format(s);
		}
		s = "{}{}".format(integer[i],s);
		index++;
	}
	s = "{}{}.{}".format(minus,s,decimal);
	return s;
}

//static functions
function isArray(object) {
	return object && object.constructor === Array;
}
function map(func,list) {
	var results = [];
	for (var i=0;i<list.length;i++) {
		results.push(func(list[i]));
	}
	return results;
}
function filter(func,list) {
	var results = [];
	for (var i=0;i<list.length;i++) {
		if (func(list[i])) {
			results.push(list[i]);
		}
	}
	return results;
}
function trackPage(page) {
	page = "/mobile/{}".format(page);
	pageTracker._trackPageview(page);
}
