// Prototypes
String.prototype.trim = function () {
	return this.replace(/^\s+|\s+$/g,'');
}

String.prototype.toHtmlEntities = function () {
	return this.replace(/&/g, "&amp;").replace(/</g, "&lt;"); 
}

String.prototype.fromHtmlEntities = function () {
	return this.replace(/&lt;/g, "<").replace(/&amp;/g, "&"); 
}

String.prototype.toMonth = function () 
{
	var date = new Date(); 
	var month = this.toString(); 
	
	for (var i = 0; i < date.monthNames.length; i++) 
		if (date.monthNames[i] == month) 
			return i + 1;
	
	return null;  
}

Number.prototype.addCommas = String.prototype.addCommas = function () {
	var str = this.toString().replace(/\.[0-9]*(.*)$/, ""); 
	
	var reg = /(\d+)(\d{3})/g;  
	while (reg.test(str)) 
		str = str.replace(reg, "$1,$2");
	
	return str;  
}


Date.prototype.monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; 
Date.prototype.getMonthName = function (month) 
{
	if (typeof month != 'undefined' && month !== null && month >= 1 && month <= 12) 
		return this.monthNames[month - 1]; 
	else 
		return this.monthNames[this.getMonth()]; 
}




// Globals
var debug = new Debug();
var event = new Event();
var cookie = new Cookie();
var popup = new Popup(); 
var json = new Json();  
var ajax = new Ajax();
var user = new User();
var display = new Display(); 
var dom = new DOM();
var html = new Html();
var spenderList = new SpenderList();
var expenseList = new ExpenseList();
var analyst = new ExpenseAnalyst();
var _ = null;

// Classes
function ExpenseAnalyst () 
{ 
	this.date = new Date();
	this.filterMonth = null; 
	this.filterYear = null;  
	this.id = "analyst_" + getRandomId(); 
	
	this.createExpensesTab = function () 
	{
		var ea = this; 
		var createButton = dom.button(null, "New Expense", function () 
			{				 
				var id = expenseList.autoAddExpense(); 
				getEle(id + "_name").focus(); 
			});
		
		this.filterMonth = new Select(_, this.date.monthNames, this.date.getMonthName(), 
			function () 
			{
				expenseList.filterByMonth(ea.filterMonth.getSelectedValue().toMonth());
			});
		this.filterYear = new Select(_, range(this.date.getFullYear() - 5, this.date.getFullYear() + 5), this.date.getFullYear(), 
		function () 
			{
				expenseList.filterByYear(ea.filterYear.getSelectedValue());
			});
		var prevButton = dom.button(_, "<", 
			function () 
			{
				var year = ea.filterYear.getSelectedValue();
				var thisYear = new Date().getFullYear(); 
				var month = ea.filterMonth.getSelectedValue().toMonth(); 
				
				if (month <= 1) 
				{
					if (year > thisYear - 5)
					{
						month = 12;
						year--; 
					} 					
				} else 
					month--; 
				ea.filterYear.setSelectedValue(year);
				ea.filterMonth.setSelectedValue(new Date().getMonthName(month)); 
			}); 
		var nextButton = dom.button(_, ">", 
			function () 
			{
				var year = ea.filterYear.getSelectedValue();
				var thisYear = new Date().getFullYear(); 
				var month = ea.filterMonth.getSelectedValue().toMonth(); 
				
				if (month >= 12) 
				{
					if (year < thisYear + 5)
					{
						month = 1;
						year++; 
					} 					
				} else 
					month++;
				 
				month = new Date().getMonthName(month); 
				ea.filterYear.setSelectedValue(year);
				ea.filterMonth.setSelectedValue(month); 
			}); 
		var filterControls = 
			dom.table(_, _, _, 
				dom.create("tr", _, _, _, 
					[
						dom.create("td", _, _, {align:"center"}, prevButton), 
						dom.create("td", _, _, {align:"left"}, this.filterMonth.getDomNode()), 
						dom.create("td", _, _, {align:display.isMobile() ? "center" : "left"}, this.filterYear.getDomNode()), 
						dom.create("td", _, _, {align:"center"}, nextButton)
					]
				)
			); 
		var leftTd = dom.create("td", _, _, _, createButton); 
		var rightTd = dom.create("td", _, _, _, filterControls);
		rightTd.style.width = "150px";
		var tr = dom.create("tr", _, _, _, [leftTd, rightTd]); 
		var controls = dom.table(_, _, _, tr);
		controls.style.width = "99%";  
		
		/* 
		var expense = new Expense(_, "Rent", "789.99", "Monthly", ["Me", "Chao"], 1);	 		 
		var expense2 = new Expense(_, "Water", "68", "Monthly", ["Me", "Chao", "Limei"], 2);
		this.list.addExpense(expense);
		this.list.addExpense(new Expense()); 
		this.list.addExpense(expense2);
		this.list.addExpense(new Expense());*/		
		
		var container = dom.create("div", null, null, null, [controls, expenseList.getDomNode()]);
		return container;
	}
	
	this.createSettingsTab = function () 
	{
		var createButton = dom.button(null, "New Spender", 
		function () 
		{				 
			var id = spenderList.autoAddSpender(); 
			getEle(id + "_name").focus(); 
		});
		createButton.style.fontSize = "0.6em"; 
	
		var spenders = dom.fieldSet(_, _, _, "Spenders");
		spenders.appendChild(createButton);
		spenders.appendChild(spenderList.getDomNode()); 
		var container = dom.create("div", _, _, _, [spenders]);
		container.style.fontSize = "1em";  		
		return container;  		
	}
	
	this.getAnalysis = function () 
	{
		var summary = expenseList.getExpenseSummary();
		var sections = [];  
	
		if (summary.count)
			var intro = dom.text("Based on expenses for " + this.filterMonth.getSelectedValue() + ", " + this.filterYear.getSelectedValue() + ".");
		else 
			var intro = dom.text("For " + this.filterMonth.getSelectedValue() + " " + this.filterYear.getSelectedValue() + ", there isn't any expense to analyze.");

		sections.push(intro); 
		
		if (summary.count) 
		{
			var titles = dom.create("tr", _, _, _, 
				[
					dom.create("th", _, "max_table_title", {align: "left"}, "Spender"), 
					dom.create("th", _, "max_table_title_right", {align: "right"}, "Daily"),
					dom.create("th", _, "max_table_title_right", {align: "right"}, "Weekly"),
					dom.create("th", _, "max_table_title_right", {align: "right"}, "Monthly"),
					dom.create("th", _, "max_table_title_right", {align: "right"}, "Yearly")
				]);
			 
			var summaryRows= [titles];
			if (summary.spenderCount > 1)
				summary.spenderTotals["Total   "] = summary.total;   
			for (var spender in summary.spenderTotals) 
			{
				var spenderSummary = dom.create("tr", _, _, _, 
					[
						dom.create("th", _, spender == "Total   " ? "max_top_border" : _, {align: "left"}, spender.toHtmlEntities()), 
						dom.create("td", _, spender == "Total   " ? "max_top_border" : _, {align: "right"}, Math.round(summary.spenderTotals[spender] * 12 / 365).addCommas()),
						dom.create("td", _, spender == "Total   " ? "max_top_border" : _, {align: "right"}, Math.round(summary.spenderTotals[spender] * 12 / 52).addCommas()),
						dom.create("td", _, spender == "Total   " ? "max_top_border" : _, {align: "right"}, summary.spenderTotals[spender].addCommas()),
						dom.create("td", _, spender == "Total   " ? "max_top_border" : _, {align: "right"}, (summary.spenderTotals[spender] * 12).addCommas())
					]); 
				summaryRows.push(spenderSummary); 
			}
			summary = dom.table(_, _, _, summaryRows); 
			
			sections.push(dom.create("br")); 
			sections.push(dom.create("br"));
			var expenses = dom.fieldSet(_, _, _, "Expenses", [summary]);
			sections.push(expenses);
			sections.push(dom.create("br"));
			
			
			
			//var income = dom.fieldSet(_, _, _, "Income & Savings");			
			//sections.push(income);			
		}
		
		return dom.create("div", _, _, _, sections);  
	}
	
	this.createAnalysisTab = function () 
	{
		return dom.create("div", this.id, _, _, this.getAnalysis()); 
	}
	
	this.updateAnalysis = function () 
	{
		var div = getEle(this.id);
  
		if (div)
		{ 
			if (div.firstChild) 
				div.removeChild(div.firstChild); 
			div.appendChild(this.getAnalysis()); 
		}
	}
	
	var app = getEle("web_application"); 
	app.innerHTML = ""; 
	var tabs = new TabContainer("app", app);
	
	tabs.addTab("expenses", "Expenses", this.createExpensesTab());
	tabs.addTab("analysis", "Analysis", this.createAnalysisTab(), _, function () {analyst.updateAnalysis()});
	tabs.addTab("settings", "Settings", this.createSettingsTab()); 
	
	spenderList.load(); 
	expenseList.load();
	
	user.onLogin = user.onLogout = 
		function () {
			spenderList.load(false); 
			expenseList.load();
			analyst.updateAnalysis(); 	
		}; 
}

