///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//                        Operations on HTML elements                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

// Scans HTML elements, calls callback for every element.
// Implemented algorithm works 1.5 times faster than recursion.
function DHTML_Walk(oDomNode, rCallbackFunction, arrArguments, rCallbackObject)
{
	var oStartNode = oDomNode;

	if (!rCallbackFunction)
		return;
	if (!arrArguments)
		arrArguments = new Array(1);
	if (!rCallbackObject)
		rCallbackObject = this;

	oDomNode = oStartNode.firstChild;
	while (oDomNode)
	{
		var bDoNotParseSubNodes = false;

		if (oDomNode.nodeType == 1)
		{
			arrArguments[0] = oDomNode;
			bDoNotParseSubNodes = rCallbackFunction.apply(rCallbackObject, arrArguments);
		}

		// Move to the next node
		if (oDomNode.firstChild && !bDoNotParseSubNodes)
			oDomNode = oDomNode.firstChild;
		else 
		{
			while (!oDomNode.nextSibling)
			{
				oDomNode = oDomNode.parentNode;
				if (oDomNode == oStartNode)
					return;
			}
			oDomNode = oDomNode.nextSibling;
		}
	}
}

// Returns attribute value or null if none.
function DHTML_getAttr(oHtmlElem, sAttrName)
{
	if (!oHtmlElem || !oHtmlElem.getAttributeNode || !sAttrName || sAttrName == "")
		return null;
	var oAttrNode = oHtmlElem.getAttributeNode(sAttrName);
	return (oAttrNode ? oAttrNode.value : null);
}

function DHTML_getValue(oHtmlNode)
{
	if (!oHtmlNode)
		return null;

	var vValue = null;
	if (oHtmlNode.nodeType == 1)
	{
		if ("value" in oHtmlNode)
			vValue = oHtmlNode.value;
		else if (oHtmlNode.tagName.toLowerCase() == "select")
			vValue = oHtmlNode.options[oHtmlNode.selectedIndex].value;
		else
			vValue = DHTML_getTextContent(oHtmlNode);
	} // HTML element
	else
		vValue = DHTML_getTextContent(oHtmlNode);
	return vValue;
} // DHTML_getValue()

function DHTML_getTextContent(obj)
{
	if (obj.textContent)	// Mozilla browsers, w3c DOM Level 3
		return obj.textContent;
	if (obj.innerText)		// IE, Safari
		return obj.innerText;
	if (obj.text)			// XML
		return obj.text;
	else if (	obj.childNodes && 
				obj.childNodes.length > 0 && 
				obj.childNodes[0].nodeType == 3)
		return obj.childNodes[0].nodeValue;
	return "";
}

function DHTML_setValue(oHtmlNode, sValue, sFormattedValue)
{
	if (!sFormattedValue || (oHtmlNode.nodeType == 1 && oHtmlNode.tagName.toLowerCase() == "select"))
		sFormattedValue = String(sValue);
	else
		sFormattedValue = String(sFormattedValue);

	if ("value" in oHtmlNode)
		oHtmlNode.value = sFormattedValue;
	else if ("innerText" in oHtmlNode)
		oHtmlNode.innerText = sFormattedValue;
	else
		oHtmlNode.innerHTML = sFormattedValue;
} // DHTML_setValue()

function DHTML_pasteHtmlContent(oHtmlElem, sHtmlContent)
{
	if (oHtmlElem.innerHTML == sHtmlContent)
		return;
	oHtmlElem.innerHTML = sHtmlContent;
}

function DHTML_addCssClassToElem(oHtmlElem, sClass)
{
	if (!oHtmlElem)
		return;
	var reClass = new RegExp(sClass, "gi");
	if (reClass.exec(oHtmlElem.className) == null)
	{
		if (oHtmlElem.className == "")
			oHtmlElem.className = sClass;
		else
			oHtmlElem.className += " " + sClass;
	}
}

