
/* ConversionFlags enum; */
var strictConversion = 0;
var lenientConversion = 1;

/* ConversionResult enum; */
var conversionOK = 0; 		/* conversion successful */
var sourceExhausted = 1;	/* partial character in source, but hit end */
var targetExhausted = 2;	/* insuff. room in target for conversion */
var sourceIllegal = 3;		/* source sequence is illegal/malformed */

// C defines
var UNI_SUR_HIGH_START = 0xD800;
var UNI_SUR_HIGH_END = 0xDBFF; 
var UNI_SUR_LOW_START = 0xDC00; 
var UNI_SUR_LOW_END = 0xDFFF;
var UNI_REPLACEMENT_CHAR = 0x0000FFFD;
var UNI_MAX_BMP = 0x0000FFFF;
var UNI_MAX_UTF16 = 0x0010FFFF;
var UNI_MAX_UTF32 = 0x7FFFFFFF;
var halfShift	= 10; // used for shifting by 10 bits
var halfBase	= 0x0010000;
var halfMask	= 0x3FF;

var firstByteMark = new Array( 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC );

// introduced, since there is no exceptions and no byref argument in javascript
function ResultObject(code, result) {
	this.code = code;
	this.result = result;
}

// was ConvertUTF16toUTF8	
function encodeUTF8 ( source, sourceStart, sourceEnd, flags )
{
	var result = conversionOK;
	if (source == null) return new ResultObject(result, null);
	
	var target = "";
	var targetPos = 0;
	if (sourceStart == null) sourceStart = 0;
	if (sourceEnd == null) sourceEnd = source.length;
	var sourcePos = sourceStart;
	
	while (sourcePos < sourceEnd) {
		var ch;
		var bytesToWrite = 0;
		var byteMask = 0xBF;
		var byteMark = 0x80; 
		ch = source.charCodeAt(sourcePos); sourcePos++;
		
		/* If we have a surrogate pair, convert to UTF32 first. */
		if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END && sourcePos < sourceEnd) {
			var ch2 = source.charAt(sourcePos); // UTF32 ch2 = *source;
			if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
				ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
					+ (ch2 - UNI_SUR_LOW_START) + halfBase;
				++sourcePos;
			} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
				--sourcePos;
				result = sourceIllegal;
				break;
			}
		}
		else if ((flags == strictConversion) && (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)) {
			--sourcePos; 
			result = sourceIllegal;
			break;
		}
		
		/* Figure out how many bytes the result will require */
		if (ch < 0x80) {
			bytesToWrite = 1;
		}
		else if (ch < 0x800) {
			bytesToWrite = 2;
		}
		else if (ch < 0x10000) {
			bytesToWrite = 3;
		}
		else if (ch < 0x200000) {
			bytesToWrite = 4;
		}
		else {
			bytesToWrite = 2;
			ch = UNI_REPLACEMENT_CHAR;
		}

		targetPos += bytesToWrite;
				
		var z;
		var chars = new Array(bytesToWrite);
		
		switch (bytesToWrite) {
			case 4:	--targetPos; z = (ch | byteMark) & byteMask; chars[3] = String.fromCharCode(z); ch >>= 6;
			case 3:	--targetPos; z = (ch | byteMark) & byteMask; chars[2] = String.fromCharCode(z); ch >>= 6;
			case 2:	--targetPos; z = (ch | byteMark) & byteMask; chars[1] = String.fromCharCode(z); ch >>= 6;
			case 1:	--targetPos; z =  ch | firstByteMark[bytesToWrite]; chars[0] = String.fromCharCode(z);

		}
		
		target += chars.join("");
		
		targetPos += bytesToWrite;
	} // while

	return new ResultObject(result, target);
}


/**
* Part II: Handles different versions of browsers
*/

var uriComponentChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'(),";
var uriChars = uriComponentChars + "/?:@&=+$"

// Same as encodeURI in NS>=6 and IE>=5
function encodeURI(x) {
	x = encodeUTF8(x, null, null, strictConversion);
	if (x.code != conversionOK) return null;
	return doEscape(x.result, uriChars);
}

// Same as encodeURIComponent in NS>=6 and IE>=5
function encodeURIComponent(x) {
	x = encodeUTF8(x, null, null, strictConversion);
	if (x.code != conversionOK) return null;
	return doEscape(x.result, uriComponentChars);
}

// not totally accurate, because it does not respect the given encoding as
// the functions in IE>=5 and netscape>=6, but works for now.
function decodeURIComponent(x) { return unescape(x); }
function decodeURI(x) {	return unescape(x); }

// replaces original escape, because it does not work on some 
// UTF8 encoded chars in Netscape and Mozilla
function escape(x) {
	return doEscape(x, uriChars);
}

// function handles various character encodings
function doEscape(x, okChars) {

	if (x == null) return null;

	var result = "";
	var index = 0;
	var c = 0;
	
	for (index = 0; index < x.length; index++) {
		c = x.charAt(index);
		
		if (-1 == okChars.indexOf( c ))
			result += "%" + c.charCodeAt(0).toString(16).toUpperCase();
		else
			result += c;
	}
	
	return result;
}