function ExpenseList (id) 
{
	this.id = defined(id) ? id : ("expense_list_" + getRandomId());
	this.list = {};  
	this.domNode = null; 
	
	var date = new Date(); 
	this.filterMonth = date.getMonth() + 1;
	this.filterYear = date.getFullYear();
	this.loadingMessage = {
		id:"loading", 
		getDomNode:
			function() 
			{
				if (!this.domNode) 
					this.domNode = dom.create("tr", _, _, _, dom.create("td", _, "gray", {align:"left"}, dom.text("Loading..."))); 
				return this.domNode
			}}; 
	   	
	this.createDomNode = function ()
	{
		// Expense List Headers
		var delTh = dom.create("th", _, "max_expense_title", _, "&nbsp;");
		delTh.style.width = "1%";
		var nameTh = dom.create("th", _, "max_expense_title", _, "Expense"); 
		var amountTh = dom.create("th", _, "max_expense_title", {style:"width:20%;text-align:right"}, "Amount");
		amountTh.style.width = "20%";  // for stupid ie
		amountTh.style.textAlign = "right";  // for stupid ie
		var freqTh = dom.create("th", _, "max_expense_title", {style:"width:20%"}, "Frequency");
		freqTh.style.width = "20%";  
		var spendersTh = dom.create("th", _, "max_expense_title", {style:"width:20%"}, "Spenders");
		spendersTh.style.width = "20%";  		
		var titleTr = dom.create("tr", _, _, _, [nameTh, amountTh, freqTh, spendersTh, delTh]); 
		var body = dom.create("tbody", _, _, _, titleTr);
		var expensesTable = dom.create("table", _, _, _, body);
		
		// Expense Summary
		var summaryTable = dom.create("div", this.id + "_summary", _, _, this.createSummaryTable()); ; 
		
		this.domNode = dom.create("div", this.id, "max_expense_list", _, [expensesTable, summaryTable]);
	}
	
	this.getExpenseSummary = function () 
	{
		var expenses = {total: 0, count: 0, spenderTotals:{}, spenderCount: 0};
		for (var id in this.list) 
		{
			if (this.list[id].amount && this.list[id].spenders && this.list[id].spenders.length) 
			{
				switch(this.list[id].freq) 
				{
					case "Once": freqMultiplier = 1; break;
					case "Daily": freqMultiplier = 365/12; break; 
					case "Weekly": freqMultiplier = 365/7/12; break; 
					case "Semi-Weekly": freqMultiplier = 365/7/6; break; 
					case "Bi-Weekly": freqMultiplier = 365/7/24; break; 
					case "Monthly": freqMultiplier = 1; break; 
					case "Quarterly": freqMultiplier = 1/3; break;
					case "Semi-Yearly": freqMultiplier = 1/6; break; 
					case "Yearly": freqMultiplier = 1/12; break; 
					default: 
						freqMultiplier = 1; 
						debug.log("ExpenseList.createSummary() Invalid Frequency: " + this.list[id].freq);  
				}
				expenses.count++;
				amount = Math.round(this.list[id].amount.toString().replace(/[^0-9.]/g, "") * freqMultiplier);
				expenses.total +=  amount;
				//if (this.list[id].spenders.length > expenses.spenderCount) 
				//	expenses.spenderCount = this.list[id].spenders.length;  
				for (var i = 0; i < this.list[id].spenders.length; i++)
				{  
					if (expenses.spenderTotals[this.list[id].spenders[i]]) 
						expenses.spenderTotals[this.list[id].spenders[i]] += parseFloat(amount / this.list[id].spenders.length);
					else 
						expenses.spenderTotals[this.list[id].spenders[i]] = parseFloat(amount / this.list[id].spenders.length);
				} 				
			}
		}
		for (var spender in expenses.spenderTotals)
			expenses.spenderCount++;
		
		return expenses; 
	}
	
	this.createSummaryTable = function () 
	{
		var table = null; 
		var expenses = this.getExpenseSummary();  
		var freqMultiplier = 1;
		var amount = 0;     
		
		if (expenses.count)
		{
			var totalTh = dom.create("th", _, _, _, dom.text("Total Amount: "));
			totalTh.style.width = "7em";
			totalTh.align = "right";
			var totalTd = dom.create("td", _, _, _, dom.text(expenses.total ? expenses.total.addCommas() : 0));
			
			var trs = []; 
			if (expenses.spenderCount > 1)
			{ 
				var maxCount = display.isMobile() ? 2 : 5;  
				var count = 1;
				var width = Math.round(70 / maxCount) + "%"; 
				var tds = [totalTh, totalTd];   
				for (var spender in expenses.spenderTotals) 
				{
					var th = dom.create("th", _, _, _, dom.text(spender + ": "));
					th.align = "right";
					//th.style.width = width;   
					var td = dom.create("td", _, _, _, dom.text(expenses.spenderTotals[spender].addCommas()));
					tds.push(th); 
					tds.push(td); 
					count++; 
					if (count >= maxCount) 
					{
						var tr = dom.create("tr", _, _, _, tds); 
						trs.push(tr);
						count = 0;
						tds = [];   
					}
				}
				if (count) 
				{
					var tr = dom.create("tr", _, _, _, tds); 
					trs.push(tr);
				}
			} else 
				trs.push(dom.create("tr", _, _, _, [totalTh, totalTd])); 
			var body = dom.create("tbody", _, _, _, trs);
			table = dom.create("div", _, "max_expense_list_summary", _, dom.create("table", _, _, _, body)); 			
		} else 
			table = dom.create("div");  
		
		return table; 
	}
	
	this.updateSummary = function () 
	{
		var summaryDiv = getEle(this.id + "_summary"); 
		if (summaryDiv)
		{ 
			if (summaryDiv.firstChild && summaryDiv.firstChild.tagName) 
				summaryDiv.removeChild(summaryDiv.firstChild); 
			summaryDiv.appendChild(this.createSummaryTable()); 
		} 
	}
	
	this.filter = function (month, year) 
	{
		var changed = false; 
		
		if (hasValue(month) && this.filterMonth != month) 
		{
			changed = true; 
			this.filterMonth = month; 	
		}
		
		if (hasValue(year) && this.filterYear != year) 
		{
			changed = true; 
			this.filterYear = year; 
		}
		
		if (changed) 
		{
			this.load();  			
		}
	}
	
	this.filterByMonth = function (month) 
	{
		this.filter(month); 
	}
	
	this.filterByYear = function (year) 
	{
		this.filter(_, year); 
	}

	this.getDomNode = function () 
	{
		if (!this.domNode) 
			this.createDomNode(); 
		
		return this.domNode; 
	} 	
	
	
	
	this.addExpense = function (expense) 
	{
		if (hasValue(expense)) 
		{
			if (!this.domNode) 
				this.createDomNode(); 
			
			this.list[expense.id] = expense; 
			this.domNode.firstChild.firstChild.appendChild(expense.getDomNode()); 
		}
	}
	
	this.autoAddExpense = function () 
	{
		if (this.list) 
		{
			var emptyCount = 0;
			var firstEmpty = null;   
			for (var id in this.list) 
				if (this.list[id].isEmpty())
				{ 
					emptyCount++;
					
					if (!firstEmpty) 
						firstEmpty = id; 
				}
			if (emptyCount == 0)
			{
				var expense = new Expense(); 
				this.addExpense(expense);
				firstEmpty = expense.id;   
			}
			
			return firstEmpty; 
		}  
	}
	
	this.removeExpense = function(id, deleteFromServer) 
	{
		if (this.list && this.list[id])
		{
			this.domNode.firstChild.firstChild.removeChild(this.list[id].getDomNode());
			var serverId = this.list[id].serverId; 
			delete this.list[id];
			if (defined(deleteFromServer) && deleteFromServer && serverId) 
				ajax.send("?obj=expenseList&action=delete&id=" + escape(serverId) + 
					"&month=" + escape(this.filterMonth) + "&year=" + escape(this.filterYear), 
					function (req) 
					{
					}, {add_random_number:true});
			this.updateSummary(); 
		}
	}
	
	this.removeAllExpense = function () 
	{
		if (this.list) 
			for (var id in this.list) 
				this.removeExpense(id);
		this.updateSummary();
	}
	
	this.save = function () 
	{
		var lst = new Array(); 
		
		for (var id in this.list) 
		{
			var expense = {}; 
			
			if (this.list[id].changed) 
			{
				expense.id = id; 
				expense.name = this.list[id].name; 
				expense.amount = this.list[id].amount;
				if (!expense.name.toString().length && !expense.amount.toString().length)
					continue;  
				expense.freq = this.list[id].freqObj.getSelectedValues(); 
				expense.spenders = this.list[id].spendersObj.getSelectedValues();
				expense.serverId = this.list[id].serverId;
				lst.push(expense);
				this.list[id].changed = false; 
			}    
		}
		
		var list_str = json.encode(lst);
		if (!lst.length) return; 
		var month = this.filterMonth; 
		var year = this.filterYear;
		ajax.send("?obj=expenseList&action=save", 
			function (req) 
			{
				if (req.responseText) 
				{
					serverIds = json.decode(req.responseText);
					if (serverIds) 
					{
						for (var id in serverIds) 
							if (defined(expenseList.list[id])) 
								expenseList.list[id].serverId = serverIds[id]; 
					}
				}
			}, {method:"post", params:{"list":list_str, "month":month, "year": year}});
	
		this.updateSummary(); 
	} 
	
	this.load = function () 
	{
		this.removeAllExpense(); 
		this.addExpense(this.loadingMessage);
		var month = this.filterMonth; 
		var year = this.filterYear; 
		ajax.send("?obj=expenseList&action=load",
			function (req)  
			{
				expenseList.removeAllExpense(); 
				if (req.responseText)
				{ 					 
					var list = json.decode(req.responseText);
					if (isArray(list) && list.length) 
					{
						for (var i in list)
							expenseList.addExpense(new Expense(_, list[i].name, list[i].amount, list[i].freq, list[i].spenders, list[i].serverId));
					}  
				}
				expenseList.addExpense(new Expense());
				expenseList.updateSummary();  
				analyst.updateAnalysis();
			}, {method:"post", params:{"month": month, "year": year}}); 
		
	}
	
	setInterval("expenseList.save()", 10000); 
}

