// -------------------- constants --------------------------

var CONTENT_TYPE = "Content-Type";
var APP_JSON = "application/json";
var HTTP_VERB = "POST";


// ------------------------------------ util -------------------------------------

var $g = function(i) { return document.getElementById(i); };

// removes any markup from supplied string, leaving just the original text
function StripMarkup(s)
{
	var temp = s;
	var rx = /\<[^\>]*\>/;
	var li = temp.match(rx)

	while ( li && li.length > 0 )
	{
		temp = temp.replace(rx, "" );
		li = temp.match(rx);
	}
	return temp;
}

function getreq()
{
	var r;
	if ( window.XMLHttpRequest )
	{
		r = new window.XMLHttpRequest();
	}
	else if ( window.ActiveXObject )
	{
		r = new ActiveXObject("Msxml2.XMLHTTP");
		if (!r)
			r = new ActiveXObject("Microsoft.XMLHTTP");
	}
	return r;
}

// find current value in menu and sets it as selected
function set_selected_value(menu,itemvalue)
{
	var m = ( typeof menu == "object" ) ? menu : document.getElementById(menu);
	if (m)
	{
		var trimmeditemvalue = itemvalue.trim();
		
		for ( x = 0; x < m.options.length; x++ )
		{
			if (m && m.options[x].value == trimmeditemvalue)
			{
				m.selectedIndex = x;
				break;
			}
		}
	}
}


function get_menu_val(menu)
{
	var m = ( typeof menu == "object" ) ? menu : document.getElementById(menu);
	var retval = "";
	try
	{
		retval = m ? m.options[m.selectedIndex].value : "";
	}
	catch (err)
	{
		if (m.selectedIndex < 0 || m.options.length > 0)
			retval = m.options[0].value;
	}
	return retval;
}


// returns an array of {K,V} objects
function getObjectChildrenInput(oParent)
{
	var retval = [];
	var count = 0;
	var p = ( typeof oParent == "object" ) ? oParent : document.getElementById(oParent);
	if (p)
	{
		var inputs = p.getElementsByTagName("input");
		for(var x=0; x < inputs.length; x++ )
		{
			var tObj = inputs[x];
			var arrobj = { };
			arrobj.K = typeof tObj.name == "string" ? tObj.name : tObj.id;
			if (tObj.type == "text" || tObj.type == "password" || tObj.type == "hidden")
				arrobj.V = tObj.value;
			else if (tObj.type == "checkbox")
				arrobj.V = (tObj.checked ? "true" : "false");
			else if (tObj.type == "radio" && tObj.checked)
				arrobj.V = tObj.value;
			else
				arrobj = null;
			if (arrobj)
				retval[count++] = arrobj;
		}
		var inputs = p.getElementsByTagName("select");
		for(var x=0; x < inputs.length; x++ )
		{
			var tObj = inputs[x];
			var arrobj = { };
			arrobj.K = typeof tObj.name == "string" ? tObj.name : tObj.id;
			arrobj.V = get_menu_val(tObj);
			retval[count++] = arrobj;						
		}
		var inputs = p.getElementsByTagName("textarea");
		for (var x = 0; x < inputs.length; x++)
		{
			var tObj = inputs[x];
			var arrobj = {};
			arrobj.K = typeof tObj.name == "string" ? tObj.name : tObj.id;
			arrobj.V = tObj.value;
			retval[count++] = arrobj;
		}
	}
	return retval;
}


// parses supplied text into an XML document
function text2XMLDoc(t)
{
	var doc;
	if (window.ActiveXObject)
	{
	  doc=new ActiveXObject("Microsoft.XMLDOM");
	  doc.async="false";
	  doc.loadXML(t);
	}
	else
	{
	  var parser=new DOMParser();
	  doc=parser.parseFromString(t,"text/xml");
	}
	return doc;
}

function getstringreturn2(tx)
{
	try
	{
		var xmlobj = text2XMLDoc(tx);
		return xmlobj.documentElement.firstChild.nodeValue;
	}
	catch(err)
	{
		alert(err.description);
		return "(error)";
	}
}

