// ////////////////////////////////////////////////////////////////////////////////////////////////
// Following is the CRM Encoding Decoding functions
// We have this Wrapper so that we can change the encoding decoding in one single place if needed
// ////////////////////////////////////////////////////////////////////////////////////////////////

// Decode URLs
function _crmUrlDecode(s)
{
	if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}
	//Have to compensate for %uNNNN style encoding produced by AntiXss.UrlEncode
	// decode numeric encoded sequences
	// decimal encoded
	s = s.replace(/%u[a-fA-F0-9]{4}/g, function ($1){return String.fromCharCode(parseInt($1.substr(2, $1.length-2), 16));});
	//use decodeURIComponent to decode the string
	return decodeURIComponent(s);
}

function _crmUrlEncode(s)
{
     if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}

     s = _UrlEncode(s);
          
     // This is a workaround for a known issue where extended characters are encoded as
     // %uNNNN instead of %NN%NN.  We search for the incorrect encoding and replace it
     // with the correct encoding.

	 // First we search for supplemental characters, which have to be encoded simultaneously.
	 // The range for supplemental characters is:
	 // First char:  0xD800 - 0xDBFF
	 // Second char: 0xDC00 - 0xDFFF
	 s = s.replace(/%u[dD][89aAbB][a-fA-F0-9]{2}%u[dD][cCdDeEfF][a-fA-F0-9]{2}/g, function ($1){
		return encodeURIComponent(String.fromCharCode(parseInt($1.substring(2, 6), 16)) + String.fromCharCode(parseInt($1.substring(8), 16)));
	 });
     
     // Now we search for any remaining %uNNNN encodings.
     s = s.replace(/%u[a-fA-F0-9]{4}/g, function ($1){return encodeURIComponent(String.fromCharCode(parseInt($1.substr(2, $1.length-2), 16)));});
     
     return s;
}

// Encodes strings for name=value&name=value pair usage.
function _crmNameValueEncode(s)
{
	if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}
	// We are encoding the name and value for safe usage in name=value&name=value...
	// UrlEncode already does this so we reuse it.
	return CrmEncodeDecode.CrmUrlEncode(s);
}

// Decodes strings used as name, value in name=value&name=value... pairs.
function _crmNameValueDecode(s)
{	
	if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}
	// The name=value&name=value... are encoded using UrlEncode so decode it with UrlDecode
	return CrmEncodeDecode.CrmUrlDecode(s);
}

// Decodes a complete string or only the specified character
// Params: s - string to decode
//			charToDecode - character that needs to be decoded, if null or not present all chars are decoded.
function _crmXmlDecode(s, charToDecode)
{
	if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}
	
	if(typeof(charToDecode) != "undefined" && charToDecode != null)
	{
		//only one char needs to be decoded
		if(charToDecode.length > 1) charToDecode = charToDecode.charAt(0);
		var sEncodedChar = _XmlEncode(charToDecode);
		var rex = new RegExp(sEncodedChar, "g");
		s = s.replace(rex, charToDecode);
		
		//additionaly decode the forms &lt;&gt;&amp;&quot;&apos;
		switch(charToDecode)
		{
			case "<":
				s = s.replace(/&lt;/g, "<");
				break;
			case ">":
				s = s.replace(/&gt;/g, ">");
				break;
			case "&":
				s = s.replace(/&amp;/g, "&");
				break;
			case "\"":
				s = s.replace(/&quot;/g, "\"");
				break;
			case "'":
				s = s.replace(/&apos;/g, "'");
				break;
		}
		return s;
	}
	
	//Decode all encoded chars
	// Decode special encoding sequences
	s = s.replace(/&lt;/g, "<");
	s = s.replace(/&gt;/g, ">");
	s = s.replace(/&apos;/g, "'");
	s = s.replace(/&quot;/g, "\"");
	s = s.replace(/&amp;/g, "&");
	
	// decode numeric encoded sequences
	// decimal encoded
	s = s.replace(/&#[0-9]+;/g, function ($1){return String.fromCharCode($1.substr(2, $1.length-3));});
	
	// hex encoded
	s = s.replace(/&#x[a-fA-F0-9]+;/g, function ($1){
		var charCode = parseInt($1.substr(3, $1.length-4), 16);
		if (charCode >= 0x10000 && charCode <= 0x10FFFF) {
			var hi = Math.floor((charCode - 0x10000) / 0x400) + 0xD800;
			var lo = ((charCode - 0x10000) % 0x400) + 0xDC00;
			return String.fromCharCode(hi) + String.fromCharCode(lo);
		}
		else
		{
			return String.fromCharCode(charCode);
		}
	});
	
	return s;
}

