// Prototypes
String.prototype.trim = function () {
	return this.replace(/^\s+|\s+$/g,'');
}

String.prototype.toHtmlEntities = function () {
	return this.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); 
}

String.prototype.fromHtmlEntities = function () {
	return this.replace(/&lt;/g, "<").replace(/&gt;/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 dom = new DOM();
var display = new Display();
var keyboard = new Keyboard(); 
var user = new User(); 
var html = new Html();
var highlight = new BorderHighlighter();
var search = new SearchMAX()  
//var game = new Game();  
var _ = null;
  
// Classes
function SearchMAX () 
{
	var iSearch = this;  
	this.appNode = getEle("web_application"); 
	this.tabs = null;
	this.recentQueries = []; 
	this.recentNode = null;
	this.popularNode = null;  
	this.gSearch = null; 
	this.searchNode = null;
	this.popup = new Popup();
	this.popup.easyClose = true; 
	
	this.init = function () 
	{
		var gSearchNode = dom.create("td", "search_control"); 				  
				   
		this.appNode.innerHTML = "";  
		this.searchNode = dom.table(_, "google_search", _, dom.create("tr", _, _, _, gSearchNode));
		this.recentNode = dom.create("div", _, "gray", _, "There are no recent searches."); 
		this.popularNode = dom.create("div", _, "gray", _, "There are no popular searches.");
		
		var clearButton = dom.button(_, "Clear List", 
			function () 
			{
				iSearch.popup.setTitle("Clear Recent Searches"); 
				iSearch.popup.setBody("All recent searches will be deleted.<br>Are you sure?<br><br>" + 
					html.button(_, "Yes", "search.clearRecent()") + "&nbsp;" + 
					html.button(_, "Cancel", "search.popup.hide()"));
				iSearch.popup.show();  
			}); 
		var recents = dom.create("div", _, _, _, [this.recentNode, dom.create("br"), clearButton]);  
		
		this.tabs = new TabContainer("search", this.appNode);
		this.tabs.addTab("search", "Search", this.searchNode, true);
		this.tabs.addTab("recent", "Recent", recents, false);  
		this.tabs.addTab("popular", "Popular", this.popularNode, false, function () {iSearch.updatePopular();});		
					
		this.gSearch = new google.search.SearchControl();
		var webSearch = new google.search.WebSearch(); 
		webSearch.setRestriction(google.search.Search.RESTRICT_SAFESEARCH, google.search.Search.SAFESEARCH_OFF);	
	    this.gSearch.addSearcher(webSearch);
	    var imageSearch = new google.search.ImageSearch();	     
		imageSearch.setRestriction(google.search.Search.RESTRICT_SAFESEARCH, google.search.Search.SAFESEARCH_OFF);
	    this.gSearch.addSearcher(imageSearch);
	    if (!display.isMobile()) 
	    	this.gSearch.addSearcher(new GvideoSearch());	    	
	    this.gSearch.addSearcher(new google.search.NewsSearch());
	    this.gSearch.addSearcher(new google.search.BlogSearch());
	    this.gSearch.addSearcher(new google.search.LocalSearch());
	    this.gSearch.setResultSetSize(google.search.Search.LARGE_RESULTSET);
	    this.gSearch.setSearchStartingCallback(this, 
	    	function (sc, searcher, query) 
	    	{
	    		iSearch.addRecent(query);
   				keyboard.hide();	 
	    	});	            
	    	
	    // draw in searchControl layout mode
	    var drawOptions = new google.search.DrawOptions();
	    drawOptions.setDrawMode(google.search.SearchControl.DRAW_MODE_TABBED);
	    this.gSearch.draw(gSearchNode, drawOptions);
	    
	    this.updateRecent(); 
		this.updatePopular();
		
		user.onLogin = user.onLogout = 
			function () {
			    iSearch.updateRecent(); 
			};
			
		if (display.isMobile()) 
			setTimeout("display.setTop()", 4000);	      		
	}
	
	this.addRecent = function (query) 
	{
		var exists = false; 
		for (var i = 0; i < this.recentQueries.length; i++) 
			if (query.toLowerCase() == this.recentQueries[i].toLowerCase())
			{
				exists = true; 
				break; 
			}
		if (!exists) 
		{
			this.recentQueries.push(query); 
			this.addToRecentNode([query]);
		}
		 			
		ajax.send("?obj=search&method=addRecent", 
			function (req) 
			{
				if (req.responseText) 
					debug.log(req.responseText); 
			}, {params:{query:query}});
	}
	
	this.addToQueryNode = function (queryNode, queries, clearCurrent) 
	{
		if (isNull(queryNode) || isNull(queries)) return; 
		if (queryNode.className == "gray" || hasValue(clearCurrent) && clearCurrent)
		{
			queryNode.className = "";
			queryNode.innerHTML = "";  
		}
			
		for (var i = 0; i < queries.length; i++) 
		{
			var query = dom.create("span", _, "max_search_query", _, queries[i].toHtmlEntities());
			event.add(query, "click", 
				function (e) 
				{
					e = e || window.event;
					ele = e.target || e.srcElement;  
					iSearch.gSearch.execute(ele.innerHTML.fromHtmlEntities()); 
					iSearch.tabs.setCurrentTab("search");  
				});
			if (queryNode.childNodes.length)
				queryNode.insertBefore(query, queryNode.firstChild); 
			else 	
				queryNode.appendChild(query); 
		} 
	}
	
	this.addToRecentNode = function (queries, clearCurrent) 
	{
		this.addToQueryNode(this.recentNode, queries, clearCurrent); 
	}	

	this.addToPopularNode = function (queries, clearCurrent) 
	{
		this.addToQueryNode(this.popularNode, queries, clearCurrent); 
	}	
	
	this.updateRecent = function () 
	{
		ajax.send("?obj=search&method=getRecent", 
			function (req) 
			{
				iSearch.recentNode.innerHTML = "<div class='gray'>There are no recent searches.</div>";
				if (req.responseText) 
				{
					var result = json.decode(req.responseText); 
					if (result.length) 
					{
						iSearch.recentNode.innerHTML = "";
						var recentQuery = "";
						iSearch.recentQueries = [];  
						for (var i = 0; i < result.length; i++)
						{
							iSearch.recentQueries.push(result[i].query);
							recentQuery = result[i].query; 
						}
						iSearch.addToRecentNode(iSearch.recentQueries);
						iSearch.gSearch.execute(recentQuery);  
					}
				}
			}); 
	}
	
	this.updatePopular = function () 
	{
		ajax.send("?obj=search&method=getPopular", 
			function (req) 
			{
				if (req.responseText) 
				{
					var result = json.decode(req.responseText); 
					if (result.length) 
					{
						var queries = []; 
						for (var i = 0; i < result.length; i++) 
							queries.push(result[i].query); 
						iSearch.addToPopularNode(queries, true); 
					}
				}
			}); 		
	}
	
	this.clearRecent = function () 
	{
		ajax.send("?obj=search&method=clearRecent"); 
		this.recentNode.innerHTML = "<div class='gray'>There are no recent searches.</div>"; 
	}
	
	this.init(); 
}

function Game () 
{
	var gameInstance = this;
	this.id = "game_" + getRandomId();
	this.domNode = null;
	this.highScoreNode = null;
	this.size = display.isMobile() ? 9 : 21;
	this.wins = 0; 
	this.losses = 0;
	this.score = 0;  
	this.highScore = 0; 
	this.message = null;
	this.xDelta = 0; 
	this.speed = 1;   
	this.paused = false;
	// Pads 
	this.comPad = []; 
	this.comPadColor = "goldenrod"; 
	this.userPad = [];
	this.userPadColor = "#0000aa";
	this.mouseoverCell = null;  
	// Ball 
	this.ball = null; 
	this.ballX = 0; 
	this.ballY = 0; 
	this.ballXDelta = 0; 
	this.ballYDelta = 0; 	
	
		
	
	this.init = function () 
	{
		var app = getEle("web_application");
		app.innerHTML = "<div class='center gray'>Initializing...</div><br>";
		 
		this.createDomNode();
		this.createHighScoreNode();  
		//app.appendChild(this.domNode); 
		app.innerHTML = ""; 
		var tabs = new TabContainer("tictactie", app);
		tabs.addTab("play", "Play", this.domNode, true, function () {game.resume()});
		tabs.addTab("pause", "Pause", "<div class='center' style='font-size: 1.2em'><br>Taking a break, huh? ^_^<br><br></div>", 
			false, function() {game.paused = true;});  
		tabs.addTab("score", "High Scores", this.highScoreNode, false, function () {game.paused = true; game.loadHighScores();}); 
		
		this.loadHighScores(); 
		user.onLogin = user.onLogout = 
			function () {
				//game.reset();
				game.saveScore();  
				game.loadHighScores();	
				//game.updateScore(true); 		  			 
			};
			
		if (display.isMobile()) 
			setTimeout("display.setTop()", 2000);	      
		
		event.add(document, "keydown", 
			function (e)
			{
				var e = e || window.event;
				var code = e.keyCode || e.which;
				//debug.log(code);  
				if (code >= 37 && code <= 40 || code == 27 || code == 19) 
				{
					switch (code) 
					{
						case 37: // left
							game.movePad(game.userPad, "left");
							game.mouseoverCell = null; 							 					
							break;  
						case 38: // up						
							break; 
						case 39: // right
							game.movePad(game.userPad, "right");
							game.mouseoverCell = null;
							break; 
						case 40: // down
							break; 
						case 27: // esc
						case 19: // pause
							if (game.paused) 
								game.resume(); 
							else 
								game.pause(); 
							break; 
					}
					
					event.stop(e); 
					return false;  			
				}		
			}); 	
			
			this.reset(); 	
			this.moveBall(true); 				 	  			
	}

	this.moveBall = function (persist) 
	{		
		if (hasValue(persist) && persist) 
			setTimeout("game.moveBall(true);", (display.isMobile() ? 30 : 10) + (100 / (this.score / 300 + 1)));
		if (!this.paused && this.grid.enabled && this.ballYDelta) 
		{
		 	// move user pad
			this.moveUserPad();
			if ((this.flip = !this.flip) == true) return; 
			
			this.ballX += this.ballXDelta;
			this.ballY += this.ballYDelta;
			var x = Math.round(this.ballX); 
			var y = Math.round(this.ballY);
			
			if (y < 0 || y > this.size - 1)
				this.gameOver(); 
			else
			{	 
				if (x < 0 || x > this.size - 1)
				{ 
			 		this.ballXDelta = -this.ballXDelta;
			 		x = this.ball.attribs.x; 
			 	} 
			 	
							
				// check padding & move comp pad. 
				if (x != this.ball.attribs.x || y != this.ball.attribs.y)
				{
					var bgColor = this.grid.cells[y][x].getBgColor(); 
					switch (bgColor) 
					{
						case this.comPadColor: 
							this.ballYDelta = 1;
							this.moveBall();  
							break; 
						case this.userPadColor: 
							this.ballYDelta = -1;
							this.score+=15;
							this.updateScore(); 
							this.moveBall();
							break;  
						default: 
							this.ball.setEmpty(); 
							this.ball = this.grid.cells[y][x];
							this.ball.setContent("O");
					}
					
					var dir = ""; 
					if (this.comPad[0].attribs.x > this.ball.attribs.x) 
						dir = "left"; 
					else if (this.comPad[0].attribs.x < this.ball.attribs.x)
						dir = "right"; 
					if (dir)  
						this.movePad(this.comPad, dir);    
				} 				
			}
		} 
	}
	
	this.moveUserPad = function () 
	{
		var dir = ""; 
		if (this.mouseoverCell) 
		{
			if (gameInstance.userPad[0].attribs.x > this.mouseoverCell.attribs.x) 
				dir = "left"; 
			else if (gameInstance.userPad[0].attribs.x < this.mouseoverCell.attribs.x)
				dir = "right"; 
			if (dir)  
				gameInstance.movePad(gameInstance.userPad, dir);
			else 
				this.mouseoverCell = null; 	
		}
	}
	
	this.gameOverMsg = new Popup("Game Over", "<div class='center bigger'><br>You dropped the ball!! <br>Play again? :o)<br><br>" + 
		html.button(_, "Ok") + "</div>", false, true, 
		{close: 
			function() 
			{
				game.reset(); 
			}
		});
	this.pausedMsg = new Popup ("Paused", "<div class='center bigger'><br>Taking a break, huh?</br><br>" + 
		html.button(_, "Resume") + "</div>", false, true, 
		{close: 
			function () 
			{
				game.paused = false; 				 
			}
		}); 
	
	this.pausedMsg.width = this.gameOverMsg.width = display.isMobile() ? 300 : 400;  
	this.grid = new GameGrid({size: gameInstance.size}, _, 
		{click: function (e, grid, cell) 
			{
				if (game.paused) 
					game.resume(); 
				else 
					game.pause();  					
			}, 
		mouseover: function (e, grid, cell) 
			{
				game.mouseoverCell = cell; 				 				
				if (!game.ballYDelta) 
				{
					game.ballYDelta = -1;
					game.updateScore(); 
				} 
				if (!game.ballXDelta) 
					game.ballXDelta = 1; 
			}
		}); 
		
	this.reset = function () 
	{
		this.grid.walkCells(function (cell){cell.setBgColor("#ffffff")}); 
		this.grid.setEmpty(); 
		this.score = this.highScore ? Math.round(this.highScore / 2) : 0;
		this.grid.enabled = true;	
		this.paused = false; 				

		var middle = parseInt(this.size / 2); 
		this.ball = this.grid.cells[middle][middle];
		this.ball.setContent("O"); 
		this.ballX = this.ball.attribs.x; 
		this.ballY = this.ball.attribs.y; 
		
		this.comPad = []; 
 		var y = display.isMobile() ? 0 : 1; 
		this.comPad.push(this.grid.cells[y][middle]); 
		for (var i = 1; i < 2; i++)
		{ 
			this.comPad.push(this.grid.cells[y][middle - i]);
			this.comPad.push(this.grid.cells[y][middle + i]);
		} 
		for (var i = 0; i < this.comPad.length; i++) 
			this.comPad[i].setBgColor(this.comPadColor); 

		this.userPad = []; 
		this.userPad.push(this.grid.cells[this.size - y - 1][middle]); 
		for (var i = 1; i < 2; i++)
		{ 
			this.userPad.push(this.grid.cells[this.size - y - 1][middle - i]);
			this.userPad.push(this.grid.cells[this.size - y - 1][middle + i]);
		} 
		for (var i = 0; i < this.userPad.length; i++) 
			this.userPad[i].setBgColor(this.userPadColor);
		this.score = 0; 
		this.ballYDelta = 0; 
		this.updateScore(true); 		 
	}
	
	this.movePad = function (pad, dir) 
	{
		if (!this.grid.enabled) return;
		if (!this.ballYDelta) 
		{
			this.ballYDelta = -1;
			this.updateScore(); 
		} 
		if (!this.ballXDelta) 
			this.ballXDelta = 1; 
		switch (dir) 
		{
			case "left": 
				if (pad[pad.length - 2].attribs.x > 0)
				{
					var bgColor = pad[pad.length - 2].getBgColor();
					var y = pad[pad.length - 2].attribs.y; 
					var x = pad[pad.length - 2].attribs.x;  
					pad[pad.length - 1].setBgColor("#ffffff"); 
					for (var i = 0; i < pad.length; i++)
					{ 
						pad[i] = this.grid.cells[y][pad[i].attribs.x - 1];
						pad[i].setBgColor(bgColor);  
					}
				}	
				break; 
			case "right":
				if (pad[pad.length - 1].attribs.x < this.size - 1)
				{
					var bgColor = pad[pad.length - 2].getBgColor();
					var y = pad[pad.length - 2].attribs.y; 
					var x = pad[pad.length - 2].attribs.x;  
					pad[pad.length - 2].setBgColor("#ffffff");
					for (var i = 0; i < pad.length; i++)
					{ 
						pad[i] = this.grid.cells[y][pad[i].attribs.x + 1];
						pad[i].setBgColor(bgColor);  
					}
				}	
				break; 
		}
	}

	this.gameOver = function () 
	{
		if (this.grid.enabled) 
		{
			this.grid.enabled = false;
			this.gameOverMsg.show();  
			dom.setOpacity(this.gameOverMsg.window, 9);
		}		 
	}
		
	this.move = function () 
	{
	}

	
	this.updateScore = function (intro) 
	{
		if (this.score)
		{ 
			var msgs = [];
			var score = "";
			score = "<span class='max_field_title'>Score:</span> " + this.score + "<br>";  
			//msgs.push("<span class='max_field_title'>Wins:</span> " + this.wins); 
			//msgs.push("<span class='max_field_title'>Losses:</span> " + this.losses);
			this.message.innerHTML = score; // + msgs.join(" &nbsp; | &nbsp; "); 	
		} else if (defined(intro) && intro) 
			this.message.innerHTML = (display.isMobile() ? "Click on the board " : "Mouse over the board or use arrow keys ") + "to play<br>"; 
		else 
			this.message.innerHTML = "Let the game begin!<br>";
		if (this.score > this.highScore)
		{ 
			ajax.send("?obj=game&method=save_score", function() {}, {params:{score:game.score}});
			this.highScore = this.score;
		}  
		
	}	
	
	this.saveScore = function () 
	{
		if (this.highScore)
			ajax.send("?obj=game&method=save_score", function() {}, {params:{score:game.highScore}});		
	}
	
	this.createDomNode = function () 
	{
		this.message = dom.create("div", _, "max_score_message", _, (display.isMobile() ? "Click on the board " : "Mouse over the board or use arrow keys ") + "to play<br>"); 
		var container = dom.create("div", _, _, _, [this.message, dom.create("br"), this.grid.domNode]); 
		container.style.textAlign = "center";
		this.domNode = dom.create("div", _, _, _, container); 
	}
	
	this.loadScore = function () 
	{
		ajax.send("?obj=game&method=get_score", 
			function (req) 
			{
				var response = json.decode(req.responseText);
				game.score = response.score;
				game.updateScore(true);  
			}); 
	} 
	
	this.createHighScoreNode = function () 
	{	
		this.highScoreNode = dom.create("div", _, _, _, "<span class='gray'>There are no high scores.</span>");  
	}
	
	this.pause = function () 
	{
		this.paused = true; 
		this.pausedMsg.show(); 
		dom.setOpacity(this.pausedMsg.window, 9);
		
	}
	
	this.resume = function () 	
	{
		this.pausedMsg.hide(); 
		this.paused = false; 
	}
	
	this.loadHighScores = function () 
	{
		if (this.highScoreNode) 
		{			 
			ajax.send("?obj=game&method=get_high_scores", 
				function (req) 
				{					 
					if (req.responseText) 
					{
						var response = json.decode(req.responseText); 
						if (response.scores && response.scores.length) 
						{
							game.highScoreNode.innerHTML = ""; 
							if (response.rank > 0) 
								game.highScoreNode.appendChild(dom.create("div", _, "center max_score_message", _, "<b class='max_field_title'>Your Rank:</b> " + response.rank));
							else if (game.highScore)
								game.highScoreNode.appendChild(dom.create("div", _, "center", _, "<span style='font-size:0.65em'>Want to see where you are ranked?<br><a href='#' onclick='user.login(); return false'>Login</a> or " + 
									"<a href='#' onclick='user.register(); return false'>Register</a></span>")); 
							
							var trs = []; 
							trs.push(dom.create("tr", _, _, _, 
								[
									dom.create("th", _, "max_table_title", {width: "20%"}, "<b>Rank</b>"),  
									dom.create("th", _, "max_table_title", {align: "left"}, "Player"),
									dom.create("th", _, "max_table_title_right", {align: "right"}, "Score")
								])); 
							for (var i = 0; i < response.scores.length; i++) 
								trs.push(dom.create("tr", _, _, _, 
									[
										dom.create("td", _, "max_table_data", _, "<b>" + (i+1) + "</b>"), 
										dom.create("td", _, "max_table_data", _, response.scores[i].user.toString().toHtmlEntities()), 
										dom.create("td", _, "max_table_data", {align:"right"}, response.scores[i].score)
									]));
							var table = dom.table(_, _, {align:"center"}, trs); 
							//table.style.width = "90%";
							table.style.marginTop = "1em";
							table.style.marginBottom = "1em";  
							table.style.border = "1px solid #f0f0f0"; 
							game.highScoreNode.appendChild(table);  						
						}
					}
				}); 
		}
	}	
		
	this.init(); 
}

function GameGrid(attribs, styles, events) 
{
	this.className = "max_game_grid"; 
	if (hasValue(attribs) && attribs.size) 
		this.rows = this.cols = attribs.size
	else
	{ 
		this.rows = hasValue(attribs) && attribs.rows ? attribs.rows : 3; 
		this.cols = hasValue(attribs) && attribs.cols ? attribs.cols : this.rows;
	}
	this.width = display.getWidth(true) * 0.85;	
	this.cellWidth = this.width / this.cols * 0.7;
	//this.cellWidthPercent = (this.cellWidth / this.width * 100).toFixed(2);   
	this.height = this.width * this.rows / this.cols * (display.isMobile() ? 0.9 : 0.7);
	this.cellHeight = this.height / this.rows * (display.isMobile() ? 0.8 : 0.7); 
	//this.cellHeightPercent = (this.cellHeight / this.height * 100).toFixed(2);
	this.styles = hasValue(styles) ? styles : {};  
	this.events = hasValue(events) ? events : {};   
	this.cells = []; 
	this.domNode = null;
	this.enabled = true;  
	var grid = this;
	this.createDomNode = function () 
	{	
		var cellRows = [];
		for (y = 0; y < this.rows; y++) 
		{
			this.cells[y] = [];
			var cellCols = [];  
			for (x = 0; x < this.cols; x++) 
			{
				this.cells[y][x] = new GameCell({x:x, y:y}, {width: grid.cellWidth, height: grid.cellHeight}, 
					{click: 
						function(e, cell) 
						{
							if (grid.enabled)
							{
								if (grid.events.change) 
									grid.events.change(e, grid, cell);
								if (grid.events.click) 
									grid.events.click(e, grid, cell);
							}
						},
					mouseover: 
						function(e, cell) 
						{
							if (grid.enabled)
							{
								if (grid.events.mouseover) 
									grid.events.mouseover(e, grid, cell);
							}
						}										
					});				 
				cellCols.push(this.cells[y][x].domNode); 
			}
			cellRows.push(dom.create("tr", _, _, _, cellCols));  
		}

		this.domNode = dom.table(_, this.className, {align: "center", "cellspacing":0}, cellRows);
		this.domNode.style.width = this.width; 
		this.domNode.style.height = this.height;
	}

	this.setEmpty = function () 
	{
		this.walkCells(
			function(cell) 
			{
				cell.setEmpty(); 
			}); 
	}	
	
	this.walkCells = function (callback) 
	{
		for (var y = 0;  y < this.rows; y++) 
			for (var x = 0; x < this.cols; x++) 
				callback(this.cells[y][x]); 
	}
	
	this.hasEmpty = function () 
	{
		for (var y = 0; y < this.rows; y++) 
			for (var x = 0; x < this.cols; x++) 
				if (this.cells[y][x].isEmpty()) 
					return true; 
		return false; 
	}
	
	this.countEmpty = function () 
	{
		var count = 0; 
		for (var y = 0; y < this.rows; y++) 
			for (var x = 0; x < this.cols; x++) 
				if (this.cells[y][x].isEmpty()) 
					count++;  
					
		return count;  
	}
		
	this.isFull = function () 
	{
		return !this.hasEmpty(); 
	}
	
	this.createDomNode();  
}

function GameCell(attribs, styles, events) 
{
	var cell = this;
	this.className = "max_game_cell"; 
	this.emptyPlaceholder = "&nbsp;"; 
	this.attribs = hasValue(attribs) ? attribs : {};
	if (!this.attribs.content) 
		this.attribs.content = this.emptyPlaceholder;  
	this.styles = hasValue(styles) ? styles : {}; 
	this.events = hasValue(events) ? events : {}; 
	this.domNode = null;
	this.bgColor = ""; 
	
	this.createDomNode = function () 
	{
		this.domNode = dom.create("td", _, this.className, this.attribs, this.attribs.content);		 				 
		for (var name in this.styles) 
			this.domNode.style[name] = this.styles[name];  
		 
		for (var name in this.events) 
			event.add(this.domNode, name, 
				function (e) 
				{
					cell.events[name](e, cell); 
				}); 
	} 
	
	this.setBgColor = function (color) 
	{
		this.domNode.style.backgroundColor = color;
		this.bgColor = color;  
	}
	
	this.getBgColor = function () 
	{
		if (this.bgColor)
			return this.bgColor; 
		else if (this.domNode && this.domNode.style.backgroundColor) 
			return this.domNode.style.backgroundColor; 
		else 
			return ""; 
	}
	
	this.setContent = function (content) 
	{
		if (typeof content == "string") 
			this.domNode.innerHTML = content; 
		else 
		{
			this.domNode.innerHTML = ""; 
			this.domNode.appendChild(content); 
		}
	}
	
	this.getContent = function () 
	{
		return this.domNode.innerHTML; 	
	}
	
	this.isEmpty = function () 
	{		
		if (this.domNode.innerHTML.charCodeAt(0) == 160 || this.domNode.innerHTML == "" || this.domNode.innerHTML == this.emptyPlaceholder) 
			return true; 
		else 
			return false; 
	}
	
	this.setEmpty = function () 
	{
		this.domNode.innerHTML = this.emptyPlaceholder; 
	}
	
	this.createDomNode(); 
}

function Keyboard () 
{
	if (display.isMobile()) 
	{
		this.hiddenInput = dom.create("input", _, _, {type:"checkbox"});	
		this.hiddenInput.style.fontSize = "0.01em"; 
		this.hiddenInput.style.width = "1px"; 
		this.hiddenInput.style.height = "1px"; 
		this.hiddenInput.style.border = "none"; 
		var body = document.getElementsByTagName("body")[0]; 
		body.appendChild(this.hiddenInput);
	}	
	
	this.hide = function () 
	{
		if (display.isMobile())
			this.hiddenInput.focus(); 
	} 	 	
}

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 = eventObj || window.event; 
		if (typeof e === "undefined" || !e) return;  
	 
		e.cancelBubble = true; 
		if (e.stopPropagation) 
			e.stopPropagation();
		if (e.preventDefault) 
			e.preventDefault(); 
	}
}

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 (container) 
	{
		if (hasValue(container) && container) 
			return this.container.offsetWidth; 
		else 
			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; 
	}
	
	this.setOpacity = function (obj, value) {
		obj.style.opacity = value / 10;
		obj.style.filter = 'alpha(opacity=' + value * 10 + ')';
	}	
} 

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, events) 
{
	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.events = hasValue(events) ? events : {};
	var popupInstance = this;  
	    
	 
	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;		
		
		if (getEle(this.id))
		{
			getEle(this.id).style.display = "none";
			var ad = getEle("ad"); 
			if (ad && display.isMobile()) 
				ad.style.display = ""; 				
			display.setTop(this.displayTop);
			if (this.events.close) 
				this.events.close(); 
		}
	}
	
	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;
		popupInstance.displayTop = display.getTop();
		var close = function() 
			{
				popupInstance.hide();  
			}
		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) 
		{
			popupInstance.displayTop = display.getTop(); 			
			event.add(this.window, "click", function() {popupInstance.hide();}); 
		}
	}
	
	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() 
{
	var iUser = this; 
	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.onRegister = 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)
							{
								if (display.isMobile()) 
									keyboard.hide();      
								user.loginBox.hide();  
								user.userName = name.value.toHtmlEntities();
								user.updateLoginStatus(); 
								if (!register && user.onLogin)
									user.onLogin();  
								if (register && user.onRegister)
									user.onRegister();  
							} 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, "keypress", doLogin);    	
			event.add(pass, "keypress", 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 = function () {}; 
				
				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 BorderHighlighter(attribs) 
{
	var highlightInstance = this;
	this.attribs = hasValue(attribs) ? attribs : {type: "rainbow"}; 
	this.colors = []; 
	this.type = null;					// value set at end. 		Possible Values: red, green, blue, rainbow
	this.element = null;				// value set at end.
	this.speed = null;					// value set at end   	 
	this.elementOriginalBorder = null; 	// value st at end.  
	this.started = false; 
	this.colorIndex = 0;
	this.rainbowColors = 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');
 
	this.start = function (attribs) 
	{
		if (attribs.element === this.element) 
			return; // avoid repeated calls with the same element. causes browser to use lots of CPU. 
			 
		if (this.started) 
			this.stop(); 
		if (attribs) 
		{
			this.setElement(attribs.element); 
			this.setOriginalBorder(attribs.border); 
			this.setType(attribs.type); 
			this.setSpeed(attribs.speed);
			  
			//this.element = hasValue(element) ? element : this.element;
			//this.elementOriginalBorder = hasValue(originalBorder) ? originalBorder : this.elementOriginalBorder;
		} 
		
		if (this.element) 
		{
			if (this.element.style) 
			{				
				if (!this.elementOriginalBorder && this.element.style.border) 
					this.elementOriginalBorder = this.element.style.border; 
				this.changeBorder(); 
				this.started = true;
			} else
				debug.log("BorderHighlighter.start() Element (" + this.element + ") doesn't have a style attribute.");  
		}
	}
	
	this.stop = function () 
	{
		if (this.element) 
		{
			if (this.elementOriginalBorder) 
				this.element.style.border = this.elementOriginalBorder;
			else 
				this.element.style.border = "1px solid #ffffff";  
			this.element = null;
			this.elementOriginalBorder = ""; 
		}		
		this.started = false;  
	}
	
	this.setElement = function (element) 
	{
		element = (typeof element == "string") ? getEle(element) : element; 
		if (this.started)
			this.start(element); 
		else 
			this.element = hasValue(element) ? element : null; 
	}
	
	this.changeBorder = function () 
	{
		if (this.element) 
		{
			this.colorIndex += this.speed; 
			if (this.colorIndex >= this.colors.length - 1)
				this.colorIndex = 0; 
			this.element.style.border = "1px solid " + this.colors[this.colorIndex];
			setTimeout(function() { highlightInstance.changeBorder() }, 300);
		} 
	}
	
	this.setType = function (type)
	{
		this.type = hasValue(type) ? type : (this.type ? this.type : "rainbow");
		this.colors = [];  
		switch (this.type) 
		{		 
			case "red":
				for (var i = 0; i < 255; i+=20) 
					this.colors.push("rgb(255, " + i + ", " + i + ")"); 
				for (var i = 255; i > 0; i-=20) 
					this.colors.push("rgb(255, " + i + ", " + i + ")"); 							
				break; 
			case "green": 
				for (var i = 0; i < 255; i+=20) 
					this.colors.push("rgb(" + i + ", 255, " + i + ")"); 
				for (var i = 255; i > 0; i-=20) 
					this.colors.push("rgb(" + i + ", 255, " + i + ")"); 							
				break; 
			case "blue": 
				for (var i = 0; i < 255; i+=20) 
					this.colors.push("rgb(" + i + ", " + i + ", 255)"); 
				for (var i = 255; i > 0; i-=20) 
					this.colors.push("rgb(" + i + ", " + i + ", 255)"); 							
				break; 
			case "rainbow": 
			default: 
				this.colors = this.rainbowColors;  	
		} 
	}
	
	this.setSpeed = function (speed) 
	{
		this.speed = hasValue(speed) ? speed : (this.speed ? this.speed : 1);
		if (this.speed < 1) 
			this.speed = 1; 
	}
	
	this.setOriginalBorder = function (border) 
	{
		this.elementOriginalBorder = hasValue(border) ? border : (this.elementOriginalBorder ? this.elementOriginalBorder : ""); 
	}
	
	this.setType(this.attribs.type);  
	this.setElement(this.attribs.element);
	this.setSpeed(this.attribs.speed);   	
	this.setOriginalBorder(this.attribs.border); 	
	
	if (this.element && this.type)
		this.start()  		
}

function getEle(id) 
{
	return document.getElementById(id); 
}		

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 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 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; 
}