function Expense(id, name, amount, freq, spenders, serverId)
{
	this.id = hasValue(id) ? id : ("expense_" + getRandomId()); 
	this.serverId = hasValue(serverId) ? serverId : ""; 
	this.amount = hasValue(amount) ? amount : ""; 
	this.freq = hasValue(freq) ? freq : "Once";
	this.freqObj = null;  
	this.name = hasValue(name) ? name : ""; 
	this.spenders = hasValue(spenders) || hasValue(serverId) ? spenders : spenderList.getDefaultNames();
	this.spendersObj = null;  
	this.domNode = null; 
	
	this.createDomNode = function () 
	{
		var expense = this;  

		// delete
		var delButton = dom.button(_, "X",
			function () 
			{
				popup.setTitle("Delete Expense"); 
				popup.setBody("Delete this expense" + (expense.name ? " (" + expense.name + ")" : "") + "?<br><br>" + 
					html.button(_, "Yes", "popup.hide(); expenseList.removeExpense(\"" + expense.id + "\", true)") + "&nbsp;" + 
					html.button(_, "Cancel", "popup.hide()")); 
				popup.show();
			});
		delButton.tabIndex = -1; 
		if (display.isMobile()) 
		{
			delButton.style.width = "0.1em";
			delButton.style.margin = "0px -10px 0px 0px";
			delButton.style.fontSize = "0.1em";
		} 
		var delTd = dom.create("td", _, _, _, delButton); 	 		

		// name
		var name = dom.inputSelect(this.id + "_name", this.name, "_EXPENSE_NAMES");
		event.add(name, "blur", 
			function() {
				var name = getEle(expense.id + "_name").value;
				if (hasValue(name) && name !== expense.name)
				{
					expense.name = name;
					expense.changed = true; 
					expenseList.autoAddExpense();
					expenseList.save(); 
				}
			}); 
		event.add(name, "keydown", 
			function(evt) 
			{
				if (checkEnter(evt)) 
				{
					var inputs = document.getElementsByTagName("input");
					var found = false;  
					if (hasValue(inputs) && inputs.length) 
						for (var i = 0; i < inputs.length; i++) 
							if (inputs[i].id == name.id)
								found = true; 
							else if (found) 
							{
								inputs[i].focus()
								break; 
							}
				}
			}); 
		var nameTd = dom.create("td");
		nameTd.appendChild(name);
		  
		// amount
		var amount = dom.inputSelect(this.id + "_amount", this.amount.addCommas());
		amount.style.textAlign = 'right';
		event.add(amount, "blur", 
			function() {
				var amount = getEle(expense.id + "_amount").value.replace(/[^0-9.\+\-*/]|/g, "");
				try { 
					if (/\+|\-|\/|\*/.test(amount)) 
						amount = eval(amount);
				} catch (e) {}; 
				amount = parseFloat(amount.toString().replace(/[^0-9.]|/g, ""));  
				if (isNaN(amount)) 
					amount = ""; 
				else 
					amount = Math.round(amount); 
				if (hasValue(amount) && amount != expense.amount)
				{
					expense.amount = amount;					 
					expense.changed = true;
					expenseList.autoAddExpense();
					expenseList.save(); 
				}
				getEle(expense.id + "_amount").value = expense.amount.addCommas();
			}); 
		event.add(amount, "keydown", 
			function(evt) 
			{
				if (checkEnter(evt)) 
				{
					if (amount.value) 
						amount.blur();  
					var inputs = document.getElementsByTagName("input");
					var found = false;  
					if (hasValue(inputs) && inputs.length) 
						for (var i = 0; i < inputs.length; i++) 
							if (inputs[i].id == amount.id)
								found = true; 
							else if (found && inputs[i].id.indexOf("_name") > 0) 
							{
								inputs[i].focus(); 
								break; 
							}
				}
			}); 
		var amountTd = dom.create("td"); 
		amountTd.appendChild(amount);

		// freq
		this.freqObj = new Select(_, ["Once", "Daily", "Weekly", "Bi-Weekly", "Monthly", "Quarterly", "Semi-Yearly", "Yearly"], this.freq ? this.freq : "Once", 
			function() {				
				var value = expense.freqObj.getSelectedValues();
				if (value !== expense.freq)
				{ 
					expense.freq = value;
					expense.changed = true;
					expenseList.save();
				}
			}, false);
		//freq.style.width = display.isMobile() ? "50px" : "100%"; 				
		var freqTd = dom.create("td", _, _, _, this.freqObj.getDomNode());
		
			
		// spenders
		this.spendersObj = new Select(_, spenderList.getNames(), this.spenders, 
			function () 
			{
				var selectedValues = expense.spendersObj.getSelectedValues(); 
				if (expense.spenders !== selectedValues)
				{  
					expense.spenders = selectedValues;
					expense.changed = true; 
					expenseList.save();
				} 
			}, true);
		//spenders.style.width = freq.style.width;
		var spendersTd = dom.create("td", _, _, _, this.spendersObj.getDomNode()); 	
		
		
		// expense
		this.domNode = dom.create("tr", this.id, "max_expense", _, [nameTd, amountTd, freqTd, spendersTd, delTd]); 
	}
	
	this.getDomNode = function () 
	{
		if (!this.domNode) 
			this.createDomNode(); 
		return this.domNode; 
	}
	
	this.show = function () 
	{
		if (this.gui) 
			this.gui.style.display = "";
		else 
			this.createGui();  
	}
	
	this.hide = function () 
	{
	}
	
	this.remove = function () 
	{
	}
	
	this.hasValue = function () 
	{
		if (this.domNode) 
		{
			var name = getEle(this.id + "_name"); 
			var amount = getEle(this.id + "_amount"); 
			
			return hasValue(name.value) && name.value.length || hasValue(amount.value) && amount.value.length; 
		} else 
			return false; 
	}
	
	this.isEmpty = function () 
	{
		return !this.hasValue(); 
	}
}

function SpenderList (id) 
{
	this.id = defined(id) ? id : ("spender_list_" + getRandomId());
	this.list = {};  
	this.domNode = null; 
	
	this.loadingMessage = {
		id:"loading", 
		getDomNode:
			function() 
			{
				if (!this.domNode) 
					this.domNode = dom.create("tr", _, _, _, dom.create("td", _, "gray", {align:"left"}, dom.text("Loading..."))); 
				return this.domNode
			}}; 
	   	
	this.createDomNode = function ()
	{
		// Spender List Headers
		var delTh = dom.create("th", _, "max_table_title", _, "&nbsp;");
		delTh.style.width = "1%";
		var nameTh = dom.create("th", _, "max_table_title", _, "Spender");
		var defTh = dom.create("th", _, "max_table_title", _, "Set as Default");
		defTh.style.width = "7em"; 
		defTh.style.textAlign = "center";
		var titleTr = dom.create("tr", _, _, _, [nameTh, defTh, delTh]); 
		var body = dom.create("tbody", _, _, _, titleTr);
		var table = dom.create("table", _, _, _, body); 
				
		this.domNode = dom.create("div", this.id, "max_table_list", _, table);		
	}

	this.getDomNode = function () 
	{
		if (!this.domNode) 
			this.createDomNode(); 
		
		return this.domNode; 
	} 	
	
	this.addSpender = function (spender) 
	{
		if (hasValue(spender)) 
		{
			if (!this.domNode) 
				this.createDomNode(); 
			
			this.list[spender.id] = spender; 
			this.domNode.firstChild.firstChild.appendChild(spender.getDomNode()); 
		}
	}
	
	this.autoAddSpender = function () 
	{
		if (this.list) 
		{
			var emptyCount = 0;
			var firstEmpty = null;   
			for (var id in this.list) 
				if (this.list[id].isEmpty())
				{ 
					emptyCount++;
					
					if (!firstEmpty) 
						firstEmpty = id; 
				}
			if (emptyCount == 0)
			{
				var spender = new Spender(); 
				this.addSpender(spender);
				firstEmpty = spender.id;   
			}
			
			return firstEmpty; 
		}  
	}
	
	this.removeSpender = function(id, deleteFromServer, reloadExpenseList) 
	{
		if (this.list && this.list[id])
		{
			this.domNode.firstChild.firstChild.removeChild(this.list[id].getDomNode());
			var serverId = this.list[id].serverId; 
			delete this.list[id];
			if (defined(deleteFromServer) && deleteFromServer && serverId) 
				ajax.send("?obj=spenderList&action=delete&id=" + escape(serverId));
			if (id != "loading" && (isNull(reloadExpenseList) || reloadExpenseList))  			
				setTimeout("expenseList.load()", 1000); 
		}
	}
	
	this.removeAllSpender = function (reloadExpenseList) 
	{
		if (this.list) 
			for (var id in this.list) 
				this.removeSpender(id, false, reloadExpenseList);
	}
	
	this.getNames = function (default_only) 
	{
		if (isNull(default_only))
			default_only = false; 

		var spenders = [];
		for (var id in this.list)
		{ 
			if (this.list[id].name && (!default_only || default_only && this.list[id].def)) 
				spenders.push(this.list[id].name);
		}
		return spenders;  
	}
	
	this.getDefaultNames = function () 
	{
		return this.getNames(true); 
	}
	
	this.save = function () 
	{
		var lst = new Array(); 
		
		for (var id in this.list) 
		{
			var spender = {}; 
			
			if (this.list[id].changed) 
			{
				spender.id = id; 
				spender.name = this.list[id].name; 
				spender.def = this.list[id].def;
				if (!spender.name.toString().length)
					continue;  
				spender.serverId = this.list[id].serverId;
				lst.push(spender);
				this.list[id].changed = false; 
			}    
		}
		
		var list_str = json.encode(lst);
		if (!lst.length) return; 
		ajax.send("?obj=spenderList&action=save", 
			function (req) 
			{
				if (req.responseText) 
				{
					serverIds = json.decode(req.responseText);
					if (serverIds) 
					{
						for (var id in serverIds) 
							if (defined(spenderList.list[id])) 
								spenderList.list[id].serverId = serverIds[id]; 
					}
					expenseList.load(); 
				}
			}, {method:"post", params:{"list":list_str}});
	} 
	
	this.load = function (reloadExpenseList) 
	{
		this.removeAllSpender(reloadExpenseList); 
		this.addSpender(this.loadingMessage);
		  
		ajax.send("?obj=spenderList&action=load",
			function (req)  
			{
				spenderList.removeAllSpender(reloadExpenseList);
				if (req.responseText)
				{ 		
					var list = json.decode(req.responseText);
					if (isArray(list) && list.length) 
					{
						for (var i in list)
							spenderList.addSpender(new Spender(_, list[i].name, list[i].def, list[i].serverId));
					}  
				}
				//spenderList.addSpender(new Spender());
			}, {add_random_number:true}); 
		
	}
	
}