function getstringreturn(tx)
{
	try
	{
		var start_point = tx.indexOf("<string");
		var end_point = tx.indexOf("</string>") - 1;
		if ( end_point > 0 )
		{
			start_point = tx.indexOf(">",start_point) + 1;
			return tx.substring(start_point,end_point+1);
		}
		else
			return ""; // in case it was something like <string />, i.e. empty string
	}
	catch(err)
	{
		return "(error)";
	}
}

// Add a trim method to all strings:
// i.e. " string surrounded with whitespace   ".trim() would return "string surrounded with whitespace";

String.prototype.trim = function(){
	var rx = new RegExp("(\\S+(\\s+\\S+)*)", "g");
	try
	{
		var rxo = rx.exec(this);
		return rxo[0];
	}
	catch(err) { return ""; }
};


// typically you'll pass a text box intended to be a dollar amount in the onblur event. It updates the
// contents of the text box. It also returns the value as a float.
function format_amt(inputitem)
{
	try
	{
		var t = typeof (inputitem);
		var amt = parseFloat(inputitem.value.replace(/[^0-9\.]/g, ""));
		if (isNaN(amt))
			amt = 0;
		amt = Math.round(amt * 100);
		var cents = amt % 100;
		amt -= cents;
		if (!cents)
			cents = ".00";
		else if (cents < 10)
			cents = ".0" + cents;
		else
			cents = "." + cents;
		inputitem.value = "$ " + Math.round(amt / 100) + cents;
	}
	catch (err)
	{
		inputitem.value = "";
	}
	return parseFloat("" + (amt) / 100 + cents);
}



// ---------------------------- cookie reading/writing/deleting ------------------000000000

function createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') 
			c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) 
			return c.substring(nameEQ.length,c.length);
	}
	return null;
}

function eraseCookie(name) {
	createCookie(name,"",-1);
}

function readCartCookie()
{
	var retval = null;
	var cartCookie = readCookie("CART_SUMMARY");
	if (cartCookie != null)
	{
		retval = JSON.parse(Base64.decode(cartCookie));
	}
	return retval;
}


// ----------------------------- Keyboard filtering -----------------------------------
// Add: onfocus="document.onkeydown=numbersOnly;" onblur="document.onkeydown=null;"

function numbersOnly(e)
{
	if (!e)
		e=window.event;
	var k = (window.event ?  e.keyCode : e.which);
	return (((k>47) && (k<58)) || k==8 || k==45 || k==189 || k==109 || k== 9 || ((k>95) && (k<106))); // 0-9, bksp, hyphen(s), tab
}

function amountsOnly(e)
{
	if (!e)
		e=window.event;
	var k = (window.event ?  e.keyCode : e.which);
	if (k==16) return true;
	return (((k>47) && (k<58)) || k==8 || k==9 || k==46 || k==110 || k==188 || k==190 || ((k>95) && (k<106)) ); // 0-9, bksp, tab, decimalpoint
}

// Add: onfocus="onreturnkey='alert()';document.onkeydown=acceptReturn;" onblur="document.onkeydown=null;"
// use this on an input field that you want to accept a return or enter key as an
// equivalent of pressing a particular button

function acceptReturn(e)
{
	if (!e)
		e = window.event;
	var k = (window.event ? e.keyCode : e.which);
	if ((k == 10 || k == 13) && typeof onreturnkey != "undefined")
		setTimeout(onreturnkey, 50);
	return true; // 0-9, bksp, hyphen(s), tab
}
// -----------------------------------------------------------------------------------

// passed an HTML element as a parameter, returns an object 
// with 2 properties: absolute coordinates (x,y) of the element
function getxy(el) 
{
	var xy = {x:0,y:0};
	var element = (typeof el == "object") ? el : document.getElementById(el);
	while (element) {
		try
		{
			try
			{
				xy.x += element.offsetLeft;
				xy.y += element.offsetTop;
			}
			catch(e)
			{
				if (element.style && element.style.left)
				{
					xy.x += parseInt(element.style.left);
					xy.y += parseInt(element.style.top);
				}
			}
		}
		catch(err) { /* ignore, continue */ }
		element = element.offsetParent;
	}
	return xy;
}