function DHTML_removeCssClassFromElem(oHtmlElem, sClass)
{
	if (!oHtmlElem)
		return;
	var reClass = new RegExp("\\s*" + sClass + "\\s*", "gi");
	var arrMatch = reClass.exec(oHtmlElem.className);
	if (arrMatch != null)
	{
		var sClassName = oHtmlElem.className.substr(0, arrMatch.index);
		if (arrMatch.lastIndex < oHtmlElem.className.length)
		{
			if (sClassName != "")
				sClassName += " ";
			sClassName += oHtmlElem.className.substr(arrMatch.lastIndex);
		}
		oHtmlElem.className = sClassName;
	}
}

function DHTML_replaceCssClassToElem(oHtmlElem, sFindClass, sReplaceBy, bDoNotAddIfNone)
{
	if (!oHtmlElem)
		return;

	var reClass = new RegExp("(\\s|^)" + sFindClass + "(\\s|$)", "gi");
	var arrMatch = reClass.exec(oHtmlElem.className);
	if (arrMatch != null)
		oHtmlElem.className = oHtmlElem.className.replace(reClass, arrMatch[1] + sReplaceBy + arrMatch[2]);
	else if (!bDoNotAddIfNone)
		oHtmlElem.className += (((oHtmlElem.className != "") ? " " : "") + sReplaceBy);
}

//------------------------------ HTML DOM helpers ------------------------------
function DHTML_getOwnerDoc(oDomNode)
{
	if (!oDomNode)
		return null;
	
	if (oDomNode.ownerDocument)
		return oDomNode.ownerDocument;
	else if (oDomNode.document)
		return oDomNode.document
	return null;
}

function DHTML_getParent(oDomNode)
{
	if (!oDomNode)
		return null;
	
	if (oDomNode.parentElement)
		return oDomNode.parentElement;
	else if (oDomNode.parentNode)
		return oDomNode.parentNode;
	return null;
}

function DHTML_getElementById(sId, oDocument)
{
	if (!sId || sId == "")
		return null;
	if (!oDocument)
		oDocument = document;

	if (oDocument.getElementById)
		return oDocument.getElementById(sId);
	else if (oDocument.all)
		return oDocument.all[sId];
	return null;
}

function DHTML_getElementsByName(sName, oParent)
{
	if (!sName || sName == "")
		return null;
	if (!oParent)
		oParent = document;
	
	if (oParent.getElementsByName)
		return oParent.getElementsByName(name);
	else if (oParent.all)
	{
		var oRetVal = oParent.all[sName];
		if (!oRetVal)
			return null;
		if (typeof(oRetVal.length) == "undefined") // If only one element is matched, doc.all returns
			oRetVal = new Array(oRetVal);             // the element itself, rather than a collection
		return oRetVal;
	}
	return null;
}

// Get all descendant (direct and indirect child) nodes, bearing the given tag name. * selects all
// descendant nodes, regardless of the tag. Safari 1.2 and Opera 7.5 cannot select custom tags.
function DHTML_getElementsByTagName(sTagName, oParent)
{
	if (!sTagName || sTagName == "")
		return null;
	if (!oParent)
		oParent = document;

	var oRetVal = null;
	
	if (typeof(oParent.getElementsByTagName) != "undefined")
	{	// Try the W3C DOM method first
		oRetVal = oParent.getElementsByTagName(sTagName);
		if (!oRetVal || (oRetVal.length == 0))
			oRetVal = oParent.getElementsByTagName(sTagName.toLowerCase());
		if (!oRetVal || (oRetVal.length == 0))
			oRetVal = oParent.getElementsByTagName(sTagName.toUpperCase());
	}

	// It might not work for MSIE5.0 for "*" (any tag). In that case, 
	// try again using the Microsoft DOM level 0 method
	if (oRetVal && oRetVal.length == 0 && sTagName == "*")
		oRetVal = null;
	
	 // If the W3C DOM method did not succeed, try again using the Microsoft 
	 // DOM level 0 method
	if (!oRetVal && oParent.all && oParent.all.tags)
	{
		oRetVal = oParent.all.tags(sTagName);
		if (!oRetVal || (oRetVal.length == 0))
			oRetVal = oParent.all.tags(sTagName.toUpperCase());
		if (!oRetVal || (oRetVal.length == 0))
			oRetVal = oParent.all.tags(sTagName.toLowerCase());
	}

	return oRetVal;
}