function Spender(id, name, def, serverId)
{
	this.id = hasValue(id) ? id : ("spender_" + getRandomId()); 
	this.serverId = hasValue(serverId) ? serverId : ""; 
	this.def = hasValue(def) && (def == 1) || isNull(serverId) ? 1 : 0; 
	this.name = hasValue(name) ? name : "";  
	this.domNode = null; 
	
	this.createDomNode = function () 
	{
		var spender = this;  

		// delete
		var delButton = dom.button(_, "X",
			function () 
			{
				popup.setTitle("Delete Spender"); 
				popup.setBody("Delete this spender" + (spender.name ? " (" + spender.name + ")" : "") + "?<br><br>" + 
					html.button(_, "Yes", "popup.hide(); spenderList.removeSpender(\"" + spender.id + "\", true)") + "&nbsp;" + 
					html.button(_, "Cancel", "popup.hide()")); 
				popup.show();
			});
		delButton.tabIndex = -1; 
		if (display.isMobile()) 
		{
			delButton.style.width = "0.1em";
			delButton.style.margin = "0px -10px 0px 0px";
			delButton.style.fontSize = "0.1em";
		} 
		var delTd = dom.create("td", _, _, _, delButton); 	 		

		// name
		var name = dom.inputSelect(this.id + "_name", this.name);
		event.add(name, "blur", 
			function() {
				var name = getEle(spender.id + "_name").value;
				if (hasValue(name) && name !== spender.name)
				{
					spender.name = name;
					spender.changed = true; 
					//spenderList.autoAddSpender();
					spenderList.save(); 
				}
			}); 
		event.add(name, "keydown", 
			function(evt) 
			{
				if (checkEnter(evt)) 
				{
					var inputs = document.getElementsByTagName("input");
					var found = false;  
					if (hasValue(inputs) && inputs.length) 
						for (var i = 0; i < inputs.length; i++) 
							if (inputs[i].id == name.id)
								found = true; 
							else if (found) 
							{
								inputs[i].focus()
								break; 
							}
				}
				
				//spenderList.autoAddSpender();
			}); 
		var nameTd = dom.create("td");
		nameTd.appendChild(name);
		  
		// default
		var attribs = {};
		attribs["checked"] = (this.def == 1) ? "checked" : null;
		attribs["type"] = "checkbox"; 
		var def = dom.create("input", this.id + "_def", _, attribs);
		def.checked = this.def;  
		def.style.backgroundColor = "#ffffff";   
		def.style.width = "auto"; 
		event.add(def, "click", 
			function() {
				var def = getEle(spender.id + "_def").checked
				if (hasValue(def) && def != spender.def)
				{
					spender.def = def;					 
					spender.changed = true;
					spenderList.save(); 
				}
			}); 
		var defTd = dom.create("td"); 
		defTd.style.textAlign = 'center';
		defTd.appendChild(def);
		
		// spender
		this.domNode = dom.create("tr", this.id, "max_spender", _, [nameTd, defTd, delTd]); 
	}
	
	this.getDomNode = function () 
	{
		if (!this.domNode) 
			this.createDomNode(); 
		return this.domNode; 
	}
	
	this.hasValue = function () 
	{
		if (this.domNode) 
		{
			var name = getEle(this.id + "_name"); 
			
			return hasValue(name.value) && name.value.length; 
		} else 
			return false; 
	}
	
	this.isEmpty = function () 
	{
		return !this.hasValue(); 
	}
}

function Debug() 
{	
	this.output = getEle('debug_output');
	this.logs = new Array();  
	
	this.log = function (msg, show_alert) 
	{
		this.logs.push(defined(msg) ? (msg === null ? "null" : (typeof msg == "string" ? msg.replace(/</g, "&lt;") : msg.toString())) : "undefined"); 
		this.showLogs();
		
		if (defined(show_alert) && show_alert) 
			alert(msg);   
	}
	
	this.alert = function(msg) 
	{
		this.log(msg, true); 
	}
	
	this.showLogs = function () 
	{
		if (this.output && this.logs.length)
			this.output.innerHTML = this.logs.join('<br>');   
	}
	
	this.dump = function (object, numeric_only) 
	{
		if (typeof object !== 'undefined' && object) 
		{
			if (typeof object == "string") 
				this.logs.push(object); 
			else 
				for (var member in object) 
				{
					try { 
						if (defined(numeric_only) && numeric_only && isNaN(parseFloat(object[member])))
							continue; 
						this.logs.push("<pre><b>" + member + '</b>: ' + object[member] + "</pre>");
					} catch (e)   
					{
						this.logs.push("<pre><b>" + member + '</b>: (Exception) ' + e.name + "</pre>");
					}
				}
			this.showLogs(); 
		} else 
			this.log(object, true); 
	}	
}

function Event () 
{
	this.add  = function (obj, event, callback) 
	{
		if (obj.addEventListener) 
			obj.addEventListener(event, callback, false); 
		else if (obj.attachEvent)
			obj.attachEvent("on" + event, callback);
		else 
			debug.log(obj + " doesn't support event (" + event + ") attachments.");
	}
	
	this.remove = function (obj, event, callback) 
	{
		if (obj.removeEventListener) 
			obj.removeEventListener(event, callback, false);
		else if (obj.detachEvent) 
			obj.detachEvent(event, callback); 
		else 
			debug.log(obj + " doesn't support event (" + event + ") detachments.");
	}
	
	this.stop = function (eventObj) 
	{
		var e = window.event || eventObj; 
		if (typeof e === "undefined" || !e) return;  
	 
		e.cancelBubble = true; 
		if (e.stopPropagation) 
			e.stopPropagation();
	}
}

function Display() 
{
	this.container = getEle('display_container');
	
	this.isMobile = function () 
	{
		return navigator.appVersion.indexOf("Mobile") >= 0 || navigator.platform && navigator.platform == "iPhone";  
	}
	 
	this.getTop = function () 
	{
	 	return window.pageYOffset || document.body.scrollTop;
	}
	
	this.getHeight = function () 
	{
		if (this.container && this.container.className == "mobile_container") 
			return window.innerHeight; 
		else 
			return this.container.offsetHeight; 
	}
	 
	this.getBottom = function () 
	{
		return this.getTop() + this.getHeight();
	}
	
	this.getWidth = function () 
	{
		return document.body.offsetWidth || window.innerWidth;
	}
	
	this.getLeft = function () 
	{
		if (this.container)
			return this.container.offsetLeft;
		else 
			return 0;  
	}
	
	this.setTop = function(top) 
	{
		window.scrollTo(0, parseInt(top) ? parseInt(top) : 1);
	}  
}	
 
 
function DOM ()
{
	this.create = function (tag, id, className, attribs, childrenOrHtml) 
	{
		var ele = null; 
		if (hasValue(attribs) && (attribs.name || attribs.checked))
		{	// IE workaround for creating named elements. 
			try {
				ele = document.createElement("<" + tag + " name='" + attribs.name + "'" + (attribs.checked ? "checked='checked'" : "") + ">");
			} catch (e) {
				ele = document.createElement(tag);
			}
		} else
		 
		if (isNull(ele)) 
			ele = document.createElement(tag);
		if (hasValue(id)) 
			ele.id = id; 
		if (hasValue(className)) 
			ele.className = className; 
		if (hasValue(attribs)) 
			for (var key in attribs) 
				ele.setAttribute(key, attribs[key]);
		if (hasValue(childrenOrHtml))
		{
			if (isArray(childrenOrHtml)) 
				for (var i = 0; i < childrenOrHtml.length; i++) 
					ele.appendChild(childrenOrHtml[i]);
			else if (typeof childrenOrHtml == "object")
			{ 
				ele.appendChild(childrenOrHtml)
			} else
				ele.innerHTML = childrenOrHtml; 
		}		 
		return ele;   
	}
	
	this.text = function (text) 
	{
		return document.createTextNode(text); 
	}
	
	this.button = function (id, name, onclick) 
	{
		butt_name = this.text(name);
		/* 
		butt = this.create("a"); 
		butt.setAttribute("href", "#");
		butt.id = id; 
		butt.className = "max_button"; 
		butt.appendChild(butt_name);
		*/
		butt = this.create("button"); 
		butt.appendChild(butt_name); 
		butt.className = "max_button"; 
		butt.id = id;  
		if (defined(onclick))
			event.add(butt, "click", onclick); 
		
		return butt; 
	}
	
	this.getStyle = function (id, name)
	{
		var ele = getEle(id);
 		if (ele) 
 		{
	 		if (ele.currentStyle)
	   			return ele.currentStyle[name];
	 		else
				try {
					var cs = document.defaultView.getComputedStyle(ele,null);
	     			return cs.getPropertyValue(name);
	   			} catch(e) {}
		}
		   			
   		return false; 
 	}
	
	this.setStyle = function (id, name, style) 
	{ 
		var ele = getEle(id);
 		if (ele) 
 		{  		
	 		if (defined(style)) {
	 			if (ele.runtimeStyle)
	     			return ele.runtimeStyle[name] = style;
	   			else
	     			return ele.style[name] = style;
	 		} else 
	 			return this.getStyle(id, name);
	 	} 
	 	
	 	return false;  	
	}
	
	this.select = function (id, className, attribs, values, value)
	{
		var options = new Array(); 
		
		if (hasValue(values)) 
			for (var i = 0; i < values.length; i++)
			{
				var opt_attribs = {}; 
				opt_attribs.value = values[i];
				if (isArray(value) && value.length) 
				{
					for (var j = 0; j < value.length; j++) 
						if (values[i] == value[j])  
							opt_attribs.selected = "selected";    
				} else if (values[i] == value)  
							opt_attribs.selected = "selected";
				options.push(this.create("option", null, null, opt_attribs, values[i]));			
			}	
		
		var select = this.create("select", id, className, attribs, options);

		return select;  
	} 
	
	this.mdd = function (id, className, attribs, values, selectedValues, events)
	{
		id = hasValue(id) ? id : ("mdd_" + getRandomId);
		
		if (!attribs) 
			attribs = {}; 
		attribs.multiple = true; 
		var normalMdd = this.select(id, className, attribs, values, selectedValues);
		
		if (hasValue(events)) 
			for (var name in events) 
				normalMdd["on" + name] = events[name]; 
		
		return normalMdd;  		
	}
	
	this.inputSelect = function (id, value, dbName, attribs) 
	{
		var input = dom.create("input"); 
		input.type = "text";
		input.id = id; 
		input.value = value;
	
		if (hasValue(attribs)) 
			for (var key in attribs) 
				input.setAttribute(key, attribs[key]); 

		return input; 		
	}
	
	this.table = function (id, className, attribs, children) 
	{
		var tbody = this.create("tbody", _, _, _, children);
		var table = this.create("table", id, className, attribs, tbody);
		
		return table; 
	}
	
	this.check = function (id, className, attribs, name) 
	{
		if (isNull(attribs)) 
			attribs = {}; 
		if (isNull(id)) 
			id = "checkbox_" + getRandomId(); 
		attribs["type"] = "checkbox";
		var check = dom.create("input", id, _, attribs);
		check.style.width = "auto";
		check.style.border = "none";
		check.checked = attribs.checked; 
		var label = dom.create("label", _, _, _, dom.text(name));		
		var container = dom.create("span", _, className, _, [check, label]);
		event.add(container, "click", 
			function(evt) 		
			{
				var evt = evt || window.event; 
				var target = evt.target || evt.srcElement; 				
				if (target.tagName !== "INPUT")
					check.checked = !check.checked;
			})
		event.add(container, "mouseout", 
			function () 		
			{
				container.style.backgroundColor = "#ffffff";  
			}); 
		event.add(container, "mouseover", 
			function () 		
			{
				container.style.backgroundColor = "#f7f7ff";  
			}); 		
		return container; 
	}

	this.radio = function (id, className, attribs, name) 
	{
		if (!attribs) 
			attribs = {}; 
		var checkAttribs = {}; 
		var internalId = "radio_" + getRandomId();
		if (isNull(id)) 
			id = internalId;  
		attribs["type"] = "radio"; 
		attribs["name"] = id;   
		var check = dom.create("input", internalId, _, attribs);
		check.name = id; 		
		check.style.width = "auto";
		check.style.border = "none";
		check.checked = attribs.checked;    
		var label = dom.create("label", _, _, _, dom.text(name)); 
		var container = dom.create("span", id, className, _, [check, label]);
		event.add(container, "click", 
			function(evt) 		
			{	
				var evt = evt || window.event; 
				var target = evt.target || evt.srcElement;
				if (target.tagName !== "INPUT")
					check.checked = true;
			})
		event.add(container, "mouseout", 
			function () 		
			{
				container.style.backgroundColor = "#ffffff";  
			}); 
		event.add(container, "mouseover", 
			function () 		
			{
				container.style.backgroundColor = "#f7f7ff";  
			}); 
		return container; 
	}
	
	this.getTop = function (element)
	{
		var top = 0;
		while (element != null) {
			top += element.offsetTop;
			element = element.offsetParent;
		}
		
		return top;
	}
	
	this.getLeft = function (element)
	{
		var left = 0;
		while (element != null) {
			left += element.offsetLeft;
			element = element.offsetParent;
		}
		
		return left;
	}
	
	this.getHeight = function (ele) 
	{
		return ele.offsetHeight; 
	}
	
	this.getWidth = function (ele) 
	{
		return ele.offsetWidth; 
	}
	
	this.fieldSet = function (id, className, attribs, title, children) 
	{
		var legend = dom.create("legend", _, _, _, dom.text(title)); 
		var set = dom.create("fieldset", id, className, attribs, legend); 
		if (hasValue(children) && children.length) 
			for (var i = 0; i < children.length; i++) 
				set.appendChild(children[i]); 
		return set; 
	}
} 