function getwindowsize()
{
	var xy = { x:0, y:0 };
	if (self.innerHeight) // all except Explorer
	{
		xy.x = self.innerWidth;
		xy.y = self.innerHeight;
	}
	else if (document.documentElement && document.documentElement.clientHeight) // Explorer 6 Strict Mode
	{
		xy.x = document.documentElement.clientWidth;
		xy.y = document.documentElement.clientHeight;
	}
	else if (document.body) // other Explorers
	{
		xy.x = document.body.clientWidth;
		xy.y = document.body.clientHeight;
	}
	return xy;
}

function getscrolltop()
{
	var xy = { x:0, y:0 };
	if (self.pageYOffset) // all except Explorer
	{
		xy.x = self.pageXOffset;
		xy.y = self.pageYOffset;
	}
	else if (document.documentElement && document.documentElement.scrollTop) // Explorer 6 Strict
	{
		xy.x = document.documentElement.scrollLeft;
		xy.y = document.documentElement.scrollTop;
	}
	else if (document.body) // all other Explorers
	{
		xy.x = document.body.scrollLeft;
		xy.y = document.body.scrollTop;
	}
	return xy;
}

function getStyle(el, styleProp)
{
    var x = document.getElementById(el);
	if (x.currentStyle)
		var y = x.currentStyle[styleProp];
	else if (window.getComputedStyle)
		var y = document.defaultView.getComputedStyle(x,null).getPropertyValue(styleProp);
	return y;
}

// --------------- validation --------------------

function email_validation(em)
{
	return /^((?:\w+[^\w\s@]?)+)@((?:[^\.@\s]+\.)+[a-z]{2,}|(?:\d{1,3}\.){3}\d{1,3})$/.test(em)
}


// --------------- misc interface --------------------------------------------------

var Drag = {

	obj: null,
	OnDragStart: new Function(),
	OnDragEnd: new Function(),
	OnDrag: new Function(),

	init: function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
	{
		o.onmousedown = Drag.start;

		o.hmode = bSwapHorzRef ? false : true;
		o.vmode = bSwapVertRef ? false : true;

		o.root = oRoot && oRoot != null ? oRoot : o;

		if (o.hmode && isNaN(parseInt(o.root.style.left))) o.root.style.left = "0px";
		if (o.vmode && isNaN(parseInt(o.root.style.top))) o.root.style.top = "0px";
		if (!o.hmode && isNaN(parseInt(o.root.style.right))) o.root.style.right = "0px";
		if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";

		o.minX = typeof minX != 'undefined' ? minX : null;
		o.minY = typeof minY != 'undefined' ? minY : null;
		o.maxX = typeof maxX != 'undefined' ? maxX : null;
		o.maxY = typeof maxY != 'undefined' ? maxY : null;

		o.xMapper = fXMapper ? fXMapper : null;
		o.yMapper = fYMapper ? fYMapper : null;

		/*
		o.root.onDragStart = new Function();
		o.root.onDragEnd = new Function();
		o.root.onDrag = new Function();
		*/
		o.root.onDragStart = Drag.OnDragStart;
		o.root.onDragEnd = Drag.OnDragEnd;
		o.root.onDrag = Drag.OnDrag;
	},

	start: function(e)
	{
		var o = Drag.obj = this;
		e = Drag.fixE(e);
		var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom);
		var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right);
		o.root.onDragStart(x, y);

		o.lastMouseX = e.clientX;
		o.lastMouseY = e.clientY;

		if (o.hmode)
		{
			if (o.minX != null) o.minMouseX = e.clientX - x + o.minX;
			if (o.maxX != null) o.maxMouseX = o.minMouseX + o.maxX - o.minX;
		} else
		{
			if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
			if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
		}

		if (o.vmode)
		{
			if (o.minY != null) o.minMouseY = e.clientY - y + o.minY;
			if (o.maxY != null) o.maxMouseY = o.minMouseY + o.maxY - o.minY;
		} else
		{
			if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
			if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
		}

		document.onmousemove = Drag.drag;
		document.onmouseup = Drag.end;

		return false;
	},

	drag: function(e)
	{
		e = Drag.fixE(e);
		var o = Drag.obj;

		var ey = e.clientY;
		var ex = e.clientX;
		var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom);
		var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right);
		var nx, ny;

		if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
		if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
		if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
		if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);

		nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
		ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));

		if (o.xMapper) nx = o.xMapper(y)
		else if (o.yMapper) ny = o.yMapper(x)

		Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
		Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
		Drag.obj.lastMouseX = ex;
		Drag.obj.lastMouseY = ey;

		Drag.obj.root.onDrag(nx, ny);
		return false;
	},

	end: function()
	{
		document.onmousemove = null;
		document.onmouseup = null;
		Drag.obj.root.onDragEnd(parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]),
									parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
		Drag.obj = null;
	},

	fixE: function(e)
	{
		if (typeof e == 'undefined') e = window.event;
		if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
		if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
		return e;
	}
};