function DHTML_getElementByTagName(sTagName, oParent, iIndex)
{
	var arrElem = DHTML_getElementsByTagName(sTagName, oParent);
	if (!arrElem)
		return null;
	if (!iIndex)
		iIndex = 0;
	if (iIndex < 0 || iIndex >= arrElem.length)
		return null;
	return arrElem[iIndex];
}

//------------------------- Operations on DHTML tables -------------------------
function DHTML_TableGetRow(oTable, iRow)
{
	if (!oTable || !oTable.rows)
		return null;
	return oTable.rows.item(iRow);
}

function DHTML_TableGetRowCells(oTable, iRow)
{
	var oRow = DHTML_TableGetRow(oTable, iRow);
	if (oRow)
		return oRow.cells;
	return null;
}

function DHTML_TableGetCell(oTable, iRow, iCol)
{
	var oCells = DHTML_TableGetRowCells(oTable, iRow);
	if (!oCells)
		return null;
	return oCells.item(iCol);
}

//------------------------- Operations on DHTML events -------------------------
if (document.addEventListener)
{
	DHTML_attachEvent = function (rObject, sEventName, rHandler, bUseCapture)
	{
		if (!rObject || !sEventName)
			return false;
		if (!bUseCapture)
			bUseCapture = false;
		rObject.addEventListener(sEventName, rHandler, bUseCapture);
	}

	DHTML_detachEvent = function (rObject, sEventName, rHandler, bUseCapture)
	{
		if (!rObject || !sEventName)
			return false;
		if (!bUseCapture)
			bUseCapture = false;
		rObject.removeEventListener(sEventName, rHandler, bUseCapture);
	}
} // {if} Event listenners are available
else if (document.attachEvent)
{
	DHTML_attachEvent = function (rObject, sEventName, rHandler /*, bUseCapture */)
	{
		if (!rObject || !sEventName)
			return false;
		// Workaround IE bug (redefine "this" pointer)
		var sPropName = "on" + sEventName.toLowerCase();
		return rObject.attachEvent(sPropName, function() {rHandler.apply(rObject, arguments); } );
	}

	DHTML_detachEvent = function (rObject, sEventName, rHandler /*, bUseCapture */)
	{
		if (!rObject || !sEventName)
			return false;
		var sPropName = "on" + sEventName.toLowerCase();
		rObject.detachEvent(sPropName, rHandler);
	}
} // {else if} attach/detach events are avalable (IE)
else
{
	DHTML_attachEvent = function (rObject, sEventName, rHandler /*, bUseCapture */)
	{
		if (!rObject || !sEventName)
			return false;
		var sPropName = "on" + sEventName.toLowerCase();
		rObject[sPropName] = rHandler;
		return true;
	}

	DHTML_detachEvent = function (rObject, sEventName, rHandler /*, bUseCapture */)
	{
		if (!rObject || !sEventName)
			return false;
		var sPropName = "on" + sEventName.toLowerCase();
		rObject[sPropName] = null;
	}
} // {else} Neither event listenners nor attach/detach events are avalable

function DHTML_getEvent(oEvent)
{
	if (!oEvent)
		oEvent = window.event;
	return oEvent;
}

function DHTML_getEventTarget(oEvent)
{
	oEvent = DHTML_getEvent(oEvent);
	
	var target;
	if (oEvent.target) target = oEvent.target; // W3C syntax
	else if (oEvent.srcElement) target = oEvent.srcElement; // MSIE syntax
	
	// Safari will get the text node instead of the element, if the text node has been clicked
	if (target.nodeType == 3)
		target = target.parentNode;
	return target;
}