// Encodes a single or all characters in a string for usage in Xml elements
// Param charToEncode is optional. If null or not passed in then all characters are encoded
function _crmXmlEncode(s, charToEncode)
{
	if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}
	if(typeof(charToEncode) != "undefined" && charToEncode != null)
	{
		//only one char needs to be encoded
		if(charToEncode.length > 1) charToEncode = charToEncode.charAt(0);
		var sEncodedChar = _XmlEncode(charToEncode);
		var rex = new RegExp(charToEncode, "g");
		return s.replace(rex, sEncodedChar);
	}
	
	return _surrogateAmpersandWorkaround(s, _XmlEncode);
}

function _crmHtmlEncode(s)
{
	if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}
	return _surrogateAmpersandWorkaround(s, _HtmlEncode);
}

function _crmHtmlEncodeForFormatString(s)
{
	if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}
	s = _surrogateAmpersandWorkaround(s, _HtmlEncode);
	return s.replace(/&#123;/g, "{").replace(/&#125;/g, "}");
}

function _crmHtmlAttributeEncode(s)
{
	if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}
	
	return _surrogateAmpersandWorkaround(s, _HtmlAttributeEncode);
}

function _crmXmlAttributeEncode(s)
{
	if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}
	
	return _surrogateAmpersandWorkaround(s, _XmlAttributeEncode);
}

function _crmJavaScriptEncode(s)
{
	if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}
	return _JavaScriptEncode(s);
}

function _crmVisualBasicScriptEncode(s)
{
	if(IsNull(s)){return s;}else{if(typeof(s) != "string"){s = s.toString();}}
	return _VisualBasicScriptEncode(s);
}

// When encoding surrogate characters, AntiXSS incorrectly encodes them in the format
// "&#55360;&#56579;" instead of the correct format "&#x20103;".  This function is a
// workaround for all encodings of this form.
//
// s is the string to encode.
// encodingFunction is the function used to encode the rest of the string.
function _surrogateAmpersandWorkaround(s, encodingFunction)
{	
	// Encode surrogate pairs in Unicode scaler value
	s = s.replace(/([\uD800-\uDBFF][\uDC00-\uDFFF])/g, function ($1){return "CRMEntityReferenceOpen" + ((($1.charCodeAt(0) - 0xD800) * 0x400) + ($1.charCodeAt(1) & 0x03FF) + 0x10000).toString(16) + "CRMEntityReferenceClose";});
	
	// Fix issue 12224: sometimes the user just put invalid surrogate pairs (The correct surrogate pair: Low Surrogate followed by High Surrogate). 
	// We need to replace the character with Replacement character #UFFFD. This character is used to replace incoming charater whose value is unknown
	s = s.replace(/[\uD800-\uDFFF]/g, '\uFFFD');
	
	//encode whole string
	s = encodingFunction(s);
	
	s = s.replace(/CRMEntityReferenceOpen/g, "&#x");
	s = s.replace(/CRMEntityReferenceClose/g, ";");
	return s;
}

function CrmEncodeDecodeLibrary()
{
	//encoding methods
	this.CrmHtmlEncode = _crmHtmlEncode;
	this.CrmHtmlAttributeEncode = _crmHtmlAttributeEncode;
	this.CrmXmlEncode = _crmXmlEncode;
	this.CrmXmlAttributeEncode = _crmXmlAttributeEncode;
	this.CrmJavaScriptEncode = _crmJavaScriptEncode;
	this.CrmVisualBasicScriptEncode = _crmVisualBasicScriptEncode;
	this.CrmUrlEncode = _crmUrlEncode;
	this.CrmNameValueEncode = _crmNameValueEncode;
	this.CrmHtmlEncodeForFormatString = _crmHtmlEncodeForFormatString;
	
	//decoding methods
	this.CrmXmlDecode = _crmXmlDecode;
	//for htmldecode we reuse xmldecode since the encoding is the same
	this.CrmHtmlDecode = _crmXmlDecode;
	this.CrmUrlDecode = _crmUrlDecode;
	this.CrmNameValueDecode = _crmNameValueDecode;
}
//Use the following object for performing all encoding/decoding in CRM client side code
var CrmEncodeDecode = new CrmEncodeDecodeLibrary();



