		//
		// base64.js
		//
		//
		// simple base64 string encoding and decoding routines
		// includes utf-8 "character byte" support
		// 
		// copyright ©2008-2009 logicdriven, llc.  all rights reserved.
		// permission is granted for unlimited restribution, as long
		// as logicdriven is attributed
		//
		// some concepts, particularly the utf-8 support, are based on
		// code from webtoolkit (http://www.webtoolkit.info)
		//
		// 
		// note that the code here is written for readability, not maximum
		// efficiency or minimum code size.  in particular, we could have
		// used the fall-through capabilities of the switch() statement to
		// reduce the code size in the encodeQuanta() routine, but it would
		// have been a lot harder to follow without much in the way of a 
		// performance gain
		//
		//
		//
		
		
		
			var _encodingTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
		
		
			//
			// base64 decoding routines
			//
			//
			function decodeBase64String(base64String) {
			// decodes the base64 string passed in, returns the
			// decoded string
		
				if (base64String == null) return "";
				if (base64String == "") return "";
		
				var decodedString = "";
		
				// strip non-base64 characters
				//
				// if your ECMAScript language doesn't support regular expressions
				// in its .replace method, you'll probably want to use a split/join
				// expression to pull out invalid characters.  an example of this
				// for selected whitespace elements is in the commented code lines below
				//
				base64String = base64String.replace(/[^A-Za-z0-9\+\/\=]/g, "");
				//base64String = base64String.split("\r").join("");
				//base64String = base64String.split("\n").join("");
				//base64String = base64String.split("\t").join("");
				//base64String = base64String.split(" ").join("");
				//base64String = base64String.split("+").join("");
		
				// ensure string length is a multiple of four
				switch (base64String.length % 4) {
					case 1:
						base64String += "===";
						break;
					case 2:
						base64String += "==";
						break;
					case 3:
						base64String += "=";
						break;
				}
		
		
		
				// iterate through the string, adding each decoded
				// chunk (quanta) to the result
				for (var i=0; i<(base64String.length-1); i=i+4) {
					decodedString += decodeBase64Quanta(base64String.substring(i,i+4));
				}
				return decodedString;
		
			}
		
		
		
			function decodeBase64Quanta(base64Quanta) {
			// decodes a four-character chunk of base64 data and
			// returns the three-character result
			//
			// each of the encoded characters passed can be considered as a group
			// of six bits (2^6 = 64; thus, base64).  If all four encoded characters
			// are smooshed together, we get 24 bits (6*4 = 24).  These 24 bits are
			// then split up into three chunks of eight bits each (8*3 = 24), which
			// are then (usually) returned as three bytes.
			//
			// as mentioned in the comments, we're only working with encoded strings,
			// so we save the byte->string conversion step and return a three-character
			// string
			//
			// the four six-bit characters make up the eight-bit results as follows:
			//    11111122 22223333 33444444
			//
			//
		
				// stick encoding values from string into bytes
				//
				var byte1 = _encodingTable.indexOf(base64Quanta.charAt(0));
				var byte2 = _encodingTable.indexOf(base64Quanta.charAt(1));
				var byte3 = _encodingTable.indexOf(base64Quanta.charAt(2));
				var byte4 = _encodingTable.indexOf(base64Quanta.charAt(3));
		
				// adjust for the padding character -- "=", which is not listed in our table
				//
				if (byte1 < 0) byte1 = 0;
				if (byte2 < 0) byte2 = 0;
				if (byte3 < 0) byte3 = 0;
				if (byte4 < 0) byte4 = 0;
		
		
				var output = "";
		
				// generate the output characters
				//
				// we do the bitwise ANDing here because, despite what the documentation
				// says, some ECMAScript languages (I'm looking at you, Flash) appear to 
				// not always fill in the emptied bits with 0 when left shifting
				//
				output = output + String.fromCharCode(((byte1 << 2) & 0xFC) | (byte2 >>> 4));
				output = output + String.fromCharCode(((byte2 << 4) & 0xF0) | (byte3 >>> 2));
				output = output + String.fromCharCode(((byte3 << 6) & 0xC0) | byte4);
		
				// return the result
				return output;
		
			}
		
		
		
		
			//
			// base64 encoding routines
			//
			//
			function encodeBase64String(sourceString) {
			// encodes the passed string into base64 format
		
				if (sourceString == null) return "";
				if (sourceString == "") return "";
		
				sourceString = String(sourceString);
		
				var encodedString = "";
		
				// iterate through the string, adding each encoded
				// chunk (quanta) to the result
				for (var i=0; i<(sourceString.length); i=i+3) {
					encodedString += encodeBase64Quanta(sourceString.substring(i,i+3));
				}
				return encodedString;
		
			}
		
		
			function encodeBase64Quanta(sourceQuanta) {
			// encodes a one-, two-, or three-character chunk of data and returns
			// the four-character base64-encoded result
			//
			// essentially the inverse of the decodeQuanta function listed above, our
			// three characters make up the four six-bit results as follows:
			//    111111 112222 222233 333333
			//
			
				if (sourceQuanta.length = 0) return "";
			
				// create base64 "bytes" w/default values
				//
				var byte1 = -1;
				var byte2 = -1;
				var byte3 = -1;
				var byte4 = -1;
				
				
				// shift input bits into byte vars as appropriate
				//
				// we do extra bitwise ANDing here because, despite what the documentation
				// says, some ECMA script languages (I'm looking at you, Flash) appear to 
				// not always fill in the emptied bits with 0 when left shifting
				//
				switch (sourceQuanta.length) {
					case 1:
						byte1 = (sourceQuanta.charCodeAt(0) >> 2) & 0x3F;
						byte2 = ((sourceQuanta.charCodeAt(0) & 0x03) << 4) & 0x30;
						break;
					case 2:
						byte1 = (sourceQuanta.charCodeAt(0) >> 2) & 0x3F;
						byte2 = (((sourceQuanta.charCodeAt(0) & 0x03) << 4) & 0x30) | (sourceQuanta.charCodeAt(1) >>> 4);
						byte3 = ((sourceQuanta.charCodeAt(1) & 0x0F) << 2) & 0x3C;
						break;
					case 3:
						byte1 = (sourceQuanta.charCodeAt(0) >> 2) & 0x3F;
						byte2 = (((sourceQuanta.charCodeAt(0) & 0x03) << 4) &0x30) | (sourceQuanta.charCodeAt(1) >>> 4);
						byte3 = (((sourceQuanta.charCodeAt(1) & 0x0F) << 2) & 0x3C) | (sourceQuanta.charCodeAt(2) >>> 6);
						byte4 = sourceQuanta.charCodeAt(2) & 0x3F;
						break;
				}
				
				
				// create output characters
				//
				var char1 = "=";
				var char2 = "=";
				var char3 = "=";
				var char4 = "=";
				
				if (byte1 >= 0) char1 = _encodingTable.charAt(byte1);
				if (byte2 >= 0) char2 = _encodingTable.charAt(byte2);
				if (byte3 >= 0) char3 = _encodingTable.charAt(byte3);
				if (byte4 >= 0) char4 = _encodingTable.charAt(byte4);
				
				// return the result
				return char1 + char2 + char3 + char4;
		
			}
		
		
			
			//
			// utf-8 encoding/decoding routines
			//
			// use these before calling the encodeString() routine or
			// after calling decodeString()
			//
			function encodeUTF8String(sourceString) {
			// function returns a version of the UTF-8 source string where high-order (non-ASCII)
			// characters have been converted into a sequence of "character bytes".  the resulting
			// string can be base64 encoded using the routines in this file, which normally only
			// accept ASCII encodings.
			//
			// note that this function does not work for unicode characters not in the Unicode
			// Basic Multilingual Plane.  for more information on UTF-8, check out the article
			// at Wikipedia (http://en.wikipedia.org/wiki/UTF-8)
			//
			// as you can see from the code, calling this method when the string contains nothing
			// but ASCII characters has no effect -- the returned string is identical to the 
			// source parameter
		
				if (sourceString == null) return "";
				if (sourceString == "") return "";
		
		
				var utf8String = "";
		
				for (var i=0; i<sourceString.length; i++) {
		
					var code = sourceString.charCodeAt(i);
		
					// return 1,2, or 3 characters based on character type
					//
					if (code < 0x80) {
					// us-ascii characters.  returns the plain character (byte begins with 0)
						utf8String += String.fromCharCode(code);
					} else if (code < 0x800) {
					// latin, greek, cyrillic, etc. 
					//
					// return two characters. first character byte begins with 110, the second
					// begins with 10
					//
						utf8String += String.fromCharCode(0xC0 | (code >> 6));
						utf8String += String.fromCharCode(0x80 | (code & 0x3F));
					} else {
					// all other basic multilingual plane chars
					//
					// return three characters. first character byte begins with 1110, both the
					// second and third character bytes begin with 10
					//
						utf8String += String.fromCharCode(0xE0 | (code >> 12));
						utf8String += String.fromCharCode(0x80 | ((code >> 6) & 0x3F));
						utf8String += String.fromCharCode(0x80 | (code & 0x3F));
					}
				}
		
				return utf8String;
			}
		
		
		
			function decodeUTF8String(utf8String) {
			// function takes a string previously encoded with the encodeUTF8 method
			// above and returns the original (pre-encoded) string
			
				if (utf8String == null) return "";
				if (utf8String == "") return "";
		
				var output = "";
				var i = 0;
		
		
				while (i < utf8String.length) {
		
					var code1 = utf8String.charCodeAt(i);
					var code2 = utf8String.charCodeAt(i+1);
					var code3 = utf8String.charCodeAt(i+2);
					var resultCode = 0;
		
					if (code1 < 0x80) {
					// plain character
						resultCode = code1;
						i++;
					} else if((code1  >= 0xC0) && (code1  < 0xE0)) {
					// two-character sequence.  merge and convert
						resultCode = ((code1 & 0x1F) << 6);
						resultCode = resultCode | (code2 & 0x3F);
						i += 2;
					} else {
					// three-character sequence.  merge and convert
						resultCode = ((code1 & 0x0F) << 12);
						resultCode = resultCode | ((code2 & 0x3F) << 6);
						resultCode = resultCode | (code3 & 0x3F);
						i += 3;
					}
		
					// append calculation result
					output += String.fromCharCode(resultCode); 
				}
		
				return output;
			}