// ------------------ JSON ---------------------------------------

var JSON = {
	// JSON.serialize(o)
	// serializes object o and returns a string 
	serialize: function(o)
	{
		var i, v, s = JSON.serialize, t;

		if (o == null)
			return 'null';

		t = typeof o;

		if (t == 'string')
		{
			v = '\bb\tt\nn\ff\rr\""\'\'\\\\';

			return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'])/g, function(a, b)
			{
				i = v.indexOf(b);

				if (i + 1)
					return '\\' + v.charAt(i + 1);

				a = b.charCodeAt().toString(16);

				return '\\u' + '0000'.substring(a.length) + a;
			}) + '"';
		}

		if (t == 'object')
		{
			if (o instanceof Array)
			{
				for (i = 0, v = '['; i < o.length; i++)
					v += (i > 0 ? ',' : '') + s(o[i]);

				return v + ']';
			}

			v = '{';

			for (i in o)
				v += typeof o[i] != 'function' ? (v.length > 1 ? ',"' : '"') + i + '":' + s(o[i]) : '';

			return v + '}';
		}

		return '' + o;
	},

	// JSON.parse(str)
	// returns an object of the parsed string
	parse: function(s)
	{
		try
		{
			return eval('(' + s + ')');
		} catch (ex)
		{
			// Ignore
		}
	}
}

// ---------------------- animate --------------------------