// End of CrmEncodeDecode Library functions
// ///////////////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////////////////
// Do not change any code below this, the code below is from the AntiXss Library team
// CRM uses a wrapper for this make any adjustments to encoding decoding in the wrapper not
// in the functions below. The code below can/will be updated with latest code from the
// AntiXss Team.
// ***********************************************************
// *
// * AntiXSS Library
// *
// ***********************************************************
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// Changes
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
// 06-09-06: hassank:   Corrected URL REGEX
// 04-07-04: talhahm:   Added checks for null inputs
// 07-24-03: eddington: Quick port to ASP Javascript
// 03-26-03: v-michae:	Initial revision based on ASP classic
// 03-28-03: v-michae:	Initial testing of functions done.
// 06-01-03: erach:		AsUrl temporarily stubbed out for
//						0.9 release
// 10-29-03: erach:		AsUrl implemented introduced
//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

function _HtmlEncode( strInput )
{	
	var c;
	var HtmlEncode = '';
	
	if(strInput == null)
	{
	    return null;
    }
	if (strInput == '')
	{
		return '';
	}
	
	for(var cnt = 0; cnt < strInput.length; cnt++)
	{
		c = strInput.charCodeAt(cnt);
		
		if (( ( c > 96 ) && ( c < 123 ) ) ||
			( ( c > 64 ) && ( c < 91 ) ) ||
			( c == 32 ) ||
			( ( c > 47 ) && ( c < 58 ) ) ||
			( c == 46 ) ||
			( c == 44 ) ||
			( c == 45 ) ||
			( c == 95 ))
		{
			HtmlEncode = HtmlEncode + String.fromCharCode(c);
		}
		else
		{
			HtmlEncode = HtmlEncode + '&#' + c + ';';
		}
	}
	
	return HtmlEncode;
}

function _HtmlAttributeEncode( strInput )
{
	var c;
	var HtmlAttributeEncode = '';

    if(strInput == null)
	{
	    return null;
    }
	if (strInput == '')
	{
		return '';
	}
	
	for(var cnt = 0; cnt < strInput.length; cnt++)
	{
		c = strInput.charCodeAt(cnt);

		if (( ( c > 96 ) && ( c < 123 ) ) ||
			( ( c > 64 ) && ( c < 91 ) ) ||
			( ( c > 47 ) && ( c < 58 ) ) ||
			( c == 46 ) ||
			( c == 44 ) ||
			( c == 45 ) ||
			( c == 95 ))
		{
			HtmlAttributeEncode = HtmlAttributeEncode + String.fromCharCode(c);
		}
		else
		{
			HtmlAttributeEncode = HtmlAttributeEncode + '&#' + c + ';';
		}
	}
	
	return HtmlAttributeEncode;
}

function _XmlEncode( strInput )
{
	// HtmlEncode will handle null string
	return _HtmlEncode( strInput );
}

function _XmlAttributeEncode( strInput )
{
	// EncodeHtmlAttribute will handle null string
	return _HtmlAttributeEncode( strInput );
}
	
function _JavaScriptEncode( strInput )
{
	var c;
	var EncodeJs = '';
	
    if(strInput == null)
	{
	    return null;
    }
	if (strInput == '')
	{
		return '';
	}
	
	for(var cnt = 0; cnt < strInput.length; cnt++)
	{
		c = strInput.charCodeAt(cnt);

		if (( ( c > 96 ) && ( c < 123 ) ) ||
			( ( c > 64 ) && ( c < 91 ) ) ||
			( c == 32 ) ||
			( ( c > 47 ) && ( c < 58 ) ) ||
			( c == 46 ) ||
			( c == 44 ) ||
			( c == 45 ) ||
			( c == 95 ))
		{
			EncodeJs = EncodeJs + String.fromCharCode(c);
		}
		else if ( c > 127 )
		{
			EncodeJs = EncodeJs + '\\u' + OutputEncoder_TwoByteHex(c);
		}
		else
		{
			EncodeJs = EncodeJs + '\\x' + OutputEncoder_SingleByteHex(c);
		}
	}
	
	return '\'' + EncodeJs + '\'';
}

