// 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 dictionary = new Dictionary(); 
var _ = null;

// Classes
function Dictionary () 
{
	this.id = "dictionary_" + getRandomId();
	this.domNode = null;   
		
	this.loadRecentWords = function () 
	{
		var recentWords = getEle("recent_words"); 
		if (recentWords) 
		{
			ajax.send("?obj=dictionary&method=get_recently_viewed", 
				function (req) 
				{
					recentWords.innerHTML = ""; 
					if (req.responseText) 
					{
						var words = json.decode(req.responseText); 
						if (words && words.length) 
						{
							for (var i = 0; i < words.length; i++) 
							{
								recentWords.appendChild(dom.create("span", _, "word", _, 
									"<a href='#' onclick='getEle(\"word\").value = this.innerHTML; dictionary.define()'>" + words[i].WORD + "</a>")); 
							}
						}
					}
					if (recentWords.innerHTML == "") 
						recentWords.innerHTML = "<span class='gray'>You haven't looked up any words yet.</span>"; 
				}); 
		}
	}
	
	this.createDomNode = function () 
	{
		var word = null; 
		var wordTable = dom.table(_, _, _,
			dom.create("tr", _, _, _,			
				[
					dom.create("td", _, _, _, word = dom.create("input", "word", _, {type: "text", placeholder: "Enter a word here"})), 
					dom.create("td", _, "word_label", _, dom.button(_, "Define", function () {dictionary.define()}))   
				] 
			)  
		);
		wordTable.style.width = "310px";
		event.add(word, "keydown", 
			function(e)
			{
				if (checkEnter(e)) 
				{
					dictionary.define(); 
				}
			}); 
		var recentWords = dom.create("div", _, _, _, 
			[
				dom.create("h3", _, "max_section_title", _, "Recently Viewed"), 
				dom.create("div", "recent_words", _, _, "<span class='gray'>You haven't looked up any words yet.</span>")  
			]); 
		this.domNode = dom.create("div", _, _, _, [wordTable, recentWords]); 
	}
	
	this.define = function () 
	{
		var word = getEle("word"); 
		
		word.value = word.value.replace(/[^a-zA-Z]/g, "");
		word.blur();
		if (word.value) 
		{  
			ajax.send("?obj=dictionary&method=get_definition&word=" + word.value, 
				function (req) 
				{	
					word.blur();					 				 
					var response = json.decode(req.responseText);
					if (response.definition)
					{ 						
						if (!dictionary.popup)
						{ 
							dictionary.popup = new Popup();					 
							dictionary.popup.width = display.isMobile() ? 312 : 790;
							dictionary.popup.easyClose = true; 
						}  
						if (!display.isMobile()) 
							dictionary.popup.height = 400;
						dictionary.popup.setTitle("Definition: " + word.value);
						dictionary.popup.setBody(response.definition);
						dictionary.popup.show(); 
						dictionary.addToRecentWords(word.value);  
					} 
					else if (response.error)
						new Popup("Error", response.error, true, true);
					else 
						new Popup("Connection Problem", "There was a problem connecting to the Internet.<br>Please try again later.", true, true);					
				});
		}   
	}
	
	this.addToRecentWords = function (word) 
	{
		var recentWords = getEle("recent_words"); 
		if (recentWords) 
		{
			if (recentWords.childNodes.length) 
			{
				if (recentWords.childNodes[0].className == "word")
				{ 
					var found = false; 
					for (var i = 0; i < recentWords.childNodes.length; i++) 
						if (recentWords.childNodes[i].firstChild.innerHTML == word)
							found = true;
					if (!found)  
						recentWords.insertBefore(dom.create("span", _, "word", _, 	
							"<a href='#' onclick='getEle(\"word\").value = this.innerHTML; dictionary.define()'>" + word + "</a>"), recentWords.firstChild);
				} else 
				{
					recentWords.innerHTML = ""; 
					recentWords.appendChild(dom.create("span", _, "word", _, 	
						"<a href='#' onclick='getEle(\"word\").value = this.innerHTML; dictionary.define()'>" + word + "</a>")); 
				}
			} // else never happens... 
		}		
	}
	
	var app = getEle("web_application");
	this.createDomNode(); 
	app.appendChild(this.domNode); 
	
	this.loadRecentWords();
	user.onLogin = user.onLogout = 
		function () {
			dictionary.loadRecentWords();  	
		}; 
}


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 (attribs)
{
	// new mdd 
	this.id = "mdd_" + getRandomId(); 
	this.values = hasValue(attribs) && attribs.allValues ? attribs.allValues : []; 
	this.multiple = hasValue(attribs) && attribs.multiple ? attribs.multiple : false; 
	this.selectedValues = hasValue(attribs) && attribs.selectedValues ? attribs.selectedValues : (this.multiple ? [] : "");
	this.onChange = hasValue(attribs) && attribs.events && attribs.events.change ? attribs.events.change : null;
	this.domNode = null; 
	
	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";
						} 
						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 (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";  			 																
				} 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";
				}				
			});
			
		this.domNode = dom.create("div", this.id, "max_select", _, [summary, values]);
		event.add(values, "mouseout", 
			function(evt) 
			{ 
				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(); 
				}
			}); 
	}
	
	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, easyClose) 
{
	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.easyClose = hasValue(easyClose) ? easyClose : false; 
	    
	 
	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);
				if (display.isMobile())
				{ 
					var ad = getEle("ad"); 
					if (ad) 
						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";
			if (display.isMobile())
			{ 
				var ad = getEle("ad"); 
				if (ad) 
					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); 
		if (this.easyClose) 
			this.enableEasyClose(); 		 
	}
	
	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);
				if (display.isMobile())
				{ 
					var ad = getEle("ad"); 
					if (ad) 
						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 <= display.getTop()) 
				top = display.getTop() + 2; 
			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&method=" + 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&method=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 = "post";
				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; 
}