function DHTML_getEventMouseButtons(oEvent)
{
	oEvent = DHTML_getEvent(oEvent);
	if ("which" in oEvent)
		return oEvent.which;
	return oEvent.button;
}

// Stop the event from bubbling upwards (cancelBubble, stopPropagation)
function stopEventBubbling(event)
{
	if (!event) var event = window.event;
	
	event.cancelBubble = true; // MSIE syntax
	if (event.stopPropagation) event.stopPropagation(); // W3C syntax
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//                            Operations on dates                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////
if (!Date.prototype.getDaysInMonth)
	Date.prototype.getDaysInMonth = Date_getDaysInMonth;
if (!Date.prototype.getDayOfYear)
	Date.prototype.getDayOfYear = Date_getDayOfYear;
if (!Date.prototype.getWeekOfYear)
	Date.prototype.getWeekOfYear = Date_getWeekOfYear;
if (!Date.prototype.formatDateTimeString)
	Date.prototype.formatDateTimeString = Date_formatDateTimeString;
if (!Date.prototype.prevDay)
	Date.prototype.prevDay = Date_prevDay;
if (!Date.prototype.nextDay)
	Date.prototype.nextDay = Date_nextDay;
if (!Date.prototype.prevWeek)
	Date.prototype.prevWeek = Date_prevWeek;
if (!Date.prototype.nextWeek)
	Date.prototype.nextWeek = Date_nextWeek;
if (!Date.prototype.prevMonth)
	Date.prototype.prevMonth = Date_prevMonth;
if (!Date.prototype.nextMonth)
	Date.prototype.nextMonth = Date_nextMonth;
if (!Date.prototype.prevYear)
	Date.prototype.prevYear = Date_prevYear;
if (!Date.prototype.nextYear)
	Date.prototype.nextYear = Date_nextYear;
if (!Date.prototype.addDate)
	Date.prototype.addDate = Date_addDate;
	
function Date_getDaysInMonth(nMonth, nYear)
{
	if (arguments.length < 2)
	{
		nYear = this.getFullYear();
		if (arguments.length == 0)
			nMonth = this.getMonth();
	}

	// Compute number of days in month
	var nDays;
	if (nMonth == 1)
		nDays = ((!(nYear % 4) && ((nYear % 100) || !(nYear % 400))) ? 29 : 28);
	else
		nDays = ((nMonth == 3 || nMonth == 5 || nMonth == 8 || nMonth == 10) ? 30 : 31); 
	return nDays;
}

function Date_getDayOfYear()
{
	var dateJanFirst = new Date(this);
	dateJanFirst.setDate(1);
	dateJanFirst.setMonth(0);
	var nDiff = this - dateJanFirst;

	return Math.floor(nDiff / 86400000) + 1;
}

function Date_getWeekOfYear(nFirstWeekDay)
{
	var dateJanFirst = new Date(this.getFullYear(), 0, 1);
	var nOffset = dateJanFirst.getDay();
	if (nFirstWeekDay)
		nOffset += (nOffset < nFirstWeekDay ? 7 : 0) - nFirstWeekDay;
	return Math.floor((nOffset + this.getDayOfYear()) / 7) + 1;
}

function Date_formatDateTimeString(sFormat, oLocale)
{
	return Date_Format(sFormat, this, oLocale);
}

function Date_prevDay(dateValue)
{
	return Date_addDate(0, 0, -1, (dateValue ? dateValue : this));
}

function Date_nextDay(dateValue)
{
	return Date_addDate(0, 0, 1, (dateValue ? dateValue : this));
}

function Date_prevWeek(dateValue)
{
	return Date_addDate(0, 0, -7, (dateValue ? dateValue : this));
}

function Date_nextWeek(dateValue)
{
	return Date_addDate(0, 0, 7, (dateValue ? dateValue : this));
}

function Date_prevMonth(dateValue)
{
	return Date_addDate(0, -1, 0, (dateValue ? dateValue : this));
}

function Date_nextMonth(dateValue)
{
	return Date_addDate(0, 1, 0, (dateValue ? dateValue : this));
}

function Date_prevYear(dateValue)
{
	return Date_addDate(-1, 0, 0, (dateValue ? dateValue : this));
}

function Date_nextYear(dateValue)
{
	return Date_addDate(1, 0, 0, (dateValue ? dateValue : this));
}

function Date_addDate(nYears, nMonthes, nDays, dateValue)
{
	if (!dateValue)
		dateValue = this;

	// First of all, add days - it may change year and month
	var nDayMSec = 86400000;
	var nNewValue = dateValue.valueOf() + (nDayMSec * nDays);
	dateValue.setTime(nNewValue);

	// Now, compute new year and month
	var nYear = dateValue.getFullYear();
	var nMonth = dateValue.getMonth();
	var nDay = dateValue.getDate();

	nYear += nYears;
	if (Math.abs(nMonthes) > 11)
	{
		var nMonthedOrg = nMonthes;
		nMonthes = nMonthes % 12;
		nYears = Math.floor((nMonthedOrg - nMonthes) / 12);
		nYear += nYears;
	}

	nMonth += nMonthes;
	if (nMonth < 0)
	{
		--nYear;
		nMonth += 12;
	}
	else if (nMonth > 11)
	{
		++nYear;
		nMonth -= 12;
	}
	
	// Now, adjust day (do not let it cross the month boundary)
	var nDaysInMonth = Date_getDaysInMonth(nMonth, dateValue.getFullYear());
	if (nDay > nDaysInMonth)
		nDay = nDaysInMonth;
	dateValue.setFullYear(nYear, nMonth, nDay);
	return dateValue;
}

//---------------------------------- Locales ----------------------------------
var Locale_Default = new Object();
Locale_Default.MonthName = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
Locale_Default.AbbrMonthName = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
Locale_Default.WeekDayName = new Array("Sunday", "Monday", "Tuesday", "Wednesday", "Thurday", "Friday", "Saturday");
Locale_Default.AbbrWeekDayName = new Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
Locale_Default.AmPm = new Array("AM", "PM");
Locale_Default.ShortDateFormat = "%m/%d/%Y"
Locale_Default.FirstDayOfWeek = 7;

//-------------------------------- Format dates --------------------------------
function Date_Format(sFormat, oDate, oLocale)
{
	var reFmtCode = /%\#?([aAbBcdgHIjmMpSUwWxXyYzZ%])/;
	var sRemained = sFormat;
	var sResult = "";
	var nValue;
	if (!oLocale)
		oLocale = Locale_Default;

	while (sRemained.length > 0)
	{
		var arrMatches = sRemained.match(reFmtCode);
		if (!arrMatches)
		{
			sResult += sRemained;
			break;
		}
		
		sResult += sRemained.substr(0, arrMatches.index);
		var bLeadingZeros = (sRemained.charAt(arrMatches.index+1) != "#");
		
		switch (arrMatches[1])
		{
			case "a":	// Abbreviated weekday name 
				sResult += oLocale.AbbrWeekDayName[oDate.getDay()];
				break;
			case "A":	// Full weekday name 
				sResult += oLocale.WeekDayName[oDate.getDay()];
				break;
			case "b":	// Abbreviated month name 
				sResult += oLocale.AbbrMonthName[oDate.getMonth()];
				break;
			case "B":	// Full month name 
				sResult += oLocale.MonthName[oDate.getMonth()];
				break;
			case "c":	// Date and time representation appropriate for locale 
				sResult += oDate.toLocaleString();
				break;
			case "d":	// Day of month as decimal number (01 – 31) 
				nValue = oDate.getDate();
				if (bLeadingZeros && nValue < 10)
					sResult += "0" + String(nValue);
				else
					sResult += String(nValue);
				break;
			case "g":	// GMT representation of date and time
				sResult += oDate.toGMTString();
				break;
			case "H":	// Hour in 24-hour format (00 – 23) 
				nValue = oDate.getHours();
				if (bLeadingZeros && nValue < 10)
					sResult += "0" + String(nValue);
				else
					sResult += String(nValue);
				break;
			case "I":	// Hour in 12-hour format (01 – 12) 
				nValue = oDate.getHours();
				if (nValue > 12)
					nValue -= 12;
				if (nValue < 10)
					sResult += "0" + String(nValue);
				else
					sResult += String(nValue);
				break;
			case "j":	// Day of year as decimal number (001 – 366)
				nValue = oDate.getDayOfYear();
				if (bLeadingZeros && nValue < 10)
					sResult += "0" + String(nValue);
				else
					sResult += String(nValue);
				break;
			case "m":	// Month as decimal number (01 – 12)
				nValue = oDate.getMonth() + 1;
				if (bLeadingZeros && nValue < 10)
					sResult += "0" + String(nValue);
				else
					sResult += String(nValue);
				break;
			case "M":	// Minute as decimal number (00 – 59)
				nValue = oDate.getMinutes();
				if (bLeadingZeros && nValue < 10)
					sResult += "0" + String(nValue);
				else
					sResult += String(nValue);
				break;
			case "p":	// Current locale's A.M./P.M. indicator for 12-hour clock
				sResult += oLocale.AmPm[oDate.getHours() < 12 ? 0 : 1];
				break;
			case "S":	// Second as decimal number (00 – 59) 
				nValue = oDate.getSeconds();
				if (bLeadingZeros && nValue < 10)
					sResult += "0" + String(nValue);
				else
					sResult += String(nValue);
				break;
			case "U":	// Week of year as decimal number, with Sunday as first day of week (00 – 53) 
				sResult += String(oDate.getWeekOfYear(0));
				break;
			case "w":	// Weekday as decimal number (0 – 6; Sunday is 0) 
				sResult += String(oDate.getDay());
				break;
			case "W":	// Week of year as decimal number, with Monday as first day of week (00 – 53) 
				sResult += String(oDate.getWeekOfYear(1));
				break;
			case "x":	// Date representation for current locale 
				sResult += oDate.toLocaleDateString();
				break;
			case "X":	// Time representation for current locale 
				sResult += oDate.toLocaleTimeString();
				break;
			case "y":	// Year without century, as decimal number (00 – 99) 
				nValue = (oDate.getFullYear() % 100);
				if (bLeadingZeros && nValue < 10)
					sResult += "0" + String(nValue);
				else
					sResult += String(nValue);
				break;
			case "Y":	// Year with century, as decimal number 
				sResult += String(oDate.getFullYear());
				break;
			case "z":	// Timezone offset
				nValue = -1 * (oDate.getTimezoneOffset() / 60);
				sResult += (nValue < 0 ? String(nValue) : "+" + String(nValue));
				break;
			case "%":	// Percent sign 
				sResult += "%";
				break;
		}
		sRemained = sRemained.substr(arrMatches.index + arrMatches[0].length);
	}

	return sResult;
}

function Date_createDate(nYear, nMonth, nDay)
{
	var dateValue = new Date(nYear, nMonth, 1);
	var nMaxDay = dateValue.getDaysInMonth();
	dateValue.setDate(Math.min(nDay, nMaxDay));
	return dateValue;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//                            Operations on Object                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////
function Object_Clone(objSrc)
{
	var objClone;
	try {
		objClone = new objSrc.constructor();//sometimes throws error
	} catch(x) {
		var oMatch = objSrc.constructor.toString().match( /\s*function (.*)\(/ );
		eval( "objClone = new " + oMatch[1] + "();");
	}
	for (var sProp in objSrc)
	{
		var oSrcProp = objSrc[sProp];
		//don't overwrite a member function with a blank object "function anonymous() {}"
		if( ! ( typeof objClone[ sProp ] == "function" && typeof objSrc[sProp] != "function" ) ) {
			if (oSrcProp && (typeof(oSrcProp) == "object"))
				objClone[sProp] = Object_Clone(oSrcProp);
			else
				objClone[sProp] = oSrcProp;
		}
	}
	return objClone;
}

function Object_Destroy(rObject)
{
	for (var sProp in rObject)
		rObject[sProp] = null;
}

//-------------------------------- Query string --------------------------------
function QueryString()
{
}

function QueryString_ApplyPrototype(rObject)
{
	if (RegExp.compile)
	{
		rObject.m_reParam = new RegExp();
		rObject.m_reParam.compile("(?:&|^)(\\w+)\\s*=([^&]*)", "g");
	}
	else
		rObject.m_reParam = new RegExp("(?:&|^)(\\w+)\\s*=([^&]*)", "g");
	rObject.m_arrParams = null;
	rObject.m_bCaseSensitive = false;
	rObject.Count = 0;

	rObject.clear = QueryString_clear;
	rObject.parseUrl = QueryString_parseUrl;
	rObject.parseUrlQuery = QueryString_parseUrlQuery;
	rObject.getQueryString = QueryString_getQueryString;
	rObject.addParam = QueryString_addParam;
	rObject.getParam = QueryString_getParam;
	rObject.removeParam = QueryString_removeParam;
}

QueryString_ApplyPrototype(QueryString.prototype);

function QueryString_clear()
{
	this.m_arrParams = null;
}

function QueryString_parseUrl(sUrl)
{
	var sUrlQuery = "";
	var nQueryStringStart = sUrl.indexOf("?");
	if (nQueryStringStart >= 0)
		sUrlQuery = sUrl.substr(nQueryStringStart+1);
	this.parseUrlQuery(sUrlQuery);
}

function QueryString_parseUrlQuery(sUrlQuery)
{
	this.clear();

	if (!sUrlQuery)
		return;	// Nothing to do

	var arrMatches = null;
	while ((arrMatches = this.m_reParam.exec(sUrlQuery)) != null)
	{
		var sParamName = arrMatches[1];
		var sParamValue = arrMatches[2];

		this.addParam(sParamName, decodeURIComponent(sParamValue));
	}
}

function QueryString_getQueryString()
{
	if (!this.m_arrParams)
		return "";
	
	var sQueryString = "";
	for (var sParamName in this.m_arrParams)
	{
		var arrParamValues = this.m_arrParams[sParamName];
		if (!arrParamValues)
			continue;
		for (var iValue=0; iValue<arrParamValues.length; ++iValue)
		{
			if (sQueryString.length > 0)
				sQueryString += "&";
			sQueryString += sParamName + "=";
			sQueryString += encodeURIComponent(arrParamValues[iValue]);
		}
	}

	return sQueryString;
}

function QueryString_addParam(sParamName, sParamValue)
{
	if (!this.m_arrParams)
		this.m_arrParams = new Object();

	if (!this.m_bCaseSensitive)
		sParamName = sParamName.toLowerCase();

	var arrParamValues = null;
	if (!(sParamName in this.m_arrParams))
	{
		arrParamValues = new Array();
		this.m_arrParams[sParamName] = arrParamValues;
		++this.Count;
	}
	else
		arrParamValues = this.m_arrParams[sParamName];

	arrParamValues.push(sParamValue);
}

function QueryString_getParam(sParamName, iIndex)
{
	if (!this.m_arrParams)
		return null;

	if (!this.m_bCaseSensitive)
		sParamName = sParamName.toLowerCase();

	if (sParamName in this.m_arrParams)
	{
		var arrParamValues = this.m_arrParams[sParamName];
		if (typeof(iIndex) != "undefined")
			return arrParamValues[iIndex];
		else
			return arrParamValues;
	}
	return null;
}

function QueryString_removeParam(sParamName)
{
	if (!this.m_bCaseSensitive)
		sParamName = sParamName.toLowerCase();
	if (sParamName in this.m_arrParams)
		delete this.m_arrParams[sParamName];
}

