		/*
		 * Global variables
		 */

			//When true, all pages reload - no cache
			var realtime = false;
			var datahome = "";
		/*
		 * Functions called from page
		 */

			//Called to load a page
			//FE all kinds of checks to avoid useless routines
			function loadpage(num,subnum,force)
			{
				//Reload submenu only if necessary
				if (currentnum != num)
					redrawsubmenu(num);

				//Redraw tail only if necessary
				if (currentnum != num || currentsubnum != subnum)
					redrawtail(num,subnum);

				//Load content always - for let that function determine if it needs updating
				populatecontent(num,subnum,force);

				currentnum = num;
				currentsubnum = subnum;
			}

			//Initiate a refresh of the contents of the page
			function refreshpage(num,subnum)
			{
				if (num == currentnum && subnum == currentsubnum)
				{
					var contentdiv = document.getElementById('content');		
					contentdiv.innerHTML = "<p>Loading...</p>";
				}
				db_getpage(num,subnum);
			}

			//Initiate a refresh of the contents of a section of pages
			function refreshpages(num)
			{
				if (num == currentnum)
				{
					var contentdiv = document.getElementById('content');		
					contentdiv.innerHTML = "<p>Loading...</p>";
				}
				db_getpages(num);
			}

			//Initiate a refresh of the contents of all sections of pages
			function refreshallpages()
			{
				var contentdiv = document.getElementById('content');		
				contentdiv.innerHTML = "<p>Reloading all pages to reload user permissions...</p>";

				setTimeout("db_getallpages()",1);
			}

			//Login function called by local login form
			function login(mode)
			{
				if (!mode && getById('login_username') && getById('login_password'))
				{
					//Local authentication initiated
					var username = getById('login_username').value;
					var password = md5(login_hash+""+md5(getById('login_password').value));
					var password = md5(md5(getById('login_password').value));

					//Clear plain-text password for security
					//getById('login_password').value = "";
					
					db_sendlogin(username,password);
				}
				else
				{
					//MTU ISO initiated...
					setTimeout("db_sendlogin()",1);
				}
			}
			
			function checklogin(timeout)
			{
				if (!timeout)
				{
					timeout = 60000;
				}
					
				setTimeout("db_sendlogin()",1);
				setTimeout("checklogin("+timeout+")",timeout);
			}

			//Function called by logout form
			function logout()
			{
				if (user.authentication != "mtuiso")
				{
					db_logout();
				}
				else
				{	
					replaceHTML(getById('content'),'<iframe src="http://CrusadeMember:password@ccc.students.mtu.edu/new/login/logout.php" id="logoutframe" width=600px height=400px></iframe>');
					datahome = "";
					
				}
			}

			function loadeditor()
			{
				var content = getById("content");
				//div.innerHTML = "<textarea name=contenteditor  id=contenteditor>"+submenu[currentnum][currentsubnum]['text']+"</textarea>";
				content.innerHTML = "<div id=contenteditor>"+submenu[currentnum][currentsubnum]['text']+"</div>";
				content.innerHTML += '<br><a href="#" onclick="submiteditor(); return false;">Save changes</a> &nbsp; &nbsp; <a href="#" onclick="reseteditor(); return false;">Discard changes</a>';
				var fck = new FCKeditor("contenteditor");
				fck.Config["CustomConfigurationsPath"] = "/new/fcksettings.js";
				fck.BasePath = "/new/FCKeditor/";
				fck.Width = "100%";
				fck.Height = "90%";
				fck.Value = submenu[currentnum][currentsubnum]['text'];

				var contenteditor = getById("contenteditor");
				contenteditor.innerHTML = fck.CreateHtml();
				//fck.ReplaceTextarea() ;
			}

			function submiteditor()
			{
				var contenteditor = FCKeditorAPI.GetInstance('contenteditor') ;
				db_updatepage(currentnum,currentsubnum,contenteditor.GetXHTML());
			}
			
			function reseteditor()
			{
				loadpage(currentnum,currentsubnum);
			}
		

		/*
		 * Support functions - can be called manually - but prefer the above functions
		 */

			//Support function to keep navigation icons (tail, dots, w/e) updated
			function redrawtail(num,subnum)
			{
				if (num != null)
				{
					var head = document.getElementById('tail0');
					head.innerHTML = "<img src='tail_start.gif'>";
					for (var i=1;document.getElementById('tail'+i) != null;++i)
					{
						current = document.getElementById('tail'+i);
						if (i < num)
						{
							current.innerHTML= "<img src='tail_mid.gif'>";
						}
						else if (i == num)
						{
							current.innerHTML = "<img src='tail_end.gif'>";
						}
						else
						{
							current.innerHTML = "&nbsp;";
						}
					}
				}
				if (subnum != null)
				{
					var head = document.getElementById('subtail0');
					head.innerHTML = "<img src='subtail_start.gif'>";

					var space = document.getElementById('subtail0.5');
					if (space != null)
						space.innerHTML = "<img src='subtail_mid.gif'>";

					for (var i=1;document.getElementById('subtail'+i) != null;++i)
					{
						current = document.getElementById('subtail'+i);
						if (i < subnum)
						{
							current.innerHTML= "<img src='subtail_mid.gif'>";
						}
						else if (i == subnum)
						{
							current.innerHTML = "<img src='subtail_end.gif'>";
						}
						else
						{
							current.innerHTML = "&nbsp;";
						}
					}
				}
			}

			//Support function to update submenu on side
			//FE check date_fetched - refetch/reload menu if old, and implement "Loading..." feature
			function redrawsubmenu(num)
			{	
				var submenudiv = document.getElementById('submenu');
				submenudiv.innerHTML = '';

				for (var i=1;submenu[num][i] != null;++i)
				{
					submenudiv.innerHTML += '<div class="submenurow"><div class="submenuitem" id="submenu'+i+'"><a href="#" onclick="loadpage('+num+','+i+');return false">'+submenu[num][i]["title"]+'</a></div><div class="submenutail" id="subtail'+i+'">&nbsp;</div></div>';
				}
			}

			//Support function to load content
			//FE check date_fetched - refetch old content, and implement "Loading..." feature
			//FE implement page age, date_modified, user_modified, etc
			function populatecontent(num,subnum,force)
			{
				var contentdiv = document.getElementById('content');
				if (contentdiv != null && contentdiv.innerHTML != null && submenu[num][subnum] != null)
				{
					if (force == null && (realtime || pageage(num,subnum) >= submenu[num][subnum]['cache']))
					{
						refreshpage(num,subnum);
					}
					else
					{
						contentdiv.innerHTML = submenu[num][subnum]["text"];
					}
					redrawcontentinfo(num,subnum);
						
					/*<p><a href="#" onclick="refreshpage(currentnum,currentsubnum);return false;">Refresh</a></p>*/
				}
			}

			function redrawcontentinfo(num,subnum)
			{
				var infodiv = document.getElementById('content_info');

				if (infodiv != null && infodiv.innerHTML != null)
				{
					var Days = new Array('Sunday','Monday','Tuesday','Wednesday',
	'Thursday','Friday','Saturday');
					var Months = new Array('Jan','Feb','Mar','Apr',
	'May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
					var now = new Date();
					var off = (now.getTimezoneOffset()-60)*60;

					var date_modified = new Date(1970, 0, 1, 0, 0,submenu[num][subnum]['date_modified'] - off);

					var date_fetched = new Date(1970, 0, 1, 0, 0,submenu[num][subnum]['date_fetched'] - off);

					var page_age = pageage(num,subnum);

					var text = "Last modified: ";
					
					if (date_modified.getDate() == now.getDate() && date_modified.getMonth() == now.getMonth() && date_modified.getFullYear() == now.getFullYear())
						text += "Today";
					else if (date_modified.getFullYear() == now.getFullYear() && date_modified.getDate() == now.getDate()-1 && date_modified.getMonth() == now.getMonth())
						text += "Yesterday";
					else
						text += Months[date_modified.getMonth()]+" "+date_modified.getDate()+" "+date_modified.getFullYear();
					//+" "+leadingZero(date_modified.getHours())+":"+leadingZero(date_modified.getMinutes());
					
					if (submenu[num][subnum]['rank'] > 1)
						text += ' (<a href="#" onclick="loadeditor();return false;">Edit</a>)';

					text += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";

					text += "Last refreshed: ";
					if (date_fetched.getDay() == now.getDay())
						text += "Today";
					else
						text += Days[date_fetched.getDay()];
					text +=" "+leadingZero(date_fetched.getHours())+":"+leadingZero(date_fetched.getMinutes());

					/*text += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";

					text += 'Page is '+page_age+' minute';
						if (page_age != 1)
							text += 's';
						text += ' old';*/

					text += ' (<a href="#" onclick="refreshpage('+num+','+subnum+');return false;">Refresh page</a>)';
					if (user['username'] != "guest")
					{
						text += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
						text += "<input type=checkbox ";
						if (realtime)
							text += "checked=checked ";
						text += "onchange=\"";
						text += "if(this.checked) realtime=true;else realtime=false;";
						text += "\">Realtime";
					}
					
					infodiv.innerHTML = text;
				}
			}

			//Redraw user information
			function redrawuser()
			{
				var userdiv = document.getElementById('user');
				if (userdiv != null && userdiv.innerHTML != null)
				{
					if (user.username != "guest")
						userdiv.innerHTML = "Welcome "+user.first_name+"!";
					else
						userdiv.innerHTML = " ";
				}
			}
	
			function pageage(num,subnum)
			{	
				var now = new Date();
				var off = (now.getTimezoneOffset()-60)*60;

				var date_fetched = new Date(1970, 0, 1, 0, 0,submenu[num][subnum]['date_fetched'] - off);
				var page_age = Math.ceil((now.getTime()-date_fetched.getTime())/(1000*60));
				return page_age;
			}

			function getById(element_id) {
				return document.getElementById(element_id);
			}

			function getByName(element_name) {
				test = document.getElementsByName(element_name);
				return test[0];
			}
			
			function replaceHTML(target, html) {
				target.innerHTML = html;
			}
			
			function appendHTML(target, html) {
				target.innerHTML += html;
			}
			
			function prependHTML(target, html) {
				target.innerHTML = html += target.innerHTML;
			}
			
			function clearHTML(target) {
				target.innerHTML = "";
			}
			
			function parseReturn(text) {
				return eval('('+text+')');
			}


		/*
		 * Functions called after data return - not to be called manually
		 */

			//Called to update a page - not called manually - but by db_getpage
			function updatepage(num,subnum,newdata)
			{
				submenu[num][subnum] = newdata;
				
				checklink(num,subnum);
				
				if (currentnum == num && currentsubnum == subnum)
					loadpage(num,subnum,true);
			}

			//Called to update a whole section - not called manually - but by db_getpages
			function updatepages(num,newdata)
			{
				submenu[num] = newdata;
				
				for (subnum=1;subnum<=submenu[num].length;++subnum)
				{
					checklink(num,subnum);
				}
				
				if (currentnum == num)
				{
					redrawsubmenu(num);
					redrawtail(null,currentsubnum);
					loadpage(currentnum,currentsubnum,true);
				}
			}

			//Called to update all sections - not called manually - but by db_getallpages
			function updateallpages(newdata)
			{
				submenu = newdata;
				i = 0;
				for (num=1;num<=submenu.length-1;++num)
				{
					for (subnum=1;subnum<=submenu[num].length-1;++subnum)
					{
						checklink(num,subnum);
					}
				}
				
				redrawsubmenu(currentnum);
				redrawtail(currentnum,currentsubnum);
				loadpage(currentnum,currentsubnum,true);
			}
			
			function checklink(num,subnum)
			{
				if (submenu[num][subnum]['link'])
				{
					submenu[num][subnum]['text'] = "<iframe src=\""+datahome+"pages/"+submenu[num][subnum]['link']+"\" id=\"contentframe\" width=600px height=395px style=\"border:0px;\"></iframe>";
				}
			}

			//Called to update user information - not called manually - but by db_login
			function updateuser(newdata,dontrefresh)
			{
				var olduser = user;
				user = newdata;
				if(user.authentication == "mtuiso")
				{
					datahome = "login/";
				}
				else
				{
					datahome = "";
				}
				
				if (olduser.id != user.id)
				{
					redrawuser();
					
					if (!dontrefresh)
						refreshallpages();
				}
			}


		/*
		 * Data calling functions - not to be called directly from page - use top group of functions
		 */
			
//Not called manually, but through refreshpage()
function db_getpage(num,subnum) {
	getpageClient = new HTTPClient();
	getpageClient.init(datahome+"database/return_page.php");
	var post = "page_id="+submenu[num][subnum]['id'];

	getpageClient.post(post);
	getpageClient.asyncGET({
		onInit: function(){
			status.set("Loading page..",statusBusy);
		},
		onError: function(statusNum,statusText){
			status.set("I/O Error "+statusNum+": "+statusText,statusError);
			if (statusNum == 302)
			{
				datahome = "";
				checklogin();
			}
		},
		onProgress: function(){
			status.append('..');
		},
		onLoad:function(result){
			if (result == 1)
			{
				status.set("Incorrect paramters.",statusError);
			}
			else if (result == 2)
			{
				status.set("Access Denied.",statusError);
			}
			else if (result == 3)
			{
				status.set("Page not found.",statusError);
			}
			else
			{
				status.reset();
				updatepage(this.pointer,this.pointer2,parseReturn(result));
			}
		}
	},num,subnum);
}

//Not called manually, but through refreshpages()
function db_getpages(num) {
	getpagesClient = new HTTPClient();
	getpagesClient.init(datahome+"database/return_pages.php");
	var post = "parent_id="+menu[num]['id'];

	getpagesClient.post(post);
	getpagesClient.asyncGET({
		onInit: function(){
			status.set("Loading pages..",statusBusy);
		},
		onError: function(statusNum,statusText){
			status.set("I/O Error "+statusNum+": "+statusText,statusError);
			if (statusNum == 302)
			{
				datahome = "";
				checklogin();
			}
		},
		onProgress: function(){
			status.append('..');
		},
		onLoad:function(result){
			if (result == 1)
			{
				status.set("Incorrect paramters.",statusError);
			}
			else if (result == 2)
			{
				status.set("Access Denied.",statusError);
			}
			else if (result == 3)
			{
				status.set("Pages not found.",statusError);
			}
			else
			{
				status.reset();
				updatepages(this.pointer,parseReturn(result));
			}
		}
	},num);
}

//Not called manually, but through refreshallpages()
function db_getallpages() {
	getallpagesClient = new HTTPClient();
	getallpagesClient.init(datahome+"database/return_allpages.php");
	var post = "nothing=nothing";
	
	getallpagesClient.post(post);
	getallpagesClient.asyncGET({
		onInit: function(){
			status.set("Loading pages..",statusBusy);
		},
		onError: function(statusNum,statusText){
			status.set("I/O Error "+statusNum+": "+statusText,statusError);
			if (statusNum == 302)
			{
				datahome = "";
				checklogin();
			}
		},
		onProgress: function(){
			status.append('..');
		},
		onLoad:function(result){
			if (result == 1)
			{
				status.set("Incorrect paramters.",statusError);
			}
			else if (result == 2)
			{
				status.set("Access Denied.",statusError);
			}
			else if (result == 3)
			{
				status.set("Pages not found.",statusError);
			}
			else
			{
				status.reset();
				updateallpages(parseReturn(result));
			}
		}
	});
}

//Not called manually, but through submiteditor()
function db_updatepage(num,subnum,newcontent) {
	getpageClient = new HTTPClient();
	getpageClient.init(datahome+"database/update_page.php");
	var post = "page_id="+submenu[num][subnum]['id']+"&text="+escape(newcontent);

	getpageClient.post(post);
	getpageClient.asyncGET({
		onInit: function(){
			status.set("Saving changes..",statusBusy);
		},
		onError: function(statusNum,statusText){
			status.set("I/O Error "+statusNum+": "+statusText,statusError);
			if (statusNum == 302)
			{
				datahome = "";
				checklogin();
			}
		},
		onProgress: function(){
			status.append('..');
		},
		onLoad:function(result){
			if (result == 1)
			{
				status.set("Incorrect paramters.",statusError);
				//loadpage(this.pointer,this.pointer2);
			}
			else if (result == 2)
			{
				status.set("Access Denied.",statusError);
				//loadpage(this.pointer,this.pointer2);
			}
			else if (result == 3)
			{
				status.set("Page not found.",statusError);
				//loadpage(this.pointer,this.pointer2);
			}
			else if (result == 4)
			{
				status.set("Unknown error.",statusError);
				//loadpage(this.pointer,this.pointer2);
			}
			else
			{
				status.reset("Page saved.");
				updatepage(this.pointer,this.pointer2,parseReturn(result));
			}
		}
	},num,subnum);
}

//Not called manually, but through login()
function db_sendlogin(username,password) {
	loginClient = new HTTPClient();
	loginClient.init(datahome+"database/check_login.php");
	if (username && password)
		var post = "username="+username+"&password="+password;
	else
		var post = "";

	loginClient.post(post);
	loginClient.asyncGET({
		onInit: function(){
			status.set("Checking login..",statusBusy);
		},
		onError: function(statusNum,statusText){
			status.set("I/O Error "+statusNum+": "+statusText,statusError);
			if (statusNum == 302)
			{
				datahome = "";
				checklogin();
				loadpage(4,2);
			}
		},
		onProgress: function(){
			status.append('..');
		},
		onLoad:function(result){
			if (result == 1)
			{
				status.set("Incorrect paramters.",statusError);
			}
			else if (result == 2 || result == 3)
			{
				status.set("Access Denied.",statusError);
			}
			else
			{
				status.reset();
				updateuser(parseReturn(result));
			}
		}
	});

}

//Not called manually, but through logout()
function db_logout() {
	loginClient = new HTTPClient();
	loginClient.init(datahome+"database/logout.php");
	var post = "";

	loginClient.post(post);
	loginClient.asyncGET({
		onInit: function(){
			status.set("Logging out..",statusBusy);
		},
		onError: function(statusNum,statusText){
			status.set("I/O Error "+statusNum+": "+statusText,statusError);
			if (statusNum == 302)
			{
				datahome = "";
				checklogin();
			}
		},
		onProgress: function(){
			status.append('..');
		},
		onLoad:function(result){
			if (result == 1 || result == 2 || result == 3)
			{
				status.set("Unknown Error.",statusError);
			} else
			{
				status.reset();
				updateuser(parseReturn(result));
				refreshallpages();
			}
		}
	});

}


	/*
	 * Status class and object
	 */

var statusReady = 1;
var statusBusy = 2;
var statusError = 3;
var status = new function() {
	this.text = '';
	this.extended = '';

	/**
	 * Determines type of status
	 * 1 => statusReady - green
	 * 2 => statusBusy - yellow
	 * 3 => statusError - red
	 */
	this.type = 1;
	this.defaultType = 1;

	this.typeColor = function() {
		switch(this.type) {
			case 1:
				return 'green';
			break;
			case 2:
				return 'yellow';
			break;
			case 3:
				return 'red';
			break;
			default:
				return 'black';
		}
	};

	this.set = function(str,num) {
		this.text = str;
		this.type = (num) ? num : this.defaultType;
		this.update();
	};
	this.append = function(str) {
		this.text += str;
		this.update();
	};
	this.reset = function(str) {
		if (str) {
			change = str;
		} else {
			change = "Page loaded.";
		}
		this.set(change,this.defaultType);
	};
	this.update = function() {
		replaceHTML(getById('status'),"<div style=\"position:absolute;color:white;background-color:"+this.typeColor()+";width:14px;height:14px\"></div><div style=\"position:relative;left:16px\">"+this.text+"</div>");
	};
}



/*
 * 3rd party functions
 */


function leadingZero(nr)
{
	if (nr < 10) nr = "0" + nr;
	return nr;
}



// ******************** //
// HTTP Client function //
// ******************** //

function HTTPClient() {};// Constructor for generic HTTP client

HTTPClient.prototype = {// Add methods and properties as array
	url: null,
	xmlhttp: null,// Instance of XMLHttpRequest
	callinprogress: false,// Used to make sure multiple calls are not placed with the same client object while another in progress
	userhandler: null,// The user defined handler - see MyHandler below
	postdata : null,
	method : null,
	
	init: function(url) {
		this.url = url;
		this.method = "GET";
		//alert(typeof XMLHttpRequest);
		this.xmlhttp = (typeof XMLHttpRequest != "object" && typeof XMLHttpRequest != "function") ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
	},
	
	post: function(post) {
		this.postdata = post;
		this.method = "POST";
		//this.xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	},

	asyncGET: function (handler,pointer,pointer2) {// handler argument is a user defined object to be called
		if (this.callinprogress) {// Prevent multiple calls
			throw "Call in progress";
		};

		this.userhandler = handler;
		this.userhandler.pointer = pointer;
		this.userhandler.pointer2 = pointer2;
		
		this.xmlhttp.open(this.method,this.url,true);// Open an async request - third argument makes it async
		if (this.method == "POST")
			this.xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		var self = this;// Have to assign "this" to a variable - not sure why can't use directly
		this.xmlhttp.onreadystatechange = function() {// Assign a closure to the onreadystatechange callback
			self.stateChangeCallback(self);
		}

		this.xmlhttp.send(this.postdata);// Send the request
	},

	stateChangeCallback: function(client) {
		switch (client.xmlhttp.readyState) {
			case 1:// Request not yet made
				try {
					client.userhandler.onInit();
				} catch (e) { /* Handler method not defined */ }
				break;
			case 2:// Contact established with server but nothing downloaded yet
				try {

					if ( client.xmlhttp.status != 200 ) {// Check for HTTP status 200
						client.userhandler.onError(
							client.xmlhttp.status,
							client.xmlhttp.statusText
							);
						client.xmlhttp.abort();// Abort the request
						client.callinprogress = false;// Call no longer in progress
					}
				} catch (e) {
					/* Handler method not defined */
				}
				break;
			case 3:// Called multiple while downloading in progress
				try {// Notify user handler of download progress
					try {// Get the total content length -useful to work out how much has been downloaded
						var contentLength = 
							client.xmlhttp.getResponseHeader("Content-Length");
					} catch (e) {
						var contentLength = NaN;
					}
					client.userhandler.onProgress(// Call the progress handler with what we've got
      						//Commented out due to IE incompatibility
						//client.xmlhttp.responseText,
						//contentLength
					);

				} catch (e) { /* Handler method not defined */ }
				break;
			case 4:// Download complete
				try {
					if (client.xmlhttp.status == 200)
						client.userhandler.onLoad(client.xmlhttp.responseText);
				} catch (e) {
					/* Handler method not defined */
				} finally {
				//	client.xmlhttp.callinprogress = false;// Call no longer in progress
					client.callinprogress = false;
				}
				break;
		}
	}
}


/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Copyright (C) Paul Johnston 1999 - 2000.
 * Updated by Greg Holt 2000 - 2001.
 * See http://pajhome.org.uk/site/legal.html for details.
 */

/*
 * Call function
 */
function md5(str)
{
	return calcMD5(str);
}

/*
 * Convert a 32-bit number to a hex string with ls-byte first
 */
var hex_chr = "0123456789abcdef";
function rhex(num)
{
  str = "";
  for(j = 0; j <= 3; j++)
    str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) +
           hex_chr.charAt((num >> (j * 8)) & 0x0F);
  return str;
}

/*
 * Convert a string to a sequence of 16-word blocks, stored as an array.
 * Append padding bits and the length, as described in the MD5 standard.
 */
function str2blks_MD5(str)
{
  nblk = ((str.length + 8) >> 6) + 1;
  blks = new Array(nblk * 16);
  for(i = 0; i < nblk * 16; i++) blks[i] = 0;
  for(i = 0; i < str.length; i++)
    blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8);
  blks[i >> 2] |= 0x80 << ((i % 4) * 8);
  blks[nblk * 16 - 2] = str.length * 8;
  return blks;
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally 
 * to work around bugs in some JS interpreters.
 */
function add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left
 */
function rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * These functions implement the basic operation for each round of the
 * algorithm.
 */
function cmn(q, a, b, x, s, t)
{
  return add(rol(add(add(a, q), add(x, t)), s), b);
}
function ff(a, b, c, d, x, s, t)
{
  return cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function gg(a, b, c, d, x, s, t)
{
  return cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function hh(a, b, c, d, x, s, t)
{
  return cmn(b ^ c ^ d, a, b, x, s, t);
}
function ii(a, b, c, d, x, s, t)
{
  return cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
 * Take a string and return the hex representation of its MD5.
 */
function calcMD5(str)
{
  x = str2blks_MD5(str);
  a =  1732584193;
  b = -271733879;
  c = -1732584194;
  d =  271733878;

  for(i = 0; i < x.length; i += 16)
  {
    olda = a;
    oldb = b;
    oldc = c;
    oldd = d;

    a = ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = ff(c, d, a, b, x[i+10], 17, -42063);
    b = ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = ff(d, a, b, c, x[i+13], 12, -40341101);
    c = ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = ff(b, c, d, a, x[i+15], 22,  1236535329);    

    a = gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = gg(c, d, a, b, x[i+11], 14,  643717713);
    b = gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = gg(c, d, a, b, x[i+15], 14, -660478335);
    b = gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = gg(b, c, d, a, x[i+12], 20, -1926607734);
    
    a = hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = hh(b, c, d, a, x[i+14], 23, -35309556);
    a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = hh(d, a, b, c, x[i+ 0], 11, -358537222);
    c = hh(c, d, a, b, x[i+ 3], 16, -722521979);
    b = hh(b, c, d, a, x[i+ 6], 23,  76029189);
    a = hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = hh(d, a, b, c, x[i+12], 11, -421815835);
    c = hh(c, d, a, b, x[i+15], 16,  530742520);
    b = hh(b, c, d, a, x[i+ 2], 23, -995338651);

    a = ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = ii(c, d, a, b, x[i+10], 15, -1051523);
    b = ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = ii(d, a, b, c, x[i+15], 10, -30611744);
    c = ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = ii(b, c, d, a, x[i+ 9], 21, -343485551);

    a = add(a, olda);
    b = add(b, oldb);
    c = add(c, oldc);
    d = add(d, oldd);
  }
  return rhex(a) + rhex(b) + rhex(c) + rhex(d);
}


// www.sean.co.uk

function pause(millis)
{
var date = new Date();
var curDate = null;

do { curDate = new Date(); }
while(curDate-date < millis);
}