function Html () 
{
	this.button = function (id, name, onclick) 
	{
		return "<button id='" + id + "' onclick='" + onclick + "' class='max_button'>" + name + "</button>"; 
	}
	
	this.input = function (id, value, attribs) 
	{
		var attribs_str = ""; 
		
		if (defined(attribs) && attribs)
		{ 
			if (!attribs.type)
				attribs.type = "text";  
			for (var key in attribs) 
				attribs_str += key + "='" + attribs[key] + "' ";
		} else 
			attribs_str = "type='text'"; 
			 		
		if (!defined(value)) 
			value = ""; 
		if (!defined(id)) 
			id = ""; 
		
		return "<input id='" + id + "' value='" + value + "' " + attribs_str + " />"; 
	}
	

}

function Select (existingId, allValues, selectedValues, onChange, multiple)
{
	if (hasValue(existingId)) 
	{	// existing mdd
		this.domNode = getEle(existingId); 
		if (!this.domNode) 
			debug.log("Select() Failed to get DOM node for existing ID: " + existingId);  
	} else 
	{	// new mdd 
		this.id = "mdd_" + getRandomId(); 
		this.values = hasValue(allValues) ? allValues : []; 
		this.multiple = hasValue(multiple) ? multiple : false; 
		this.selectedValues = hasValue(selectedValues) ? selectedValues : (this.multiple ? [] : "");
		this.onChange = hasValue(onChange) ? onChange : null;
		this.domMode = null; 
		this.mouseOver = false; 		 
	}
	
	this.createDomNode = function () 
	{
		var select = this; 
		var summary = dom.create("div", this.id + "_summary", "max_select_summary"); 
		var values = dom.create("div", this.id + "_values", "max_select_values");  
	
		// set selected values
		if (this.values && this.values.length)
		{
			var onChange = null; 
			var onChange = function (evt) 
			{
				setTimeout(
					function() 
					{
						if (!select.multiple)
						{ 
							if (select.onChange) 
								select.onChange(); 				 						
							values.style.display = "none";
							if (display.isMobile())
							{ 
								var ad = getEle("ad"); 						
								ad.style.display = "";
							}
						} 
						select.updateSummary();
					}, 100); 
			} 
			var attribs = {}; 
			for (var i = 0; i < this.values.length; i++) 
			{				
				delete attribs.checked;
				if (this.selectedValues)
				{ 
					if (this.multiple)
					{ 
						for (var j = 0; j < this.selectedValues.length; j++) 
							if (this.values[i] == this.selectedValues[j])
								attribs.checked = "checked";
					} else if (this.values[i] == this.selectedValues) 
						attribs.checked = "checked";					 
				} 
				if (this.multiple) 
					var check = dom.check(_, "max_select_value", attribs, this.values[i].toString().toHtmlEntities());
				else 
				{
					var check = dom.radio(this.id + "_value", "max_select_value", attribs, this.values[i].toString().toHtmlEntities());
					check.firstChild.style.display = "none";
				}
				event.add(check, "click", onChange); 
				values.appendChild(check); 
			}
		} else 
		{
			var none = dom.create("div", _, _, _, dom.text("None")); 
			none.className = "gray"; 
			none.style.textAlign = "center"; 
			values.appendChild(none);
		}
		var doClose = function () 
				{
					values.style.display = "none";  
					if (select.onChange) 
						select.onChange();
					if (display.isMobile())
					{ 
						var ad = getEle("ad"); 						
						ad.style.display = "";
					}
					 				 											
				}; 
		//if (this.multiple)
		{ 
			var doneButton = dom.create("div", _, _, _, dom.button(_, "Done"));
			event.add(doneButton, "click", doClose);			
			doneButton.style.textAlign = "center";
			doneButton.style.borderTop = "1px dashed #999999";
			doneButton.style.padding = "0.2em";    
			values.appendChild(doneButton);
		} 

		summary.innerHTML = this.getSummary();
			
		event.add(summary, "click", 
			function () 
			{
				values.style.top = dom.getTop(summary) + summary.offsetHeight; 
				if (values.style.display == "inline") 
				{
					values.style.display = "none";  			 																
					if (display.isMobile())
					{ 
						var ad = getEle("ad"); 						
						ad.style.display = "";
					}
				} else 
				{
					var divs = document.getElementsByTagName("div");
					for (var i = 0; i < divs.length; i++) 
						if (divs[i].className == "max_select_values") 
							divs[i].style.display = "none"; 
					values.style.display = "inline";
					//setTimeout(function() {select.mouseOver = true;}, 200);
					if (display.isMobile())
					{ 
						var ad = getEle("ad"); 						
						ad.style.display = "none";
					}
					
				}				
			});
			
		this.domNode = dom.create("div", this.id, "max_select", _, [summary, values]);
		event.add(values, "mouseout", 
			function(evt) 
			{ 
				//if (select.mouseOver)
				{
					var evt = evt || window.event;
					var x = evt.clientX; 
					var y = evt.clientY; 
					var top = dom.getTop(values);
					var left = dom.getLeft(values); 
					var right = left + dom.getWidth(values);
					var bottom = top + dom.getHeight(values);
					//debug.log (bottom + " " + y); 
					if (x <= left || x >=right || y >= bottom || y <= top)
					{ 
						doClose(); 
						select.mouseOver = false; 
					}
				}  				
			}); 
	}
	
	this.getDomNode = function () 
	{
		if (!this.domNode) 
			this.createDomNode(); 
			
		return this.domNode; 
	}
	
	this.updateSummary = function () 
	{
		var summary = getEle(this.id + "_summary"); 
		
		if (summary) 
			summary.innerHTML = this.getSummary();
	}
	
	this.getSummary = function () 
	{
		var values = this.getSelectedValues();
		var summary = "";  
		
		if (isArray(values)) 
		{
			if (values.length == 1) 
				summary = values[0]; 
			else 
				for (var i = 0; i < values.length; i++)
					summary += values[i].charAt(0) + "&nbsp;";  				
		} else 
			summary = values; 
		
		if (!summary) 
			summary = "<span class='gray'>None</span>";  
		
		return summary; 
	}
	
	this.getSelectedValues = function () 
	{
		var valuesDiv = getEle(this.id + "_values");
		if (valuesDiv) 
		{
			var selectedValues = []; 
			try {
				for (var i = 0; i < valuesDiv.childNodes.length; i++)
					if (valuesDiv.childNodes[i].firstChild.checked) 
						selectedValues.push(valuesDiv.childNodes[i].lastChild.firstChild.nodeValue.toString().fromHtmlEntities());
			} catch (e)
			{
				debug.log("Select.getSelectedValues() " + e); 
			}
			if (this.multiple)
			{ 
				this.selectedValues = selectedValues;
			} else 
				this.selectedValues = hasValue(selectedValues[0]) ? selectedValues[0] : ""; 
		}		 
		if (isArray(this.selectedValues)) 
			this.selectedValues.sort(); 
		return this.selectedValues;  
	}
	
	this.getSelectedValue = function () 
	{
		return this.getSelectedValues(); 
	}
	
	this.setSelectedValues = function (values) 
	{
		var valuesDiv = getEle(this.id + "_values");
		if (valuesDiv) 
		{
			try {
				for (var i = 0; i < valuesDiv.childNodes.length; i++)
				{
					valuesDiv.childNodes[i].firstChild.checked = false; 
					for (var j = 0; j < values.length; j++) 
						if (valuesDiv.childNodes[i].lastChild.innerHTML.fromHtmlEntities() == values[j])
							valuesDiv.childNodes[i].firstChild.checked = true;
						 
					//if (valuesDiv.childNodes[i].firstChild.checked) 
					//	selectedValues.push(valuesDiv.childNodes[i].lastChild.firstChild.nodeValue.toString().fromHtmlEntities());
				}
			} catch (e)
			{
				debug.log("Select.getSelectedValues() " + e); 
			}
			if (this.multiple) 
				this.selectedValues = values; 
			else 
				this.selectedValues = hasValue(values[0]) ? values[0] : "";
			this.updateSummary();
			if (this.onChange) 
				this.onChange();  
		}	
	}
	
	this.setSelectedValue = function (value) 
	{
		this.setSelectedValues([value]);
	}
}

