	//// BEGIN HEADER ////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////////////////////
	////                                                                                          ////
	////    CLIENTAPP CLASS.                                                                      ////
	////                                                                                          ////
	////    Copyright 2007, Jose Cao-Garcia                                                       ////
	////                                                                                          ////
	////    This software is licensed under the Creative Commons                                  ////
	////    Attribution-ShareAlike 2.5 License:                                                   ////
	////    <http://creativecommons.org/licenses/by-sa/2.5/legalcode>                             ////
	////                                                                                          ////
	////    HELP/INFO/DEVELOPER CONTACT: jose@jcao.com, http://jcao.com                           ////
	////                                                                                          ////
	////    FOR MORE INFO VISIT: http://jcao.com/scripts/clientApp/                               ////
	////                                                                                          ////
	//////////////////////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////////////////////
	////                                                                                          ////
	////    CLIENTAPP is a browser-identification utility that can reliably determine what        ////
	////    rendering engine, browser, browser version, and operating system a web page is        ////
	////    running in. CLIENTAPP can also tell whether or not a browser has basic DOM and        ////
	////    XMLHTTP support. Finally, clientApp will tell you what the user's screen resolution   ////
	////    is, and what the browsers viewport dimensions are.                                    ////
	////                                                                                          ////
	////    Wherever possible, this class defers to object based detection over user-agent        ////
	////    string examination: rendering engine tests are all object based tests and more        ////
	////    reliable than browser tests. version tests (engine and browser) rely on the user      ////
	////    agent string by necessity, so the rendering engine test is most trustworthy. This     ////
	////    script differentiantes between engine and browser due to the new-ish phenomenon of    ////
	////    multiple browsers relying on the same engine (for example, omniweb, shiira,           ////
	////    and safari all relying on webkit, or netscape 8 which can use gecko or msie           ////
	////    interchangably.                                                                       ////
	////                                                                                          ////
	////    I don't recommend using this script as a javascript code-forking utility, I use       ////
	////    it to assign special styling to browsers in order to compensate for their             ////
	////    special rendering quirks. Never use browser detection to perform the job of           ////
	////    object detection if you can avoid it.                                                 ////
	////                                                                                          ////
	////                                                                                          ////
	////    currently, no documentation exists for this script. I will eventually post            ////
	////    documentation for this script (or a more mature release) at my website, along         ////
	////    with other scripts.                                                                   ////
	////                                                                                          ////
	//////////////////////////////////////////////////////////////////////////////////////////////////
	//// END HEADER //////////////////////////////////////////////////////////////////////////////////


	//// OBJECT CLASS FOR GETTING INFORMATION ABOUT THE USERS BROWSER/ENVIRONMENT
		function clientApp() {
			var root   = this;
		// get the usual browser identification strings, make the lowercase, and easier to parse.
			root.agent      = navigator.userAgent.toLowerCase();
			root.name       = navigator.appName.toLowerCase();
			root.version    = navigator.appVersion.toLowerCase();
			root.system     = navigator.platform.toLowerCase();
			root.engine     = false;
		// test for various operating strings via non object-based test
			if (root.system == 'macppc' || root.system == 'macintel')     { root.os = 'mac';        }
			else if (root.system == 'iphone')                             { root.os = 'iphone';     }
			else if (root.system == 'win32'  || root.system == 'win64')   { root.os = 'win';        }
			else if (root.agent.indexOf(' linux') != -1)                  { root.os = 'linux';      }
			else if (!root.os && root.system)                             { root.os = root.system;  }
			else if (!root.os)                                            { root.os = false;        }
		// test for rendering engine (not browser) via object-based test
			try { if (navigator.product.toLowerCase() == 'gecko')         { root.engine = 'gecko';  }} catch (e) {}
			try { if ((document.childNodes) && (!navigator.taintEnabled)) { root.engine = 'webkit'; }} catch (e) {}
			try { if (typeof(window.opera) == 'object')                   { root.engine = 'opera';  }} catch (e) {}
			try { if (typeof(window.ScriptEngine) == 'function')          { root.engine = 'icab';   }} catch (e) {}
			try { if (typeof(ActiveXObject) == 'function')                { root.engine = 'msie';   }} catch (e) {}
			if (!root.engine)                                             { root.engine = false;    }
		// get rendering engine revision # agent-based tests, by specific rendering engine
			switch(root.engine){
				case 'webkit' :
					try {
						root.engRev = root.agent.split(' applewebkit/')[1].split(' (khtml')[0];
					} catch (e) {
						root.engRev = root.agent.split(' khtml/')[1].split(' (like ')[0];
					}
				break;
				case 'gecko'  : root.engRev = root.agent.split(') gecko/')[1].split(' ')[0];                     break;
				case 'opera'  : root.engRev = root.agent.split('opera')[1].substring(1).split(' (')[0];          break;
				case 'icab'   : root.engRev = root.engRev = root.agent.split(' icab ')[1].split(';')[0];         break;
				case 'msie'   : root.engRev = root.engRev = root.agent.split(' msie ')[1].split(';')[0];         break;
				case false    : root.engRev = false;                                                             break;
			}
		// test for browsers via non object-based test
		// mozilla must come first, because 'mozilla/' appears in many other more specific gecko browsers
			if (root.agent.indexOf('mozilla/') != -1)                     { root.app = 'mozilla';    }
			if (root.engine == 'msie')                                    { root.app = 'msie';       }
			if (root.agent.indexOf(' netscape6/') != -1)                  { root.app = 'netscape';   }
			if (root.agent.indexOf(' netscape/') != -1)                   { root.app = 'netscape';   }
			if (root.agent.indexOf(' safari/') != -1)                     { root.app = 'safari';     }
			if (root.agent.indexOf('safari) omniweb/') != -1)             { root.app = 'omniweb';    }
			if (root.agent.indexOf(' gecko) shiira') != -1)               { root.app = 'shiira';     }
			if (root.agent.indexOf(' camino/') != -1)                     { root.app = 'camino';     }
			if (root.agent.indexOf('konqueror/') != -1)                   { root.app = 'konqueror';  }
			if (root.agent.indexOf(' firefox/') != -1)                    { root.app = 'firefox';    }
			if (root.agent.indexOf(' epiphany/') != -1)                   { root.app = 'epiphany';   }
			if (root.agent.indexOf('opera') != -1)                        { root.app = 'opera';      }
			if (root.engine == 'icab')                                    { root.app = 'icab';       }
			if (!root.app)                                                { root.app = false;        }
		// then get browser version, using the best method for this browser.
			switch(root.app){
				case 'mozilla'   : root.appRev = root.agent.split(' rv:')[1].split(') ')[0];                     break;
				case 'msie'      : root.appRev = root.engRev = root.agent.split(' msie ')[1].split(';')[0];      break;
				case 'safari'    : root.appRev = root.agent.split(' safari/')[1];                                break;
				case 'omniweb'   : root.appRev = root.agent.split('omniweb/v')[1];                               break;
				case 'shiira'    : root.appRev = root.agent.split('shiira/')[1].split(' safari')[0];             break;
				case 'camino'    : root.appRev = root.agent.split('camino/')[1];                                 break;
			// netscape's user agent strings are a mess ...
				case 'netscape'  : root.appRev = (root.agent.indexOf(' netscape6/') != -1) ? root.agent.split(' netscape6/')[1] : root.agent.split(' netscape/')[1]; break;
				case 'navigator' : root.appRev = root.agent.split('ozilla/')[1].split(' ')[0].split('-')[0];     break;
				case 'konqueror' : root.appRev = root.agent.split(' konqueror/')[1].split(';')[0].split('-')[0]; break;
				case 'firefox'   : root.appRev = root.agent.split(' firefox/')[1];                               break;
				case 'epiphany'  : root.appRev = root.agent.split(' epiphany/')[1].split(' ')[0];                break;
				case 'opera'     : root.appRev = root.engRev;                                                    break;
				case 'icab'      : root.appRev = root.engRev = root.agent.split(' icab ')[1].split(';')[0];      break;
				case false       : root.appRev =  false;                                                         break;
			}
		// some object-based tests for basic DOM support
			root.supportsDom = !!(
				typeof(document.getElementById)                  != 'undefined' &&
				typeof(document.getElementsByTagName)            != 'undefined' &&
				typeof(document.nextSibling)                     != 'undefined' &&
				typeof(document.previousSibling)                 != 'undefined' &&
				typeof(document.childNodes)                      != 'undefined' &&
				typeof(document.parentNode)                      != 'undefined' &&
				typeof(document.hasChildNodes)                   != 'undefined' &&
				typeof(document.nodeType)                        != 'undefined' &&
				typeof(document.cloneNode)                       != 'undefined' &&
				typeof(document.insertBefore)                    != 'undefined' &&
				typeof(document.appendChild)                     != 'undefined' &&
				typeof(document.removeChild)                     != 'undefined' &&
				typeof(document.replaceChild)                    != 'undefined' &&
				typeof(document.documentElement.tagName)         != 'undefined' &&
				typeof(document.documentElement.getAttribute)    != 'undefined' &&
				typeof(document.documentElement.removeAttribute) != 'undefined' &&
				typeof(document.documentElement.attributes)      != 'undefined'
			);
		// some object-based tests for xmlHttp support
			root.xmlhttp = false;
			if (typeof(ActiveXObject) != 'function' && typeof(XMLHttpRequest) != 'undefined') {
				root.xmlhttp = true;
			} else {
				try {
					var tempXmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
					root.xmlhttp = true;
				} catch (e) {
					try {
						xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
						root.xmlhttp = true;
					} catch (e) {}
				}
			}
		// get the viewport dimensions
			root.viewport = function(windowName) {
				var dimensions = false;
				if (!windowName) { windowName = window.self; }
				try {
					if (self.innerHeight) {
						dimensions = [windowName.innerWidth, windowName.innerHeight];
					} else if (document.body) {
						dimensions = [windowName.document.body.clientWidth, windowName.document.body.clientHeight];
					} else if (document.documentElement && document.documentElement.clientHeight) {
						dimensions = [windowName.document.documentElement.clientWidth, windowName.document.documentElement.clientHeight];
					}
				} catch (e) { }
				return dimensions;
			}
		// get the window position
			root.position = function(windowName) {
				var coordinates = false;
				if (!windowName) { windowName = window.self; }
				if (window.screenLeft) {
					coordinates = [windowName.screenLeft, windowName.screenTop];
				} else if (window.screenX) {
					coordinates = [windowName.screenX, windowName.screenY];
				}
				return coordinates;
			}
		// screen resolution (total, including menubar/taskbar etc...)
			root.screenX       = screen.width;
			root.screenY       = screen.height;
		// screen resolution (total available for use)
			root.screenAvailY  = screen.availWidth;
			root.screenAvailX  = screen.availHeight;
		}
	//// INITIALIZE THE clientApp OBJECT
		var client = new clientApp();




	//// HANDLES COOKIES
	function cookieHandler() {
		var root = this;
		root.values = new Array();
	// populate the values array
		root.getValues = function() {
			var cookieString   = document.cookie;
			root.values.length = 0;
			if (cookieString) {
			// loop through pairs and assign them in the array
				for (var loop = 0; loop < cookieString.split('; ').length; loop++) {
					var thisPair = cookieString.split('; ')[loop];
					var thisName = unescape(thisPair.split('=')[0]);
					var thisValue = unescape(thisPair.split('=')[1]);
					root.values[thisName] = thisValue;
				}
			}
		}
	// write a cookie, for 'expires' pass it a date (Month Day, 4-digit-year), or a number (days until expiration).
		root.write = function(cookieName, cookieValue, expires, path) {
			cookieValue   = cookieName + '=' + escape(cookieValue);
			var cookieExpires = '';
			// date handling could be much more precise but this will do for now  ...
			if (expires) {
				// Assume expires is a Date
				var cookieDate  = new Date(expires).toGMTString();
				// if date is invalid, assume expires is a number, representing days to expire in
				if (expires) {
					var futureDate = new Date();
					futureDate.setTime(futureDate.getTime()+(expires*24*60*60*1000));
					cookieDate = futureDate.toGMTString();
				}
				cookieValue+= ';expires=' + cookieDate;
			}
			if (path) { cookieValue+= path; }
			document.cookie = cookieValue;
			root.getValues();
		}
	// expire a cookie
		root.expire = function(cookieName) { root.write(cookieName, 'false', 'April 1, 1976'); }
	// get a cookie value
		root.get = function(getValue) {
			root.getValues();
			return (root.values[getValue]) ? root.values[getValue] : false;
		}
		root.getValues();
	}

	var cookie = new cookieHandler();