function _VisualBasicScriptEncode( strInput )
{
	var c;
	var EncodeVbs = '';
	var bInQuotes = false;
	
    if(strInput == null)
	{
	    return null;
    }
	if (strInput == '')
	{
		return '';
	}
	
	for(var cnt = 0; cnt < strInput.length; cnt++)
	{
		c = strInput.charCodeAt(cnt);

		if (( ( c > 96 ) && ( c < 123 ) ) ||
			( ( c > 64 ) && ( c < 91 ) ) ||
			( c == 32 ) ||
			( ( c > 47 ) && ( c < 58 ) ) ||
			( c == 46 ) ||
			( c == 44 ) ||
			( c == 45 ) ||
			( c == 95 ))
		{
			// do the "unencoded" ones
			if ( !bInQuotes )
			{
				EncodeVbs = EncodeVbs + '&\"';
				bInQuotes = true;
			}
			
			EncodeVbs = EncodeVbs + String.fromCharCode(c);
		}
		else
		{
			// do the "encoded" ones
			if ( bInQuotes )
			{
				EncodeVbs = EncodeVbs + '\"';
				bInQuotes = false;
			}
			
			EncodeVbs = EncodeVbs + '&chrw(' + c + ')';
		}
	}

	if ( EncodeVbs.charAt(0) == '&' )
	{
		// Remove starting '&'
		EncodeVbs = EncodeVbs.substring(1);
	}
	
	if ( EncodeVbs.length == 0 )
	{
		// if null, add quotes
		EncodeVbs = '\"\"';
	}
	
	if ( bInQuotes )
	{
		//	but if we're in quotes, then close them
		EncodeVbs = EncodeVbs + '\"';
	}
	
	return EncodeVbs;
}

	
function _UrlEncode( strInput )
{
	var c;
	var EncodeUrl = '';
	
    if(strInput == null)
	{
	    return null;
    }
	if (strInput == '')
	{
		return '';
	}
	
	for(var cnt = 0; cnt < strInput.length; cnt++)
	{
		c = strInput.charCodeAt(cnt);

		if (( ( c > 96 ) && ( c < 123 ) ) ||
			( ( c > 64 ) && ( c < 91 ) )  ||
			( ( c > 47 ) && ( c < 58 ) ) ||
			( c == 46 ) ||
			( c == 45 ) ||
			( c == 95 ))
		{
			EncodeUrl = EncodeUrl + String.fromCharCode(c);
		}
		else if ( c > 127 )
		{
			EncodeUrl = EncodeUrl + '%u' + OutputEncoder_TwoByteHex(c);
		}
		else
		{
			EncodeUrl = EncodeUrl + '%' + OutputEncoder_SingleByteHex(c);
		}
	}
	
	return EncodeUrl;
}
	
function OutputEncoder_SingleByteHex( charC )
{
	if (charC == null)
	{
		return '';
	}
	
	var SingleByteHex = charC.toString(16);

	for ( var cnt = SingleByteHex.length; cnt < 2; cnt++ )
	{
		SingleByteHex = "0" + SingleByteHex;
	}
	
	return SingleByteHex;
}

function OutputEncoder_TwoByteHex( charC )
{
	if (charC == null)
	{
		return '';
	}
	
	var TwoByteHex = charC.toString(16);
	
	for( var cnt = TwoByteHex.length; cnt < 4; cnt++ )
	{
		TwoByteHex = "0" + TwoByteHex;
	}
	
	return TwoByteHex;
}

//NOTE: do not use this directly use the CrmEncodeDecode wrapper
function AntiXss()
{
	this.HtmlEncode=_HtmlEncode;
	this.HtmlAttributeEncode = _HtmlAttributeEncode;
	this.XmlEncode = _XmlEncode;
	this.XmlAttributeEncode = _XmlAttributeEncode;
	this.JavaScriptEncode = _JavaScriptEncode;
	this.VisualBasicScriptEncode = _VisualBasicScriptEncode;
	this.UrlEncode = _UrlEncode;
}
var AntiXssLibrary = new AntiXss();

// Upto this point is code from the AntiXss library team, do not change any code above this
//////////////////////////////////////////////////////////////////////////////////////////////////