function Popup (title, body, visible) 
{
	this.title = defined(title) ? title : ""; 
	this.body = defined(body) ? body : "";
	this.visible = defined(visible) ? visible : (this.title && this.body ? true : false);
	this.id = "popup_" + getRandomId();
	this.width = 314; 
	this.height = 0; 
	this.left = 0; 
	this.top = 0;    
	 
	this.show = function() 
	{
		this.visible = true;
		this.displayTop = display.getTop();  
		
		if (this.window)
		{
			this.window.style.display = "inline";
			this.setTop(this.top); 
		} else 
			this.createWindow(); 
			
		var ad = getEle("ad"); 
		if (ad && display.isMobile()) 
			ad.style.display = "none"; 		
	}
	
	this.hide = function() 
	{
		this.visible = false;
		display.setTop(this.displayTop);
		
		if (getEle(this.id))
		{
			getEle(this.id).style.display = "none";
			var ad = getEle("ad"); 
			if (ad && display.isMobile()) 
				ad.style.display = ""; 
		}
	}
	
	this.createWindow = function() 
	{
		var id = this.id;
		
		if (!defined(html))
			html = new Html(); 
		
		td = dom.create('td');
		td2 = dom.create('td');
		td3 = dom.create('td'); 
		this.window = dom.create("div");  
		tr = dom.create('tr'); 
		tr2 = dom.create('tr');
		tbody = dom.create('tbody');   
		table = dom.create('table');
		table.style.width = "100%"; 
		
		td.className = "max_popup_title";
		td.id = this.id + "_title";
		td.innerHTML = this.title;    
		td2.className = "max_popup_body";
		td2.id = this.id + "_body";
		td2.innerHTML = this.body;
		td2.setAttribute("colspan", 2);
		td3.className = "max_popup_title"; 
		td3.width = 1;
		displayTop = display.getTop();
		var close = function() 
			{ 
				getEle(id).style.display = "none";
				display.setTop(displayTop); 
				var ad = getEle("ad"); 
				if (ad && display.isMobile()) 
					ad.style.display = ""; 
			}
		td3.appendChild(dom.button(null, "X", close)); 
		event.add(td, "click", close); 
		tr.appendChild(td);
		tr.appendChild(td3);  
		tr2.appendChild(td2);		  		
		tbody.appendChild(tr); 
		tbody.appendChild(tr2);
		table.appendChild(tbody); 
		table.cellSpacing = 0;   
				
		this.window.appendChild(table);
		this.window.className = "max_popup";
		this.window.id = this.id;
		this.window.style.height = this.height ? this.height : "auto"; 
		var top = this.top ? display.getTop() + this.top : (display.getTop() + (display.getHeight() - this.height) / 2);
		if (top < 0) 
			top = 0; 
		this.window.style.top = top; 		
		 
		this.window.style.left = this.left ? display.getLeft() + this.left : ((display.getWidth() - this.width)/2);
		this.window.style.width = this.width; 
		
		//event.add(this.window, 'click', close); 
		if (this.visible)
		{ 
			this.window.style.display = "inline"; 
			var ad = getEle("ad"); 
			if (ad && display.isMobile()) 
				ad.style.display = ""; 
		} else 
			this.window.style.display = "none"; 		
		
		body = document.getElementsByTagName("body")[0];
		body.appendChild(this.window);
		
		this.window = getEle(this.id);
		this.setTop(this.top); 		 
	}
	
	this.enableEasyClose = function ()
	{
		if (this.window) 
		{
			var id = this.id; 
			var displayTop = display.getTop(); 
			var close = function() 
			{ 
				getEle(id).style.display = "none";
				display.setTop(displayTop); 
				var ad = getEle("ad"); 
				if (ad && display.isMobile()) 
					ad.style.display = ""; 
			}
			
			event.add(this.window, "click", close); 
		}
	}
	
	this.setTitle = function (title) 
	{
		this.title = title;
		
		if (getEle(this.id + '_title')) 
			getEle(this.id + '_title').innerHTML = title;   
	}
	
	this.setBody = function (body) 
	{
		this.body = body; 
		
		if (getEle(this.id + '_body')) 
			getEle(this.id + '_body').innerHTML = body;   
	}
	
	this.setTop = function (top) 
	{
		this.top = top; 
		
		if (this.window) 
		{
			top = this.top ? display.getTop() + this.top : (display.getTop() + (display.getHeight() - this.window.offsetHeight) / 2);
			if (top < 1) 
				top = 1; 
			this.window.style.top = top; 
			
		}  
	}

	this.setLeft = function(left) 
	{
		this.left = left;  
		
		if (this.window) 
			this.window.style.left = this.left ? display.getLeft() + this.left : ((display.getWidth() - this.width)/2); 
	}
	
	this.setWidth = function(width) 
	{
		this.width = width; 
		
		if (this.window) 
		{
			this.window.style.width = width;
			this.setLeft(this.left);
		} 
	}
	
	this.setHeight = function (height) 
	{
		this.height = height; 
		
		if (this.window) 
		{
			this.window.style.height = height;
			this.setTop(this.top); 
		} 
	}
	
	if (this.visible) 
		this.show();  
}

function TabContainer(id, parent) 
{
	this.tabs = {};
	this.currentTabId = null;
	this.container = null; 
	
	if (defined(parent)) 
		this.parent = parent; 
	else 
		this.parent = null;  

	if (defined(id) && id) 
		this.id = id; 
	else 
		this.id = "tab_" + getRandomId();
		
	this.createContainer = function() 
	{
		this.container = dom.create("div"); 
		this.container.className = "max_tab_container"; 
		this.container.id = this.id; 
		
		this.containerTitle = dom.create("ul");
		this.containerTitle.className = "max_tab_container_title";  
		this.containerBody = dom.create("div"); 
		this.containerBody.className = "max_tab_container_body";
		
		this.container.appendChild(this.containerTitle); 
		this.container.appendChild(this.containerBody);  
		
		if (this.parent && this.parent.appendChild) 
			this.parent.appendChild(this.container); 
	}
	
	this.setCurrentTab = function (id) 
	{
		if (this.currentTabId != id) 
		{
			if (this.currentTabId) 
			{
				var tab = getEle(this.id + "_" + this.currentTabId); 
				if (tab) 
					tab.className = "max_tab_title"; 
				var tab_body = getEle(this.id + "_" + this.currentTabId + "_body"); 
				if (tab_body) 
					tab_body.className = "max_tab_body";
			} 		

			tab = getEle(this.id + "_" + id);			  
			if (tab) 
				tab.className = "max_tab_title_active"; 
			tab_body = getEle(this.id + "_" + id + "_body");
			if (tab_body) 
				tab_body.className = "max_tab_body_active";
			
			this.currentTabId = id;  		
		}
	}
	
	this.addTab = function (id, title, body, currentTab, onClick) 
	{
		var container_id = this.id; 
		var tab_id = this.tabs[id] = this.id + "_" + id;
		var container = this; 
					
		var tab = dom.create("li"); 
		tab.className = "max_tab_title"; 
		tab.innerHTML = title;
		tab.id = this.tabs[id];		  
		event.add(tab, "click", function() 
			{
				container.setCurrentTab(id); 
				if (onClick) 
					onClick(); 
			}); 
		this.containerTitle.appendChild(tab); 
		
		var spacer = dom.create("li"); 
		spacer.className = "max_tab_spacer"; 
		spacer.innerHTML = "&nbsp;";
		//this.containerTitle.appendChild(spacer); 
		
		var tab_body = dom.create("div"); 
		tab_body.className = "max_tab_body";
		tab_body.id = this.tabs[id] + "_body";
		if (typeof body == "object")
			tab_body.appendChild(body); 
		else 
			tab_body.innerHTML = body; 
		this.containerBody.appendChild(tab_body);  
				
		if (!this.currentTabId || defined(currentTab) && currentTab) 
			this.setCurrentTab(id); 
	}
	
	this.removeTab = function (id) 
	{
	}
	
	this.showTab = function (id) 
	{
	} 
	
	this.hideTab = function (id) 
	{
		
	} 
	
	this.hide = function() 
	{
		if (this.container) 
			this.container.style.display = "none"; 
	}
	
	this.show = function() 
	{
		if (!this.container) 
			this.createContainer();
		
		this.container.style.display = ""; 
	}
	
	this.show();  
}