/*	a general purpose animator
Written by Peter Gutowski, 2007-12-08
	
Can be called most simply as 
	
animate.open(obj); or...
animate.close(obj) to close
		
Methods:
	
open(targetdiv,callback,displaydiv,curpos,rect,steps,speed)

close(targetdiv,callback,displaydiv,curpos,rect,steps,speed)
			
targetdiv (required): object or ID of item to animate to
callback: (optional) parameter-less callback function after completion
displaydiv: (optional): object or ID of item that is displayed in the animation (if you include a reference to an object you can style it better)
curpos: (optional) [object {x:0, y:0} ] that defines the starting focal point. If null, then the center of the screen is used
rect: (optional) [object { left: 0, top:0, width: 0, height: 0}]. Sometimes that positioning of the targetdiv cannot be determines
so passing this object allows you to set the target rectangle size and coordinates.
steps: (optional) [integer] number of animation steps you want, default is 10
speed: (optional) [integer] delay in milliseconds between steps; default is 20
		
resetdefaults()
			
sets animate object back to defaults		

Other Properties:
		
border: default for new animation object: 1px solid #999;
backgroundColor: default for new animation object: null
*/
var animate =
{
	anisteps: 10, //
	anicurstep: 0,
	anispeed: 25, // milliseconds
	anipos: null,
	anidispl: null,
	targinfo: { left: 0, top: 0, width: 0, height: 0 },
	anitarg: null,
	anicallback: null,
	border: "1px solid #999",
	backgroundColor: null,

	resetdefaults: function()
	{
		this.anisteps = 10;
		this.anispeed = 15;
		this.border = "1px solid #999";
		this.backgroundColor = null;
	},

	doOpen: function()
	{
		if (this.anicurstep <= this.anisteps)
		{
			this.anidispl.style.left = "" + Math.floor(this.anipos.x + ((this.targinfo.left - this.anipos.x) * (this.anicurstep / this.anisteps))) + "px";
			this.anidispl.style.top = "" + Math.floor(this.anipos.y + ((this.targinfo.top - this.anipos.y) * (this.anicurstep / this.anisteps))) + "px";
			this.anidispl.style.width = "" + Math.floor(this.targinfo.width * this.anicurstep / this.anisteps) + "px";
			this.anidispl.style.height = "" + Math.floor(this.targinfo.height * this.anicurstep / this.anisteps) + "px";
			try
			{
				this.anidispl.style.display = "inherit";
			}
			catch (err)
			{
				$('#' + this.anidispl.id).show();
			}
			setTimeout("animate.doOpen()", this.anispeed);
		}
		else
		{
			try
			{
				this.anidispl.style.display = "none";
			}
			catch (err)
			{
				$('#' + this.anidispl.id).hide();
			}
			if (typeof (this.anicallback) == "function")
				this.anicallback();
		}
		this.anicurstep++;
	},

	doClose: function()
	{
		if (this.anicurstep > 0)
		{
			this.anidispl.style.left = "" + Math.floor(this.anipos.x + ((this.targinfo.left - this.anipos.x) * (this.anicurstep / this.anisteps))) + "px";
			this.anidispl.style.top = "" + Math.floor(this.anipos.y + ((this.targinfo.top - this.anipos.y) * (this.anicurstep / this.anisteps))) + "px";
			this.anidispl.style.width = "" + Math.floor(this.targinfo.width * this.anicurstep / this.anisteps) + "px";
			this.anidispl.style.height = "" + Math.floor(this.targinfo.height * this.anicurstep / this.anisteps) + "px";
			try
			{
				this.anidispl.style.display = "inherit";
			}
			catch (err)
			{
				$('#' + this.anidispl.id).show();
			}
			setTimeout("animate.doClose()", this.anispeed);
		}
		else
		{
			try
			{
				this.anidispl.style.display = "none";
			}
			catch (err)
			{
				$('#' + this.anidispl.id).hide();
			}
			if (typeof (this.anicallback) == "function")
				this.anicallback();
		}
		this.anicurstep--;
	},


	setup: function(targetdiv, callback, displaydiv, curpos, rect, steps, speed)
	{
		this.anisteps = steps ? steps : this.anisteps;
		this.anicurstep = 0;
		this.anispeed = speed ? speed : this.anispeed;
		this.anitarg = typeof (targetdiv) == "object" ? targetdiv : document.getElementById(targetdiv);
		this.anicallback = (typeof (callback) == "function") ? callback : null;
		if (curpos)
			this.anipos = curpos;
		else
		{
			var ws = getwindowsize();
			var st = getscrolltop();
			this.anipos = { x: Math.floor((ws.width + st.x) / 2), y: Math.floor((ws.height + st.y) / 2) };
		}
		if (typeof (displaydiv) == "object")
			this.anidispl = displaydiv;
		else if (typeof (displaydiv) == "string")
		{
			this.anidispl = document.getElementById(displaydiv);
		}
		if (this.anidispl == null)
		{
			this.anidispl = document.getElementById(this.anitarg.id + "_ani");
			if (this.anidispl == null)
			{
				this.anidispl = document.createElement("div");
				this.anidispl.style.position = "absolute";
				this.anidispl.style.zIndex = 10000000;
				this.anidispl.id = this.anitarg.id + "_ani";
				this.anidispl.style.display = "none";
				this.anidispl.style.border = this.border
				this.anidispl.style.backgroundColor = this.backgroundColor;
				document.body.appendChild(this.anidispl);
			}
		}
		if (rect)
			this.targinfo = rect;
		else
		{
			var xy = getxy(this.anitarg);
			this.targinfo.left = xy.x;
			this.targinfo.top = xy.y;
			this.targinfo.width = parseInt(this.anitarg.style.width);
			this.targinfo.height = parseInt(this.anitarg.style.height);
		}
	},

	open: function(targetdiv, callback, displaydiv, curpos, rect, steps, speed)
	{
		this.setup(targetdiv, callback, displaydiv, curpos, rect, steps, speed)
		this.doOpen();
	},

	close: function(targetdiv, callback, displaydiv, curpos, rect, steps, speed)
	{
		if (!curpos && typeof (this.anipos) == "object")
			curpos = this.anipos;
		this.setup(targetdiv, callback, displaydiv, curpos, rect, steps, speed)
		this.anicurstep = this.anisteps; // must reset this since we're going reverse
		this.doClose();
	}

}