function User() 
{
	this.loggedIn = false; 
	this.loginBox = null; 
	this.logoutBox = null;
	this.userNameId = "userName"; 
	this.passwordId = "password"; 
	this.errorId = "loginError"; 
	this.loginStatusId = "loginStatus"; 
	this.onLogin = null; 
	this.onLogout = null; 

	this.login = function (register, submit) 
	{
		if (!defined(register)) 
			register = false; 
					
		if (defined(submit) && submit) 
		{
			this.clearError(); 
			var name = getEle(this.userNameId); 
			var pass = getEle(this.passwordId);
			
			if (defined(name) && defined(pass) && name.value.length && pass.value.length) 
			{
				var params = "userName=" + escape(name.value) + "&password=" + escape(pass.value);
				var action = register ? "register" : "login"; 
				ajax.send("?obj=user&action=" + action, function (req) 
					{
						var response = json.decode(req.responseText); 
						if (response) 
						{
							if (response.error) 
								user.setError(response.error); 
							else if (response.sessionId)
							{
								pass.blur();    
								user.loginBox.hide();  
								user.userName = name.value.toHtmlEntities();
								user.updateLoginStatus(); 
								if (user.onLogin)
									user.onLogin();  
							} else 
								user.setError("Server is unavailable. Please try again later.");
						} else 
							user.setError("Server is unavailable. Please try again later."); 
					}, {method:"post", params:params});
			} else 
				this.setError("Name and password are required.");  
		} else 
		{
			if (!this.loginBox)
				this.loginBox = new Popup();
			var name = html.input(this.userNameId, "", {autocorrect:"off"});
			var pass = html.input(this.passwordId, "", {autocapitalize:"off", autocorrect:"off"}); 
			var regButt = html.button(null, register ? "Register" : "Login", "user.login(" + register + ", true)"); 
			var cancelButt = html.button(null, "Cancel", "user.loginBox.hide()");
			var body = "<table><tr><td class='max_field_title' style='width: 5em'>Name:</td><td>" + name + "</td></tr>" + 
				"<tr><td class='max_field_title'>Password:</td><td>" + pass + "</td></tr>" + 
				"<tr><td>&nbsp;</td><td class='max_error_msg' id='" + this.errorId + "'></td></tr></table>" + 
				regButt + "&nbsp;" + cancelButt;  
			this.loginBox.setTitle(register ? "Register" : "Login");
			this.loginBox.setBody(body); 
			this.loginBox.show();
			name = getEle(this.userNameId); 
			pass = getEle(this.passwordId);  
			name.focus();
			var doLogin = function (evt) 
				{
					if (checkEnter(evt)) 
					{
						pass.blur();	
						user.login(register, true);					 
					}
				}
			event.add(name, "keydown", doLogin);    	
			event.add(pass, "keydown", doLogin);
		}
	}
	
	
	this.register = function () 
	{
		this.login(true); 
	}
	
	this.logout = function (confirmed) 
	{
		if (defined(confirmed) && confirmed) 
		{
			cookie.remove("USER_ID"); 
			cookie.remove("SESSION_ID");
			this.logoutBox.hide();
			this.updateLoginStatus();
			if (this.onLogout)
				this.onLogout(); 
		} else if (this.logoutBox) 
			this.logoutBox.show(); 
		else
		{						 
			var yesButt = html.button(null, "Yes, log me out.", "user.logout(true);"); 
			var noButton = html.button(null, "Cancel", "user.logoutBox.hide()");
			this.logoutBox = new Popup("Logout", "Are you sure? You will be logged out.<br><br>" + yesButt + "&nbsp;" + noButton);  
		} 		
	}
	
	this.updateLoginStatus = function () 
	{
		var sessionId = cookie.get("SESSION_ID");
		var loginStatus = getEle(this.loginStatusId);  
		
		if (loginStatus) 
		{			
			if (sessionId) 
			{
				if (!this.userName) 
				{
					ajax.send("?obj=user&action=getName",
						function (req) 
						{
							var obj = json.decode(req.responseText); 
							if (obj && obj.name)
								user.userName = obj.name.toHtmlEntities();
							user.updateLoginStatus();  
						}, {aync: false});  
				}
				var name = this.userName ? this.userName : "?";  
				loginStatus.innerHTML = "You are logged in as <b>" + name + "</b>. <a href='#' onclick='user.logout()' style='color: blue'>Logout</a>"; 
			} else
			{
				loginStatus.innerHTML = "<a href='#' onclick='window.user.login()' $style>Login</a> or <a href='#' onclick='user.register()' $style>register</a> to enhance your experience while&nbsp;using this web application."; 
			} 
		}			
	}
	
	this.setError = function (err) 
	{
		if (errCell = getEle(this.errorId)) 
			errCell.innerHTML = err; 
	}
	
	this.clearError = function () 
	{
		this.setError(""); 
	}	
}

function Json () 
{
	this.encode = function (obj) 
	{
		return this.stringify(obj); 
	}
	
	this.decode = function (str) 
	{
		return eval("(" + str + ")"); 
	}
	
    function f(n) {
        return n < 10 ? '0' + n : n;
    }
	
    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';
    };

	var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
    	escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
    	gap,
    	indent,
    	meta = {    // table of character substitutions
	        '\b': '\\b',
	        '\t': '\\t',
	        '\n': '\\n',
	        '\f': '\\f',
	        '\r': '\\r',
	        '"' : '\\"',
	        '\\': '\\\\'
	    },
    	rep;

	function quote(string) {
	    escapeable.lastIndex = 0;
	    return escapeable.test(string) ?
	        '"' + string.replace(escapeable, function (a) {
	            var c = meta[a];
	            if (typeof c === 'string') {
	                return c;
	            }
	            return '\\u' + ('0000' +
	                    (+(a.charCodeAt(0))).toString(16)).slice(-4);
	        }) + '"' :
	        '"' + string + '"';
	}


	function str(key, holder) {
	    var i,          // The loop counter.
	        k,          // The member key.
	        v,          // The member value.
	        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 (typeof value.length === 'number' &&
                        !(value.propertyIsEnumerable('length'))) {
                    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, rep);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                } else {
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = str(k, value, rep);
                            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;
		}
	}

	this.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});
	}
		
		
	this.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');
	}
}

function Ajax ()
{
	this.requestPool = []; 
	
	this.getHTTPRequest = function () {
		try {
			http_request = new XMLHttpRequest();
			http_request.overrideMimeType('application/text'); 
		}
		catch (e) {
			try {
				http_request = new ActiveXObject("Msxml2.XMLHTTP");
			}
			catch (e) {
				try {
					http_request = new ActiveXObject("Microsoft.XMLHTTP");
				}
				catch (e) {
					http_request = false;
				}
			}
		}
	
		return http_request;
	} 
	
	this.request = this.getHTTPRequest();
 		
	if (this.request === false)
		debug.alert("This web application requires a modern browser to work. Please upgrade to the latest release of Safari, Firefox, or Internet Explorer. Hope to see you soon! :)"); 

	this.send = function (url, callback_function, options)
	{
		if (this.request)
		{				 
			if (this.request.readyState == 0 || this.request.readyState == 4) 
			{ 				
				if (!defined(options)) 	
					options = {}; 
				if (options.add_random_number)
				{
					if (url.indexOf('?') != -1)
						url += '&x=' + Math.random();
					else 
						url += '?x=' + Math.random();
				}
				if (!defined(options.async))	
					options.async = true;
									
				if (!options.method)
					options.method = "get";
				if (!options.params) 
					options.params = null; 
					  
				this.request.open(options.method, url, options.async);
				if (options.method == "post") 
				{
					this.request.setRequestHeader("content-type", "application/x-www-form-urlencoded");
					this.request.setRequestHeader("content-length", options.params ? options.params.length : 0);
					this.request.setRequestHeader("connection", "close");				
				}
				if (defined(callback_function) && callback_function) 
				{
					this.request.onreadystatechange = function() 
					{						 
						if (defined(ajax) && ajax && ajax.request && ajax.request.readyState == 4) 
						{
							if (ajax.request.status == 200) 
								callback_function(ajax.request);
							else 
								debug.log("Ajax request failed (URL: " + url + " | Status: " + ajax.request.status + "). <br>Result: " + ajax.request.responseText); 
							ajax.checkPooledRequest(); // checks pool for more requests.							 
						}
					};
				} else 
					this.request.onreadystatechange = null; 
				
				var params_str = null; 
				if (options.params) 
				{
					switch (typeof options.params)
					{
						case "object":
							var params_arr = []; 
							for (var key in options.params)
								params_arr.push(key + "=" + escape(options.params[key])); 
							params_str = params_arr.join("&"); 
							break;  
						case "string":
							params_str = options.params;  
							break; 
						default: 
							debug.log("Ajax.send() options.params must be an object or a string"); 
					}
				}
				this.request.send(params_str);
			} else 
				this.poolRequest({'url': url, 'callback': callback_function, 'options': options}); 		
		}
	}		
	
	 
	this.poolRequest = function(req) 
	{			
		if (defined(req) && req) 
			this.requestPool.push(req);
		else if (this.requestPool.length)
		{
			req = this.requestPool.pop();
			this.send(req.url, req.callback, req.options);  
		}
	}
	
	this.checkPooledRequest = function() 
	{
		this.poolRequest(); 
	}
}
 
function Highlighter() 
{
	var pcolors = new Array('#E56717', '#F87217', '#F88017', '#E8A317', '#FBB117', '#FBB917', '#FDD017', '#EDDA74', '#CCFB5D', '#B1FB17', '#59E817', '#57E964', '#41A317',
	'#41A317', '#57E964', '#59E817', '#B1FB17', '#CCFB5D', '#EDDA74', '#FDD017', '#FBB917', '#FBB117', '#E8A317', '#F88017', '#F87217');  
	
}
				
function do_progress_highlight(element) 
{
	if (progress_element && progress_element == element) return;
	if (element) 
	{			
		pcolor_index = 0; 
		progress_element = element;
		progress_element_border = 'none';
		if (last_progress_element)
		{
			last_progress_element.style.border = last_progress_element_border;
			//alert(last_progress_element_border); 				  
		}
		 
		setTimeout('do_progress_highlight()', 100);  
	}
	if (progress_element) 
	{
		pcolor_index += 1;
		if (pcolor_index > 24)
			pcolor_index = 0; 
		progress_element.style.border = "1px solid " + pcolors[pcolor_index];
		setTimeout('do_progress_highlight()', 100);
	}
}

function stop_progress_highlight(border) 
{
	last_progress_element = progress_element;
	if (border) 
		last_progress_element_border = border;
	else 
		last_progress_element_border = progress_element_border;  
	progress_element = null;   
}



function sd(element, border) 
{
	var word = element.firstChild ? element.firstChild.nodeValue : element.value;
	word = word.trim();
	if (word.indexOf(' ') !== -1)
	{				
		word = word.substring(0, word.indexOf(' ')); 
	}
	word = word.replace(/[^a-zA-Z]/g, '');
	if (word) 
	{
		if (wordField = getEle('word'))
			wordField.value = word; 
		do_progress_highlight(element);
		sendRequest('$dictionary_url' + escape(word), function() {show_definition(border)});				
	}
	return !ajax; 
}

function getEle(id) 
{
	return document.getElementById(id); 
}		
function show_popup(body_html, title) 
{
	cancelEvent(); 
	 
	header_html = '<table class="popup_header"><tr><td class="popup_title">' + title + '</td><td align="right"><a class="button_cyan" onclick="hide_popup()">X</a></td></tr></table>';
	popup.innerHTML = header_html + body_html;
	 
	popup.style.top = parseInt($popup_top) + 4;
	popup.style.left = parseInt((document.body.clientWidth - $screen_size) / 2) + 5;
	
	popup.style.display = 'inline';
	popup.scrollTop = 0;
	if (window.ad) 
		ad.style.display = 'none';
	  
}

function hide_popup()
{
	popup.style.display = 'none';
	$restore_scroll_top
	if (window.ad)  
		ad.style.display = ''; 
}	


function show_definition(border)
{
	switch (request.readyState)
	{
		case 4: 			
			if (request.status == 200)
			{ 
				var word = progress_element.firstChild ? progress_element.firstChild.nodeValue : progress_element.value;
				show_popup(request.responseText, 'Definition'); 
				progress_element.style.border= '1px dotted #bbbbbb';
				stop_progress_highlight(border);
			}	
	} 
}		

function switch_tab(name, title, tbody) 
{
	document.getElementById(current_tab_title_id).className = 'tab_title'; 
	title.className = 'current_tab_title'; 
	
	document.getElementById(current_tab_body_id).style.display = 'none'; 
	tbody.style.display = 'block';  
	
	if (window.intro) intro.innerHTML = intros[title.firstChild.firstChild.nodeValue]; 
	if (title.firstChild.firstChild.nodeValue != 'X') 
		cookie.set(name + '_CURRENT_TAB', title.firstChild.firstChild.nodeValue, 365); 
	
	current_tab_title_id = title.id;
	current_tab_body_id = tbody.id;
	 
	for (var delay = 0; delay < 1000; delay++); // Oddly, iPhone crashes sometimes without this delay.   

	return !ajax; 
}
 
function Cookie() 
{
	this.path = "/"; // default values for set / remove. 
	this.domain = ".almightymax.com"; 	
	
	this.set = function (name, value, expires, path, domain, secure)
	{
		if (!defined(path)) 
			path = this.path; 
		if (!defined(domain)) 
			domain = this.domain;
		 
		var today = new Date();
		today.setTime(today.getTime());
	
		if (expires)
			expires = expires * 1000 * 60 * 60 * 24;
		var expires_date = new Date(today.getTime() + expires);
		
		document.cookie = name + "=" +escape(value) +
		((expires) ? ";expires=" + expires_date.toGMTString() : "" ) + 
		((path) ? ";path=" + path : "" ) + 
		((domain) ? ";domain=" + domain : "" ) +
		((secure) ? ";secure" : "" );
	}
	
	this.get = function (name) 
	{
		if (document.cookie) 
		{
			cookies = document.cookie.split("; "); 
			for (var i = 0; i < cookies.length; i++) 
			{
				var pair = cookies[i].split("=");
				if (pair[0] === name) 
					return unescape(pair[1]);   
			} 
		} 
	}
	
	this.remove = function (name, path, domain) 
	{
		if (!defined(path)) 
			path = this.path; 
		if (!defined(domain)) 
			domain = this.domain; 
		var expires_date = new Date(1); 
		document.cookie = name + "=" +
		";expires=" + expires_date.toGMTString() + 
		((path) ? ";path=" + path : "" ) + 
		((domain) ? ";domain=" + domain : "" );
	} 
}
	


var browser_cache = []; 
var browser_top = 0;

function browser_change_page(id, page, highlight_target, buffer) 
{			
	var browser_body = document.getElementById(id + '_body'); 
	var browser_next = document.getElementById(id + '_next');
	var browser_prev = document.getElementById(id + '_prev');
	var browser_num = document.getElementById(id + '_num');
	var browser_total_page = document.getElementById(id + '_total_page').innerHTML;
	var clone_next = document.getElementById(id + '_next_clone');
	var clone_prev = document.getElementById(id + '_prev_clone');
	var clone_num = document.getElementById(id + '_num_clone');
	var url = eval(id + '_browser_url');
	var browser_top = eval(id + '_browser_top'); 

	if (window.table_of_contents) 
		table_of_contents.style.display = 'none';
	if (browser_cache[id] && browser_cache[id][page]) 
	{
		browser_body.innerHTML = browser_cache[id][page];
		browser_num.value = page;
		browser_num.style.width = (page.toString().length * $size * 0.45 ) + $size + 'px'; 
		if (clone_num)
		{ 
			clone_num.value = page;
			clone_num.style.width = (page.toString().length * $size * 0.45 ) + $size + 'px';;
		}
		if (window.table_of_contents && page == 1) 
			table_of_contents.style.display = '';
		
		if (browser_top) 
		{
			if (browser_top > 0) 
				window.scrollTo(0, browser_top);
			else 
			{
				var top = getTop(browser_body); 
				var scrollTop = window.pageYOffset || document.body.scrollTop;
				if (top < scrollTop)
					window.scrollTo(0, top + 2);
			}
		} 

		if (page < browser_total_page)				 
			browser_next.onclick = function() { browser_change_page(id, page+1, this) };
		else 
			browser_next.onclick = function() { browser_change_page(id, browser_total_page, this) };
		if (page > 1) 
			browser_prev.onclick = function() { browser_change_page(id, page-1, this) };
		else 
			browser_prev.onclick = function() { browser_change_page(id, 1, this) };
		if (clone_num) 
		{ 
			clone_next.onclick = browser_next.onclick; // = function() { browser_change_page(id, page+1, this) };
			clone_prev.onclick = browser_prev.onclick; // = function() { browser_change_page(id, 1, this) };					
		}				
				
		if (page < browser_total_page && !browser_cache[id][page+1]) 
		{
			cache_function = function() 
			{
				if (request.readyState == 4 && request.status == 200)
				{
					browser_cache[id][page+1] = request.responseText;
				}							
			} 				
			sendRequest(url + '&page=browser&browser=' + id + '&' + id + '_num=' + (page + 1) + '&action=get_content_only&current_page=' + page, cache_function, true);
		} else if (page > 1 && !browser_cache[id][page-1]) 
		{
			cache_function = function() 
			{
				if (request.readyState == 4 && request.status == 200)
				{
					browser_cache[id][page-1] = request.responseText;
				}							
			} 				
			sendRequest(url + '&page=browser&browser=' + id + '&' + id + '_num=' + (page - 1) + '&action=get_content_only&current_page=' + page, cache_function, true);
		} else 
			sendRequest(url + '&page=browser&browser=' + id + '&' + id + '_num=' + page + '&action=set_page_only', function(){}, true);	
	} else			
	{
		if (!browser_cache[id]) 
			browser_cache[id] = [];
		if (highlight_target) 
			do_progress_highlight(highlight_target);
		page_function = function() 
			{
				if (request.readyState == 4 && request.status == 200)
				{ 
					browser_body.innerHTML = request.responseText;
					browser_num.value = page;
					browser_num.style.width = (page.toString().length * $size * 0.45 ) + $size + 'px'; 
					if (clone_num)
					{ 
						clone_num.value = page;
						clone_num.style.width = (page.toString().length * $size * 0.45 ) + $size + 'px';;
					}
					
					if (window.table_of_contents && page == 1) 
						table_of_contents.style.display = '';
					if (browser_top) 
					{
						if (browser_top > 0) 
							window.scrollTo(0, browser_top);
						else 
						{
							var top = getTop(browser_body);
							var scrollTop = window.pageYOffset || document.body.scrollTop;
							if (top < scrollTop) 
								window.scrollTo(0, top + 2);
						}
					} 
					
					if (page < browser_total_page) 
						browser_next.onclick = function() { browser_change_page(id, page+1, this) };
					else 
						browser_next.onclick = function() { browser_change_page(id, browser_total_page, this) };
					if (page > 1) 
						browser_prev.onclick = function() { browser_change_page(id, page-1, this) };
					else 
						browser_prev.onclick = function() { browser_change_page(id, 1, this) };
					if (clone_num) 
					{
						clone_next.onclick = browser_next.onclick; // = function() { browser_change_page(id, page+1, this) };
						clone_prev.onclick = browser_prev.onclick; // = function() { browser_change_page(id, 1, this) }; 
					}
					browser_cache[id][page] = request.responseText;							
					progress_element.style.border = '1px solid #cccccc';
					progress_element = null;
					if (page < browser_total_page && !browser_cache[id][page+1]) 
					{
						cache_function = function() 
						{
							if (request.readyState == 4 && request.status == 200)
							{
								browser_cache[id][page+1] = request.responseText;
							}							
						} 				
						sendRequest(url + '&page=browser&browser=' + id + '&' + id + '_num=' + (page + 1) + '&action=get_content_only&current_page=' + page, cache_function, true);
					} else if (page > 1 && !browser_cache[id][page-1]) 
					{
						cache_function = function() 
						{
							if (request.readyState == 4 && request.status == 200)
							{
								browser_cache[id][page-1] = request.responseText;
							}							
						} 				
						sendRequest(url + '&page=browser&browser=' + id + '&' + id + '_num=' + (page - 1) + '&action=get_content_only&current_page=' + page, cache_function, true);
					}
				}	
			} 
		sendRequest(url + '&page=browser&browser=' + id + '&' + id + '_num=' + page, page_function, true); 
	}
	
	return !ajax; 
}	

function checkEnter(e, callback, obj) 
{
	e = e || event; 			
	keyCode = e.which || e.keyCode;
	  
	if (keyCode==13 || keyCode==10)
	{ 
		if (hasValue(callback)) 
			callback(obj);
		return true; 				
	} 
}

function defined(obj) 
{
	return typeof obj !== 'undefined'; 
}

function isNull(obj) 
{
	if (!defined(obj) || defined(obj) && obj === null) 
		return true; 
	else 
		return false; 
}

function isArray(obj) 
{
	return hasValue(obj) && obj.constructor == Array; 
}

function hasValue(obj) 
{
	return !isNull(obj); 
}

function getRandomId() 
{
	return Math.floor(Math.random() * 1000000000 + 1); 
}

function range(start, end, step) 
{	
	var range = new Array();
	step = hasValue(step) ? step : 1; 
	
	for (var i = start; i <= end; i+=step)
		range.push(i);   
		
	if (i < end) 
		range.push(end); 
		
	return range; 
}