var Base64 = {

	// private property
	_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

	// public method for encoding
	encode: function(input)
	{
		var output = "";
		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
		var i = 0;

		input = Base64._utf8_encode(input);

		while (i < input.length)
		{

			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);

			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;

			if (isNaN(chr2))
			{
				enc3 = enc4 = 64;
			} else if (isNaN(chr3))
			{
				enc4 = 64;
			}

			output = output +
            this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
            this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

		}

		return output;
	},

	// public method for decoding
	decode: function(input)
	{
		var output = "";
		var chr1, chr2, chr3;
		var enc1, enc2, enc3, enc4;
		var i = 0;

		input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

		while (i < input.length)
		{

			enc1 = this._keyStr.indexOf(input.charAt(i++));
			enc2 = this._keyStr.indexOf(input.charAt(i++));
			enc3 = this._keyStr.indexOf(input.charAt(i++));
			enc4 = this._keyStr.indexOf(input.charAt(i++));

			chr1 = (enc1 << 2) | (enc2 >> 4);
			chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
			chr3 = ((enc3 & 3) << 6) | enc4;

			output = output + String.fromCharCode(chr1);

			if (enc3 != 64)
			{
				output = output + String.fromCharCode(chr2);
			}
			if (enc4 != 64)
			{
				output = output + String.fromCharCode(chr3);
			}

		}

		output = Base64._utf8_decode(output);

		return output;

	},

	// private method for UTF-8 encoding
	_utf8_encode: function(string)
	{
		string = string.replace(/\r\n/g, "\n");
		var utftext = "";

		for (var n = 0; n < string.length; n++)
		{

			var c = string.charCodeAt(n);

			if (c < 128)
			{
				utftext += String.fromCharCode(c);
			}
			else if ((c > 127) && (c < 2048))
			{
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else
			{
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}

		}

		return utftext;
	},

	// private method for UTF-8 decoding
	_utf8_decode: function(utftext)
	{
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;

		while (i < utftext.length)
		{

			c = utftext.charCodeAt(i);

			if (c < 128)
			{
				string += String.fromCharCode(c);
				i++;
			}
			else if ((c > 191) && (c < 224))
			{
				c2 = utftext.charCodeAt(i + 1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else
			{
				c2 = utftext.charCodeAt(i + 1);
				c3 = utftext.charCodeAt(i + 2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}

		}

		return string;
	}

}

function setTransparency(el, amt)
{
	if (el && typeof el == "string")
		el = document.getElementById(el);
	if (!el)
		return;
	if (amt > 99.99)
	{
		el.style.filter = "alpha(opacity=100)";
		el.style.mozOpacity = ".9999";
		el.style.opacity = ".9999";
	}
	else
	{
		el.style.filter = "alpha(opacity=" + amt + ")";
		el.style.mozOpacity = "" + (amt / 100);
		el.style.opacity = "" + (amt / 100);
	}

}


/// <summary>
/// Passed a date in the format of a string looking like "\/Date(1234567899)/"
/// returns a correct date.
/// </summary>
function parseDate(sDate)
{
	if (sDate != null && sDate.length > 0)
	{
		var dnum = sDate.substr(sDate.indexOf('(') + 1);
		var dnum = dnum.substring(0, dnum.indexOf(')'));
		var retval = new Date();
		retval.setTime(dnum);
		return retval;
	}
}

/// <summary>
/// Parallel javascript method that logs to dbo.Log
/// </summary>
function LogError(sMethodname, sAddlinfo, iLine_no) {
    var req = getreq();
    var sendObj = { "methodname": sMethodname, "addlinfo": sAddlinfo, "line_no": iLine_no };
    req.open("POST", (srvr + WEBSERVICE + "LogError"), true);
    req.setRequestHeader(CONTENT_TYPE, APP_JSON);
    req.send(JSON.serialize(sendObj));
   }

// 		public virtual void PageView(string title, string url)
function PageView(title, url)
{
   	var req = getreq();
   	var sendObj = { "title": title.trim(), "url": url };
   	req.open("POST", (srvr + WEBSERVICE + "PageView"), true);
   	req.setRequestHeader(CONTENT_TYPE, APP_JSON);
   	req.send(JSON.serialize(sendObj));
}